Skip to content

Add Jetson platform to the CE OEM MIPI camera tests (New) - #2699

Open
seankingyang wants to merge 10 commits into
canonical:mainfrom
seankingyang:migration_jetson_camera
Open

Add Jetson platform to the CE OEM MIPI camera tests (New)#2699
seankingyang wants to merge 10 commits into
canonical:mainfrom
seankingyang:migration_jetson_camera

Conversation

@seankingyang

@seankingyang seankingyang commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Description

Migrate Jetson camera testing onto the CE OEM MIPI camera framework, adding
jetson as a fourth platform alongside genio, imx and rz (per the
"Adding New Platforms" process in units/camera/README.md). The change is
purely additive: the legacy units/Jetson/camera_*.pxu jobs are untouched,
no test plan is modified, and the new jobs only run on DUTs whose manifest
sets has_vendor_specific_mipi_camera.

Ref: https://warthogs.atlassian.net/browse/OEMQA-6806

Data-format changes (scenario JSON and resource output):

  • The scenario JSON gains an optional camera_id key that overrides
    v4l2_device_name (which remains the default identifier) for capture
    methods that don't address the camera by its v4l2 name. On Jetson it holds
    the Argus source index consumed by nvarguscamerasrc sensor-id= /
    nvargus_nvraw --c. Every item must declare at least one of the two keys.
  • The mipi_camera_resource output gains name (always) and mode (only
    when the scenario declares it). All pre-existing fields are unchanged, and
    the rendered job ids are byte-identical for all 12 existing Genio/NXP/RZ
    scenario files (verified by regenerating resources old-vs-new).

Resolved issues

Resolves https://warthogs.atlassian.net/browse/OEMQA-6806

Documentation

  • units/camera/README.md: new job-id/name composition, the camera_id
    override, and the optional GStreamer plugin-path environment variables.
  • data/Jetson-MIPI-Camera-TestScenario-TestSetup/Test_Scenario_and_Test_Setup.md:
    Jetson sensor/mode tables, per-connector IMX219 scenario files, and the
    required checkbox environment (deb and snap GStreamer plugin path values).

Tests

Add jetson as a fourth platform to the CE OEM camera framework,
alongside genio, imx and rz, following the "Contributing: Adding New
Platforms" process in units/camera/README.md.

Captures use the sensor's native format with no encoder, mirroring
camera_genio.py: nvarguscamerasrc -> nvvidconv -> filesink for raw
NV12, and nvargus_nvraw --format nvraw for native Bayer raw. Dropping
the H265/JPEG encoders removes the NVENC dependency, so the Orin Nano,
which has no NVENC, can record video like any other board, and one
imx219 scenario file serves both the Orin NX and the Orin Nano.

The Argus mode index is declared per resolution in the scenario JSON
rather than discovered at runtime. It is load-bearing: the IMX274
exposes four Argus modes across only two resolutions, so modes 1 and 3
(both 1920x1080@60, differing only in gain/exposure range) are
indistinguishable on every field the framework keys on. Without an
explicit mode, mode 3 is unreachable and the two render the same job
id. The mode therefore also enters both job-id templates and the
artifact pattern, behind a jinja2 conditional so platforms that
declare no mode render exactly as before.

v4l2_device_name carries the Argus source_index ("0"/"1"), the
identifier that nvargus_nvraw --c and nvarguscamerasrc sensor-id=
actually consume. The Tegra v4l2 names embed an i2c bus number and a
device-tree VI channel, neither of which tracks the sensor index: the
AGX Orin's two sensors sit on VI channels 0 and 2, and the Orin NX's
only sensor sits on VI channel 1.

Sensor modes are taken from the DUTs' own GST_ARGUS mode tables and
cross-checked against vendor documentation. Coverage per device: AGX
Orin 6 to 24 jobs, Orin NX 3 to 15, Orin Nano 2 to 15.

Purely additive: units/Jetson/camera_*.pxu and
bin/jetson_camera_sensors.py are untouched, and no test plan is
modified. The new jobs run only on a DUT whose manifest sets
has_vendor_specific_mipi_camera.

Verified: all 11 Genio/NXP/RZ scenario files generate byte-identical
resources with no mode key; Jetson generates 24/15 records each
carrying its mode; the four IMX274 records render 4 distinct job ids
(2 without the mode suffix); Genio job ids and command lines are
byte-identical.
@seankingyang seankingyang added the ce-oem The PR only contains to the ce-oem provider under the contrib area label Jul 17, 2026
Address review feedback on the Jetson camera addition:

- GStreamer plugin paths are no longer hardcoded in camera_jetson.py.
  GST_PLUGIN_PATH / GST_PLUGIN_SYSTEM_PATH / GST_PLUGIN_SCANNER now flow
  from the checkbox configuration through the jobs' environ list (the
  capture subprocess inherits them), so any project can supply its own
  values, the same way GST_LAUNCH_BIN works. Only the DISPLAY unset
  stays in code. GST_LAUNCH_BIN / MEDIA_CTL_CMD / V4L2_CTL_CMD are also
  added to the template jobs' environ so their config overrides reach
  the testing jobs, not just the readiness job.

- The scenario JSON gains an optional camera_id key that overrides
  v4l2_device_name, which remains the default camera identifier, for
  capture methods that do not address the camera by its v4l2 name. The
  Jetson files declare camera_id (the Argus source index) and drop the
  overloaded v4l2_device_name.

- The resource job now composes a per-record name field and the job-id
  templates collapse to capture-image_{{ name }} /
  record-video_{{ name }}, removing the hard-to-read jinja2 mode
  conditionals from the ids. Rendered ids are byte-identical for all 12
  existing scenario files (verified old-vs-new across Genio, NXP, RZ
  and Jetson IMX274).

- The IMX219 scenario is split into cam0 and cam1 files: the Orin NX
  and Orin Nano fit the same module on different carrier connectors,
  and the connector label is part of every job id. camera_id stays 0 in
  both files since Argus indexes detected sensors, not connectors.

- Add unit tests for CameraResources name composition and the camera_id
  override.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR extends the CE OEM MIPI camera test framework to support NVIDIA Jetson (Argus-based capture) by adding Jetson-specific scenario definitions, a Jetson camera backend, and resource/job-id generation updates to accommodate sensor modes and non-v4l2 camera identifiers.

Changes:

  • Add Jetson scenario documentation and JSON scenarios for IMX274 dual and IMX219 (cam0/cam1).
  • Extend resource generation and job templates to use a composed name, support optional mode, and allow camera_id to override the default v4l2_device_name identifier.
  • Introduce a Jetson camera handler (nvargus_nvraw + Argus GStreamer path) and add unit tests for the new resource behaviors.

Reviewed changes

Copilot reviewed 7 out of 10 changed files in this pull request and generated no comments.

Show a summary per file
File Description
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/camera/README.md Documents camera_id override, composed name format, Jetson env passthrough, and links Jetson setup docs.
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/camera/jobs.pxu Switches job IDs to {{ name }}, passes optional -md/--mode, and allows GStreamer plugin env passthrough.
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/tests/test_camera_utils.py Adds tests for composed name, camera_id override, and missing-identifier behavior.
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/Jetson-MIPI-Camera-TestScenario-TestSetup/Test_Scenario_and_Test_Setup.md Adds Jetson-specific scenario/setup documentation and environment guidance.
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/Jetson-MIPI-Camera-TestScenario-TestSetup/jetson_mipi_camera_test_scenario_imx274_dual.json Adds IMX274 dual-sensor Jetson scenarios (capture + record, with mode indices).
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/Jetson-MIPI-Camera-TestScenario-TestSetup/jetson_mipi_camera_test_scenario_imx219_cam0.json Adds IMX219 cam0 Jetson scenarios (capture + record, with mode indices).
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/data/Jetson-MIPI-Camera-TestScenario-TestSetup/jetson_mipi_camera_test_scenario_imx219_cam1.json Adds IMX219 cam1 Jetson scenarios (capture + record, with mode indices).
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/camera_utils.py Adds nvargus_nvraw, adds Jetson factory routing, and composes resource name plus optional mode + camera_id override.
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/camera_test.py Adds --mode CLI and includes mode in artifact patterns + forwards mode to handlers.
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/bin/camera_jetson.py Adds Jetson camera implementation using Argus (nvargus_nvraw + nvarguscamerasrc) with timeouts and headless-safe execution.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@seankingyang
seankingyang marked this pull request as ready for review July 17, 2026 07:33
@codecov

codecov Bot commented Jul 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 59.97%. Comparing base (615ef39) to head (255fddf).
⚠️ Report is 9 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2699   +/-   ##
=======================================
  Coverage   59.97%   59.97%           
=======================================
  Files         487      487           
  Lines       48918    48918           
  Branches     8757     8757           
=======================================
  Hits        29339    29339           
  Misses      18654    18654           
  Partials      925      925           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

The ce-oem-mipi-camera-full plan nested mipi-camera-manual and
mipi-camera-automated, plan ids that do not exist in any provider; the
actual plans are ce-oem-mipi-camera-manual/-automated (pre-existing,
the top-level ce-oem plan already nests the correct ids).

The IMX219 doc note assumed the Orin NX and Orin Nano DUTs sit on
different carrier connectors; the certification DUTs both carry the
module on cam0. Keep one scenario file per connector and describe the
selection by actual wiring.
First run on the Orin NX (202304-31523, Ubuntu Core 22) generated all
15 expected cam0 jobs with correct ids, but every capture failed. Four
test-definition fixes, all reproduced and root-caused on the DUT:

- Add user: root to both camera templates. checkbox normalizes job
  execution to the unprivileged session user, which has no EGL/Argus
  device access on Jetson (Argus 'No EGL device available', nvraw exit
  4). The legacy Jetson jobs all carry user: root. This also switches
  existing platforms' generated jobs to root, a superset of their
  current privileges.
- Pin the capture framerate: gstreamer captures negotiated the Argus
  30 fps default, which exceeds IMX219 mode 0 (21) and mode 1 (28)
  maxima, so those jobs could never pass. The scenario JSON now
  declares fps on gstreamer capture resolutions, the resource emits
  fps whenever a resolution declares it, and the capture command and
  Jetson handler pass it through to the caps. Capture job names and
  ids are unchanged.
- Tolerate post-EOS teardown errors: some CSI modules (Arducam IMX219
  clones) stochastically post 'Argus Correctable Error' after EOS,
  failing gst-launch although every frame was captured. If the
  artifact is non-empty the error is logged and tolerated;
  check_nonzero_files() remains the final pass criterion.
- Log the child's stdout/stderr when execute_command fails, so the
  Argus diagnostics reach the job io-log.

Also add NVARGUS_NVRAW_BIN to the template jobs' environ (the code
honors it but the config override never reached the jobs) and document
the Ubuntu Core setup: capture tools come from the NVIDIA multimedia
snap via snap aliases (gst-launch-1.0, nvargus_nvraw), which hosts the
Argus daemon and resolves its own GStreamer plugin paths, so no
GST_PLUGIN_* variables are needed there.
Snap-packaged checkbox pre-exports GST_PLUGIN_SYSTEM_PATH and
GST_PLUGIN_SCANNER in its wrapper, and checkbox only injects config
environment values for variables not already set, so GST_PLUGIN_*
overrides take no effect under a checkbox snap (verified on checkbox
7.3.0 on the Orin Nano DUT). GST_LAUNCH_BIN and NVARGUS_NVRAW_BIN are
not preset and always inject, which makes the multimedia-snap aliases
the supported route on Ubuntu Core.
Three .pyc files slipped into the previous commit; byte-code caches do
not belong in the repository. Ignore __pycache__ globally so it cannot
recur.
Isaac: the capture tools are guaranteed by the pre-test setup (the
multimedia snap aliases on Ubuntu Core, native binaries on classic), so
the NVARGUS_NVRAW_BIN override knob is unnecessary. Resolve
nvargus_nvraw from PATH directly and drop the variable from the jobs'
environ list and the docs.
@seankingyang
seankingyang marked this pull request as draft July 17, 2026 09:12
The three jetson-core-camera/* (snap-path) templates carried user: root
to reach the Tegra camera nodes (root:video 0660, runner not in the
video group). Rootless capture is now proven on the IGX UC22 DUT, so the
elevation is no longer needed and the jobs run as the invoking user.

The deb-path jetson-camera/* templates never set user: root and are
left untouched.

DEPENDENCY: rootless operation is not self-contained in this provider.
It requires both, shipped by the image/gadget, not by checkbox or the
snap:
  (a) the runner user holding an active seat (autologin console
      session), so logind uaccess ACLs apply; and
  (b) a uaccess udev rule tagging the Tegra camera nodes
      (video4linux, nvmap, nvhost-*, capture-isp/vi-channel*,
      tegra_camera_ctrl, nvidia-gpu-v2*) with an explicit
      RUN{builtin}+="uaccess" fallback, because a 99- rule lands after
      the stock uaccess ACL builtin at 73-seat-late.rules.
DUTs lacking either (e.g. AGX Orin / Orin Nano images without the rule)
will fail these jobs rootless.

Ref: OEMQA-6807, OEMQA-6806.
@seankingyang

Copy link
Copy Markdown
Contributor Author

Removed user: root from the jetson-core-camera/* templates

Commit 4fa847281 drops the user: root line from the three snap-path
jetson-core-camera/* templates in
contrib/checkbox-ce-oem/checkbox-provider-ce-oem/units/Jetson/camera_job.pxu
(nvargus image, gstreamer image, gstreamer video). The deb-path
jetson-camera/* templates never had user: root and are untouched.
Net diff: 3 deletions, nothing else.

Why this is safe — rootless capture proven (OEMQA-6807)

Verified first-hand on the IGX UC22 DUT (2026-07-18), rootless over plain
ssh, uid 1000, no sudo:

  • gst nvarguscamerasrc → nvjpegenc rootless → 346526 B JPEG.
  • nvargus-nvraw rootless → 2020549 B.
  • Independently re-verified → 344982 B JPEG, GST_ARGUS: Done Success.

The original blocker was kernel DAC, not AppArmor: the Tegra device nodes
are root:video 0660 and the runner is not in the video group (EACCES
with no AppArmor denial). The fix does not add group membership — the
runner already holds an active seat0 (tty autologin), so logind
uaccess ACLs apply once the nodes are tagged. One udev rule file
(/etc/udev/rules.d/99-tegra-camera-uaccess.rules):

SUBSYSTEM=="video4linux", TAG+="uaccess"
KERNEL=="nvmap", TAG+="uaccess"
KERNEL=="nvhost-*", TAG+="uaccess"
KERNEL=="capture-isp-channel*", TAG+="uaccess"
KERNEL=="capture-vi-channel*", TAG+="uaccess"
KERNEL=="tegra_camera_ctrl", TAG+="uaccess"
SUBSYSTEM=="nvidia-gpu-v2", TAG+="uaccess"
SUBSYSTEM=="nvidia-gpu-v2-power", TAG+="uaccess"
TAG=="uaccess", ENV{MAJOR}!="", RUN{builtin}+="uaccess"

The last line is required: the stock uaccess ACL builtin runs at
73-seat-late.rules, before a 99- file's tag lands, so the tag alone
would be applied too late without the explicit RUN{builtin}+="uaccess"
re-trigger.

Dependency — please weigh before merge

Rootless operation is not self-contained in this provider. It requires
both of the following, which are properties of the image / gadget, not
of checkbox or the multimedia snap:

  1. The runner user holding an active seat (autologin console session),
    so logind uaccess ACLs apply; and
  2. The uaccess udev rule above shipped in the image, tagging the Tegra
    camera nodes.

DUTs lacking either of these — e.g. the AGX Orin / Orin Nano used in
earlier validation, unless their images add the rule — will fail these
jobs rootless
. Reviewers should confirm the rule lands in the target
test images before relying on these jobs without elevation.

Reference: OEMQA-6807 (multimedia snap home + var-tmp plugs, uaccess
rootless enablement) and OEMQA-6806.

Author decision: the jetson-core-camera templates keep user: root
because rootless capture on Jetson depends on an image-level udev
uaccess rule not yet shipped (see OEMQA-6807 follow-up; PR comment
2026-07-18 has the evidence). The generic ce-oem-mipi-camera jobs
drop user: root instead.

Restores the three user: root lines in units/Jetson/camera_job.pxu
(reverting 4fa8472) and removes the two user: root lines from
units/camera/jobs.pxu.

Ref: OEMQA-6807.
@seankingyang

seankingyang commented Jul 18, 2026

Copy link
Copy Markdown
Contributor Author

Update: Jetson user: root restored; rootless scoped to the generic MIPI camera jobs

  • units/Jetson/camera_job.pxuuser: root restored on the three
    jetson-core-camera/* templates (reverting 4fa847281). As the previous
    comment's own "Dependency" section flagged, rootless Tegra capture is
    not self-contained: it needs the uaccess udev rule shipped in the
    image/gadget, which is not yet in the target Jetson images. Until that
    rule ships, these templates keep the elevation so the jobs pass on images
    without it. Net effect: this file is now unchanged vs main (the PR no
    longer touches it).

  • units/camera/jobs.pxuuser: root removed from the two generic
    ce-oem-mipi-camera/* templates, which run as the invoking user. These
    had user: root added earlier in this branch; main never had it, so the
    net PR state simply leaves them rootless.

The two generic jobs both run camera_test.py testing … against a
vendor-specific MIPI camera (V4L2 / GStreamer): capture-image-scenario
captures a {width}x{height} {format} still, and record-video-scenario
records the same at {fps} fps. No device-elevation is part of these jobs
on main, so dropping user: root returns them to their unelevated state;
any per-platform device-node access is handled at the image level, same as
the Jetson dependency above.

Reference: OEMQA-6807, OEMQA-6806.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ce-oem The PR only contains to the ce-oem provider under the contrib area

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants