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