Repository navigation
208 lines (178 loc) · 9.66 KB
/
Copy pathtest-symmetry.yml
File metadata and controls
208 lines (178 loc) · 9.66 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
name: Test Cross-Language Symmetry
on:
push:
branches: [main]
# No branch filter: see ci.yml.
pull_request:
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
symmetry:
name: MATLAB ↔ Python symmetry tests
runs-on: ubuntu-latest
steps:
# ── Checkout both repos ────────────────────────────────────────────
- name: Check out NDI-python
uses: actions/checkout@v4
with:
path: NDI-python
- name: Check out NDI-matlab (matching branch, fall back to main)
uses: actions/checkout@v4
with:
repository: VH-Lab/NDI-matlab
# Use the same branch name as NDI-python so paired feature
# branches stay in sync; fall back to main when the matlab
# branch does not exist.
ref: ${{ github.head_ref || github.ref_name }}
path: NDI-matlab
continue-on-error: true
id: matlab_checkout_branch
- name: Check out NDI-matlab (main fallback)
if: steps.matlab_checkout_branch.outcome == 'failure'
uses: actions/checkout@v4
with:
repository: VH-Lab/NDI-matlab
ref: main
path: NDI-matlab
# ── Runtime setup ──────────────────────────────────────────────────
- name: Start virtual display server
run: |
sudo apt-get install -y xvfb
Xvfb :99 &
echo "DISPLAY=:99" >> $GITHUB_ENV
- name: Set up MATLAB
uses: matlab-actions/setup-matlab@v2
with:
release: latest
cache: true
products: |
Communications_Toolbox
Statistics_and_Machine_Learning_Toolbox
Signal_Processing_Toolbox
- name: Install MatBox
uses: ehennestad/matbox-actions/install-matbox@v1
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.12"
- name: Install NDI-python
working-directory: NDI-python
run: |
python -m pip install --upgrade pip
python ndi_install.py --dev --no-validate --verbose
# ── Download test datasets ────────────────────────────────────────
- name: Download test datasets
run: |
curl -L -o /tmp/69a8705aa9ab25373cdc6563.tgz \
https://github.com/Waltham-Data-Science/file-passing/raw/refs/heads/main/69a8705aa9ab25373cdc6563.tgz
# ── Stage 1: MATLAB makeArtifacts ──────────────────────────────────
# Scope by directory: matbox.installRequirements points at the
# +ndi/+symmetry folder (which has its own narrow requirements.txt),
# and TestSuite.fromFolder discovers tests in that directory tree.
- name: "Stage 1: MATLAB makeArtifacts"
uses: matlab-actions/run-command@v2
with:
command: |
cd("NDI-matlab");
addpath(genpath("src"));
addpath(genpath("tests"));
addpath(genpath("tools"));
symDir = fullfile(pwd, "tests", "+ndi", "+symmetry");
matbox.installRequirements(symDir);
% Persist the fully-resolved search path (NDI dirs + the matbox
% dependencies just installed) so Stage 3 can reuse this one
% installation instead of running installRequirements a second
% time. A second install re-validates the already-installed repos
% and would fail any dependency that lacks a LICENSE/README.
savedPathFile = fullfile(getenv("GITHUB_WORKSPACE"), "ndi_symmetry_matlabpath.txt");
fid = fopen(savedPathFile, "w");
assert(fid > 0, "Could not write saved MATLAB path file.");
fprintf(fid, "%s", path());
fclose(fid);
import matlab.unittest.TestSuite
import matlab.unittest.TestRunner
suite = TestSuite.fromFolder( ...
fullfile(symDir, "+makeArtifacts"), "IncludingSubfolders", true);
fprintf("\n=== MATLAB makeArtifacts: %d test(s) ===\n", numel(suite));
assert(~isempty(suite), "No MATLAB makeArtifacts tests found.")
runner = TestRunner.withTextOutput("Verbosity", "Detailed");
results = runner.run(suite);
fprintf("\n=== MATLAB makeArtifacts: %d passed, %d failed, %d incomplete out of %d ===\n", ...
nnz([results.Passed]), nnz([results.Failed]), ...
nnz([results.Incomplete]), numel(results));
assert(all(~[results.Failed]), "MATLAB makeArtifacts failed")
% An Incomplete test verified nothing. In THIS workflow every
% artifact is produced by an earlier stage, so a filtered
% assumption means a generator broke -- it does not mean the
% artifact legitimately does not exist. So gate on it. NDI-matlab's
% own symmetry job deliberately does NOT: Python artifacts really
% are absent there, and its incomplete count is honest reporting.
% See Waltham-Data-Science/NDI-python#77.
incompleteNames = string({results([results.Incomplete]).Name});
% "%s" as the format, message as the argument: a test name
% containing a percent sign must not be read as a format spec.
assert(isempty(incompleteNames), "%s", ...
"MATLAB makeArtifacts: " + numel(incompleteNames) + ...
" test(s) verified nothing because an assumption was" + ...
" filtered, which in this workflow means a missing" + ...
" artifact:" + newline + " " + ...
strjoin(incompleteNames, newline + " "))
# ── Stage 2: Python makeArtifacts + readArtifacts ──────────────────
- name: "Stage 2: Python symmetry tests (make + read)"
working-directory: NDI-python
run: |
echo "=== Running Python makeArtifacts ==="
pytest tests/symmetry/make_artifacts/ -v --tb=short
echo ""
echo "=== Running Python readArtifacts (reads both MATLAB and Python artifacts) ==="
# Strict mode: a missing artifact is a failure here, not a skip.
# Stage 1 produced the matlabArtifacts and the step above produced
# the pythonArtifacts, so nothing is legitimately absent. Locally
# the same tests still skip, since most contributors have no MATLAB.
NDI_SYMMETRY_STRICT=1 pytest tests/symmetry/read_artifacts/ -v --tb=short
# ── Stage 3: MATLAB readArtifacts ──────────────────────────────────
# Reuse the installation from Stage 1: restore the saved search path
# instead of re-running installRequirements. The dependency files
# persist on the runner between steps; only the path is lost when the
# fresh MATLAB session starts. This avoids a redundant install and the
# re-validation that install would perform on the existing repos.
- name: "Stage 3: MATLAB readArtifacts (reads Python artifacts)"
uses: matlab-actions/run-command@v2
with:
command: |
cd("NDI-matlab");
savedPathFile = fullfile(getenv("GITHUB_WORKSPACE"), "ndi_symmetry_matlabpath.txt");
assert(isfile(savedPathFile), ...
"Saved MATLAB path from Stage 1 is missing; cannot reuse install.");
path(fileread(savedPathFile));
symDir = fullfile(pwd, "tests", "+ndi", "+symmetry");
import matlab.unittest.TestSuite
import matlab.unittest.TestRunner
suite = TestSuite.fromFolder( ...
fullfile(symDir, "+readArtifacts"), "IncludingSubfolders", true);
fprintf("\n=== MATLAB readArtifacts: %d test(s) ===\n", numel(suite));
assert(~isempty(suite), "No MATLAB readArtifacts tests found.")
runner = TestRunner.withTextOutput("Verbosity", "Detailed");
results = runner.run(suite);
fprintf("\n=== MATLAB readArtifacts: %d passed, %d failed, %d incomplete out of %d ===\n", ...
nnz([results.Passed]), nnz([results.Failed]), ...
nnz([results.Incomplete]), numel(results));
assert(all(~[results.Failed]), "MATLAB readArtifacts failed")
% An Incomplete test verified nothing. In THIS workflow every
% artifact is produced by an earlier stage, so a filtered
% assumption means a generator broke -- it does not mean the
% artifact legitimately does not exist. So gate on it. NDI-matlab's
% own symmetry job deliberately does NOT: Python artifacts really
% are absent there, and its incomplete count is honest reporting.
% See Waltham-Data-Science/NDI-python#77.
incompleteNames = string({results([results.Incomplete]).Name});
% "%s" as the format, message as the argument: a test name
% containing a percent sign must not be read as a format spec.
assert(isempty(incompleteNames), "%s", ...
"MATLAB readArtifacts: " + numel(incompleteNames) + ...
" test(s) verified nothing because an assumption was" + ...
" filtered, which in this workflow means a missing" + ...
" artifact:" + newline + " " + ...
strjoin(incompleteNames, newline + " "))