Skip to content

Commit fe5d9e4

Browse files
committed
Fix bug in lakefile rewriting, improve test
The lakefile rewriting logic was assuming the relevant [[require]] block was followed by another [[require]] in the TOML (or EOF). This is wrong: any other block (like [[lean_lib]]) should mark the end of it. Also reorganize and improve the tests around it.
1 parent 75f6857 commit fe5d9e4

File tree

4 files changed

+343
-120
lines changed

4 files changed

+343
-120
lines changed

Hopscotch/LakefileProcessor.lean

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ private def preferredNewline (contents : String) : String :=
1313
private def isRequireHeader (line : String) : Bool :=
1414
line.trimAscii.copy == "[[require]]"
1515

16+
/-- Detect any TOML section header (`[table]` or `[[array-of-tables]]`) after trimming indentation. -/
17+
private def isTomlSectionHeader (line : String) : Bool :=
18+
line.trimAscii.startsWith "["
19+
1620
/-- Read `name = "..."` or `rev = "..."` values from a trimmed TOML assignment line.
1721
Splits on `"` to extract the value; does not handle escaped quotes inside values. -/
1822
private def quotedAssignmentValue? (key : String) (line : String) : Option String :=
@@ -62,14 +66,17 @@ private def findNamedRequireBlocks (lines : Array String) (dependencyName : Stri
6266
let mut blockEnd := lines.size
6367
let mut j := blockStart
6468
while j < lines.size do
65-
if isRequireHeader lines[j]! then
69+
if isTomlSectionHeader lines[j]! then
6670
blockEnd := j
6771
break
6872
j := j + 1
6973
-- Record this block if its `name` field matches.
7074
let hasName := (lines.extract blockStart blockEnd).any
7175
fun line => quotedAssignmentValue? "name" line == some dependencyName
7276
if hasName then
77+
-- Decrement blockEnd while it's whitespace or newline
78+
while blockEnd > blockStart && lines[blockEnd - 1]!.isEmpty do
79+
blockEnd := blockEnd - 1
7380
blocks := blocks.push (blockStart, blockEnd)
7481
i := blockEnd
7582
else

HopscotchTest/Main.lean

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import HopscotchTestLib.LakefileLeanReadTests
66
import HopscotchTestLib.ParseTests
77
import HopscotchTestLib.ToolchainIOTests
88
import HopscotchTestLib.UtilTests
9+
import HopscotchTestLib.LakefileRewriting
910

1011
open HopscotchTestLib
1112

@@ -37,6 +38,7 @@ def testSuitesToRun : IO (Array (String × TestSuite)) := do
3738
("ParseTests", ParseTests.suite),
3839
("IOTests", IOTests.suite),
3940
("LakefileReadTests", LakefileReadTests.suite),
41+
("LakefileRewriting", LakefileRewriting.suite),
4042
("LakefileLeanReadTests", LakefileLeanReadTests.suite),
4143
("ToolchainIOTests", ToolchainIOTests.suite),
4244
("UtilTests", UtilTests.suite)

HopscotchTestLib/BehaviorTests.lean

Lines changed: 0 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -51,124 +51,6 @@ private def «commit list normalization» : IO Unit := do
5151
catch _ =>
5252
pure ()
5353

54-
/-- Scenario: the lakefile rewrite updates the first matching dependency rev in place. -/
55-
private def «lakefile dependency rev rewriting» : IO Unit := do
56-
-- Prepare: a lakefile with two dependency blocks and one target rev to rewrite.
57-
let base := makeMultiline [
58-
"name = \"demo\"",
59-
"",
60-
"[[require]]",
61-
"name = \"mathlib\"",
62-
"git = \"https://github.com/leanprover-community/mathlib4.git\"",
63-
"rev = \"upstream\" # keep this comment",
64-
"",
65-
"[[require]]",
66-
"name = \"batteries\"",
67-
"git = \"https://github.com/leanprover-community/batteries.git\"",
68-
"rev = \"old\"",
69-
""
70-
]
71-
72-
-- Act: rewrite the target dependency rev in the prepared lakefile.
73-
let rewritten ← IO.ofExcept <| LakefileProcessor.rewriteContents base "batteries" "abc123"
74-
-- Assert: the rewritten lakefile changes only the requested dependency rev.
75-
assertTrue (rewritten.contains "rev = \"abc123\"")
76-
"rewrite should update the requested dependency rev"
77-
assertTrue (rewritten.contains "rev = \"upstream\" # keep this comment")
78-
"rewrite should leave unrelated dependency rev lines untouched"
79-
assertEq 1 ((rewritten.splitOn "rev = \"abc123\"" |>.length) - 1)
80-
"rewrite should touch only one rev line"
81-
-- Prepare: a lakefile that omits the requested dependency block.
82-
let missing := makeMultiline [
83-
"name = \"demo\"",
84-
"",
85-
"[[require]]",
86-
"name = \"mathlib\"",
87-
"rev = \"main\"",
88-
""
89-
]
90-
91-
-- Act: attempt the rewrite
92-
-- Asssert the missing dependency is rejected.
93-
match LakefileProcessor.rewriteContents missing "batteries" "oops" with
94-
| .ok _ => fail "missing dependency should be rejected"
95-
| .error _ => pure ()
96-
97-
-- Prepare: a lakefile with duplicate dependency blocks for the requested name.
98-
let duplicate := makeMultiline [
99-
"name = \"demo\"",
100-
"",
101-
"[[require]]",
102-
"name = \"batteries\"",
103-
"rev = \"old1\"",
104-
"",
105-
"[[require]]",
106-
"name = \"batteries\"",
107-
"rev = \"old2\"",
108-
""
109-
]
110-
111-
-- Act: attempt the rewrite
112-
-- Assert: duplicate dependency blocks are rejected.
113-
match LakefileProcessor.rewriteContents duplicate "batteries" "oops" with
114-
| .ok _ => fail "multiple matching dependency blocks should be rejected"
115-
| .error _ => pure ()
116-
-- Prepare: a matching dependency block without a rev line.
117-
let missingRev := makeMultiline [
118-
"name = \"demo\"",
119-
"",
120-
"[[require]]",
121-
"name = \"batteries\"",
122-
"git = \"https://github.com/leanprover-community/batteries.git\"",
123-
""
124-
]
125-
126-
-- Act: rewrite a dependency block that has no existing rev entry.
127-
let insertedRev ← IO.ofExcept <| LakefileProcessor.rewriteContents missingRev "batteries" "inserted"
128-
-- Assert: the rev is inserted into the block rather than causing an error.
129-
assertTrue (insertedRev.contains "rev = \"inserted\"")
130-
"rewrite should insert a rev entry when none exists"
131-
-- Prepare: a matching dependency block without a rev that is followed by another block.
132-
let missingRevMulti := makeMultiline [
133-
"name = \"demo\"",
134-
"",
135-
"[[require]]",
136-
"name = \"batteries\"",
137-
"git = \"https://github.com/leanprover-community/batteries.git\"",
138-
"",
139-
"[[require]]",
140-
"name = \"mathlib\"",
141-
"rev = \"keepme\"",
142-
""
143-
]
144-
145-
-- Act: insert a rev only into the target block when followed by another block.
146-
let insertedRevMulti ← IO.ofExcept <| LakefileProcessor.rewriteContents missingRevMulti "batteries" "inserted2"
147-
-- Assert: only the target block receives the inserted rev; the other block is untouched.
148-
assertTrue (insertedRevMulti.contains "rev = \"inserted2\"")
149-
"rewrite should insert a rev entry before the next [[require]] block"
150-
assertTrue (insertedRevMulti.contains "rev = \"keepme\"")
151-
"rewrite should leave other dependency revs untouched when inserting"
152-
-- Prepare: a CRLF-formatted lakefile with an inline rev comment.
153-
let crlf := "\r\n"
154-
let baseCrlf := String.intercalate crlf [
155-
"name = \"demo\"",
156-
"",
157-
"[[require]]",
158-
"name = \"batteries\"",
159-
"git = \"https://github.com/leanprover-community/batteries.git\"",
160-
"rev = \"old\" # trailing comment",
161-
""
162-
]
163-
164-
-- Act: rewrite the target dependency rev in the CRLF-formatted lakefile.
165-
let rewrittenCrlf ← IO.ofExcept <| LakefileProcessor.rewriteContents baseCrlf "batteries" "newcrlf"
166-
-- Assert: the CRLF rewrite preserves the inline suffix and line endings.
167-
assertTrue (rewrittenCrlf.contains s!"rev = \"newcrlf\" # trailing comment")
168-
"rewrite should preserve inline suffixes on the updated rev line"
169-
assertTrue (rewrittenCrlf.contains crlf)
170-
"rewrite should preserve CRLF line endings"
171-
17254
/-- Scenario: console color helpers only add ANSI escapes when enabled. -/
17355
private def «console color helper behavior» : IO Unit := do
17456
-- Act: evaluate the color helper cases
@@ -437,7 +319,6 @@ private def «bisect 2-element list is immediately resolved» : IO Unit := do
437319

438320
def suite : TestSuite := #[
439321
test_case «commit list normalization»,
440-
test_case «lakefile dependency rev rewriting»,
441322
test_case «console color helper behavior»,
442323
test_case «summary display formatting»,
443324
test_case «summary git check formatting»,

0 commit comments

Comments
 (0)