services/sync/tests/unit/test_resource.js

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

michael@0 1 /* Any copyright is dedicated to the Public Domain.
michael@0 2 * http://creativecommons.org/publicdomain/zero/1.0/ */
michael@0 3
michael@0 4 Cu.import("resource://gre/modules/Log.jsm");
michael@0 5 Cu.import("resource://services-common/observers.js");
michael@0 6 Cu.import("resource://services-sync/identity.js");
michael@0 7 Cu.import("resource://services-sync/resource.js");
michael@0 8 Cu.import("resource://services-sync/util.js");
michael@0 9
michael@0 10 let logger;
michael@0 11
michael@0 12 let fetched = false;
michael@0 13 function server_open(metadata, response) {
michael@0 14 let body;
michael@0 15 if (metadata.method == "GET") {
michael@0 16 fetched = true;
michael@0 17 body = "This path exists";
michael@0 18 response.setStatusLine(metadata.httpVersion, 200, "OK");
michael@0 19 } else {
michael@0 20 body = "Wrong request method";
michael@0 21 response.setStatusLine(metadata.httpVersion, 405, "Method Not Allowed");
michael@0 22 }
michael@0 23 response.bodyOutputStream.write(body, body.length);
michael@0 24 }
michael@0 25
michael@0 26 function server_protected(metadata, response) {
michael@0 27 let body;
michael@0 28
michael@0 29 if (basic_auth_matches(metadata, "guest", "guest")) {
michael@0 30 body = "This path exists and is protected";
michael@0 31 response.setStatusLine(metadata.httpVersion, 200, "OK, authorized");
michael@0 32 response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
michael@0 33 } else {
michael@0 34 body = "This path exists and is protected - failed";
michael@0 35 response.setStatusLine(metadata.httpVersion, 401, "Unauthorized");
michael@0 36 response.setHeader("WWW-Authenticate", 'Basic realm="secret"', false);
michael@0 37 }
michael@0 38
michael@0 39 response.bodyOutputStream.write(body, body.length);
michael@0 40 }
michael@0 41
michael@0 42 function server_404(metadata, response) {
michael@0 43 let body = "File not found";
michael@0 44 response.setStatusLine(metadata.httpVersion, 404, "Not Found");
michael@0 45 response.bodyOutputStream.write(body, body.length);
michael@0 46 }
michael@0 47
michael@0 48 let pacFetched = false;
michael@0 49 function server_pac(metadata, response) {
michael@0 50 pacFetched = true;
michael@0 51 let body = 'function FindProxyForURL(url, host) { return "DIRECT"; }';
michael@0 52 response.setStatusLine(metadata.httpVersion, 200, "OK");
michael@0 53 response.setHeader("Content-Type", "application/x-ns-proxy-autoconfig", false);
michael@0 54 response.bodyOutputStream.write(body, body.length);
michael@0 55 }
michael@0 56
michael@0 57
michael@0 58 let sample_data = {
michael@0 59 some: "sample_data",
michael@0 60 injson: "format",
michael@0 61 number: 42
michael@0 62 };
michael@0 63
michael@0 64 function server_upload(metadata, response) {
michael@0 65 let body;
michael@0 66
michael@0 67 let input = readBytesFromInputStream(metadata.bodyInputStream);
michael@0 68 if (input == JSON.stringify(sample_data)) {
michael@0 69 body = "Valid data upload via " + metadata.method;
michael@0 70 response.setStatusLine(metadata.httpVersion, 200, "OK");
michael@0 71 } else {
michael@0 72 body = "Invalid data upload via " + metadata.method + ': ' + input;
michael@0 73 response.setStatusLine(metadata.httpVersion, 500, "Internal Server Error");
michael@0 74 }
michael@0 75
michael@0 76 response.bodyOutputStream.write(body, body.length);
michael@0 77 }
michael@0 78
michael@0 79 function server_delete(metadata, response) {
michael@0 80 let body;
michael@0 81 if (metadata.method == "DELETE") {
michael@0 82 body = "This resource has been deleted";
michael@0 83 response.setStatusLine(metadata.httpVersion, 200, "OK");
michael@0 84 } else {
michael@0 85 body = "Wrong request method";
michael@0 86 response.setStatusLine(metadata.httpVersion, 405, "Method Not Allowed");
michael@0 87 }
michael@0 88 response.bodyOutputStream.write(body, body.length);
michael@0 89 }
michael@0 90
michael@0 91 function server_json(metadata, response) {
michael@0 92 let body = JSON.stringify(sample_data);
michael@0 93 response.setStatusLine(metadata.httpVersion, 200, "OK");
michael@0 94 response.bodyOutputStream.write(body, body.length);
michael@0 95 }
michael@0 96
michael@0 97 const TIMESTAMP = 1274380461;
michael@0 98
michael@0 99 function server_timestamp(metadata, response) {
michael@0 100 let body = "Thank you for your request";
michael@0 101 response.setHeader("X-Weave-Timestamp", ''+TIMESTAMP, false);
michael@0 102 response.setStatusLine(metadata.httpVersion, 200, "OK");
michael@0 103 response.bodyOutputStream.write(body, body.length);
michael@0 104 }
michael@0 105
michael@0 106 function server_backoff(metadata, response) {
michael@0 107 let body = "Hey, back off!";
michael@0 108 response.setHeader("X-Weave-Backoff", '600', false);
michael@0 109 response.setStatusLine(metadata.httpVersion, 200, "OK");
michael@0 110 response.bodyOutputStream.write(body, body.length);
michael@0 111 }
michael@0 112
michael@0 113 function server_quota_notice(request, response) {
michael@0 114 let body = "You're approaching quota.";
michael@0 115 response.setHeader("X-Weave-Quota-Remaining", '1048576', false);
michael@0 116 response.setStatusLine(request.httpVersion, 200, "OK");
michael@0 117 response.bodyOutputStream.write(body, body.length);
michael@0 118 }
michael@0 119
michael@0 120 function server_quota_error(request, response) {
michael@0 121 let body = "14";
michael@0 122 response.setHeader("X-Weave-Quota-Remaining", '-1024', false);
michael@0 123 response.setStatusLine(request.httpVersion, 400, "OK");
michael@0 124 response.bodyOutputStream.write(body, body.length);
michael@0 125 }
michael@0 126
michael@0 127 function server_headers(metadata, response) {
michael@0 128 let ignore_headers = ["host", "user-agent", "accept", "accept-language",
michael@0 129 "accept-encoding", "accept-charset", "keep-alive",
michael@0 130 "connection", "pragma", "cache-control",
michael@0 131 "content-length"];
michael@0 132 let headers = metadata.headers;
michael@0 133 let header_names = [];
michael@0 134 while (headers.hasMoreElements()) {
michael@0 135 let header = headers.getNext().toString();
michael@0 136 if (ignore_headers.indexOf(header) == -1) {
michael@0 137 header_names.push(header);
michael@0 138 }
michael@0 139 }
michael@0 140 header_names = header_names.sort();
michael@0 141
michael@0 142 headers = {};
michael@0 143 for each (let header in header_names) {
michael@0 144 headers[header] = metadata.getHeader(header);
michael@0 145 }
michael@0 146 let body = JSON.stringify(headers);
michael@0 147 response.setStatusLine(metadata.httpVersion, 200, "OK");
michael@0 148 response.bodyOutputStream.write(body, body.length);
michael@0 149 }
michael@0 150
michael@0 151 function run_test() {
michael@0 152 initTestLogging("Trace");
michael@0 153
michael@0 154 do_test_pending();
michael@0 155
michael@0 156 logger = Log.repository.getLogger('Test');
michael@0 157 Log.repository.rootLogger.addAppender(new Log.DumpAppender());
michael@0 158
michael@0 159 let server = httpd_setup({
michael@0 160 "/open": server_open,
michael@0 161 "/protected": server_protected,
michael@0 162 "/404": server_404,
michael@0 163 "/upload": server_upload,
michael@0 164 "/delete": server_delete,
michael@0 165 "/json": server_json,
michael@0 166 "/timestamp": server_timestamp,
michael@0 167 "/headers": server_headers,
michael@0 168 "/backoff": server_backoff,
michael@0 169 "/pac1": server_pac,
michael@0 170 "/quota-notice": server_quota_notice,
michael@0 171 "/quota-error": server_quota_error
michael@0 172 });
michael@0 173
michael@0 174 Svc.Prefs.set("network.numRetries", 1); // speed up test
michael@0 175
michael@0 176 // This apparently has to come first in order for our PAC URL to be hit.
michael@0 177 // Don't put any other HTTP requests earlier in the file!
michael@0 178 _("Testing handling of proxy auth redirection.");
michael@0 179 PACSystemSettings.PACURI = server.baseURI + "/pac1";
michael@0 180 installFakePAC();
michael@0 181 let proxiedRes = new Resource(server.baseURI + "/open");
michael@0 182 let content = proxiedRes.get();
michael@0 183 do_check_true(pacFetched);
michael@0 184 do_check_true(fetched);
michael@0 185 do_check_eq(content, "This path exists");
michael@0 186 pacFetched = fetched = false;
michael@0 187 uninstallFakePAC();
michael@0 188
michael@0 189 _("Resource object members");
michael@0 190 let res = new Resource(server.baseURI + "/open");
michael@0 191 do_check_true(res.uri instanceof Ci.nsIURI);
michael@0 192 do_check_eq(res.uri.spec, server.baseURI + "/open");
michael@0 193 do_check_eq(res.spec, server.baseURI + "/open");
michael@0 194 do_check_eq(typeof res.headers, "object");
michael@0 195 do_check_eq(typeof res.authenticator, "object");
michael@0 196 // Initially res.data is null since we haven't performed a GET or
michael@0 197 // PUT/POST request yet.
michael@0 198 do_check_eq(res.data, null);
michael@0 199
michael@0 200 _("GET a non-password-protected resource");
michael@0 201 content = res.get();
michael@0 202 do_check_eq(content, "This path exists");
michael@0 203 do_check_eq(content.status, 200);
michael@0 204 do_check_true(content.success);
michael@0 205 // res.data has been updated with the result from the request
michael@0 206 do_check_eq(res.data, content);
michael@0 207
michael@0 208 // Observe logging messages.
michael@0 209 let logger = res._log;
michael@0 210 let dbg = logger.debug;
michael@0 211 let debugMessages = [];
michael@0 212 logger.debug = function (msg) {
michael@0 213 debugMessages.push(msg);
michael@0 214 dbg.call(this, msg);
michael@0 215 }
michael@0 216
michael@0 217 // Since we didn't receive proper JSON data, accessing content.obj
michael@0 218 // will result in a SyntaxError from JSON.parse.
michael@0 219 // Furthermore, we'll have logged.
michael@0 220 let didThrow = false;
michael@0 221 try {
michael@0 222 content.obj;
michael@0 223 } catch (ex) {
michael@0 224 didThrow = true;
michael@0 225 }
michael@0 226 do_check_true(didThrow);
michael@0 227 do_check_eq(debugMessages.length, 1);
michael@0 228 do_check_eq(debugMessages[0],
michael@0 229 "Parse fail: Response body starts: \"\"This path exists\"\".");
michael@0 230 logger.debug = dbg;
michael@0 231
michael@0 232 _("Test that the BasicAuthenticator doesn't screw up header case.");
michael@0 233 let res1 = new Resource(server.baseURI + "/foo");
michael@0 234 res1.setHeader("Authorization", "Basic foobar");
michael@0 235 do_check_eq(res1.headers["authorization"], "Basic foobar");
michael@0 236
michael@0 237 _("GET a password protected resource (test that it'll fail w/o pass, no throw)");
michael@0 238 let res2 = new Resource(server.baseURI + "/protected");
michael@0 239 content = res2.get();
michael@0 240 do_check_eq(content, "This path exists and is protected - failed");
michael@0 241 do_check_eq(content.status, 401);
michael@0 242 do_check_false(content.success);
michael@0 243
michael@0 244 _("GET a password protected resource");
michael@0 245 let res3 = new Resource(server.baseURI + "/protected");
michael@0 246 let identity = new IdentityManager();
michael@0 247 let auth = identity.getBasicResourceAuthenticator("guest", "guest");
michael@0 248 res3.authenticator = auth;
michael@0 249 do_check_eq(res3.authenticator, auth);
michael@0 250 content = res3.get();
michael@0 251 do_check_eq(content, "This path exists and is protected");
michael@0 252 do_check_eq(content.status, 200);
michael@0 253 do_check_true(content.success);
michael@0 254
michael@0 255 _("GET a non-existent resource (test that it'll fail, but not throw)");
michael@0 256 let res4 = new Resource(server.baseURI + "/404");
michael@0 257 content = res4.get();
michael@0 258 do_check_eq(content, "File not found");
michael@0 259 do_check_eq(content.status, 404);
michael@0 260 do_check_false(content.success);
michael@0 261
michael@0 262 // Check some headers of the 404 response
michael@0 263 do_check_eq(content.headers.connection, "close");
michael@0 264 do_check_eq(content.headers.server, "httpd.js");
michael@0 265 do_check_eq(content.headers["content-length"], 14);
michael@0 266
michael@0 267 _("PUT to a resource (string)");
michael@0 268 let res5 = new Resource(server.baseURI + "/upload");
michael@0 269 content = res5.put(JSON.stringify(sample_data));
michael@0 270 do_check_eq(content, "Valid data upload via PUT");
michael@0 271 do_check_eq(content.status, 200);
michael@0 272 do_check_eq(res5.data, content);
michael@0 273
michael@0 274 _("PUT to a resource (object)");
michael@0 275 content = res5.put(sample_data);
michael@0 276 do_check_eq(content, "Valid data upload via PUT");
michael@0 277 do_check_eq(content.status, 200);
michael@0 278 do_check_eq(res5.data, content);
michael@0 279
michael@0 280 _("PUT without data arg (uses resource.data) (string)");
michael@0 281 res5.data = JSON.stringify(sample_data);
michael@0 282 content = res5.put();
michael@0 283 do_check_eq(content, "Valid data upload via PUT");
michael@0 284 do_check_eq(content.status, 200);
michael@0 285 do_check_eq(res5.data, content);
michael@0 286
michael@0 287 _("PUT without data arg (uses resource.data) (object)");
michael@0 288 res5.data = sample_data;
michael@0 289 content = res5.put();
michael@0 290 do_check_eq(content, "Valid data upload via PUT");
michael@0 291 do_check_eq(content.status, 200);
michael@0 292 do_check_eq(res5.data, content);
michael@0 293
michael@0 294 _("POST to a resource (string)");
michael@0 295 content = res5.post(JSON.stringify(sample_data));
michael@0 296 do_check_eq(content, "Valid data upload via POST");
michael@0 297 do_check_eq(content.status, 200);
michael@0 298 do_check_eq(res5.data, content);
michael@0 299
michael@0 300 _("POST to a resource (object)");
michael@0 301 content = res5.post(sample_data);
michael@0 302 do_check_eq(content, "Valid data upload via POST");
michael@0 303 do_check_eq(content.status, 200);
michael@0 304 do_check_eq(res5.data, content);
michael@0 305
michael@0 306 _("POST without data arg (uses resource.data) (string)");
michael@0 307 res5.data = JSON.stringify(sample_data);
michael@0 308 content = res5.post();
michael@0 309 do_check_eq(content, "Valid data upload via POST");
michael@0 310 do_check_eq(content.status, 200);
michael@0 311 do_check_eq(res5.data, content);
michael@0 312
michael@0 313 _("POST without data arg (uses resource.data) (object)");
michael@0 314 res5.data = sample_data;
michael@0 315 content = res5.post();
michael@0 316 do_check_eq(content, "Valid data upload via POST");
michael@0 317 do_check_eq(content.status, 200);
michael@0 318 do_check_eq(res5.data, content);
michael@0 319
michael@0 320 _("DELETE a resource");
michael@0 321 let res6 = new Resource(server.baseURI + "/delete");
michael@0 322 content = res6.delete();
michael@0 323 do_check_eq(content, "This resource has been deleted")
michael@0 324 do_check_eq(content.status, 200);
michael@0 325
michael@0 326 _("JSON conversion of response body");
michael@0 327 let res7 = new Resource(server.baseURI + "/json");
michael@0 328 content = res7.get();
michael@0 329 do_check_eq(content, JSON.stringify(sample_data));
michael@0 330 do_check_eq(content.status, 200);
michael@0 331 do_check_eq(JSON.stringify(content.obj), JSON.stringify(sample_data));
michael@0 332
michael@0 333 _("X-Weave-Timestamp header updates AsyncResource.serverTime");
michael@0 334 // Before having received any response containing the
michael@0 335 // X-Weave-Timestamp header, AsyncResource.serverTime is null.
michael@0 336 do_check_eq(AsyncResource.serverTime, null);
michael@0 337 let res8 = new Resource(server.baseURI + "/timestamp");
michael@0 338 content = res8.get();
michael@0 339 do_check_eq(AsyncResource.serverTime, TIMESTAMP);
michael@0 340
michael@0 341 _("GET: no special request headers");
michael@0 342 let res9 = new Resource(server.baseURI + "/headers");
michael@0 343 content = res9.get();
michael@0 344 do_check_eq(content, '{}');
michael@0 345
michael@0 346 _("PUT: Content-Type defaults to text/plain");
michael@0 347 content = res9.put('data');
michael@0 348 do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
michael@0 349
michael@0 350 _("POST: Content-Type defaults to text/plain");
michael@0 351 content = res9.post('data');
michael@0 352 do_check_eq(content, JSON.stringify({"content-type": "text/plain"}));
michael@0 353
michael@0 354 _("setHeader(): setting simple header");
michael@0 355 res9.setHeader('X-What-Is-Weave', 'awesome');
michael@0 356 do_check_eq(res9.headers['x-what-is-weave'], 'awesome');
michael@0 357 content = res9.get();
michael@0 358 do_check_eq(content, JSON.stringify({"x-what-is-weave": "awesome"}));
michael@0 359
michael@0 360 _("setHeader(): setting multiple headers, overwriting existing header");
michael@0 361 res9.setHeader('X-WHAT-is-Weave', 'more awesomer');
michael@0 362 res9.setHeader('X-Another-Header', 'hello world');
michael@0 363 do_check_eq(res9.headers['x-what-is-weave'], 'more awesomer');
michael@0 364 do_check_eq(res9.headers['x-another-header'], 'hello world');
michael@0 365 content = res9.get();
michael@0 366 do_check_eq(content, JSON.stringify({"x-another-header": "hello world",
michael@0 367 "x-what-is-weave": "more awesomer"}));
michael@0 368
michael@0 369 _("Setting headers object");
michael@0 370 res9.headers = {};
michael@0 371 content = res9.get();
michael@0 372 do_check_eq(content, "{}");
michael@0 373
michael@0 374 _("PUT/POST: override default Content-Type");
michael@0 375 res9.setHeader('Content-Type', 'application/foobar');
michael@0 376 do_check_eq(res9.headers['content-type'], 'application/foobar');
michael@0 377 content = res9.put('data');
michael@0 378 do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
michael@0 379 content = res9.post('data');
michael@0 380 do_check_eq(content, JSON.stringify({"content-type": "application/foobar"}));
michael@0 381
michael@0 382
michael@0 383 _("X-Weave-Backoff header notifies observer");
michael@0 384 let backoffInterval;
michael@0 385 function onBackoff(subject, data) {
michael@0 386 backoffInterval = subject;
michael@0 387 }
michael@0 388 Observers.add("weave:service:backoff:interval", onBackoff);
michael@0 389
michael@0 390 let res10 = new Resource(server.baseURI + "/backoff");
michael@0 391 content = res10.get();
michael@0 392 do_check_eq(backoffInterval, 600);
michael@0 393
michael@0 394
michael@0 395 _("X-Weave-Quota-Remaining header notifies observer on successful requests.");
michael@0 396 let quotaValue;
michael@0 397 function onQuota(subject, data) {
michael@0 398 quotaValue = subject;
michael@0 399 }
michael@0 400 Observers.add("weave:service:quota:remaining", onQuota);
michael@0 401
michael@0 402 res10 = new Resource(server.baseURI + "/quota-error");
michael@0 403 content = res10.get();
michael@0 404 do_check_eq(content.status, 400);
michael@0 405 do_check_eq(quotaValue, undefined); // HTTP 400, so no observer notification.
michael@0 406
michael@0 407 res10 = new Resource(server.baseURI + "/quota-notice");
michael@0 408 content = res10.get();
michael@0 409 do_check_eq(content.status, 200);
michael@0 410 do_check_eq(quotaValue, 1048576);
michael@0 411
michael@0 412
michael@0 413 _("Error handling in _request() preserves exception information");
michael@0 414 let error;
michael@0 415 let res11 = new Resource("http://localhost:12345/does/not/exist");
michael@0 416 try {
michael@0 417 content = res11.get();
michael@0 418 } catch(ex) {
michael@0 419 error = ex;
michael@0 420 }
michael@0 421 do_check_eq(error.result, Cr.NS_ERROR_CONNECTION_REFUSED);
michael@0 422 do_check_eq(error.message, "NS_ERROR_CONNECTION_REFUSED");
michael@0 423 do_check_eq(typeof error.stack, "string");
michael@0 424
michael@0 425 _("Checking handling of errors in onProgress.");
michael@0 426 let res18 = new Resource(server.baseURI + "/json");
michael@0 427 let onProgress = function(rec) {
michael@0 428 // Provoke an XPC exception without a Javascript wrapper.
michael@0 429 Services.io.newURI("::::::::", null, null);
michael@0 430 };
michael@0 431 res18._onProgress = onProgress;
michael@0 432 let oldWarn = res18._log.warn;
michael@0 433 let warnings = [];
michael@0 434 res18._log.warn = function(msg) { warnings.push(msg) };
michael@0 435 error = undefined;
michael@0 436 try {
michael@0 437 content = res18.get();
michael@0 438 } catch (ex) {
michael@0 439 error = ex;
michael@0 440 }
michael@0 441
michael@0 442 // It throws and logs.
michael@0 443 do_check_eq(error.result, Cr.NS_ERROR_MALFORMED_URI);
michael@0 444 do_check_eq(error, "Error: NS_ERROR_MALFORMED_URI");
michael@0 445 do_check_eq(warnings.pop(),
michael@0 446 "Got exception calling onProgress handler during fetch of " +
michael@0 447 server.baseURI + "/json");
michael@0 448
michael@0 449 // And this is what happens if JS throws an exception.
michael@0 450 res18 = new Resource(server.baseURI + "/json");
michael@0 451 onProgress = function(rec) {
michael@0 452 throw "BOO!";
michael@0 453 };
michael@0 454 res18._onProgress = onProgress;
michael@0 455 oldWarn = res18._log.warn;
michael@0 456 warnings = [];
michael@0 457 res18._log.warn = function(msg) { warnings.push(msg) };
michael@0 458 error = undefined;
michael@0 459 try {
michael@0 460 content = res18.get();
michael@0 461 } catch (ex) {
michael@0 462 error = ex;
michael@0 463 }
michael@0 464
michael@0 465 // It throws and logs.
michael@0 466 do_check_eq(error.result, Cr.NS_ERROR_XPC_JS_THREW_STRING);
michael@0 467 do_check_eq(error, "Error: NS_ERROR_XPC_JS_THREW_STRING");
michael@0 468 do_check_eq(warnings.pop(),
michael@0 469 "Got exception calling onProgress handler during fetch of " +
michael@0 470 server.baseURI + "/json");
michael@0 471
michael@0 472
michael@0 473 _("Ensure channel timeouts are thrown appropriately.");
michael@0 474 let res19 = new Resource(server.baseURI + "/json");
michael@0 475 res19.ABORT_TIMEOUT = 0;
michael@0 476 error = undefined;
michael@0 477 try {
michael@0 478 content = res19.get();
michael@0 479 } catch (ex) {
michael@0 480 error = ex;
michael@0 481 }
michael@0 482 do_check_eq(error.result, Cr.NS_ERROR_NET_TIMEOUT);
michael@0 483
michael@0 484 _("Testing URI construction.");
michael@0 485 let args = [];
michael@0 486 args.push("newer=" + 1234);
michael@0 487 args.push("limit=" + 1234);
michael@0 488 args.push("sort=" + 1234);
michael@0 489
michael@0 490 let query = "?" + args.join("&");
michael@0 491
michael@0 492 let uri1 = Utils.makeURI("http://foo/" + query)
michael@0 493 .QueryInterface(Ci.nsIURL);
michael@0 494 let uri2 = Utils.makeURI("http://foo/")
michael@0 495 .QueryInterface(Ci.nsIURL);
michael@0 496 uri2.query = query;
michael@0 497 do_check_eq(uri1.query, uri2.query);
michael@0 498 server.stop(do_test_finished);
michael@0 499 }

mercurial