<feed xmlns='http://www.w3.org/2005/Atom'>
<title>tinyusb.git/test, branch etmtrace-rp2350</title>
<subtitle>Unnamed repository; edit this file 'description' to name the repository.</subtitle>
<id>http://cgit.235523.xyz/tinyusb.git/atom/test?h=etmtrace-rp2350</id>
<link rel='self' href='http://cgit.235523.xyz/tinyusb.git/atom/test?h=etmtrace-rp2350'/>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/'/>
<updated>2026-08-26T06:41:05Z</updated>
<entry>
<title>test/hil: make main() readable and stop the suite sleeping (#3848)</title>
<updated>2026-08-26T06:41:05Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-26T06:41:05Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=b15c720ab0a36ee16c1cf7984c7c0f4ccf3a7015'/>
<id>urn:sha1:b15c720ab0a36ee16c1cf7984c7c0f4ccf3a7015</id>
<content type='text'>
Three readability changes with no behaviour change on the healthy path —
every pre-existing test passes untouched.

main() was 368 lines with try/finally three deep, its two abort paths
near-identical 40-line blocks; _abort_report holds that shape once, and the
controller-hint cache and pool construction move to their own helpers.
368 -&gt; 279, test_board 180 -&gt; 151, test_device_usbtest 164 -&gt; 125.

test_hil_bounded.py cost 78s on every commit under test/hil/, mostly one 3s
post-flash settle paid by ten tests against a fake rig. Now 37s.

Fixes two pre-existing defects the extraction exposed: _write_failed_spec was
unguarded inside the abort path, so an OSError there replaced the caller's
RuntimeError and no report was written at all; _save_controller_hints overlaid
a startup snapshot onto the re-read cache, clobbering a concurrent job's newer
values. Also five comments that stated the opposite of the code, and both table
renderers measuring width with len() against two-column status marks.</content>
</entry>
<entry>
<title>hil: make hil_report.md a rendering of hil_report.json (#3840)</title>
<updated>2026-08-25T08:04:42Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-25T08:04:42Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=375e20090a0a60fd2d47ba6553d189e05482dfea'/>
<id>urn:sha1:375e20090a0a60fd2d47ba6553d189e05482dfea</id>
<content type='text'>
hil_report.json and hil_report.md were written independently. Four writers
produced the markdown and three wrote no JSON at all -- and those three are the
paths where a run died, so a JSON consumer saw nothing exactly when it mattered:
the per-board verdicts an agent hands back reported the whole fleet as "no
report row" while a human read the real story from the markdown.

Every writer now goes through render_report(), so a table can never contain
something the JSON does not. The document gains `scope` (a three-board PR run
and a full run that lost 24 boards were indistinguishable) and `caveat` (how the
run ended). `banner` carries rig health across an --accumulate retry; `caveat`
records how a run ended and must not -- conflating them made a clean retry
publish an abandonment that never happened.

helper/hil_report.py owns the document end to end, dissolving the import cycle
that forced write_timeout_report to compose its own markdown and removing a
duplicate cell classifier kept in sync by hand. hil_summary.py is deleted; its
CLI moves there. hil_ci.sh uploads the sidecar so a remote --accumulate has a
merge bas</content>
</entry>
<entry>
<title>ci_select: fix the membrowse test's env dependence, and stop HIL unit tests taking the rig (#3846)</title>
<updated>2026-08-25T03:35:45Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-25T03:35:45Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=b610ff039bafa1040c19d6a11cb04adcb22936e5'/>
<id>urn:sha1:b610ff039bafa1040c19d6a11cb04adcb22936e5</id>
<content type='text'>
test_the_upload_board_can_diverge_from_the_built_board called
get_family_boards without ci=True, so it pinned the developer's set, not the
runner's: the CI skip lists move the one-first pick on three families. It
held locally and went red on its first CI run. Pass ci=True, as
_prune_buildable already does, and pin the runner's twelve.

Rule 2 is a bare test/hil/ prefix, so the harness's own unit tests booked the
full 27-board rig for diffs that cannot reach it. Carve test/hil/test/** out
to rule 1b, beside test/{fuzz,unit-test}/**; the harness itself is untouched.
A test pins that directory's file list, so anything added there that the rig
does read fails rather than silently skipping hardware. Rule table updated in
the spec and its carbon in the docstring.</content>
</entry>
<entry>
<title>ci: an empty selection must build nothing, plus selector follow-ups (#3845)</title>
<updated>2026-08-25T02:46:42Z</updated>
<author>
<name>Ha Thach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-25T02:46:42Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=da255b1d2db10b8f31332a779b2a526f579acee1'/>
<id>urn:sha1:da255b1d2db10b8f31332a779b2a526f579acee1</id>
<content type='text'>
ci: an empty selection must build nothing, plus selector follow-ups

A PR whose build axis legitimately selected nothing rebuilt everything.
build.yml reads .build.families twice - as a |-joined regex, and implicitly
as "is anything selected" - but tested only -z "$FAMILY_REGEX", which an
empty list and a charset-rejected one both satisfy while meaning opposite
things. ci_set_matrix had already returned the correct all-empty matrix;
the fall-open branch discarded it. #3842 and #3840 each spent 74 cmake legs
on it. Branch on the two cases instead, rename FAM_* to FAMILY_*, and cover
the block with a test that extracts it from build.yml and executes it - it
had no test at all, which is how this shipped through two merges.

Follow-ups to the same machinery: glob.escape the repo root at five sites,
so a checkout path containing [ or * stops failing closed; drop the ci-full
label, read after the matrix was already computed and so never functional;
delete 13 mcu:MKL25ZXX / mcu:SAME5X skip tokens matching no board; carry the
rule table in the module docstring, guarded against drift; and pin six
selection behaviours a mutation pass proved untested.

Cut the selector's cost 1.8x (26.0s -&gt; 14.6s) with 0 divergences over 260
paths, and stop scoping the membrowse upload by the PR example filter.</content>
</entry>
<entry>
<title>ci_select: address Copilot review - anchor _META_RE, cover rule 12b</title>
<updated>2026-08-22T16:10:14Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-22T16:10:14Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=050595d64f9f130783853a2342eb1114d32199e8'/>
<id>urn:sha1:050595d64f9f130783853a2342eb1114d32199e8</id>
<content type='text'>
Anchor the .github file alternatives. FUNDING.yml, labeler.yml and
membrowse_pr_message.j2 sat inside a group whose only `$` belonged to the
workflows/ branch, so they matched as prefixes: .github/labeler.yml.bak and
.github/FUNDING.yml.old were classified as metadata and would have selected
nothing. No such file exists today - the workflows/ alternative was already
anchored and ISSUE_TEMPLATE/ is a directory prefix on purpose.

Rule 12b had no test of its own: TestNoTrackedFileIsUnclassified only proved
src/typec no longer reaches rule 17, not that the answer is right. TestTypecRule
pins it - non-full, every selected example under typec/, all four src/typec files
answering alike, no rig board, and the set derived from CFG_TUC_ENABLED rather
than hardcoded, so it follows a new typec example on its own. Verified all four
fail with rule 12b removed.
</content>
</entry>
<entry>
<title>build_utils: key the caches on the tree, not just the arguments</title>
<updated>2026-08-21T16:14:25Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-21T16:14:25Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=6ff0ef97702c0e6b6d17b7a8fe856b31164efe57'/>
<id>urn:sha1:6ff0ef97702c0e6b6d17b7a8fe856b31164efe57</id>
<content type='text'>
The eight lru_cache layers take repo-RELATIVE paths - 'hw/bsp/&lt;fam&gt;',
'examples/&lt;ex&gt;/skip.txt', the literal 'hw/bsp' glob - while ci_select._in_repo()
chdirs around every call so one process can classify more than one tree. With no
cwd in the key the second tree gets the first tree's answers.

Reproduced: skip_example('host/bare_api','metro_m0_express') is False at the repo
root and STILL False after chdir into a tree where that board does not exist; only
cache_clear() gave the right answer. It bites the code-size skill's base-vs-branch
worktree compare, /pre-pr, and the first test that points classify_build at a
fixture tree. Master had no caching here, so the hazard arrived with it.

_cwd_cache puts os.getcwd() in the key. The 199-test suite passed before only
because every test happens to pass the real REPO; the new
TestCachesAreKeyedOnTheTree crosses trees deliberately.

Also adds the drift guard the class rule was missing. Ports, hw/mcu, get_deps
tokens and bsp families each have one; the class rule had only a comment claiming
vendor_host.c was the sole "enabled by no example config" case until its removal -
which src/class/bth falsifies today. TestClassesWithNoEnablingExample pins the set
to {bth}, so a class added before its first example, or an example config flipped
to 0, fails here instead of silently selecting nothing on both axes. Verified it
fires by adding a class dir nothing enables.
</content>
</entry>
<entry>
<title>ci_select: classify the 254 files that were reaching rule 17</title>
<updated>2026-08-21T15:17:51Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-21T15:17:51Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=03a329eeda09e073d7de9be84df55d65392e4013'/>
<id>urn:sha1:03a329eeda09e073d7de9be84df55d65392e4013</id>
<content type='text'>
Rule 17 (unclassified -&gt; full on both axes) is the fail-open net for paths nobody
anticipated, and it must stay that way: a wrong `full` costs runner minutes and is
visible in the run, a wrong `empty` costs a merged regression and is invisible.
But nothing in the tree should REACH it, and 254 tracked files did.

The cost was real. PR #3842 changed a skill, a README and .gitignore; .gitignore
matched no rule, so both axes went full and 74 cmake legs span up runners to do
checkout + toolchain + get_deps before skipping the build, plus the whole 30-board
rig. Three changes, none of which touch rule 17 itself:

1. _META_RE - repo metadata and tooling no Build step reads: .gitignore,
   .gitattributes, .clang-format, .codespellrc, .pre-commit-config.yaml,
   .readthedocs.yaml, .PVS-Studio/, .idea/, sonar-project.properties, the
   packaging manifests, CMakePresets, udev rules, test/{fuzz,unit-test} (their own
   jobs build those), the non-build .github/ files, and the tools/*.py scripts no
   build invokes. Deliberately NOT included, and still full: .circleci/**,
   .github/workflows/build*.yml, .github/actions/**, .github/scripts/**. The line
   is "does a Build step read this", not "is it source".

2. Rules 15 and 16 now match what they already claimed. Row 15 names
   examples/&lt;role&gt;/CMakeLists.txt and the regex never had it; row 16 says
   tools/build*.py but anchored tools/build\.py$. Both got the right answer only
   because rule 17 caught them on the way past. Also names their siblings -
   family_support.mk, family_rules.mk, src/CMakeLists.txt, src/tinyusb.mk - and
   .circleci/**, which generates the whole CircleCI matrix and was in no row at all.

3. src/typec/** gets row 12b. It is listed unconditionally by both build systems
   but its body is `#if CFG_TUC_ENABLED`, which only examples/typec/power_delivery
   sets - the same shape as the class rule, so the same answer: the examples that
   enable it (stm32g4 and stm32u5 after the buildability prune), and nothing on the
   rig, which runs no typec test. It was force-fulling 82 families and all 30 boards.

TestNoTrackedFileIsUnclassified walks every tracked file and asserts none reaches
rule 17, on both axes - 254 -&gt; 0. Verified it fails when a new unclassified path
appears. That turns 17 into what it should be: unreachable for anything in the
tree, so it fires only for genuinely new shapes, and the author is told to write
the row rather than letting the fall-through pick an answer for them.

test_full_paths used sonar-project.properties as its stand-in for "unclassified";
that is now metadata, so the case moved to the new
test_repo_metadata_is_not_a_build_input, with test_the_build_machinery_is_still_full
pinning the other side of the line.
</content>
</entry>
<entry>
<title>ci_set_matrix: fall open when no selected family builds anywhere</title>
<updated>2026-08-21T09:08:25Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-21T09:08:25Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=f17be6770b601a32bdfcc1459e8851becf7fea84'/>
<id>urn:sha1:f17be6770b601a32bdfcc1459e8851becf7fea84</id>
<content type='text'>
family_list maps a family to the toolchains that build it, and seven hw/bsp
families are in neither: cxd56, efm32, espressif, f1c100s, pic32mz, py32f0,
same7x. Scoping to one of them intersected to nothing, so every toolchain key was
[], every cmake leg skipped on `if: inputs.build-args != '[]'`, code-metrics took
its no-metrics branch, and the PR went green from a build job that ran no
compiler. The only signal was a stderr line nothing greps for.

Not a coverage regression - master gave the same diff no compile coverage either,
since none of the other families compiles same7x's board.h. What is new is that
the gap used to be masked by the full matrix and is now the whole answer, and
that green now means "ran no compiler" rather than "compiled 64 families".

A selection whose families ALL miss is now unusable rather than empty: it prints
UNSCOPED, which build.yml and .circleci/config.yml already grep to drop the build
extras with it, and emits the full matrix. The two neighbouring cases keep their
own answers - an explicit families: [] is still a legitimate nothing-selected, and
a partial miss still scopes to the families that do build, noting the rest.

The contract test pinned an exact count of fall-open markers, which this would
have broken; it now pins the invariant (every message that emits the full matrix
carries the marker) and was checked to still fail when a marker is removed.

Also corrects the drift guard's note about espressif: hil-build-esp builds its
boards by name, but that job is gated on repository_owner, so on a fork an
espressif-only PR builds nowhere.
</content>
</entry>
<entry>
<title>hil: express a board's always-on defines as a variant, dropping build.args</title>
<updated>2026-08-21T07:23:40Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-21T07:23:40Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=a408a8e9af4a043202f79a2b8e20d229093148e5'/>
<id>urn:sha1:a408a8e9af4a043202f79a2b8e20d229093148e5</id>
<content type='text'>
The roster had two ways to pass a cmake -D to a board's build: `build.args`,
applied to every variant, and `variant[].defines`, applied to one. They did the
same thing, and only metro_m4_express used the first - for MAX3421_HOST=1, which
is what makes it the one rig board that compiles hcd_max3421.c.

A board whose define is always on now carries a single variant named after itself,
which is exactly the shape `board.get('variant') or [{'name': name, 'flags': ''}]`
already synthesises everywhere - so the build dir, the HIL report row and the
variant-boundary handling are unchanged. raspberry_pi_pico has used that shape
for its flags all along.

Removes the BuildCfg type and the parallel code path from all four consumers:
hil_test.build_board, hil_pool_check's two builders, hil_ci_set_matrix and
ci_select.board_options.

Verified: the hil-build matrix entry is byte-identical
(`-b metro_m4_express -DMAX3421_HOST=1`), hil_test's build command is unchanged,
ci_select still selects the board for a max3421 diff with MAX3421_HOST in its
options, and a real build of dual/host_info_to_device_cdc and host/cdc_msc_hid on
that board still compiles hcd_max3421.c.
</content>
</entry>
<entry>
<title>ci: fix nine ways the selection under-selected or mismatched</title>
<updated>2026-08-21T05:41:47Z</updated>
<author>
<name>hathach</name>
<email>thach@tinyusb.org</email>
</author>
<published>2026-08-21T05:41:47Z</published>
<link rel='alternate' type='text/html' href='http://cgit.235523.xyz/tinyusb.git/commit/?id=e13eff8d4e757ebe7709a58fce44017b8be5a84d'/>
<id>urn:sha1:e13eff8d4e757ebe7709a58fce44017b8be5a84d</id>
<content type='text'>
Every one of these dropped coverage silently - the worst failure mode here,
because the PR still goes green. Found by review, each reproduced first.

Selection rules:

* class_macros derived the config macro from the class DIRECTORY, so a change to
  src/class/midi/midi2_device.c selected the midi_test examples (which do not
  compile it) and never examples/device/midi2_device (the only one that enables
  CFG_TUD_MIDI2, and the only one that does). The file's own macro is unioned in
  where it differs - union, never replace: over-selecting costs a build,
  under-selecting merges a break.
* the ${FAMILY_MCUS} fallback added for espressif fired on any family whose
  _family_mcus came back empty, and _cmake_sets is if()-blind and keeps the FIRST
  definition - so mcx/frdm_mcxn947 answered MCXA15, a token six examples' skip.txt
  names, dropping 12 firmware images CMake builds. Limited now to families that
  never spell set(FAMILY_MCUS ...) at all.
* lib_examples read only an example's top-level CMakeLists.txt/Makefile;
  host/msc_file_explorer_freertos names lib/embedded-cli in src/CMakeLists.txt and
  survived by luck. The whole example tree is scanned. (SEGGER_RTT and rt-thread
  still resolve to nothing: all three references sit inside a LOGGER=rtt guard no
  CI build sets - the documented ruling, not a miss.)
* get_family_boards applied ci_skip_boards/ci_preferred_boards only under
  GITHUB_ACTIONS/CIRCLECI, so the selector answered differently on a laptop than
  on a runner; _prune_buildable forces CI semantics. Its one-board pick also
  abandoned the whole preferred list when entry one could not build the -e set,
  and asked skip_example without the build's -D tokens.
* _config_enables and lib_examples still read with the locale encoding - under
  LC_ALL=C the selector tracebacked on three tracked tusb_config.h files. The
  whole selector and its suite run clean there now.

Workflows:

* the Membrowse Upload step omitted $EX_ARGS, but --one-first now picks the board
  from the -e set, so it configured a different, empty build dir and uploaded
  --identical for a board never compiled. It takes $EX_ARGS for the BOARD; the
  target stays the aggregate, which has no DEPENDS and still records every example.
* blanking FAM_REGEX reset only build_filtered, leaving the build scoped while
  code-metrics took the UNSCOPED branch and diffed a 1-family run against the full
  averaged baseline. All three drop together now, as CircleCI's fall-open does.
* CircleCI's EX_ARGS had no character screen and is used unquoted, and its
  code-metrics job still exit 1'd on an empty metrics set - which a scoped build
  makes a legitimate outcome.
* a `ci-full` PR label now turns the scoping off for one PR. A selector bug
  under-selects silently, and without a label the only ways back to a full matrix
  are accidental.

Performance, since the selector gates every other job: family.cmake texts are read
once rather than per changed directory (a 6,000-file dep bump re-read 84 files
99,892 times) and _scrape_mcu is cached: 2.2s -&gt; 0.29s there, 0.8s -&gt; 0.33s on a
class diff.

Tests: a drift guard for hw/bsp families absent from ci_set_matrix.family_list
(they select zero legs now, where they used to ride the full matrix); the rule-4
port test asserted a SUBSET, which set() satisfies, so it could not fail on the
empty selection it exists to catch; the GITHUB_ENV guard test counted a SUM of two
guards. Drops metrics.py's --only-examples, which nothing called, and applies the
TOTAL scrub to the by-example branch that skipped it.
</content>
</entry>
</feed>
