Three tree tests fail on an unmodified checkout whenever FORCE_COLOR is set in the environment, while CI — which does not set it — stays green.
Reproduction
FORCE_COLOR=3 cargo test --workspace
failures:
output::tree::tests::ascii_points_a_member_at_its_own_tree (crates/dependable/src/output/tree.rs)
tree_distinguishes_workspace_and_external (crates/dependable/tests/fixture_tree.rs)
a_member_used_by_another_member_points_at_its_own_tree (crates/dependable/tests/fixture_tree.rs)
Every assertion that fails is a contains against a plain label — "└── lib v0.1.0 (workspace) (see root)" and friends. The actual output carries SGR codes around the label, so the substring is not present.
Cause
label in crates/dependable/src/output/tree.rs styles through
text.if_supports_color(Stream::Stdout, …). owo-colors delegates that to the
supports-color crate, which honours FORCE_COLOR ahead of the TTY check.
Both test sets assumed the check would answer "no":
- the unit tests because a test harness captures stdout;
fixture_tree.rs explicitly — its module doc reads "Piped stdout is not a TTY, so labels are plain (uncolored) and assertable as text", and the spawned child inherits the parent's environment, FORCE_COLOR included.
Both are defaults rather than guarantees.
Why it matters
FORCE_COLOR is exported by terminal multiplexers, task runners and CI images. A contributor whose shell sets it sees mise run test fail on a clean master with no way to tell the failure from a real regression, and the pre-push hook blocks them. A test that holds only in some terminals is reporting the terminal, not the code.
Direction
dependable itself is behaving correctly — honouring FORCE_COLOR through a pipe is exactly what the variable is for — so the change belongs in the tests. The assertions are about the shape of the tree and never about its colour, so they should say so: strip styling before matching in the unit tests, and pin the child's colour environment in fixture_tree.rs's run helper rather than inferring it from the pipe.
Three
treetests fail on an unmodified checkout wheneverFORCE_COLORis set in the environment, while CI — which does not set it — stays green.Reproduction
Every assertion that fails is a
containsagainst a plain label —"└── lib v0.1.0 (workspace) (see root)"and friends. The actual output carries SGR codes around the label, so the substring is not present.Cause
labelincrates/dependable/src/output/tree.rsstyles throughtext.if_supports_color(Stream::Stdout, …).owo-colorsdelegates that to thesupports-colorcrate, which honoursFORCE_COLORahead of the TTY check.Both test sets assumed the check would answer "no":
fixture_tree.rsexplicitly — its module doc reads "Piped stdout is not a TTY, so labels are plain (uncolored) and assertable as text", and the spawned child inherits the parent's environment,FORCE_COLORincluded.Both are defaults rather than guarantees.
Why it matters
FORCE_COLORis exported by terminal multiplexers, task runners and CI images. A contributor whose shell sets it seesmise run testfail on a cleanmasterwith no way to tell the failure from a real regression, and the pre-push hook blocks them. A test that holds only in some terminals is reporting the terminal, not the code.Direction
dependableitself is behaving correctly — honouringFORCE_COLORthrough a pipe is exactly what the variable is for — so the change belongs in the tests. The assertions are about the shape of the tree and never about its colour, so they should say so: strip styling before matching in the unit tests, and pin the child's colour environment infixture_tree.rs'srunhelper rather than inferring it from the pipe.