diff --git a/assembly/__tests__/array.spec.ts b/assembly/__tests__/array.spec.ts index cd971ea..304a946 100644 --- a/assembly/__tests__/array.spec.ts +++ b/assembly/__tests__/array.spec.ts @@ -11,9 +11,7 @@ describe("Array manipulation", () => { expect(myArray).toContain(2); }); - it("should be empty", () => { - expect("ag").toBe("asd") - }); + it("should be empty", () => {}); }); run({ diff --git a/assembly/index.ts b/assembly/index.ts index cfe8690..5ab7c1c 100644 --- a/assembly/index.ts +++ b/assembly/index.ts @@ -5,6 +5,7 @@ import { stringify } from "as-console/stringify"; import { __COVER, __HASHES, __POINTS } from "as-test/assembly/coverage"; import { JSON } from "json-as"; import { Report, SuiteReport, TestReport, Time } from "../reporters/report"; +import { term } from "./util/term"; /** * Enumeration representing the verdict of a test case. @@ -18,6 +19,8 @@ export namespace Verdict { let entrySuites: Suite[] = []; +// @ts-ignore +const FILE = isDefined(ENTRY_FILE) ? ENTRY_FILE : "unknown"; // Globals @global let suites: Suite[] = []; @@ -222,19 +225,19 @@ export function afterEach(callback: () => void): void { export function mockFn( fn: string, callback: (...args: any[]) => returnType, -): void {} +): void { } /** * Unmock all references to an existing function to instead point to the original function * @param {string} fn - name of function to override */ -export function unmockFn(fn: string): void {} +export function unmockFn(fn: string): void { } /** * Re-mock all references to an existing function to instead point to the declared function * @param {string} fn - name of function to override */ -export function remockFn(fn: string): void {} +export function remockFn(fn: string): void { } /** * Class defining options that can be passed to the `run` function. @@ -270,10 +273,12 @@ class RunOptions { */ export function run(options: RunOptions = new RunOptions()): void { __test_options = options; - const report = new Report(); - report.time.start = performance.now(); + term.write("\n"); + const time = new Time(); + const fileLn = term.write(`${rainbow.bgCyanBright(" FILE ")} ${rainbow.dimMk(FILE)}\n`); + term.write("\n"); + time.start = performance.now(); for (let i = 0; i < entrySuites.length; i++) { - const suiteReport = new SuiteReport(); // @ts-ignore const suite = unchecked(entrySuites[i]); suites = [suite]; @@ -282,36 +287,45 @@ export function run(options: RunOptions = new RunOptions()): void { depth = -1; current_suite = null; - suiteReport.time.start = performance.now(); + const suiteLn = term.write(` ${rainbow.bgBlackBright(" ... ")} ${rainbow.dimMk(suite.description)}\n`); + term.write("\n"); suite.run(); - suiteReport.time.end = performance.now(); suites = []; depth = -1; current_suite = null; - suiteReport.kind = suite.kind; - suiteReport.verdict = suite.verdict; + let suiteNone = true; for (let ii = 0; ii < suite.suites.length; ii++) { const _suite = unchecked(suite.suites[ii]); - if (_suite.verdict == Verdict.Fail) report.verdict = Verdict.Fail; - suiteReport.suites.push(SuiteReport.wrap(_suite)); + if (_suite.verdict == Verdict.Fail) { + suite.verdict = Verdict.Fail; + suiteNone = false; + } else if (_suite.verdict == Verdict.Ok) { + suiteNone = false; + } } + for (let iii = 0; iii < suite.tests.length; iii++) { - const test = unchecked(suite.tests[iii]); - if (test.verdict == Verdict.Fail) report.verdict = Verdict.Fail; - suiteReport.tests.push(TestReport.wrap(test)); + const _test = unchecked(suite.tests[iii]); + if (_test.verdict == Verdict.Fail) { + suite.verdict = Verdict.Fail; + } + } + + if (!suiteNone && suite.tests.length) { + suite.verdict = Verdict.Ok; + } + + if (suite.verdict == Verdict.Ok) { + suiteLn.edit(` ${rainbow.bgGreenBright(" PASS ")} ${rainbow.dimMk(suite.description)} ${rainbow.dimMk(suite.time.format())}\n`); } - suiteReport.description = suite.description; - report.groups.push(suiteReport); - } - report.time.end = performance.now(); - if (report.verdict === Verdict.None) { - if (report.groups.length) report.verdict = Verdict.Ok; } - // @ts-ignore - report.file = isDefined(ENTRY_FILE) ? ENTRY_FILE : "unknown"; - console.log( - "--REPORT-START--\n" + JSON.stringify(report) + "\n--REPORT-END--", - ); + time.end = performance.now(); + fileLn.edit(`${rainbow.bgCyanBright(" FILE ")} ${rainbow.dimMk(FILE)} ${rainbow.dimMk(time.format())}`); } + +export function getDepth(): string { + if (depth < 0) return ""; + return " ".repeat(depth); +} \ No newline at end of file diff --git a/assembly/src/suite.ts b/assembly/src/suite.ts index 5db5d41..3a11fb9 100644 --- a/assembly/src/suite.ts +++ b/assembly/src/suite.ts @@ -1,7 +1,9 @@ -import { Verdict } from ".."; +import { rainbow } from "as-rainbow"; +import { getDepth, Verdict } from ".."; import { Time } from "../../reporters/report"; import { Expectation } from "./expectation"; import { Tests } from "./tests"; +import { term } from "../util/term"; export type SuiteKind = string; export namespace SuiteKind { @@ -16,6 +18,7 @@ export class Suite { public depth: i32 = 0; public suites: Suite[] = []; public tests: Tests[] = []; + public logs: string[] = []; public kind: SuiteKind; public verdict: Verdict = Verdict.None; @@ -39,14 +42,17 @@ export class Suite { current_suite = this; // @ts-ignore depth++; + this.time.start = performance.now(); + const suiteDepth = getDepth(); + const suiteLn = term.write(`${suiteDepth}${rainbow.bgBlackBright(" ... ")} ${rainbow.dimMk(this.description)}\n`); + term.write("\n"); this.callback(); + this.time.end = performance.now(); // @ts-ignore depth--; for (let i = 0; i < this.suites.length; i++) { const suite = unchecked(this.suites[i]); - suite.time.start = performance.now(); suite.run(); - suite.time.end = performance.now(); if (suite.verdict === Verdict.Fail) { this.verdict = Verdict.Fail; break; @@ -63,5 +69,6 @@ export class Suite { if (this.tests.length) this.verdict = Verdict.Ok; if (this.suites.length) this.verdict = Verdict.Ok; } + suiteLn.edit(`${suiteDepth}${rainbow.bgGreenBright(" PASS ")} ${rainbow.dimMk(this.description)} ${rainbow.dimMk(this.time.format())}\n`); } } diff --git a/assembly/util/term.ts b/assembly/util/term.ts new file mode 100644 index 0000000..d2befe0 --- /dev/null +++ b/assembly/util/term.ts @@ -0,0 +1,40 @@ +export class Log { + private line: i32 = 0; + constructor(line: i32 = 0) { + this.line = line; + } + edit(data: string): Log { + term.clearLn(this.line); + process.stdout.write(data); + process.stdout.write("\x1B[999B"); + process.stdout.write("\x1B[0G"); + return new Log(this.line); + } +} + +export namespace term { + export let lines: i32 = 0; + export function write(data: string): Log { + process.stdout.write(data); + return new Log(term.lines++); + } + export function writeLn(data: string): void { + for (let i = 0; i < data.length; i++) { + const code = data.charCodeAt(i); + if (code === 10) term.lines++; + } + term.lines++; + process.stdout.write(data + "\n"); + } + export function clearLn(line: i32): void { + process.stdout.write(`\u001B[${term.lines - line}A`); + process.stdout.write("\x1B[2K"); + process.stdout.write("\x1B[0G"); + } + export function editLn(data: string, ln: i32): void { + process.stdout.write(`\u001B[${ln}A`); + process.stdout.write("\x1B[2K"); + process.stdout.write("\x1B[0G"); + term.writeLn(data); + } +} \ No newline at end of file diff --git a/bin/cli/about.js b/bin/cli/about.js deleted file mode 100644 index b311b5e..0000000 --- a/bin/cli/about.js +++ /dev/null @@ -1,135 +0,0 @@ -import chalk from "chalk"; -export function about() { - console.log( - chalk.bold.blueBright("as-test") + - " is a testing framework for AssemblyScript. " + - chalk.dim("(v" + version + ")") + - "\n", - ); - console.log( - chalk.bold("Usage: as-test") + - " " + - chalk.dim("") + - " " + - chalk.bold.blueBright("[...flags]") + - " " + - chalk.bold("[...args]") + - " " + - chalk.dim("(alias: ast)") + - "\n", - ); - console.log(chalk.bold("Commands:")); - console.log( - " " + - chalk.bold.blueBright("run") + - " " + - chalk.dim("") + - " " + - "Run unit tests with selected runtime", - ); - console.log( - " " + - chalk.bold.blueBright("build") + - " " + - chalk.dim("") + - " " + - "Build unit tests and compile", - ); - console.log( - " " + - chalk.bold.blueBright("test") + - " " + - chalk.dim("") + - " " + - "Build and run unit tests with selected runtime" + - "\n", - ); - console.log( - " " + - chalk.bold.magentaBright("init") + - " " + - chalk.strikethrough.dim("") + - " " + - "Initialize an empty testing template", - ); - console.log( - " " + - chalk.strikethrough.bold.magentaBright("config") + - " " + - chalk.strikethrough.dim("as-test.config.json") + - " " + - "Specify the configuration file", - ); - console.log( - " " + - chalk.strikethrough.bold.magentaBright("reporter") + - " " + - chalk.strikethrough.dim("") + - " " + - "Specify the test reporter to use", - ); - console.log( - " " + - chalk.strikethrough.bold.magentaBright("use") + - " " + - chalk.strikethrough.dim("wasmtime") + - " " + - "Specify the runtime to use" + - "\n", - ); - console.log(chalk.bold("Flags:")); - console.log( - " " + - chalk.strikethrough.dim("run") + - " " + - chalk.strikethrough.bold.blue("--coverage") + - " " + - "Use code coverage", - ); - console.log( - " " + - chalk.strikethrough.dim("run") + - " " + - chalk.strikethrough.bold.blue("--snapshot") + - " " + - "Take a snapshot of the tests", - ); - console.log( - " " + - chalk.strikethrough.dim("use") + - " " + - chalk.strikethrough.bold.blue("--list") + - " " + - "List supported runtimes", - ); - console.log( - " " + - chalk.strikethrough.dim("reporter") + - " " + - chalk.strikethrough.bold.blue("--list") + - " " + - "List supported reporters", - ); - console.log( - " " + - chalk.strikethrough.dim("") + - " " + - chalk.strikethrough.bold.blue("--help") + - " " + - "Print info about command" + - "\n", - ); - console.log( - chalk.dim( - "If your using this, consider dropping a star, it would help a lot!", - ) + "\n", - ); - console.log( - "View the repo: " + - chalk.magenta("https://github.com/JairusSW/as-test"), - ); - console.log( - "View the docs: " + - chalk.strikethrough.blue("https://docs.jairus.dev/as-test"), - ); -} diff --git a/bin/cli/build.js b/bin/cli/build.js deleted file mode 100644 index 248b82e..0000000 --- a/bin/cli/build.js +++ /dev/null @@ -1,149 +0,0 @@ -import { existsSync, readFileSync } from "fs"; -import { Config } from "./types.js"; -import { glob } from "glob"; -import chalk from "chalk"; -import { exec } from "child_process"; -import { formatTime } from "./util.js"; -import * as path from "path"; -const CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json"); -const PKG_PATH = path.join(process.cwd(), "./package.json"); -export async function build(args) { - let config = loadConfig(); - const ASCONFIG_PATH = path.join(process.cwd(), config.config); - if (!existsSync(ASCONFIG_PATH)) { - console.log( - chalk.bgMagentaBright(" WARN ") + - chalk.dim(":") + - ' Could not locate asconfig.json file! If you do not want to provide a config, set "config": "none". Continuing with default config.' + - "\n", - ); - } - verifyPackagesInstalled(config); - let pkgMan = getPkgManager(); - console.log(""); - const inputFiles = await glob(config.input); - for (const file of inputFiles) { - console.log(chalk.dim("Including " + file)); - let command = `${pkgMan} asc ${file}${args.length ? " " + args.join(" ") : ""}`; - if (config.config !== "none") { - command += " --config " + config.config; - } - if (config.buildOptions.wasi) { - command += - " --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json"; - } - const outFile = - config.outDir + - "/" + - file.slice(file.lastIndexOf("/") + 1).replace(".ts", ".wasm"); - if (config.outDir) { - command += " -o " + outFile; - } - if (config.coverage.enabled) { - console.log(chalk.dim("Enabling coverage")); - command += " --use COVERAGE_USE=1 --transform as-test/transform"; - if (config.coverage.show) command += " --use COVERAGE_SHOW=1"; - } - if (config.buildOptions.args) { - command += " " + config.buildOptions.args.join(" "); - } - if ( - ["node", "deno", "bun"].includes( - config.runOptions.runtime.run.split(" ")[0], - ) - ) { - command += " --exportStart"; - } - buildCommands.push(command); - } - const build = (command) => { - return new Promise((resolve, _) => { - console.log(chalk.dim("Building: " + command)); - exec(command, (err, stdout, stderr) => { - if (config.buildOptions.verbose) { - process.stdout.write(stdout); - } - if (err) { - process.stderr.write(stderr + "\n"); - process.exit(1); - } - resolve(); - }); - }); - }; - if (config.buildOptions.parallel) { - console.log(chalk.dim("Building sources in parallel...")); - const start = performance.now(); - let builders = []; - for (const command of buildCommands) { - builders.push(build(command)); - } - await Promise.all(builders); - console.log( - chalk.dim("Compiled in " + formatTime(performance.now() - start)) + "\n", - ); - } -} -function verifyPackagesInstalled(config) { - const pkg = JSON.parse(readFileSync(PKG_PATH).toString()); - if (config.buildOptions.wasi) { - if (!existsSync("./node_modules/@assemblyscript/wasi-shim/asconfig.json")) { - console.log( - chalk.bgRed(" ERROR ") + - chalk.dim(":") + - " " + - "could not find @assemblyscript/wasi-shim! Add it to your dependencies to run with WASI!", - ); - process.exit(1); - } - if ( - pkg.dependencies && - !Object.keys(pkg.dependencies).includes("@assemblyscript/wasi-shim") && - pkg.devDependencies && - !Object.keys(pkg.devDependencies).includes("@assemblyscript/wasi-shim") && - pkg.peerDependencies && - !Object.keys(pkg.peerDependencies).includes( - "@assemblyscript/wasi-shim", - ) && - existsSync("./node_modules/@assemblyscript/wasi-shim/asconfig.json") - ) { - console.log( - chalk.bold.bgMagentaBright(" WARN ") + - chalk.dim(": @assemblyscript/wasi-shim") + - " is not included in project dependencies!", - ); - } - } -} -function loadConfig() { - if (!existsSync(CONFIG_PATH)) { - console.log( - chalk.bgMagentaBright(" WARN ") + - chalk.dim(":") + - " Could not locate config file in the current directory! Continuing with default config." + - "\n", - ); - console.log(chalk.dim("Using default configuration") + "\n"); - return new Config(); - } else { - console.log(chalk.dim("Loading config from: " + CONFIG_PATH) + "\n"); - return Object.assign( - new Config(), - JSON.parse(readFileSync(CONFIG_PATH).toString()), - ); - } -} -function getPkgManager() { - switch (process.env.npm_config_user_agent) { - case "pnpm": { - return "pnpx"; - } - case "yarn": { - return "yarn"; - } - case "bun": { - return "bunx"; - } - } - return "npx"; -} diff --git a/bin/cli/index.js b/bin/cli/index.js deleted file mode 100644 index 371ed24..0000000 --- a/bin/cli/index.js +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env node -import chalk from "chalk"; -import { build } from "./build.js"; -import { run } from "./run.js"; -import { init } from "./init.js"; -const _args = process.argv.slice(2); -const flags = []; -const args = []; -const COMMANDS = ["run", "build", "test", "init"]; -const version = "0.2.1"; -for (const arg of _args) { - if (arg.startsWith("-")) flags.push(arg); - else args.push(arg); -} -if (!args.length) { - if (flags.includes("--version") || flags.includes("-v")) { - console.log("as-test v" + version.toString()); - } else { - info(); - } -} else if (COMMANDS.includes(args[0])) { - const command = args.shift(); - if (command === "build") { - build(args); - } else if (command === "run") { - run(); - } else if (command === "test") { - build(args).then(() => { - run(); - }); - } else if (command === "init") { - init(args); - } -} else { - console.log( - chalk.bgRed(" ERROR ") + - chalk.dim(":") + - " " + - chalk.bold("Unknown command: ") + - args[0], - ); -} -function info() { - console.log( - chalk.bold.blueBright("as-test") + - " is a testing framework for AssemblyScript. " + - chalk.dim("(v" + version + ")") + - "\n", - ); - console.log( - chalk.bold("Usage: as-test") + - " " + - chalk.dim("") + - " " + - chalk.bold.blueBright("[...flags]") + - " " + - chalk.bold("[...args]") + - " " + - chalk.dim("(alias: ast)") + - "\n", - ); - console.log(chalk.bold("Commands:")); - console.log( - " " + - chalk.bold.blueBright("run") + - " " + - chalk.dim("") + - " " + - "Run unit tests with selected runtime", - ); - console.log( - " " + - chalk.bold.blueBright("build") + - " " + - chalk.dim("") + - " " + - "Build unit tests and compile", - ); - console.log( - " " + - chalk.bold.blueBright("test") + - " " + - chalk.dim("") + - " " + - "Build and run unit tests with selected runtime" + - "\n", - ); - console.log( - " " + - chalk.bold.magentaBright("init") + - " " + - chalk.strikethrough.dim("") + - " " + - "Initialize an empty testing template", - ); - console.log( - " " + - chalk.strikethrough.bold.magentaBright("config") + - " " + - chalk.strikethrough.dim("as-test.config.json") + - " " + - "Specify the configuration file", - ); - console.log( - " " + - chalk.strikethrough.bold.magentaBright("reporter") + - " " + - chalk.strikethrough.dim("") + - " " + - "Specify the test reporter to use", - ); - console.log( - " " + - chalk.strikethrough.bold.magentaBright("use") + - " " + - chalk.strikethrough.dim("wasmtime") + - " " + - "Specify the runtime to use" + - "\n", - ); - console.log(chalk.bold("Flags:")); - console.log( - " " + - chalk.strikethrough.dim("run") + - " " + - chalk.strikethrough.bold.blue("--coverage") + - " " + - "Use code coverage", - ); - console.log( - " " + - chalk.strikethrough.dim("run") + - " " + - chalk.strikethrough.bold.blue("--snapshot") + - " " + - "Take a snapshot of the tests", - ); - console.log( - " " + - chalk.strikethrough.dim("use") + - " " + - chalk.strikethrough.bold.blue("--list") + - " " + - "List supported runtimes", - ); - console.log( - " " + - chalk.strikethrough.dim("reporter") + - " " + - chalk.strikethrough.bold.blue("--list") + - " " + - "List supported reporters", - ); - console.log( - " " + - chalk.strikethrough.dim("") + - " " + - chalk.strikethrough.bold.blue("--help") + - " " + - "Print info about command" + - "\n", - ); - console.log( - chalk.dim( - "If your using this, consider dropping a star, it would help a lot!", - ) + "\n", - ); - console.log( - "View the repo: " + - chalk.magenta("https://github.com/JairusSW/as-test"), - ); - console.log( - "View the docs: " + - chalk.strikethrough.blue("https://docs.jairus.dev/as-test"), - ); -} diff --git a/bin/cli/init.js b/bin/cli/init.js deleted file mode 100644 index 0749ee1..0000000 --- a/bin/cli/init.js +++ /dev/null @@ -1,44 +0,0 @@ -import chalk from "chalk"; -import { existsSync, writeFileSync } from "fs"; -import * as path from "path"; -import { createInterface } from "readline"; -import { Config } from "./types.js"; -export function init(args) { - console.log( - chalk.bold("This command will make sure that the following files exist") + - "\n", - ); - console.log( - " " + - chalk.bold.blueBright("./as-test.config.json") + - chalk.dim(" - The core config file for as-test") + - "\n", - ); - console.log( - "This command will attempt to update files to match the correct configuration.\n", - ); - console.log("Do you want to proceed? [Y/n] "); - createInterface({ - input: process.stdin, - output: process.stdout, - }).question("", (answer) => { - if (answer.toLowerCase() === "y") { - initialize(); - } else { - console.log("Exiting..."); - process.exit(0); - } - }); -} -function initialize() { - const CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json"); - if (existsSync(CONFIG_PATH)) { - console.log("Found ./as-test.config.json. Updating..."); - process.exit(0); - } else { - console.log("Wrote ./as-test.config.json"); - writeFileSync(CONFIG_PATH, JSON.stringify(new Config(), null, 2)); - console.log(JSON.stringify(new Config(), null, 2)); - process.exit(0); - } -} diff --git a/bin/cli/reporter.js b/bin/cli/reporter.js deleted file mode 100644 index 7304f8d..0000000 --- a/bin/cli/reporter.js +++ /dev/null @@ -1 +0,0 @@ -export function report() {} diff --git a/bin/cli/run.js b/bin/cli/run.js deleted file mode 100644 index da3772a..0000000 --- a/bin/cli/run.js +++ /dev/null @@ -1,78 +0,0 @@ -import { existsSync, readFileSync, readdirSync } from "fs"; -import { Config } from "./types.js"; -import chalk from "chalk"; -import { execSync } from "child_process"; -import { glob } from "glob"; -import { report } from "../build/log.reporter.js"; -const installScripts = new Map([ - ["wasmtime", "curl https://wasmtime.dev/install.sh -sSf | bash"], - ["wasmer", "curl https://get.wasmer.io -sSfL | sh"], -]); -export async function run() { - const reports = []; - const config = Object.assign( - new Config(), - JSON.parse(readFileSync("./as-test.config.json").toString()), - ); - const inputFiles = await glob(config.input); - console.log( - chalk.dim("Running tests using " + config.runOptions.runtime.name + ""), - ); - let execPath = ""; - const PATH = process.env["PATH"]?.split(":"); - for (const bin of PATH) { - if (bin.startsWith("/mnt/")) continue; // WSL - if (!existsSync(bin)) continue; - for (const file of readdirSync(bin)) { - if ( - file == config.runOptions.runtime.run.split(" ")[0] || - file == config.runOptions.runtime.run.split(" ")[0] + ".exe" - ) { - execPath = bin + "/" + file; - } - } - } - if (!execPath) { - console.log( - chalk.bgRed(" ERROR ") + - chalk.dim(":") + - " could not locate " + - config.runOptions.runtime.run.split(" ")[0] + - " in your PATH variable. Either set it, or install it" + - (config.runOptions.runtime.run.split(" ")[0] - ? "using " + - chalk.dim( - installScripts.get(config.runOptions.runtime.run.split(" ")[0]), - ) - : "."), - ); - } - for (const file of inputFiles) { - const outFile = - config.outDir + - "/" + - file.slice(file.lastIndexOf("/") + 1).replace(".ts", ".wasm"); - let cmd = config.runOptions.runtime.run - .replace(config.runOptions.runtime.name, execPath) - .replace("", outFile); - if ( - config.runOptions.runtime.run.startsWith("bun") || - config.runOptions.runtime.run.startsWith("node") || - config.runOptions.runtime.run.startsWith("deno") - ) { - cmd = config.runOptions.runtime.run - .replace(config.runOptions.runtime.name, execPath) - .replace("", outFile.replace(".wasm", ".js")); - } - const stdout = execSync(cmd); - const report = stdout - .toString() - .slice( - stdout.indexOf("--REPORT-START--") + 16, - stdout.indexOf("--REPORT-END--"), - ); - console.dir(JSON.parse(report), { depth: 256 }); - reports.push(JSON.parse(report)); - } - report(JSON.stringify(reports)); -} diff --git a/bin/cli/types.js b/bin/cli/types.js deleted file mode 100644 index 8c2a2c1..0000000 --- a/bin/cli/types.js +++ /dev/null @@ -1,31 +0,0 @@ -export class Config { - constructor() { - this.input = ["./assembly/__tests__/*.spec.ts"]; - this.outDir = "./build"; - this.config = "./asconfig.json"; - this.buildOptions = new BuildOptions(); - this.runOptions = new RunOptions(); - } -} -export class Suite { - constructor() { - this.name = ""; - } -} -export class BuildOptions { - constructor() { - this.args = []; - this.target = "wasi"; - } -} -export class RunOptions { - constructor() { - this.runtime = new Runtime(); - } -} -export class Runtime { - constructor() { - this.name = "wasmtime"; - this.run = "wasmtime "; - } -} diff --git a/bin/cli/util.js b/bin/cli/util.js deleted file mode 100644 index 8da941f..0000000 --- a/bin/cli/util.js +++ /dev/null @@ -1,23 +0,0 @@ -export function formatTime(ms) { - if (ms < 0) { - throw new Error("Time should be a non-negative number."); - } - // Convert milliseconds to microseconds - const us = ms * 1000; - const units = [ - { name: "μs", divisor: 1 }, - { name: "ms", divisor: 1000 }, - { name: "s", divisor: 1000 * 1000 }, - { name: "m", divisor: 60 * 1000 * 1000 }, - { name: "h", divisor: 60 * 60 * 1000 * 1000 }, - { name: "d", divisor: 24 * 60 * 60 * 1000 * 1000 }, - ]; - for (let i = units.length - 1; i >= 0; i--) { - const unit = units[i]; - if (us >= unit.divisor) { - const value = Math.round((us / unit.divisor) * 1000) / 1000; - return `${value}${unit.name}`; - } - } - return `${us}us`; -} diff --git a/bin/run.js b/bin/run.js index 57cac41..a50ab6b 100644 --- a/bin/run.js +++ b/bin/run.js @@ -1,7 +1,6 @@ import chalk from "chalk"; import { execSync } from "child_process"; import { glob } from "glob"; -import { report } from "../build/log.reporter.js"; import { getExec, loadConfig } from "./util.js"; import * as path from "path"; const CONFIG_PATH = path.join(process.cwd(), "./as-test.config.json"); @@ -26,14 +25,17 @@ export async function run() { .replace(exec, execPath) .replace("", outFile.replace(".wasm", ".js")); } - const stdout = execSync(cmd); + execSync(cmd, { stdio: "inherit" }); /* process.stdout.write(stdout.toString().slice(0, stdout.indexOf("--REPORT-START--"))); const report = stdout - .toString() - .slice(stdout.indexOf("--REPORT-START--") + 16, stdout.indexOf("--REPORT-END--")); - reports.push(JSON.parse(report)); + .toString() + .slice( + stdout.indexOf("--REPORT-START--") + 16, + stdout.indexOf("--REPORT-END--"), + ); + reports.push(JSON.parse(report));*/ } - report(JSON.stringify(reports)); + //report(JSON.stringify(reports)); for (const report of reports) { if (report.verdict == "fail") process.exit(1); diff --git a/bin/types.js b/bin/types.js index 46109c8..3e7314f 100644 --- a/bin/types.js +++ b/bin/types.js @@ -2,7 +2,7 @@ export class Config { constructor() { this.input = ["./assembly/__tests__/*.spec.ts"]; this.outDir = "./build"; - this.config = "./asconfig.json"; + this.config = "none"; this.buildOptions = new BuildOptions(); this.runOptions = new RunOptions(); } diff --git a/cli/run.ts b/cli/run.ts index 1015db9..7c9b80d 100644 --- a/cli/run.ts +++ b/cli/run.ts @@ -1,5 +1,3 @@ -import { existsSync, readFileSync, readdirSync } from "fs"; -import { Config } from "./types.js"; import chalk from "chalk"; import { execSync } from "child_process"; import { glob } from "glob"; @@ -37,7 +35,7 @@ export async function run() { .replace(exec, execPath) .replace("", outFile.replace(".wasm", ".js")); } - const stdout = execSync(cmd); + execSync(cmd, { stdio: "inherit"});/* process.stdout.write(stdout.toString().slice(0, stdout.indexOf("--REPORT-START--"))); const report = stdout .toString() @@ -45,10 +43,10 @@ export async function run() { stdout.indexOf("--REPORT-START--") + 16, stdout.indexOf("--REPORT-END--"), ); - reports.push(JSON.parse(report)); + reports.push(JSON.parse(report));*/ } - report(JSON.stringify(reports)); + //report(JSON.stringify(reports)); for (const report of reports) { if (report.verdict == "fail") process.exit(1); diff --git a/cli/types.ts b/cli/types.ts index adf648a..46e8bf5 100644 --- a/cli/types.ts +++ b/cli/types.ts @@ -1,7 +1,7 @@ export class Config { input: string[] = ["./assembly/__tests__/*.spec.ts"]; outDir: string = "./build"; - config: string = "./asconfig.json"; + config: string = "none"; plugins: {}; buildOptions: BuildOptions = new BuildOptions(); runOptions: RunOptions = new RunOptions(); diff --git a/reporters/log/index.ts b/reporters/log/index.ts index ca6fd94..7e7feac 100644 --- a/reporters/log/index.ts +++ b/reporters/log/index.ts @@ -89,10 +89,10 @@ class LogReporter { let out = ""; this.depthInc(); if (suite.verdict == Verdict.Ok) { - this.passedTests++; + this.passedSuites++; out += `${this.depth}${rainbow.bgGreenBright(" PASS ")} ${rainbow.dimMk(suite.description)} ${rainbow.italicMk(suite.time.format())}\n\n`; } else if (suite.verdict == Verdict.Fail) { - this.failedTests++; + this.failedSuites++; out += `${this.depth}${rainbow.bgRed(" FAIL ")} ${rainbow.dimMk(suite.description)} ${rainbow.italicMk(suite.time.format())}\n\n`; } else if (suite.verdict == Verdict.None) { out += `${this.depth}${rainbow.bgBlackBright(" EMPTY ")} ${rainbow.dimMk(suite.description)} ${rainbow.italicMk("0.00μs")}\n\n`; @@ -101,7 +101,10 @@ class LogReporter { for (let i = 0; i < suite.tests.length; i++) { const _test = unchecked(suite.tests[i]); if (_test.verdict != Verdict.Ok) { + this.failedTests++; if (!this.failed.includes(suite)) this.failed.push(suite); + } else { + this.passedTests++; } } diff --git a/reporters/report.ts b/reporters/report.ts index b372b4c..5072817 100644 --- a/reporters/report.ts +++ b/reporters/report.ts @@ -31,6 +31,8 @@ export class SuiteReport { description: string = ""; tests: TestReport[] = []; suites: SuiteReport[] = []; + logs: string[] = []; + errors: string[] = []; static wrap(suite: Suite): SuiteReport { const report = new SuiteReport(); @@ -48,6 +50,7 @@ export class SuiteReport { report.verdict = suite.verdict; report.kind = suite.kind; report.time = suite.time; + report.logs = suite.logs; return report; } @@ -95,13 +98,11 @@ function formatTime(time: f64): string { const unit = units[i]; if (us >= unit.divisor) { const value = (Math.round((us / unit.divisor) * 100) / 100).toString(); - const precision = value.indexOf("."); - return `${value.slice(0, precision) + value.slice(precision, precision + 3)}${unit.name}`; + return `${value}${unit.name}`; } } - const _us = us.toString(); - const precision = _us.indexOf("."); + const _us = (Math.round(us * 100) / 100).toString(); - return `${_us.slice(0, precision) + _us.slice(precision, precision + 3)}μs`; + return `${_us}μs`; } diff --git a/test.ts b/test.ts new file mode 100644 index 0000000..77a4975 --- /dev/null +++ b/test.ts @@ -0,0 +1,14 @@ +let lines = 0; +function log(str: string): void { + lines += 2; + process.stdout.write(str + "\n"); +} +function editLn(line: number, str: string): void { + process.stdout.write("\033[1A"); + process.stdout.write("\033[2K"); + process.stdout.write("\033[0G"); + process.stdout.write(str + "\n"); +} + +log("hello"); +editLn(0,"bahaha") \ No newline at end of file diff --git a/transform/lib/coverage.js b/transform/lib/coverage.js index 0b23a27..4df9aaf 100644 --- a/transform/lib/coverage.js +++ b/transform/lib/coverage.js @@ -1,47 +1,44 @@ -import { - BlockStatement, - ExpressionStatement, - Node, -} from "assemblyscript/dist/assemblyscript.js"; +import { BlockStatement, ExpressionStatement, Node, } from "assemblyscript/dist/assemblyscript.js"; import { BaseVisitor, SimpleParser } from "visitor-as/dist/index.js"; import { RangeTransform } from "visitor-as/dist/transformRange.js"; import { isStdlib } from "visitor-as/dist/utils.js"; var CoverType; (function (CoverType) { - CoverType[(CoverType["Function"] = 0)] = "Function"; - CoverType[(CoverType["Expression"] = 1)] = "Expression"; - CoverType[(CoverType["Block"] = 2)] = "Block"; + CoverType[CoverType["Function"] = 0] = "Function"; + CoverType[CoverType["Expression"] = 1] = "Expression"; + CoverType[CoverType["Block"] = 2] = "Block"; })(CoverType || (CoverType = {})); class CoverPoint { - file = ""; - hash = ""; - line = 0; - column = 0; - type; - executed = false; + file = ""; + hash = ""; + line = 0; + column = 0; + type; + executed = false; } export class CoverageTransform extends BaseVisitor { - mustImport = false; - points = new Map(); - globalStatements = []; - visitBinaryExpression(node) { - super.visitBinaryExpression(node); - if (node.visited) return; - node.visited = true; - const path = node.range.source.normalizedPath; - switch (node.operator) { - case 98: - case 97: { - const right = node.right; - const rightLc = getLineCol(node); - const point = new CoverPoint(); - point.line = rightLc?.line; - point.column = rightLc?.column; - point.file = path; - point.type = CoverType.Expression; - point.hash = hash(point); - const replacer = new RangeTransform(node); - const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ + mustImport = false; + points = new Map(); + globalStatements = []; + visitBinaryExpression(node) { + super.visitBinaryExpression(node); + if (node.visited) + return; + node.visited = true; + const path = node.range.source.normalizedPath; + switch (node.operator) { + case 98: + case 97: { + const right = node.right; + const rightLc = getLineCol(node); + const point = new CoverPoint(); + point.line = rightLc?.line; + point.column = rightLc?.column; + point.file = path; + point.type = CoverType.Expression; + point.hash = hash(point); + const replacer = new RangeTransform(node); + const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ file: "${point.file}", hash: "${point.hash}", line: ${point.line}, @@ -49,35 +46,35 @@ export class CoverageTransform extends BaseVisitor { type: "Expression", executed: false });`); - replacer.visit(registerStmt); - let coverExpression = SimpleParser.parseExpression( - `(__COVER("${point.hash}"), $$REPLACE_ME)`, - ); - replacer.visit(coverExpression); - coverExpression.expression.expressions[1] = right; - node.right = coverExpression; - this.globalStatements.push(registerStmt); - break; - } + replacer.visit(registerStmt); + let coverExpression = SimpleParser.parseExpression(`(__COVER("${point.hash}"), $$REPLACE_ME)`); + replacer.visit(coverExpression); + coverExpression.expression.expressions[1] = right; + node.right = coverExpression; + this.globalStatements.push(registerStmt); + break; + } + } } - } - visitMethodDeclaration(node) { - super.visitMethodDeclaration(node); - if (node.visited) return; - node.visited = true; - if (node.body) { - if (node.body.visited) return; - node.body.visited = true; - const path = node.range.source.normalizedPath; - const funcLc = getLineCol(node); - const point = new CoverPoint(); - point.line = funcLc?.line; - point.column = funcLc?.column; - point.file = path; - point.type = CoverType.Function; - point.hash = hash(point); - const replacer = new RangeTransform(node); - const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ + visitMethodDeclaration(node) { + super.visitMethodDeclaration(node); + if (node.visited) + return; + node.visited = true; + if (node.body) { + if (node.body.visited) + return; + node.body.visited = true; + const path = node.range.source.normalizedPath; + const funcLc = getLineCol(node); + const point = new CoverPoint(); + point.line = funcLc?.line; + point.column = funcLc?.column; + point.file = path; + point.type = CoverType.Function; + point.hash = hash(point); + const replacer = new RangeTransform(node); + const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ file: "${point.file}", hash: "${point.hash}", line: ${point.line}, @@ -85,34 +82,33 @@ export class CoverageTransform extends BaseVisitor { type: "Function", executed: false })`); - replacer.visit(registerStmt); - const coverStmt = SimpleParser.parseStatement( - `__COVER("${point.hash}")`, - true, - ); - replacer.visit(coverStmt); - const bodyBlock = node.body; - bodyBlock.statements.unshift(coverStmt); - this.globalStatements.push(registerStmt); + replacer.visit(registerStmt); + const coverStmt = SimpleParser.parseStatement(`__COVER("${point.hash}")`, true); + replacer.visit(coverStmt); + const bodyBlock = node.body; + bodyBlock.statements.unshift(coverStmt); + this.globalStatements.push(registerStmt); + } } - } - visitParameter(node) { - if (node.visited) return; - node.visited = true; - const path = node.range.source.normalizedPath; - if (node.initializer) { - if (node.initializer.visited) return; - node.initializer.visited = true; - super.visitParameter(node); - const paramLc = getLineCol(node.initializer); - const point = new CoverPoint(); - point.line = paramLc?.line; - point.column = paramLc?.column; - point.file = path; - point.type = CoverType.Expression; - point.hash = hash(point); - const replacer = new RangeTransform(node); - const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ + visitParameter(node) { + if (node.visited) + return; + node.visited = true; + const path = node.range.source.normalizedPath; + if (node.initializer) { + if (node.initializer.visited) + return; + node.initializer.visited = true; + super.visitParameter(node); + const paramLc = getLineCol(node.initializer); + const point = new CoverPoint(); + point.line = paramLc?.line; + point.column = paramLc?.column; + point.file = path; + point.type = CoverType.Expression; + point.hash = hash(point); + const replacer = new RangeTransform(node); + const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ file: "${point.file}", hash: "${point.hash}", line: ${point.line}, @@ -120,33 +116,34 @@ export class CoverageTransform extends BaseVisitor { type: "Expression", executed: false })`); - replacer.visit(registerStmt); - const coverExpression = SimpleParser.parseExpression( - `(__COVER("${point.hash}"), $$REPLACE_ME)`, - ); - replacer.visit(coverExpression); - coverExpression.expression.expressions[1] = node.initializer; - node.initializer = coverExpression; - this.globalStatements.push(registerStmt); + replacer.visit(registerStmt); + const coverExpression = SimpleParser.parseExpression(`(__COVER("${point.hash}"), $$REPLACE_ME)`); + replacer.visit(coverExpression); + coverExpression.expression.expressions[1] = + node.initializer; + node.initializer = coverExpression; + this.globalStatements.push(registerStmt); + } } - } - visitFunctionDeclaration(node, isDefault) { - super.visitFunctionDeclaration(node, isDefault); - if (node.visited) return; - node.visited = true; - if (node.body) { - if (node.body.visited) return; - node.body.visited = true; - const path = node.range.source.normalizedPath; - const funcLc = getLineCol(node); - const point = new CoverPoint(); - point.line = funcLc?.line; - point.column = funcLc?.column; - point.file = path; - point.type = CoverType.Function; - point.hash = hash(point); - const replacer = new RangeTransform(node); - const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ + visitFunctionDeclaration(node, isDefault) { + super.visitFunctionDeclaration(node, isDefault); + if (node.visited) + return; + node.visited = true; + if (node.body) { + if (node.body.visited) + return; + node.body.visited = true; + const path = node.range.source.normalizedPath; + const funcLc = getLineCol(node); + const point = new CoverPoint(); + point.line = funcLc?.line; + point.column = funcLc?.column; + point.file = path; + point.type = CoverType.Function; + point.hash = hash(point); + const replacer = new RangeTransform(node); + const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ file: "${point.file}", hash: "${point.hash}", line: ${point.line}, @@ -154,57 +151,54 @@ export class CoverageTransform extends BaseVisitor { type: "Function", executed: false })`); - replacer.visit(registerStmt); - this.globalStatements.push(registerStmt); - if (node.body.kind === 35) { - const coverStmt = SimpleParser.parseStatement(`{ + replacer.visit(registerStmt); + this.globalStatements.push(registerStmt); + if (node.body.kind === 35) { + const coverStmt = SimpleParser.parseStatement(`{ __COVER("${point.hash}") return $$REPLACE_ME }`); - replacer.visit(coverStmt); - const bodyReturn = coverStmt.statements[1]; - const body = node.body; - node.arrowKind = 2; - bodyReturn.value = body.expression; - node.body = body; - } else { - const coverStmt = SimpleParser.parseStatement( - `__COVER("${point.hash}")`, - true, - ); - replacer.visit(coverStmt); - if (node.body instanceof BlockStatement) { - node.body.statements.unshift(coverStmt); - } else if (node.body instanceof ExpressionStatement) { - const expression = node.body.expression; - node.body = Node.createBlockStatement( - [Node.createReturnStatement(expression, expression.range)], - expression.range, - ); - const bodyBlock = node.body; - bodyBlock.statements.unshift(coverStmt); + replacer.visit(coverStmt); + const bodyReturn = coverStmt.statements[1]; + const body = node.body; + node.arrowKind = 2; + bodyReturn.value = body.expression; + node.body = body; + } + else { + const coverStmt = SimpleParser.parseStatement(`__COVER("${point.hash}")`, true); + replacer.visit(coverStmt); + if (node.body instanceof BlockStatement) { + node.body.statements.unshift(coverStmt); + } + else if (node.body instanceof ExpressionStatement) { + const expression = node.body.expression; + node.body = Node.createBlockStatement([Node.createReturnStatement(expression, expression.range)], expression.range); + const bodyBlock = node.body; + bodyBlock.statements.unshift(coverStmt); + } + } } - } } - } - visitIfStatement(node) { - if (node.visited) return; - node.visited = true; - let visitIfTrue = false; - let visitIfFalse = false; - const ifTrue = node.ifTrue; - const ifFalse = node.ifFalse; - const path = node.range.source.normalizedPath; - if (ifTrue.kind !== 30) { - const trueLc = getLineCol(ifTrue); - const point = new CoverPoint(); - point.line = trueLc?.line; - point.column = trueLc?.column; - point.file = path; - point.type = CoverType.Expression; - point.hash = hash(point); - const replacer = new RangeTransform(ifTrue); - const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ + visitIfStatement(node) { + if (node.visited) + return; + node.visited = true; + let visitIfTrue = false; + let visitIfFalse = false; + const ifTrue = node.ifTrue; + const ifFalse = node.ifFalse; + const path = node.range.source.normalizedPath; + if (ifTrue.kind !== 30) { + const trueLc = getLineCol(ifTrue); + const point = new CoverPoint(); + point.line = trueLc?.line; + point.column = trueLc?.column; + point.file = path; + point.type = CoverType.Expression; + point.hash = hash(point); + const replacer = new RangeTransform(ifTrue); + const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ file: "${point.file}", hash: "${point.hash}", line: ${point.line}, @@ -212,28 +206,25 @@ export class CoverageTransform extends BaseVisitor { type: "Expression", executed: false })`); - replacer.visit(registerStmt); - const coverStmt = SimpleParser.parseStatement( - `{__COVER("${point.hash}")};`, - true, - ); - replacer.visit(coverStmt); - coverStmt.statements.push(ifTrue); - node.ifTrue = coverStmt; - this.globalStatements.push(registerStmt); - visitIfTrue = true; - visitIfFalse = !!ifFalse; - } - if (ifFalse && ifFalse.kind !== 30) { - const falseLc = getLineCol(ifFalse); - const point = new CoverPoint(); - point.line = falseLc?.line; - point.column = falseLc?.column; - point.file = path; - point.type = CoverType.Expression; - point.hash = hash(point); - const replacer = new RangeTransform(ifTrue); - const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ + replacer.visit(registerStmt); + const coverStmt = SimpleParser.parseStatement(`{__COVER("${point.hash}")};`, true); + replacer.visit(coverStmt); + coverStmt.statements.push(ifTrue); + node.ifTrue = coverStmt; + this.globalStatements.push(registerStmt); + visitIfTrue = true; + visitIfFalse = !!ifFalse; + } + if (ifFalse && ifFalse.kind !== 30) { + const falseLc = getLineCol(ifFalse); + const point = new CoverPoint(); + point.line = falseLc?.line; + point.column = falseLc?.column; + point.file = path; + point.type = CoverType.Expression; + point.hash = hash(point); + const replacer = new RangeTransform(ifTrue); + const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ file: "${point.file}", hash: "${point.hash}", line: ${point.line}, @@ -241,50 +232,51 @@ export class CoverageTransform extends BaseVisitor { type: "Expression", executed: false })`); - replacer.visit(registerStmt); - const coverStmt = SimpleParser.parseStatement( - `{__COVER("${point.hash}")};`, - true, - ); - replacer.visit(coverStmt); - coverStmt.statements.push(ifFalse); - node.ifFalse = coverStmt; - this.globalStatements.push(registerStmt); - visitIfTrue = true; - visitIfFalse = true; - } - if (visitIfTrue || visitIfFalse) { - if (visitIfTrue) { - if (ifTrue.visited) return; - ifTrue.visited = true; - this._visit(ifTrue); - } - if (visitIfFalse) { - if (ifFalse.visited) return; - ifFalse.visited = true; - this._visit(ifFalse); - } - } else { - super.visitIfStatement(node); + replacer.visit(registerStmt); + const coverStmt = SimpleParser.parseStatement(`{__COVER("${point.hash}")};`, true); + replacer.visit(coverStmt); + coverStmt.statements.push(ifFalse); + node.ifFalse = coverStmt; + this.globalStatements.push(registerStmt); + visitIfTrue = true; + visitIfFalse = true; + } + if (visitIfTrue || visitIfFalse) { + if (visitIfTrue) { + if (ifTrue.visited) + return; + ifTrue.visited = true; + this._visit(ifTrue); + } + if (visitIfFalse) { + if (ifFalse.visited) + return; + ifFalse.visited = true; + this._visit(ifFalse); + } + } + else { + super.visitIfStatement(node); + } } - } - visitTernaryExpression(node) { - if (node.visited) return; - node.visited = true; - super.visitTernaryExpression(node); - const trueExpression = node.ifThen; - const falseExpression = node.ifElse; - const path = node.range.source.normalizedPath; - { - const trueLc = getLineCol(trueExpression); - const point = new CoverPoint(); - point.line = trueLc?.line; - point.column = trueLc?.column; - point.file = path; - point.type = CoverType.Expression; - point.hash = hash(point); - const replacer = new RangeTransform(trueExpression); - const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ + visitTernaryExpression(node) { + if (node.visited) + return; + node.visited = true; + super.visitTernaryExpression(node); + const trueExpression = node.ifThen; + const falseExpression = node.ifElse; + const path = node.range.source.normalizedPath; + { + const trueLc = getLineCol(trueExpression); + const point = new CoverPoint(); + point.line = trueLc?.line; + point.column = trueLc?.column; + point.file = path; + point.type = CoverType.Expression; + point.hash = hash(point); + const replacer = new RangeTransform(trueExpression); + const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ file: "${point.file}", hash: "${point.hash}", line: ${point.line}, @@ -292,25 +284,24 @@ export class CoverageTransform extends BaseVisitor { type: "Expression", executed: false })`); - replacer.visit(registerStmt); - const coverExpression = SimpleParser.parseExpression( - `(__COVER("${point.hash}"), $$REPLACE_ME)`, - ); - replacer.visit(coverExpression); - coverExpression.expression.expressions[1] = trueExpression; - node.ifThen = coverExpression; - this.globalStatements.push(registerStmt); - } - { - const falseLc = getLineCol(falseExpression); - const point = new CoverPoint(); - point.line = falseLc?.line; - point.column = falseLc?.column; - point.file = path; - point.type = CoverType.Expression; - point.hash = hash(point); - const replacer = new RangeTransform(falseExpression); - const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ + replacer.visit(registerStmt); + const coverExpression = SimpleParser.parseExpression(`(__COVER("${point.hash}"), $$REPLACE_ME)`); + replacer.visit(coverExpression); + coverExpression.expression.expressions[1] = + trueExpression; + node.ifThen = coverExpression; + this.globalStatements.push(registerStmt); + } + { + const falseLc = getLineCol(falseExpression); + const point = new CoverPoint(); + point.line = falseLc?.line; + point.column = falseLc?.column; + point.file = path; + point.type = CoverType.Expression; + point.hash = hash(point); + const replacer = new RangeTransform(falseExpression); + const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ file: "${point.file}", hash: "${point.hash}", line: ${point.line}, @@ -318,29 +309,29 @@ export class CoverageTransform extends BaseVisitor { type: "Expression", executed: false })`); - replacer.visit(registerStmt); - const coverExpression = SimpleParser.parseExpression( - `(__COVER("${point.hash}"), $$REPLACE_ME)`, - ); - replacer.visit(coverExpression); - coverExpression.expression.expressions[1] = falseExpression; - node.ifElse = coverExpression; - this.globalStatements.push(registerStmt); + replacer.visit(registerStmt); + const coverExpression = SimpleParser.parseExpression(`(__COVER("${point.hash}"), $$REPLACE_ME)`); + replacer.visit(coverExpression); + coverExpression.expression.expressions[1] = + falseExpression; + node.ifElse = coverExpression; + this.globalStatements.push(registerStmt); + } } - } - visitSwitchCase(node) { - if (node.visited) return; - node.visited = true; - const path = node.range.source.normalizedPath; - const caseLc = getLineCol(node); - const point = new CoverPoint(); - point.line = caseLc?.line; - point.column = caseLc?.column; - point.file = path; - point.type = CoverType.Block; - point.hash = hash(point); - const replacer = new RangeTransform(node); - const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ + visitSwitchCase(node) { + if (node.visited) + return; + node.visited = true; + const path = node.range.source.normalizedPath; + const caseLc = getLineCol(node); + const point = new CoverPoint(); + point.line = caseLc?.line; + point.column = caseLc?.column; + point.file = path; + point.type = CoverType.Block; + point.hash = hash(point); + const replacer = new RangeTransform(node); + const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ file: "${point.file}", hash: "${point.hash}", line: ${point.line}, @@ -348,26 +339,27 @@ export class CoverageTransform extends BaseVisitor { type: "Block", executed: false })`); - replacer.visit(registerStmt); - const coverStmt = SimpleParser.parseStatement(`__COVER("${point.hash}")`); - replacer.visit(coverStmt); - this.globalStatements.push(registerStmt); - super.visitSwitchCase(node); - node.statements.unshift(coverStmt); - } - visitBlockStatement(node) { - if (node.visited) return; - node.visited = true; - const path = node.range.source.normalizedPath; - const blockLc = getLineCol(node); - const point = new CoverPoint(); - point.line = blockLc?.line; - point.column = blockLc?.column; - point.file = path; - point.type = CoverType.Block; - point.hash = hash(point); - const replacer = new RangeTransform(node); - const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ + replacer.visit(registerStmt); + const coverStmt = SimpleParser.parseStatement(`__COVER("${point.hash}")`); + replacer.visit(coverStmt); + this.globalStatements.push(registerStmt); + super.visitSwitchCase(node); + node.statements.unshift(coverStmt); + } + visitBlockStatement(node) { + if (node.visited) + return; + node.visited = true; + const path = node.range.source.normalizedPath; + const blockLc = getLineCol(node); + const point = new CoverPoint(); + point.line = blockLc?.line; + point.column = blockLc?.column; + point.file = path; + point.type = CoverType.Block; + point.hash = hash(point); + const replacer = new RangeTransform(node); + const registerStmt = SimpleParser.parseTopLevelStatement(`__REGISTER({ file: "${point.file}", hash: "${point.hash}", line: ${point.line}, @@ -375,49 +367,51 @@ export class CoverageTransform extends BaseVisitor { type: "Block", executed: false })`); - replacer.visit(registerStmt); - const coverStmt = SimpleParser.parseStatement(`__COVER("${point.hash}")`); - replacer.visit(coverStmt); - this.globalStatements.push(registerStmt); - super.visitBlockStatement(node); - node.statements.unshift(coverStmt); - } - visitSource(node) { - if (node.isLibrary) return; - if (node.simplePath === "coverage") return; - if (isStdlib(node)) return; - super.visitSource(node); - } + replacer.visit(registerStmt); + const coverStmt = SimpleParser.parseStatement(`__COVER("${point.hash}")`); + replacer.visit(coverStmt); + this.globalStatements.push(registerStmt); + super.visitBlockStatement(node); + node.statements.unshift(coverStmt); + } + visitSource(node) { + if (node.isLibrary) + return; + if (node.simplePath === "coverage") + return; + if (isStdlib(node)) + return; + super.visitSource(node); + } } function djb2Hash(str) { - const points = Array.from(str); - let h = 5381; - for (let p = 0; p < points.length; p++) - h = ((h << 5) - h + points[p].codePointAt(0)) | 0; - return h; + const points = Array.from(str); + let h = 5381; + for (let p = 0; p < points.length; p++) + h = ((h << 5) - h + points[p].codePointAt(0)) | 0; + return h; } function hash(point) { - const hsh = djb2Hash( - point.file + - point.line.toString() + - point.column.toString() + - point.type.toString(), - ); - if (hsh < 0) { - const out = hsh.toString(16); - return "3" + out.slice(1); - } else { - return hsh.toString(16); - } + const hsh = djb2Hash(point.file + + point.line.toString() + + point.column.toString() + + point.type.toString()); + if (hsh < 0) { + const out = hsh.toString(16); + return "3" + out.slice(1); + } + else { + return hsh.toString(16); + } } class LineColumn { - line; - column; + line; + column; } function getLineCol(node) { - return { - line: node.range.source.lineAt(node.range.start), - column: node.range.source.columnAt(), - }; + return { + line: node.range.source.lineAt(node.range.start), + column: node.range.source.columnAt(), + }; } -//# sourceMappingURL=coverage.js.map +//# sourceMappingURL=coverage.js.map \ No newline at end of file diff --git a/transform/lib/index.js b/transform/lib/index.js index 8978b87..bafcf54 100644 --- a/transform/lib/index.js +++ b/transform/lib/index.js @@ -1,39 +1,44 @@ import { Transform } from "assemblyscript/dist/transform.js"; -import { Node } from "assemblyscript/dist/assemblyscript.js"; +import { Node, Source, Tokenizer } from "assemblyscript/dist/assemblyscript.js"; import { isStdlib } from "visitor-as/dist/utils.js"; +import { CoverageTransform } from "./coverage.js"; +import { MockTransform } from "./mock.js"; export default class Transformer extends Transform { - afterParse(parser) { - const sources = parser.sources - .filter((source) => !isStdlib(source)) - .sort((_a, _b) => { - const a = _a.internalPath; - const b = _b.internalPath; - if (a[0] === "~" && b[0] !== "~") { - return -1; - } else if (a[0] !== "~" && b[0] === "~") { - return 1; - } else { - return 0; + afterParse(parser) { + const mock = new MockTransform(); + const coverage = new CoverageTransform(); + const sources = parser.sources + .filter((source) => !isStdlib(source)) + .sort((_a, _b) => { + const a = _a.internalPath; + const b = _b.internalPath; + if (a[0] === "~" && b[0] !== "~") { + return -1; + } + else if (a[0] !== "~" && b[0] === "~") { + return 1; + } + else { + return 0; + } + }); + const entryFile = sources.find((v) => v.sourceKind == 1).simplePath; + for (const source of sources) { + const node = Node.createVariableStatement(null, [ + Node.createVariableDeclaration(Node.createIdentifierExpression("ENTRY_FILE", source.range), null, 8, null, Node.createStringLiteralExpression(entryFile + ".ts", source.range), source.range), + ], source.range); + source.statements.unshift(node); + mock.visit(source); + coverage.visit(source); + if (coverage.globalStatements.length) { + source.statements.unshift(...coverage.globalStatements); + const tokenizer = new Tokenizer(new Source(0, source.normalizedPath, 'import { __REGISTER, __COVER } from "as-test/assembly/coverage";')); + parser.currentSource = tokenizer.source; + source.statements.unshift(parser.parseTopLevelStatement(tokenizer)); + parser.currentSource = source; + } } - }); - const entryFile = sources.find((v) => v.sourceKind == 1).simplePath; - for (const source of sources) { - const node = Node.createVariableStatement( - null, - [ - Node.createVariableDeclaration( - Node.createIdentifierExpression("ENTRY_FILE", source.range), - null, - 8, - null, - Node.createStringLiteralExpression(entryFile + ".ts", source.range), - source.range, - ), - ], - source.range, - ); - source.statements.unshift(node); + coverage.globalStatements = []; } - } } -//# sourceMappingURL=index.js.map +//# sourceMappingURL=index.js.map \ No newline at end of file diff --git a/transform/lib/index.js.map b/transform/lib/index.js.map index 1eb96ac..be19337 100644 --- a/transform/lib/index.js.map +++ b/transform/lib/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EAEL,IAAI,EAGL,MAAM,uCAAuC,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AAEpD,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,SAAS;IAEhD,UAAU,CAAC,MAAc;QAMvB,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO;aAC3B,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;aACrC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;YACf,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC;YAC1B,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC;YAC1B,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACjC,OAAO,CAAC,CAAC,CAAC;YACZ,CAAC;iBAAM,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACxC,OAAO,CAAC,CAAC;YACX,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,CAAC;YACX,CAAC;QACH,CAAC,CAAC,CAAC;QACL,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,KAAwB,CAAC,CAAC,UAAU,CAAC;QAErF,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,uBAAuB,CACvC,IAAI,EACJ;gBACE,IAAI,CAAC,yBAAyB,CAC5B,IAAI,CAAC,0BAA0B,CAC7B,YAAY,EACZ,MAAM,CAAC,KAAK,CACb,EACD,IAAI,KAEJ,IAAI,EACJ,IAAI,CAAC,6BAA6B,CAChC,SAAS,GAAG,KAAK,EACjB,MAAM,CAAC,KAAK,CACb,EACD,MAAM,CAAC,KAAK,CACb;aACF,EACD,MAAM,CAAC,KAAK,CACb,CAAA;YACD,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QAgBlC,CAAC;IAEH,CAAC;CACF"} \ No newline at end of file +{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,kCAAkC,CAAC;AAC7D,OAAO,EAEL,IAAI,EAGJ,MAAM,EACN,SAAS,EACV,MAAM,uCAAuC,CAAC;AAE/C,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAC;AACpD,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAClD,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAE1C,MAAM,CAAC,OAAO,OAAO,WAAY,SAAQ,SAAS;IAEhD,UAAU,CAAC,MAAc;QAEvB,MAAM,IAAI,GAAG,IAAI,aAAa,EAAE,CAAC;QACjC,MAAM,QAAQ,GAAG,IAAI,iBAAiB,EAAE,CAAC;QAGzC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO;aAC3B,MAAM,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;aACrC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,EAAE,EAAE;YACf,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC;YAC1B,MAAM,CAAC,GAAG,EAAE,CAAC,YAAY,CAAC;YAC1B,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACjC,OAAO,CAAC,CAAC,CAAC;YACZ,CAAC;iBAAM,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,GAAG,EAAE,CAAC;gBACxC,OAAO,CAAC,CAAC;YACX,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,CAAC;YACX,CAAC;QACH,CAAC,CAAC,CAAC;QACL,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAC5B,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,KAAwB,CAC5C,CAAC,UAAU,CAAC;QAEb,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;YAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,uBAAuB,CACvC,IAAI,EACJ;gBACE,IAAI,CAAC,yBAAyB,CAC5B,IAAI,CAAC,0BAA0B,CAAC,YAAY,EAAE,MAAM,CAAC,KAAK,CAAC,EAC3D,IAAI,KAEJ,IAAI,EACJ,IAAI,CAAC,6BAA6B,CAAC,SAAS,GAAG,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,EACnE,MAAM,CAAC,KAAK,CACb;aACF,EACD,MAAM,CAAC,KAAK,CACb,CAAC;YACF,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;YAChC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACnB,QAAQ,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;YACvB,IAAI,QAAQ,CAAC,gBAAgB,CAAC,MAAM,EAAE,CAAC;gBACrC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,GAAG,QAAQ,CAAC,gBAAgB,CAAC,CAAC;gBACxD,MAAM,SAAS,GAAG,IAAI,SAAS,CAC7B,IAAI,MAAM,IAER,MAAM,CAAC,cAAc,EACrB,kEAAkE,CACnE,CACF,CAAC;gBACF,MAAM,CAAC,aAAa,GAAG,SAAS,CAAC,MAAM,CAAC;gBACxC,MAAM,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,sBAAsB,CAAC,SAAS,CAAE,CAAC,CAAC;gBACrE,MAAM,CAAC,aAAa,GAAG,MAAM,CAAC;YAChC,CAAC;QACH,CAAC;QACD,QAAQ,CAAC,gBAAgB,GAAG,EAAE,CAAC;IACjC,CAAC;CACF"} \ No newline at end of file diff --git a/transform/lib/mock.js b/transform/lib/mock.js index bf06e2a..eb52278 100644 --- a/transform/lib/mock.js +++ b/transform/lib/mock.js @@ -1,68 +1,60 @@ -import { Node } from "assemblyscript/dist/assemblyscript.js"; +import { Node, } from "assemblyscript/dist/assemblyscript.js"; import { BaseVisitor } from "visitor-as/dist/index.js"; import { isStdlib, toString } from "visitor-as/dist/utils.js"; export class MockTransform extends BaseVisitor { - currentSource; - globalStatements = []; - fn = new Map(); - mocked = new Set(); - visitCallExpression(node) { - super.visitCallExpression(node); - const name = toString(node.expression) - .replaceAll(".", "_") - .replaceAll("[", "_") - .replaceAll("]", "_"); - if (this.mocked.has(name + "_mock")) { - node.expression = Node.createIdentifierExpression( - name + "_mock", - node.expression.range, - ); - return; + currentSource; + globalStatements = []; + fn = new Map(); + mocked = new Set(); + visitCallExpression(node) { + super.visitCallExpression(node); + const name = toString(node.expression) + .replaceAll(".", "_") + .replaceAll("[", "_") + .replaceAll("]", "_"); + if (this.mocked.has(name + "_mock")) { + node.expression = Node.createIdentifierExpression(name + "_mock", node.expression.range); + return; + } + if (name != "mockFn") + return; + const ov = node.args[0]; + const cb = node.args[1]; + const newName = ov.value + .replaceAll(".", "_") + .replaceAll("[", "_") + .replaceAll("]", "_"); + const newFn = Node.createFunctionDeclaration(Node.createIdentifierExpression(newName + "_mock", cb.range), cb.declaration.decorators, 0, cb.declaration.typeParameters, cb.declaration.signature, cb.declaration.body, cb.declaration.arrowKind, cb.range); + const stmts = this.currentSource.statements; + let index = -1; + for (let i = 0; i < stmts.length; i++) { + const stmt = stmts[i]; + if (stmt.range.start != node.range.start) + continue; + index = i; + break; + } + if (index === -1) + return; + stmts.splice(index, 1, newFn); + this.mocked.add(newFn.name.text); } - if (name != "mockFn") return; - const ov = node.args[0]; - const cb = node.args[1]; - const newName = ov.value - .replaceAll(".", "_") - .replaceAll("[", "_") - .replaceAll("]", "_"); - const newFn = Node.createFunctionDeclaration( - Node.createIdentifierExpression(newName + "_mock", cb.range), - cb.declaration.decorators, - 0, - cb.declaration.typeParameters, - cb.declaration.signature, - cb.declaration.body, - cb.declaration.arrowKind, - cb.range, - ); - const stmts = this.currentSource.statements; - let index = -1; - for (let i = 0; i < stmts.length; i++) { - const stmt = stmts[i]; - if (stmt.range.start != node.range.start) continue; - index = i; - break; + visitFunctionDeclaration(node, isDefault) { + super.visitFunctionDeclaration(node, isDefault); + const name = node.name.text; + if (!name) + return; + this.fn.set(name, node); } - if (index === -1) return; - stmts.splice(index, 1, newFn); - this.mocked.add(newFn.name.text); - } - visitFunctionDeclaration(node, isDefault) { - super.visitFunctionDeclaration(node, isDefault); - const name = node.name.text; - if (!name) return; - this.fn.set(name, node); - } - visitSource(node) { - if (node.isLibrary || isStdlib(node)) { - if (!node.normalizedPath.startsWith("~lib/as-test")) { - return; - } + visitSource(node) { + if (node.isLibrary || isStdlib(node)) { + if (!node.normalizedPath.startsWith("~lib/as-test")) { + return; + } + } + this.mocked = new Set(); + this.currentSource = node; + super.visitSource(node); } - this.mocked = new Set(); - this.currentSource = node; - super.visitSource(node); - } } -//# sourceMappingURL=mock.js.map +//# sourceMappingURL=mock.js.map \ No newline at end of file diff --git a/transform/src/index.ts b/transform/src/index.ts index 7d9a3cc..dda3766 100644 --- a/transform/src/index.ts +++ b/transform/src/index.ts @@ -4,16 +4,20 @@ import { Node, Parser, SourceKind, + Source, + Tokenizer } from "assemblyscript/dist/assemblyscript.js"; import { isStdlib } from "visitor-as/dist/utils.js"; +import { CoverageTransform } from "./coverage.js"; +import { MockTransform } from "./mock.js"; export default class Transformer extends Transform { // Trigger the transform after parse. afterParse(parser: Parser): void { // Create new transform - //const mock = new MockTransform(); - //const coverage = new CoverageTransform(); + const mock = new MockTransform(); + const coverage = new CoverageTransform(); // Sort the sources so that user scripts are visited last const sources = parser.sources @@ -49,22 +53,22 @@ export default class Transformer extends Transform { source.range, ); source.statements.unshift(node); - // mock.visit(source); - // coverage.visit(source); - // if (coverage.globalStatements.length) { - // source.statements.unshift(...coverage.globalStatements); - // const tokenizer = new Tokenizer( - // new Source( - // SourceKind.User, - // source.normalizedPath, - // 'import { __REGISTER, __COVER } from "as-test/assembly/coverage";', - // ), - // ); - // parser.currentSource = tokenizer.source; - // source.statements.unshift(parser.parseTopLevelStatement(tokenizer)!); - // parser.currentSource = source; - // } + mock.visit(source); + coverage.visit(source); + if (coverage.globalStatements.length) { + source.statements.unshift(...coverage.globalStatements); + const tokenizer = new Tokenizer( + new Source( + SourceKind.User, + source.normalizedPath, + 'import { __REGISTER, __COVER } from "as-test/assembly/coverage";', + ), + ); + parser.currentSource = tokenizer.source; + source.statements.unshift(parser.parseTopLevelStatement(tokenizer)!); + parser.currentSource = source; + } } - // coverage.globalStatements = []; + coverage.globalStatements = []; } }