diff --git a/karma-dart-evalcache.js b/karma-dart-evalcache.js new file mode 100644 index 0000000000..7caa8a13ec --- /dev/null +++ b/karma-dart-evalcache.js @@ -0,0 +1,86 @@ +// This module provides a customFileHandler for karma +// that serves files with urls like /packages_/... +// with maximum cache. +// We are using these urls when we spawn isolates +// so that the isolates don't reload files every time. + +var common = require('karma/lib/middleware/common'); +var fs = require('fs'); + +var DART_EVAL_PATH_RE = /.*\/packages_\d+\/(.*)$/; + +module.exports = createFactory; + +function createFactory(proxyPaths) { + return { + 'framework:dart-evalcache': ['factory', dartEvalCacheFactory] + }; + + function dartEvalCacheFactory(emitter, logger, customFileHandlers) { + var filesPromise = new common.PromiseContainer(); + emitter.on('file_list_modified', function(files) { + filesPromise.set(files); + }); + + var serveFile = common.createServeFile(fs); + var log = logger.create('dart-evalcache'); + + customFileHandlers.push({ + urlRegex: DART_EVAL_PATH_RE, + handler: handler + }); + + // See source_files handler + function handler(request, response, fa, fb, basePath) { + return filesPromise.then(function(files) { + try { + var requestedFilePath = mapUrlToFile(request.url, proxyPaths, basePath, log); + // TODO(vojta): change served to be a map rather then an array + var file = findByPath(files.served, requestedFilePath); + if (file) { + serveFile(file.contentPath || file.path, response, function() { + common.setHeavyCacheHeaders(response); + }, file.content); + } else { + response.writeHead(404); + response.end('Not found'); + } + } catch (e) { + log.error(e.stack); + response.writeHead(500); + response.end('Error', e.stack); + } + }); + } + }; +} + +function mapUrlToFile(url, proxyPaths, basePath, log) { + var originalUrl = url; + url = url.indexOf('?') > -1 ? url.substring(0, url.indexOf('?')) : url; + var match = DART_EVAL_PATH_RE.exec(url); + var packagePath = match[1]; + var result = null; + var lastProxyFromLength = 0; + Object.keys(proxyPaths).forEach(function(proxyFrom) { + if (startsWith(packagePath, proxyFrom) && proxyFrom.length > lastProxyFromLength) { + lastProxyFromLength = proxyFrom.length; + result = proxyPaths[proxyFrom] + packagePath.substring(proxyFrom.length); + } + }); + return basePath + '/' + result; +} + +function startsWith(string, subString) { + return string.length >= subString.length && string.slice(0, subString.length) === subString; +} + +function findByPath(files, path) { + for (var i = 0; i < files.length; i++) { + if (files[i].path === path) { + return files[i]; + } + } + + return null; +} diff --git a/karma-dart.conf.js b/karma-dart.conf.js index dd72ddf51e..307c710b41 100644 --- a/karma-dart.conf.js +++ b/karma-dart.conf.js @@ -1,11 +1,41 @@ var sauceConf = require('./sauce.conf'); +var packageSources = { + // Dependencies installed with `pub install`. + 'unittest': 'packages/unittest', + 'guinness': 'packages/guinness', + 'matcher': 'packages/matcher', + 'stack_trace': 'packages/stack_trace', + 'collection': 'packages/collection', + 'path': 'packages/path', + 'observe': 'packages/observe', + 'quiver': 'packages/quiver', + 'intl': 'packages/intl', + 'smoke': 'packages/smoke', + 'logging': 'packages/logging', + 'utf': 'packages/utf', + + // Local dependencies, transpiled from the source. + 'angular2/test/': 'dist/dart/angular2/test/', + 'angular2': 'dist/dart/angular2/lib', + 'http': 'dist/dart/http/lib', + 'angular2_material': 'dist/dart/angular2_material/lib', + 'benchpress': 'dist/dart/benchpress/lib', + 'examples': 'dist/dart/examples/lib' +}; + +var proxyPaths = {}; +Object.keys(packageSources).map(function(packageName) { + var filePath = packageSources[packageName]; + proxyPaths['/packages/'+packageName] = '/base/'+filePath; +}); + // Karma configuration // Generated on Thu Sep 25 2014 11:52:02 GMT-0700 (PDT) module.exports = function(config) { config.set({ - frameworks: ['dart-unittest'], + frameworks: ['dart-unittest', 'dart-evalcache'], files: [ // Init and configure guiness. @@ -35,27 +65,18 @@ module.exports = function(config) { }, // Map packages to the correct urls where Karma serves them. - proxies: { - // Dependencies installed with `pub install`. - '/packages/unittest': '/base/packages/unittest', - '/packages/guinness': '/base/packages/guinness', - '/packages/matcher': '/base/packages/matcher', - '/packages/stack_trace': '/base/packages/stack_trace', - '/packages/collection': '/base/packages/collection', - '/packages/path': '/base/packages/path', - - // Local dependencies, transpiled from the source. - '/packages/angular2/test/': '/base/dist/dart/angular2/test/', - '/packages/angular2': '/base/dist/dart/angular2/lib', - '/packages/http': '/base/dist/dart/http/lib', - '/packages/angular2_material': '/base/dist/dart/angular2_material/lib', - '/packages/benchpress': '/base/dist/dart/benchpress/lib', - '/packages/examples': '/base/dist/dart/examples/lib' - }, + proxies: proxyPaths, customLaunchers: sauceConf.customLaunchers, browsers: ['DartiumWithWebPlatform'], - port: 9877 + port: 9877, + + plugins: [ + require('karma-dart'), + require('karma-chrome-launcher'), + require('karma-sauce-launcher'), + require('./karma-dart-evalcache')(packageSources) + ] }); }; diff --git a/modules/angular2/test/compiler/eval_module.dart b/modules/angular2/test/compiler/eval_module.dart index e555b2b5ee..d2bf8b920d 100644 --- a/modules/angular2/test/compiler/eval_module.dart +++ b/modules/angular2/test/compiler/eval_module.dart @@ -13,7 +13,7 @@ createIsolateSource(String moduleSource, List> moduleImports) { String modAlias = sourceImport[1]; moduleSourceParts.add("import 'package:${modName}.dart' as ${modAlias};"); }); - moduleSourceParts.add(moduleSource); + moduleSourceParts.add(moduleSource); return """ import "dart:isolate"; @@ -26,6 +26,8 @@ main(List args, SendPort replyPort) { } +var timeStamp = new DateTime.now().millisecondsSinceEpoch; + dynamic callModule(dynamic data) { return data.map( (a) => a+1); } evalModule(String moduleSource, List> moduleImports, List args) { @@ -36,7 +38,11 @@ evalModule(String moduleSource, List> moduleImports, List args) { receivePort.close(); completer.complete(message); }); - var packageRoot = Uri.parse('/packages'); + // Note: we have a special karma plugin that sends files under + // urls like /package_1234 as permanently cached. + // With this, spawning multiple isolates gets faster as Darts does not + // reload the files from the server. + var packageRoot = Uri.parse('/packages_${timeStamp}'); return Isolate.spawnUri(toDartDataUri(source), args, receivePort.sendPort, packageRoot: packageRoot).then( (isolate) { RawReceivePort errorPort; errorPort = new RawReceivePort( (message) {