From cb94ce159b692b382bf46bdaa1ef5d4293630fba Mon Sep 17 00:00:00 2001 From: Owen Wu <1449069+yubing744@users.noreply.github.com> Date: Sat, 9 Jul 2022 08:54:44 +0800 Subject: [PATCH] Support select single test run (#22) * feat: Support select single test run --- src/commands/index.ts | 5 +- src/commands/mpm_commands.ts | 13 ++ src/download/commons.ts | 2 +- src/extension.ts | 3 + src/move-test/explore.ts | 122 +++++++++++++ src/move-test/index.ts | 1 + src/move-test/resolve.ts | 161 ++++++++++++++++++ src/move-test/run.ts | 84 +++++++++ src/move-test/utils.ts | 48 ++++++ src/utils.ts | 31 ++++ .../{extension.test.ts => commands.test.ts} | 32 ++++ test/functional/download.test.ts | 45 +++++ test/functional/index.ts | 2 +- test/functional/move-test.test.ts | 51 ++++++ test/unit/downloader.test.ts | 32 ---- 15 files changed, 597 insertions(+), 35 deletions(-) create mode 100644 src/move-test/explore.ts create mode 100644 src/move-test/index.ts create mode 100644 src/move-test/resolve.ts create mode 100644 src/move-test/run.ts create mode 100644 src/move-test/utils.ts rename test/functional/{extension.test.ts => commands.test.ts} (91%) create mode 100644 test/functional/download.test.ts create mode 100644 test/functional/move-test.test.ts diff --git a/src/commands/index.ts b/src/commands/index.ts index 7e19b6c..4c25fb5 100644 --- a/src/commands/index.ts +++ b/src/commands/index.ts @@ -9,7 +9,10 @@ export * from './mpm_commands'; type CommandCallback = (...args: T) => Promise | unknown; // eslint-disable-next-line @typescript-eslint/no-explicit-any -export type CommandFactory = (ideCtx: IDEExtensionContext) => CommandCallback; +export type CommandFactory = ( + ideCtx: IDEExtensionContext, + ...args: any +) => CommandCallback; export function createRegisterCommand(ctx: IDEExtensionContext) { return function registerCommand(name: string, fn: CommandFactory) { diff --git a/src/commands/mpm_commands.ts b/src/commands/mpm_commands.ts index 5629acf..debe60f 100644 --- a/src/commands/mpm_commands.ts +++ b/src/commands/mpm_commands.ts @@ -221,6 +221,19 @@ export const mpmTestFile: CommandFactory = (ctx: IDEExtensionContext) => { }; }; +export const mpmTestFunction = (ctx: IDEExtensionContext) => { + return async (filter: string): Promise => { + const document = window.activeTextEditor?.document; + if (!document) { + throw new Error('No document opened'); + } + + return mpmExecute(ctx, 'testUnit', 'package test', Marker.None, { + shellArgs: ['--filter', filter] + }); + }; +}; + export const mpmPublish: CommandFactory = (ctx: IDEExtensionContext) => { return async (): Promise => { return mpmExecute(ctx, 'publish', 'sandbox publish', Marker.None); diff --git a/src/download/commons.ts b/src/download/commons.ts index 7a423a7..9798938 100644 --- a/src/download/commons.ts +++ b/src/download/commons.ts @@ -192,7 +192,7 @@ function isZip(release: Release): boolean { * @returns */ export function hasBinary(loader: Downloader): boolean { - return fs.existsSync(loader.executatePath); + return fs.existsSync(loader.executatePath) && fs.existsSync(loader.versionPath); } /** diff --git a/src/extension.ts b/src/extension.ts index 0ff5fe6..9ec68f1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -7,6 +7,7 @@ import * as vscode from 'vscode'; import { IDEExtensionContext } from './context'; import { Logger } from './log'; +import { MoveTestExplorer } from './move-test'; import * as commands from './commands'; /** @@ -37,11 +38,13 @@ export async function activate(ctx: vscode.ExtensionContext): Promise { await commands.checkAndUpdateAll(ideCtx)(); await commands.startLanguageServer(ideCtx)(); + MoveTestExplorer.setup(ideCtx); registerCommand('starcoin.build', commands.mpmBuild); registerCommand('starcoin.testUnit', commands.mpmTestUnit); registerCommand('starcoin.testIntegration', commands.mpmTestIntegration); registerCommand('starcoin.testFile', commands.mpmTestFile); + registerCommand('starcoin.testFunction', commands.mpmTestFunction); registerCommand('starcoin.publish', commands.mpmPublish); registerCommand('starcoin.doctor', commands.mpmDoctor); registerCommand('starcoin.checkCompatibility', commands.mpmCheckCompatibility); diff --git a/src/move-test/explore.ts b/src/move-test/explore.ts new file mode 100644 index 0000000..cd00da1 --- /dev/null +++ b/src/move-test/explore.ts @@ -0,0 +1,122 @@ +import * as vscode from 'vscode'; +import { IDEExtensionContext } from '../context'; +import { MoveTestResolver } from './resolve'; +import { MoveTestRunner } from './run'; +import { isInTest } from './utils'; +import { Logger } from '../log'; + +// Set true only if the Testing API is available (VSCode version >= 1.59). +export const isVscodeTestingAPIAvailable = + // eslint-disable-next-line @typescript-eslint/no-explicit-any + 'object' === typeof (vscode as any).tests && 'function' === typeof (vscode as any).tests.createTestController; + +export class MoveTestExplorer { + static setup(ctx: IDEExtensionContext) { + if (!isVscodeTestingAPIAvailable) throw new Error('VSCode Testing API is unavailable'); + + const ctrl = vscode.tests.createTestController('move', 'Move Test Explorer'); + const inst = new this(ctx, ctrl); + + // Process already open editors + vscode.window.visibleTextEditors.forEach((ed) => { + inst.documentUpdate(ed.document); + }); + + ctx.vscode.subscriptions.push( + vscode.workspace.onDidOpenTextDocument(async (x) => { + try { + await inst.didOpenTextDocument(x); + } catch (error) { + if (isInTest()) throw error; + else ctx.logger.info(`Failed while handling 'onDidOpenTextDocument': ${error}`); + } + }) + ); + + ctx.vscode.subscriptions.push( + vscode.workspace.onDidChangeTextDocument(async (x) => { + try { + await inst.didChangeTextDocument(x); + } catch (error) { + if (isInTest()) throw error; + else ctx.logger.info(`Failed while handling 'onDidChangeTextDocument': ${error}`); + } + }) + ); + + ctx.vscode.subscriptions.push(ctrl); + + // register commands + ctx.vscode.subscriptions.push( + vscode.commands.registerCommand('starcoin.tests', () => { + return inst.allItems; + }) + ); + + return inst; + } + + private readonly ctx: IDEExtensionContext; + private readonly logger: Logger; + public readonly resolver: MoveTestResolver; + private readonly runner: MoveTestRunner; + + private constructor(ctx: IDEExtensionContext, ctrl: vscode.TestController) { + this.ctx = ctx; + this.logger = ctx.logger; + + this.resolver = new MoveTestResolver(ctx, ctrl); + this.runner = new MoveTestRunner(ctx, ctrl); + } + + /* ***** Private ***** */ + + // Handle opened documents, document changes, and file creation. + private async documentUpdate(doc: vscode.TextDocument, ranges?: vscode.Range[]) { + if (!doc.uri.path.endsWith('.move')) { + return; + } + + this.logger.info(`documentUpdate Processing ${doc.uri.fsPath}`); + + // If we don't do this, then we attempt to resolve tests in virtual + // documents such as those created by the Git, GitLens, and GitHub PR + // extensions + if (doc.uri.scheme !== 'file') { + return; + } + + await this.resolver.processDocument(doc, ranges); + } + + /* ***** Listeners ***** */ + + protected async didOpenTextDocument(doc: vscode.TextDocument) { + await this.documentUpdate(doc); + } + + protected async didChangeTextDocument(e: vscode.TextDocumentChangeEvent) { + await this.documentUpdate( + e.document, + e.contentChanges.map((x) => x.range) + ); + } + + get items() { + return this.resolver.items; + } + + get allItems() { + function* it(coll: vscode.TestItemCollection): Generator { + const arr: vscode.TestItem[] = []; + coll.forEach((x) => arr.push(x)); + + for (const item of arr) { + yield item; + yield* it(item.children); + } + } + + return it(this.items); + } +} diff --git a/src/move-test/index.ts b/src/move-test/index.ts new file mode 100644 index 0000000..e994edb --- /dev/null +++ b/src/move-test/index.ts @@ -0,0 +1 @@ +export { MoveTestExplorer } from './explore'; diff --git a/src/move-test/resolve.ts b/src/move-test/resolve.ts new file mode 100644 index 0000000..4ec8fb7 --- /dev/null +++ b/src/move-test/resolve.ts @@ -0,0 +1,161 @@ +import * as path from 'path'; +import * as vscode from 'vscode'; +import { IDEExtensionContext } from '../context'; +import { MoveTestKind, genMoveTestId, parseMoveTestId } from './utils'; + +export class MoveTestResolver { + private ctx: IDEExtensionContext; + private ctrl: vscode.TestController; + public readonly all = new Map(); + + constructor(ctx: IDEExtensionContext, ctrl: vscode.TestController) { + this.ctx = ctx; + this.ctrl = ctrl; + } + + public async provideDocumentSymbols(document: vscode.TextDocument): Promise { + const symbols: vscode.DocumentSymbol[] | undefined = await vscode.commands.executeCommand( + 'vscode.executeDocumentSymbolProvider', + document.uri + ); + + if (!symbols || symbols.length === 0) { + return []; + } + + return symbols; + } + + get items() { + return this.ctrl.items; + } + + // Processes a Move document, calling processSymbol for each symbol in the + // document. + // + // Any previously existing tests that no longer have a corresponding symbol in + // the file will be disposed. If the document contains no tests, it will be + // disposed. + async processDocument(doc: vscode.TextDocument, ranges: vscode.Range[] | undefined) { + const seen = new Set(); + const item = await this.getFile(doc.uri); + const symbols = await this.provideDocumentSymbols(doc); + + for (const symbol of symbols) { + await this.processSymbol(doc, item, seen, [], symbol); + } + + item.children.forEach((child) => { + const { name } = parseMoveTestId(child.id); + if (!name || !seen.has(name)) { + this.dispose(child); + return; + } + + if (ranges?.some((r: vscode.Range) => !!child.range?.intersection(r))) { + item.children.forEach((x) => this.dispose(x)); + } + }); + + this.disposeIfEmpty(item); + } + + // Create an item. + private createItem(label: string, uri: vscode.Uri, kind: MoveTestKind, name?: string): vscode.TestItem { + const id = genMoveTestId(uri, kind, name); + const item = this.ctrl.createTestItem(id, label, uri.with({ query: '', fragment: '' })); + this.all.set(id, item); + return item; + } + + // Retrieve an item. + private getItem( + parent: vscode.TestItem | undefined, + uri: vscode.Uri, + kind: MoveTestKind, + name?: string + ): vscode.TestItem | undefined { + return (parent?.children || this.ctrl.items).get(genMoveTestId(uri, kind, name)); + } + + // Create or retrieve an item. + private getOrCreateItem( + parent: vscode.TestItem | undefined, + label: string, + uri: vscode.Uri, + kind: MoveTestKind, + name?: string + ): vscode.TestItem { + const existing = this.getItem(parent, uri, kind, name); + if (existing) return existing; + + const item = this.createItem(label, uri, kind, name); + (parent?.children || this.ctrl.items).add(item); + return item; + } + + // Retrieve or create an item for a Go file. + private async getFile(uri: vscode.Uri): Promise { + const label = path.basename(uri.path); + const item = this.getOrCreateItem(undefined, label, uri, 'file'); + item.canResolveChildren = true; + return item; + } + + private dispose(item: vscode.TestItem) { + this.all.delete(item.id); + item.parent?.children.delete(item.id); + } + + // Dispose of the item if it has no children, recursively. This facilitates + // cleaning up package/file trees that contain no tests. + private disposeIfEmpty(item: vscode.TestItem | undefined) { + if (!item) return; + // Don't dispose of empty top-level items + const { kind } = parseMoveTestId(item.id); + if (kind === 'module') { + return; + } + + if (item.children.size > 0) { + return; + } + + this.dispose(item); + this.disposeIfEmpty(item.parent); + } + + // Recursively process a Go AST symbol. If the symbol represents a test, fuzz test, + // benchmark, or example function, a test item will be created for it, if one + // does not already exist. If the symbol is not a function and contains + // children, those children will be processed recursively. + private async processSymbol( + doc: vscode.TextDocument, + file: vscode.TestItem, + seen: Set, + parents: Array, + symbol: vscode.DocumentSymbol + ) { + // Recursively process symbols that are nested + if (symbol.kind !== vscode.SymbolKind.Function && symbol.kind !== vscode.SymbolKind.Method) { + const parentName = symbol.name; + + for (const sym of symbol.children) { + await this.processSymbol(doc, file, seen, parents.concat(parentName), sym); + } + + return; + } + + const match = symbol.detail.indexOf('test') > 0; + if (!match) { + return; + } + + const longName = parents.concat(symbol.name).join('::'); + seen.add(longName); + const item = this.getOrCreateItem(file, symbol.name, doc.uri, 'func', longName); + item.range = symbol.range; + this.all.set(item.id, item); + } +} diff --git a/src/move-test/run.ts b/src/move-test/run.ts new file mode 100644 index 0000000..11965e9 --- /dev/null +++ b/src/move-test/run.ts @@ -0,0 +1,84 @@ +import * as vscode from 'vscode'; +import { Logger } from '../log'; +import { IDEExtensionContext } from '../context'; +import { parseMoveTestId } from './utils'; +import { getTaskResult } from '../utils'; + +type CollectedTest = { item: vscode.TestItem; explicitlyIncluded?: boolean }; + +export class MoveTestRunner { + private readonly ctx: IDEExtensionContext; + private readonly logger: Logger; + private readonly ctrl: vscode.TestController; + + constructor(ctx: IDEExtensionContext, ctrl: vscode.TestController) { + this.ctx = ctx; + this.logger = ctx.logger; + this.ctrl = ctrl; + + ctrl.createRunProfile( + 'Move', + vscode.TestRunProfileKind.Run, + async (request, token) => { + try { + await this.run(request, token); + } catch (error) { + const m = 'Failed to execute tests'; + ctx.logger.info(`${m}: ${error}`); + await vscode.window.showErrorMessage(m); + } + }, + true + ); + } + + // Execute tests - TestController.runTest callback + async run(request: vscode.TestRunRequest, token?: vscode.CancellationToken): Promise { + this.logger.info('Running tests...'); + + const collected = new Set(); + this.collecteTests(request, collected); + + const run = this.ctrl.createTestRun(request); + + for (const test of collected) { + const item = test.item; + const moveTest = parseMoveTestId(item.id); + + run.started(item); + + const tokens = moveTest.name.split('::'); + let testFunc = moveTest.name; + if (tokens.length > 0) { + testFunc = tokens[tokens.length - 1]; + } + + const exec: vscode.TaskExecution = await vscode.commands.executeCommand('starcoin.testFunction', testFunc); + const exitCode = await getTaskResult(exec); + if (exitCode !== 0) { + this.logger.info(`Test ${moveTest.name} failed`); + item.error = `Test ${moveTest.name} failed`; + run.failed(item, { message: 'Test failed!' }); + } else { + run.passed(item); + } + + if (token?.isCancellationRequested) { + break; + } + } + + run.end(); + + return true; + } + + // Collect tests + collecteTests(request: vscode.TestRunRequest, collected: Set) { + if (request.include !== undefined) { + for (const test of request.include) { + collected.add({ item: test, explicitlyIncluded: true }); + } + } + } +} diff --git a/src/move-test/utils.ts b/src/move-test/utils.ts new file mode 100644 index 0000000..4a93b89 --- /dev/null +++ b/src/move-test/utils.ts @@ -0,0 +1,48 @@ +import * as vscode from 'vscode'; +export type MoveTestKind = 'file' | 'module' | 'func'; + +// The subset of vscode.FileSystem that is used by the test explorer. +export type FileSystem = Pick; + +// The subset of vscode.workspace that is used by the test explorer. +export interface Workspace + extends Pick { + // use custom FS type + readonly fs: FileSystem; + + // only include one overload + openTextDocument(uri: vscode.Uri): Thenable; +} + +/** + * Gen Move test id + * + * @param uri + * @param kind + * @param name + * @returns + */ +export function genMoveTestId(uri: vscode.Uri, kind: MoveTestKind, name?: string): string { + uri = uri.with({ query: kind }); + if (name) uri = uri.with({ fragment: name }); + return uri.toString(); +} + +/** + * Parses the ID as a URI and extracts the kind and name. + * The URI of the relevant file or folder should be retrieved wil + * TestItem.uri. + * @param id + * @returns + */ +export function parseMoveTestId(id: string): { kind: MoveTestKind; name?: string } { + const u = vscode.Uri.parse(id); + const kind = u.query as MoveTestKind; + const name = u.fragment; + return { kind, name }; +} + +// Check whether the process is running as a test. +export function isInTest() { + return process.env.VSCODE_GO_IN_TEST === '1'; +} diff --git a/src/utils.ts b/src/utils.ts index 7405b2f..722f535 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,3 +1,5 @@ +import * as vscode from 'vscode'; + export function dos2unix(rootDir: string, grep: string) { const globArray = require('glob-array'); const fs = require('fs'); @@ -13,3 +15,32 @@ export function dos2unix(rootDir: string, grep: string) { fs.writeFileSync(f, Buffer.from(content.replace(/\r\n/g, '\n'), 'latin1')); }); } + +export function getTaskResult(taskExecution: vscode.TaskExecution): Promise { + const disposables = new Array(); + const disposeAll = function () { + for (let i = 0; i < disposables.length; i++) { + disposables[i].dispose(); + } + }; + + return new Promise((resolve) => { + disposables.push( + vscode.tasks.onDidEndTask((e) => { + if (e.execution === taskExecution) { + disposeAll(); + resolve(0); + } + }) + ); + + disposables.push( + vscode.tasks.onDidEndTaskProcess((e) => { + if (e.execution === taskExecution) { + disposeAll(); + resolve(e.exitCode); + } + }) + ); + }); +} diff --git a/test/functional/extension.test.ts b/test/functional/commands.test.ts similarity index 91% rename from test/functional/extension.test.ts rename to test/functional/commands.test.ts index 3aa3a7c..317a181 100644 --- a/test/functional/extension.test.ts +++ b/test/functional/commands.test.ts @@ -310,5 +310,37 @@ suite('Starcoin-IDE.functional.test', () => { assert.fail('Error in test command, error: ' + err); } }); + + test('test starcoin test function command', async () => { + const ext = vscode.extensions.getExtension('starcoinorg.starcoin-ide'); + assert.ok(ext); + + await ext.activate(); + await sleep(1000); + + try { + // 1. get workdir + let workDir = ''; + if (vscode.workspace.workspaceFolders) { + workDir = vscode.workspace.workspaceFolders[0].uri.fsPath; + } + + // 2. open doc + const docs = await vscode.workspace.openTextDocument(path.join(workDir, 'sources/SimpleNFT.move')); + await vscode.window.showTextDocument(docs); + await sleep(1000); + + // 3. execute testFile command + const exec: vscode.TaskExecution = await vscode.commands.executeCommand( + 'starcoin.testFunction', + 'this_is_a_test' + ); + const exitCode = await getTaskResult(exec); + await sleep(1000); + assert.strictEqual(0, exitCode); + } catch (err) { + assert.fail('Error in test command, error: ' + err); + } + }); }); }); diff --git a/test/functional/download.test.ts b/test/functional/download.test.ts new file mode 100644 index 0000000..4f83103 --- /dev/null +++ b/test/functional/download.test.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as os from 'os'; +import * as fse from 'fs-extra'; +import * as path from 'path'; +import * as assert from 'assert'; +import { MPMDownloader } from '../../src/download/mpm'; + +suite('Downloader', () => { + suite('#checkNewRelease', () => { + test('check move new release should be ok', async () => { + const loader = new MPMDownloader(os.tmpdir()); + const result = await loader.checkRelease(loader.latestVersion); + + assert.ok(result.tag, 'Check new release latest tag should be ok'); + assert.ok(result.release, 'Check new release should be ok'); + assert.ok(result.release.browser_download_url, 'Check new release browser_download_url should be ok'); + }); + }); + + suite('#hasBinary', () => { + test('new user hasBinary should be false', async () => { + const loader = new MPMDownloader(path.join(os.tmpdir(), 'starcoin-ide', 'test', '' + new Date().getTime())); + const result = loader.hasBinary(); + + assert.strictEqual(result, false); + }); + + test('after installRelease hasBinary should be true', async () => { + const devPath = path.join(os.tmpdir(), 'starcoin-ide', 'test', '' + new Date().getTime()); + const loader = new MPMDownloader(devPath); + + // make faker move + fse.mkdirsSync(loader.binPath('')); + fse.writeFileSync(loader.executatePath, 'xxx'); + fse.writeFileSync(loader.versionPath, 'v0.1.0'); + + const result = loader.hasBinary(); + assert.strictEqual(result, true); + }); + }); +}); diff --git a/test/functional/index.ts b/test/functional/index.ts index 4267e65..6f77104 100644 --- a/test/functional/index.ts +++ b/test/functional/index.ts @@ -21,7 +21,7 @@ export function run(): Promise { const testsRoot = path.resolve(__dirname, '.'); return new Promise((c, e) => { - glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { + glob('**/download.test.js', { cwd: testsRoot }, (err, files) => { if (err) { return e(err); } diff --git a/test/functional/move-test.test.ts b/test/functional/move-test.test.ts new file mode 100644 index 0000000..3467c0f --- /dev/null +++ b/test/functional/move-test.test.ts @@ -0,0 +1,51 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See LICENSE.md in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +// You can import and use all API from the 'vscode' module +// as well as import your extension to test it +import * as vscode from 'vscode'; +import * as assert from 'assert'; +import * as path from 'path'; + +import { sleep } from './utils'; + +suite('Starcoin-IDE.functional.test', () => { + suite('Move-test test', () => { + test('test explore all tests', async () => { + const ext = vscode.extensions.getExtension('starcoinorg.starcoin-ide'); + assert.ok(ext); + + await ext.activate(); + await sleep(1000); + + try { + // 1. get workdir + let workDir = ''; + if (vscode.workspace.workspaceFolders) { + workDir = vscode.workspace.workspaceFolders[0].uri.fsPath; + } + + // 2. open doc + const docs = await vscode.workspace.openTextDocument(path.join(workDir, 'sources/SimpleNFT.move')); + await vscode.window.showTextDocument(docs); + await sleep(1000); + + // 3. execute testFile command + const tests: Generator = await vscode.commands.executeCommand('starcoin.tests'); + assert.ok(tests); + + const rootItem = tests.next(); + assert.ok(rootItem); + assert.deepStrictEqual(rootItem.value.label, 'SimpleNFT.move'); + + const testItem = tests.next(); + assert.ok(testItem); + assert.deepStrictEqual(testItem.value.label, 'this_is_a_test'); + } catch (err) { + assert.fail('Error in test command, error: ' + err); + } + }); + }); +}); diff --git a/test/unit/downloader.test.ts b/test/unit/downloader.test.ts index a384138..dd3014f 100644 --- a/test/unit/downloader.test.ts +++ b/test/unit/downloader.test.ts @@ -11,38 +11,6 @@ import * as assert from 'assert'; import { MoveDownloader } from '../../src/download/move'; suite('Downloader', () => { - suite('#checkNewRelease', () => { - test('check move new release should be ok', async () => { - const loader = new MoveDownloader(os.tmpdir()); - const result = await loader.checkRelease(loader.latestVersion); - - assert.ok(result.tag, 'Check new release latest tag should be ok'); - assert.ok(result.release, 'Check new release should be ok'); - assert.ok(result.release.browser_download_url, 'Check new release browser_download_url should be ok'); - }); - }); - - suite('#hasBinary', () => { - test('new user hasBinary should be false', async () => { - const loader = new MoveDownloader(path.join(os.tmpdir(), 'starcoin-ide', 'test', '' + new Date().getTime())); - const result = loader.hasBinary(); - - assert.strictEqual(result, false); - }); - - test('after installRelease hasBinary should be true', async () => { - const devPath = path.join(os.tmpdir(), 'starcoin-ide', 'test', '' + new Date().getTime()); - const loader = new MoveDownloader(devPath); - - // make faker move - fse.mkdirsSync(loader.binPath('')); - fse.writeFileSync(loader.executatePath, 'xxx'); - - const result = loader.hasBinary(); - assert.strictEqual(result, true); - }); - }); - suite('#isBinaryOutdated', () => { test('v1.5.1 should outdated when new release is v1.5.6', async () => { const loader = new MoveDownloader(path.join(os.tmpdir(), 'starcoin-ide', 'test', '' + new Date().getTime()));