1414
1515import importlib .util
1616import tempfile
17+ import subprocess
18+ import sys
1719import unittest
1820from pathlib import Path
1921
@@ -31,6 +33,93 @@ def write(path: Path, contents: str = "") -> None:
3133
3234
3335class RewriteLakeVendorTests (unittest .TestCase ):
36+ def test_toml_require_rewrite_preserves_following_tables (self ) -> None :
37+ with tempfile .TemporaryDirectory () as tmp :
38+ root = Path (tmp )
39+ lakefile = root / "lakefile.toml"
40+ following = ('[[lean_lib]]\n name = "Library"\n \n '
41+ '[[lean_exe]]\n name = "driver"\n root = "Main"\n '
42+ '[leanOptions]\n autoImplicit = false\n ' )
43+ write (lakefile , 'name = "consumer"\n \n [[require]]\n name = "dep"\n '
44+ 'scope = "owner"\n rev = "main"\n \n ' + following )
45+ self .assertTrue (rewrite_lake_vendor .replace_require_toml_blocks (
46+ lakefile , {"dep" : root / "packages/dep" }))
47+ rewritten = lakefile .read_text ()
48+ self .assertTrue (rewritten .endswith (following ))
49+ self .assertIn ('path = "packages/dep"' , rewritten )
50+ self .assertNotIn ('rev =' , rewritten )
51+ self .assertFalse (rewrite_lake_vendor .replace_require_toml_blocks (
52+ lakefile , {"dep" : root / "packages/dep" }))
53+
54+ def test_toml_require_stops_before_single_bracket_table (self ) -> None :
55+ with tempfile .TemporaryDirectory () as tmp :
56+ path = Path (tmp ) / 'lakefile.toml'
57+ following = '[leanOptions]\n autoImplicit = false\n '
58+ write (path , '[[require]]\n name = "dep"\n rev = "main"\n ' + following )
59+ rewrite_lake_vendor .replace_require_toml_blocks (path , {"dep" : Path (tmp ) / 'dep' })
60+ self .assertTrue (path .read_text ().endswith (following ))
61+
62+ def test_pinned_aesop_and_importgraph_keep_all_declared_targets (self ) -> None :
63+ fixtures = SCRIPT .parent / "tests/fixtures/lake-vendor"
64+ for name , dependency in [("aesop" , "batteries" ), ("importGraph" , "Cli" )]:
65+ with self .subTest (package = name ), tempfile .TemporaryDirectory () as tmp :
66+ path = Path (tmp ) / "lakefile.toml"
67+ original = (fixtures / (name + ".toml" )).read_text ()
68+ write (path , original )
69+ following = original [original .index ('[[lean_lib]]' ):]
70+ self .assertTrue (rewrite_lake_vendor .replace_require_toml_blocks (
71+ path , {dependency : Path (tmp ) / dependency }))
72+ self .assertTrue (path .read_text ().endswith (following ))
73+ self .assertIn ('path = "' + dependency + '"' , path .read_text ())
74+
75+ def test_external_symlink_rejected_before_any_rewrite (self ) -> None :
76+ for entry in [".lake" , "lakefile.lean" ]:
77+ with self .subTest (entry = entry ), tempfile .TemporaryDirectory () as tmp :
78+ base = Path (tmp )
79+ root , packages , shared = base / "private" , base / "packages" , base / "shared"
80+ root .mkdir ()
81+ packages .mkdir ()
82+ shared .mkdir ()
83+ original = 'require dep from git "https://example.invalid/dep" @ "rev"\n '
84+ write (root / "lakefile.lean" , original )
85+ write (shared / "lakefile.lean" , original )
86+ if entry == ".lake" :
87+ (root / entry ).symlink_to (shared , target_is_directory = True )
88+ else :
89+ (root / entry ).unlink ()
90+ (root / entry ).symlink_to (shared / entry )
91+ (packages / "dep" ).mkdir ()
92+ result = subprocess .run ([sys .executable , str (SCRIPT ), "--root" , str (root ),
93+ "--packages-dir" , str (packages )],
94+ capture_output = True , text = True )
95+ self .assertNotEqual (result .returncode , 0 )
96+ self .assertIn ("symlink leaves private roots" , result .stderr )
97+ self .assertEqual ((shared / "lakefile.lean" ).read_text (), original )
98+ self .assertEqual ((root / "lakefile.lean" ).read_text (), original )
99+
100+ def test_internal_payload_links_are_allowed (self ) -> None :
101+ with tempfile .TemporaryDirectory () as tmp :
102+ root , packages = Path (tmp ) / "private" , Path (tmp ) / "packages"
103+ root .mkdir ()
104+ packages .mkdir ()
105+ write (root / "real.lean" , "def value := 1\n " )
106+ (root / "linked.lean" ).symlink_to ("real.lean" )
107+ rewrite_lake_vendor .validate_vendoring_roots (root , packages )
108+
109+ def test_long_unmatched_trace_path_does_not_backtrack_exponentially (self ) -> None :
110+ with tempfile .TemporaryDirectory () as tmp :
111+ root , packages = Path (tmp ) / 'root' , Path (tmp ) / 'packages'
112+ root .mkdir ()
113+ (packages / 'mathlib' ).mkdir (parents = True )
114+ unrelated = '/' + '/' .join (['longsegment' ] * 80 ) + '/Unrelated.lean'
115+ trace = root / 'Module.trace'
116+ write (trace , unrelated + '\n /upstream/packages/mathlib/Mathlib.lean\n ' )
117+ result = subprocess .run ([sys .executable , str (SCRIPT ), '--root' , str (root ),
118+ '--packages-dir' , str (packages ), '--rewrite-traces' ],
119+ capture_output = True , text = True , timeout = 5 )
120+ self .assertEqual (result .returncode , 0 , result .stderr )
121+ self .assertEqual (trace .read_text (), unrelated + '\n Mathlib.lean\n ' )
122+
34123 def test_rewrites_upstream_aeneas_backend_trace_prefix (self ) -> None :
35124 with tempfile .TemporaryDirectory () as tmp :
36125 root = Path (tmp ) / "aeneas" / "backends" / "lean"
0 commit comments