1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/modules/tests/browser/browser_Troubleshoot.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,440 @@ 1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.7 + 1.8 +// Ideally this would be an xpcshell test, but Troubleshoot relies on things 1.9 +// that aren't initialized outside of a XUL app environment like AddonManager 1.10 +// and the "@mozilla.org/xre/app-info;1" component. 1.11 + 1.12 +Components.utils.import("resource://gre/modules/Services.jsm"); 1.13 +Components.utils.import("resource://gre/modules/Troubleshoot.jsm"); 1.14 + 1.15 +function test() { 1.16 + waitForExplicitFinish(); 1.17 + function doNextTest() { 1.18 + if (!tests.length) { 1.19 + finish(); 1.20 + return; 1.21 + } 1.22 + tests.shift()(doNextTest); 1.23 + } 1.24 + doNextTest(); 1.25 +} 1.26 + 1.27 +registerCleanupFunction(function () { 1.28 + // Troubleshoot.jsm is imported into the global scope -- the window -- above. 1.29 + // If it's not deleted, it outlives the test and is reported as a leak. 1.30 + delete window.Troubleshoot; 1.31 +}); 1.32 + 1.33 +let tests = [ 1.34 + 1.35 + function snapshotSchema(done) { 1.36 + Troubleshoot.snapshot(function (snapshot) { 1.37 + try { 1.38 + validateObject(snapshot, SNAPSHOT_SCHEMA); 1.39 + ok(true, "The snapshot should conform to the schema."); 1.40 + } 1.41 + catch (err) { 1.42 + ok(false, err); 1.43 + } 1.44 + done(); 1.45 + }); 1.46 + }, 1.47 + 1.48 + function modifiedPreferences(done) { 1.49 + let prefs = [ 1.50 + "javascript.troubleshoot", 1.51 + "troubleshoot.foo", 1.52 + "javascript.print_to_filename", 1.53 + "network.proxy.troubleshoot", 1.54 + ]; 1.55 + prefs.forEach(function (p) { 1.56 + Services.prefs.setBoolPref(p, true); 1.57 + is(Services.prefs.getBoolPref(p), true, "The pref should be set: " + p); 1.58 + }); 1.59 + Troubleshoot.snapshot(function (snapshot) { 1.60 + let p = snapshot.modifiedPreferences; 1.61 + is(p["javascript.troubleshoot"], true, 1.62 + "The pref should be present because it's whitelisted " + 1.63 + "but not blacklisted."); 1.64 + ok(!("troubleshoot.foo" in p), 1.65 + "The pref should be absent because it's not in the whitelist."); 1.66 + ok(!("javascript.print_to_filename" in p), 1.67 + "The pref should be absent because it's blacklisted."); 1.68 + ok(!("network.proxy.troubleshoot" in p), 1.69 + "The pref should be absent because it's blacklisted."); 1.70 + prefs.forEach(function (p) Services.prefs.deleteBranch(p)); 1.71 + done(); 1.72 + }); 1.73 + }, 1.74 +]; 1.75 + 1.76 +// This is inspired by JSON Schema, or by the example on its Wikipedia page 1.77 +// anyway. 1.78 +const SNAPSHOT_SCHEMA = { 1.79 + type: "object", 1.80 + required: true, 1.81 + properties: { 1.82 + application: { 1.83 + required: true, 1.84 + type: "object", 1.85 + properties: { 1.86 + name: { 1.87 + required: true, 1.88 + type: "string", 1.89 + }, 1.90 + version: { 1.91 + required: true, 1.92 + type: "string", 1.93 + }, 1.94 + userAgent: { 1.95 + required: true, 1.96 + type: "string", 1.97 + }, 1.98 + vendor: { 1.99 + type: "string", 1.100 + }, 1.101 + supportURL: { 1.102 + type: "string", 1.103 + }, 1.104 + }, 1.105 + }, 1.106 + crashes: { 1.107 + required: false, 1.108 + type: "object", 1.109 + properties: { 1.110 + pending: { 1.111 + required: true, 1.112 + type: "number", 1.113 + }, 1.114 + submitted: { 1.115 + required: true, 1.116 + type: "array", 1.117 + items: { 1.118 + type: "object", 1.119 + properties: { 1.120 + id: { 1.121 + required: true, 1.122 + type: "string", 1.123 + }, 1.124 + date: { 1.125 + required: true, 1.126 + type: "number", 1.127 + }, 1.128 + pending: { 1.129 + required: true, 1.130 + type: "boolean", 1.131 + }, 1.132 + }, 1.133 + }, 1.134 + }, 1.135 + }, 1.136 + }, 1.137 + extensions: { 1.138 + required: true, 1.139 + type: "array", 1.140 + items: { 1.141 + type: "object", 1.142 + properties: { 1.143 + name: { 1.144 + required: true, 1.145 + type: "string", 1.146 + }, 1.147 + version: { 1.148 + required: true, 1.149 + type: "string", 1.150 + }, 1.151 + id: { 1.152 + required: true, 1.153 + type: "string", 1.154 + }, 1.155 + isActive: { 1.156 + required: true, 1.157 + type: "boolean", 1.158 + }, 1.159 + }, 1.160 + }, 1.161 + }, 1.162 + modifiedPreferences: { 1.163 + required: true, 1.164 + type: "object", 1.165 + }, 1.166 + graphics: { 1.167 + required: true, 1.168 + type: "object", 1.169 + properties: { 1.170 + numTotalWindows: { 1.171 + required: true, 1.172 + type: "number", 1.173 + }, 1.174 + numAcceleratedWindows: { 1.175 + required: true, 1.176 + type: "number", 1.177 + }, 1.178 + windowLayerManagerType: { 1.179 + type: "string", 1.180 + }, 1.181 + windowLayerManagerRemote: { 1.182 + type: "boolean", 1.183 + }, 1.184 + numAcceleratedWindowsMessage: { 1.185 + type: "array", 1.186 + }, 1.187 + adapterDescription: { 1.188 + type: "string", 1.189 + }, 1.190 + adapterVendorID: { 1.191 + type: "string", 1.192 + }, 1.193 + adapterDeviceID: { 1.194 + type: "string", 1.195 + }, 1.196 + adapterRAM: { 1.197 + type: "string", 1.198 + }, 1.199 + adapterDrivers: { 1.200 + type: "string", 1.201 + }, 1.202 + driverVersion: { 1.203 + type: "string", 1.204 + }, 1.205 + driverDate: { 1.206 + type: "string", 1.207 + }, 1.208 + adapterDescription2: { 1.209 + type: "string", 1.210 + }, 1.211 + adapterVendorID2: { 1.212 + type: "string", 1.213 + }, 1.214 + adapterDeviceID2: { 1.215 + type: "string", 1.216 + }, 1.217 + adapterRAM2: { 1.218 + type: "string", 1.219 + }, 1.220 + adapterDrivers2: { 1.221 + type: "string", 1.222 + }, 1.223 + driverVersion2: { 1.224 + type: "string", 1.225 + }, 1.226 + driverDate2: { 1.227 + type: "string", 1.228 + }, 1.229 + isGPU2Active: { 1.230 + type: "boolean", 1.231 + }, 1.232 + direct2DEnabled: { 1.233 + type: "boolean", 1.234 + }, 1.235 + directWriteEnabled: { 1.236 + type: "boolean", 1.237 + }, 1.238 + directWriteVersion: { 1.239 + type: "string", 1.240 + }, 1.241 + clearTypeParameters: { 1.242 + type: "string", 1.243 + }, 1.244 + webglRenderer: { 1.245 + type: "string", 1.246 + }, 1.247 + info: { 1.248 + type: "object", 1.249 + }, 1.250 + failures: { 1.251 + type: "array", 1.252 + items: { 1.253 + type: "string", 1.254 + }, 1.255 + }, 1.256 + direct2DEnabledMessage: { 1.257 + type: "array", 1.258 + }, 1.259 + webglRendererMessage: { 1.260 + type: "array", 1.261 + }, 1.262 + }, 1.263 + }, 1.264 + javaScript: { 1.265 + required: true, 1.266 + type: "object", 1.267 + properties: { 1.268 + incrementalGCEnabled: { 1.269 + type: "boolean", 1.270 + }, 1.271 + }, 1.272 + }, 1.273 + accessibility: { 1.274 + required: true, 1.275 + type: "object", 1.276 + properties: { 1.277 + isActive: { 1.278 + required: true, 1.279 + type: "boolean", 1.280 + }, 1.281 + forceDisabled: { 1.282 + type: "number", 1.283 + }, 1.284 + }, 1.285 + }, 1.286 + libraryVersions: { 1.287 + required: true, 1.288 + type: "object", 1.289 + properties: { 1.290 + NSPR: { 1.291 + required: true, 1.292 + type: "object", 1.293 + properties: { 1.294 + minVersion: { 1.295 + required: true, 1.296 + type: "string", 1.297 + }, 1.298 + version: { 1.299 + required: true, 1.300 + type: "string", 1.301 + }, 1.302 + }, 1.303 + }, 1.304 + NSS: { 1.305 + required: true, 1.306 + type: "object", 1.307 + properties: { 1.308 + minVersion: { 1.309 + required: true, 1.310 + type: "string", 1.311 + }, 1.312 + version: { 1.313 + required: true, 1.314 + type: "string", 1.315 + }, 1.316 + }, 1.317 + }, 1.318 + NSSUTIL: { 1.319 + required: true, 1.320 + type: "object", 1.321 + properties: { 1.322 + minVersion: { 1.323 + required: true, 1.324 + type: "string", 1.325 + }, 1.326 + version: { 1.327 + required: true, 1.328 + type: "string", 1.329 + }, 1.330 + }, 1.331 + }, 1.332 + NSSSSL: { 1.333 + required: true, 1.334 + type: "object", 1.335 + properties: { 1.336 + minVersion: { 1.337 + required: true, 1.338 + type: "string", 1.339 + }, 1.340 + version: { 1.341 + required: true, 1.342 + type: "string", 1.343 + }, 1.344 + }, 1.345 + }, 1.346 + NSSSMIME: { 1.347 + required: true, 1.348 + type: "object", 1.349 + properties: { 1.350 + minVersion: { 1.351 + required: true, 1.352 + type: "string", 1.353 + }, 1.354 + version: { 1.355 + required: true, 1.356 + type: "string", 1.357 + }, 1.358 + }, 1.359 + }, 1.360 + }, 1.361 + }, 1.362 + userJS: { 1.363 + required: true, 1.364 + type: "object", 1.365 + properties: { 1.366 + exists: { 1.367 + required: true, 1.368 + type: "boolean", 1.369 + }, 1.370 + }, 1.371 + }, 1.372 + experiments: { 1.373 + type: "array", 1.374 + }, 1.375 + }, 1.376 +}; 1.377 + 1.378 +/** 1.379 + * Throws an Error if obj doesn't conform to schema. That way you get a nice 1.380 + * error message and a stack to help you figure out what went wrong, which you 1.381 + * wouldn't get if this just returned true or false instead. There's still 1.382 + * room for improvement in communicating validation failures, however. 1.383 + * 1.384 + * @param obj The object to validate. 1.385 + * @param schema The schema that obj should conform to. 1.386 + */ 1.387 +function validateObject(obj, schema) { 1.388 + if (obj === undefined && !schema.required) 1.389 + return; 1.390 + if (typeof(schema.type) != "string") 1.391 + throw schemaErr("'type' must be a string", schema); 1.392 + if (objType(obj) != schema.type) 1.393 + throw validationErr("Object is not of the expected type", obj, schema); 1.394 + let validatorFnName = "validateObject_" + schema.type; 1.395 + if (!(validatorFnName in this)) 1.396 + throw schemaErr("Validator function not defined for type", schema); 1.397 + this[validatorFnName](obj, schema); 1.398 +} 1.399 + 1.400 +function validateObject_object(obj, schema) { 1.401 + if (typeof(schema.properties) != "object") 1.402 + // Don't care what obj's properties are. 1.403 + return; 1.404 + // First check that all the schema's properties match the object. 1.405 + for (let prop in schema.properties) 1.406 + validateObject(obj[prop], schema.properties[prop]); 1.407 + // Now check that the object doesn't have any properties not in the schema. 1.408 + for (let prop in obj) 1.409 + if (!(prop in schema.properties)) 1.410 + throw validationErr("Object has property not in schema", obj, schema); 1.411 +} 1.412 + 1.413 +function validateObject_array(array, schema) { 1.414 + if (typeof(schema.items) != "object") 1.415 + // Don't care what the array's elements are. 1.416 + return; 1.417 + array.forEach(function (elt) validateObject(elt, schema.items)); 1.418 +} 1.419 + 1.420 +function validateObject_string(str, schema) {} 1.421 +function validateObject_boolean(bool, schema) {} 1.422 +function validateObject_number(num, schema) {} 1.423 + 1.424 +function validationErr(msg, obj, schema) { 1.425 + return new Error("Validation error: " + msg + 1.426 + ": object=" + JSON.stringify(obj) + 1.427 + ", schema=" + JSON.stringify(schema)); 1.428 +} 1.429 + 1.430 +function schemaErr(msg, schema) { 1.431 + return new Error("Schema error: " + msg + ": " + JSON.stringify(schema)); 1.432 +} 1.433 + 1.434 +function objType(obj) { 1.435 + let type = typeof(obj); 1.436 + if (type != "object") 1.437 + return type; 1.438 + if (Array.isArray(obj)) 1.439 + return "array"; 1.440 + if (obj === null) 1.441 + return "null"; 1.442 + return type; 1.443 +}