Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

test: Add folding tests #414

Merged
merged 1 commit into from
Mar 7, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
60 changes: 60 additions & 0 deletions server/src/e2e/suite/folding.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import * as vscode from "vscode"
import * as assert from "node:assert"
import {BaseTestSuite} from "./BaseTestSuite"
import type {TestCase} from "./TestParser"

suite("Folding Test Suite", () => {
const testSuite = new (class extends BaseTestSuite {
protected runTest(testFile: string, testCase: TestCase): void {
test(`Folding: ${testCase.name}`, async () => {
await this.replaceDocumentText(testCase.input)

const foldingRanges = await vscode.commands.executeCommand<vscode.FoldingRange[]>(
"vscode.executeFoldingRangeProvider",
this.document.uri,
)

if (foldingRanges.length <= 0) {
throw new Error("No folding ranges found")
}

const rangesInfo = foldingRanges
.map(range => `[${range.start + 1}, ${range.end + 1}]`)
.join(", ")

const lines = this.document.getText().split("\n")
const startLines = new Set(foldingRanges.map(range => range.start))

lines.forEach((lineContent, index) => {
if (startLines.has(index)) {
lines[index] = lineContent.replace(/\s*$/, "...")
}
})

const actualText = `${rangesInfo}\n${lines.join("\n")}`.trimEnd()
const expectedText = testCase.expected.trimEnd()

if (BaseTestSuite.UPDATE_SNAPSHOTS) {
this.updates.push({
filePath: testFile,
testName: testCase.name,
actual: actualText,
})
} else {
assert.deepStrictEqual(actualText, expectedText)
}
})
}
})()

suiteSetup(async function () {
this.timeout(10_000)
await testSuite.suiteSetup()
})

setup(async () => testSuite.setup())
teardown(async () => testSuite.teardown())
suiteTeardown(() => testSuite.suiteTeardown())

testSuite.runTestsFromDirectory("folding")
})
215 changes: 215 additions & 0 deletions server/src/e2e/suite/testcases/folding/assembly.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,215 @@
========================================================================
Assembly function with unknown instruction
========================================================================
asm fun foo() {
10 PUSHINT2
}
------------------------------------------------------------------------
[1, 2]
asm fun foo() {...
10 PUSHINT2
}

========================================================================
Big assembly function with PUSHCONT
========================================================================
asm fun send(params: SendParameters) {
// Instructions are grouped, and the stack states they produce as a group are shown right after.
// In the end, our message Cell should have the following TL-B structure:
// message$_ {X:Type}
// info:CommonMsgInfoRelaxed
// init:(Maybe (Either StateInit ^StateInit))
// body:(Either X ^X)
// = MessageRelaxed X;

// Group 1: Rearrangements
3 4 BLKSWAP
s2 XCHG0
// → Stack state
// s0: `params.bounce`
// s1: `params.to`
// s2: `params.value`
// s3: `params.data`
// s4: `params.code`
// s5: `params.body`
// s6: `params.mode`
// For brevity, the "params" prefix will be omitted from now on.

// Group 2: Storing the `bounce`, `to` and `value` into a Builder
NEWC
b{01} STSLICECONST // store tag = $0 and ihr_disabled = true
1 STI // store `bounce`
b{000} STSLICECONST // store bounced = false and src = addr_none
STSLICE // store `to`
SWAP
STGRAMS // store `value`
105 PUSHINT // 1 + 4 + 4 + 64 + 32
STZEROES // store currency_collection, ihr_fee, fwd_fee, created_lt and created_at
// → Stack state
// s0: Builder
// s1: `data`
// s2: `code`
// s3: `body`
// s4: `mode`

// Group 3: Placing the Builder after code and data, then checking those for nullability
s2 XCHG0
DUP2
ISNULL
SWAP
ISNULL
MUL // note that -1 * -1 wraps back to -1
// → Stack state
// s0: -1 (true) if `data` and `code` are both null, 0 (false) otherwise
// s1: `code`
// s2: `data`
// s3: Builder
// s4: `body`
// s5: `mode`

// Group 4: Left branch of the IFELSE, executed if s0 is -1 (true)
<{
DROP2 // drop `data` and `code`, since either of those is null
NULL // push a single null
SWAP // place Builder on top
}> PUSHCONT

// Group 4: Right branch of the IFELSE, executed if s0 is 0 (false)
<{
// _ split_depth:(Maybe (## 5))
// special:(Maybe TickTock)
// code:(Maybe ^Cell)
// data:(Maybe ^Cell)
// library:(Maybe ^Cell)
// = StateInit;
NEWC // start composing StateInit
b{00} STSLICECONST // split_depth and special
STDICT // store code
STDICT // store data
b{0} STSLICECONST // store library
ENDC // end composing StateInit
SWAP // place Builder on top
b{1} STSLICECONST // an extra bit for storing StateInit as ref
}> PUSHCONT

// Group 4: IFELSE that does the branching shown above
IFELSE
// → Stack state
// s0: Builder
// s1: null or StateInit
// s2: `body`
// s3: `mode`

// Group 5: Finalizing the message
STDICT // store StateInit
STDICT // store `body` as ref with an extra Maybe bit, since `body` might be null
ENDC
// → Stack state
// s0: Cell
// s1: `mode`

// Group 6: Sending the message, with `mode` on top
SWAP
SENDRAWMSG // https://github.com/tact-lang/tact/issues/1558
}
------------------------------------------------------------------------
[1, 98], [56, 59], [63, 77]
asm fun send(params: SendParameters) {...
// Instructions are grouped, and the stack states they produce as a group are shown right after.
// In the end, our message Cell should have the following TL-B structure:
// message$_ {X:Type}
// info:CommonMsgInfoRelaxed
// init:(Maybe (Either StateInit ^StateInit))
// body:(Either X ^X)
// = MessageRelaxed X;

// Group 1: Rearrangements
3 4 BLKSWAP
s2 XCHG0
// → Stack state
// s0: `params.bounce`
// s1: `params.to`
// s2: `params.value`
// s3: `params.data`
// s4: `params.code`
// s5: `params.body`
// s6: `params.mode`
// For brevity, the "params" prefix will be omitted from now on.

// Group 2: Storing the `bounce`, `to` and `value` into a Builder
NEWC
b{01} STSLICECONST // store tag = $0 and ihr_disabled = true
1 STI // store `bounce`
b{000} STSLICECONST // store bounced = false and src = addr_none
STSLICE // store `to`
SWAP
STGRAMS // store `value`
105 PUSHINT // 1 + 4 + 4 + 64 + 32
STZEROES // store currency_collection, ihr_fee, fwd_fee, created_lt and created_at
// → Stack state
// s0: Builder
// s1: `data`
// s2: `code`
// s3: `body`
// s4: `mode`

// Group 3: Placing the Builder after code and data, then checking those for nullability
s2 XCHG0
DUP2
ISNULL
SWAP
ISNULL
MUL // note that -1 * -1 wraps back to -1
// → Stack state
// s0: -1 (true) if `data` and `code` are both null, 0 (false) otherwise
// s1: `code`
// s2: `data`
// s3: Builder
// s4: `body`
// s5: `mode`

// Group 4: Left branch of the IFELSE, executed if s0 is -1 (true)
<{...
DROP2 // drop `data` and `code`, since either of those is null
NULL // push a single null
SWAP // place Builder on top
}> PUSHCONT

// Group 4: Right branch of the IFELSE, executed if s0 is 0 (false)
<{...
// _ split_depth:(Maybe (## 5))
// special:(Maybe TickTock)
// code:(Maybe ^Cell)
// data:(Maybe ^Cell)
// library:(Maybe ^Cell)
// = StateInit;
NEWC // start composing StateInit
b{00} STSLICECONST // split_depth and special
STDICT // store code
STDICT // store data
b{0} STSLICECONST // store library
ENDC // end composing StateInit
SWAP // place Builder on top
b{1} STSLICECONST // an extra bit for storing StateInit as ref
}> PUSHCONT

// Group 4: IFELSE that does the branching shown above
IFELSE
// → Stack state
// s0: Builder
// s1: null or StateInit
// s2: `body`
// s3: `mode`

// Group 5: Finalizing the message
STDICT // store StateInit
STDICT // store `body` as ref with an extra Maybe bit, since `body` might be null
ENDC
// → Stack state
// s0: Cell
// s1: `mode`

// Group 6: Sending the message, with `mode` on top
SWAP
SENDRAWMSG // https://github.com/tact-lang/tact/issues/1558
}
Loading