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
55 push :
6+
7+ # Run tests every 6 months after prerelease comes out to check for regressions.
8+ # Prerelease for "a" release is out by Jan 1. for "b" release by Jul 1
9+ schedule :
10+ - cron : " 0 0 1 1,7 *"
611
712 # Allows you to run this workflow manually from the Actions tab
813 workflow_dispatch :
914
1015jobs :
11- my-job :
12- name : Run tests
16+ book-tests :
17+ name : Run ${{ matrix.chapter }} tests
1318 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+
1462 steps :
15- - name : Check out RVC3-MATLAB repository (for unit tests)
16- uses : actions/checkout@v3
63+ - name : Check out RVC3-MATLAB repository
64+ uses : actions/checkout@v4
1765 - run : echo "The ${{ github.repository }} repository has been cloned to the runner."
1866
1967 - name : Update apt package cache
@@ -34,20 +82,138 @@ jobs:
3482
3583 - name : Set up MATLAB
3684 # This uses the latest release of MATLAB. Can specify "release" if needed.
37- # Use v2-beta for Java Swing access
85+ # Use v2 for Java Swing access
3886 uses : matlab-actions/setup-matlab@v2
3987 with :
40- products : MATLAB Simulink Robotics_System_Toolbox Navigation_Toolbox UAV_Toolbox Automated_Driving_Toolbox Computer_Vision_Toolbox Image_Processing_Toolbox Deep_Learning_Toolbox Model_Predictive_Control_Toolbox Optimization_Toolbox ROS_Toolbox Signal_Processing_Toolbox Statistics_and_Machine_Learning_Toolbox Symbolic_Math_Toolbox Control_System_Toolbox
88+ # Define products for each chapter in strategy matrix above
89+ products : ${{ matrix.products }}
90+
91+ # Run tests with prerelease as soon as available; switch to GR once it's live
92+ release : latest-including-prerelease
4193
4294 - name : Print out ver details
4395 uses : matlab-actions/run-command@v1
4496 with :
4597 command : ver; exit;
4698
47- - name : Run MATLAB Tests
48- # Only run tests in folder test/book
49- uses : matlab-actions/run-tests@v1
99+ - name : Run MATLAB Tests for ${{ matrix.chapter }}
100+ uses : matlab-actions/run-tests@v2
50101 with :
51- 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/tools
52110 startup-options : -webfigures
53- code-coverage-cobertura : code-coverage/coverage.xml
111+ # Run tests in parallel. Requires Parallel_Computing_Toolbox in "products" list above
112+ 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