/* -*- indent-tabs-mode: nil; js-indent-level: 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.replace(/\\/g, '/'); if (testPath == "false" || testPath == false) { testPath = ""; } testPath = testPath.replace(/\\\\/g, '\\').replace(/\\/g, '/'); 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); }