// Copyright (c) 2013, the Dart project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. /** * Helper library to run tests in content_shell */ library polymer.testing.end2end; import 'dart:io'; import 'package:args/args.dart'; import 'package:path/path.dart' as path; import 'package:unittest/unittest.dart'; import 'package:polymer/dwc.dart' as dwc; /** * Compiles [testFile] with the web-ui compiler, and then runs the output as a * unit test in content_shell. */ void endToEndTests(String inputDir, String outDir, {List arguments}) { _testHelper(new _TestOptions(inputDir, inputDir, null, outDir, arguments: arguments)); } /** * Compiles [testFile] with the web-ui compiler, and then runs the output as a * render test in content_shell. */ void renderTests(String baseDir, String inputDir, String expectedDir, String outDir, {List arguments, String script, String pattern, bool deleteDir: true}) { _testHelper(new _TestOptions(baseDir, inputDir, expectedDir, outDir, arguments: arguments, script: script, pattern: pattern, deleteDir: deleteDir)); } void _testHelper(_TestOptions options) { expect(options, isNotNull); var paths = new Directory(options.inputDir).listSync() .where((f) => f is File).map((f) => f.path) .where((p) => p.endsWith('_test.html') && options.pattern.hasMatch(p)); if (paths.isEmpty) return; // First clear the output folder. Otherwise we can miss bugs when we fail to // generate a file. var dir = new Directory(options.outDir); if (dir.existsSync() && options.deleteDir) { print('Cleaning old output for ${path.normalize(options.outDir)}'); dir.deleteSync(recursive: true); } dir.createSync(); for (var filePath in paths) { var filename = path.basename(filePath); test('compile $filename', () { var testArgs = ['-o', options.outDir, '--basedir', options.baseDir, '--deploy'] ..addAll(options.compilerArgs) ..add(filePath); expect(dwc.run(testArgs, printTime: false).then((res) { expect(res.messages.length, 0, reason: res.messages.join('\n')); }), completes); }); } var filenames = paths.map(path.basename).toList(); // Sort files to match the order in which run.sh runs diff. filenames.sort(); // Get the path from "input" relative to "baseDir" var relativeToBase = path.relative(options.inputDir, from: options.baseDir); var finalOutDir = path.join(options.outDir, relativeToBase); runTests(String search) { var output; for (var filename in filenames) { test('content_shell run $filename$search', () { var args = ['--dump-render-tree', 'file://$finalOutDir/$filename$search']; var env = {'DART_FLAGS': '--checked'}; expect(Process.run('content_shell', args, environment: env).then((res) { expect(res.exitCode, 0, reason: 'content_shell exit code: ' '${res.exitCode}. Contents of stderr: \n${res.stderr}'); var outs = res.stdout.split('#EOF\n') .where((s) => !s.trim().isEmpty).toList(); expect(outs.length, 1); output = outs.first; }), completes); }); test('verify $filename $search', () { expect(output, isNotNull, reason: 'Output not available, maybe content_shell failed to run.'); var outPath = path.join(options.outDir, '$filename.txt'); new File(outPath).writeAsStringSync(output); if (options.isRenderTest) { var expectedPath = path.join(options.expectedDir, '$filename.txt'); var expected = new File(expectedPath).readAsStringSync(); expect(output, expected, reason: 'unexpected output for <$filename>'); } else { bool passes = matches( new RegExp('All .* tests passed')).matches(output, {}); expect(passes, true, reason: 'unit test failed:\n$output'); } }); } } bool compiled = false; ensureCompileToJs() { if (compiled) return; compiled = true; for (var filename in filenames) { test('dart2js $filename', () { // TODO(jmesserly): this depends on DWC's output scheme. // Alternatively we could use html5lib to find the script tag. var inPath = '${filename}_bootstrap.dart'; var outPath = '${inPath}.js'; inPath = path.join(finalOutDir, inPath); outPath = path.join(finalOutDir, outPath); expect(Process.run('dart2js', ['-o$outPath', inPath]).then((res) { expect(res.exitCode, 0, reason: 'dart2js exit code: ' '${res.exitCode}. Contents of stderr: \n${res.stderr}. ' 'Contents of stdout: \n${res.stdout}.'); expect(new File(outPath).existsSync(), true, reason: 'input file ' '$inPath should have been compiled to $outPath.'); }), completes); }); } } if (options.runAsDart) { runTests(''); } if (options.runAsJs) { ensureCompileToJs(); runTests('?js=1'); } if (options.forcePolyfillShadowDom) { ensureCompileToJs(); runTests('?js=1&shadowdomjs=1'); } } class _TestOptions { final String baseDir; final String inputDir; final String expectedDir; bool get isRenderTest => expectedDir != null; final String outDir; final bool deleteDir; final bool runAsDart; final bool runAsJs; final bool forcePolyfillShadowDom; final List compilerArgs; final RegExp pattern; factory _TestOptions(String baseDir, String inputDir, String expectedDir, String outDir, {List arguments, String script, String pattern, bool deleteDir: true}) { if (arguments == null) arguments = new Options().arguments; if (script == null) script = new Options().script; var args = _parseArgs(arguments, script); if (args == null) return null; var compilerArgs = args.rest; var filePattern; if (pattern != null) { filePattern = new RegExp(pattern); } else if (compilerArgs.length > 0) { filePattern = new RegExp(compilerArgs[0]); compilerArgs = compilerArgs.sublist(1); } else { filePattern = new RegExp('.'); } var scriptDir = path.absolute(path.dirname(script)); baseDir = path.join(scriptDir, baseDir); inputDir = path.join(scriptDir, inputDir); outDir = path.join(scriptDir, outDir); if (expectedDir != null) { expectedDir = path.join(scriptDir, expectedDir); } return new _TestOptions._(baseDir, inputDir, expectedDir, outDir, deleteDir, args['dart'] == true, args['js'] == true, args['shadowdom'] == true, compilerArgs, filePattern); } _TestOptions._(this.baseDir, this.inputDir, this.expectedDir, this.outDir, this.deleteDir, this.runAsDart, this.runAsJs, this.forcePolyfillShadowDom, this.compilerArgs, this.pattern); } ArgResults _parseArgs(List arguments, String script) { var parser = new ArgParser() ..addFlag('dart', abbr: 'd', help: 'run on Dart VM', defaultsTo: true) ..addFlag('js', abbr: 'j', help: 'run compiled dart2js', defaultsTo: true) ..addFlag('shadowdom', abbr: 's', help: 'run dart2js and polyfilled ShadowDOM', defaultsTo: true) ..addFlag('help', abbr: 'h', help: 'Displays this help message', defaultsTo: false, negatable: false); showUsage() { print('Usage: $script [options...] [test_name_regexp]'); print(parser.getUsage()); return null; } try { var results = parser.parse(arguments); if (results['help']) return showUsage(); return results; } on FormatException catch (e) { print(e.message); return showUsage(); } }