|
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim:set ts=2 sw=2 sts=2 et: */ |
|
3 /* This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 |
|
8 Components.utils.import("resource://gre/modules/NetUtil.jsm"); |
|
9 |
|
10 /* |
|
11 * getChromeURI converts a URL to a URI |
|
12 * |
|
13 * url: string of a URL (http://mochi.test/test.html) |
|
14 * returns: a nsiURI object representing the given URL |
|
15 * |
|
16 */ |
|
17 function getChromeURI(url) { |
|
18 var ios = Components.classes["@mozilla.org/network/io-service;1"]. |
|
19 getService(Components.interfaces.nsIIOService); |
|
20 return ios.newURI(url, null, null); |
|
21 } |
|
22 |
|
23 /* |
|
24 * Convert a URL (string) into a nsIURI or NSIJARURI |
|
25 * This is intended for URL's that are on a file system |
|
26 * or in packaged up in an extension .jar file |
|
27 * |
|
28 * url: a string of a url on the local system(http://localhost/blah.html) |
|
29 */ |
|
30 function getResolvedURI(url) { |
|
31 var chromeURI = getChromeURI(url); |
|
32 var resolvedURI = Components.classes["@mozilla.org/chrome/chrome-registry;1"]. |
|
33 getService(Components.interfaces.nsIChromeRegistry). |
|
34 convertChromeURL(chromeURI); |
|
35 |
|
36 try { |
|
37 resolvedURI = resolvedURI.QueryInterface(Components.interfaces.nsIJARURI); |
|
38 } catch (ex) {} //not a jar file |
|
39 |
|
40 return resolvedURI; |
|
41 } |
|
42 |
|
43 /** |
|
44 * getChromeDir is intended to be called after getResolvedURI and convert |
|
45 * the input URI into a nsILocalFile (actually the directory containing the |
|
46 * file). This can be used for copying or referencing the file or extra files |
|
47 * required by the test. Usually we need to load a secondary html file or library |
|
48 * and this will give us file system access to that. |
|
49 * |
|
50 * resolvedURI: nsIURI (from getResolvedURI) that points to a file:/// url |
|
51 */ |
|
52 function getChromeDir(resolvedURI) { |
|
53 |
|
54 var fileHandler = Components.classes["@mozilla.org/network/protocol;1?name=file"]. |
|
55 getService(Components.interfaces.nsIFileProtocolHandler); |
|
56 var chromeDir = fileHandler.getFileFromURLSpec(resolvedURI.spec); |
|
57 return chromeDir.parent.QueryInterface(Components.interfaces.nsILocalFile); |
|
58 } |
|
59 |
|
60 /* |
|
61 * given a .jar file, we get all test files located inside the archive |
|
62 * |
|
63 * aBasePath: base URL to determine chrome location and search for tests |
|
64 * aTestPath: passed in testPath value from command line such as: dom/tests/mochitest |
|
65 * aDir: the test dir to append to the baseURL after getting a directory interface |
|
66 * |
|
67 * As a note, this is hardcoded to the .jar structure we use for mochitest. |
|
68 * Please don't assume this works for all jar files. |
|
69 */ |
|
70 function getMochitestJarListing(aBasePath, aTestPath, aDir) |
|
71 { |
|
72 var zReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]. |
|
73 createInstance(Components.interfaces.nsIZipReader); |
|
74 var fileHandler = Components.classes["@mozilla.org/network/protocol;1?name=file"]. |
|
75 getService(Components.interfaces.nsIFileProtocolHandler); |
|
76 |
|
77 var fileName = fileHandler.getFileFromURLSpec(getResolvedURI(aBasePath).JARFile.spec); |
|
78 zReader.open(fileName); |
|
79 //hardcoded 'content' as that is the root dir in the mochikit.jar file |
|
80 var idx = aBasePath.indexOf('/content'); |
|
81 var basePath = aBasePath.slice(0, idx); |
|
82 |
|
83 var base = "content/" + aDir + "/"; |
|
84 |
|
85 if (aTestPath) { |
|
86 var extraPath = aTestPath; |
|
87 var pathToCheck = base + aTestPath; |
|
88 if (zReader.hasEntry(pathToCheck)) { |
|
89 var pathEntry = zReader.getEntry(pathToCheck); |
|
90 if (pathEntry.isDirectory) { |
|
91 base = pathToCheck; |
|
92 } else { |
|
93 var singleTestPath = basePath + '/' + base + aTestPath; |
|
94 var singleObject = {}; |
|
95 singleObject[singleTestPath] = true; |
|
96 return singleObject; |
|
97 } |
|
98 } |
|
99 else if (zReader.hasEntry(pathToCheck + "/")) { |
|
100 base = pathToCheck + "/"; |
|
101 } |
|
102 else { |
|
103 return null; |
|
104 } |
|
105 } |
|
106 var [links, count] = zList(base, zReader, basePath, true); |
|
107 return links; |
|
108 } |
|
109 |
|
110 /* |
|
111 * Replicate the server.js list() function with a .jar file |
|
112 * |
|
113 * base: string value of base directory we are testing |
|
114 * zReader: handle to opened nsIZipReader object |
|
115 * recurse: true|false if we do subdirs |
|
116 * |
|
117 * returns: |
|
118 * [json object of {dir:{subdir:{file:true, file:true, ...}}}, count of tests] |
|
119 */ |
|
120 function zList(base, zReader, baseJarName, recurse) { |
|
121 var dirs = zReader.findEntries(base + "*"); |
|
122 var links = {}; |
|
123 var count = 0; |
|
124 var fileArray = []; |
|
125 |
|
126 while(dirs.hasMore()) { |
|
127 var entryName = dirs.getNext(); |
|
128 if (entryName.substr(-1) == '/' && entryName.split('/').length == (base.split('/').length + 1) || |
|
129 (entryName.substr(-1) != '/' && entryName.split('/').length == (base.split('/').length))) { |
|
130 fileArray.push(entryName); |
|
131 } |
|
132 } |
|
133 fileArray.sort(); |
|
134 count = fileArray.length; |
|
135 for (var i=0; i < fileArray.length; i++) { |
|
136 var myFile = fileArray[i]; |
|
137 if (myFile.substr(-1) === '/' && recurse) { |
|
138 var childCount = 0; |
|
139 [links[myFile], childCount] = zList(myFile, zReader, baseJarName, recurse); |
|
140 count += childCount; |
|
141 } else { |
|
142 if (myFile.indexOf("SimpleTest") == -1) { |
|
143 //we add the '/' so we don't try to run content/content/chrome |
|
144 links[baseJarName + '/' + myFile] = true; |
|
145 } |
|
146 } |
|
147 } |
|
148 return [links, count]; |
|
149 } |
|
150 |
|
151 /** |
|
152 * basePath: the URL base path to search from such as chrome://mochikit/content/a11y |
|
153 * testPath: the optional testPath passed into the test such as dom/tests/mochitest |
|
154 * dir: the test dir to append to the uri after getting a directory interface |
|
155 * srvScope: loaded javascript to server.js so we have aComponents.classesess to the list() function |
|
156 * |
|
157 * return value: |
|
158 * single test: [json object, path to test] |
|
159 * list of tests: [json object, null] <- directory [heirarchy] |
|
160 */ |
|
161 function getFileListing(basePath, testPath, dir, srvScope) |
|
162 { |
|
163 var uri = getResolvedURI(basePath); |
|
164 var chromeDir = getChromeDir(uri); |
|
165 chromeDir.appendRelativePath(dir); |
|
166 basePath += '/' + dir; |
|
167 |
|
168 if (testPath == "false" || testPath == false) { |
|
169 testPath = ""; |
|
170 } |
|
171 |
|
172 var ioSvc = Components.classes["@mozilla.org/network/io-service;1"]. |
|
173 getService(Components.interfaces.nsIIOService); |
|
174 var testsDirURI = ioSvc.newFileURI(chromeDir); |
|
175 var testsDir = ioSvc.newURI(testPath, null, testsDirURI) |
|
176 .QueryInterface(Components.interfaces.nsIFileURL).file; |
|
177 |
|
178 if (testPath != undefined) { |
|
179 var extraPath = testPath; |
|
180 |
|
181 var fileNameRegexp = /(browser|test)_.+\.(xul|html|js)$/; |
|
182 |
|
183 // Invalid testPath... |
|
184 if (!testsDir.exists()) |
|
185 return null; |
|
186 |
|
187 if (testsDir.isFile()) { |
|
188 if (fileNameRegexp.test(testsDir.leafName)) { |
|
189 var singlePath = basePath + '/' + testPath; |
|
190 var links = {}; |
|
191 links[singlePath] = true; |
|
192 return links; |
|
193 } |
|
194 // We were passed a file that's not a test... |
|
195 return null; |
|
196 } |
|
197 |
|
198 // otherwise, we were passed a directory of tests |
|
199 basePath += "/" + testPath; |
|
200 } |
|
201 var [links, count] = srvScope.list(basePath, testsDir, true); |
|
202 return links; |
|
203 } |
|
204 |
|
205 |
|
206 //used by tests to determine their directory based off window.location.path |
|
207 function getRootDirectory(path, chromeURI) { |
|
208 if (chromeURI === undefined) |
|
209 { |
|
210 chromeURI = getChromeURI(path); |
|
211 } |
|
212 var myURL = chromeURI.QueryInterface(Components.interfaces.nsIURL); |
|
213 var mydir = myURL.directory; |
|
214 |
|
215 if (mydir.match('/$') != '/') |
|
216 { |
|
217 mydir += '/'; |
|
218 } |
|
219 |
|
220 return chromeURI.prePath + mydir; |
|
221 } |
|
222 |
|
223 //used by tests to determine their directory based off window.location.path |
|
224 function getChromePrePath(path, chromeURI) { |
|
225 |
|
226 if (chromeURI === undefined) { |
|
227 chromeURI = getChromeURI(path); |
|
228 } |
|
229 |
|
230 return chromeURI.prePath; |
|
231 } |
|
232 |
|
233 /* |
|
234 * Given a URI, return nsIJARURI or null |
|
235 */ |
|
236 function getJar(uri) { |
|
237 var resolvedURI = getResolvedURI(uri); |
|
238 var jar = null; |
|
239 try { |
|
240 if (resolvedURI.JARFile) { |
|
241 jar = resolvedURI; |
|
242 } |
|
243 } catch (ex) {} |
|
244 return jar; |
|
245 } |
|
246 |
|
247 /* |
|
248 * input: |
|
249 * jar: a nsIJARURI object with the jarfile and jarentry (path in jar file) |
|
250 * |
|
251 * output; |
|
252 * all files and subdirectories inside jarentry will be extracted to TmpD/mochikit.tmp |
|
253 * we will return the location of /TmpD/mochikit.tmp* so you can reference the files locally |
|
254 */ |
|
255 function extractJarToTmp(jar) { |
|
256 var tmpdir = Components.classes["@mozilla.org/file/directory_service;1"] |
|
257 .getService(Components.interfaces.nsIProperties) |
|
258 .get("ProfD", Components.interfaces.nsILocalFile); |
|
259 tmpdir.append("mochikit.tmp"); |
|
260 // parseInt is used because octal escape sequences cause deprecation warnings |
|
261 // in strict mode (which is turned on in debug builds) |
|
262 tmpdir.createUnique(Components.interfaces.nsIFile.DIRECTORY_TYPE, parseInt("0777", 8)); |
|
263 |
|
264 var zReader = Components.classes["@mozilla.org/libjar/zip-reader;1"]. |
|
265 createInstance(Components.interfaces.nsIZipReader); |
|
266 |
|
267 var fileHandler = Components.classes["@mozilla.org/network/protocol;1?name=file"]. |
|
268 getService(Components.interfaces.nsIFileProtocolHandler); |
|
269 |
|
270 var fileName = fileHandler.getFileFromURLSpec(jar.JARFile.spec); |
|
271 zReader.open(fileName); |
|
272 |
|
273 //filepath represents the path in the jar file without the filename |
|
274 var filepath = ""; |
|
275 var parts = jar.JAREntry.split('/'); |
|
276 for (var i =0; i < parts.length - 1; i++) { |
|
277 if (parts[i] != '') { |
|
278 filepath += parts[i] + '/'; |
|
279 } |
|
280 } |
|
281 |
|
282 /* Create dir structure first, no guarantee about ordering of directories and |
|
283 * files returned from findEntries. |
|
284 */ |
|
285 var dirs = zReader.findEntries(filepath + '*/'); |
|
286 while (dirs.hasMore()) { |
|
287 var targetDir = buildRelativePath(dirs.getNext(), tmpdir, filepath); |
|
288 // parseInt is used because octal escape sequences cause deprecation warnings |
|
289 // in strict mode (which is turned on in debug builds) |
|
290 if (!targetDir.exists()) { |
|
291 targetDir.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, parseInt("0777", 8)); |
|
292 } |
|
293 } |
|
294 |
|
295 //now do the files |
|
296 var files = zReader.findEntries(filepath + "*"); |
|
297 while (files.hasMore()) { |
|
298 var fname = files.getNext(); |
|
299 if (fname.substr(-1) != '/') { |
|
300 var targetFile = buildRelativePath(fname, tmpdir, filepath); |
|
301 zReader.extract(fname, targetFile); |
|
302 } |
|
303 } |
|
304 return tmpdir; |
|
305 } |
|
306 |
|
307 /* |
|
308 * Take a relative path from the current mochitest file |
|
309 * and returns the absolute path for the given test data file. |
|
310 */ |
|
311 function getTestFilePath(path) { |
|
312 if (path[0] == "/") { |
|
313 throw new Error("getTestFilePath only accepts relative path"); |
|
314 } |
|
315 // Get the chrome/jar uri for the current mochitest file |
|
316 // gTestPath being defined by the test harness in browser-chrome tests |
|
317 // or window is being used for mochitest-browser |
|
318 var baseURI = typeof(gTestPath) == "string" ? gTestPath : window.location.href; |
|
319 var parentURI = getResolvedURI(getRootDirectory(baseURI)); |
|
320 var file; |
|
321 if (parentURI.JARFile) { |
|
322 // If it's a jar/zip, we have to extract it first |
|
323 file = extractJarToTmp(parentURI); |
|
324 } else { |
|
325 // Otherwise, we can directly cast it to a file URI |
|
326 var fileHandler = Components.classes["@mozilla.org/network/protocol;1?name=file"]. |
|
327 getService(Components.interfaces.nsIFileProtocolHandler); |
|
328 file = fileHandler.getFileFromURLSpec(parentURI.spec); |
|
329 } |
|
330 // Then walk by the given relative path |
|
331 path.split("/") |
|
332 .forEach(function (p) { |
|
333 if (p == "..") { |
|
334 file = file.parent; |
|
335 } else if (p != ".") { |
|
336 file.append(p); |
|
337 } |
|
338 }); |
|
339 return file.path; |
|
340 } |
|
341 |
|
342 /* |
|
343 * Simple utility function to take the directory structure in jarentryname and |
|
344 * translate that to a path of a nsILocalFile. |
|
345 */ |
|
346 function buildRelativePath(jarentryname, destdir, basepath) |
|
347 { |
|
348 var baseParts = basepath.split('/'); |
|
349 if (baseParts[baseParts.length-1] == '') { |
|
350 baseParts.pop(); |
|
351 } |
|
352 |
|
353 var parts = jarentryname.split('/'); |
|
354 |
|
355 var targetFile = Components.classes["@mozilla.org/file/local;1"] |
|
356 .createInstance(Components.interfaces.nsILocalFile); |
|
357 targetFile.initWithFile(destdir); |
|
358 |
|
359 for (var i = baseParts.length; i < parts.length; i++) { |
|
360 targetFile.append(parts[i]); |
|
361 } |
|
362 |
|
363 return targetFile; |
|
364 } |
|
365 |
|
366 function readConfig(filename) { |
|
367 filename = filename || "testConfig.js"; |
|
368 |
|
369 var fileLocator = Components.classes["@mozilla.org/file/directory_service;1"]. |
|
370 getService(Components.interfaces.nsIProperties); |
|
371 var configFile = fileLocator.get("ProfD", Components.interfaces.nsIFile); |
|
372 configFile.append(filename); |
|
373 |
|
374 if (!configFile.exists()) |
|
375 return {}; |
|
376 |
|
377 var fileInStream = Components.classes["@mozilla.org/network/file-input-stream;1"]. |
|
378 createInstance(Components.interfaces.nsIFileInputStream); |
|
379 fileInStream.init(configFile, -1, 0, 0); |
|
380 |
|
381 var str = NetUtil.readInputStreamToString(fileInStream, fileInStream.available()); |
|
382 fileInStream.close(); |
|
383 return JSON.parse(str); |
|
384 } |
|
385 |
|
386 function registerTests() { |
|
387 var testsURI = Components.classes["@mozilla.org/file/directory_service;1"]. |
|
388 getService(Components.interfaces.nsIProperties). |
|
389 get("ProfD", Components.interfaces.nsILocalFile); |
|
390 testsURI.append("tests.manifest"); |
|
391 var ioSvc = Components.classes["@mozilla.org/network/io-service;1"]. |
|
392 getService(Components.interfaces.nsIIOService); |
|
393 var manifestFile = ioSvc.newFileURI(testsURI). |
|
394 QueryInterface(Components.interfaces.nsIFileURL).file; |
|
395 |
|
396 Components.manager.QueryInterface(Components.interfaces.nsIComponentRegistrar). |
|
397 autoRegister(manifestFile); |
|
398 } |
|
399 |
|
400 function getTestList(params, callback) { |
|
401 registerTests(); |
|
402 |
|
403 var baseurl = 'chrome://mochitests/content'; |
|
404 if (window.parseQueryString) { |
|
405 params = parseQueryString(location.search.substring(1), true); |
|
406 } |
|
407 if (!params.baseurl) { |
|
408 params.baseurl = baseurl; |
|
409 } |
|
410 |
|
411 var config = readConfig(); |
|
412 for (var p in params) { |
|
413 if (params[p] == 1) { |
|
414 config[p] = true; |
|
415 } else if (params[p] == 0) { |
|
416 config[p] = false; |
|
417 } else { |
|
418 config[p] = params[p]; |
|
419 } |
|
420 } |
|
421 params = config; |
|
422 if (params.manifestFile) { |
|
423 getTestManifest("http://mochi.test:8888/" + params.manifestFile, params, callback); |
|
424 return; |
|
425 } |
|
426 |
|
427 var links = {}; |
|
428 // load server.js in so we can share template functions |
|
429 var scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"]. |
|
430 getService(Ci.mozIJSSubScriptLoader); |
|
431 var srvScope = {}; |
|
432 scriptLoader.loadSubScript('chrome://mochikit/content/server.js', |
|
433 srvScope); |
|
434 |
|
435 if (getResolvedURI(baseurl).JARFile) { |
|
436 links = getMochitestJarListing(baseurl, params.testPath, params.testRoot); |
|
437 } else { |
|
438 links = getFileListing(baseurl, params.testPath, params.testRoot, srvScope); |
|
439 } |
|
440 callback(links); |
|
441 } |