diff -r 000000000000 -r 6474c204b198 testing/mochitest/chrome-harness.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/testing/mochitest/chrome-harness.js Wed Dec 31 06:09:35 2014 +0100 @@ -0,0 +1,441 @@ +/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim:set ts=2 sw=2 sts=2 et: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + + +Components.utils.import("resource://gre/modules/NetUtil.jsm"); + +/* + * getChromeURI converts a URL to a URI + * + * url: string of a URL (http://mochi.test/test.html) + * returns: a nsiURI object representing the given URL + * + */ +function getChromeURI(url) { + var ios = Components.classes["@mozilla.org/network/io-service;1"]. + getService(Components.interfaces.nsIIOService); + return ios.newURI(url, null, null); +} + +/* + * Convert a URL (string) into a nsIURI or NSIJARURI + * This is intended for URL's that are on a file system + * or in packaged up in an extension .jar file + * + * url: a string of a url on the local system(http://localhost/blah.html) + */ +function getResolvedURI(url) { + var chromeURI = getChromeURI(url); + var resolvedURI = Components.classes["@mozilla.org/chrome/chrome-registry;1"]. + getService(Components.interfaces.nsIChromeRegistry). + convertChromeURL(chromeURI); + + try { + resolvedURI = resolvedURI.QueryInterface(Components.interfaces.nsIJARURI); + } catch (ex) {} //not a jar file + + return resolvedURI; +} + +/** + * getChromeDir is intended to be called after getResolvedURI and convert + * the input URI into a nsILocalFile (actually the directory containing the + * file). This can be used for copying or referencing the file or extra files + * required by the test. Usually we need to load a secondary html file or library + * and this will give us file system access to that. + * + * resolvedURI: nsIURI (from getResolvedURI) that points to a file:/// url + */ +function getChromeDir(resolvedURI) { + + var fileHandler = Components.classes["@mozilla.org/network/protocol;1?name=file"]. + getService(Components.interfaces.nsIFileProtocolHandler); + var chromeDir = fileHandler.getFileFromURLSpec(resolvedURI.spec); + return chromeDir.parent.QueryInterface(Components.interfaces.nsILocalFile); +} + +/* + * given a .jar file, we get all test files located inside the archive + * + * aBasePath: base URL to determine chrome location and search for tests + * aTestPath: passed in testPath value from command line such as: dom/tests/mochitest + * aDir: the test dir to append to the baseURL after getting a directory interface + * + * As a note, this is hardcoded to the .jar structure we use for mochitest. + * Please don't assume this works for all jar files. + */ +function getMochitestJarListing(aBasePath, aTestPath, aDir) +{ + var zReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]. + createInstance(Components.interfaces.nsIZipReader); + var fileHandler = Components.classes["@mozilla.org/network/protocol;1?name=file"]. + getService(Components.interfaces.nsIFileProtocolHandler); + + var fileName = fileHandler.getFileFromURLSpec(getResolvedURI(aBasePath).JARFile.spec); + zReader.open(fileName); + //hardcoded 'content' as that is the root dir in the mochikit.jar file + var idx = aBasePath.indexOf('/content'); + var basePath = aBasePath.slice(0, idx); + + var base = "content/" + aDir + "/"; + + if (aTestPath) { + var extraPath = aTestPath; + var pathToCheck = base + aTestPath; + if (zReader.hasEntry(pathToCheck)) { + var pathEntry = zReader.getEntry(pathToCheck); + if (pathEntry.isDirectory) { + base = pathToCheck; + } else { + var singleTestPath = basePath + '/' + base + aTestPath; + var singleObject = {}; + singleObject[singleTestPath] = true; + return singleObject; + } + } + else if (zReader.hasEntry(pathToCheck + "/")) { + base = pathToCheck + "/"; + } + else { + return null; + } + } + var [links, count] = zList(base, zReader, basePath, true); + return links; +} + +/* + * Replicate the server.js list() function with a .jar file + * + * base: string value of base directory we are testing + * zReader: handle to opened nsIZipReader object + * recurse: true|false if we do subdirs + * + * returns: + * [json object of {dir:{subdir:{file:true, file:true, ...}}}, count of tests] + */ +function zList(base, zReader, baseJarName, recurse) { + var dirs = zReader.findEntries(base + "*"); + var links = {}; + var count = 0; + var fileArray = []; + + while(dirs.hasMore()) { + var entryName = dirs.getNext(); + if (entryName.substr(-1) == '/' && entryName.split('/').length == (base.split('/').length + 1) || + (entryName.substr(-1) != '/' && entryName.split('/').length == (base.split('/').length))) { + fileArray.push(entryName); + } + } + fileArray.sort(); + count = fileArray.length; + for (var i=0; i < fileArray.length; i++) { + var myFile = fileArray[i]; + if (myFile.substr(-1) === '/' && recurse) { + var childCount = 0; + [links[myFile], childCount] = zList(myFile, zReader, baseJarName, recurse); + count += childCount; + } else { + if (myFile.indexOf("SimpleTest") == -1) { + //we add the '/' so we don't try to run content/content/chrome + links[baseJarName + '/' + myFile] = true; + } + } + } + return [links, count]; +} + +/** + * basePath: the URL base path to search from such as chrome://mochikit/content/a11y + * testPath: the optional testPath passed into the test such as dom/tests/mochitest + * dir: the test dir to append to the uri after getting a directory interface + * srvScope: loaded javascript to server.js so we have aComponents.classesess to the list() function + * + * return value: + * single test: [json object, path to test] + * list of tests: [json object, null] <- directory [heirarchy] + */ +function getFileListing(basePath, testPath, dir, srvScope) +{ + var uri = getResolvedURI(basePath); + var chromeDir = getChromeDir(uri); + chromeDir.appendRelativePath(dir); + basePath += '/' + dir; + + if (testPath == "false" || testPath == false) { + testPath = ""; + } + + var ioSvc = Components.classes["@mozilla.org/network/io-service;1"]. + getService(Components.interfaces.nsIIOService); + var testsDirURI = ioSvc.newFileURI(chromeDir); + var testsDir = ioSvc.newURI(testPath, null, testsDirURI) + .QueryInterface(Components.interfaces.nsIFileURL).file; + + if (testPath != undefined) { + var extraPath = testPath; + + var fileNameRegexp = /(browser|test)_.+\.(xul|html|js)$/; + + // Invalid testPath... + if (!testsDir.exists()) + return null; + + if (testsDir.isFile()) { + if (fileNameRegexp.test(testsDir.leafName)) { + var singlePath = basePath + '/' + testPath; + var links = {}; + links[singlePath] = true; + return links; + } + // We were passed a file that's not a test... + return null; + } + + // otherwise, we were passed a directory of tests + basePath += "/" + testPath; + } + var [links, count] = srvScope.list(basePath, testsDir, true); + return links; +} + + +//used by tests to determine their directory based off window.location.path +function getRootDirectory(path, chromeURI) { + if (chromeURI === undefined) + { + chromeURI = getChromeURI(path); + } + var myURL = chromeURI.QueryInterface(Components.interfaces.nsIURL); + var mydir = myURL.directory; + + if (mydir.match('/$') != '/') + { + mydir += '/'; + } + + return chromeURI.prePath + mydir; +} + +//used by tests to determine their directory based off window.location.path +function getChromePrePath(path, chromeURI) { + + if (chromeURI === undefined) { + chromeURI = getChromeURI(path); + } + + return chromeURI.prePath; +} + +/* + * Given a URI, return nsIJARURI or null + */ +function getJar(uri) { + var resolvedURI = getResolvedURI(uri); + var jar = null; + try { + if (resolvedURI.JARFile) { + jar = resolvedURI; + } + } catch (ex) {} + return jar; +} + +/* + * input: + * jar: a nsIJARURI object with the jarfile and jarentry (path in jar file) + * + * output; + * all files and subdirectories inside jarentry will be extracted to TmpD/mochikit.tmp + * we will return the location of /TmpD/mochikit.tmp* so you can reference the files locally + */ +function extractJarToTmp(jar) { + var tmpdir = Components.classes["@mozilla.org/file/directory_service;1"] + .getService(Components.interfaces.nsIProperties) + .get("ProfD", Components.interfaces.nsILocalFile); + tmpdir.append("mochikit.tmp"); + // parseInt is used because octal escape sequences cause deprecation warnings + // in strict mode (which is turned on in debug builds) + tmpdir.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, parseInt("0777", 8)); + + var zReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]. + createInstance(Components.interfaces.nsIZipReader); + + var fileHandler = Components.classes["@mozilla.org/network/protocol;1?name=file"]. + getService(Components.interfaces.nsIFileProtocolHandler); + + var fileName = fileHandler.getFileFromURLSpec(jar.JARFile.spec); + zReader.open(fileName); + + //filepath represents the path in the jar file without the filename + var filepath = ""; + var parts = jar.JAREntry.split('/'); + for (var i =0; i < parts.length - 1; i++) { + if (parts[i] != '') { + filepath += parts[i] + '/'; + } + } + + /* Create dir structure first, no guarantee about ordering of directories and + * files returned from findEntries. + */ + var dirs = zReader.findEntries(filepath + '*/'); + while (dirs.hasMore()) { + var targetDir = buildRelativePath(dirs.getNext(), tmpdir, filepath); + // parseInt is used because octal escape sequences cause deprecation warnings + // in strict mode (which is turned on in debug builds) + if (!targetDir.exists()) { + targetDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, parseInt("0777", 8)); + } + } + + //now do the files + var files = zReader.findEntries(filepath + "*"); + while (files.hasMore()) { + var fname = files.getNext(); + if (fname.substr(-1) != '/') { + var targetFile = buildRelativePath(fname, tmpdir, filepath); + zReader.extract(fname, targetFile); + } + } + return tmpdir; +} + +/* + * Take a relative path from the current mochitest file + * and returns the absolute path for the given test data file. + */ +function getTestFilePath(path) { + if (path[0] == "/") { + throw new Error("getTestFilePath only accepts relative path"); + } + // Get the chrome/jar uri for the current mochitest file + // gTestPath being defined by the test harness in browser-chrome tests + // or window is being used for mochitest-browser + var baseURI = typeof(gTestPath) == "string" ? gTestPath : window.location.href; + var parentURI = getResolvedURI(getRootDirectory(baseURI)); + var file; + if (parentURI.JARFile) { + // If it's a jar/zip, we have to extract it first + file = extractJarToTmp(parentURI); + } else { + // Otherwise, we can directly cast it to a file URI + var fileHandler = Components.classes["@mozilla.org/network/protocol;1?name=file"]. + getService(Components.interfaces.nsIFileProtocolHandler); + file = fileHandler.getFileFromURLSpec(parentURI.spec); + } + // Then walk by the given relative path + path.split("/") + .forEach(function (p) { + if (p == "..") { + file = file.parent; + } else if (p != ".") { + file.append(p); + } + }); + return file.path; +} + +/* + * Simple utility function to take the directory structure in jarentryname and + * translate that to a path of a nsILocalFile. + */ +function buildRelativePath(jarentryname, destdir, basepath) +{ + var baseParts = basepath.split('/'); + if (baseParts[baseParts.length-1] == '') { + baseParts.pop(); + } + + var parts = jarentryname.split('/'); + + var targetFile = Components.classes["@mozilla.org/file/local;1"] + .createInstance(Components.interfaces.nsILocalFile); + targetFile.initWithFile(destdir); + + for (var i = baseParts.length; i < parts.length; i++) { + targetFile.append(parts[i]); + } + + return targetFile; +} + +function readConfig(filename) { + filename = filename || "testConfig.js"; + + var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties); + var configFile = fileLocator.get("ProfD", Components.interfaces.nsIFile); + configFile.append(filename); + + if (!configFile.exists()) + return {}; + + var fileInStream = Components.classes["@mozilla.org/network/file-input-stream;1"]. + createInstance(Components.interfaces.nsIFileInputStream); + fileInStream.init(configFile, -1, 0, 0); + + var str = NetUtil.readInputStreamToString(fileInStream, fileInStream.available()); + fileInStream.close(); + return JSON.parse(str); +} + +function registerTests() { + var testsURI = Components.classes["@mozilla.org/file/directory_service;1"]. + getService(Components.interfaces.nsIProperties). + get("ProfD", Components.interfaces.nsILocalFile); + testsURI.append("tests.manifest"); + var ioSvc = Components.classes["@mozilla.org/network/io-service;1"]. + getService(Components.interfaces.nsIIOService); + var manifestFile = ioSvc.newFileURI(testsURI). + QueryInterface(Components.interfaces.nsIFileURL).file; + + Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar). + autoRegister(manifestFile); +} + +function getTestList(params, callback) { + registerTests(); + + var baseurl = 'chrome://mochitests/content'; + if (window.parseQueryString) { + params = parseQueryString(location.search.substring(1), true); + } + if (!params.baseurl) { + params.baseurl = baseurl; + } + + var config = readConfig(); + for (var p in params) { + if (params[p] == 1) { + config[p] = true; + } else if (params[p] == 0) { + config[p] = false; + } else { + config[p] = params[p]; + } + } + params = config; + if (params.manifestFile) { + getTestManifest("http://mochi.test:8888/" + params.manifestFile, params, callback); + return; + } + + var links = {}; + // load server.js in so we can share template functions + var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. + getService(Ci.mozIJSSubScriptLoader); + var srvScope = {}; + scriptLoader.loadSubScript('chrome://mochikit/content/server.js', + srvScope); + + if (getResolvedURI(baseurl).JARFile) { + links = getMochitestJarListing(baseurl, params.testPath, params.testRoot); + } else { + links = getFileListing(baseurl, params.testPath, params.testRoot, srvScope); + } + callback(links); +}