|
2 | 2 |
|
3 | 3 | // Copyright (C) 2014, Microsoft Corporation. All rights reserved. |
4 | 4 | // This code is governed by the BSD License found in the LICENSE file. |
5 | | - |
6 | | -var args = require('minimist')(process.argv.slice(2), { |
7 | | - alias: { |
8 | | - consoleCommand: 'e', |
9 | | - consolePrintCommand: 'p', |
10 | | - runner: 'r', |
11 | | - reporter: 'R', |
12 | | - threads: 't', |
13 | | - batch: 'b', |
14 | | - config: 'c', |
15 | | - compile: 'C', |
16 | | - outputDir: 'o', |
17 | | - test262Dir: 'T', |
18 | | - help: 'h', |
19 | | - version: 'v' |
20 | | - } |
| 5 | +const compile = require('test262-compiler'); |
| 6 | +const fs = require('fs'); |
| 7 | +const globber = require('../lib/globber.js'); |
| 8 | +const argv = require('../lib/cli.js').argv; |
| 9 | +const validator = require('../lib/validator.js'); |
| 10 | +const Rx = require('rx'); |
| 11 | +const util = require('util'); |
| 12 | +const simpleReporter = require('../lib/reporters/simple.js'); |
| 13 | +const resultsEmitter = require('../lib/resultsEmitter.js'); |
| 14 | +const agentPool = require('../lib/agentPool.js'); |
| 15 | +const test262Finder = require('../lib/findTest262.js'); |
| 16 | +const scenariosForTest = require('../lib/scenarios.js'); |
| 17 | + |
| 18 | +let includesDir = argv.includesDir; |
| 19 | +let test262Dir = argv.test262Dir; |
| 20 | + |
| 21 | +// Test Pipeline |
| 22 | +const pool = agentPool(Number(argv.threads), argv.hostType, argv.hostArgs, argv.hostPath); |
| 23 | +const paths = globber(argv._[0]); |
| 24 | +if (!includesDir && !test262Dir) { |
| 25 | + test262Dir = test262Finder(paths.fileEvents); |
| 26 | +} |
| 27 | +const files = paths.map(pathToTestFile); |
| 28 | +const tests = files.map(compileFile); |
| 29 | +const scenarios = tests.flatMap(scenariosForTest); |
| 30 | +const pairs = Rx.Observable.zip(pool, scenarios); |
| 31 | +const rawResults = pairs.flatMap(pool.runTest); |
| 32 | +const results = rawResults.map(function (test) { |
| 33 | + test.result = validator(test); |
| 34 | + return test; |
21 | 35 | }); |
22 | | - |
23 | | -var parser = require('test262-parser'); |
24 | | -var tapify = require('../lib/tapify'); |
25 | | -var simpleReporter = require('../lib/simpleReporter.js'); |
26 | | -var _ = require('highland'); |
27 | | -var glob = require('glob'); |
28 | | -var fs = require('fs'); |
29 | | -var path = require('path'); |
30 | | -var jss = require('JSONStream').stringify(); |
31 | | -var Tributary = require('stream-bifurcate') |
32 | | -var scenarios = require('../lib/scenarios'); |
33 | | -var readFile = _.wrapCallback(fs.readFile); |
34 | | -var DEFAULT_BATCH_SIZE = 75; |
35 | | -var t262 = require('../index'); |
36 | | -var minimatch = require('minimatch'); |
37 | | -var Liftoff = require('liftoff'); |
38 | | -var cli = new Liftoff({ name: 'test262-harness'}) |
39 | | - |
40 | | -if(args.version) { |
41 | | - printVersion(); |
42 | | - process.exit(0); |
43 | | -} |
44 | | - |
45 | | -if(args.help) { |
46 | | - printHelp(); |
47 | | - process.exit(0); |
48 | | -} |
49 | | - |
50 | | -cli.launch({ |
51 | | - cwd: args.cwd, |
52 | | - configPath: args.config |
53 | | -}, function(env) { |
54 | | - var t262 = initEnv(env); |
55 | | - |
56 | | - // load config file if specified |
57 | | - if(env.configPath) { |
58 | | - require(env.configPath); |
59 | | - } |
60 | | - |
61 | | - // If we've loaded a console command config path, |
62 | | - // add it to the |
63 | | - if(t262.config.consoleCommand) { |
64 | | - t262.config.consoleCommand = path.join(env.cwd, t262.config.consoleCommand); |
65 | | - } |
66 | | - |
67 | | - // command line flags override anything specified by config files |
68 | | - t262.useConfig(args); |
69 | | - |
70 | | - applyDefaults(t262.config); |
71 | | - |
72 | | - var Runner = t262.loadRunner(); |
73 | | - |
74 | | - var files = getFilesStream(t262.config); |
75 | | - var contents = files.fork().map(readFile).sequence(); |
76 | | - |
77 | | - var start = Date.now(); |
78 | | - |
79 | | - var tests = contents.zip(files.fork()).map(function(d) { |
80 | | - return parser.parseFile({ contents: d[0].toString('utf8'), file: d[1]}); |
81 | | - }); |
82 | | - |
83 | | - var scenarioStream = getScenarioStream(t262.config); |
84 | | - var scenarios = tests.flatMap(scenarioStream); |
85 | | - |
86 | | - if(t262.config.batch) scenarios = _(scenarios).batch(t262.config.batch); |
87 | | - |
88 | | - var trb = scenarios.pipe(new Tributary()); |
89 | | - var results = _(function(push) { |
90 | | - for(var i = 0; i < t262.config.threads; i++) push(null, run(Runner, t262.config, trb.fork())); |
91 | | - push(null, _.nil); |
92 | | - }).merge(); |
93 | | - |
94 | | - if(t262.config.compile) compile(Runner, results); |
95 | | - |
96 | | - if(t262.config.reporter === 'json') { |
97 | | - results.pipe(jss).pipe(process.stdout); |
98 | | - } else if(t262.config.reporter === 'tap') { |
99 | | - results.pipe(tapify).pipe(process.stdout); |
100 | | - } else if(t262.config.reporter === 'simple') { |
101 | | - results.pipe(simpleReporter); |
102 | | - |
103 | | - results.on('end', function() { |
104 | | - console.log("Took " + ((Date.now() - start) / 1000) + " seconds"); |
105 | | - }) |
106 | | - } |
107 | | -}); |
108 | | - |
109 | | -// takes a test collateral stream. |
110 | | -// Returns test results stream. |
111 | | -function run(Runner, config, tests) { |
112 | | - var runner = new Runner(config); |
113 | | - |
114 | | - var results = _(tests).map(function(test) { |
115 | | - return _(function(push) { |
116 | | - if(config.batch) { |
117 | | - runner.runBatch(test, function() { |
118 | | - test.forEach(function(t) { |
119 | | - push(null, t); |
120 | | - }) |
121 | | - push(null, _.nil); |
122 | | - }); |
123 | | - } else { |
124 | | - runner.run(test, function() { |
125 | | - push(null, test); |
126 | | - push(null, _.nil); |
127 | | - }); |
128 | | - } |
129 | | - }); |
130 | | - }).sequence(); |
131 | | - |
132 | | - results.on('end', function() { |
133 | | - runner.end(); |
134 | | - }); |
135 | | - |
136 | | - return results; |
137 | | -} |
138 | | - |
139 | | -// takes a file path and returns a stream of filenames |
140 | | -// that match it |
141 | | -function globStream(p) { |
142 | | - var mg = glob(p); |
143 | | - var source = _('match', mg); |
144 | | - mg.on('end', function() { source.end() }) |
145 | | - |
146 | | - return source; |
147 | | -} |
148 | | - |
149 | | -// takes a test and returns a stream of all the scenarios |
150 | | -function getScenarioStream(config) { |
151 | | - return function(test) { |
152 | | - var iter = scenarios(config)(test); |
153 | | - return _(function(push) { |
154 | | - var rec = iter.next(); |
155 | | - while(!rec.done) { |
156 | | - push(null, rec.value); |
157 | | - rec = iter.next(); |
158 | | - } |
159 | | - |
160 | | - push(null, _.nil); |
161 | | - }) |
162 | | - } |
163 | | -} |
164 | | - |
165 | | -function shallowCopy(obj) { |
166 | | - return Object.keys(obj).reduce(function(v, k) { |
167 | | - return v[k] = obj[k], v; |
168 | | - }, {}); |
169 | | -} |
170 | | - |
171 | | -// exclude tests specified in config |
172 | | -function exclude(test) { |
173 | | - var f = true; |
174 | | - for (var i = 0; i<t262.config.exclude.length; i++) { |
175 | | - if (minimatch(test, t262.config.exclude[i])) { |
176 | | - f = false; |
177 | | - break; |
178 | | - } |
179 | | - } |
180 | | - return f; |
181 | | -} |
182 | | - |
183 | | -// dump files to disk based on the compile flag |
184 | | -function compile(Runner, results) { |
185 | | - optionsCopy = shallowCopy(t262.config); |
186 | | - optionsCopy.batch = false; |
187 | | - optionsCopy.compileOnly = true; |
188 | | - var compiler = new Runner(optionsCopy); |
189 | | - |
190 | | - _(results).observe().each(function(test) { |
191 | | - var compile = t262.config.compile === 'failures' && !test.pass; |
192 | | - compile = compile || t262.config.compile === 'all'; |
193 | | - compile = compile || t262.config.compile === 'passes' && !!test.pass |
194 | | - |
195 | | - if(compile) { |
196 | | - var testCopy = shallowCopy(test); |
197 | | - compiler.compile(testCopy) |
198 | | - var startPath = path.join(process.cwd(), t262.config.outputDir, path.basename(test.file)); |
199 | | - var currentPath = startPath; |
200 | | - var counter = 0; |
201 | | - while(fs.existsSync(currentPath)) { |
202 | | - currentPath = startPath.replace(/.js$/, "-" + counter++ + ".js"); |
203 | | - } |
204 | | - fs.writeFileSync(currentPath, testCopy.contents.replace(/\r\n/g, '\n')); |
205 | | - } |
206 | | - }); |
207 | | - |
208 | | - results.on('end', function() { |
209 | | - compiler.end(); |
210 | | - }); |
211 | | - |
212 | | -} |
213 | | - |
214 | | -// Walk recursively up the directory tree looking for a directory which |
215 | | -// has a "harness" directory next to a "test" directory. Return the |
216 | | -// path to the "test" directory. |
217 | | -function includesDirFromGlob(p) { |
218 | | - if(!p) return null; |
219 | | - |
220 | | - var dir = path.dirname(p); |
221 | | - var base = path.basename(p); |
222 | | - |
223 | | - if(base !== "test") { |
224 | | - if(dir === p) return null; // reached the root directory |
225 | | - |
226 | | - return includesDirFromGlob(dir); |
227 | | - } |
228 | | - |
229 | | - var harnessPath = path.join(dir, 'harness'); |
230 | | - |
231 | | - // see if the harness directory exists here |
232 | | - if(fs.existsSync(harnessPath)) { |
233 | | - return harnessPath; |
234 | | - } else { |
235 | | - return includesDirFromGlob(dir); |
236 | | - } |
237 | | -} |
238 | | - |
239 | | -function initEnv(env) { |
240 | | - if(!env.modulePath) { |
241 | | - // try loading local module (to support running from test262-harness directory) |
242 | | - try { |
243 | | - var t262 = require("../index.js"); |
244 | | - |
245 | | - // possibly il-advised sanity check to make sure we're not requiring the index |
246 | | - // of some other project. |
247 | | - if(!t262.useConfig) throw 1; |
248 | | - |
249 | | - return t262; |
250 | | - } catch(e) { |
251 | | - console.error("Cannot find local test262-harness install. Run npm install test262-harness.") |
252 | | - process.exit(1); |
253 | | - } |
254 | | - } else { |
255 | | - return require(env.modulePath); |
256 | | - } |
257 | | -} |
258 | | - |
259 | | -function applyDefaults(config) { |
260 | | - if(config.batch === true) config.batch = DEFAULT_BATCH_SIZE; |
261 | | - if(!config.threads) config.threads = 4; |
262 | | - if(!config.reporter) config.reporter = 'simple'; |
263 | | - if(config.compile === true) config.compile = 'all'; |
264 | | - if(!config.outputDir && config.compile) |
265 | | - throw 'need output directory for compiled collateral'; |
266 | | - if(config.compile && !fs.existsSync(config.outputDir)) fs.mkdirSync(config.outputDir); |
267 | | - |
268 | | - if(!config.includesDir) { |
269 | | - if(config.test262Dir) { |
270 | | - // relative to test262dir |
271 | | - config.includesDir = path.join(config.test262Dir, 'harness'); |
272 | | - } else { |
273 | | - var fromGlob = includesDirFromGlob(config._[0]); |
274 | | - |
275 | | - if(fromGlob) { |
276 | | - config.includesDir = fromGlob; |
277 | | - } else if(fs.existsSync('harness/')) { |
278 | | - // harness dir is present in our cwd |
279 | | - config.includesDir = 'harness/'; |
280 | | - } else { |
281 | | - // else fall back to local harness deps |
282 | | - config.includesDir = path.join(__dirname, '../lib/helpers/'); |
283 | | - } |
284 | | - } |
285 | | - } |
286 | | - |
287 | | - if(!config.consoleArguments) config.consoleArguments = ""; |
288 | | -} |
289 | | - |
290 | | -function getFilesStream(config) { |
291 | | - var files = config._; |
292 | | - |
293 | | - // by default, run all files recursively when we pass test262Dir |
294 | | - if(config.test262Dir && files.length === 0) { |
295 | | - files = ['**/*.js'] |
296 | | - } |
297 | | - |
298 | | - files = files.map(function(p) { |
299 | | - if(config.test262Dir) { |
300 | | - p = path.join(config.test262Dir, 'test', p); |
301 | | - } |
302 | | - |
303 | | - if(fs.existsSync(p) && fs.statSync(p).isDirectory()) { |
304 | | - p = path.join(p, '**/*.js'); |
305 | | - } |
306 | | - |
307 | | - return p; |
308 | | - }); |
309 | | - |
310 | | - files = _(files.map(globStream)).merge(); |
311 | | - |
312 | | - if (config.exclude) files = files.filter(exclude); |
313 | | - |
314 | | - return files; |
315 | | -} |
| 36 | +const resultEmitter = resultsEmitter(results); |
| 37 | +simpleReporter(resultEmitter); |
316 | 38 |
|
317 | 39 | function printVersion() { |
318 | 40 | var p = require(path.resolve(__dirname, "..", "package.json")); |
319 | 41 | console.log("test262-harness v" + p.version); |
320 | 42 | } |
321 | 43 |
|
322 | | -function printHelp() { |
323 | | - |
324 | | - printVersion(); |
325 | | - |
326 | | - console.log("Usage: test262-harness [options] [files]"); |
327 | | - console.log(""); |
328 | | - console.log("Options:"); |
329 | | - console.log(" -r, --runner Specify runner (node, node-ip, jsshell, console)"); |
330 | | - console.log(" -c, --config Load a config.js file"); |
331 | | - console.log(" -C, --compile Save compiled tests."); |
332 | | - console.log(" -o, --outputDir Output directory for compiled tests."); |
333 | | - console.log(" -e, --consoleCommand Command for console runner."); |
334 | | - console.log(" --consoleArguments Arguments for console runner."); |
335 | | - console.log(" -p, --consolePrintCommand Print command."); |
336 | | - console.log(" -t, --threads Run this many tests in parallel."); |
337 | | - console.log(" -b, --batch How many tests to batch together."); |
338 | | - console.log(" --testStrict Tests both strict and non-strict mode."); |
339 | | - console.log(" -R, --reporter Specify reporter (json, tap, simple)."); |
340 | | - console.log(" --prelude Prepends specified file to each test file."); |
341 | | - console.log(" --includesDir Directory with includes. Usually inferred."); |
342 | | - console.log(" -T, --test262Dir Root directory of test262."); |
343 | | - console.log(" -v, --version Print test262-harness version."); |
344 | | - console.log(" -h, --help Print short help."); |
| 44 | +function pathToTestFile(path) { |
| 45 | + return { file: path, contents: fs.readFileSync(path, 'utf-8')}; |
345 | 46 | } |
346 | 47 |
|
| 48 | +function compileFile(contents) { |
| 49 | + return compile(contents, { test262Dir: test262Dir, includesDir: includesDir }); |
| 50 | +} |
0 commit comments