-
Notifications
You must be signed in to change notification settings - Fork 30
248 lines (208 loc) · 7.88 KB
/
Copy pathv4l2-test.yml
File metadata and controls
248 lines (208 loc) · 7.88 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
name: V4L2 On-Device Tests
on:
workflow_dispatch:
inputs:
test_filter:
description: 'Pytest -k filter (e.g. "streaming", "test_depth_streaming"). Leave empty for all tests.'
required: false
default: ''
push:
branches: [master, dev]
paths:
- 'kernel/realsense/**'
- 'test/v4l2_test/**'
pull_request:
branches: [master, dev]
paths:
- 'kernel/realsense/**'
- 'test/v4l2_test/**'
permissions: read-all
jobs:
v4l2-test:
runs-on: [self-hosted, jetson, xavier]
timeout-minutes: 30
env:
TEST_DIR: test/v4l2_test
RESULTS_DIR: v4l2_test_results
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Create results directory
run: mkdir -p "$RESULTS_DIR"
- name: Collect system info
run: |
echo "=== System Information ==="
echo "Hostname: $(hostname)"
echo "Kernel: $(uname -r)"
echo "Date: $(date -Iseconds)"
echo ""
echo "--- JetPack / L4T ---"
cat /etc/nv_tegra_release 2>/dev/null || echo "N/A"
echo ""
echo "--- Video devices ---"
ls -la /dev/video* 2>/dev/null || echo "No video devices found"
echo ""
echo "--- D4XX module ---"
lsmod | grep d4xx || echo "d4xx module not loaded"
# Save structured info for artifact
python3 -c "
import json, subprocess, datetime
info = {
'timestamp': datetime.datetime.now().isoformat(),
'hostname': '$(hostname)',
'kernel': '$(uname -r)',
}
r = subprocess.run(['cat', '/etc/nv_tegra_release'], capture_output=True, text=True)
info['jetpack'] = r.stdout.strip() if r.returncode == 0 else 'N/A'
r = subprocess.run(['bash', '-c', 'ls /dev/video* 2>/dev/null'], capture_output=True, text=True)
info['video_devices'] = r.stdout.strip().split('\n') if r.stdout.strip() else []
r = subprocess.run(['bash', '-c', 'lsmod | grep d4xx'], capture_output=True, text=True)
info['d4xx_module'] = r.stdout.strip()
with open('$RESULTS_DIR/system_info.json', 'w') as f:
json.dump(info, f, indent=2)
"
- name: Reload D4XX driver
run: |
echo "Removing d4xx module..."
sudo rmmod d4xx 2>/dev/null || echo "Module was not loaded"
sleep 1
echo "Loading d4xx module..."
sudo modprobe d4xx
sleep 2
echo "Driver status:"
lsmod | grep d4xx
- name: Hardware reset
run: |
if [ -e /dev/video0 ]; then
v4l2-ctl -d /dev/video0 --set-ctrl=hw_reset=1 2>/dev/null || echo "hw_reset not supported (non-fatal)"
sleep 2
else
echo "Warning: /dev/video0 not found, skipping hw_reset"
fi
- name: Sanity check
id: sanity
run: |
echo "=== Sanity Check ==="
PASS=true
# 1. Video devices exist
echo "Checking video devices..."
if ls /dev/video* >/dev/null 2>&1; then
echo "OK: Video devices found"
ls /dev/video*
else
echo "FAIL: No video devices"
PASS=false
fi
# 2. D4XX module loaded
echo ""
echo "Checking D4XX driver..."
if lsmod | grep -q d4xx; then
echo "OK: d4xx module loaded"
else
echo "FAIL: d4xx module not loaded"
PASS=false
fi
# 3. Camera responds to V4L2
echo ""
echo "Querying camera capabilities..."
if v4l2-ctl -d /dev/video0 --info 2>/dev/null; then
echo "OK: Camera responds"
else
echo "FAIL: Camera not responding"
PASS=false
fi
# 4. Quick stream test (1 frame)
echo ""
echo "Testing basic streaming..."
if timeout 10 v4l2-ctl -d /dev/video0 \
--set-fmt-video=width=640,height=480 \
--stream-mmap --stream-count=1 2>&1; then
echo "OK: Captured test frame"
else
echo "FAIL: Cannot stream from camera"
PASS=false
fi
if [ "$PASS" = false ]; then
echo ""
echo "SANITY CHECK FAILED - camera is not operational"
echo "sanity_passed=false" >> "$GITHUB_OUTPUT"
exit 1
fi
echo ""
echo "SANITY CHECK PASSED"
echo "sanity_passed=true" >> "$GITHUB_OUTPUT"
- name: Run V4L2 pytest suite
if: steps.sanity.outputs.sanity_passed == 'true'
run: |
cd test
PYTEST_ARGS="-vs --tb=short -m d457 v4l2_test/"
if [ -n "${{ github.event.inputs.test_filter }}" ]; then
PYTEST_ARGS="$PYTEST_ARGS -k '${{ github.event.inputs.test_filter }}'"
fi
echo "Running: python3 -m pytest $PYTEST_ARGS"
echo ""
# Run pytest; tee output to file for artifact, allow non-zero exit
python3 -m pytest $PYTEST_ARGS 2>&1 | tee "../$RESULTS_DIR/pytest_output.log" || true
# Capture actual exit code by re-running with --co (collect only) to not re-run
# Instead, parse the log for the summary line
if grep -q "failed" "../$RESULTS_DIR/pytest_output.log"; then
echo "::warning::Some tests failed - see pytest_output.log artifact"
fi
- name: Collect dmesg logs
if: always()
run: |
sudo dmesg -T | grep -E '(d4xx|D4XX|GMSL|V4L2|video|media|tegra-camrtc|nvcsi|vi5)' \
| tail -500 > "$RESULTS_DIR/dmesg_filtered.log" 2>/dev/null || true
sudo dmesg -T | tail -200 > "$RESULTS_DIR/dmesg_tail.log" 2>/dev/null || true
echo "Dmesg logs collected"
- name: Generate test summary
if: always()
run: |
python3 -c "
import re, sys
log_path = '$RESULTS_DIR/pytest_output.log'
try:
with open(log_path) as f:
content = f.read()
except FileNotFoundError:
print('No pytest output (sanity check may have failed)')
sys.exit(0)
# Parse summary line like: '== 19 passed, 38 failed, 7 skipped in 45.32s =='
m = re.search(r'=+\s*(.*?)\s*=+\s*$', content, re.MULTILINE)
if m:
summary = m.group(1)
print(f'## Test Summary\n\n{summary}\n')
# Extract counts
passed = re.search(r'(\d+) passed', summary)
failed = re.search(r'(\d+) failed', summary)
skipped = re.search(r'(\d+) skipped', summary)
p = int(passed.group(1)) if passed else 0
f = int(failed.group(1)) if failed else 0
s = int(skipped.group(1)) if skipped else 0
total = p + f + s
print(f'| Metric | Count |')
print(f'|--------|-------|')
print(f'| Passed | {p} |')
print(f'| Failed | {f} |')
print(f'| Skipped | {s} |')
print(f'| Total | {total} |')
if f > 0:
print(f'\n### Failed Tests\n')
for line in content.split('\n'):
if 'FAILED' in line:
print(f'- {line.strip()}')
else:
print('Could not parse pytest summary')
" | tee "$RESULTS_DIR/summary.md"
# Write to GitHub step summary
if [ -f "$RESULTS_DIR/summary.md" ]; then
cat "$RESULTS_DIR/summary.md" >> "$GITHUB_STEP_SUMMARY"
fi
- name: Upload test artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: v4l2-test-results-${{ github.run_number }}
path: |
${{ env.RESULTS_DIR }}/
retention-days: 30