Skip to content

Commit 2613cfa

Browse files
committed
judge a pipeline on its log, not on exiting
the run only exited early on gst, where a leaky queue was inserted after filesrc and cut the input to 65 of 5554 frames. g2g got no such queue, ran the whole video, and hit the timeout, so the two backends were never compared on the same work. both now run to the cap and pass if the log is clean. the gap counts recorded here came from that comparison, so they go until the suite is run again.
1 parent 8d19f85 commit 2613cfa

2 files changed

Lines changed: 29 additions & 34 deletions

File tree

DESIGN_TODO.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,19 @@ Python-element host are tracked in that repo's `DESIGN_TODO.md`, under
66

77
## g2g backend coverage
88

9-
- **32 README pipelines run on gst and fail under `PYML_BACKEND=g2g`.**
10-
`tests/test_pipelines.py` at a 30 s timeout passes 48 of 79 on gst and 16 on
11-
g2g. 31 fail on both, which is the environment rather than g2g, and nothing
12-
passes on g2g that fails on gst. Most of the 32 report `pipeline error:
13-
Hardware(Other)`, which is how g2g reports a hosted element raising, so each
14-
one needs its log in `tests/logs` read to name the cause. `pyml_overlay` is in
15-
16 of them, the largest cluster. Two causes known: `pyml_kafkasink` calls
16-
`Gst.Pad` APIs directly and dies on `Gst.init`, and `demo_soccer`'s engine
17-
raises `TypeError: MLEngine.__init__() got an unexpected keyword argument
18-
'device'`. The suite takes about 17 minutes per backend and wants the GPU, so
19-
run one backend at a time on a 6 GB card, and keep the machine otherwise idle
20-
or the 30 s timeout starts measuring load instead.
9+
- **How many README pipelines run under `PYML_BACKEND=g2g` needs measuring.**
10+
Run `tests/test_pipelines.py` under each backend and compare: one that passes
11+
on gst and fails on g2g is a gap, one that fails on both is the environment.
12+
Only the error categories count as gaps. `pipeline error: Hardware(Other)` is
13+
how g2g reports a hosted element raising, so each needs its log in
14+
`tests/logs` read to name the cause. Known so far: `pyml_kafkasink` calls
15+
`Gst.Pad` APIs directly and dies on `Gst.init`, `demo_soccer`'s engine raises
16+
`TypeError: MLEngine.__init__() got an unexpected keyword argument 'device'`,
17+
and `pyml_streammux` is refused with `pyelement: more than one input links
18+
here, but it is not a registered muxer`. The suite wants the GPU for about 20
19+
minutes per backend, so run one backend at a time on a 6 GB card and leave the
20+
machine otherwise idle, including between backends: a model still resident
21+
from the previous run fails the next one at preroll.
2122

2223
- **Eleven elements have no per-frame seam, so they cannot run on g2g at all.**
2324
`alert`, `tracker`, `vad`, `clap`, `overlay_counter`, `kafkasink`,

tests/test_pipelines.py

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -64,8 +64,10 @@ def get_pipelines_from_readme():
6464
pipeline = pipeline.replace("python pyml-launch.py", LAUNCHER, 1)
6565

6666
parts = pipeline.split("!")
67-
modified = False
6867

68+
# A `filesrc` run has no equivalent cap: these mp4s carry `moov` at the
69+
# end, so bounding the source by bytes leaves the decoder with no index.
70+
# Those pipelines run until PIPELINE_TIMEOUT instead.
6971
for i, part in enumerate(parts):
7072
part_clean = part.strip()
7173
if "videotestsrc" in part_clean:
@@ -74,23 +76,8 @@ def get_pipelines_from_readme():
7476
parts[i] = f"{part_clean} num-buffers=100"
7577
else:
7678
parts[i] = re.sub(r"num-buffers=\d+", "num-buffers=100", part_clean)
77-
modified = True
7879
break
7980

80-
# Only on gst: g2g's `decodebin` takes its input caps from the element
81-
# ahead of it, and a `queue` declares none, so capping the run this way
82-
# would stop the pipeline parsing at all.
83-
if not modified and BACKEND == "gst":
84-
for i, part in enumerate(parts):
85-
part_clean = part.strip()
86-
if "filesrc" in part_clean:
87-
parts.insert(i + 1, "queue max-size-buffers=100 leaky=upstream")
88-
modified = True
89-
break
90-
91-
if not modified:
92-
print(f"Warning: No filesrc or videotestsrc found in pipeline: {pipeline}")
93-
9481
modified_pipeline = " ! ".join(parts).strip()
9582
print(f"Modified pipeline: {modified_pipeline}")
9683
modified_pipelines.append(modified_pipeline)
@@ -137,7 +124,10 @@ def rewrite(match):
137124
@pytest.mark.parametrize("pipeline", PIPELINES, ids=lambda p: p)
138125
def test_pipeline(pipeline, tmp_path):
139126
"""
140-
Test a GStreamer pipeline for 100 frames, checking for errors, with latency tracing.
127+
Run a README pipeline and check its log for errors.
128+
129+
A pipeline still running at `PIPELINE_TIMEOUT` passes: only `videotestsrc`
130+
takes a frame cap, so a file-backed one runs as long as its media lasts.
141131
"""
142132
LOG_DIR.mkdir(parents=True, exist_ok=True)
143133
os.sync()
@@ -187,6 +177,7 @@ def test_pipeline(pipeline, tmp_path):
187177
env["GST_DEBUG_NO_COLOR"] = "1"
188178

189179
# Run the pipeline
180+
ran_to_the_cap = False
190181
try:
191182
with open(log_file, "w") as log:
192183
# Own process group: the shell is not the pipeline, it is the
@@ -205,10 +196,12 @@ def test_pipeline(pipeline, tmp_path):
205196
process.wait(timeout=PIPELINE_TIMEOUT)
206197
return_code = process.returncode
207198
except subprocess.TimeoutExpired:
199+
# Still running at the cap, which is what a healthy uncapped pipeline
200+
# does: the media outlasts any timeout worth waiting. The log below says
201+
# whether it was working, so the run is judged on that, not on exiting.
208202
end_process_group(process)
209-
pytest.fail(
210-
f"Pipeline timed out after {PIPELINE_TIMEOUT}s. Full pipeline: {pipeline}. See {log_file}"
211-
)
203+
ran_to_the_cap = True
204+
return_code = None
212205
except Exception as e:
213206
end_process_group(process)
214207
pytest.fail(
@@ -234,7 +227,7 @@ def test_pipeline(pipeline, tmp_path):
234227
)
235228

236229
# Check exit code
237-
if return_code != 0:
230+
if not ran_to_the_cap and return_code != 0:
238231
if (
239232
"End-Of-Stream" not in log_content
240233
and "reached end of stream" not in log_content
@@ -243,7 +236,8 @@ def test_pipeline(pipeline, tmp_path):
243236
f"Pipeline failed with exit code {return_code}. Full pipeline: {pipeline}. See {log_file}"
244237
)
245238

246-
print(f"Pipeline processed 100 frames successfully: {pipeline}")
239+
ending = f"ran the full {PIPELINE_TIMEOUT}s" if ran_to_the_cap else "ran to the end"
240+
print(f"Pipeline {ending} with no errors: {pipeline}")
247241

248242

249243
def test_pipelines_found():

0 commit comments

Comments
 (0)