diff --git a/gulpfile.js b/gulpfile.js index 776c578e4b..8051e5957b 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -19,6 +19,7 @@ var multicopy = require('./tools/build/multicopy'); var karma = require('karma').server; var minimist = require('minimist'); var es5build = require('./tools/build/es5build'); +var runServerDartTests = require('./tools/build/run_server_dart_tests'); var DART_SDK = require('./tools/build/dartdetect')(gulp); // ----------------------- @@ -484,38 +485,46 @@ gulp.task('docs/serve', function() { }); // ------------------ -// tests +// karma tests +// These tests run in the browser and are allowed to access +// HTML DOM APIs. function getBrowsersFromCLI() { var args = minimist(process.argv.slice(2)); return [args.browsers?args.browsers:'DartiumWithWebPlatform'] } -gulp.task('test.js', function (done) { +gulp.task('test.unit.js', function (done) { karma.start({configFile: __dirname + '/karma-js.conf.js'}, done); }); -gulp.task('test.dart', function (done) { +gulp.task('test.unit.dart', function (done) { karma.start({configFile: __dirname + '/karma-dart.conf.js'}, done); }); -gulp.task('test.js/ci', function (done) { - karma.start({configFile: __dirname + '/karma-js.conf.js', singleRun: true, reporters: ['dots'], browsers: getBrowsersFromCLI()}, done); +gulp.task('test.unit.js/ci', function (done) { + karma.start({configFile: __dirname + '/karma-js.conf.js', + singleRun: true, reporters: ['dots'], browsers: getBrowsersFromCLI()}, done); }); -gulp.task('test.dart/ci', function (done) { - karma.start({configFile: __dirname + '/karma-dart.conf.js', singleRun: true, reporters: ['dots'], browsers: getBrowsersFromCLI()}, done); +gulp.task('test.unit.dart/ci', function (done) { + karma.start({configFile: __dirname + '/karma-dart.conf.js', + singleRun: true, reporters: ['dots'], browsers: getBrowsersFromCLI()}, done); }); + +// ------------------ +// server tests +// These tests run on the VM on the command-line and are +// allowed to access the file system and network. +gulp.task('test.server.dart', runServerDartTests(gulp, gulpPlugins, { + dest: 'dist/dart' +})); + +// ----------------- +// test builders gulp.task('test.transpiler.unittest', function (done) { return gulp.src('tools/transpiler/unittest/**/*.js') .pipe(jasmine({ includeStackTrace: true })) }); -gulp.task('ci', function(done) { - runSequence( - 'test.transpiler.unittest', - 'test.js/ci', - 'test.dart/ci', - done - ); -}); +// Copy test resources to dist gulp.task('tests/transform.dart', function() { return gulp.src('modules/angular2/test/transform/**') .pipe(gulp.dest('dist/dart/angular2/test/transform')); diff --git a/modules/angular2/src/facade/lang.dart b/modules/angular2/src/facade/lang.dart index 0250a99a3c..ceb79f6633 100644 --- a/modules/angular2/src/facade/lang.dart +++ b/modules/angular2/src/facade/lang.dart @@ -95,6 +95,14 @@ class StringJoiner { } class NumberWrapper { + static String toFixed(num n, int fractionDigits) { + return n.toStringAsFixed(fractionDigits); + } + + static bool equal(num a, num b) { + return a == b; + } + static int parseIntAutoRadix(String text) { return int.parse(text); } diff --git a/modules/angular2/src/facade/lang.es6 b/modules/angular2/src/facade/lang.es6 index 78198143bc..613e0d51b7 100644 --- a/modules/angular2/src/facade/lang.es6 +++ b/modules/angular2/src/facade/lang.es6 @@ -138,6 +138,14 @@ export class NumberParseError extends Error { export class NumberWrapper { + static toFixed(n:number, fractionDigits:int):string { + return n.toFixed(fractionDigits); + } + + static equal(a, b):boolean { + return a === b; + } + static parseIntAutoRadix(text:string):int { var result:int = parseInt(text); if (isNaN(result)) { diff --git a/modules/angular2/test/transform/transform_test.dart b/modules/angular2/test/transform/transform.server.spec.dart similarity index 96% rename from modules/angular2/test/transform/transform_test.dart rename to modules/angular2/test/transform/transform.server.spec.dart index ebaaa342ab..de9ba0d246 100644 --- a/modules/angular2/test/transform/transform_test.dart +++ b/modules/angular2/test/transform/transform.server.spec.dart @@ -123,11 +123,11 @@ void _runTests() { // Read in input & output files. config.assetPathToInputPath.forEach((key, value) { config.assetPathToInputPath[key] = - cache.putIfAbsent(value, () => new File(value).readAsStringSync()); + cache.putIfAbsent(value, () => new File('test/transform/${value}').readAsStringSync()); }); config.assetPathToExpectedOutputPath.forEach((key, value) { config.assetPathToExpectedOutputPath[key] = cache.putIfAbsent(value, () { - var code = new File(value).readAsStringSync(); + var code = new File('test/transform/${value}').readAsStringSync(); return value.endsWith('dart') ? formatter.format(code) : code; }); }); diff --git a/modules/benchpress/pubspec.yaml b/modules/benchpress/pubspec.yaml index 31a03fd9a7..e7d6a15874 100644 --- a/modules/benchpress/pubspec.yaml +++ b/modules/benchpress/pubspec.yaml @@ -12,5 +12,6 @@ dependencies: stack_trace: '>=1.1.1 <1.2.0' angular2: path: ../angular2 + webdriver: ">=0.9.0 <0.10.0" dev_dependencies: guinness: ">=0.1.16 <0.2.0" diff --git a/modules/benchpress/src/metric/perflog_metric.js b/modules/benchpress/src/metric/perflog_metric.js index c15eb33821..0b4b95876d 100644 --- a/modules/benchpress/src/metric/perflog_metric.js +++ b/modules/benchpress/src/metric/perflog_metric.js @@ -1,5 +1,5 @@ import { PromiseWrapper, Promise } from 'angular2/src/facade/async'; -import { isPresent, isBlank, int, BaseException, StringWrapper } from 'angular2/src/facade/lang'; +import { isPresent, isBlank, int, BaseException, StringWrapper, Math } from 'angular2/src/facade/lang'; import { ListWrapper, StringMap, StringMapWrapper } from 'angular2/src/facade/collection'; import { bind, OpaqueToken } from 'angular2/di'; @@ -95,7 +95,12 @@ export class PerflogMetric extends Metric { if (needSort) { // Need to sort because of the ph==='X' events ListWrapper.sort(this._remainingEvents, (a,b) => { - return a['ts'] - b['ts']; + var diff = a['ts'] - b['ts']; + return diff > 0 + ? 1 + : diff < 0 + ? -1 + : 0; }); } } diff --git a/modules/benchpress/src/reporter/console_reporter.js b/modules/benchpress/src/reporter/console_reporter.js index 6050611d41..b343617c0f 100644 --- a/modules/benchpress/src/reporter/console_reporter.js +++ b/modules/benchpress/src/reporter/console_reporter.js @@ -1,4 +1,4 @@ -import { print, isPresent, isBlank } from 'angular2/src/facade/lang'; +import { print, isPresent, isBlank, NumberWrapper } from 'angular2/src/facade/lang'; import { StringMapWrapper, ListWrapper, List } from 'angular2/src/facade/collection'; import { Promise, PromiseWrapper } from 'angular2/src/facade/async'; import { Math } from 'angular2/src/facade/math'; @@ -28,14 +28,8 @@ export class ConsoleReporter extends Reporter { return result + value; } - static _formatNum(num) { - var result; - if (num === 0) { - result = '000'; - } else { - result = `${Math.floor(num * 100)}`; - } - return result.substring(0, result.length - 2) + '.' + result.substring(result.length-2); + static _formatNum(n) { + return NumberWrapper.toFixed(n, 2); } static _sortedProps(obj) { @@ -89,7 +83,8 @@ export class ConsoleReporter extends Reporter { var sample = ListWrapper.map(validSample, (measureValues) => measureValues.values[metricName]); var mean = Statistic.calculateMean(sample); var cv = Statistic.calculateCoefficientOfVariation(sample, mean); - return `${ConsoleReporter._formatNum(mean)}\u00B1${Math.floor(cv)}%`; + var formattedCv = NumberWrapper.isNaN(cv) ? 'NaN' : Math.floor(cv); + return `${ConsoleReporter._formatNum(mean)}\u00B1${formattedCv}%`; }) ); return PromiseWrapper.resolve(null); diff --git a/modules/benchpress/src/webdriver/async_webdriver_adapter.dart b/modules/benchpress/src/webdriver/async_webdriver_adapter.dart index 08c2d226a4..ab3095bf30 100644 --- a/modules/benchpress/src/webdriver/async_webdriver_adapter.dart +++ b/modules/benchpress/src/webdriver/async_webdriver_adapter.dart @@ -1,23 +1,32 @@ library benchpress.src.webdriver.async_webdriver_adapter_dart; +import 'package:webdriver/webdriver.dart' show WebDriver, LogEntry; import 'package:angular2/src/facade/async.dart' show Future; import '../web_driver_adapter.dart' show WebDriverAdapter; class AsyncWebDriverAdapter extends WebDriverAdapter { - dynamic _driver; - AsyncWebDriverAdapter(driver) { - this._driver = driver; - } + WebDriver _driver; + AsyncWebDriverAdapter(this._driver); + Future waitFor(Function callback) { return callback(); } + Future executeScript(String script) { - return this._driver.execute(script); + return _driver.execute(script, const[]); } - Future capabilities() { - return this._driver.capabilities; + + Map capabilities() { + return _driver.capabilities; } - Future logs(String type) { - return this._driver.logs.get(type); + + Future> logs(String type) { + return _driver.logs.get(type) + .map((LogEntry entry) => { + 'message': entry.message + }) + .fold([], (log, Map entry) { + return log..add(entry); + }); } } diff --git a/modules/examples/pubspec.yaml b/modules/examples/pubspec.yaml index dfde3a67f8..c44d3f51e2 100644 --- a/modules/examples/pubspec.yaml +++ b/modules/examples/pubspec.yaml @@ -7,6 +7,8 @@ dependencies: browser: '>=0.10.0 <0.11.0' dev_dependencies: guinness: ">=0.1.16 <0.2.0" + benchpress: + path: ../benchpress transformers: - angular2: bootstrap_entry_point: web/src/hello_world/index_common.dart diff --git a/modules/examples/src/benchpress/index.html b/modules/examples/src/benchpress/index.html new file mode 100644 index 0000000000..1ca2cddef8 --- /dev/null +++ b/modules/examples/src/benchpress/index.html @@ -0,0 +1,16 @@ + + + + Benchpress test + + + +
+ + + + diff --git a/modules/examples/test/benchpress/webdriver_async.server.spec.dart b/modules/examples/test/benchpress/webdriver_async.server.spec.dart new file mode 100644 index 0000000000..86990dc720 --- /dev/null +++ b/modules/examples/test/benchpress/webdriver_async.server.spec.dart @@ -0,0 +1,58 @@ +import 'dart:async'; +import 'dart:io' show Platform; +import 'package:guinness/guinness.dart'; +import 'package:benchpress/benchpress.dart'; +import 'package:benchpress/src/webdriver/async_webdriver_adapter.dart'; +import 'package:webdriver/webdriver.dart' show WebDriver, Capabilities, LogType, LogLevel, By; + +main() { + describe('benchpress', () { + WebDriver driver; + Runner runner; + + beforeEach(() async { + driver = await createTestDriver(); + await driver.get('http://localhost:8002/examples/src/benchpress/index.html'); + + var bindings = [ + bind(WebDriverAdapter).toFactory(() => new AsyncWebDriverAdapter(driver), []) + ]; + runner = new Runner(bindings); + }); + + afterEach(() async { + await driver.close(); + }); + + it('should work', () { + return runner.sample( + id: 'benchpress smoke test', + execute: () async { + var button = await driver.findElement(const By.tagName('button')); + await button.click(); + var logText = await (await driver.findElement(const By.id('log'))).text; + expect(logText, 'hi'); + } + ); + }); + }); +} + +Future createTestDriver() { + Map env = Platform.environment; + return WebDriver.createDriver(desiredCapabilities: { + 'name': 'Dartium', + 'browserName': 'chrome', + 'chromeOptions': { + 'binary': env['DARTIUM_BIN'], + 'args': ['--js-flags=--expose-gc'], + 'perfLoggingPrefs': { + 'traceCategories': 'blink.console,disabled-by-default-devtools.timeline' + }, + }, + 'loggingPrefs': { + 'performance': 'ALL', + 'browser': 'ALL', + } + }); +} diff --git a/scripts/ci/build_and_test.sh b/scripts/ci/build_and_test.sh index 7a276a8b68..2192043496 100755 --- a/scripts/ci/build_and_test.sh +++ b/scripts/ci/build_and_test.sh @@ -10,4 +10,7 @@ cd $SCRIPT_DIR/../.. ${SCRIPT_DIR}/build_$MODE.sh ${SCRIPT_DIR}/test_unit_$MODE.sh +if [ "$MODE" == "dart" ]; then # JS doesn't yet have server tests + ${SCRIPT_DIR}/test_server_$MODE.sh +fi ${SCRIPT_DIR}/test_e2e_$MODE.sh diff --git a/scripts/ci/test_server_dart.sh b/scripts/ci/test_server_dart.sh new file mode 100755 index 0000000000..7d1d2bea28 --- /dev/null +++ b/scripts/ci/test_server_dart.sh @@ -0,0 +1,29 @@ +#!/bin/bash +set -e + +echo ============================================================================= +# go to project dir +SCRIPT_DIR=$(dirname $0) +source $SCRIPT_DIR/env_dart.sh +cd $SCRIPT_DIR/../.. + +./node_modules/.bin/webdriver-manager update +./node_modules/.bin/webdriver-manager start& +webdriverServerPid=$! +ps -ef | grep webdriver-manager + +./node_modules/.bin/gulp serve.js.dart2js& +serverPid=$! + +function killAllServers () { + kill $serverPid + pkill -P $webdriverServerPid +} + +trap killAllServers EXIT + +# wait for server to come up! +sleep 3 + +./node_modules/.bin/gulp test.transpiler.unittest +./node_modules/.bin/gulp test.server.dart --browsers=$KARMA_BROWSERS diff --git a/scripts/ci/test_unit_dart.sh b/scripts/ci/test_unit_dart.sh index 7d767b415d..1cedbeb980 100755 --- a/scripts/ci/test_unit_dart.sh +++ b/scripts/ci/test_unit_dart.sh @@ -8,4 +8,4 @@ source $SCRIPT_DIR/env_dart.sh cd $SCRIPT_DIR/../.. ./node_modules/.bin/gulp test.transpiler.unittest -./node_modules/.bin/gulp test.dart/ci --browsers=$KARMA_BROWSERS +./node_modules/.bin/gulp test.unit.dart/ci --browsers=$KARMA_BROWSERS diff --git a/scripts/ci/test_unit_js.sh b/scripts/ci/test_unit_js.sh index 5b940aa117..13c245f3cb 100755 --- a/scripts/ci/test_unit_js.sh +++ b/scripts/ci/test_unit_js.sh @@ -8,4 +8,4 @@ source $SCRIPT_DIR/env_dart.sh cd $SCRIPT_DIR/../.. ./node_modules/.bin/gulp test.transpiler.unittest -./node_modules/.bin/gulp test.js/ci --browsers=$KARMA_BROWSERS +./node_modules/.bin/gulp test.unit.js/ci --browsers=$KARMA_BROWSERS diff --git a/tools/build/dartanalyzer.js b/tools/build/dartanalyzer.js index 98d9e08e10..cfde6cd1f0 100644 --- a/tools/build/dartanalyzer.js +++ b/tools/build/dartanalyzer.js @@ -4,30 +4,32 @@ var spawn = require('child_process').spawn; var path = require('path'); var glob = require('glob'); var fs = require('fs'); +var util = require('./util'); module.exports = function(gulp, plugins, config) { return function() { - var dartModuleFolders = [].slice.call(glob.sync(config.dest + '/*')); var tempFile = '_analyzer.dart'; - // analyze in parallel! - return Q.all(dartModuleFolders.map(function(dir) { - var srcFiles = [].slice.call(glob.sync(dir + '{/lib,/web}/**/*.dart', { - cwd: dir - })); - var testFiles = [].slice.call(glob.sync('test/**/*_spec.dart', { - cwd: dir - })); - var analyzeFile = ['library _analyzer;']; - srcFiles.concat(testFiles).forEach(function(fileName, index) { - if (fileName !== tempFile && fileName.indexOf("/packages/") === -1) { - analyzeFile.push('import "./'+fileName+'" as mod'+index+';'); - } - }); - fs.writeFileSync(path.join(dir, tempFile), analyzeFile.join('\n')); - var defer = Q.defer(); - analyze(dir, defer.makeNodeResolver()); - return defer.promise; - })); + return util.forEachSubDir( + config.dest, + function(dir) { + var srcFiles = [].slice.call(glob.sync('{/lib,/web}/**/*.dart', { + cwd: dir + })); + var testFiles = [].slice.call(glob.sync('test/**/*_spec.dart', { + cwd: dir + })); + var analyzeFile = ['library _analyzer;']; + srcFiles.concat(testFiles).forEach(function(fileName, index) { + if (fileName !== tempFile && fileName.indexOf("/packages/") === -1) { + analyzeFile.push('import "./'+fileName+'" as mod'+index+';'); + } + }); + fs.writeFileSync(path.join(dir, tempFile), analyzeFile.join('\n')); + var defer = Q.defer(); + analyze(dir, defer.makeNodeResolver()); + return defer.promise; + } + ); function analyze(dirName, done) { //TODO remove --package-warnings once dartanalyzer handles transitive libraries diff --git a/tools/build/run_server_dart_tests.js b/tools/build/run_server_dart_tests.js new file mode 100644 index 0000000000..76dbceb7d4 --- /dev/null +++ b/tools/build/run_server_dart_tests.js @@ -0,0 +1,48 @@ +var Q = require('q'); +var glob = require('glob'); +var fs = require('fs'); +var path = require('path'); +var spawn = require('child_process').spawn; + +var util = require('./util'); + +module.exports = function(gulp, plugins, config) { + return function() { + return util.forEachSubDir( + config.dest, + function(dir) { + var testDir = path.join(dir, 'test'); + var relativeMasterTestFile = 'test/_all_tests.dart'; + var testFiles = [].slice.call(glob.sync('**/*.server.spec.dart', { + cwd: testDir + })); + if (testFiles.length == 0) { + // No test files found + return; + } + var header = ['library _all_tests;', '']; + var main = ['main() {']; + testFiles.forEach(function(fileName, index) { + header.push('import "' + fileName + '" as test_' + index + ';'); + main.push(' test_' + index + '.main();'); + }); + header.push(''); + main.push('}'); + + var absMasterTestFile = path.join(dir, relativeMasterTestFile); + fs.writeFileSync(absMasterTestFile, header.concat(main).join('\n')); + + var defer = Q.defer(); + var done = defer.makeNodeResolver(); + util.processToPromise(spawn('dart', ['-c', relativeMasterTestFile], { + stdio: 'inherit', + cwd: dir + })).then( + function() { done(); }, + function(error) { done(error); } + ); + return defer.promise; + } + ); + }; +}; diff --git a/tools/build/util.js b/tools/build/util.js index a1f60650c3..36dfca0726 100644 --- a/tools/build/util.js +++ b/tools/build/util.js @@ -1,15 +1,24 @@ var Q = require('q'); var path = require('path'); var minimatch = require('minimatch'); +var glob = require('glob'); module.exports = { processToPromise: processToPromise, streamToPromise: streamToPromise, insertSrcFolder: insertSrcFolder, - filterByFile: filterByFile + filterByFile: filterByFile, + forEachSubDir: forEachSubDir }; +function forEachSubDir(dir, callback) { + var moduleFolders = [].slice.call(glob.sync(dir + '/*')); + return Q.all(moduleFolders.map(function(subDir) { + return callback(subDir); + })); +}; + function processToPromise(process) { var defer = Q.defer(); process.on('close', function(code) {