Skip to content

test(library-select): teensy41 LDF leak diagnostic (#267 follow-up)#269

Merged
zackees merged 1 commit into
mainfrom
investigate/267-ldf-leak
May 24, 2026
Merged

test(library-select): teensy41 LDF leak diagnostic (#267 follow-up)#269
zackees merged 1 commit into
mainfrom
investigate/267-ldf-leak

Conversation

@zackees
Copy link
Copy Markdown
Member

@zackees zackees commented May 24, 2026

Follow-up to #267 (Fix 1 investigation).

Summary

Adds an #[ignore]-gated diagnostic test that runs the real LDF resolver against the cached framework-arduinoteensy library set with two scenarios:

  • Scenario A: FastLED's full local source tree as the project root. Selects 10 framework libs; ssd1351 NOT among them.
  • Scenario B: sketch-only project (just #include <FastLED.h>). Selects 4 framework libs; ssd1351 NOT among them.

The diagnostic was used to confirm that Bug 1 from #267 (LDF selecting ssd1351 when Blink doesn't reference it) does not reproduce on current code (v2.2.6). Full investigation summary on the issue: #267 (comment)

Why ship the diagnostic as a permanent test

Future LDF-leak investigations can quickly answer "does the resolver select X for board Y?" without rebuilding the harness from scratch. The #[ignore] gate keeps it out of normal CI; it skips silently if the cached Teensyduino framework isn't on the host.

Test plan

  • soldr cargo check -p fbuild-library-select --tests — clean
  • soldr cargo fmt --check — clean
  • Run with --ignored --nocapture — both scenarios pass; ssd1351 NOT selected in either
  • CI passes (the test is #[ignore]d so doesn't run by default)

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Tests
    • Added diagnostic test for LDF resolver validation against framework libraries.

Note: This release contains no user-facing changes or feature updates.

Review Change Stack

 Fix 1)

Per the Fix 1 investigation on #267, this `#[ignore]`-gated test runs
the real LDF resolver against the cached `framework-arduinoteensy`
library set with two scenarios:

- Scenario A: FastLED's full local source tree (`~/dev/fastled/src`)
  as the project root. Selects 10 framework libs; ssd1351 is NOT one
  of them.
- Scenario B: sketch-only project with a Blink.ino that does just
  `#include <FastLED.h>`. Selects 4 framework libs; ssd1351 is still
  NOT one of them.

The test prints what's selected and stats, then asserts via output
whether ssd1351 appears. It's diagnostic-only — `#[ignore]`d so it
only runs on demand:

    soldr cargo test -p fbuild-library-select \
        --test teensy41_ldf_diag -- --ignored --nocapture

Requires cached `~/.fbuild/prod/cache/platforms/dl-framework-arduinoteensy-*/`
and local `~/dev/fastled/src/`. Skips with a printed reason if missing
so it stays harmless in CI.

Lets future investigations quickly answer "does the LDF select X?"
without rebuilding the issue from scratch.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 24, 2026

📝 Walkthrough

Walkthrough

Adds a new ignored diagnostic test file that exercises the LDF resolver against real Teensyduino framework libraries and a user's local FastLED source tree. Includes helper functions for path discovery and file collection, plus two test scenarios that run the resolver and log selected libraries and source files.

Changes

Diagnostic Test Infrastructure for LDF Resolver

Layer / File(s) Summary
Path resolution and file collection utilities
crates/fbuild-library-select/tests/teensy41_ldf_diag.rs
Helper functions locate the user home directory, discover cached Teensyduino framework libraries by scanning the fbuild cache structure, locate the local FastLED source tree, and collect C/C++/assembly/Arduino source and header files by extension filtering.
Diagnostic test scenarios A and B
crates/fbuild-library-select/tests/teensy41_ldf_diag.rs
An ignored diagnostic test that loads framework libraries and FastLED seeds, runs resolve_with_stats, and logs resolver stats, required libraries, and selected source files. Scenario A uses both framework and local FastLED source; Scenario B creates a temporary sketch-only project with #include <FastLED.h> and demonstrates resolver behavior when local FastLED is not visible.

Estimated Code Review Effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Possibly Related Issues

Poem

🐰 A test takes shape, with paths unfurled,
Through cache and source trees, the resolver's world,
Two scenarios dance—one with libraries near,
One with just sketches—let the diagnostics cheer! 📋

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the main change: a new diagnostic test for teensy41 LDF behavior. It references the follow-up to issue #267, making the purpose and context explicit and specific.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch investigate/267-ldf-leak

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
crates/fbuild-library-select/tests/teensy41_ldf_diag.rs (1)

31-54: ⚡ Quick win

Avoid hardcoding ~/.fbuild/prod/cache/platforms in test discovery.

Using a fixed prod path can cause false skips for dev mode users or when FBUILD_CACHE_DIR is overridden. Prefer deriving the cache root from the canonical path helper and appending platforms.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/fbuild-library-select/tests/teensy41_ldf_diag.rs` around lines 31 -
54, In find_teensy_libraries(), don't hardcode
home/.fbuild/prod/cache/platforms; instead obtain the canonical cache root via
the crate's existing helper (e.g., the canonical cache root function such as
fbuild::paths::cache_root() or equivalent that respects FBUILD_CACHE_DIR and
dev/prod modes) and then append "platforms" (root =
canonical_cache_root().join("platforms")), keeping the rest of the directory
traversal logic the same so discovery works for both dev and prod/custom cache
locations.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@crates/fbuild-library-select/tests/teensy41_ldf_diag.rs`:
- Around line 130-177: The test only logs whether "ssd1351" was picked but
doesn't fail on regressions; after computing ssd1351_selected (from
selection.required_libraries) add a hard assertion such as
assert!(!ssd1351_selected, "ssd1351 leaked into Scenario A: selection={:?}",
selection.required_libraries) to fail the test if it was selected; similarly,
after computing scen_b_ssd (from sketch_sel.required_libraries) add
assert!(!scen_b_ssd, "ssd1351 leaked into Scenario B: sketch_sel={:?}",
sketch_sel.required_libraries). Use the existing boolean variables
ssd1351_selected and scen_b_ssd and include a clear message showing the relevant
selection vector for debugging.

---

Nitpick comments:
In `@crates/fbuild-library-select/tests/teensy41_ldf_diag.rs`:
- Around line 31-54: In find_teensy_libraries(), don't hardcode
home/.fbuild/prod/cache/platforms; instead obtain the canonical cache root via
the crate's existing helper (e.g., the canonical cache root function such as
fbuild::paths::cache_root() or equivalent that respects FBUILD_CACHE_DIR and
dev/prod modes) and then append "platforms" (root =
canonical_cache_root().join("platforms")), keeping the rest of the directory
traversal logic the same so discovery works for both dev and prod/custom cache
locations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ea77565d-9401-40eb-88d4-f8a0d7b88e59

📥 Commits

Reviewing files that changed from the base of the PR and between 0bdba41 and 2807529.

📒 Files selected for processing (1)
  • crates/fbuild-library-select/tests/teensy41_ldf_diag.rs

Comment on lines +130 to +177
let ssd1351_selected = selection
.required_libraries
.iter()
.any(|n| n.eq_ignore_ascii_case("ssd1351"));
eprintln!(
"---\nssd1351 SELECTED? {}",
if ssd1351_selected {
"YES (leak — bug)"
} else {
"no"
}
);

// ---- Scenario B: project has NO local FastLED visible to the walker,
// only an example sketch that does `#include <FastLED.h>`. This is
// what happens if the per-example build's src_dir doesn't include the
// repo src. Walker falls through to bundled FastLED → expect leak.
eprintln!("---\n=== Scenario B: sketch-only project (no local FastLED in roots) ===");
let tmp = tempfile::tempdir().expect("tempdir");
let sketch_src = tmp.path().join("src");
std::fs::create_dir_all(&sketch_src).unwrap();
std::fs::write(
sketch_src.join("Blink.ino"),
"#include <FastLED.h>\nvoid setup(){} void loop(){}\n",
)
.unwrap();
let sketch_seeds = collect_seeds(&sketch_src);
let sketch_paths = vec![sketch_src.clone()];
let (sketch_sel, sketch_stats) = resolve_with_stats(&sketch_seeds, &sketch_paths, &libraries);
eprintln!(
"Scenario B STATS: passes={} files_read={}",
sketch_stats.passes, sketch_stats.files_read
);
eprintln!(
"Scenario B SELECTED ({}):",
sketch_sel.required_libraries.len()
);
for name in &sketch_sel.required_libraries {
eprintln!(" - {name}");
}
let scen_b_ssd = sketch_sel
.required_libraries
.iter()
.any(|n| n.eq_ignore_ascii_case("ssd1351"));
eprintln!(
"Scenario B ssd1351? {}",
if scen_b_ssd { "YES (leak)" } else { "no" }
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add hard assertions for ssd1351 absence in both scenarios.

This diagnostic currently only logs leak status; it never fails when ssd1351 is selected. That makes regressions easy to miss.

Proposed fix
@@
     let ssd1351_selected = selection
         .required_libraries
         .iter()
         .any(|n| n.eq_ignore_ascii_case("ssd1351"));
@@
     eprintln!(
         "---\nssd1351 SELECTED? {}",
         if ssd1351_selected {
             "YES (leak — bug)"
         } else {
             "no"
         }
     );
+    assert!(
+        !ssd1351_selected,
+        "Scenario A regression: ssd1351 was selected but should not be"
+    );
@@
     let scen_b_ssd = sketch_sel
         .required_libraries
         .iter()
         .any(|n| n.eq_ignore_ascii_case("ssd1351"));
@@
     eprintln!(
         "Scenario B ssd1351? {}",
         if scen_b_ssd { "YES (leak)" } else { "no" }
     );
+    assert!(
+        !scen_b_ssd,
+        "Scenario B regression: ssd1351 was selected but should not be"
+    );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
let ssd1351_selected = selection
.required_libraries
.iter()
.any(|n| n.eq_ignore_ascii_case("ssd1351"));
eprintln!(
"---\nssd1351 SELECTED? {}",
if ssd1351_selected {
"YES (leak — bug)"
} else {
"no"
}
);
// ---- Scenario B: project has NO local FastLED visible to the walker,
// only an example sketch that does `#include <FastLED.h>`. This is
// what happens if the per-example build's src_dir doesn't include the
// repo src. Walker falls through to bundled FastLED → expect leak.
eprintln!("---\n=== Scenario B: sketch-only project (no local FastLED in roots) ===");
let tmp = tempfile::tempdir().expect("tempdir");
let sketch_src = tmp.path().join("src");
std::fs::create_dir_all(&sketch_src).unwrap();
std::fs::write(
sketch_src.join("Blink.ino"),
"#include <FastLED.h>\nvoid setup(){} void loop(){}\n",
)
.unwrap();
let sketch_seeds = collect_seeds(&sketch_src);
let sketch_paths = vec![sketch_src.clone()];
let (sketch_sel, sketch_stats) = resolve_with_stats(&sketch_seeds, &sketch_paths, &libraries);
eprintln!(
"Scenario B STATS: passes={} files_read={}",
sketch_stats.passes, sketch_stats.files_read
);
eprintln!(
"Scenario B SELECTED ({}):",
sketch_sel.required_libraries.len()
);
for name in &sketch_sel.required_libraries {
eprintln!(" - {name}");
}
let scen_b_ssd = sketch_sel
.required_libraries
.iter()
.any(|n| n.eq_ignore_ascii_case("ssd1351"));
eprintln!(
"Scenario B ssd1351? {}",
if scen_b_ssd { "YES (leak)" } else { "no" }
);
let ssd1351_selected = selection
.required_libraries
.iter()
.any(|n| n.eq_ignore_ascii_case("ssd1351"));
eprintln!(
"---\nssd1351 SELECTED? {}",
if ssd1351_selected {
"YES (leak — bug)"
} else {
"no"
}
);
assert!(
!ssd1351_selected,
"Scenario A regression: ssd1351 was selected but should not be"
);
// ---- Scenario B: project has NO local FastLED visible to the walker,
// only an example sketch that does `#include <FastLED.h>`. This is
// what happens if the per-example build's src_dir doesn't include the
// repo src. Walker falls through to bundled FastLED → expect leak.
eprintln!("---\n=== Scenario B: sketch-only project (no local FastLED in roots) ===");
let tmp = tempfile::tempdir().expect("tempdir");
let sketch_src = tmp.path().join("src");
std::fs::create_dir_all(&sketch_src).unwrap();
std::fs::write(
sketch_src.join("Blink.ino"),
"`#include` <FastLED.h>\nvoid setup(){} void loop(){}\n",
)
.unwrap();
let sketch_seeds = collect_seeds(&sketch_src);
let sketch_paths = vec![sketch_src.clone()];
let (sketch_sel, sketch_stats) = resolve_with_stats(&sketch_seeds, &sketch_paths, &libraries);
eprintln!(
"Scenario B STATS: passes={} files_read={}",
sketch_stats.passes, sketch_stats.files_read
);
eprintln!(
"Scenario B SELECTED ({}):",
sketch_sel.required_libraries.len()
);
for name in &sketch_sel.required_libraries {
eprintln!(" - {name}");
}
let scen_b_ssd = sketch_sel
.required_libraries
.iter()
.any(|n| n.eq_ignore_ascii_case("ssd1351"));
eprintln!(
"Scenario B ssd1351? {}",
if scen_b_ssd { "YES (leak)" } else { "no" }
);
assert!(
!scen_b_ssd,
"Scenario B regression: ssd1351 was selected but should not be"
);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/fbuild-library-select/tests/teensy41_ldf_diag.rs` around lines 130 -
177, The test only logs whether "ssd1351" was picked but doesn't fail on
regressions; after computing ssd1351_selected (from
selection.required_libraries) add a hard assertion such as
assert!(!ssd1351_selected, "ssd1351 leaked into Scenario A: selection={:?}",
selection.required_libraries) to fail the test if it was selected; similarly,
after computing scen_b_ssd (from sketch_sel.required_libraries) add
assert!(!scen_b_ssd, "ssd1351 leaked into Scenario B: sketch_sel={:?}",
sketch_sel.required_libraries). Use the existing boolean variables
ssd1351_selected and scen_b_ssd and include a clear message showing the relevant
selection vector for debugging.

@zackees zackees merged commit 700bc69 into main May 24, 2026
90 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant