diff --git a/Makefile b/Makefile index 42df8a1a4..dfe6b3771 100644 --- a/Makefile +++ b/Makefile @@ -37,6 +37,7 @@ endif .PHONY: ci ci: generate test coverage +.PHONY: $(BINARY) $(BINARY): CGO_ENABLED=1 \ CGO_CFLAGS="-O2 -D__BLST_PORTABLE__ -std=gnu11" \ diff --git a/internal/cadence/lint_test.go b/internal/cadence/lint_test.go index 29f3ba9f4..8523513df 100644 --- a/internal/cadence/lint_test.go +++ b/internal/cadence/lint_test.go @@ -45,9 +45,8 @@ func Test_Lint(t *testing.T) { }) t.Run("lints file with no issues", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "NoError.cdc") require.NoError(t, err) @@ -67,9 +66,8 @@ func Test_Lint(t *testing.T) { }) t.Run("lints file with import", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "foo/WithImports.cdc") require.NoError(t, err) @@ -90,9 +88,8 @@ func Test_Lint(t *testing.T) { }) t.Run("lints multiple files", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "NoError.cdc", "foo/WithImports.cdc") require.NoError(t, err) @@ -116,9 +113,8 @@ func Test_Lint(t *testing.T) { }) t.Run("lints file with warning", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "LintWarning.cdc") require.NoError(t, err) @@ -148,9 +144,8 @@ func Test_Lint(t *testing.T) { }) t.Run("lints file with error", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "LintError.cdc") require.NoError(t, err) @@ -190,9 +185,8 @@ func Test_Lint(t *testing.T) { }) t.Run("generates synthetic replacement for replacement category diagnostics", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "ReplacementHint.cdc") require.NoError(t, err) @@ -217,9 +211,8 @@ func Test_Lint(t *testing.T) { }) t.Run("linter resolves imports from flowkit state", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "WithFlowkitImport.cdc") require.NoError(t, err) @@ -239,9 +232,8 @@ func Test_Lint(t *testing.T) { }) t.Run("resolves stdlib imports contracts", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "StdlibImportsContract.cdc") require.NoError(t, err) @@ -273,9 +265,8 @@ func Test_Lint(t *testing.T) { }) t.Run("resolves stdlib imports transactions", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "StdlibImportsTransaction.cdc") require.NoError(t, err) @@ -307,9 +298,8 @@ func Test_Lint(t *testing.T) { }) t.Run("resolves stdlib imports scripts", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "StdlibImportsScript.cdc") require.NoError(t, err) @@ -329,9 +319,8 @@ func Test_Lint(t *testing.T) { }) t.Run("resolves stdlib imports Crypto", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "StdlibImportsCrypto.cdc") require.NoError(t, err) @@ -351,9 +340,8 @@ func Test_Lint(t *testing.T) { }) t.Run("resolves nested imports when contract imported by name", func(t *testing.T) { - t.Parallel() - state := setupMockState(t) + t.Parallel() results, err := lintFiles(state, "TransactionImportingContractWithNestedImports.cdc") require.NoError(t, err) @@ -373,9 +361,8 @@ func Test_Lint(t *testing.T) { }) t.Run("allows access(account) when contracts on same account", func(t *testing.T) { - t.Parallel() - state := setupMockStateWithAccountAccess(t) + t.Parallel() results, err := lintFiles(state, "ContractA.cdc") require.NoError(t, err) @@ -396,9 +383,8 @@ func Test_Lint(t *testing.T) { }) t.Run("denies access(account) when contracts on different accounts", func(t *testing.T) { - t.Parallel() - state := setupMockStateWithAccountAccess(t) + t.Parallel() results, err := lintFiles(state, "ContractC.cdc") require.NoError(t, err) @@ -412,9 +398,8 @@ func Test_Lint(t *testing.T) { }) t.Run("allows access(account) when dependencies on same account (peak-money repro)", func(t *testing.T) { - t.Parallel() - state := setupMockStateWithDependencies(t) + t.Parallel() results, err := lintFiles(state, "imports/testaddr/DepA.cdc") require.NoError(t, err) @@ -435,9 +420,8 @@ func Test_Lint(t *testing.T) { }) t.Run("allows access(account) when dependencies have Source but no Aliases", func(t *testing.T) { - t.Parallel() - state := setupMockStateWithSourceOnly(t) + t.Parallel() // Verify that AddDependencyAsContract automatically adds Source to Aliases sourceAContract, _ := state.Contracts().ByName("SourceA") diff --git a/internal/test/bench_test.go b/internal/test/bench_test.go new file mode 100644 index 000000000..9fdf63079 --- /dev/null +++ b/internal/test/bench_test.go @@ -0,0 +1,56 @@ +/* + * Flow CLI + * + * Copyright Flow Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package test + +import ( + "fmt" + "testing" + + "github.com/onflow/flow-go-sdk/crypto" + "github.com/onflow/flowkit/v2" + "github.com/onflow/flowkit/v2/accounts" + "github.com/onflow/flowkit/v2/tests" +) + +func buildTestFiles(n int) map[string][]byte { + script := tests.TestScriptSimple + files := make(map[string][]byte, n) + for i := range n { + files[fmt.Sprintf("test_%02d_%s", i, script.Filename)] = script.Source + } + return files +} + +func BenchmarkTestCode_NFiles(b *testing.B) { + rw, _ := tests.ReaderWriter() + state, err := flowkit.Init(rw) + if err != nil { + b.Fatal(err) + } + emulatorAccount, _ := accounts.NewEmulatorAccount(rw, crypto.ECDSA_P256, crypto.SHA3_256, "") + state.Accounts().AddOrUpdate(emulatorAccount) + testFiles := buildTestFiles(10) + + for b.Loop() { + _, err := testCode(testFiles, state, flagsTests{}) + if err != nil { + b.Fatal(err) + } + } +} diff --git a/internal/test/test.go b/internal/test/test.go index 382879556..f00e8afd8 100644 --- a/internal/test/test.go +++ b/internal/test/test.go @@ -28,6 +28,7 @@ import ( "regexp" goRuntime "runtime" "strings" + "sync" cdcTests "github.com/onflow/cadence-tools/test" "github.com/onflow/cadence/common" @@ -77,6 +78,7 @@ type flagsTests struct { Random bool `default:"false" flag:"random" info:"Use the random flag to execute test cases randomly"` Seed int64 `default:"0" flag:"seed" info:"Use the seed flag to manipulate random execution of test cases"` Name string `default:"" flag:"name" info:"Use the name flag to run only tests that match the given name"` + Jobs int `default:"0" flag:"jobs" info:"Maximum number of test files to run concurrently (default: number of CPU cores)"` // Fork mode flags Fork string // Use definition in init() @@ -190,41 +192,6 @@ func testCode( // Track network resolutions per file for pragma-based fork detection // Map: filename -> resolved network name fileNetworkResolutions := make(map[string]string) - var currentTestFile string - - // Resolve network labels using flow.json state - resolveNetworkFromState := func(label string) (string, bool) { - normalizedLabel := strings.ToLower(strings.TrimSpace(label)) - network, err := state.Networks().ByName(normalizedLabel) - if err != nil || network == nil { - return "", false - } - - // If network has a fork, resolve the fork network's host - host := strings.TrimSpace(network.Host) - if network.Fork != "" { - forkName := strings.ToLower(strings.TrimSpace(network.Fork)) - forkNetwork, err := state.Networks().ByName(forkName) - if err != nil { - return "", false - } - host = strings.TrimSpace(forkNetwork.Host) - } - - if host == "" { - return "", false - } - - // Track network resolution for current test file (indicates pragma-based fork usage) - // Only track if it's not the default "testing" network - if currentTestFile != "" && normalizedLabel != "testing" { - if _, exists := fileNetworkResolutions[currentTestFile]; !exists { - fileNetworkResolutions[currentTestFile] = normalizedLabel - } - } - - return host, true - } // Configure fork mode if requested var effectiveForkHost string @@ -300,92 +267,173 @@ func testCode( seed = int64(rand.Intn(150000)) } - testResults := make(map[string]cdcTests.Results, 0) - exitCode := 0 + // Limit concurrency to flags.Jobs, defaulting to number of CPU cores. + jobs := flags.Jobs + if jobs <= 0 { + jobs = goRuntime.NumCPU() + } + sem := make(chan struct{}, jobs) + + type fileResult struct { + scriptPath string + results cdcTests.Results + networkResolution string + err error + } + + resultCh := make(chan fileResult, len(testFiles)) + var wg sync.WaitGroup + for scriptPath, code := range testFiles { - // Set current test file for network resolution tracking - currentTestFile = scriptPath - - // Create a new test runner per file to ensure complete isolation. - // Each file gets its own runner with its own backend state. - fileRunner := cdcTests.NewTestRunner(). - WithLogger(logger). - WithNetworkResolver(resolveNetworkFromState). - WithNetworkLabel(networkLabel). - WithImportResolver(importResolver(scriptPath, state)). - WithFileResolver(fileResolver(scriptPath, state)). - WithContractAddressResolver(func(network string, contractName string) (common.Address, error) { - contractsByName := make(map[string]config.Contract) - for _, c := range *state.Contracts() { - contractsByName[c.Name] = c + wg.Add(1) + go func(scriptPath string, code []byte) { + defer wg.Done() + sem <- struct{}{} + defer func() { <-sem }() + + // Each file gets its own resolver so network resolution tracking is per-file. + var resolvedNetwork string + resolveNetworkFromState := func(label string) (string, bool) { + normalizedLabel := strings.ToLower(strings.TrimSpace(label)) + network, err := state.Networks().ByName(normalizedLabel) + if err != nil || network == nil { + return "", false } - contract, exists := contractsByName[contractName] - if !exists { - return common.Address{}, fmt.Errorf("contract not found: %s", contractName) + // If network has a fork, resolve the fork network's host + host := strings.TrimSpace(network.Host) + if network.Fork != "" { + forkName := strings.ToLower(strings.TrimSpace(network.Fork)) + forkNetwork, err := state.Networks().ByName(forkName) + if err != nil { + return "", false + } + host = strings.TrimSpace(forkNetwork.Host) } - alias := contract.Aliases.ByNetwork(network) - if alias != nil { - return common.Address(alias.Address), nil + if host == "" { + return "", false } - // Fallback to fork network if configured - networkConfig, err := state.Networks().ByName(network) - if err == nil && networkConfig != nil && networkConfig.Fork != "" { - forkAlias := contract.Aliases.ByNetwork(networkConfig.Fork) - if forkAlias != nil { - return common.Address(forkAlias.Address), nil - } + // Track network resolution for current test file (indicates pragma-based fork usage) + // Only track if it's not the default "testing" network + if resolvedNetwork == "" && normalizedLabel != "testing" { + resolvedNetwork = normalizedLabel } - return common.Address{}, fmt.Errorf("no address for contract %s on network %s", contractName, network) - }) + return host, true + } - if forkCfg != nil { - fileRunner = fileRunner.WithFork(*forkCfg) - } - if coverageReport != nil { - fileRunner = fileRunner.WithCoverageReport(coverageReport) - } - if seed > 0 { - fileRunner = fileRunner.WithRandomSeed(seed) - } + // Create a new test runner per file to ensure complete isolation. + // Each file gets its own runner with its own backend state. + fileRunner := cdcTests.NewTestRunner(). + WithLogger(logger). + WithNetworkResolver(resolveNetworkFromState). + WithNetworkLabel(networkLabel). + WithImportResolver(importResolver(scriptPath, state)). + WithFileResolver(fileResolver(scriptPath, state)). + WithContractAddressResolver(func(network string, contractName string) (common.Address, error) { + contractsByName := make(map[string]config.Contract) + for _, c := range *state.Contracts() { + contractsByName[c.Name] = c + } + + contract, exists := contractsByName[contractName] + if !exists { + return common.Address{}, fmt.Errorf("contract not found: %s", contractName) + } + + alias := contract.Aliases.ByNetwork(network) + if alias != nil { + return common.Address(alias.Address), nil + } - if flags.Name != "" { - testFunctions, err := fileRunner.GetTests(string(code)) - if err != nil { - return nil, err + // Fallback to fork network if configured + networkConfig, err := state.Networks().ByName(network) + if err == nil && networkConfig != nil && networkConfig.Fork != "" { + forkAlias := contract.Aliases.ByNetwork(networkConfig.Fork) + if forkAlias != nil { + return common.Address(forkAlias.Address), nil + } + } + + return common.Address{}, fmt.Errorf("no address for contract %s on network %s", contractName, network) + }) + + if forkCfg != nil { + fileRunner = fileRunner.WithFork(*forkCfg) + } + if coverageReport != nil { + fileRunner = fileRunner.WithCoverageReport(coverageReport) + } + if seed > 0 { + fileRunner = fileRunner.WithRandomSeed(seed) } - for _, testFunction := range testFunctions { - if testFunction != flags.Name { - continue - } + var fileResults cdcTests.Results + var runErr error - result, err := fileRunner.RunTest(string(code), flags.Name) + if flags.Name != "" { + testFunctions, err := fileRunner.GetTests(string(code)) if err != nil { - return nil, err + resultCh <- fileResult{scriptPath: scriptPath, err: err} + return + } + + for _, testFunction := range testFunctions { + if testFunction != flags.Name { + continue + } + + r, err := fileRunner.RunTest(string(code), flags.Name) + if err != nil { + runErr = err + break + } + fileResults = []cdcTests.Result{*r} } - testResults[scriptPath] = []cdcTests.Result{*result} + } else { + fileResults, runErr = fileRunner.RunTests(string(code)) } - } else { - results, err := fileRunner.RunTests(string(code)) - if err != nil { - return nil, err + + resultCh <- fileResult{ + scriptPath: scriptPath, + results: fileResults, + networkResolution: resolvedNetwork, + err: runErr, } - testResults[scriptPath] = results - } + }(scriptPath, code) + } + + go func() { + wg.Wait() + close(resultCh) + }() - for _, result := range testResults[scriptPath] { - if result.Error != nil { + testResults := make(map[string]cdcTests.Results, 0) + exitCode := 0 + var firstErr error + + for r := range resultCh { + if r.err != nil && firstErr == nil { + firstErr = r.err + } + if r.results != nil { + testResults[r.scriptPath] = r.results + } + if r.networkResolution != "" { + fileNetworkResolutions[r.scriptPath] = r.networkResolution + } + for _, res := range testResults[r.scriptPath] { + if res.Error != nil { exitCode = 1 break } } + } - // Clear current test file after processing - currentTestFile = "" + if firstErr != nil { + return nil, firstErr } // Track fork test usage metrics - aggregate into single event diff --git a/internal/test/test_test.go b/internal/test/test_test.go index 71440ff24..82c9efdec 100644 --- a/internal/test/test_test.go +++ b/internal/test/test_test.go @@ -45,9 +45,8 @@ func TestExecutingTests(t *testing.T) { }} t.Run("simple", func(t *testing.T) { - t.Parallel() - _, state, _ := util.TestMocks(t) + t.Parallel() script := tests.TestScriptSimple testFiles := map[string][]byte{ @@ -61,9 +60,8 @@ func TestExecutingTests(t *testing.T) { }) t.Run("simple failing", func(t *testing.T) { - t.Parallel() - _, state, _ := util.TestMocks(t) + t.Parallel() script := tests.TestScriptSimpleFailing testFiles := map[string][]byte{ @@ -81,16 +79,14 @@ func TestExecutingTests(t *testing.T) { }) t.Run("with import", func(t *testing.T) { - t.Parallel() - _, state, _ := util.TestMocks(t) - c := config.Contract{ Name: tests.ContractHelloString.Name, Location: tests.ContractHelloString.Filename, Aliases: aliases, } state.Contracts().AddOrUpdate(c) + t.Parallel() // Execute script script := tests.TestScriptWithImport @@ -105,8 +101,6 @@ func TestExecutingTests(t *testing.T) { }) t.Run("with relative imports", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) readerWriter := state.ReaderWriter() @@ -133,6 +127,7 @@ func TestExecutingTests(t *testing.T) { Aliases: aliases, } state.Contracts().AddOrUpdate(contractFoo) + t.Parallel() // Execute script script := tests.TestScriptWithRelativeImports @@ -147,9 +142,8 @@ func TestExecutingTests(t *testing.T) { }) t.Run("with helper script import", func(t *testing.T) { - t.Parallel() - _, state, _ := util.TestMocks(t) + t.Parallel() // Execute script script := tests.TestScriptWithHelperImport @@ -164,10 +158,9 @@ func TestExecutingTests(t *testing.T) { }) t.Run("with missing contract in config", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) + t.Parallel() // Execute script script := tests.TestScriptWithMissingContract @@ -185,11 +178,8 @@ func TestExecutingTests(t *testing.T) { }) t.Run("with missing testing alias in config", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) - c := config.Contract{ Name: tests.ContractHelloString.Name, Location: tests.ContractHelloString.Filename, @@ -199,6 +189,7 @@ func TestExecutingTests(t *testing.T) { }}, } state.Contracts().AddOrUpdate(c) + t.Parallel() // Execute script script := tests.TestScriptWithImport @@ -216,11 +207,8 @@ func TestExecutingTests(t *testing.T) { }) t.Run("without testing alias for common contracts", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) - c := config.Contract{ Name: tests.ContractHelloString.Name, Location: tests.ContractHelloString.Filename, @@ -234,6 +222,7 @@ func TestExecutingTests(t *testing.T) { Location: "cadence/contracts/FungibleToken.cdc", } state.Contracts().AddOrUpdate(fungibleToken) + t.Parallel() // Execute script script := tests.TestScriptWithImport @@ -246,15 +235,13 @@ func TestExecutingTests(t *testing.T) { }) t.Run("with file read", func(t *testing.T) { - t.Parallel() - _, state, rw := util.TestMocks(t) - _ = rw.WriteFile( tests.SomeFile.Filename, tests.SomeFile.Source, os.ModeTemporary, ) + t.Parallel() // Execute script script := tests.TestScriptWithFileRead @@ -269,16 +256,14 @@ func TestExecutingTests(t *testing.T) { }) t.Run("with code coverage", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) - state.Contracts().AddOrUpdate(config.Contract{ Name: tests.ContractFooCoverage.Name, Location: tests.ContractFooCoverage.Filename, Aliases: aliases, }) + t.Parallel() // Execute script script := tests.TestScriptWithCoverage @@ -397,16 +382,14 @@ func TestExecutingTests(t *testing.T) { }) t.Run("with code coverage for contracts only", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) - state.Contracts().AddOrUpdate(config.Contract{ Name: tests.ContractFooCoverage.Name, Location: tests.ContractFooCoverage.Filename, Aliases: aliases, }) + t.Parallel() // Execute script script := tests.TestScriptWithCoverage @@ -523,16 +506,14 @@ func TestExecutingTests(t *testing.T) { }) t.Run("with random test case execution", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) - state.Contracts().AddOrUpdate(config.Contract{ Name: tests.ContractFooCoverage.Name, Location: tests.ContractFooCoverage.Filename, Aliases: aliases, }) + t.Parallel() // Execute script script := tests.TestScriptWithCoverage @@ -558,16 +539,14 @@ func TestExecutingTests(t *testing.T) { }) t.Run("with input seed for test case execution", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) - state.Contracts().AddOrUpdate(config.Contract{ Name: tests.ContractFooCoverage.Name, Location: tests.ContractFooCoverage.Filename, Aliases: aliases, }) + t.Parallel() // Execute script script := tests.TestScriptWithCoverage @@ -607,16 +586,14 @@ Seed: 1521 }) t.Run("with JSON output", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) - state.Contracts().AddOrUpdate(config.Contract{ Name: tests.ContractFooCoverage.Name, Location: tests.ContractFooCoverage.Filename, Aliases: aliases, }) + t.Parallel() // Execute script script := tests.TestScriptWithCoverage @@ -660,10 +637,9 @@ Seed: 1521 }) t.Run("run specific test case by name", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) + t.Parallel() // Execute script script := tests.TestScriptSimple @@ -689,10 +665,9 @@ Seed: 1521 }) t.Run("run specific test case by name multiple files", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) + t.Parallel() scriptPassing := tests.TestScriptSimple scriptFailing := tests.TestScriptSimpleFailing @@ -730,10 +705,9 @@ Seed: 1521 }) t.Run("run specific test case by name will do nothing if not found", func(t *testing.T) { - t.Parallel() - // Setup _, state, _ := util.TestMocks(t) + t.Parallel() // Execute script script := tests.TestScriptSimple @@ -760,7 +734,6 @@ func TestForkMode_UsesMainnetAliases(t *testing.T) { if os.Getenv("SKIP_NETWORK_TESTS") != "" { t.Skip("skipping network-dependent test") } - t.Parallel() _, state, _ := util.TestMocks(t) @@ -787,6 +760,7 @@ func TestForkMode_UsesMainnetAliases(t *testing.T) { Aliases: mainnetAliases, } state.Contracts().AddOrUpdate(c) + t.Parallel() // Test script that deploys and uses the contract testScript := []byte(` @@ -799,7 +773,7 @@ func TestForkMode_UsesMainnetAliases(t *testing.T) { arguments: [] ) Test.expect(err, Test.beNil()) - + // Verify the contract deployed and works let script = "import TestContract from 0x1654653399040a61\naccess(all) fun main(): Int { return TestContract.getValue() }" let result = Test.executeScript(script, []) @@ -828,7 +802,6 @@ func TestForkMode_UsesTestnetAliasesExplicit(t *testing.T) { if os.Getenv("SKIP_NETWORK_TESTS") != "" { t.Skip("skipping network-dependent test") } - t.Parallel() _, state, _ := util.TestMocks(t) @@ -855,6 +828,7 @@ func TestForkMode_UsesTestnetAliasesExplicit(t *testing.T) { Aliases: testnetAliases, } state.Contracts().AddOrUpdate(c) + t.Parallel() // Test script that deploys and uses the contract testScript := []byte(` @@ -867,7 +841,7 @@ func TestForkMode_UsesTestnetAliasesExplicit(t *testing.T) { arguments: [] ) Test.expect(err, Test.beNil()) - + // Verify the contract deployed and works let script = "import TestContract from 0x7e60df042a9c0868\naccess(all) fun main(): String { return TestContract.getValue() }" let result = Test.executeScript(script, []) @@ -893,9 +867,8 @@ func TestForkMode_UsesTestnetAliasesExplicit(t *testing.T) { } func TestForkMode_AutodetectFailureRequiresExplicitNetwork(t *testing.T) { - t.Parallel() - _, state, _ := util.TestMocks(t) + t.Parallel() // No network hints in URL; expect early error flags := flagsTests{ @@ -911,7 +884,6 @@ func TestNetworkForkResolution_Success(t *testing.T) { if os.Getenv("SKIP_NETWORK_TESTS") != "" { t.Skip("skipping network-dependent test") } - t.Parallel() _, state, _ := util.TestMocks(t) @@ -926,6 +898,7 @@ func TestNetworkForkResolution_Success(t *testing.T) { Name: "mainnet-fork", Fork: "mainnet", }) + t.Parallel() // Create a simple test that uses the test_fork pragma testScript := []byte(` @@ -950,8 +923,6 @@ access(all) fun testSimple() { } func TestNetworkForkResolution_ForkNetworkNotFound(t *testing.T) { - t.Parallel() - _, state, _ := util.TestMocks(t) // Add mainnet-fork that references non-existent network @@ -959,6 +930,7 @@ func TestNetworkForkResolution_ForkNetworkNotFound(t *testing.T) { Name: "mainnet-fork", Fork: "nonexistent", }) + t.Parallel() // Create a simple test that uses the fork network testScript := []byte(` @@ -982,8 +954,6 @@ access(all) fun testSimple() { } func TestNetworkForkResolution_ForkNetworkHasNoHost(t *testing.T) { - t.Parallel() - _, state, _ := util.TestMocks(t) // Add mainnet network with no host @@ -996,6 +966,7 @@ func TestNetworkForkResolution_ForkNetworkHasNoHost(t *testing.T) { Name: "mainnet-fork", Fork: "mainnet", }) + t.Parallel() // Create a simple test that uses the fork network testScript := []byte(` @@ -1023,7 +994,6 @@ func TestNetworkForkResolution_WithOwnHost(t *testing.T) { if os.Getenv("SKIP_NETWORK_TESTS") != "" { t.Skip("skipping network-dependent test") } - t.Parallel() _, state, _ := util.TestMocks(t) @@ -1040,6 +1010,7 @@ func TestNetworkForkResolution_WithOwnHost(t *testing.T) { Host: "127.0.0.1:3569", Fork: "mainnet", }) + t.Parallel() // Create a simple test that uses the fork network testScript := []byte(` @@ -1067,7 +1038,6 @@ func TestContractAddressForkResolution_UsesMainnetForkFirst(t *testing.T) { if os.Getenv("SKIP_NETWORK_TESTS") != "" { t.Skip("skipping network-dependent test") } - t.Parallel() _, state, _ := util.TestMocks(t) @@ -1105,6 +1075,7 @@ access(all) contract TestContract { }, } state.Contracts().AddOrUpdate(c) + t.Parallel() testScript := []byte(` #test_fork(network: "mainnet-fork", height: nil) @@ -1134,7 +1105,6 @@ func TestContractAddressForkResolution_FallbackToMainnet(t *testing.T) { if os.Getenv("SKIP_NETWORK_TESTS") != "" { t.Skip("skipping network-dependent test") } - t.Parallel() _, state, _ := util.TestMocks(t) @@ -1172,6 +1142,7 @@ access(all) contract TestContract { }, } state.Contracts().AddOrUpdate(c) + t.Parallel() testScript := []byte(` #test_fork(network: "mainnet-fork", height: nil) @@ -1201,7 +1172,6 @@ func TestContractAddressForkResolution_PrioritizesForkOverParent(t *testing.T) { if os.Getenv("SKIP_NETWORK_TESTS") != "" { t.Skip("skipping network-dependent test") } - t.Parallel() _, state, _ := util.TestMocks(t) @@ -1245,6 +1215,7 @@ access(all) contract TestContract { }, } state.Contracts().AddOrUpdate(c) + t.Parallel() // Should use the mainnet-fork address (0xf233dcee88fe0abe), not mainnet (0x1654653399040a61) testScript := []byte(` @@ -1270,3 +1241,77 @@ access(all) fun testPrioritizesFork() { require.Len(t, result.Results, 1) assert.NoError(t, result.Results["test_priority.cdc"][0].Error) } + +func TestMultipleFiles_ForkNetwork(t *testing.T) { + if os.Getenv("SKIP_NETWORK_TESTS") != "" { + t.Skip("skipping network-dependent test") + } + + _, state, _ := util.TestMocks(t) + + state.Networks().AddOrUpdate(config.Network{ + Name: "mainnet", + Host: "access.mainnet.nodes.onflow.org:9000", + }) + state.Networks().AddOrUpdate(config.Network{ + Name: "mainnet-fork", + Host: "127.0.0.1:3569", + Fork: "mainnet", + }) + + addrA := flowsdk.HexToAddress("0x1654653399040a61") + addrB := flowsdk.HexToAddress("0xf233dcee88fe0abe") + + _ = state.ReaderWriter().WriteFile("ContractA.cdc", []byte(` +access(all) contract ContractA { + access(all) var value: Int + init() { self.value = 1 } +} +`), 0644) + _ = state.ReaderWriter().WriteFile("ContractB.cdc", []byte(` +access(all) contract ContractB { + access(all) var value: Int + init() { self.value = 2 } +} +`), 0644) + + state.Contracts().AddOrUpdate(config.Contract{ + Name: "ContractA", + Location: "ContractA.cdc", + Aliases: config.Aliases{{Network: "mainnet-fork", Address: addrA}}, + }) + state.Contracts().AddOrUpdate(config.Contract{ + Name: "ContractB", + Location: "ContractB.cdc", + Aliases: config.Aliases{{Network: "mainnet-fork", Address: addrB}}, + }) + t.Parallel() + + testFiles := map[string][]byte{ + "test_file_a.cdc": []byte(` +#test_fork(network: "mainnet-fork", height: nil) +import Test +import "ContractA" +access(all) fun testContractA() { + let addr = Type().address! + Test.assertEqual(0x1654653399040a61 as Address, addr) +} +`), + "test_file_b.cdc": []byte(` +#test_fork(network: "mainnet-fork", height: nil) +import Test +import "ContractB" +access(all) fun testContractB() { + let addr = Type().address! + Test.assertEqual(0xf233dcee88fe0abe as Address, addr) +} +`), + } + + result, err := testCode(testFiles, state, flagsTests{}) + + require.NoError(t, err) + require.Len(t, result.Results, 2) + assert.NoError(t, result.Results["test_file_a.cdc"][0].Error) + assert.NoError(t, result.Results["test_file_b.cdc"][0].Error) +} diff --git a/internal/transactions/profile_test.go b/internal/transactions/profile_test.go index 7b513ca67..8763f5b55 100644 --- a/internal/transactions/profile_test.go +++ b/internal/transactions/profile_test.go @@ -148,8 +148,6 @@ func Test_ProfilingResult(t *testing.T) { func Test_Profile_Integration_LocalEmulator(t *testing.T) { t.Run("Profile user transaction", func(t *testing.T) { - t.Parallel() - port := getFreePort(t) emulatorHost := fmt.Sprintf("127.0.0.1:%d", port) emulatorServer, testTxID, testBlockHeight := startEmulatorWithTestTransaction(t, emulatorHost, port) @@ -161,8 +159,6 @@ func Test_Profile_Integration_LocalEmulator(t *testing.T) { }) t.Run("Profile failed transaction", func(t *testing.T) { - t.Parallel() - port := getFreePort(t) emulatorHost := fmt.Sprintf("127.0.0.1:%d", port) emulatorServer, failedTxID, testBlockHeight := startEmulatorWithFailedTransaction(t, emulatorHost, port) @@ -174,8 +170,6 @@ func Test_Profile_Integration_LocalEmulator(t *testing.T) { }) t.Run("Profile transaction with multiple prior transactions", func(t *testing.T) { - t.Parallel() - port := getFreePort(t) emulatorHost := fmt.Sprintf("127.0.0.1:%d", port) emulatorServer, targetTxID, testBlockHeight := startEmulatorWithMultipleTransactions(t, emulatorHost, port, 5) @@ -187,8 +181,6 @@ func Test_Profile_Integration_LocalEmulator(t *testing.T) { }) t.Run("Profile system transaction", func(t *testing.T) { - t.Parallel() - port := getFreePort(t) emulatorHost := fmt.Sprintf("127.0.0.1:%d", port)