1- name : Run all book tests
1+ name : Run all book tests (matrix, per chapter)
22
33on :
44 # Automatically run this action when a new push is made to repo
1313 workflow_dispatch :
1414
1515jobs :
16- my-job :
17- name : Run tests
16+ book-tests :
17+ name : Run ${{ matrix.chapter }} tests
1818 runs-on : ubuntu-latest
19+
20+ strategy :
21+ fail-fast : false # keep running other chapters if one fails
22+ matrix :
23+ include :
24+ - chapter : Chapter02
25+ test_folder : test/book/chapter02
26+ products : >
27+ Image_Processing_Toolbox
28+ MATLAB
29+ Navigation_Toolbox
30+ Symbolic_Math_Toolbox
31+ junit : test-results/Chapter02.xml
32+ cobertura : code-coverage/Chapter02.xml
33+
34+ - chapter : Chapter03
35+ test_folder : test/book/chapter03
36+ products : >
37+ MATLAB
38+ Robotics_System_Toolbox
39+ junit : test-results/Chapter03.xml
40+ cobertura : code-coverage/Chapter03.xml
41+
42+ # Automated_Driving_Toolbox
43+ # Computer_Vision_Toolbox
44+ # Control_System_Toolbox
45+ # Deep_Learning_Toolbox
46+ # Image_Processing_Toolbox
47+ # MATLAB
48+ # Model_Predictive_Control_Toolbox
49+ # Navigation_Toolbox
50+ # Optimization_Toolbox
51+ # Parallel_Computing_Toolbox
52+ # Simulink
53+ # Robotics_System_Toolbox
54+ # ROS_Toolbox
55+ # Signal_Processing_Toolbox
56+ # Statistics_and_Machine_Learning_Toolbox
57+ # Symbolic_Math_Toolbox
58+ # UAV_Toolbox
59+ # ...one row per tChapterXX.m
60+
61+
1962 steps :
20- - name : Check out RVC3-MATLAB repository (for unit tests)
63+ - name : Check out RVC3-MATLAB repository
2164 uses : actions/checkout@v4
2265 - run : echo "The ${{ github.repository }} repository has been cloned to the runner."
2366
4285 # Use v2 for Java Swing access
4386 uses : matlab-actions/setup-matlab@v2
4487 with :
45- products : >
46- Automated_Driving_Toolbox
47- Computer_Vision_Toolbox
48- Control_System_Toolbox
49- Deep_Learning_Toolbox
50- Image_Processing_Toolbox
51- MATLAB
52- Model_Predictive_Control_Toolbox
53- Navigation_Toolbox
54- Optimization_Toolbox
55- Parallel_Computing_Toolbox
56- Simulink
57- Robotics_System_Toolbox
58- ROS_Toolbox
59- Signal_Processing_Toolbox
60- Statistics_and_Machine_Learning_Toolbox
61- Symbolic_Math_Toolbox
62- UAV_Toolbox
88+ # Define products for each chapter in strategy matrix above
89+ products : ${{ matrix.products }}
90+
6391 # Run tests with prerelease as soon as available; switch to GR once it's live
6492 release : latest-including-prerelease
6593
@@ -68,12 +96,124 @@ jobs:
6896 with :
6997 command : ver; exit;
7098
71- - name : Run MATLAB Tests
72- # Only run tests in folder test/book
99+ - name : Run MATLAB Tests for ${{ matrix.chapter }}
73100 uses : matlab-actions/run-tests@v2
74101 with :
75- select-by-folder : test/book
102+ # Only tests for this chapter
103+ select-by-folder : ${{ matrix.test_folder }}
104+
105+ # Per-chapter logs
106+ test-results-junit : ${{ matrix.junit }}
107+ code-coverage-cobertura : ${{ matrix.cobertura }}
108+
109+ source-folder : toolbox; toolbox/internal; test/book/base
76110 startup-options : -webfigures
77- code-coverage-cobertura : code-coverage/coverage.xml
78111 # Run tests in parallel. Requires Parallel_Computing_Toolbox in "products" list above
79112 use-parallel : false
113+
114+ - name : Upload logs and coverage for ${{ matrix.chapter }}
115+ if : always()
116+ uses : actions/upload-artifact@v4
117+ with :
118+ name : book-${{ matrix.chapter }}
119+ path : |
120+ ${{ matrix.junit }}
121+ ${{ matrix.cobertura }}
122+
123+ test-dashboard :
124+ name : Book test dashboard
125+ needs : book-tests
126+ runs-on : ubuntu-latest
127+ if : always() # run even if some chapter jobs failed
128+
129+ steps :
130+ - name : Download all test artifacts
131+ uses : actions/download-artifact@v4
132+ with :
133+ path : artifacts # everything ends up under ./artifacts/
134+
135+ - name : Summarize JUnit failures into run summary
136+ run : |
137+ python - << 'PY'
138+ import os
139+ import pathlib
140+ import xml.etree.ElementTree as ET
141+
142+ artifacts_root = pathlib.Path("artifacts")
143+
144+ chapters = {} # chapter -> {"passed": bool, "failures": [..]}
145+
146+ # Look for JUnit XML files inside any artifact
147+ for junit in artifacts_root.rglob("test-results/*.xml"):
148+ chapter = junit.stem # e.g. "Chapter10"
149+ tree = ET.parse(junit)
150+ root = tree.getroot()
151+
152+ failures = []
153+
154+ # Handle both <testsuite> root and <testsuites> -> <testsuite>
155+ testsuites = []
156+ if root.tag == "testsuite":
157+ testsuites = [root]
158+ else:
159+ testsuites = list(root.iter("testsuite"))
160+
161+ for ts in testsuites:
162+ for case in ts.iter("testcase"):
163+ case_name = case.get("name", "")
164+ classname = case.get("classname", "")
165+ for failure in case.findall("failure"):
166+ msg = (failure.get("message") or "").strip()
167+ text = (failure.text or "").strip()
168+ detail = msg or text or "Test failed"
169+ failures.append({
170+ "classname": classname,
171+ "name": case_name,
172+ "detail": detail,
173+ })
174+
175+ chapters[chapter] = {
176+ "passed": len(failures) == 0,
177+ "failures": failures,
178+ }
179+
180+ lines = []
181+ lines.append("# Book Test Dashboard\n")
182+
183+ if not chapters:
184+ lines.append("_No JUnit result files found in artifacts. "
185+ "Check that the matrix jobs ran and uploaded artifacts._")
186+ else:
187+ # Sort chapters like Chapter01, Chapter02, ...
188+ for chapter in sorted(chapters.keys()):
189+ info = chapters[chapter]
190+ if info["passed"]:
191+ lines.append(f"- ✅ **{chapter}** – all tests passed")
192+ else:
193+ fails = info["failures"]
194+ lines.append(f"- ❌ **{chapter}** – {len(fails)} failing test(s)")
195+ for f in fails:
196+ name = f["name"] or "<unnamed>"
197+ cls = f["classname"] or "<no class>"
198+ detail = f["detail"].replace("\n", " ")
199+ # Keep detail short-ish
200+ if len(detail) > 160:
201+ detail = detail[:157] + "..."
202+ lines.append(
203+ f" - `{cls}.{name}` – {detail}"
204+ )
205+
206+ lines.append("\n---\n")
207+ lines.append(
208+ "🔎 For full logs and coverage for a chapter, open the "
209+ "_Run ChapterXX tests_ job and download the "
210+ "artifact named `book-ChapterXX`."
211+ )
212+
213+ summary_path = os.environ.get("GITHUB_STEP_SUMMARY")
214+ if summary_path:
215+ with open(summary_path, "w", encoding="utf-8") as f:
216+ f.write("\n".join(lines))
217+ else:
218+ print("\\n".join(lines))
219+ PY
0 commit comments