alice-physics 2.0.0

Deterministic physics engine: 128-bit fixed-point rigid bodies with bit-exact, replayable results, typed observation and rollback for search and planning, plus FEM, CFD and 3D-print analysis modules
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
#!/usr/bin/env python3
"""scripts/wiring_guard.py の oracle.

検査器は「比較件数 0 でも green になる」型の空振りが最も危ないので、
(1) 配線済みで通る (2) 未配線で落ちる (3) 落ちるべき 5 つの形 (test のみ参照 /
tests/ のみ参照 / doc 言及のみ / use 行のみ / 自身の定義のみ) で落ちる
(4) マーカーと baseline の欠落・古さで落ちる (5) 検査対象 0 件で落ちる
を fixture crate で固定する。期待値は fixture の構造から決まり、検査器を呼んで作らない。

run: python3 scripts/test_wiring_guard.py
"""
from __future__ import annotations

import sys
import tempfile
import textwrap
import unittest
from pathlib import Path

sys.path.insert(0, str(Path(__file__).resolve().parent))
import wiring_guard as wg  # noqa: E402


def crate(files: dict[str, str]) -> Path:
    root = Path(tempfile.mkdtemp(prefix="wiring-fixture-"))
    for rel, body in files.items():
        p = root / rel
        p.parent.mkdir(parents=True, exist_ok=True)
        p.write_text(textwrap.dedent(body), encoding="utf-8")
    return root


def unwired(vs):
    return {v.key.split("::")[-1] for v in vs if v.kind == "unwired"}


def kinds(vs):
    return {v.kind for v in vs}


LIB = "pub mod a;\npub mod b;\n"


MARKED = "// ALLOW-UNWIRED: wiring debt {key} (kept until the driver exists)\npub fn helper() {{}}\n"
LEDGER = "# Wiring debt\n\n| key | what | closes |\n|---|---|---|\n{rows}"


def ledger_row(key: str) -> str:
    return f"| `{key}` | a helper | a caller |\n"


class DebtLedger(unittest.TestCase):
    """検査 E: `wiring debt <key>` と docs/wiring-debt.md の行は 2 方向で一致する"""

    def run_on(self, key: str = "driver-later", rows: str | None = None, marker: bool = True):
        files = {"src/lib.rs": LIB, "src/a.rs": MARKED.format(key=key) if marker else "pub fn helper() {}\n",
                 "src/b.rs": "pub fn x() {}\n" if marker else "pub fn x() { crate::a::helper(); }\n"}
        if rows is not None:
            files["docs/wiring-debt.md"] = LEDGER.format(rows=rows)
        return wg.check(crate(files))

    def test_a_listed_key_passes(self):
        self.assertNotIn("debt_unlisted", kinds(self.run_on(rows=ledger_row("driver-later"))))
        self.assertFalse({"debt_unlisted", "debt_stale", "debt_ledger", "bad_debt_key"} & kinds(
            self.run_on(rows=ledger_row("driver-later"))))

    def test_a_key_missing_from_the_ledger_fails(self):
        vs = self.run_on(rows=ledger_row("something-else") + ledger_row("x"))
        self.assertIn("debt_unlisted", kinds(vs))

    def test_a_ledger_row_no_marker_uses_fails(self):
        vs = self.run_on(rows=ledger_row("driver-later") + ledger_row("closed-long-ago"))
        self.assertEqual([v.message for v in vs if v.kind == "debt_stale"][0].split()[0], "台帳の")
        self.assertIn("closed-long-ago", " ".join(v.message for v in vs if v.kind == "debt_stale"))

    def test_markers_without_a_ledger_fail(self):
        self.assertIn("debt_ledger", kinds(self.run_on(rows=None)))

    def test_a_ledger_without_rows_fails(self):
        self.assertIn("debt_ledger", kinds(self.run_on(rows="")))

    def test_a_key_listed_twice_fails(self):
        vs = self.run_on(rows=ledger_row("driver-later") * 2)
        self.assertTrue(any(v.kind == "debt_ledger" and "2 行" in v.message for v in vs), vs)

    def test_keys_must_be_kebab_case(self):
        for bad in ("`driver_later`", "2.0.0", "DriverLater"):
            vs = self.run_on(key=bad, rows=ledger_row("driver-later"))
            self.assertIn("bad_debt_key", kinds(vs), bad)

    def test_trailing_punctuation_is_not_part_of_the_key(self):
        self.assertFalse({"debt_unlisted", "bad_debt_key"} & kinds(
            self.run_on(key="driver-later,", rows=ledger_row("driver-later"))))

    def test_no_marker_and_no_ledger_is_not_a_finding(self):
        self.assertFalse({"debt_ledger", "debt_unlisted", "debt_stale"} & kinds(self.run_on(marker=False)))


class UnwiredItems(unittest.TestCase):
    def test_an_item_called_from_another_production_module_is_wired(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn helper() {}\n",
            "src/b.rs": "pub fn entry() { crate::a::helper(); }\n",
            "examples/e.rs": "fn main() { mycrate::b::entry(); }\n",  # entry 自身が配線済でないと helper も配線済にならない
        })
        self.assertNotIn("helper", unwired(wg.check(r)))

    def test_an_item_nobody_calls_is_unwired(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn lonely() {}\n", "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("lonely", unwired(wg.check(r)))

    def test_pub_crate_items_are_checked_too(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub(crate) fn lonely_c() {}\n", "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("lonely_c", unwired(wg.check(r)))

    def test_struct_enum_const_trait_are_checked(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub struct Sx;\npub enum Ex { A }\npub const CX: u32 = 1;\npub trait Tx {}\n",
            "src/b.rs": "pub fn x() {}\n",
        })
        self.assertTrue({"Sx", "Ex", "CX", "Tx"} <= unwired(wg.check(r)))

    def test_referenced_only_inside_cfg_test_does_not_count(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn only_tested() {}\n",
            "src/b.rs": """
                pub fn x() {}
                #[cfg(test)]
                mod tests {
                    #[test]
                    fn t() { crate::a::only_tested(); }
                }
            """,
        })
        self.assertIn("only_tested", unwired(wg.check(r)))

    def test_referenced_only_from_the_tests_directory_does_not_count(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn only_integration() {}\n",
            "src/b.rs": "pub fn x() {}\n",
            "tests/t.rs": "fn t() { mycrate::a::only_integration(); }\n",
        })
        self.assertIn("only_integration", unwired(wg.check(r)))

    def test_a_mention_in_a_doc_comment_or_string_does_not_count(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn ghosted() {}\n",
            "src/b.rs": '/// see [`ghosted`] for details\npub fn x() { let _ = "ghosted"; }\n',
        })
        self.assertIn("ghosted", unwired(wg.check(r)))

    def test_a_use_line_alone_does_not_count(self):
        r = crate({
            "src/lib.rs": LIB + "pub use a::reexported;\n",
            "src/a.rs": "pub fn reexported() {}\n",
            "src/b.rs": "pub fn x() {}\n",
        })
        self.assertIn("reexported", unwired(wg.check(r)))

    def test_a_call_from_examples_or_benches_counts(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn demo_entry() {}\n",
            "src/b.rs": "pub fn x() {}\n",
            "examples/e.rs": "fn main() { mycrate::a::demo_entry(); }\n",
        })
        self.assertNotIn("demo_entry", unwired(wg.check(r)))

    def test_private_items_are_not_checked(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "fn private_lonely() {}\n", "src/b.rs": "pub fn x() {}\n"})
        self.assertNotIn("private_lonely", unwired(wg.check(r)))


class Markers(unittest.TestCase):
    def test_allow_unwired_with_a_reason_silences_the_item(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "// ALLOW-UNWIRED: public entry point reached from downstream crates\npub fn api() {}\n",
            "src/b.rs": "pub fn x() {}\n",
        })
        self.assertNotIn("api", unwired(wg.check(r)))

    def test_allow_unwired_with_a_short_reason_is_rejected(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "// ALLOW-UNWIRED: later\npub fn api() {}\n",
            "src/b.rs": "pub fn x() {}\n",
        })
        self.assertIn("bad_marker", kinds(wg.check(r)))

    def test_a_stale_allow_unwired_marker_is_rejected(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "// ALLOW-UNWIRED: public entry point reached from downstream crates\npub fn api() {}\n",
            "src/b.rs": "pub fn x() { crate::a::api(); }\n",
            "examples/e.rs": "fn main() { mycrate::b::x(); }\n",
        })
        self.assertIn("stale_marker", kinds(wg.check(r)))

    def test_allow_dead_code_without_a_marker_is_rejected(self):
        r = crate({"src/lib.rs": LIB + "#![allow(dead_code)]\n", "src/a.rs": "", "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("dead_code", kinds(wg.check(r)))

    def test_allow_dead_code_with_a_marker_passes(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "// ALLOW-DEAD: reserved variants awaiting solver integration\n#![allow(dead_code)]\n",
            "src/b.rs": "pub fn x() {}\n",
        })
        self.assertNotIn("dead_code", kinds(wg.check(r)))

    def test_allow_dead_code_marker_with_a_short_reason_is_rejected(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "// ALLOW-DEAD: todo\n#[allow(dead_code)]\nfn f() {}\n",
            "src/b.rs": "pub fn x() {}\n",
        })
        self.assertIn("bad_marker", kinds(wg.check(r)))

    def test_a_stale_allow_dead_marker_is_rejected(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "// ALLOW-DEAD: reserved variants awaiting solver integration\nfn f() {}\n",
            "src/b.rs": "pub fn x() {}\n",
        })
        self.assertIn("stale_marker", kinds(wg.check(r)))


class Baseline(unittest.TestCase):
    FILES = {"src/lib.rs": LIB, "src/a.rs": "pub fn legacy() {}\n", "src/b.rs": "pub fn x() {}\n"}

    def test_an_item_in_the_baseline_is_tolerated(self):
        r = crate(self.FILES)
        self.assertNotIn("legacy", unwired(wg.check(r, "unwired src/a.rs::legacy\n")))

    def test_an_item_not_in_the_baseline_fails(self):
        r = crate(self.FILES)
        self.assertIn("legacy", unwired(wg.check(r, "")))

    def test_a_stale_baseline_entry_fails_so_the_ratchet_tightens(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn legacy() {}\n", "src/b.rs": "pub fn x() { crate::a::legacy(); }\n", "examples/e.rs": "fn main() { mycrate::b::x(); }\n"})
        self.assertIn("stale_baseline", kinds(wg.check(r, "unwired src/a.rs::legacy\n")))

    def test_dead_code_count_may_not_grow_beyond_the_baseline(self):
        body = "#![allow(dead_code)]\n#[allow(dead_code)]\nfn f() {}\n"
        r = crate({"src/lib.rs": LIB, "src/a.rs": body, "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("dead_code", kinds(wg.check(r, "dead_code src/a.rs 1\n")))
        self.assertNotIn("dead_code", kinds(wg.check(r, "dead_code src/a.rs 2\n")))


class EmptyScan(unittest.TestCase):
    def test_scanning_zero_items_is_a_failure_not_a_green(self):
        r = crate({"src/lib.rs": "fn private_only() {}\n"})
        self.assertIn("empty_scan", kinds(wg.check(r)))

    def test_a_missing_src_directory_is_a_failure(self):
        r = crate({"README.md": "x\n"})
        self.assertIn("empty_scan", kinds(wg.check(r)))


def keys(vs, kind="unwired"):
    return {v.key for v in vs if v.kind == kind}


EX = "examples/e.rs"


class TransitiveWiring(unittest.TestCase):
    """未配線の item の本体からしか参照されない item も未配線 (不動点反復)."""

    CHAIN = {
        "src/lib.rs": LIB,
        "src/a.rs": "pub fn a_fn() { b_helper(); }\nfn b_helper() { crate::b::c_fn(); }\n",
        "src/b.rs": "pub fn c_fn() {}\n",
    }

    def test_a_chain_hanging_off_an_unwired_pub_is_unwired(self):
        got = unwired(wg.check(crate(self.CHAIN)))
        self.assertTrue({"a_fn", "c_fn"} <= got, got)

    def test_a_private_helper_is_a_node_but_is_never_reported(self):
        got = unwired(wg.check(crate(self.CHAIN)))
        self.assertNotIn("b_helper", got)

    def test_allow_unwired_on_the_head_makes_the_rest_of_the_chain_wired(self):
        files = dict(self.CHAIN)
        files["src/a.rs"] = "// ALLOW-UNWIRED: public entry point reached from downstream crates\n" + files["src/a.rs"]
        vs = wg.check(crate(files))
        self.assertNotIn("c_fn", unwired(vs))
        self.assertNotIn("a_fn", unwired(vs))
        self.assertNotIn("stale_marker", kinds(vs))

    def test_a_call_to_the_head_from_examples_wires_the_whole_chain(self):
        files = dict(self.CHAIN)
        files[EX] = "fn main() { mycrate::a::a_fn(); }\n"
        self.assertEqual(unwired(wg.check(crate(files))) & {"a_fn", "c_fn"}, set())

    def test_a_live_chain_of_depth_five_is_fully_wired(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn l1() { l2(); }\nfn l2() { l3(); }\nfn l3() { crate::b::l4(); }\n",
            "src/b.rs": "pub fn l4() { l5(); }\nfn l5() { l6(); }\npub fn l6() {}\n",
            EX: "fn main() { mycrate::a::l1(); }\n",
        })
        self.assertEqual(unwired(wg.check(r)), set())

    def test_mutual_recursion_with_no_outside_caller_is_unwired_and_terminates(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn ping() { pong(); }\npub fn pong() { ping(); }\n",
            "src/b.rs": "pub fn x() {}\n",
        })
        self.assertTrue({"ping", "pong"} <= unwired(wg.check(r)))

    def test_mutual_recursion_called_from_examples_is_wired(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn ping() { pong(); }\npub fn pong() { ping(); }\n",
            "src/b.rs": "pub fn x() {}\n",
            EX: "fn main() { mycrate::a::ping(); }\n",
        })
        self.assertEqual(unwired(wg.check(r)) & {"ping", "pong"}, set())

    def test_self_recursion_alone_is_unwired(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn rec(n: u32) { if n > 0 { rec(n - 1); } }\n", "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("rec", unwired(wg.check(r)))

    def test_a_dead_private_fn_does_not_keep_its_callee_alive(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn helper() {}\n", "src/b.rs": "fn dead() { crate::a::helper(); }\npub fn x() {}\n"})
        self.assertIn("helper", unwired(wg.check(r)))

    DISPLAY_T = "struct T;\nimpl core::fmt::Display for T {\n    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { crate::a::helper(); Ok(()) }\n}\n"

    def test_trait_impl_members_of_a_live_type_wire_their_callees(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn helper() {}\n",
            "src/b.rs": self.DISPLAY_T + "pub fn x() { let _t = T; }\n",
            EX: "fn main() { mycrate::b::x(); }\n",
        })
        self.assertNotIn("helper", unwired(wg.check(r)))

    def test_trait_impl_members_of_an_unused_type_do_not_wire_their_callees(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn helper() {}\n",
            "src/b.rs": self.DISPLAY_T + "pub fn x() {}\n",
            EX: "fn main() { mycrate::b::x(); }\n",
        })
        self.assertIn("helper", unwired(wg.check(r)))

    USER_TRAIT = "pub trait Tr { fn run_it(&self); }\npub struct T;\nimpl Tr for T {\n    fn run_it(&self) { crate::a::helper(); }\n}\n"

    def test_a_user_trait_impl_member_wires_its_callees_once_the_type_is_used(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn helper() {}\n",
            "src/b.rs": self.USER_TRAIT,
            EX: "fn main() { let _t = mycrate::b::T; }\n",
        })
        self.assertNotIn("helper", unwired(wg.check(r)))

    def test_a_user_trait_impl_member_of_an_unused_type_is_not_a_root(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn helper() {}\n", "src/b.rs": self.USER_TRAIT})
        self.assertTrue({"helper", "T", "Tr"} <= unwired(wg.check(r)))

    def test_code_outside_src_is_a_root_even_in_a_function_nobody_calls(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn helper() {}\n",
            "src/b.rs": "pub fn x() {}\n",
            "examples/e.rs": "fn dead_helper() { mycrate::a::helper(); }\nfn main() {}\n",
        })
        self.assertNotIn("helper", unwired(wg.check(r)))

    def test_a_reference_at_module_level_outside_any_body_is_a_root(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn helper() {}\n", "src/b.rs": "register!(crate::a::helper);\npub fn x() {}\n"})
        self.assertNotIn("helper", unwired(wg.check(r)))

    def test_exempt_attribute_items_are_roots_for_their_callees(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn helper() {}\n",
            "src/b.rs": '#[no_mangle]\npub extern "C" fn ffi() { crate::a::helper(); }\n',
        })
        self.assertNotIn("helper", unwired(wg.check(r)))

    def test_a_type_used_only_in_an_unwired_signature_is_unwired(self):
        r = crate({
            "src/lib.rs": LIB,
            "src/a.rs": "pub struct Cfg;\n",
            "src/b.rs": "pub fn make() -> crate::a::Cfg { crate::a::Cfg }\n",
        })
        self.assertTrue({"Cfg", "make"} <= unwired(wg.check(r)))

    def test_a_reference_in_the_last_character_of_a_body_belongs_to_that_body(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn z() {}\n", "src/b.rs": "pub fn top() -> fn() {crate::a::z}\n"})
        self.assertTrue({"z", "top"} <= unwired(wg.check(r)))
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn z() {}\n", "src/b.rs": "pub fn top() -> fn() {crate::a::z}\n",
                   EX: "fn main() { mycrate::b::top(); }\n"})
        self.assertEqual(unwired(wg.check(r)), set())

    def test_a_reference_right_after_a_body_belongs_to_the_outside(self):
        # `}` の直後 (同じ行) の module 直下の参照は、直前の fn の本体に含めない
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn zz() {}\n", "src/b.rs": "pub fn top() {}crate::a::zz;\n"})
        self.assertNotIn("zz", unwired(wg.check(r)))

    def test_the_stale_baseline_check_sees_the_transitive_entries(self):
        r = crate(self.CHAIN)
        base = "unwired src/a.rs::a_fn\nunwired src/b.rs::c_fn\n"
        self.assertEqual(keys(wg.check(r, base)), set())
        self.assertEqual(keys(wg.check(r, "unwired src/a.rs::a_fn\n")), {"src/b.rs::c_fn"})


class NameCollisions(unittest.TestCase):
    """local 変数 / 引数 / フィールドと同名の pub fn は、それだけでは配線済にならない."""

    def run_with(self, user_src: str, extra: dict[str, str] | None = None):
        files = {
            "src/lib.rs": LIB,
            "src/a.rs": "pub fn flip() {}\n",
            "src/b.rs": user_src,
            EX: "fn main() { mycrate::b::x(); }\n",
        }
        files.update(extra or {})
        return unwired(wg.check(crate(files)))

    def test_a_let_binding_is_not_a_reference(self):
        self.assertIn("flip", self.run_with("pub fn x() { let flip = 1; let _ = flip + 1; }\n"))

    def test_a_let_mut_binding_is_not_a_reference(self):
        self.assertIn("flip", self.run_with("pub fn x() { let mut flip = 1; flip += 1; }\n"))

    def test_a_tuple_pattern_binding_is_not_a_reference(self):
        self.assertIn("flip", self.run_with("pub fn x() { let (flip, other) = (1, 2); let _ = flip + other; }\n"))

    def test_an_if_let_binding_is_not_a_reference(self):
        self.assertIn("flip", self.run_with("pub fn x(o: Option<u8>) { if let Some(flip) = o { let _ = flip; } }\n"))

    def test_a_function_parameter_is_not_a_reference(self):
        self.assertIn("flip", self.run_with("pub fn x(flip: bool) -> bool { flip }\n"))

    def test_a_closure_parameter_is_not_a_reference(self):
        self.assertIn("flip", self.run_with("pub fn x() { let _f = |flip: i32| flip + 1; let _g = |flip| flip; }\n"))

    def test_a_for_loop_variable_is_not_a_reference(self):
        self.assertIn("flip", self.run_with("pub fn x() { for flip in 0..3 { let _ = flip; } }\n"))

    def test_a_field_definition_and_field_access_are_not_references(self):
        src = "pub struct S { pub flip: bool }\npub fn x(s: &S) -> bool { s.flip }\n"
        self.assertIn("flip", self.run_with(src))

    def test_a_struct_literal_field_and_shorthand_are_not_references(self):
        src = "pub struct S { pub flip: bool }\npub fn x() -> S { let v = true; S { flip: v } }\npub fn y(flip: bool) -> S { S { flip } }\n"
        self.assertIn("flip", self.run_with(src))

    def test_a_method_call_does_not_wire_a_free_fn_of_the_same_name(self):
        src = "pub struct S;\nimpl S { pub fn flip(&self) {} }\npub fn x(s: &S) { s.flip(); }\n"
        files = {"src/lib.rs": LIB, "src/a.rs": "pub fn flip() {}\n", "src/b.rs": src,
                 EX: "fn main() { mycrate::b::x(&mycrate::b::S); }\n"}
        self.assertIn("src/a.rs::flip", keys(wg.check(crate(files))))

    def test_an_actual_call_wires_the_fn(self):
        self.assertNotIn("flip", self.run_with("pub fn x() { crate::a::flip(); }\n"))

    def test_a_bare_call_wires_the_fn(self):
        self.assertNotIn("flip", self.run_with("use crate::a::flip;\npub fn x() { flip(); }\n"))

    def test_a_turbofish_call_wires_the_fn(self):
        r = self.run_with("pub fn x() { crate::a::flip::<u8>(); }\n", {"src/a.rs": "pub fn flip<T>() {}\n"})
        self.assertNotIn("flip", r)

    def test_passing_the_fn_path_as_a_value_wires_it(self):
        self.assertNotIn("flip", self.run_with("pub fn x() { call(crate::a::flip); }\n"))

    def test_a_bare_fn_pointer_argument_wires_it(self):
        self.assertNotIn("flip", self.run_with("use crate::a::flip;\npub fn x() { apply(flip); }\n"))

    def test_a_local_shadow_followed_by_a_real_call_elsewhere_still_wires_it(self):
        src = "pub fn x() { let flip = 1; let _ = flip; }\npub fn y() { crate::a::flip(); }\n"
        files = {"src/lib.rs": LIB, "src/a.rs": "pub fn flip() {}\n", "src/b.rs": src,
                 EX: "fn main() { mycrate::b::x(); mycrate::b::y(); }\n"}
        self.assertNotIn("flip", unwired(wg.check(crate(files))))

    def test_a_method_is_wired_only_by_a_method_call_or_a_path(self):
        files = {"src/lib.rs": LIB, "src/a.rs": "pub struct S;\nimpl S { pub fn flip(&self) {} }\n",
                 "src/b.rs": "pub fn x(s: &crate::a::S) { let flip = 1; let _ = flip; }\n",
                 EX: "fn main() { mycrate::b::x(&mycrate::a::S); }\n"}
        self.assertIn("flip", unwired(wg.check(crate(files))))
        files["src/b.rs"] = "pub fn x(s: &crate::a::S) { s.flip(); }\n"
        self.assertNotIn("flip", unwired(wg.check(crate(files))))
        files["src/b.rs"] = "pub fn x(s: &crate::a::S) { crate::a::S::flip(s); }\n"
        self.assertNotIn("flip", unwired(wg.check(crate(files))))

    def method_files(self, user_src: str):
        return {"src/lib.rs": LIB, "src/a.rs": "pub struct S;\nimpl S { pub fn flip(&self) {} }\n", "src/b.rs": user_src,
                EX: "fn main() { mycrate::b::x(&mycrate::a::S); }\n"}

    def test_a_field_access_does_not_wire_a_method_of_the_same_name(self):
        files = self.method_files("pub struct P { pub flip: bool }\npub fn x(_s: &crate::a::S, p: &P) -> bool { p.flip }\n")
        self.assertIn("flip", unwired(wg.check(crate(files))))

    def test_a_bare_call_of_a_local_closure_does_not_wire_a_method(self):
        files = self.method_files("pub fn x(_s: &crate::a::S) -> u8 { let flip = |a: u8| a; flip(1) }\n")
        self.assertIn("flip", unwired(wg.check(crate(files))))

    def test_a_struct_pattern_field_shorthand_is_not_a_reference(self):
        src = "pub struct S { pub flip: bool }\npub fn x(S { flip }: S) { let _ = (); }\n"
        self.assertIn("flip", self.run_with(src))

    def test_uppercase_items_keep_the_plain_occurrence_rule(self):
        files = {"src/lib.rs": LIB, "src/a.rs": "pub const LIMIT: u32 = 3;\n",
                 "src/b.rs": "pub fn x() -> u32 { crate::a::LIMIT }\n", EX: "fn main() { mycrate::b::x(); }\n"}
        self.assertNotIn("LIMIT", unwired(wg.check(crate(files))))


class OwnImplIsNotWiring(unittest.TestCase):
    """型 T 自身の `impl T` / `impl Trait for T` の見出しと本体の中の T は、T の配線の根拠にならない."""

    LONELY_STRUCT = (
        "pub struct Grid { cells: Vec<u8> }\n"
        "impl Grid {\n"
        "    pub fn new(n: usize) -> Grid { Grid { cells: vec![0; n] } }\n"
        "    pub fn merge(&self, other: &Grid) -> Grid { Grid { cells: other.cells.clone() } }\n"
        "}\n"
        "impl Clone for Grid {\n"
        "    fn clone(&self) -> Grid { Grid { cells: self.cells.clone() } }\n"
        "}\n"
    )

    def test_a_struct_mentioned_only_by_its_own_impls_is_unwired(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": self.LONELY_STRUCT, "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("src/a.rs::Grid", keys(wg.check(r)))

    def test_an_enum_matched_only_inside_its_own_impl_is_unwired(self):
        src = (
            "pub enum Mode { Fast, Slow }\n"
            "impl Mode {\n"
            "    pub fn factor(&self) -> u32 { match self { Mode::Fast => 2, Mode::Slow => 1 } }\n"
            "}\n"
            "impl core::fmt::Display for Mode {\n"
            "    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {\n"
            "        match self { Mode::Fast => write!(f, \"f\"), Mode::Slow => write!(f, \"s\") }\n"
            "    }\n"
            "}\n"
        )
        r = crate({"src/lib.rs": LIB, "src/a.rs": src, "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("src/a.rs::Mode", keys(wg.check(r)))

    def test_a_generic_impl_header_does_not_wire_the_type(self):
        src = "pub struct Wrap<T> { v: T }\nimpl<T: Copy> Wrap<T> where T: Default {\n    pub fn get(&self) -> T { self.v }\n}\n"
        r = crate({"src/lib.rs": LIB, "src/a.rs": src, "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("src/a.rs::Wrap", keys(wg.check(r)))

    def test_a_path_qualified_inherent_impl_does_not_wire_the_type(self):
        src = "pub struct Grid { n: u8 }\nimpl crate::a::Grid {\n    pub fn new() -> Grid { Grid { n: 0 } }\n}\n"
        r = crate({"src/lib.rs": LIB, "src/a.rs": src, "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("src/a.rs::Grid", keys(wg.check(r)))

    def test_a_path_qualified_trait_impl_does_not_wire_the_type(self):
        src = "pub struct Grid { n: u8 }\nimpl Clone for crate::a::Grid {\n    fn clone(&self) -> Grid { Grid { n: self.n } }\n}\n"
        r = crate({"src/lib.rs": LIB, "src/a.rs": src, "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("src/a.rs::Grid", keys(wg.check(r)))

    def test_a_path_qualified_generic_impl_does_not_wire_the_type(self):
        src = "pub struct Wrap<T> { v: T }\nimpl<T: Copy> crate::a::Wrap<T> {\n    pub fn get(&self) -> T { self.v }\n}\n"
        r = crate({"src/lib.rs": LIB, "src/a.rs": src, "src/b.rs": "pub fn x() {}\n"})
        self.assertIn("src/a.rs::Wrap", keys(wg.check(r)))

    def test_a_struct_used_from_another_file_is_wired(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": self.LONELY_STRUCT,
                   "src/b.rs": "pub fn x() -> usize { let g = crate::a::Grid::new(3); let _ = g; 0 }\n",
                   EX: "fn main() { mycrate::b::x(); }\n"})
        self.assertNotIn("src/a.rs::Grid", keys(wg.check(r)))

    def test_a_struct_used_from_an_example_is_wired(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": self.LONELY_STRUCT, "src/b.rs": "pub fn x() {}\n",
                   EX: "fn main() { let _g = mycrate::a::Grid::new(2); }\n"})
        self.assertNotIn("src/a.rs::Grid", keys(wg.check(r)))

    def test_a_struct_used_by_a_wired_fn_in_the_same_file_is_wired(self):
        src = self.LONELY_STRUCT + "pub fn total(n: usize) -> usize { let g = Grid::new(n); g.cells.len() }\n"
        r = crate({"src/lib.rs": LIB, "src/a.rs": src, "src/b.rs": "pub fn x() {}\n",
                   EX: "fn main() { mycrate::a::total(4); }\n"})
        vs = wg.check(r)
        self.assertNotIn("src/a.rs::Grid", keys(vs))
        self.assertNotIn("src/a.rs::total", keys(vs))

    def test_a_struct_used_only_by_an_unwired_fn_in_the_same_file_is_unwired(self):
        src = self.LONELY_STRUCT + "pub fn total(n: usize) -> usize { let g = Grid::new(n); g.cells.len() }\n"
        r = crate({"src/lib.rs": LIB, "src/a.rs": src, "src/b.rs": "pub fn x() {}\n"})
        self.assertTrue({"src/a.rs::Grid", "src/a.rs::total"} <= keys(wg.check(r)))

    def test_a_type_named_in_another_types_impl_is_wired_through_that_method(self):
        src = (
            "pub struct Cell(u8);\n"
            "pub struct Board;\n"
            "impl Board { pub fn cell(&self) -> Cell { Cell(0) } }\n"
        )
        r = crate({"src/lib.rs": LIB, "src/a.rs": src, "src/b.rs": "pub fn x() {}\n",
                   EX: "fn main() { let b = mycrate::a::Board; let _ = b.cell(); }\n"})
        vs = wg.check(r)
        self.assertNotIn("src/a.rs::Cell", keys(vs))
        self.assertNotIn("src/a.rs::Board", keys(vs))

    LONELY_DEFAULT = (
        "pub fn helper() -> u8 { 1 }\n"
        "pub struct Lonely;\n"
        "impl Default for Lonely { fn default() -> Lonely { helper(); Lonely } }\n"
    )

    def test_trait_impl_members_of_an_unwired_type_do_not_wire_other_items(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": self.LONELY_DEFAULT, "src/b.rs": "pub fn x() {}\n"})
        vs = wg.check(r)
        self.assertIn("src/a.rs::helper", keys(vs))
        self.assertIn("src/a.rs::Lonely", keys(vs))

    def test_trait_impl_members_of_a_wired_type_wire_other_items(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": self.LONELY_DEFAULT, "src/b.rs": "pub fn x() {}\n",
                   EX: "fn main() { let _l = mycrate::a::Lonely; }\n"})
        vs = wg.check(r)
        self.assertNotIn("src/a.rs::helper", keys(vs))
        self.assertNotIn("src/a.rs::Lonely", keys(vs))

    SHAPE = "pub trait Shape { fn area(&self) -> u32; }\npub struct Sq;\nimpl Shape for Sq { fn area(&self) -> u32 { 1 } }\n"

    def test_a_trait_named_in_an_impl_header_is_referenced_when_the_type_is_wired(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": self.SHAPE, "src/b.rs": "pub fn x() {}\n",
                   EX: "fn main() { let _s = mycrate::a::Sq; }\n"})
        self.assertNotIn("src/a.rs::Shape", keys(wg.check(r)))

    def test_a_trait_named_only_in_the_impl_header_of_an_unwired_type_is_unwired(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": self.SHAPE, "src/b.rs": "pub fn x() {}\n"})
        self.assertTrue({"src/a.rs::Shape", "src/a.rs::Sq"} <= keys(wg.check(r)))

    def test_allow_unwired_on_a_type_with_an_impl_is_not_stale(self):
        src = "// ALLOW-UNWIRED: public entry point reached from downstream crates\n" + self.LONELY_STRUCT
        vs = wg.check(crate({"src/lib.rs": LIB, "src/a.rs": src, "src/b.rs": "pub fn x() {}\n"}))
        self.assertNotIn("stale_marker", kinds(vs))
        self.assertNotIn("src/a.rs::Grid", keys(vs))


class TraitImplFollowsSelfType(unittest.TestCase):
    """`impl Trait for T` with T defined in src lives with T: the header and the members
    wire what they name only once T is wired. Impls on types from outside src and
    blanket impls on a type parameter keep their members as roots."""

    TYPES = "pub struct Foo;\npub struct Bar;\npub trait Tag<X> {}\n"
    FROM = "impl From<Foo> for Bar { fn from(_f: Foo) -> Bar { Bar } }\n"
    HEADER_ONLY = "impl Tag<Foo> for Bar {}\n"

    def run_on(self, impl: str, example: str | None = None, types: str | None = None):
        files = {"src/lib.rs": LIB, "src/a.rs": (types or self.TYPES) + impl, "src/b.rs": "pub fn x() {}\n"}
        if example is not None:
            files[EX] = example
        return keys(wg.check(crate(files)))

    def test_from_foo_for_an_unwired_bar_does_not_wire_foo(self):
        got = self.run_on(self.FROM)
        self.assertTrue({"src/a.rs::Foo", "src/a.rs::Bar"} <= got, got)

    def test_a_header_only_impl_for_an_unwired_bar_does_not_wire_foo(self):
        got = self.run_on(self.HEADER_ONLY)
        self.assertTrue({"src/a.rs::Foo", "src/a.rs::Bar"} <= got, got)

    def test_from_foo_for_a_wired_bar_wires_foo(self):
        got = self.run_on(self.FROM, "fn main() { let _b = mycrate::a::Bar; }\n")
        self.assertEqual(got & {"src/a.rs::Foo", "src/a.rs::Bar"}, set())

    def test_a_header_only_impl_for_a_wired_bar_wires_foo(self):
        got = self.run_on(self.HEADER_ONLY, "fn main() { let _b = mycrate::a::Bar; }\n")
        self.assertEqual(got & {"src/a.rs::Foo", "src/a.rs::Bar", "src/a.rs::Tag"}, set())

    def test_a_marked_type_wires_its_trait_impls(self):
        types = "pub struct Foo;\n// ALLOW-UNWIRED: public entry point reached from downstream crates\npub struct Bar;\npub trait Tag<X> {}\n"
        got = self.run_on(self.FROM, types=types)
        self.assertNotIn("src/a.rs::Foo", got)

    def test_an_impl_on_an_outside_type_keeps_its_members_as_roots(self):
        got = self.run_on("impl From<Foo> for u32 { fn from(_f: Foo) -> u32 { 0 } }\n")
        self.assertNotIn("src/a.rs::Foo", got)

    def test_an_impl_on_an_outside_generic_type_keeps_its_members_as_roots(self):
        got = self.run_on("impl core::fmt::Debug for Vec<Bar> { fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result { let _ = Foo; Ok(()) } }\n")
        self.assertNotIn("src/a.rs::Foo", got)

    def test_a_blanket_impl_on_a_type_parameter_keeps_its_members_as_roots(self):
        got = self.run_on("pub trait Mk { fn mk(&self) -> u8; }\nimpl<T: Copy> Mk for T { fn mk(&self) -> u8 { let _ = Foo; 0 } }\n")
        self.assertNotIn("src/a.rs::Foo", got)

    def test_a_type_parameter_named_like_a_src_type_is_still_a_parameter(self):
        types = self.TYPES + "pub struct T;\n"
        got = self.run_on("pub trait Mk { fn mk(&self) -> u8; }\nimpl<T> Mk for T { fn mk(&self) -> u8 { let _ = Foo; 0 } }\n", types=types)
        self.assertNotIn("src/a.rs::Foo", got)


class Robustness(unittest.TestCase):
    """検査器自身が例外終了せず、明示的な違反か empty_scan になる."""

    def test_an_empty_source_tree_is_an_explicit_empty_scan(self):
        self.assertIn("empty_scan", kinds(wg.check(crate({"src/lib.rs": ""}))))

    def test_an_unclosed_brace_is_an_explicit_violation_not_an_exception(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn broken() {\n  if x {\n", "src/b.rs": "pub fn x() {}\n"})
        vs = wg.check(r)
        self.assertIn("unbalanced_braces", kinds(vs))
        self.assertEqual(wg.main(["--root", str(r), "--baseline", "none"]), 1)

    def test_non_utf8_bytes_do_not_raise(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn ok() {}\n", "src/b.rs": "pub fn x() {}\n"})
        (r / "src/a.rs").write_bytes(b"pub fn ok() {}\n// \xff\xfe\x80 broken\n")
        vs = wg.check(r)
        self.assertIsInstance(vs, list)
        self.assertIn("ok", unwired(vs))

    def test_a_huge_file_finishes_quickly(self):
        import time

        body = "".join(f"pub fn f{i}() {{ if true {{ g{i}(); }} }}\nfn g{i}() {{}}\n" for i in range(20000))
        r = crate({"src/lib.rs": LIB, "src/a.rs": body, "src/b.rs": "pub fn x() {}\n"})
        # ⚠️ CPU time of this process, not elapsed time: the check runs in-process,
        # so a quadratic regression shows up here, while other processes' load
        # does not (elapsed time read 34-37 s at load average 41-52 with 5 s of
        # CPU), nor do wall clock steps (machine sleep, NTP: one read 943 s)
        t0 = time.process_time()
        vs = wg.check(r)
        self.assertLess(time.process_time() - t0, 30)
        self.assertEqual(len([v for v in vs if v.kind == "unwired"]), 20001)

    def test_a_pathological_header_without_a_terminator_does_not_hang(self):
        import time

        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn a\n" * 5000, "src/b.rs": "pub fn x() {}\n"})
        # ⚠️ CPU time of this process, not elapsed time: the check runs in-process,
        # so a quadratic regression shows up here, while other processes' load
        # does not (elapsed time read 34-37 s at load average 41-52 with 5 s of
        # CPU), nor do wall clock steps (machine sleep, NTP: one read 943 s)
        t0 = time.process_time()
        wg.check(r)
        self.assertLess(time.process_time() - t0, 30)

    def test_no_src_directory_is_an_empty_scan(self):
        self.assertIn("empty_scan", kinds(wg.check(crate({"README.md": "x\n"}))))

    def test_a_binary_garbage_file_does_not_raise(self):
        r = crate({"src/lib.rs": LIB, "src/a.rs": "pub fn lonely() {}\n", "src/b.rs": "pub fn x() {}\n"})
        (r / "src/c.rs").write_bytes(bytes(range(256)) * 50)
        self.assertIsInstance(wg.check(r), list)


class Workspace(unittest.TestCase):
    """Cargo workspace: member の src/ を走査し、参照は repo 全体の corpus で数える."""

    WS = '[workspace]\nmembers = ["alpha", "beta"]\nresolver = "2"\n'

    def test_a_call_from_a_sibling_member_wires_the_item(self):
        r = crate({
            "Cargo.toml": self.WS,
            "alpha/src/lib.rs": "pub fn shared() {}\n",
            "beta/src/lib.rs": "pub fn x() { alpha::shared(); }\n",
        })
        vs = wg.check(r)
        self.assertIn("beta/src/lib.rs::x", keys(vs))
        self.assertIn("alpha/src/lib.rs::shared", keys(vs))  # x が未配線なので shared も未配線 (推移)
        (r / "beta/examples").mkdir()
        (r / "beta/examples/e.rs").write_text("fn main() { beta::x(); }\n")  # x が配線済になれば shared も配線済
        self.assertEqual(keys(wg.check(r)), set())

    def test_keys_are_relative_to_the_repo_root(self):
        r = crate({
            "Cargo.toml": self.WS,
            "alpha/src/lib.rs": "pub fn lonely_a() {}\n",
            "beta/src/lib.rs": "pub fn lonely_b() {}\n",
        })
        self.assertEqual(keys(wg.check(r)), {"alpha/src/lib.rs::lonely_a", "beta/src/lib.rs::lonely_b"})

    def test_a_member_without_src_is_skipped_not_fatal(self):
        r = crate({
            "Cargo.toml": '[workspace]\nmembers = ["alpha", "gamma"]\n',
            "alpha/src/lib.rs": "pub fn lonely_a() {}\n",
            "gamma/README.md": "no src here\n",
        })
        vs = wg.check(r)
        self.assertIn("alpha/src/lib.rs::lonely_a", keys(vs))
        self.assertNotIn("empty_scan", kinds(vs))

    def test_empty_scan_only_when_no_member_defines_anything(self):
        r = crate({"Cargo.toml": self.WS, "alpha/src/lib.rs": "fn private() {}\n", "beta/README.md": "x\n"})
        self.assertIn("empty_scan", kinds(wg.check(r)))
        r2 = crate({"Cargo.toml": self.WS, "alpha/src/lib.rs": "fn private() {}\n", "beta/src/lib.rs": "pub fn d() {}\n"})
        self.assertNotIn("empty_scan", kinds(wg.check(r2)))

    def test_a_glob_member_is_expanded(self):
        r = crate({
            "Cargo.toml": '[workspace]\nmembers = ["crates/*"]\n',
            "crates/a/src/lib.rs": "pub fn def_a() {}\n",
            "crates/b/src/lib.rs": "pub fn caller() { a::def_a(); }\n",
            "crates/b/examples/e.rs": "fn main() { b::caller(); }\n",
            "crates/notes.txt": "not a crate\n",
        })
        self.assertEqual(keys(wg.check(r)), set())
        (r / "crates/b/examples/e.rs").unlink()
        self.assertEqual(keys(wg.check(r)), {"crates/b/src/lib.rs::caller", "crates/a/src/lib.rs::def_a"})

    def test_tests_and_target_under_a_member_are_still_excluded(self):
        r = crate({
            "Cargo.toml": self.WS,
            "alpha/src/lib.rs": "pub fn only_tested() {}\n",
            "beta/src/lib.rs": "pub fn x() {}\n",
            "beta/tests/t.rs": "fn t() { alpha::only_tested(); }\n",
            "alpha/target/gen.rs": "fn g() { only_tested(); }\n",
        })
        self.assertIn("alpha/src/lib.rs::only_tested", keys(wg.check(r)))

    def test_a_root_package_that_is_also_a_workspace_is_scanned_too(self):
        r = crate({
            "Cargo.toml": '[package]\nname = "root"\n[workspace]\nmembers = ["alpha"]\n',
            "src/lib.rs": "pub fn root_lonely() {}\n",
            "alpha/src/lib.rs": "pub fn alpha_lonely() {}\n",
        })
        self.assertEqual(keys(wg.check(r)), {"src/lib.rs::root_lonely", "alpha/src/lib.rs::alpha_lonely"})

    def test_a_transitive_chain_across_members(self):
        r = crate({
            "Cargo.toml": self.WS,
            "alpha/src/lib.rs": "pub fn deep() {}\n",
            "beta/src/lib.rs": "pub fn top() { mid(); }\nfn mid() { alpha::deep(); }\n",
        })
        self.assertEqual(keys(wg.check(r)), {"alpha/src/lib.rs::deep", "beta/src/lib.rs::top"})

    def test_an_excluded_member_is_not_scanned(self):
        r = crate({
            "Cargo.toml": '[workspace]\nmembers = ["crates/*"]\nexclude = ["crates/skip"]\n',
            "crates/a/src/lib.rs": "pub fn lonely_a() {}\n",
            "crates/skip/src/lib.rs": "pub fn lonely_skip() {}\n",
        })
        self.assertEqual(keys(wg.check(r)), {"crates/a/src/lib.rs::lonely_a"})

    def test_markers_and_the_baseline_use_the_repo_relative_key(self):
        r = crate({
            "Cargo.toml": self.WS,
            "alpha/src/lib.rs": "// ALLOW-UNWIRED: public entry point reached from downstream crates\npub fn api() {}\npub fn legacy() {}\n",
            "beta/src/lib.rs": "pub fn x() {}\n",
        })
        vs = wg.check(r, "unwired alpha/src/lib.rs::legacy\nunwired beta/src/lib.rs::x\n")
        self.assertEqual(keys(vs), set())


if __name__ == "__main__":
    unittest.main(verbosity=2)