1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/modules/tests/xpcshell/test_Log.js Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,572 @@ 1.4 +/* Any copyright is dedicated to the Public Domain. 1.5 + http://creativecommons.org/publicdomain/zero/1.0/ */ 1.6 + 1.7 +const {utils: Cu} = Components; 1.8 + 1.9 +Cu.import("resource://gre/modules/NetUtil.jsm"); 1.10 +Cu.import("resource://gre/modules/osfile.jsm"); 1.11 + 1.12 +Cu.import("resource://gre/modules/Log.jsm"); 1.13 + 1.14 +let testFormatter = { 1.15 + format: function format(message) { 1.16 + return message.loggerName + "\t" + 1.17 + message.levelDesc + "\t" + 1.18 + message.message; 1.19 + } 1.20 +}; 1.21 + 1.22 +function MockAppender(formatter) { 1.23 + Log.Appender.call(this, formatter); 1.24 + this.messages = []; 1.25 +} 1.26 +MockAppender.prototype = { 1.27 + __proto__: Log.Appender.prototype, 1.28 + 1.29 + doAppend: function DApp_doAppend(message) { 1.30 + this.messages.push(message); 1.31 + } 1.32 +}; 1.33 + 1.34 +function run_test() { 1.35 + run_next_test(); 1.36 +} 1.37 + 1.38 +add_task(function test_Logger() { 1.39 + let log = Log.repository.getLogger("test.logger"); 1.40 + let appender = new MockAppender(new Log.BasicFormatter()); 1.41 + 1.42 + log.level = Log.Level.Debug; 1.43 + appender.level = Log.Level.Info; 1.44 + log.addAppender(appender); 1.45 + log.info("info test"); 1.46 + log.debug("this should be logged but not appended."); 1.47 + 1.48 + do_check_eq(appender.messages.length, 1); 1.49 + 1.50 + let msgRe = /\d+\ttest.logger\t\INFO\tinfo test/; 1.51 + do_check_true(msgRe.test(appender.messages[0])); 1.52 +}); 1.53 + 1.54 +add_task(function test_Logger_parent() { 1.55 + // Check whether parenting is correct 1.56 + let grandparentLog = Log.repository.getLogger("grandparent"); 1.57 + let childLog = Log.repository.getLogger("grandparent.parent.child"); 1.58 + do_check_eq(childLog.parent.name, "grandparent"); 1.59 + 1.60 + let parentLog = Log.repository.getLogger("grandparent.parent"); 1.61 + do_check_eq(childLog.parent.name, "grandparent.parent"); 1.62 + 1.63 + // Check that appends are exactly in scope 1.64 + let gpAppender = new MockAppender(new Log.BasicFormatter()); 1.65 + gpAppender.level = Log.Level.Info; 1.66 + grandparentLog.addAppender(gpAppender); 1.67 + childLog.info("child info test"); 1.68 + Log.repository.rootLogger.info("this shouldn't show up in gpAppender"); 1.69 + 1.70 + do_check_eq(gpAppender.messages.length, 1); 1.71 + do_check_true(gpAppender.messages[0].indexOf("child info test") > 0); 1.72 +}); 1.73 + 1.74 +add_test(function test_LoggerWithMessagePrefix() { 1.75 + let log = Log.repository.getLogger("test.logger.prefix"); 1.76 + let appender = new MockAppender(new Log.MessageOnlyFormatter()); 1.77 + log.addAppender(appender); 1.78 + 1.79 + let prefixed = Log.repository.getLoggerWithMessagePrefix( 1.80 + "test.logger.prefix", "prefix: "); 1.81 + 1.82 + log.warn("no prefix"); 1.83 + prefixed.warn("with prefix"); 1.84 + 1.85 + Assert.equal(appender.messages.length, 2, "2 messages were logged."); 1.86 + Assert.deepEqual(appender.messages, [ 1.87 + "no prefix", 1.88 + "prefix: with prefix", 1.89 + ], "Prefix logger works."); 1.90 + 1.91 + run_next_test(); 1.92 +}); 1.93 + 1.94 +/* 1.95 + * A utility method for checking object equivalence. 1.96 + * Fields with a reqular expression value in expected will be tested 1.97 + * against the corresponding value in actual. Otherwise objects 1.98 + * are expected to have the same keys and equal values. 1.99 + */ 1.100 +function checkObjects(expected, actual) { 1.101 + do_check_true(expected instanceof Object); 1.102 + do_check_true(actual instanceof Object); 1.103 + for (let key in expected) { 1.104 + do_check_neq(actual[key], undefined); 1.105 + if (expected[key] instanceof RegExp) { 1.106 + do_check_true(expected[key].test(actual[key].toString())); 1.107 + } else { 1.108 + if (expected[key] instanceof Object) { 1.109 + checkObjects(expected[key], actual[key]); 1.110 + } else { 1.111 + do_check_eq(expected[key], actual[key]); 1.112 + } 1.113 + } 1.114 + } 1.115 + 1.116 + for (let key in actual) { 1.117 + do_check_neq(expected[key], undefined); 1.118 + } 1.119 +} 1.120 + 1.121 +add_task(function test_StructuredLogCommands() { 1.122 + let appender = new MockAppender(new Log.StructuredFormatter()); 1.123 + let logger = Log.repository.getLogger("test.StructuredOutput"); 1.124 + logger.addAppender(appender); 1.125 + logger.level = Log.Level.Info; 1.126 + 1.127 + logger.logStructured("test_message", {_message: "message string one"}); 1.128 + logger.logStructured("test_message", {_message: "message string two", 1.129 + _level: "ERROR", 1.130 + source_file: "test_Log.js"}); 1.131 + logger.logStructured("test_message"); 1.132 + logger.logStructured("test_message", {source_file: "test_Log.js", 1.133 + message_position: 4}); 1.134 + 1.135 + let messageOne = {"_time": /\d+/, 1.136 + "_namespace": "test.StructuredOutput", 1.137 + "_level": "INFO", 1.138 + "_message": "message string one", 1.139 + "action": "test_message"}; 1.140 + 1.141 + let messageTwo = {"_time": /\d+/, 1.142 + "_namespace": "test.StructuredOutput", 1.143 + "_level": "ERROR", 1.144 + "_message": "message string two", 1.145 + "action": "test_message", 1.146 + "source_file": "test_Log.js"}; 1.147 + 1.148 + let messageThree = {"_time": /\d+/, 1.149 + "_namespace": "test.StructuredOutput", 1.150 + "_level": "INFO", 1.151 + "action": "test_message"}; 1.152 + 1.153 + let messageFour = {"_time": /\d+/, 1.154 + "_namespace": "test.StructuredOutput", 1.155 + "_level": "INFO", 1.156 + "action": "test_message", 1.157 + "source_file": "test_Log.js", 1.158 + "message_position": 4}; 1.159 + 1.160 + checkObjects(messageOne, JSON.parse(appender.messages[0])); 1.161 + checkObjects(messageTwo, JSON.parse(appender.messages[1])); 1.162 + checkObjects(messageThree, JSON.parse(appender.messages[2])); 1.163 + checkObjects(messageFour, JSON.parse(appender.messages[3])); 1.164 + 1.165 + let errored = false; 1.166 + try { 1.167 + logger.logStructured("", {_message: "invalid message"}); 1.168 + } catch (e) { 1.169 + errored = true; 1.170 + do_check_eq(e, "An action is required when logging a structured message."); 1.171 + } finally { 1.172 + do_check_true(errored); 1.173 + } 1.174 + 1.175 + let errored = false; 1.176 + try { 1.177 + logger.logStructured("message_action", "invalid params"); 1.178 + } catch (e) { 1.179 + errored = true; 1.180 + do_check_eq(e, "The params argument is required to be an object."); 1.181 + } finally { 1.182 + do_check_true(errored); 1.183 + } 1.184 + 1.185 + // Logging with unstructured interface should produce the same messages 1.186 + // as the structured interface for these cases. 1.187 + let appender = new MockAppender(new Log.StructuredFormatter()); 1.188 + let logger = Log.repository.getLogger("test.StructuredOutput1"); 1.189 + messageOne._namespace = "test.StructuredOutput1"; 1.190 + messageTwo._namespace = "test.StructuredOutput1"; 1.191 + logger.addAppender(appender); 1.192 + logger.level = Log.Level.All; 1.193 + logger.info("message string one", {action: "test_message"}); 1.194 + logger.error("message string two", {action: "test_message", 1.195 + source_file: "test_Log.js"}); 1.196 + 1.197 + checkObjects(messageOne, JSON.parse(appender.messages[0])); 1.198 + checkObjects(messageTwo, JSON.parse(appender.messages[1])); 1.199 +}); 1.200 + 1.201 +add_task(function test_StorageStreamAppender() { 1.202 + let appender = new Log.StorageStreamAppender(testFormatter); 1.203 + do_check_eq(appender.getInputStream(), null); 1.204 + 1.205 + // Log to the storage stream and verify the log was written and can be 1.206 + // read back. 1.207 + let logger = Log.repository.getLogger("test.StorageStreamAppender"); 1.208 + logger.addAppender(appender); 1.209 + logger.info("OHAI"); 1.210 + let inputStream = appender.getInputStream(); 1.211 + let data = NetUtil.readInputStreamToString(inputStream, 1.212 + inputStream.available()); 1.213 + do_check_eq(data, "test.StorageStreamAppender\tINFO\tOHAI\n"); 1.214 + 1.215 + // We can read it again even. 1.216 + let sndInputStream = appender.getInputStream(); 1.217 + let sameData = NetUtil.readInputStreamToString(sndInputStream, 1.218 + sndInputStream.available()); 1.219 + do_check_eq(data, sameData); 1.220 + 1.221 + // Reset the appender and log some more. 1.222 + appender.reset(); 1.223 + do_check_eq(appender.getInputStream(), null); 1.224 + logger.debug("wut?!?"); 1.225 + inputStream = appender.getInputStream(); 1.226 + data = NetUtil.readInputStreamToString(inputStream, 1.227 + inputStream.available()); 1.228 + do_check_eq(data, "test.StorageStreamAppender\tDEBUG\twut?!?\n"); 1.229 +}); 1.230 + 1.231 +function fileContents(path) { 1.232 + let decoder = new TextDecoder(); 1.233 + return OS.File.read(path).then(array => { 1.234 + return decoder.decode(array); 1.235 + }); 1.236 +} 1.237 + 1.238 +add_task(function test_FileAppender() { 1.239 + // This directory does not exist yet 1.240 + let dir = OS.Path.join(do_get_profile().path, "test_Log"); 1.241 + do_check_false(yield OS.File.exists(dir)); 1.242 + let path = OS.Path.join(dir, "test_FileAppender"); 1.243 + let appender = new Log.FileAppender(path, testFormatter); 1.244 + let logger = Log.repository.getLogger("test.FileAppender"); 1.245 + logger.addAppender(appender); 1.246 + 1.247 + // Logging to a file that can't be created won't do harm. 1.248 + do_check_false(yield OS.File.exists(path)); 1.249 + logger.info("OHAI!"); 1.250 + 1.251 + yield OS.File.makeDir(dir); 1.252 + logger.info("OHAI"); 1.253 + yield appender._lastWritePromise; 1.254 + 1.255 + do_check_eq((yield fileContents(path)), 1.256 + "test.FileAppender\tINFO\tOHAI\n"); 1.257 + 1.258 + logger.info("OHAI"); 1.259 + yield appender._lastWritePromise; 1.260 + 1.261 + do_check_eq((yield fileContents(path)), 1.262 + "test.FileAppender\tINFO\tOHAI\n" + 1.263 + "test.FileAppender\tINFO\tOHAI\n"); 1.264 + 1.265 + // Reset the appender and log some more. 1.266 + yield appender.reset(); 1.267 + do_check_false(yield OS.File.exists(path)); 1.268 + 1.269 + logger.debug("O RLY?!?"); 1.270 + yield appender._lastWritePromise; 1.271 + do_check_eq((yield fileContents(path)), 1.272 + "test.FileAppender\tDEBUG\tO RLY?!?\n"); 1.273 + 1.274 + yield appender.reset(); 1.275 + logger.debug("1"); 1.276 + logger.info("2"); 1.277 + logger.info("3"); 1.278 + logger.info("4"); 1.279 + logger.info("5"); 1.280 + // Waiting on only the last promise should account for all of these. 1.281 + yield appender._lastWritePromise; 1.282 + 1.283 + // Messages ought to be logged in order. 1.284 + do_check_eq((yield fileContents(path)), 1.285 + "test.FileAppender\tDEBUG\t1\n" + 1.286 + "test.FileAppender\tINFO\t2\n" + 1.287 + "test.FileAppender\tINFO\t3\n" + 1.288 + "test.FileAppender\tINFO\t4\n" + 1.289 + "test.FileAppender\tINFO\t5\n"); 1.290 +}); 1.291 + 1.292 +add_task(function test_BoundedFileAppender() { 1.293 + let dir = OS.Path.join(do_get_profile().path, "test_Log"); 1.294 + let path = OS.Path.join(dir, "test_BoundedFileAppender"); 1.295 + // This appender will hold about two lines at a time. 1.296 + let appender = new Log.BoundedFileAppender(path, testFormatter, 40); 1.297 + let logger = Log.repository.getLogger("test.BoundedFileAppender"); 1.298 + logger.addAppender(appender); 1.299 + 1.300 + logger.info("ONE"); 1.301 + logger.info("TWO"); 1.302 + yield appender._lastWritePromise; 1.303 + 1.304 + do_check_eq((yield fileContents(path)), 1.305 + "test.BoundedFileAppender\tINFO\tONE\n" + 1.306 + "test.BoundedFileAppender\tINFO\tTWO\n"); 1.307 + 1.308 + logger.info("THREE"); 1.309 + logger.info("FOUR"); 1.310 + 1.311 + do_check_neq(appender._removeFilePromise, undefined); 1.312 + yield appender._removeFilePromise; 1.313 + yield appender._lastWritePromise; 1.314 + 1.315 + do_check_eq((yield fileContents(path)), 1.316 + "test.BoundedFileAppender\tINFO\tTHREE\n" + 1.317 + "test.BoundedFileAppender\tINFO\tFOUR\n"); 1.318 + 1.319 + yield appender.reset(); 1.320 + logger.info("ONE"); 1.321 + logger.info("TWO"); 1.322 + logger.info("THREE"); 1.323 + logger.info("FOUR"); 1.324 + 1.325 + do_check_neq(appender._removeFilePromise, undefined); 1.326 + yield appender._removeFilePromise; 1.327 + yield appender._lastWritePromise; 1.328 + 1.329 + do_check_eq((yield fileContents(path)), 1.330 + "test.BoundedFileAppender\tINFO\tTHREE\n" + 1.331 + "test.BoundedFileAppender\tINFO\tFOUR\n"); 1.332 + 1.333 +}); 1.334 + 1.335 +/* 1.336 + * Test parameter formatting. 1.337 + */ 1.338 +add_task(function log_message_with_params() { 1.339 + let formatter = new Log.BasicFormatter(); 1.340 + 1.341 + function formatMessage(text, params) { 1.342 + let full = formatter.format(new Log.LogMessage("test.logger", Log.Level.Warn, text, params)); 1.343 + return full.split("\t")[3]; 1.344 + } 1.345 + 1.346 + // Strings are substituted directly. 1.347 + do_check_eq(formatMessage("String is ${foo}", {foo: "bar"}), 1.348 + "String is bar"); 1.349 + 1.350 + // Numbers are substituted. 1.351 + do_check_eq(formatMessage("Number is ${number}", {number: 47}), 1.352 + "Number is 47") 1.353 + 1.354 + // The entire params object is JSON-formatted and substituted. 1.355 + do_check_eq(formatMessage("Object is ${}", {foo: "bar"}), 1.356 + 'Object is {"foo":"bar"}'); 1.357 + 1.358 + // An object nested inside params is JSON-formatted and substituted. 1.359 + do_check_eq(formatMessage("Sub object is ${sub}", {sub: {foo: "bar"}}), 1.360 + 'Sub object is {"foo":"bar"}'); 1.361 + 1.362 + // The substitution field is missing from params. Leave the placeholder behind 1.363 + // to make the mistake obvious. 1.364 + do_check_eq(formatMessage("Missing object is ${missing}", {}), 1.365 + 'Missing object is ${missing}'); 1.366 + 1.367 + // Make sure we don't treat the parameter name 'false' as a falsey value. 1.368 + do_check_eq(formatMessage("False is ${false}", {false: true}), 1.369 + 'False is true'); 1.370 + 1.371 + // If an object has a .toJSON method, the formatter uses it. 1.372 + let ob = function() {}; 1.373 + ob.toJSON = function() {return {sneaky: "value"}}; 1.374 + do_check_eq(formatMessage("JSON is ${sub}", {sub: ob}), 1.375 + 'JSON is {"sneaky":"value"}'); 1.376 + 1.377 + // Fall back to .toSource() if JSON.stringify() fails on an object. 1.378 + let ob = function() {}; 1.379 + ob.toJSON = function() {throw "oh noes JSON"}; 1.380 + do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}), 1.381 + 'Fail is (function () {})'); 1.382 + 1.383 + // Fall back to .toString if both .toJSON and .toSource fail. 1.384 + ob.toSource = function() {throw "oh noes SOURCE"}; 1.385 + do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}), 1.386 + 'Fail is function () {}'); 1.387 + 1.388 + // Fall back to '[object]' if .toJSON, .toSource and .toString fail. 1.389 + ob.toString = function() {throw "oh noes STRING"}; 1.390 + do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}), 1.391 + 'Fail is [object]'); 1.392 + 1.393 + // If params are passed but there are no substitution in the text 1.394 + // we JSON format and append the entire parameters object. 1.395 + do_check_eq(formatMessage("Text with no subs", {a: "b", c: "d"}), 1.396 + 'Text with no subs: {"a":"b","c":"d"}'); 1.397 + 1.398 + // If we substitute one parameter but not the other, 1.399 + // we ignore any params that aren't substituted. 1.400 + do_check_eq(formatMessage("Text with partial sub ${a}", {a: "b", c: "d"}), 1.401 + 'Text with partial sub b'); 1.402 + 1.403 + // We don't format internal fields stored in params. 1.404 + do_check_eq(formatMessage("Params with _ ${}", {a: "b", _c: "d", _level:20, _message:"froo", 1.405 + _time:123456, _namespace:"here.there"}), 1.406 + 'Params with _ {"a":"b","_c":"d"}'); 1.407 + 1.408 + // Don't print an empty params holder if all params are internal. 1.409 + do_check_eq(formatMessage("All params internal", {_level:20, _message:"froo", 1.410 + _time:123456, _namespace:"here.there"}), 1.411 + 'All params internal'); 1.412 + 1.413 + // Format params with null and undefined values. 1.414 + do_check_eq(formatMessage("Null ${n} undefined ${u}", {n: null, u: undefined}), 1.415 + 'Null null undefined undefined'); 1.416 + 1.417 + // Format params with number, bool, and Object/String type. 1.418 + do_check_eq(formatMessage("number ${n} boolean ${b} boxed Boolean ${bx} String ${s}", 1.419 + {n: 45, b: false, bx: new Boolean(true), s: new String("whatevs")}), 1.420 + 'number 45 boolean false boxed Boolean true String whatevs'); 1.421 + 1.422 + /* 1.423 + * Check that errors get special formatting if they're formatted directly as 1.424 + * a named param or they're the only param, but not if they're a field in a 1.425 + * larger structure. 1.426 + */ 1.427 + let err = Components.Exception("test exception", Components.results.NS_ERROR_FAILURE); 1.428 + let str = formatMessage("Exception is ${}", err); 1.429 + do_check_true(str.contains('Exception is [Exception... "test exception"')); 1.430 + do_check_true(str.contains("(NS_ERROR_FAILURE)")); 1.431 + let str = formatMessage("Exception is", err); 1.432 + do_check_true(str.contains('Exception is: [Exception... "test exception"')); 1.433 + let str = formatMessage("Exception is ${error}", {error: err}); 1.434 + do_check_true(str.contains('Exception is [Exception... "test exception"')); 1.435 + let str = formatMessage("Exception is", {_error: err}); 1.436 + do_print(str); 1.437 + // Exceptions buried inside objects are formatted badly. 1.438 + do_check_true(str.contains('Exception is: {"_error":{}')); 1.439 + // If the message text is null, the message contains only the formatted params object. 1.440 + let str = formatMessage(null, err); 1.441 + do_check_true(str.startsWith('[Exception... "test exception"')); 1.442 + // If the text is null and 'params' is a String object, the message is exactly that string. 1.443 + let str = formatMessage(null, new String("String in place of params")); 1.444 + do_check_eq(str, "String in place of params"); 1.445 + 1.446 + // We use object.valueOf() internally; make sure a broken valueOf() method 1.447 + // doesn't cause the logger to fail. 1.448 + let vOf = {a: 1, valueOf: function() {throw "oh noes valueOf"}}; 1.449 + do_check_eq(formatMessage("Broken valueOf ${}", vOf), 1.450 + 'Broken valueOf ({a:1, valueOf:(function () {throw "oh noes valueOf"})})'); 1.451 + 1.452 + // Test edge cases of bad data to formatter: 1.453 + // If 'params' is not an object, format it as a basic type. 1.454 + do_check_eq(formatMessage("non-object no subst", 1), 1.455 + 'non-object no subst: 1'); 1.456 + do_check_eq(formatMessage("non-object all subst ${}", 2), 1.457 + 'non-object all subst 2'); 1.458 + // If 'params' is not an object, no named substitutions can succeed; 1.459 + // therefore we leave the placeholder and append the formatted params. 1.460 + do_check_eq(formatMessage("non-object named subst ${junk} space", 3), 1.461 + 'non-object named subst ${junk} space: 3'); 1.462 + // If there are no params, we leave behind the placeholders in the text. 1.463 + do_check_eq(formatMessage("no params ${missing}", undefined), 1.464 + 'no params ${missing}'); 1.465 + // If params doesn't contain any of the tags requested in the text, 1.466 + // we leave them all behind and append the formatted params. 1.467 + do_check_eq(formatMessage("object missing tag ${missing} space", {mising: "not here"}), 1.468 + 'object missing tag ${missing} space: {"mising":"not here"}'); 1.469 + // If we are given null text and no params, the resulting formatted message is empty. 1.470 + do_check_eq(formatMessage(null), ''); 1.471 +}); 1.472 + 1.473 +/* 1.474 + * If we call a log function with a non-string object in place of the text 1.475 + * argument, and no parameters, treat that the same as logging empty text 1.476 + * with the object argument as parameters. This makes the log useful when the 1.477 + * caller does "catch(err) {logger.error(err)}" 1.478 + */ 1.479 +add_task(function test_log_err_only() { 1.480 + let log = Log.repository.getLogger("error.only"); 1.481 + let testFormatter = { format: msg => msg }; 1.482 + let appender = new MockAppender(testFormatter); 1.483 + log.addAppender(appender); 1.484 + 1.485 + /* 1.486 + * Check that log.error(err) is treated the same as 1.487 + * log.error(null, err) by the logMessage constructor; the formatMessage() 1.488 + * tests above ensure that the combination of null text and an error object 1.489 + * is formatted correctly. 1.490 + */ 1.491 + try { 1.492 + eval("javascript syntax error"); 1.493 + } 1.494 + catch (e) { 1.495 + log.error(e); 1.496 + msg = appender.messages.pop(); 1.497 + do_check_eq(msg.message, null); 1.498 + do_check_eq(msg.params, e); 1.499 + } 1.500 +}); 1.501 + 1.502 +/* 1.503 + * Test logStructured() messages through basic formatter. 1.504 + */ 1.505 +add_task(function test_structured_basic() { 1.506 + let log = Log.repository.getLogger("test.logger"); 1.507 + let appender = new MockAppender(new Log.BasicFormatter()); 1.508 + 1.509 + log.level = Log.Level.Info; 1.510 + appender.level = Log.Level.Info; 1.511 + log.addAppender(appender); 1.512 + 1.513 + // A structured entry with no _message is treated the same as log./level/(null, params) 1.514 + // except the 'action' field is added to the object. 1.515 + log.logStructured("action", {data: "structure"}); 1.516 + do_check_eq(appender.messages.length, 1); 1.517 + do_check_true(appender.messages[0].contains('{"data":"structure","action":"action"}')); 1.518 + 1.519 + // A structured entry with _message and substitution is treated the same as 1.520 + // log./level/(null, params). 1.521 + log.logStructured("action", {_message: "Structured sub ${data}", data: "structure"}); 1.522 + do_check_eq(appender.messages.length, 2); 1.523 + do_print(appender.messages[1]); 1.524 + do_check_true(appender.messages[1].contains('Structured sub structure')); 1.525 +}); 1.526 + 1.527 +/* 1.528 + * Test that all the basic logger methods pass the message and params through to the appender. 1.529 + */ 1.530 +add_task(function log_message_with_params() { 1.531 + let log = Log.repository.getLogger("error.logger"); 1.532 + let testFormatter = { format: msg => msg }; 1.533 + let appender = new MockAppender(testFormatter); 1.534 + log.addAppender(appender); 1.535 + 1.536 + let testParams = {a:1, b:2}; 1.537 + log.fatal("Test fatal", testParams); 1.538 + log.error("Test error", testParams); 1.539 + log.warn("Test warn", testParams); 1.540 + log.info("Test info", testParams); 1.541 + log.config("Test config", testParams); 1.542 + log.debug("Test debug", testParams); 1.543 + log.trace("Test trace", testParams); 1.544 + do_check_eq(appender.messages.length, 7); 1.545 + for (let msg of appender.messages) { 1.546 + do_check_true(msg.params === testParams); 1.547 + do_check_true(msg.message.startsWith("Test ")); 1.548 + } 1.549 +}); 1.550 + 1.551 +/* 1.552 + * Check that we format JS Errors reasonably. 1.553 + */ 1.554 +add_task(function format_errors() { 1.555 + let pFormat = new Log.ParameterFormatter(); 1.556 + 1.557 + // Test that subclasses of Error are recognized as errors. 1.558 + err = new ReferenceError("Ref Error", "ERROR_FILE", 28); 1.559 + str = pFormat.format(err); 1.560 + do_check_true(str.contains("ReferenceError")); 1.561 + do_check_true(str.contains("ERROR_FILE:28")); 1.562 + do_check_true(str.contains("Ref Error")); 1.563 + 1.564 + // Test that JS-generated Errors are recognized and formatted. 1.565 + try { 1.566 + eval("javascript syntax error"); 1.567 + } 1.568 + catch (e) { 1.569 + str = pFormat.format(e); 1.570 + do_check_true(str.contains("SyntaxError: missing ;")); 1.571 + // Make sure we identified it as an Error and formatted the error location as 1.572 + // lineNumber:columnNumber. 1.573 + do_check_true(str.contains(":1:11)")); 1.574 + } 1.575 +});