ic-mops 0.20.2 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cli.ts +2 -1
- package/commands/{mmf1.ts → test/mmf1.ts} +23 -13
- package/commands/test/reporters/compact-reporter.ts +95 -0
- package/commands/test/reporters/files-reporter.ts +52 -0
- package/commands/test/reporters/reporter.ts +7 -0
- package/commands/test/reporters/verbose-reporter.ts +51 -0
- package/commands/{test.ts → test/test.ts} +63 -79
- package/commands/test/utils.ts +7 -0
- package/dist/cli.js +2 -1
- package/dist/commands/test/mmf1.d.ts +26 -0
- package/dist/commands/test/mmf1.js +98 -0
- package/dist/commands/test/reporter-files.d.ts +10 -0
- package/dist/commands/test/reporter-files.js +48 -0
- package/dist/commands/test/reporter-verbose.d.ts +10 -0
- package/dist/commands/test/reporter-verbose.js +56 -0
- package/dist/commands/test/reporters/compact-reporter.d.ts +13 -0
- package/dist/commands/test/reporters/compact-reporter.js +92 -0
- package/dist/commands/test/reporters/files-reporter.d.ts +11 -0
- package/dist/commands/test/reporters/files-reporter.js +50 -0
- package/dist/commands/test/reporters/reporter.d.ts +6 -0
- package/dist/commands/test/reporters/reporter.js +1 -0
- package/dist/commands/test/reporters/verbose-reporter.d.ts +11 -0
- package/dist/commands/test/reporters/verbose-reporter.js +56 -0
- package/dist/commands/test/test.d.ts +5 -0
- package/dist/commands/test/test.js +189 -0
- package/dist/commands/test/utils.d.ts +1 -0
- package/dist/commands/test/utils.js +6 -0
- package/dist/commands/test.js +43 -58
- package/dist/package.json +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { spawn, execSync } from 'node:child_process';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import chalk from 'chalk';
|
|
6
|
+
import { globSync } from 'glob';
|
|
7
|
+
import chokidar from 'chokidar';
|
|
8
|
+
import debounce from 'debounce';
|
|
9
|
+
import { sources } from '../sources.js';
|
|
10
|
+
import { getRootDir } from '../../mops.js';
|
|
11
|
+
import { parallel } from '../../parallel.js';
|
|
12
|
+
import { MMF1 } from './mmf1.js';
|
|
13
|
+
import { absToRel } from './utils.js';
|
|
14
|
+
import { VerboseReporter } from './reporters/verbose-reporter.js';
|
|
15
|
+
import { FilesReporter } from './reporters/files-reporter.js';
|
|
16
|
+
import { CompactReporter } from './reporters/compact-reporter.js';
|
|
17
|
+
let ignore = [
|
|
18
|
+
'**/node_modules/**',
|
|
19
|
+
'**/.mops/**',
|
|
20
|
+
'**/.vessel/**',
|
|
21
|
+
'**/.git/**',
|
|
22
|
+
];
|
|
23
|
+
let globConfig = {
|
|
24
|
+
nocase: true,
|
|
25
|
+
ignore: ignore,
|
|
26
|
+
};
|
|
27
|
+
export async function test(filter = '', { watch = false, reporter = 'verbose' } = {}) {
|
|
28
|
+
let rootDir = getRootDir();
|
|
29
|
+
if (watch) {
|
|
30
|
+
// todo: run only changed for *.test.mo?
|
|
31
|
+
// todo: run all for *.mo?
|
|
32
|
+
let run = debounce(async () => {
|
|
33
|
+
console.clear();
|
|
34
|
+
process.stdout.write('\x1Bc');
|
|
35
|
+
await runAll(filter, reporter);
|
|
36
|
+
console.log('-'.repeat(50));
|
|
37
|
+
console.log('Waiting for file changes...');
|
|
38
|
+
console.log(chalk.gray((`Press ${chalk.gray('Ctrl+C')} to exit.`)));
|
|
39
|
+
}, 200);
|
|
40
|
+
let watcher = chokidar.watch([
|
|
41
|
+
path.join(rootDir, '**/*.mo'),
|
|
42
|
+
path.join(rootDir, 'mops.toml'),
|
|
43
|
+
], {
|
|
44
|
+
ignored: ignore,
|
|
45
|
+
ignoreInitial: true,
|
|
46
|
+
});
|
|
47
|
+
watcher.on('all', () => {
|
|
48
|
+
run();
|
|
49
|
+
});
|
|
50
|
+
run();
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
let passed = await runAll(filter, reporter);
|
|
54
|
+
if (!passed) {
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
let mocPath = process.env.DFX_MOC_PATH;
|
|
60
|
+
export async function runAll(filter = '', reporterName = 'verbose') {
|
|
61
|
+
let reporter;
|
|
62
|
+
if (reporterName == 'compact') {
|
|
63
|
+
reporter = new CompactReporter;
|
|
64
|
+
}
|
|
65
|
+
else if (reporterName == 'files') {
|
|
66
|
+
reporter = new FilesReporter;
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
reporter = new VerboseReporter;
|
|
70
|
+
}
|
|
71
|
+
let rootDir = getRootDir();
|
|
72
|
+
let files = [];
|
|
73
|
+
let libFiles = globSync('**/test?(s)/lib.mo', globConfig);
|
|
74
|
+
if (libFiles[0]) {
|
|
75
|
+
files = [libFiles[0]];
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
let globStr = '**/test?(s)/**/*.test.mo';
|
|
79
|
+
if (filter) {
|
|
80
|
+
globStr = `**/test?(s)/**/*${filter}*.mo`;
|
|
81
|
+
}
|
|
82
|
+
files = globSync(path.join(rootDir, globStr), globConfig);
|
|
83
|
+
}
|
|
84
|
+
if (!files.length) {
|
|
85
|
+
if (filter) {
|
|
86
|
+
console.log(`No test files found for filter '${filter}'`);
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
console.log('No test files found');
|
|
90
|
+
console.log('Put your tests in \'test\' directory in *.test.mo files');
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
reporter.addFiles(files);
|
|
94
|
+
let sourcesArr = await sources();
|
|
95
|
+
if (!mocPath) {
|
|
96
|
+
mocPath = execSync('dfx cache show').toString().trim() + '/moc';
|
|
97
|
+
}
|
|
98
|
+
let wasmDir = `${getRootDir()}/.mops/.test/`;
|
|
99
|
+
fs.mkdirSync(wasmDir, { recursive: true });
|
|
100
|
+
await parallel(os.cpus().length, files, async (file) => {
|
|
101
|
+
let mmf = new MMF1('store');
|
|
102
|
+
let wasiMode = fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
|
|
103
|
+
let promise = new Promise((resolve) => {
|
|
104
|
+
if (!mocPath) {
|
|
105
|
+
mocPath = 'moc';
|
|
106
|
+
}
|
|
107
|
+
let mocArgs = ['--hide-warnings', '--error-detail=2', ...sourcesArr.join(' ').split(' '), file].filter(x => x);
|
|
108
|
+
// build and run wasm
|
|
109
|
+
if (wasiMode) {
|
|
110
|
+
let wasmFile = `${path.join(wasmDir, path.parse(file).name)}.wasm`;
|
|
111
|
+
// build
|
|
112
|
+
let buildProc = spawn(mocPath, [`-o=${wasmFile}`, '-wasi-system-api', ...mocArgs]);
|
|
113
|
+
pipeMMF(buildProc, mmf).then(async () => {
|
|
114
|
+
if (mmf.failed > 0) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// run
|
|
118
|
+
let proc = spawn('wasmtime', [wasmFile]);
|
|
119
|
+
await pipeMMF(proc, mmf);
|
|
120
|
+
}).finally(() => {
|
|
121
|
+
fs.rmSync(wasmFile, { force: true });
|
|
122
|
+
}).then(resolve);
|
|
123
|
+
}
|
|
124
|
+
// interpret
|
|
125
|
+
else {
|
|
126
|
+
let proc = spawn(mocPath, ['-r', '-ref-system-api', ...mocArgs]);
|
|
127
|
+
pipeMMF(proc, mmf).then(resolve);
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
reporter.addRun(file, mmf, promise, wasiMode);
|
|
131
|
+
await promise;
|
|
132
|
+
});
|
|
133
|
+
fs.rmSync(wasmDir, { recursive: true, force: true });
|
|
134
|
+
return reporter.done();
|
|
135
|
+
}
|
|
136
|
+
function pipeMMF(proc, mmf) {
|
|
137
|
+
return new Promise((resolve) => {
|
|
138
|
+
// stdout
|
|
139
|
+
proc.stdout.on('data', (data) => {
|
|
140
|
+
for (let line of data.toString().split('\n')) {
|
|
141
|
+
line = line.trim();
|
|
142
|
+
if (line) {
|
|
143
|
+
mmf.parseLine(line);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
// stderr
|
|
148
|
+
proc.stderr.on('data', (data) => {
|
|
149
|
+
let text = data.toString().trim();
|
|
150
|
+
let failedLine = '';
|
|
151
|
+
text = text.replace(/([\w+._/-]+):(\d+).(\d+)(-\d+.\d+)/g, (_m0, m1, m2, m3) => {
|
|
152
|
+
// change absolute file path to relative
|
|
153
|
+
// change :line:col-line:col to :line:col to work in vscode
|
|
154
|
+
let res = `${absToRel(m1)}:${m2}:${m3}`;
|
|
155
|
+
if (!fs.existsSync(m1)) {
|
|
156
|
+
return res;
|
|
157
|
+
}
|
|
158
|
+
// show failed line
|
|
159
|
+
let content = fs.readFileSync(m1);
|
|
160
|
+
let lines = content.toString().split('\n') || [];
|
|
161
|
+
failedLine += chalk.dim('\n ...');
|
|
162
|
+
let lineBefore = lines[+m2 - 2];
|
|
163
|
+
if (lineBefore) {
|
|
164
|
+
failedLine += chalk.dim(`\n ${+m2 - 1}\t| ${lineBefore.replaceAll('\t', ' ')}`);
|
|
165
|
+
}
|
|
166
|
+
failedLine += `\n${chalk.redBright `->`} ${m2}\t| ${lines[+m2 - 1]?.replaceAll('\t', ' ')}`;
|
|
167
|
+
if (lines.length > +m2) {
|
|
168
|
+
failedLine += chalk.dim(`\n ${+m2 + 1}\t| ${lines[+m2]?.replaceAll('\t', ' ')}`);
|
|
169
|
+
}
|
|
170
|
+
failedLine += chalk.dim('\n ...');
|
|
171
|
+
return res;
|
|
172
|
+
});
|
|
173
|
+
if (failedLine) {
|
|
174
|
+
text += failedLine;
|
|
175
|
+
}
|
|
176
|
+
mmf.fail(text);
|
|
177
|
+
});
|
|
178
|
+
// exit
|
|
179
|
+
proc.on('close', (code) => {
|
|
180
|
+
if (code === 0) {
|
|
181
|
+
mmf.pass();
|
|
182
|
+
}
|
|
183
|
+
else if (code !== 1) {
|
|
184
|
+
mmf.fail(`unknown exit code: ${code}`);
|
|
185
|
+
}
|
|
186
|
+
resolve();
|
|
187
|
+
});
|
|
188
|
+
});
|
|
189
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function absToRel(p: string): string;
|
package/dist/commands/test.js
CHANGED
|
@@ -10,6 +10,8 @@ import { MMF1 } from './mmf1.js';
|
|
|
10
10
|
import { sources } from './sources.js';
|
|
11
11
|
import { getRootDir } from '../mops.js';
|
|
12
12
|
import { parallel } from '../parallel.js';
|
|
13
|
+
import { absToRel } from './test/utils.js';
|
|
14
|
+
import { VerboseReporter } from './test/reporter-verbose.js';
|
|
13
15
|
let ignore = [
|
|
14
16
|
'**/node_modules/**',
|
|
15
17
|
'**/.mops/**',
|
|
@@ -54,7 +56,7 @@ export async function test(filter = '', { watch = false } = {}) {
|
|
|
54
56
|
}
|
|
55
57
|
let mocPath = process.env.DFX_MOC_PATH;
|
|
56
58
|
export async function runAll(filter = '') {
|
|
57
|
-
let
|
|
59
|
+
let reporter = new VerboseReporter;
|
|
58
60
|
let rootDir = getRootDir();
|
|
59
61
|
let files = [];
|
|
60
62
|
let libFiles = globSync('**/test?(s)/lib.mo', globConfig);
|
|
@@ -77,73 +79,56 @@ export async function runAll(filter = '') {
|
|
|
77
79
|
console.log('Put your tests in \'test\' directory in *.test.mo files');
|
|
78
80
|
return;
|
|
79
81
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
let
|
|
87
|
-
let
|
|
82
|
+
reporter.addFiles(files);
|
|
83
|
+
// console.log('Test files:');
|
|
84
|
+
// for (let file of files) {
|
|
85
|
+
// console.log(chalk.gray(`• ${absToRel(file)}`));
|
|
86
|
+
// }
|
|
87
|
+
// console.log('='.repeat(50));
|
|
88
|
+
// let failed = 0;
|
|
89
|
+
// let passed = 0;
|
|
90
|
+
// let skipped = 0;
|
|
88
91
|
let sourcesArr = await sources();
|
|
89
92
|
if (!mocPath) {
|
|
90
93
|
mocPath = execSync('dfx cache show').toString().trim() + '/moc';
|
|
91
94
|
}
|
|
92
95
|
let wasmDir = `${getRootDir()}/.mops/.test/`;
|
|
93
96
|
fs.mkdirSync(wasmDir, { recursive: true });
|
|
94
|
-
let i = 0;
|
|
95
97
|
await parallel(os.cpus().length, files, async (file) => {
|
|
96
|
-
if (!mocPath) {
|
|
97
|
-
mocPath = 'moc';
|
|
98
|
-
}
|
|
99
98
|
let mmf = new MMF1('store');
|
|
100
99
|
let wasiMode = fs.readFileSync(file, 'utf8').startsWith('// @testmode wasi');
|
|
101
|
-
let
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
100
|
+
let promise = new Promise((resolve) => {
|
|
101
|
+
if (!mocPath) {
|
|
102
|
+
mocPath = 'moc';
|
|
103
|
+
}
|
|
104
|
+
let mocArgs = ['--hide-warnings', '--error-detail=2', ...sourcesArr.join(' ').split(' '), file].filter(x => x);
|
|
105
|
+
// build and run wasm
|
|
106
|
+
if (wasiMode) {
|
|
107
|
+
let wasmFile = `${path.join(wasmDir, path.parse(file).name)}.wasm`;
|
|
108
|
+
// build
|
|
109
|
+
let buildProc = spawn(mocPath, [`-o=${wasmFile}`, '-wasi-system-api', ...mocArgs]);
|
|
110
|
+
pipeMMF(buildProc, mmf).then(async () => {
|
|
111
|
+
if (mmf.failed > 0) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
// run
|
|
115
|
+
let proc = spawn('wasmtime', [wasmFile]);
|
|
116
|
+
await pipeMMF(proc, mmf);
|
|
117
|
+
}).finally(() => {
|
|
118
|
+
fs.rmSync(wasmFile, { force: true });
|
|
119
|
+
}).then(resolve);
|
|
120
|
+
}
|
|
121
|
+
// interpret
|
|
122
|
+
else {
|
|
123
|
+
let proc = spawn(mocPath, ['-r', '-ref-system-api', ...mocArgs]);
|
|
124
|
+
pipeMMF(proc, mmf).then(resolve);
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
reporter.addRun(file, mmf, promise, wasiMode);
|
|
128
|
+
await promise;
|
|
129
129
|
});
|
|
130
130
|
fs.rmSync(wasmDir, { recursive: true, force: true });
|
|
131
|
-
|
|
132
|
-
if (failed) {
|
|
133
|
-
console.log(chalk.redBright('Tests failed'));
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
console.log(chalk.greenBright('Tests passed'));
|
|
137
|
-
}
|
|
138
|
-
console.log(`Done in ${chalk.gray(((Date.now() - start) / 1000).toFixed(2) + 's')}`
|
|
139
|
-
+ `, passed ${chalk.greenBright(passed)}`
|
|
140
|
-
+ (skipped ? `, skipped ${chalk[skipped ? 'yellowBright' : 'gray'](skipped)}` : '')
|
|
141
|
-
+ (failed ? `, failed ${chalk[failed ? 'redBright' : 'gray'](failed)}` : ''));
|
|
142
|
-
return failed === 0;
|
|
143
|
-
}
|
|
144
|
-
function absToRel(p) {
|
|
145
|
-
let rootDir = getRootDir();
|
|
146
|
-
return path.relative(rootDir, path.resolve(p));
|
|
131
|
+
return reporter.done();
|
|
147
132
|
}
|
|
148
133
|
function pipeMMF(proc, mmf) {
|
|
149
134
|
return new Promise((resolve) => {
|
|
@@ -195,7 +180,7 @@ function pipeMMF(proc, mmf) {
|
|
|
195
180
|
else if (code !== 1) {
|
|
196
181
|
mmf.fail(`unknown exit code: ${code}`);
|
|
197
182
|
}
|
|
198
|
-
resolve(
|
|
183
|
+
resolve();
|
|
199
184
|
});
|
|
200
185
|
});
|
|
201
186
|
}
|
package/dist/package.json
CHANGED