-
Notifications
You must be signed in to change notification settings - Fork 14
/
benchmark.ts
111 lines (102 loc) 路 3.27 KB
/
benchmark.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
import * as vm from 'vm';
import * as fs from 'fs';
import * as path from 'path';
import * as _ from 'lodash';
import { Suite } from 'benchmark';
import * as minimist from 'minimist';
import * as util from './algorithms/util';
const argv = minimist(process.argv.slice(2));
const target = argv.target || argv.t;
const index = argv.index || argv.i;
const baseReStr = `(?=.*${__dirname})` + (target ? `(?=.*${target})` : '');
const re = {
target: new RegExp(baseReStr),
trace: new RegExp(`${baseReStr}(?=.*test.js)`),
match: /^.*algorithms\/(.*)\/test.js/,
funcArgs: /^(function)?\s*[^(]*\(\s*([^)]*)\)/m,
funcArgSplit: /,/,
funcArg: /(=.+)?(\s*)$/,
stripComments: /((\/\/.*$)|(\/\*[\s\S]*?\*\/))/gm,
};
const targets: any = {};
const context: any = global;
context.__dirname = __dirname;
context.require = name => {
const file = require(name);
if (re.target.test(name)) {
const key = _.get(name.match(re.match), [1]);
if (!key || !targets[key]) {
console.log(`Not found ${key}`);
} else {
const testpath = path.resolve(name, '..');
targets[key].funcs = require(testpath);
}
}
return file;
};
context.describe = (name, func) => {
const str = `(${func.toString()})()`;
const forEach = (tasks, iterator) => {
_.forEach(tasks, iterator);
const traces = new Error().stack.split(/\n/g);
const targetTrace = _.find(traces, trace => re.trace.test(trace));
if (!targetTrace) {
return;
}
const key = _.get(targetTrace.match(re.match), [1]);
if (!key) {
return;
}
targets[key] = { tasks };
};
const context = Object.assign({ console, it: _.noop, _: { forEach } }, util);
vm.runInNewContext(str, context);
};
const testpath = path.resolve(__dirname, 'test.js');
vm.runInNewContext(fs.readFileSync(testpath, 'utf8'), context);
_.forOwn(targets, ({ tasks, funcs }, name) => {
if (!_.isPlainObject(funcs) || _.size(funcs) < 2) {
return;
}
const suite = new Suite();
const task = index >= 0 ? tasks[index] : _.sample(tasks);
const { result } = task;
console.log(`\nname: ${name} \ntask: ${JSON.stringify(task, null, 2)}`);
_.forOwn(funcs, func => {
const { name } = func;
const args = _.chain(task)
.mapValues(_.clone)
.pick(parseArgs(func.toString()))
.toArray()
.value();
if (!_.isEqual(func.apply(null, args), result)) {
console.log(func.apply(null, args));
throw new Error(`Failed ${name}`);
}
suite.add(name, () => func.apply(null, args));
});
suite
.on('complete', ({ currentTarget }) => {
const nameLength = _.chain(currentTarget)
.map(({ name }) => name.length)
.max()
.value();
_.chain(currentTarget)
.map(({ name, stats, error }) => {
const { mean } = stats;
return { name, mean, error };
})
.sortBy('mean')
.forEach(({ name, mean, error }, index) => {
console.log(`[${++index}]${name}${Array(nameLength - name.length + 2).join(' ')}${error || mean}`);
})
.value();
})
.run();
});
function parseArgs(code: any) {
code = code.replace(re.stripComments, '');
code = code.match(re.funcArgs)[2].replace(' ', '');
code = code ? code.split(re.funcArgSplit) : [];
return code.map(arg => arg.replace(re.funcArg, '').trim());
}