From a998e6a9bc5ad8705e2d97fab23ec9e819d11b57 Mon Sep 17 00:00:00 2001 From: Zen Voich Date: Thu, 7 Nov 2024 07:01:02 +0400 Subject: [PATCH] [cli] run pocket-ic from dfx --- cli/cli.ts | 1 + cli/commands/replica.ts | 47 +++++++++++++++++++++++++++++++-------- cli/commands/test/test.ts | 19 +++++++++++++++- dfx.json | 2 +- mops.toml | 3 +-- 5 files changed, 59 insertions(+), 13 deletions(-) diff --git a/cli/cli.ts b/cli/cli.ts index dcd678e9..59fb6623 100755 --- a/cli/cli.ts +++ b/cli/cli.ts @@ -214,6 +214,7 @@ program .addOption(new Option('--mode ', 'Test mode').choices(['interpreter', 'wasi', 'replica']).default('interpreter')) .addOption(new Option('--replica ', 'Which replica to use to run tests in replica mode').choices(['dfx', 'pocket-ic'])) .option('-w, --watch', 'Enable watch mode') + .option('--debug', 'Show debug logs') .action(async (filter, options) => { checkConfigFile(true); await installAll({silent: true, lock: 'ignore', installFromLockFile: true}); diff --git a/cli/commands/replica.ts b/cli/commands/replica.ts index 0b21d8ba..8e4da8b6 100644 --- a/cli/commands/replica.ts +++ b/cli/commands/replica.ts @@ -8,19 +8,21 @@ import {spawn as spawnAsync} from 'promisify-child-process'; import {IDL} from '@dfinity/candid'; import {Actor, HttpAgent} from '@dfinity/agent'; import {PocketIc, PocketIcServer} from 'pic-ic'; +import chalk from 'chalk'; import {readConfig} from '../mops.js'; import {toolchain} from './toolchain/index.js'; +import {getDfxVersion} from '../helpers/get-dfx-version.js'; type StartOptions = { - type ?: 'dfx' | 'pocket-ic'; + type ?: 'dfx' | 'pocket-ic' | 'dfx-pocket-ic'; dir ?: string; verbose ?: boolean; silent ?: boolean; }; export class Replica { - type : 'dfx' | 'pocket-ic' = 'dfx'; + type : 'dfx' | 'pocket-ic' | 'dfx-pocket-ic' = 'dfx'; verbose = false; canisters : Record = {}; pocketIcServer ?: PocketIcServer; @@ -36,20 +38,33 @@ export class Replica { silent || console.log(`Starting ${this.type} replica...`); - if (this.type == 'dfx') { + if (this.type === 'dfx' || this.type === 'dfx-pocket-ic') { fs.mkdirSync(this.dir, {recursive: true}); fs.writeFileSync(path.join(this.dir, 'dfx.json'), JSON.stringify(this.dfxJson(''), null, 2)); fs.writeFileSync(path.join(this.dir, 'canister.did'), 'service : { runTests: () -> (); }'); await this.stop(); - this.dfxProcess = spawn('dfx', ['start', '--clean', '--artificial-delay', '0', (this.verbose ? '' : '-qqqq')].filter(x => x), {cwd: this.dir}); + this.dfxProcess = spawn('dfx', ['start', this.type === 'dfx-pocket-ic' ? '--pocketic' : '', '--clean', (this.verbose ? '' : '-qqqq'), '--artificial-delay', '0'].filter(x => x).flat(), {cwd: this.dir}); // process canister logs this._attachCanisterLogHandler(this.dfxProcess); this.dfxProcess.stdout.on('data', (data) => { - console.log('DFX:', data.toString()); + if (this.verbose) { + console.log('DFX:', data.toString()); + } + }); + + this.dfxProcess.stderr.on('data', (data) => { + if (this.verbose) { + console.error('DFX:', data.toString()); + } + if (data.toString().includes('Failed to bind socket to')) { + console.error(chalk.red(data.toString())); + console.log('Please run again after some time'); + process.exit(11); + } }); // await for dfx to start @@ -115,9 +130,22 @@ export class Replica { } async stop(sigint = false) { - if (this.type == 'dfx') { - this.dfxProcess?.kill(); - // execSync('dfx stop' + (this.verbose ? '' : ' -qqqq'), {cwd: this.dir, timeout: 10_000, stdio: ['pipe', this.verbose ? 'inherit' : 'ignore', 'pipe']}); + if (this.type === 'dfx' || this.type === 'dfx-pocket-ic') { + if (this.dfxProcess) { + this.dfxProcess.kill(); + // give replica some time to stop + await new Promise((resolve) => { + setTimeout(resolve, 1000); + }); + } + + // if (!this.dfxProcess) { + // try { + // execSync('dfx killall', {cwd: this.dir, timeout: 3_000, stdio: ['pipe', this.verbose ? 'inherit' : 'ignore', 'pipe']}); + // execSync('dfx stop' + (this.verbose ? '' : ' -qqqq'), {cwd: this.dir, timeout: 10_000, stdio: ['pipe', this.verbose ? 'inherit' : 'ignore', 'pipe']}); + // } + // catch {} + // } } else if (this.pocketIc && this.pocketIcServer) { if (!sigint) { @@ -128,7 +156,7 @@ export class Replica { } async deploy(name : string, wasm : string, idlFactory : IDL.InterfaceFactory, cwd : string = process.cwd(), signal ?: AbortSignal) { - if (this.type === 'dfx') { + if (this.type === 'dfx' || this.type === 'dfx-pocket-ic') { // prepare dfx.json for current canister let dfxJson = path.join(this.dir, 'dfx.json'); @@ -253,6 +281,7 @@ export class Replica { return { version: 1, canisters, + dfx: getDfxVersion(), defaults: { build: { packtool: 'mops sources', diff --git a/cli/commands/test/test.ts b/cli/commands/test/test.ts index 8626d4e0..5bdeaabf 100644 --- a/cli/commands/test/test.ts +++ b/cli/commands/test/test.ts @@ -8,6 +8,7 @@ import chalk from 'chalk'; import {globSync} from 'glob'; import chokidar from 'chokidar'; import debounce from 'debounce'; +import {SemVer} from 'semver'; import {sources} from '../sources.js'; import {getRootDir, readConfig} from '../../mops.js'; @@ -25,6 +26,7 @@ import {Replica} from '../replica.js'; import {ActorMethod} from '@dfinity/agent'; import {PassThrough, Readable} from 'node:stream'; import {TestMode} from '../../types.js'; +import {getDfxVersion} from '../../helpers/get-dfx-version.js'; let ignore = [ '**/node_modules/**', @@ -39,13 +41,14 @@ let globConfig = { }; type ReporterName = 'verbose' | 'files' | 'compact' | 'silent'; -type ReplicaName = 'dfx' | 'pocket-ic'; +type ReplicaName = 'dfx' | 'pocket-ic' | 'dfx-pocket-ic'; type TestOptions = { watch : boolean; reporter : ReporterName; mode : TestMode; replica : ReplicaName; + debug : boolean; }; @@ -66,7 +69,20 @@ export async function test(filter = '', options : Partial = {}) { let rootDir = getRootDir(); let replicaType = options.replica ?? (config.toolchain?.['pocket-ic'] ? 'pocket-ic' : 'dfx' as ReplicaName); + + if (replicaType === 'pocket-ic' && !config.toolchain?.['pocket-ic']) { + let dfxVersion = getDfxVersion(); + if (!dfxVersion || new SemVer(dfxVersion).compare('0.24.1') < 0) { + console.log(chalk.red('Please update dfx to the version >=0.24.1 or specify pocket-ic version in mops.toml')); + process.exit(1); + } + else { + replicaType = 'dfx-pocket-ic'; + } + } + replica.type = replicaType; + replica.verbose = !!options.debug; if (options.watch) { replica.ttl = 60 * 15; // 15 minutes @@ -202,6 +218,7 @@ export async function testWithReporter(reporterName : ReporterName | Reporter | let testTempDir = path.join(getRootDir(), '.mops/.test/'); replica.dir = testTempDir; + fs.rmSync(testTempDir, {recursive: true, force: true}); fs.mkdirSync(testTempDir, {recursive: true}); await parallel(os.cpus().length, files, async (file : string) => { diff --git a/dfx.json b/dfx.json index 91acce5d..271ff524 100644 --- a/dfx.json +++ b/dfx.json @@ -66,7 +66,7 @@ "packtool": "mops sources" } }, - "dfx": "0.24.0", + "dfx": "0.24.1", "networks": { "staging": { "type": "persistent", diff --git a/mops.toml b/mops.toml index e92a993d..f3f70c6a 100644 --- a/mops.toml +++ b/mops.toml @@ -17,5 +17,4 @@ bench = "1.0.0" [toolchain] moc = "0.12.1" -wasmtime = "23.0.1" -pocket-ic = "4.0.0" \ No newline at end of file +wasmtime = "23.0.1" \ No newline at end of file