Skip to content

Commit 862777e

Browse files
authoredMar 7, 2025
test: add folding tests (#414)
Fixes #363
1 parent a68ebdd commit 862777e

File tree

6 files changed

+531
-0
lines changed

6 files changed

+531
-0
lines changed
 

‎server/src/e2e/suite/folding.test.ts

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
import * as vscode from "vscode"
2+
import * as assert from "node:assert"
3+
import {BaseTestSuite} from "./BaseTestSuite"
4+
import type {TestCase} from "./TestParser"
5+
6+
suite("Folding Test Suite", () => {
7+
const testSuite = new (class extends BaseTestSuite {
8+
protected runTest(testFile: string, testCase: TestCase): void {
9+
test(`Folding: ${testCase.name}`, async () => {
10+
await this.replaceDocumentText(testCase.input)
11+
12+
const foldingRanges = await vscode.commands.executeCommand<vscode.FoldingRange[]>(
13+
"vscode.executeFoldingRangeProvider",
14+
this.document.uri,
15+
)
16+
17+
if (foldingRanges.length <= 0) {
18+
throw new Error("No folding ranges found")
19+
}
20+
21+
const rangesInfo = foldingRanges
22+
.map(range => `[${range.start + 1}, ${range.end + 1}]`)
23+
.join(", ")
24+
25+
const lines = this.document.getText().split("\n")
26+
const startLines = new Set(foldingRanges.map(range => range.start))
27+
28+
lines.forEach((lineContent, index) => {
29+
if (startLines.has(index)) {
30+
lines[index] = lineContent.replace(/\s*$/, "...")
31+
}
32+
})
33+
34+
const actualText = `${rangesInfo}\n${lines.join("\n")}`.trimEnd()
35+
const expectedText = testCase.expected.trimEnd()
36+
37+
if (BaseTestSuite.UPDATE_SNAPSHOTS) {
38+
this.updates.push({
39+
filePath: testFile,
40+
testName: testCase.name,
41+
actual: actualText,
42+
})
43+
} else {
44+
assert.deepStrictEqual(actualText, expectedText)
45+
}
46+
})
47+
}
48+
})()
49+
50+
suiteSetup(async function () {
51+
this.timeout(10_000)
52+
await testSuite.suiteSetup()
53+
})
54+
55+
setup(async () => testSuite.setup())
56+
teardown(async () => testSuite.teardown())
57+
suiteTeardown(() => testSuite.suiteTeardown())
58+
59+
testSuite.runTestsFromDirectory("folding")
60+
})
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
========================================================================
2+
Assembly function with unknown instruction
3+
========================================================================
4+
asm fun foo() {
5+
10 PUSHINT2
6+
}
7+
------------------------------------------------------------------------
8+
[1, 2]
9+
asm fun foo() {...
10+
10 PUSHINT2
11+
}
12+
13+
========================================================================
14+
Big assembly function with PUSHCONT
15+
========================================================================
16+
asm fun send(params: SendParameters) {
17+
// Instructions are grouped, and the stack states they produce as a group are shown right after.
18+
// In the end, our message Cell should have the following TL-B structure:
19+
// message$_ {X:Type}
20+
// info:CommonMsgInfoRelaxed
21+
// init:(Maybe (Either StateInit ^StateInit))
22+
// body:(Either X ^X)
23+
// = MessageRelaxed X;
24+
25+
// Group 1: Rearrangements
26+
3 4 BLKSWAP
27+
s2 XCHG0
28+
// → Stack state
29+
// s0: `params.bounce`
30+
// s1: `params.to`
31+
// s2: `params.value`
32+
// s3: `params.data`
33+
// s4: `params.code`
34+
// s5: `params.body`
35+
// s6: `params.mode`
36+
// For brevity, the "params" prefix will be omitted from now on.
37+
38+
// Group 2: Storing the `bounce`, `to` and `value` into a Builder
39+
NEWC
40+
b{01} STSLICECONST // store tag = $0 and ihr_disabled = true
41+
1 STI // store `bounce`
42+
b{000} STSLICECONST // store bounced = false and src = addr_none
43+
STSLICE // store `to`
44+
SWAP
45+
STGRAMS // store `value`
46+
105 PUSHINT // 1 + 4 + 4 + 64 + 32
47+
STZEROES // store currency_collection, ihr_fee, fwd_fee, created_lt and created_at
48+
// → Stack state
49+
// s0: Builder
50+
// s1: `data`
51+
// s2: `code`
52+
// s3: `body`
53+
// s4: `mode`
54+
55+
// Group 3: Placing the Builder after code and data, then checking those for nullability
56+
s2 XCHG0
57+
DUP2
58+
ISNULL
59+
SWAP
60+
ISNULL
61+
MUL // note that -1 * -1 wraps back to -1
62+
// → Stack state
63+
// s0: -1 (true) if `data` and `code` are both null, 0 (false) otherwise
64+
// s1: `code`
65+
// s2: `data`
66+
// s3: Builder
67+
// s4: `body`
68+
// s5: `mode`
69+
70+
// Group 4: Left branch of the IFELSE, executed if s0 is -1 (true)
71+
<{
72+
DROP2 // drop `data` and `code`, since either of those is null
73+
NULL // push a single null
74+
SWAP // place Builder on top
75+
}> PUSHCONT
76+
77+
// Group 4: Right branch of the IFELSE, executed if s0 is 0 (false)
78+
<{
79+
// _ split_depth:(Maybe (## 5))
80+
// special:(Maybe TickTock)
81+
// code:(Maybe ^Cell)
82+
// data:(Maybe ^Cell)
83+
// library:(Maybe ^Cell)
84+
// = StateInit;
85+
NEWC // start composing StateInit
86+
b{00} STSLICECONST // split_depth and special
87+
STDICT // store code
88+
STDICT // store data
89+
b{0} STSLICECONST // store library
90+
ENDC // end composing StateInit
91+
SWAP // place Builder on top
92+
b{1} STSLICECONST // an extra bit for storing StateInit as ref
93+
}> PUSHCONT
94+
95+
// Group 4: IFELSE that does the branching shown above
96+
IFELSE
97+
// → Stack state
98+
// s0: Builder
99+
// s1: null or StateInit
100+
// s2: `body`
101+
// s3: `mode`
102+
103+
// Group 5: Finalizing the message
104+
STDICT // store StateInit
105+
STDICT // store `body` as ref with an extra Maybe bit, since `body` might be null
106+
ENDC
107+
// → Stack state
108+
// s0: Cell
109+
// s1: `mode`
110+
111+
// Group 6: Sending the message, with `mode` on top
112+
SWAP
113+
SENDRAWMSG // https://github.com/tact-lang/tact/issues/1558
114+
}
115+
------------------------------------------------------------------------
116+
[1, 98], [56, 59], [63, 77]
117+
asm fun send(params: SendParameters) {...
118+
// Instructions are grouped, and the stack states they produce as a group are shown right after.
119+
// In the end, our message Cell should have the following TL-B structure:
120+
// message$_ {X:Type}
121+
// info:CommonMsgInfoRelaxed
122+
// init:(Maybe (Either StateInit ^StateInit))
123+
// body:(Either X ^X)
124+
// = MessageRelaxed X;
125+
126+
// Group 1: Rearrangements
127+
3 4 BLKSWAP
128+
s2 XCHG0
129+
// → Stack state
130+
// s0: `params.bounce`
131+
// s1: `params.to`
132+
// s2: `params.value`
133+
// s3: `params.data`
134+
// s4: `params.code`
135+
// s5: `params.body`
136+
// s6: `params.mode`
137+
// For brevity, the "params" prefix will be omitted from now on.
138+
139+
// Group 2: Storing the `bounce`, `to` and `value` into a Builder
140+
NEWC
141+
b{01} STSLICECONST // store tag = $0 and ihr_disabled = true
142+
1 STI // store `bounce`
143+
b{000} STSLICECONST // store bounced = false and src = addr_none
144+
STSLICE // store `to`
145+
SWAP
146+
STGRAMS // store `value`
147+
105 PUSHINT // 1 + 4 + 4 + 64 + 32
148+
STZEROES // store currency_collection, ihr_fee, fwd_fee, created_lt and created_at
149+
// → Stack state
150+
// s0: Builder
151+
// s1: `data`
152+
// s2: `code`
153+
// s3: `body`
154+
// s4: `mode`
155+
156+
// Group 3: Placing the Builder after code and data, then checking those for nullability
157+
s2 XCHG0
158+
DUP2
159+
ISNULL
160+
SWAP
161+
ISNULL
162+
MUL // note that -1 * -1 wraps back to -1
163+
// → Stack state
164+
// s0: -1 (true) if `data` and `code` are both null, 0 (false) otherwise
165+
// s1: `code`
166+
// s2: `data`
167+
// s3: Builder
168+
// s4: `body`
169+
// s5: `mode`
170+
171+
// Group 4: Left branch of the IFELSE, executed if s0 is -1 (true)
172+
<{...
173+
DROP2 // drop `data` and `code`, since either of those is null
174+
NULL // push a single null
175+
SWAP // place Builder on top
176+
}> PUSHCONT
177+
178+
// Group 4: Right branch of the IFELSE, executed if s0 is 0 (false)
179+
<{...
180+
// _ split_depth:(Maybe (## 5))
181+
// special:(Maybe TickTock)
182+
// code:(Maybe ^Cell)
183+
// data:(Maybe ^Cell)
184+
// library:(Maybe ^Cell)
185+
// = StateInit;
186+
NEWC // start composing StateInit
187+
b{00} STSLICECONST // split_depth and special
188+
STDICT // store code
189+
STDICT // store data
190+
b{0} STSLICECONST // store library
191+
ENDC // end composing StateInit
192+
SWAP // place Builder on top
193+
b{1} STSLICECONST // an extra bit for storing StateInit as ref
194+
}> PUSHCONT
195+
196+
// Group 4: IFELSE that does the branching shown above
197+
IFELSE
198+
// → Stack state
199+
// s0: Builder
200+
// s1: null or StateInit
201+
// s2: `body`
202+
// s3: `mode`
203+
204+
// Group 5: Finalizing the message
205+
STDICT // store StateInit
206+
STDICT // store `body` as ref with an extra Maybe bit, since `body` might be null
207+
ENDC
208+
// → Stack state
209+
// s0: Cell
210+
// s1: `mode`
211+
212+
// Group 6: Sending the message, with `mode` on top
213+
SWAP
214+
SENDRAWMSG // https://github.com/tact-lang/tact/issues/1558
215+
}

0 commit comments

Comments
 (0)