netwerk/test/unit/test_URIs.js

changeset 1
ca08bd8f51b2
equal deleted inserted replaced
-1:000000000000 0:4a38af9c1000
1 /* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 Components.utils.import("resource://gre/modules/NetUtil.jsm");
3
4 var gIoService = Components.classes["@mozilla.org/network/io-service;1"]
5 .getService(Components.interfaces.nsIIOService);
6
7
8 // Run by: cd objdir; make -C netwerk/test/ xpcshell-tests
9 // or: cd objdir; make SOLO_FILE="test_URIs.js" -C netwerk/test/ check-one
10
11 // See also test_URIs2.js.
12
13 // Relevant RFCs: 1738, 1808, 2396, 3986 (newer than the code)
14 // http://greenbytes.de/tech/webdav/rfc3986.html#rfc.section.5.4
15 // http://greenbytes.de/tech/tc/uris/
16
17 // TEST DATA
18 // ---------
19 var gTests = [
20 { spec: "about:blank",
21 scheme: "about",
22 prePath: "about:",
23 path: "blank",
24 ref: "",
25 nsIURL: false, nsINestedURI: true, immutable: true },
26 { spec: "about:foobar",
27 scheme: "about",
28 prePath: "about:",
29 path: "foobar",
30 ref: "",
31 nsIURL: false, nsINestedURI: false, immutable: true },
32 { spec: "chrome://foobar/somedir/somefile.xml",
33 scheme: "chrome",
34 prePath: "chrome://foobar",
35 path: "/somedir/somefile.xml",
36 ref: "",
37 nsIURL: true, nsINestedURI: false, immutable: true },
38 { spec: "data:text/html;charset=utf-8,<html></html>",
39 scheme: "data",
40 prePath: "data:",
41 path: "text/html;charset=utf-8,<html></html>",
42 ref: "",
43 nsIURL: false, nsINestedURI: false },
44 { spec: "data:text/html;charset=utf-8,<html>\r\n\t</html>",
45 scheme: "data",
46 prePath: "data:",
47 path: "text/html;charset=utf-8,<html></html>",
48 ref: "",
49 nsIURL: false, nsINestedURI: false },
50 { spec: "data:text/plain,hello world",
51 scheme: "data",
52 prePath: "data:",
53 path: "text/plain,hello%20world",
54 ref: "",
55 nsIURL: false, nsINestedURI: false },
56 { spec: "file:///dir/afile",
57 scheme: "data",
58 prePath: "data:",
59 path: "text/plain,2",
60 ref: "",
61 relativeURI: "data:te\nxt/plain,2",
62 nsIURL: false, nsINestedURI: false },
63 { spec: "file://",
64 scheme: "file",
65 prePath: "file://",
66 path: "/",
67 ref: "",
68 nsIURL: true, nsINestedURI: false },
69 { spec: "file:///",
70 scheme: "file",
71 prePath: "file://",
72 path: "/",
73 ref: "",
74 nsIURL: true, nsINestedURI: false },
75 { spec: "file:///myFile.html",
76 scheme: "file",
77 prePath: "file://",
78 path: "/myFile.html",
79 ref: "",
80 nsIURL: true, nsINestedURI: false },
81 { spec: "file:///dir/afile",
82 scheme: "file",
83 prePath: "file://",
84 path: "/dir/data/text/plain,2",
85 ref: "",
86 relativeURI: "data/text/plain,2",
87 nsIURL: true, nsINestedURI: false },
88 { spec: "file:///dir/dir2/",
89 scheme: "file",
90 prePath: "file://",
91 path: "/dir/dir2/data/text/plain,2",
92 ref: "",
93 relativeURI: "data/text/plain,2",
94 nsIURL: true, nsINestedURI: false },
95 { spec: "ftp://",
96 scheme: "ftp",
97 prePath: "ftp://",
98 path: "/",
99 ref: "",
100 nsIURL: true, nsINestedURI: false },
101 { spec: "ftp:///",
102 scheme: "ftp",
103 prePath: "ftp://",
104 path: "/",
105 ref: "",
106 nsIURL: true, nsINestedURI: false },
107 { spec: "ftp://ftp.mozilla.org/pub/mozilla.org/README",
108 scheme: "ftp",
109 prePath: "ftp://ftp.mozilla.org",
110 path: "/pub/mozilla.org/README",
111 ref: "",
112 nsIURL: true, nsINestedURI: false },
113 { spec: "ftp://foo:bar@ftp.mozilla.org:100/pub/mozilla.org/README",
114 scheme: "ftp",
115 prePath: "ftp://foo:bar@ftp.mozilla.org:100",
116 port: 100,
117 username: "foo",
118 password: "bar",
119 path: "/pub/mozilla.org/README",
120 ref: "",
121 nsIURL: true, nsINestedURI: false },
122 { spec: "ftp://foo:@ftp.mozilla.org:100/pub/mozilla.org/README",
123 scheme: "ftp",
124 prePath: "ftp://foo:@ftp.mozilla.org:100",
125 port: 100,
126 username: "foo",
127 password: "",
128 path: "/pub/mozilla.org/README",
129 ref: "",
130 nsIURL: true, nsINestedURI: false },
131 //Bug 706249
132 { spec: "http:x:@",
133 scheme: "http",
134 prePath: "http://x:@",
135 username: "x",
136 password: "",
137 path: "",
138 ref: "",
139 nsIURL: true, nsINestedURI: false },
140 { spec: "gopher://mozilla.org/",
141 scheme: "gopher",
142 prePath: "gopher:",
143 path: "//mozilla.org/",
144 ref: "",
145 nsIURL: false, nsINestedURI: false },
146 { spec: "http://",
147 scheme: "http",
148 prePath: "http://",
149 path: "/",
150 ref: "",
151 nsIURL: true, nsINestedURI: false },
152 { spec: "http:///",
153 scheme: "http",
154 prePath: "http://",
155 path: "/",
156 ref: "",
157 nsIURL: true, nsINestedURI: false },
158 { spec: "http://www.example.com/",
159 scheme: "http",
160 prePath: "http://www.example.com",
161 path: "/",
162 ref: "",
163 nsIURL: true, nsINestedURI: false },
164 { spec: "http://www.exa\nmple.com/",
165 scheme: "http",
166 prePath: "http://www.example.com",
167 path: "/",
168 ref: "",
169 nsIURL: true, nsINestedURI: false },
170 { spec: "http://10.32.4.239/",
171 scheme: "http",
172 prePath: "http://10.32.4.239",
173 host: "10.32.4.239",
174 path: "/",
175 ref: "",
176 nsIURL: true, nsINestedURI: false },
177 { spec: "http://[::192.9.5.5]/ipng",
178 scheme: "http",
179 prePath: "http://[::192.9.5.5]",
180 host: "::192.9.5.5",
181 path: "/ipng",
182 ref: "",
183 nsIURL: true, nsINestedURI: false },
184 { spec: "http://[FEDC:BA98:7654:3210:FEDC:BA98:7654:3210]:8888/index.html",
185 scheme: "http",
186 prePath: "http://[fedc:ba98:7654:3210:fedc:ba98:7654:3210]:8888",
187 host: "fedc:ba98:7654:3210:fedc:ba98:7654:3210",
188 port: 8888,
189 path: "/index.html",
190 ref: "",
191 nsIURL: true, nsINestedURI: false },
192 { spec: "http://bar:foo@www.mozilla.org:8080/pub/mozilla.org/README.html",
193 scheme: "http",
194 prePath: "http://bar:foo@www.mozilla.org:8080",
195 port: 8080,
196 username: "bar",
197 password: "foo",
198 host: "www.mozilla.org",
199 path: "/pub/mozilla.org/README.html",
200 ref: "",
201 nsIURL: true, nsINestedURI: false },
202 { spec: "jar:resource://!/",
203 scheme: "jar",
204 prePath: "jar:",
205 path: "resource:///!/",
206 ref: "",
207 nsIURL: true, nsINestedURI: true },
208 { spec: "jar:resource://gre/chrome.toolkit.jar!/",
209 scheme: "jar",
210 prePath: "jar:",
211 path: "resource://gre/chrome.toolkit.jar!/",
212 ref: "",
213 nsIURL: true, nsINestedURI: true },
214 { spec: "mailto:webmaster@mozilla.com",
215 scheme: "mailto",
216 prePath: "mailto:",
217 path: "webmaster@mozilla.com",
218 ref: "",
219 nsIURL: false, nsINestedURI: false },
220 { spec: "javascript:new Date()",
221 scheme: "javascript",
222 prePath: "javascript:",
223 path: "new%20Date()",
224 ref: "",
225 nsIURL: false, nsINestedURI: false },
226 { spec: "blob:123456",
227 scheme: "blob",
228 prePath: "blob:",
229 path: "123456",
230 ref: "",
231 nsIURL: false, nsINestedURI: false, immutable: true },
232 { spec: "place:sort=8&maxResults=10",
233 scheme: "place",
234 prePath: "place:",
235 path: "sort=8&maxResults=10",
236 ref: "",
237 nsIURL: false, nsINestedURI: false },
238 { spec: "resource://gre/",
239 scheme: "resource",
240 prePath: "resource://gre",
241 path: "/",
242 ref: "",
243 nsIURL: true, nsINestedURI: false },
244 { spec: "resource://gre/components/",
245 scheme: "resource",
246 prePath: "resource://gre",
247 path: "/components/",
248 ref: "",
249 nsIURL: true, nsINestedURI: false },
250 { spec: "view-source:about:blank",
251 scheme: "view-source",
252 prePath: "view-source:",
253 path: "about:blank",
254 ref: "",
255 nsIURL: false, nsINestedURI: true, immutable: true },
256 { spec: "view-source:http://www.mozilla.org/",
257 scheme: "view-source",
258 prePath: "view-source:",
259 path: "http://www.mozilla.org/",
260 ref: "",
261 nsIURL: false, nsINestedURI: true, immutable: true },
262 { spec: "x-external:",
263 scheme: "x-external",
264 prePath: "x-external:",
265 path: "",
266 ref: "",
267 nsIURL: false, nsINestedURI: false },
268 { spec: "x-external:abc",
269 scheme: "x-external",
270 prePath: "x-external:",
271 path: "abc",
272 ref: "",
273 nsIURL: false, nsINestedURI: false },
274 { spec: "http://www2.example.com/",
275 relativeURI: "a/b/c/d",
276 scheme: "http",
277 prePath: "http://www2.example.com",
278 path: "/a/b/c/d",
279 ref: "",
280 nsIURL: true, nsINestedURI: false },
281
282 // Adding more? Consider adding to test_URIs2.js instead, so that neither
283 // test runs for *too* long, risking timeouts on slow platforms.
284 ];
285
286 var gHashSuffixes = [
287 "#",
288 "#myRef",
289 "#myRef?a=b",
290 "#myRef#",
291 "#myRef#x:yz"
292 ];
293
294 // TEST HELPER FUNCTIONS
295 // ---------------------
296 function do_info(text, stack) {
297 if (!stack)
298 stack = Components.stack.caller;
299
300 dump( "\n" +
301 "TEST-INFO | " + stack.filename + " | [" + stack.name + " : " +
302 stack.lineNumber + "] " + text + "\n");
303 }
304
305 // Checks that the URIs satisfy equals(), in both possible orderings.
306 // Also checks URI.equalsExceptRef(), because equal URIs should also be equal
307 // when we ignore the ref.
308 //
309 // The third argument is optional. If the client passes a third argument
310 // (e.g. todo_check_true), we'll use that in lieu of do_check_true.
311 function do_check_uri_eq(aURI1, aURI2, aCheckTrueFunc) {
312 if (!aCheckTrueFunc) {
313 aCheckTrueFunc = do_check_true;
314 }
315
316 do_info("(uri equals check: '" + aURI1.spec + "' == '" + aURI2.spec + "')");
317 aCheckTrueFunc(aURI1.equals(aURI2));
318 do_info("(uri equals check: '" + aURI2.spec + "' == '" + aURI1.spec + "')");
319 aCheckTrueFunc(aURI2.equals(aURI1));
320
321 // (Only take the extra step of testing 'equalsExceptRef' when we expect the
322 // URIs to really be equal. In 'todo' cases, the URIs may or may not be
323 // equal when refs are ignored - there's no way of knowing in general.)
324 if (aCheckTrueFunc == do_check_true) {
325 do_check_uri_eqExceptRef(aURI1, aURI2, aCheckTrueFunc);
326 }
327 }
328
329 // Checks that the URIs satisfy equalsExceptRef(), in both possible orderings.
330 //
331 // The third argument is optional. If the client passes a third argument
332 // (e.g. todo_check_true), we'll use that in lieu of do_check_true.
333 function do_check_uri_eqExceptRef(aURI1, aURI2, aCheckTrueFunc) {
334 if (!aCheckTrueFunc) {
335 aCheckTrueFunc = do_check_true;
336 }
337
338 do_info("(uri equalsExceptRef check: '" +
339 aURI1.spec + "' == '" + aURI2.spec + "')");
340 aCheckTrueFunc(aURI1.equalsExceptRef(aURI2));
341 do_info("(uri equalsExceptRef check: '" +
342 aURI2.spec + "' == '" + aURI1.spec + "')");
343 aCheckTrueFunc(aURI2.equalsExceptRef(aURI1));
344 }
345
346 // Checks that the given property on aURI matches the corresponding property
347 // in the test bundle (or matches some function of that corresponding property,
348 // if aTestFunctor is passed in).
349 function do_check_property(aTest, aURI, aPropertyName, aTestFunctor) {
350 if (aTest[aPropertyName]) {
351 var expectedVal = aTestFunctor ?
352 aTestFunctor(aTest[aPropertyName]) :
353 aTest[aPropertyName];
354
355 do_info("testing " + aPropertyName + " of " +
356 (aTestFunctor ? "modified '" : "'" ) + aTest.spec +
357 "' is '" + expectedVal + "'");
358 do_check_eq(aURI[aPropertyName], expectedVal);
359 }
360 }
361
362 // Test that a given URI parses correctly into its various components.
363 function do_test_uri_basic(aTest) {
364 var URI;
365
366 do_info("Basic tests for " + aTest.spec + " relative URI: " + aTest.relativeURI);
367
368 try {
369 URI = NetUtil.newURI(aTest.spec);
370 } catch(e) {
371 do_info("Caught error on parse of" + aTest.spec + " Error: " + e.result);
372 if (aTest.fail) {
373 do_check_eq(e.result, aTest.result);
374 return;
375 }
376 do_throw(e.result);
377 }
378
379 if (aTest.relativeURI) {
380 var relURI;
381
382 try {
383 relURI = gIoService.newURI(aTest.relativeURI, null, URI);
384 } catch (e) {
385 do_info("Caught error on Relative parse of " + aTest.spec + " + " + aTest.relativeURI +" Error: " + e.result);
386 if (aTest.relativeFail) {
387 do_check_eq(e.result, aTest.relativeFail);
388 return;
389 }
390 do_throw(e.result);
391 }
392 do_info("relURI.path = " + relURI.path + ", was " + URI.path);
393 URI = relURI;
394 do_info("URI.path now = " + URI.path);
395 }
396
397 // Sanity-check
398 do_info("testing " + aTest.spec + " equals a clone of itself");
399 do_check_uri_eq(URI, URI.clone());
400 do_check_uri_eqExceptRef(URI, URI.cloneIgnoringRef());
401 do_info("testing " + aTest.spec + " instanceof nsIURL");
402 do_check_eq(URI instanceof Ci.nsIURL, aTest.nsIURL);
403 do_info("testing " + aTest.spec + " instanceof nsINestedURI");
404 do_check_eq(URI instanceof Ci.nsINestedURI,
405 aTest.nsINestedURI);
406
407 do_info("testing that " + aTest.spec + " throws or returns false " +
408 "from equals(null)");
409 // XXXdholbert At some point it'd probably be worth making this behavior
410 // (throwing vs. returning false) consistent across URI implementations.
411 var threw = false;
412 var isEqualToNull;
413 try {
414 isEqualToNull = URI.equals(null);
415 } catch(e) {
416 threw = true;
417 }
418 do_check_true(threw || !isEqualToNull);
419
420
421 // Check the various components
422 do_check_property(aTest, URI, "scheme");
423 do_check_property(aTest, URI, "prePath");
424 do_check_property(aTest, URI, "path");
425 do_check_property(aTest, URI, "ref");
426 do_check_property(aTest, URI, "port");
427 do_check_property(aTest, URI, "username");
428 do_check_property(aTest, URI, "password");
429 do_check_property(aTest, URI, "host");
430 do_check_property(aTest, URI, "specIgnoringRef");
431 if ("hasRef" in aTest) {
432 do_info("testing hasref: " + aTest.hasRef + " vs " + URI.hasRef);
433 do_check_eq(aTest.hasRef, URI.hasRef);
434 }
435 }
436
437 // Test that a given URI parses correctly when we add a given ref to the end
438 function do_test_uri_with_hash_suffix(aTest, aSuffix) {
439 do_info("making sure caller is using suffix that starts with '#'");
440 do_check_eq(aSuffix[0], "#");
441
442 var origURI = NetUtil.newURI(aTest.spec);
443 var testURI;
444
445 if (aTest.relativeURI) {
446 try {
447 origURI = gIoService.newURI(aTest.relativeURI, null, origURI);
448 } catch (e) {
449 do_info("Caught error on Relative parse of " + aTest.spec + " + " + aTest.relativeURI +" Error: " + e.result);
450 return;
451 }
452 try {
453 testURI = gIoService.newURI(aSuffix, null, origURI);
454 } catch (e) {
455 do_info("Caught error adding suffix to " + aTest.spec + " + " + aTest.relativeURI + ", suffix " + aSuffix + " Error: " + e.result);
456 return;
457 }
458 } else {
459 testURI = NetUtil.newURI(aTest.spec + aSuffix);
460 }
461
462 do_info("testing " + aTest.spec + " with '" + aSuffix + "' appended " +
463 "equals a clone of itself");
464 do_check_uri_eq(testURI, testURI.clone());
465
466 do_info("testing " + aTest.spec +
467 " doesn't equal self with '" + aSuffix + "' appended");
468
469 do_check_false(origURI.equals(testURI));
470
471 do_info("testing " + aTest.spec +
472 " is equalExceptRef to self with '" + aSuffix + "' appended");
473 do_check_uri_eqExceptRef(origURI, testURI);
474
475 do_check_eq(testURI.hasRef, true);
476
477 if (!origURI.ref) {
478 // These tests fail if origURI has a ref
479 do_info("testing cloneIgnoringRef on " + testURI.spec +
480 " is equal to no-ref version but not equal to ref version");
481 var cloneNoRef = testURI.cloneIgnoringRef();
482 do_check_uri_eq(cloneNoRef, origURI);
483 do_check_false(cloneNoRef.equals(testURI));
484 }
485
486 do_check_property(aTest, testURI, "scheme");
487 do_check_property(aTest, testURI, "prePath");
488 if (!origURI.ref) {
489 // These don't work if it's a ref already because '+' doesn't give the right result
490 do_check_property(aTest, testURI, "path",
491 function(aStr) { return aStr + aSuffix; });
492 do_check_property(aTest, testURI, "ref",
493 function(aStr) { return aSuffix.substr(1); });
494 }
495 }
496
497 // Tests various ways of setting & clearing a ref on a URI.
498 function do_test_mutate_ref(aTest, aSuffix) {
499 do_info("making sure caller is using suffix that starts with '#'");
500 do_check_eq(aSuffix[0], "#");
501
502 var refURIWithSuffix = NetUtil.newURI(aTest.spec + aSuffix);
503 var refURIWithoutSuffix = NetUtil.newURI(aTest.spec);
504
505 var testURI = NetUtil.newURI(aTest.spec);
506
507 // First: Try setting .ref to our suffix
508 do_info("testing that setting .ref on " + aTest.spec +
509 " to '" + aSuffix + "' does what we expect");
510 testURI.ref = aSuffix;
511 do_check_uri_eq(testURI, refURIWithSuffix);
512 do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
513
514 // Now try setting .ref but leave off the initial hash (expect same result)
515 var suffixLackingHash = aSuffix.substr(1);
516 if (suffixLackingHash) { // (skip this our suffix was *just* a #)
517 do_info("testing that setting .ref on " + aTest.spec +
518 " to '" + suffixLackingHash + "' does what we expect");
519 testURI.ref = suffixLackingHash;
520 do_check_uri_eq(testURI, refURIWithSuffix);
521 do_check_uri_eqExceptRef(testURI, refURIWithoutSuffix);
522 }
523
524 // Now, clear .ref (should get us back the original spec)
525 do_info("testing that clearing .ref on " + testURI.spec +
526 " does what we expect");
527 testURI.ref = "";
528 do_check_uri_eq(testURI, refURIWithoutSuffix);
529 do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
530
531 if (!aTest.relativeURI) {
532 // TODO: These tests don't work as-is for relative URIs.
533
534 // Now try setting .spec directly (including suffix) and then clearing .ref
535 var specWithSuffix = aTest.spec + aSuffix;
536 do_info("testing that setting spec to " +
537 specWithSuffix + " and then clearing ref does what we expect");
538 testURI.spec = specWithSuffix;
539 testURI.ref = "";
540 do_check_uri_eq(testURI, refURIWithoutSuffix);
541 do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
542
543 // XXX nsIJARURI throws an exception in SetPath(), so skip it for next part.
544 if (!(testURI instanceof Ci.nsIJARURI)) {
545 // Now try setting .path directly (including suffix) and then clearing .ref
546 // (same as above, but with now with .path instead of .spec)
547 testURI = NetUtil.newURI(aTest.spec);
548
549 var pathWithSuffix = aTest.path + aSuffix;
550 do_info("testing that setting path to " +
551 pathWithSuffix + " and then clearing ref does what we expect");
552 testURI.path = pathWithSuffix;
553 testURI.ref = "";
554 do_check_uri_eq(testURI, refURIWithoutSuffix);
555 do_check_uri_eqExceptRef(testURI, refURIWithSuffix);
556
557 // Also: make sure that clearing .path also clears .ref
558 testURI.path = pathWithSuffix;
559 do_info("testing that clearing path from " +
560 pathWithSuffix + " also clears .ref");
561 testURI.path = "";
562 do_check_eq(testURI.ref, "");
563 }
564 }
565 }
566
567 // Tests that normally-mutable properties can't be modified on
568 // special URIs that are known to be immutable.
569 function do_test_immutable(aTest) {
570 do_check_true(aTest.immutable);
571
572 var URI = NetUtil.newURI(aTest.spec);
573 // All the non-readonly attributes on nsIURI.idl:
574 var propertiesToCheck = ["spec", "scheme", "userPass", "username", "password",
575 "hostPort", "host", "port", "path", "ref"];
576
577 propertiesToCheck.forEach(function(aProperty) {
578 var threw = false;
579 try {
580 URI[aProperty] = "anothervalue";
581 } catch(e) {
582 threw = true;
583 }
584
585 do_info("testing that setting '" + aProperty +
586 "' on immutable URI '" + aTest.spec + "' will throw");
587 do_check_true(threw);
588 });
589 }
590
591
592 // TEST MAIN FUNCTION
593 // ------------------
594 function run_test()
595 {
596 // UTF-8 check - From bug 622981
597 // ASCII
598 let base = gIoService.newURI("http://example.org/xenia?", null, null);
599 let resolved = gIoService.newURI("?x", null, base);
600 let expected = gIoService.newURI("http://example.org/xenia?x",
601 null, null);
602 do_info("Bug 662981: ACSII - comparing " + resolved.spec + " and " + expected.spec);
603 do_check_true(resolved.equals(expected));
604
605 // UTF-8 character "è"
606 // Bug 622981 was triggered by an empty query string
607 base = gIoService.newURI("http://example.org/xènia?", null, null);
608 resolved = gIoService.newURI("?x", null, base);
609 expected = gIoService.newURI("http://example.org/xènia?x",
610 null, null);
611 do_info("Bug 662981: UTF8 - comparing " + resolved.spec + " and " + expected.spec);
612 do_check_true(resolved.equals(expected));
613
614 gTests.forEach(function(aTest) {
615 // Check basic URI functionality
616 do_test_uri_basic(aTest);
617
618 if (!aTest.fail) {
619 // Try adding various #-prefixed strings to the ends of the URIs
620 gHashSuffixes.forEach(function(aSuffix) {
621 do_test_uri_with_hash_suffix(aTest, aSuffix);
622 if (!aTest.immutable) {
623 do_test_mutate_ref(aTest, aSuffix);
624 }
625 });
626
627 // For URIs that we couldn't mutate above due to them being immutable:
628 // Now we check that they're actually immutable.
629 if (aTest.immutable) {
630 do_test_immutable(aTest);
631 }
632 }
633 });
634 }

mercurial