fix(test): make `evalModule` faster by caching spawned modules in the browser

This commit is contained in:
Tobias Bosch 2015-09-11 13:33:24 -07:00
parent d9036c6cf3
commit 67c79ba3f6
3 changed files with 134 additions and 21 deletions

86
karma-dart-evalcache.js Normal file
View File

@ -0,0 +1,86 @@
// This module provides a customFileHandler for karma
// that serves files with urls like /packages_<timestamp>/...
// 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;
}

View File

@ -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)
]
});
};

View File

@ -13,7 +13,7 @@ createIsolateSource(String moduleSource, List<List<String>> 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<List<String>> moduleImports, List args) {
@ -36,7 +38,11 @@ evalModule(String moduleSource, List<List<String>> 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) {