Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/labview-ci/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ installed into any LabVIEW repository — from the command line or from the
| **VIDiff** | Side-by-side visual diff reports per changed VI; PR comments | Windows / Linux |
| **VI Snapshots** | Browseable gallery of every VI's block diagram (the VI Browser) | Windows |
| **Shared image** | Builds the LabVIEW CI container image in the provider's project registry | Windows / Linux |
| **Unit Tests** | *Planned* — placeholder showing how new capabilities slot in | Windows / Linux |
| **Unit Tests** | Runs LUnit tests headlessly and publishes a JUnit-based report (`CI / Unit Tests`, `CI / Unit Tests (Linux)`) | Windows / Linux |

Everything is driven by [`catalog.json`](catalog.json) — a single capability
registry that **both** the configurator UI and the installer read. Adding a new
Expand Down
10 changes: 7 additions & 3 deletions .github/labview-ci/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -4736,7 +4736,8 @@
"status": "stable",
"recommended": false,
"supportsOs": [
"windows"
"windows",
"linux"
],
"requires": [],
"recommends": [
Expand All @@ -4752,9 +4753,12 @@
".github/workflows/unit-tests-windows-container.yml",
".github/labview/run-unit-tests.ps1"
],
"linux": []
"linux": [
".github/workflows/unit-tests-linux-container.yml",
".github/labview/run-unit-tests.sh"
]
},
"notes": "Runs the configured LabVIEW unit-test frameworks headlessly in the Windows worker, normalises their JUnit output into one report, and links each failing test to the VI under test in the VI Browser. Supports Caraya, LUnit, JKI VI Tester, and the NI Unit Test Framework. Windows-only because the frameworks ship as VIPM packages; choose which tools run and where their tests live on the Unit Testing page."
"notes": "Runs the configured LabVIEW unit-test frameworks headlessly in the worker, normalises their JUnit output into one report, and links each failing test to the VI under test in the VI Browser. Supports Caraya, LUnit, JKI VI Tester, and the NI Unit Test Framework on Windows. LUnit also runs on Linux (status CI / Unit Tests (Linux)). Choose which tools run and where their tests live on the Unit Testing page."
},
{
"id": "antidoc",
Expand Down
13 changes: 13 additions & 0 deletions .github/labview-ci/config.example.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,19 @@ activities:
- masscompile
- vi-analyzer
- vidiff
# - unit-tests

# Unit tests (read when the unit-tests activity is installed). The Unit Testing
# page writes this block. LUnit runs on Windows and Linux.
# unitTests:
# tools:
# lunit:
# enabled: true
# locations:
# - "tests/" # a directory, a glob, or a file extension; empty = whole repo
# # Optional override, same tokens on both platforms ({cli} {lv} {dir} {out} {ver}).
# # Keep -Headless last: on Linux, LabVIEWCLI ignores any argument after it.
# # command: '"{cli}" -LogToConsole TRUE -OperationName LUnit -Path "{dir}" -ReportPath "{out}" -LabVIEWPath "{lv}" -Headless'

# ----------------------------------------------------------------------------
# GitHub Actions variables the GitHub workflows honor at runtime (set with
Expand Down
27 changes: 21 additions & 6 deletions .github/labview-ci/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -794,16 +794,17 @@ def write(rel: str, content: str) -> None:
" runs-on: ubuntu-latest\n"
" steps:\n"
" - uses: actions/checkout@v5\n"
" - name: Read opted-in tooling ref from config\n"
" - name: Read opted-in tooling source from config\n"
" id: cfg\n"
" shell: bash\n"
" run: |\n"
" REF=$(awk '/^[[:space:]]*ref:[[:space:]]/{print $2; exit}' .github/labview-ci.yml 2>/dev/null)\n"
+ _DASHBOARD_SOURCE_READER +
" echo \"repo=${REPO:-" + src_repo + "}\" >> \"$GITHUB_OUTPUT\"\n"
" echo \"ref=${REF:-" + alias + "}\" >> \"$GITHUB_OUTPUT\"\n"
" - name: Check out tooling (opted-in version)\n"
" uses: actions/checkout@v5\n"
" with:\n"
f" repository: {src_repo}\n"
" repository: ${{ steps.cfg.outputs.repo }}\n"
" ref: ${{ steps.cfg.outputs.ref }}\n"
" path: _lvci\n"
" - uses: ./_lvci/actions/dashboard\n"
Expand Down Expand Up @@ -896,6 +897,19 @@ def write(rel: str, content: str) -> None:
return 0


# Shell lines (inside a `run: |` block) that read the tooling source the dashboard
# caller checks out: source.repo and source.ref from .github/labview-ci.yml. Both
# are read, so repointing `source` (e.g. to a fork and one of its branches) is
# honoured; reading only the ref made a fork-only branch get fetched from the
# canonical repo. `^ repo:` matches source.repo only, never source.distribution.repo.
# The caller appends the `echo` lines with its own install-time fallbacks.
_DASHBOARD_SOURCE_READER = (
" CFG=.github/labview-ci.yml\n"
" REPO=$(awk '/^source:[[:space:]]*$/{s=1;next} s&&/^[^[:space:]]/{exit} s&&/^ repo:[[:space:]]/{print $2;exit}' \"$CFG\" 2>/dev/null)\n"
" REF=$(awk '/^[[:space:]]*ref:[[:space:]]/{print $2; exit}' \"$CFG\" 2>/dev/null)\n"
)


def consumer_dashboard_workflow(catalog: dict, branch: str = "main") -> str:
"""Thin dashboard workflow for a vendored consumer.

Expand Down Expand Up @@ -964,16 +978,17 @@ def consumer_dashboard_workflow(catalog: dict, branch: str = "main") -> str:
" steps:\n"
" - name: Checkout repository\n"
" uses: actions/checkout@v5\n"
" - name: Read opted-in tooling ref from config\n"
" - name: Read opted-in tooling source from config\n"
" id: cfg\n"
" shell: bash\n"
" run: |\n"
" REF=$(awk '/^[[:space:]]*ref:[[:space:]]/{print $2; exit}' .github/labview-ci.yml 2>/dev/null)\n"
+ _DASHBOARD_SOURCE_READER +
' echo "repo=${REPO:-' + src_repo + '}" >> "$GITHUB_OUTPUT"\n'
' echo "ref=${REF:-' + ref + '}" >> "$GITHUB_OUTPUT"\n'
" - name: Check out tooling (opted-in version)\n"
" uses: actions/checkout@v5\n"
" with:\n"
" repository: " + src_repo + "\n"
" repository: ${{ steps.cfg.outputs.repo }}\n"
" ref: ${{ steps.cfg.outputs.ref }}\n"
" path: _lvci\n"
" - name: Build CI dashboard\n"
Expand Down
9 changes: 9 additions & 0 deletions .github/labview-ci/notes/linux-lunit-unit-tests.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Unit Tests now run LUnit in the Linux worker as well. Installing Unit Tests with
Linux adds unit-tests-linux-container.yml and run-unit-tests.sh, which publish a
Linux report under unit-tests/<sha>/linux/ with the commit status
CI / Unit Tests (Linux); the dashboard shows the Windows / Linux split and the
report's platform toggle switches between them. Keep -Headless as the last
argument of any unitTests command: override, since Linux LabVIEWCLI ignores
anything after it.
Linux worker builds also no longer fail on VIPM packages whose version carries
a revision suffix (such as jki_rsc_toolkits_palette 1.1-1).
4 changes: 3 additions & 1 deletion .github/labview-ci/providers/gitlab/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
"templates/snapshots-2-windows.yml",
"templates/snapshots-windows.yml",
"templates/unit-tests-windows.yml",
"templates/unit-tests-linux.yml",
"templates/vi-analyzer-linux.yml",
"templates/vi-analyzer-windows.yml",
"templates/antidoc-windows.yml",
Expand Down Expand Up @@ -44,7 +45,8 @@
"linux": "templates/vi-analyzer-linux.yml"
},
"unit-tests": {
"windows": "templates/unit-tests-windows.yml"
"windows": "templates/unit-tests-windows.yml",
"linux": "templates/unit-tests-linux.yml"
},
"antidoc": {
"windows": "templates/antidoc-windows.yml"
Expand Down
76 changes: 76 additions & 0 deletions .github/labview-ci/providers/gitlab/templates/unit-tests-linux.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
lvci:unit-tests-linux:
extends: .lvci:linux-docker
stage: verify
needs:
- job: lvci:worker-linux
optional: true
artifacts: false
rules:
- if: '$CI_PIPELINE_SOURCE == "web"'
- if: '$CI_PIPELINE_SOURCE == "schedule"'
when: manual
allow_failure: true
- changes:
- "**/*.vi"
- "**/*.ctl"
- "**/*.lvproj"
- "**/*.lvlib"
- "**/*.lvclass"
- "**/*.lvtest"
script:
- |
set +e
report_dir="ci-out/unit-tests"
results_dir="$report_dir/results"
mkdir -p "$results_dir"
if [ -n "${LVCI_LINUX_IMAGE:-}" ]; then
image="$LVCI_LINUX_IMAGE"
elif [ "${LVCI_USE_CUSTOM_IMAGE:-false}" = "true" ]; then
image="${CI_REGISTRY_IMAGE}/labview:linux-latest"
else
image="nationalinstruments/labview:latest-linux"
fi
if [[ "$image" == "${CI_REGISTRY_IMAGE}"/* ]]; then
echo "$CI_REGISTRY_PASSWORD" | docker login "$CI_REGISTRY" --username "$CI_REGISTRY_USER" --password-stdin
fi
docker pull "$image"
pull_exit=$?
run_exit=1
if [ "$pull_exit" -eq 0 ]; then
# One fresh container per run; run-unit-tests.sh always exits 0 and
# pass/fail comes from the JUnit files.
docker run --rm \
-e "GITHUB_REPOSITORY=$CI_PROJECT_PATH" \
-e "GITHUB_SHA=$CI_COMMIT_SHA" \
-v "$CI_PROJECT_DIR:/workspace" \
"$image" \
bash /workspace/.github/labview/run-unit-tests.sh /workspace "/workspace/$results_dir"
run_exit=$?
else
printf 'Could not pull LabVIEW image %s\n' "$image" > "$results_dir/runner-error.txt"
fi
python3 .github/labview/build-unittest-report.py \
--results "$results_dir" \
--out "$report_dir" \
--workspace "$CI_PROJECT_DIR" \
--sha "$CI_COMMIT_SHA" \
--platform linux \
--repo "$CI_PROJECT_PATH" \
--pages-url "$CI_PAGES_URL" \
--labview-version "${LVCI_LABVIEW_VERSION:-}" \
--commit-msg "$(git log -1 --pretty=%s "$CI_COMMIT_SHA")" \
--author "$(git log -1 --pretty=%an "$CI_COMMIT_SHA")" \
--date "$(git log -1 --pretty=%cI "$CI_COMMIT_SHA")" || true
python3 .gitlab/labview-ci/scripts/stage-report.py \
--source "$report_dir" \
--destination "unit-tests/$CI_COMMIT_SHA/linux" \
--fallback "Unit tests (Linux) did not produce a report. See the GitLab job log."
exit "$run_exit"
artifacts:
when: always
expire_in: 30 days
reports:
junit: ci-out/unit-tests/results/*.xml
paths:
- ci-out/unit-tests/
- ci-out/gitlab-pages/unit-tests/$CI_COMMIT_SHA/linux/
34 changes: 30 additions & 4 deletions .github/labview/build-unittest-report.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,9 +306,10 @@ def build_data(args) -> dict:
platforms = [{"id": "windows", "url": "../results.json"}, {"id": "linux", "url": None}]
snap_depth = "../../../"

# Optional marker written by run-unit-tests.ps1 when a configured tool could
# not run because the selected container lacks the required tooling. Drives
# the shared "missing container tooling" banner (see tooling_banner_html).
# Optional marker written by run-unit-tests.ps1/.sh when a configured tool could
# not run because the selected container lacks the required tooling (drives the
# shared "missing container tooling" banner, see tooling_banner_html) or has no
# runner on this platform yet (see unsupported_note_html).
tooling = {}
res_dir = Path(args.results) if args.results else None
if res_dir and (res_dir / "_tooling.json").exists():
Expand Down Expand Up @@ -391,6 +392,28 @@ def tooling_banner_html(missing: list, configure_url: str) -> str:
)


def unsupported_note_html(missing: list) -> str:
"""Render an informational note for configured tools that have no runner on
this platform yet (kind `unsupported-on-linux`, written by run-unit-tests.sh),
so the report explains why those tools have no results here."""
if isinstance(missing, dict):
missing = [missing]
unsup = [x for x in (missing or []) if isinstance(x, dict) and x.get("kind") == "unsupported-on-linux"]
if not unsup:
return ""
names = ", ".join(_esc(x.get("name") or x.get("tool") or "") for x in unsup if (x.get("name") or x.get("tool")))
return (
'<div class="lvci-needtool info" role="note">'
'<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" '
'stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">'
'<circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/>'
'<line x1="12" y1="8" x2="12.01" y2="8"/></svg>'
'<div class="lvci-needtool-t"><strong>Not run on Linux.</strong>'
f'{names} {"is" if len(unsup) == 1 else "are"} enabled but not supported in the Linux container yet. '
f'{"Its" if len(unsup) == 1 else "Their"} results come from the Windows unit-test run.</div></div>'
)


def render(data: dict) -> str:
blob = json.dumps(data, ensure_ascii=False)
blob = blob.replace("<", "\\u003c").replace(">", "\\u003e").replace("&", "\\u0026")
Expand All @@ -409,7 +432,8 @@ def render(data: dict) -> str:
dash = m.get("dash_url") or ""
repo = m.get("repo") or ""
cfg_url = (dash or "") + "configure.html" + ("?repo=" + quote(repo, safe="") if repo else "")
banner = tooling_banner_html((data.get("tooling") or {}).get("missing") or [], cfg_url)
missing = (data.get("tooling") or {}).get("missing") or []
banner = tooling_banner_html(missing, cfg_url) + unsupported_note_html(missing)

out = _TEMPLATE.replace("__UT_DATA_JSON__", blob)
out = out.replace("__UT_HEADER_CFG__", json.dumps(hdr_cfg, ensure_ascii=False))
Expand Down Expand Up @@ -516,6 +540,8 @@ def main() -> None:
.lvci-needtool-cta{flex:0 0 auto;align-self:center;font-size:.85em;font-weight:600;color:#fff;background:#bb8009;border-radius:7px;padding:8px 13px;text-decoration:none;white-space:nowrap}
.lvci-needtool-cta:hover{background:#a17609}
@media(max-width:560px){.lvci-needtool{flex-wrap:wrap}.lvci-needtool-cta{align-self:stretch;text-align:center}}
.lvci-needtool.info{background:rgba(56,139,253,.1);border-color:rgba(56,139,253,.4);border-left-color:#388bfd}
.lvci-needtool.info svg{color:#388bfd}
/* snapshot drawer */
#backdrop{position:fixed;inset:0;background:rgba(1,4,9,.5);opacity:0;pointer-events:none;transition:opacity .15s;z-index:40}
#backdrop.show{opacity:1;pointer-events:auto}
Expand Down
5 changes: 5 additions & 0 deletions .github/labview/run-unit-tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
template that can be overridden from the config (`command:` key) so the precise
invocation can be corrected on a real worker without editing this script.

LINUX: run-unit-tests.sh is the Linux counterpart (LUnit only for now). The two
scripts share no code but share a contract: the config shape, the command
tokens, the <tool>-<n>.xml output names and the _tooling.json findings. A
framework added here should be added there the same way (see its header).

HEADLESS: LabVIEW must run -Headless in LabVIEW 2026+ Windows containers (same
constraint run-vi-analyzer.ps1 documents) or VI Server fails with -350000. g-cli
launches LabVIEW the same way, so we pass the LabVIEW path/version through.
Expand Down
Loading
Loading