toolkit/modules/tests/xpcshell/test_Log.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

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 const {utils: Cu} = Components;
michael@0 5
michael@0 6 Cu.import("resource://gre/modules/NetUtil.jsm");
michael@0 7 Cu.import("resource://gre/modules/osfile.jsm");
michael@0 8
michael@0 9 Cu.import("resource://gre/modules/Log.jsm");
michael@0 10
michael@0 11 let testFormatter = {
michael@0 12 format: function format(message) {
michael@0 13 return message.loggerName + "\t" +
michael@0 14 message.levelDesc + "\t" +
michael@0 15 message.message;
michael@0 16 }
michael@0 17 };
michael@0 18
michael@0 19 function MockAppender(formatter) {
michael@0 20 Log.Appender.call(this, formatter);
michael@0 21 this.messages = [];
michael@0 22 }
michael@0 23 MockAppender.prototype = {
michael@0 24 __proto__: Log.Appender.prototype,
michael@0 25
michael@0 26 doAppend: function DApp_doAppend(message) {
michael@0 27 this.messages.push(message);
michael@0 28 }
michael@0 29 };
michael@0 30
michael@0 31 function run_test() {
michael@0 32 run_next_test();
michael@0 33 }
michael@0 34
michael@0 35 add_task(function test_Logger() {
michael@0 36 let log = Log.repository.getLogger("test.logger");
michael@0 37 let appender = new MockAppender(new Log.BasicFormatter());
michael@0 38
michael@0 39 log.level = Log.Level.Debug;
michael@0 40 appender.level = Log.Level.Info;
michael@0 41 log.addAppender(appender);
michael@0 42 log.info("info test");
michael@0 43 log.debug("this should be logged but not appended.");
michael@0 44
michael@0 45 do_check_eq(appender.messages.length, 1);
michael@0 46
michael@0 47 let msgRe = /\d+\ttest.logger\t\INFO\tinfo test/;
michael@0 48 do_check_true(msgRe.test(appender.messages[0]));
michael@0 49 });
michael@0 50
michael@0 51 add_task(function test_Logger_parent() {
michael@0 52 // Check whether parenting is correct
michael@0 53 let grandparentLog = Log.repository.getLogger("grandparent");
michael@0 54 let childLog = Log.repository.getLogger("grandparent.parent.child");
michael@0 55 do_check_eq(childLog.parent.name, "grandparent");
michael@0 56
michael@0 57 let parentLog = Log.repository.getLogger("grandparent.parent");
michael@0 58 do_check_eq(childLog.parent.name, "grandparent.parent");
michael@0 59
michael@0 60 // Check that appends are exactly in scope
michael@0 61 let gpAppender = new MockAppender(new Log.BasicFormatter());
michael@0 62 gpAppender.level = Log.Level.Info;
michael@0 63 grandparentLog.addAppender(gpAppender);
michael@0 64 childLog.info("child info test");
michael@0 65 Log.repository.rootLogger.info("this shouldn't show up in gpAppender");
michael@0 66
michael@0 67 do_check_eq(gpAppender.messages.length, 1);
michael@0 68 do_check_true(gpAppender.messages[0].indexOf("child info test") > 0);
michael@0 69 });
michael@0 70
michael@0 71 add_test(function test_LoggerWithMessagePrefix() {
michael@0 72 let log = Log.repository.getLogger("test.logger.prefix");
michael@0 73 let appender = new MockAppender(new Log.MessageOnlyFormatter());
michael@0 74 log.addAppender(appender);
michael@0 75
michael@0 76 let prefixed = Log.repository.getLoggerWithMessagePrefix(
michael@0 77 "test.logger.prefix", "prefix: ");
michael@0 78
michael@0 79 log.warn("no prefix");
michael@0 80 prefixed.warn("with prefix");
michael@0 81
michael@0 82 Assert.equal(appender.messages.length, 2, "2 messages were logged.");
michael@0 83 Assert.deepEqual(appender.messages, [
michael@0 84 "no prefix",
michael@0 85 "prefix: with prefix",
michael@0 86 ], "Prefix logger works.");
michael@0 87
michael@0 88 run_next_test();
michael@0 89 });
michael@0 90
michael@0 91 /*
michael@0 92 * A utility method for checking object equivalence.
michael@0 93 * Fields with a reqular expression value in expected will be tested
michael@0 94 * against the corresponding value in actual. Otherwise objects
michael@0 95 * are expected to have the same keys and equal values.
michael@0 96 */
michael@0 97 function checkObjects(expected, actual) {
michael@0 98 do_check_true(expected instanceof Object);
michael@0 99 do_check_true(actual instanceof Object);
michael@0 100 for (let key in expected) {
michael@0 101 do_check_neq(actual[key], undefined);
michael@0 102 if (expected[key] instanceof RegExp) {
michael@0 103 do_check_true(expected[key].test(actual[key].toString()));
michael@0 104 } else {
michael@0 105 if (expected[key] instanceof Object) {
michael@0 106 checkObjects(expected[key], actual[key]);
michael@0 107 } else {
michael@0 108 do_check_eq(expected[key], actual[key]);
michael@0 109 }
michael@0 110 }
michael@0 111 }
michael@0 112
michael@0 113 for (let key in actual) {
michael@0 114 do_check_neq(expected[key], undefined);
michael@0 115 }
michael@0 116 }
michael@0 117
michael@0 118 add_task(function test_StructuredLogCommands() {
michael@0 119 let appender = new MockAppender(new Log.StructuredFormatter());
michael@0 120 let logger = Log.repository.getLogger("test.StructuredOutput");
michael@0 121 logger.addAppender(appender);
michael@0 122 logger.level = Log.Level.Info;
michael@0 123
michael@0 124 logger.logStructured("test_message", {_message: "message string one"});
michael@0 125 logger.logStructured("test_message", {_message: "message string two",
michael@0 126 _level: "ERROR",
michael@0 127 source_file: "test_Log.js"});
michael@0 128 logger.logStructured("test_message");
michael@0 129 logger.logStructured("test_message", {source_file: "test_Log.js",
michael@0 130 message_position: 4});
michael@0 131
michael@0 132 let messageOne = {"_time": /\d+/,
michael@0 133 "_namespace": "test.StructuredOutput",
michael@0 134 "_level": "INFO",
michael@0 135 "_message": "message string one",
michael@0 136 "action": "test_message"};
michael@0 137
michael@0 138 let messageTwo = {"_time": /\d+/,
michael@0 139 "_namespace": "test.StructuredOutput",
michael@0 140 "_level": "ERROR",
michael@0 141 "_message": "message string two",
michael@0 142 "action": "test_message",
michael@0 143 "source_file": "test_Log.js"};
michael@0 144
michael@0 145 let messageThree = {"_time": /\d+/,
michael@0 146 "_namespace": "test.StructuredOutput",
michael@0 147 "_level": "INFO",
michael@0 148 "action": "test_message"};
michael@0 149
michael@0 150 let messageFour = {"_time": /\d+/,
michael@0 151 "_namespace": "test.StructuredOutput",
michael@0 152 "_level": "INFO",
michael@0 153 "action": "test_message",
michael@0 154 "source_file": "test_Log.js",
michael@0 155 "message_position": 4};
michael@0 156
michael@0 157 checkObjects(messageOne, JSON.parse(appender.messages[0]));
michael@0 158 checkObjects(messageTwo, JSON.parse(appender.messages[1]));
michael@0 159 checkObjects(messageThree, JSON.parse(appender.messages[2]));
michael@0 160 checkObjects(messageFour, JSON.parse(appender.messages[3]));
michael@0 161
michael@0 162 let errored = false;
michael@0 163 try {
michael@0 164 logger.logStructured("", {_message: "invalid message"});
michael@0 165 } catch (e) {
michael@0 166 errored = true;
michael@0 167 do_check_eq(e, "An action is required when logging a structured message.");
michael@0 168 } finally {
michael@0 169 do_check_true(errored);
michael@0 170 }
michael@0 171
michael@0 172 let errored = false;
michael@0 173 try {
michael@0 174 logger.logStructured("message_action", "invalid params");
michael@0 175 } catch (e) {
michael@0 176 errored = true;
michael@0 177 do_check_eq(e, "The params argument is required to be an object.");
michael@0 178 } finally {
michael@0 179 do_check_true(errored);
michael@0 180 }
michael@0 181
michael@0 182 // Logging with unstructured interface should produce the same messages
michael@0 183 // as the structured interface for these cases.
michael@0 184 let appender = new MockAppender(new Log.StructuredFormatter());
michael@0 185 let logger = Log.repository.getLogger("test.StructuredOutput1");
michael@0 186 messageOne._namespace = "test.StructuredOutput1";
michael@0 187 messageTwo._namespace = "test.StructuredOutput1";
michael@0 188 logger.addAppender(appender);
michael@0 189 logger.level = Log.Level.All;
michael@0 190 logger.info("message string one", {action: "test_message"});
michael@0 191 logger.error("message string two", {action: "test_message",
michael@0 192 source_file: "test_Log.js"});
michael@0 193
michael@0 194 checkObjects(messageOne, JSON.parse(appender.messages[0]));
michael@0 195 checkObjects(messageTwo, JSON.parse(appender.messages[1]));
michael@0 196 });
michael@0 197
michael@0 198 add_task(function test_StorageStreamAppender() {
michael@0 199 let appender = new Log.StorageStreamAppender(testFormatter);
michael@0 200 do_check_eq(appender.getInputStream(), null);
michael@0 201
michael@0 202 // Log to the storage stream and verify the log was written and can be
michael@0 203 // read back.
michael@0 204 let logger = Log.repository.getLogger("test.StorageStreamAppender");
michael@0 205 logger.addAppender(appender);
michael@0 206 logger.info("OHAI");
michael@0 207 let inputStream = appender.getInputStream();
michael@0 208 let data = NetUtil.readInputStreamToString(inputStream,
michael@0 209 inputStream.available());
michael@0 210 do_check_eq(data, "test.StorageStreamAppender\tINFO\tOHAI\n");
michael@0 211
michael@0 212 // We can read it again even.
michael@0 213 let sndInputStream = appender.getInputStream();
michael@0 214 let sameData = NetUtil.readInputStreamToString(sndInputStream,
michael@0 215 sndInputStream.available());
michael@0 216 do_check_eq(data, sameData);
michael@0 217
michael@0 218 // Reset the appender and log some more.
michael@0 219 appender.reset();
michael@0 220 do_check_eq(appender.getInputStream(), null);
michael@0 221 logger.debug("wut?!?");
michael@0 222 inputStream = appender.getInputStream();
michael@0 223 data = NetUtil.readInputStreamToString(inputStream,
michael@0 224 inputStream.available());
michael@0 225 do_check_eq(data, "test.StorageStreamAppender\tDEBUG\twut?!?\n");
michael@0 226 });
michael@0 227
michael@0 228 function fileContents(path) {
michael@0 229 let decoder = new TextDecoder();
michael@0 230 return OS.File.read(path).then(array => {
michael@0 231 return decoder.decode(array);
michael@0 232 });
michael@0 233 }
michael@0 234
michael@0 235 add_task(function test_FileAppender() {
michael@0 236 // This directory does not exist yet
michael@0 237 let dir = OS.Path.join(do_get_profile().path, "test_Log");
michael@0 238 do_check_false(yield OS.File.exists(dir));
michael@0 239 let path = OS.Path.join(dir, "test_FileAppender");
michael@0 240 let appender = new Log.FileAppender(path, testFormatter);
michael@0 241 let logger = Log.repository.getLogger("test.FileAppender");
michael@0 242 logger.addAppender(appender);
michael@0 243
michael@0 244 // Logging to a file that can't be created won't do harm.
michael@0 245 do_check_false(yield OS.File.exists(path));
michael@0 246 logger.info("OHAI!");
michael@0 247
michael@0 248 yield OS.File.makeDir(dir);
michael@0 249 logger.info("OHAI");
michael@0 250 yield appender._lastWritePromise;
michael@0 251
michael@0 252 do_check_eq((yield fileContents(path)),
michael@0 253 "test.FileAppender\tINFO\tOHAI\n");
michael@0 254
michael@0 255 logger.info("OHAI");
michael@0 256 yield appender._lastWritePromise;
michael@0 257
michael@0 258 do_check_eq((yield fileContents(path)),
michael@0 259 "test.FileAppender\tINFO\tOHAI\n" +
michael@0 260 "test.FileAppender\tINFO\tOHAI\n");
michael@0 261
michael@0 262 // Reset the appender and log some more.
michael@0 263 yield appender.reset();
michael@0 264 do_check_false(yield OS.File.exists(path));
michael@0 265
michael@0 266 logger.debug("O RLY?!?");
michael@0 267 yield appender._lastWritePromise;
michael@0 268 do_check_eq((yield fileContents(path)),
michael@0 269 "test.FileAppender\tDEBUG\tO RLY?!?\n");
michael@0 270
michael@0 271 yield appender.reset();
michael@0 272 logger.debug("1");
michael@0 273 logger.info("2");
michael@0 274 logger.info("3");
michael@0 275 logger.info("4");
michael@0 276 logger.info("5");
michael@0 277 // Waiting on only the last promise should account for all of these.
michael@0 278 yield appender._lastWritePromise;
michael@0 279
michael@0 280 // Messages ought to be logged in order.
michael@0 281 do_check_eq((yield fileContents(path)),
michael@0 282 "test.FileAppender\tDEBUG\t1\n" +
michael@0 283 "test.FileAppender\tINFO\t2\n" +
michael@0 284 "test.FileAppender\tINFO\t3\n" +
michael@0 285 "test.FileAppender\tINFO\t4\n" +
michael@0 286 "test.FileAppender\tINFO\t5\n");
michael@0 287 });
michael@0 288
michael@0 289 add_task(function test_BoundedFileAppender() {
michael@0 290 let dir = OS.Path.join(do_get_profile().path, "test_Log");
michael@0 291 let path = OS.Path.join(dir, "test_BoundedFileAppender");
michael@0 292 // This appender will hold about two lines at a time.
michael@0 293 let appender = new Log.BoundedFileAppender(path, testFormatter, 40);
michael@0 294 let logger = Log.repository.getLogger("test.BoundedFileAppender");
michael@0 295 logger.addAppender(appender);
michael@0 296
michael@0 297 logger.info("ONE");
michael@0 298 logger.info("TWO");
michael@0 299 yield appender._lastWritePromise;
michael@0 300
michael@0 301 do_check_eq((yield fileContents(path)),
michael@0 302 "test.BoundedFileAppender\tINFO\tONE\n" +
michael@0 303 "test.BoundedFileAppender\tINFO\tTWO\n");
michael@0 304
michael@0 305 logger.info("THREE");
michael@0 306 logger.info("FOUR");
michael@0 307
michael@0 308 do_check_neq(appender._removeFilePromise, undefined);
michael@0 309 yield appender._removeFilePromise;
michael@0 310 yield appender._lastWritePromise;
michael@0 311
michael@0 312 do_check_eq((yield fileContents(path)),
michael@0 313 "test.BoundedFileAppender\tINFO\tTHREE\n" +
michael@0 314 "test.BoundedFileAppender\tINFO\tFOUR\n");
michael@0 315
michael@0 316 yield appender.reset();
michael@0 317 logger.info("ONE");
michael@0 318 logger.info("TWO");
michael@0 319 logger.info("THREE");
michael@0 320 logger.info("FOUR");
michael@0 321
michael@0 322 do_check_neq(appender._removeFilePromise, undefined);
michael@0 323 yield appender._removeFilePromise;
michael@0 324 yield appender._lastWritePromise;
michael@0 325
michael@0 326 do_check_eq((yield fileContents(path)),
michael@0 327 "test.BoundedFileAppender\tINFO\tTHREE\n" +
michael@0 328 "test.BoundedFileAppender\tINFO\tFOUR\n");
michael@0 329
michael@0 330 });
michael@0 331
michael@0 332 /*
michael@0 333 * Test parameter formatting.
michael@0 334 */
michael@0 335 add_task(function log_message_with_params() {
michael@0 336 let formatter = new Log.BasicFormatter();
michael@0 337
michael@0 338 function formatMessage(text, params) {
michael@0 339 let full = formatter.format(new Log.LogMessage("test.logger", Log.Level.Warn, text, params));
michael@0 340 return full.split("\t")[3];
michael@0 341 }
michael@0 342
michael@0 343 // Strings are substituted directly.
michael@0 344 do_check_eq(formatMessage("String is ${foo}", {foo: "bar"}),
michael@0 345 "String is bar");
michael@0 346
michael@0 347 // Numbers are substituted.
michael@0 348 do_check_eq(formatMessage("Number is ${number}", {number: 47}),
michael@0 349 "Number is 47")
michael@0 350
michael@0 351 // The entire params object is JSON-formatted and substituted.
michael@0 352 do_check_eq(formatMessage("Object is ${}", {foo: "bar"}),
michael@0 353 'Object is {"foo":"bar"}');
michael@0 354
michael@0 355 // An object nested inside params is JSON-formatted and substituted.
michael@0 356 do_check_eq(formatMessage("Sub object is ${sub}", {sub: {foo: "bar"}}),
michael@0 357 'Sub object is {"foo":"bar"}');
michael@0 358
michael@0 359 // The substitution field is missing from params. Leave the placeholder behind
michael@0 360 // to make the mistake obvious.
michael@0 361 do_check_eq(formatMessage("Missing object is ${missing}", {}),
michael@0 362 'Missing object is ${missing}');
michael@0 363
michael@0 364 // Make sure we don't treat the parameter name 'false' as a falsey value.
michael@0 365 do_check_eq(formatMessage("False is ${false}", {false: true}),
michael@0 366 'False is true');
michael@0 367
michael@0 368 // If an object has a .toJSON method, the formatter uses it.
michael@0 369 let ob = function() {};
michael@0 370 ob.toJSON = function() {return {sneaky: "value"}};
michael@0 371 do_check_eq(formatMessage("JSON is ${sub}", {sub: ob}),
michael@0 372 'JSON is {"sneaky":"value"}');
michael@0 373
michael@0 374 // Fall back to .toSource() if JSON.stringify() fails on an object.
michael@0 375 let ob = function() {};
michael@0 376 ob.toJSON = function() {throw "oh noes JSON"};
michael@0 377 do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
michael@0 378 'Fail is (function () {})');
michael@0 379
michael@0 380 // Fall back to .toString if both .toJSON and .toSource fail.
michael@0 381 ob.toSource = function() {throw "oh noes SOURCE"};
michael@0 382 do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
michael@0 383 'Fail is function () {}');
michael@0 384
michael@0 385 // Fall back to '[object]' if .toJSON, .toSource and .toString fail.
michael@0 386 ob.toString = function() {throw "oh noes STRING"};
michael@0 387 do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
michael@0 388 'Fail is [object]');
michael@0 389
michael@0 390 // If params are passed but there are no substitution in the text
michael@0 391 // we JSON format and append the entire parameters object.
michael@0 392 do_check_eq(formatMessage("Text with no subs", {a: "b", c: "d"}),
michael@0 393 'Text with no subs: {"a":"b","c":"d"}');
michael@0 394
michael@0 395 // If we substitute one parameter but not the other,
michael@0 396 // we ignore any params that aren't substituted.
michael@0 397 do_check_eq(formatMessage("Text with partial sub ${a}", {a: "b", c: "d"}),
michael@0 398 'Text with partial sub b');
michael@0 399
michael@0 400 // We don't format internal fields stored in params.
michael@0 401 do_check_eq(formatMessage("Params with _ ${}", {a: "b", _c: "d", _level:20, _message:"froo",
michael@0 402 _time:123456, _namespace:"here.there"}),
michael@0 403 'Params with _ {"a":"b","_c":"d"}');
michael@0 404
michael@0 405 // Don't print an empty params holder if all params are internal.
michael@0 406 do_check_eq(formatMessage("All params internal", {_level:20, _message:"froo",
michael@0 407 _time:123456, _namespace:"here.there"}),
michael@0 408 'All params internal');
michael@0 409
michael@0 410 // Format params with null and undefined values.
michael@0 411 do_check_eq(formatMessage("Null ${n} undefined ${u}", {n: null, u: undefined}),
michael@0 412 'Null null undefined undefined');
michael@0 413
michael@0 414 // Format params with number, bool, and Object/String type.
michael@0 415 do_check_eq(formatMessage("number ${n} boolean ${b} boxed Boolean ${bx} String ${s}",
michael@0 416 {n: 45, b: false, bx: new Boolean(true), s: new String("whatevs")}),
michael@0 417 'number 45 boolean false boxed Boolean true String whatevs');
michael@0 418
michael@0 419 /*
michael@0 420 * Check that errors get special formatting if they're formatted directly as
michael@0 421 * a named param or they're the only param, but not if they're a field in a
michael@0 422 * larger structure.
michael@0 423 */
michael@0 424 let err = Components.Exception("test exception", Components.results.NS_ERROR_FAILURE);
michael@0 425 let str = formatMessage("Exception is ${}", err);
michael@0 426 do_check_true(str.contains('Exception is [Exception... "test exception"'));
michael@0 427 do_check_true(str.contains("(NS_ERROR_FAILURE)"));
michael@0 428 let str = formatMessage("Exception is", err);
michael@0 429 do_check_true(str.contains('Exception is: [Exception... "test exception"'));
michael@0 430 let str = formatMessage("Exception is ${error}", {error: err});
michael@0 431 do_check_true(str.contains('Exception is [Exception... "test exception"'));
michael@0 432 let str = formatMessage("Exception is", {_error: err});
michael@0 433 do_print(str);
michael@0 434 // Exceptions buried inside objects are formatted badly.
michael@0 435 do_check_true(str.contains('Exception is: {"_error":{}'));
michael@0 436 // If the message text is null, the message contains only the formatted params object.
michael@0 437 let str = formatMessage(null, err);
michael@0 438 do_check_true(str.startsWith('[Exception... "test exception"'));
michael@0 439 // If the text is null and 'params' is a String object, the message is exactly that string.
michael@0 440 let str = formatMessage(null, new String("String in place of params"));
michael@0 441 do_check_eq(str, "String in place of params");
michael@0 442
michael@0 443 // We use object.valueOf() internally; make sure a broken valueOf() method
michael@0 444 // doesn't cause the logger to fail.
michael@0 445 let vOf = {a: 1, valueOf: function() {throw "oh noes valueOf"}};
michael@0 446 do_check_eq(formatMessage("Broken valueOf ${}", vOf),
michael@0 447 'Broken valueOf ({a:1, valueOf:(function () {throw "oh noes valueOf"})})');
michael@0 448
michael@0 449 // Test edge cases of bad data to formatter:
michael@0 450 // If 'params' is not an object, format it as a basic type.
michael@0 451 do_check_eq(formatMessage("non-object no subst", 1),
michael@0 452 'non-object no subst: 1');
michael@0 453 do_check_eq(formatMessage("non-object all subst ${}", 2),
michael@0 454 'non-object all subst 2');
michael@0 455 // If 'params' is not an object, no named substitutions can succeed;
michael@0 456 // therefore we leave the placeholder and append the formatted params.
michael@0 457 do_check_eq(formatMessage("non-object named subst ${junk} space", 3),
michael@0 458 'non-object named subst ${junk} space: 3');
michael@0 459 // If there are no params, we leave behind the placeholders in the text.
michael@0 460 do_check_eq(formatMessage("no params ${missing}", undefined),
michael@0 461 'no params ${missing}');
michael@0 462 // If params doesn't contain any of the tags requested in the text,
michael@0 463 // we leave them all behind and append the formatted params.
michael@0 464 do_check_eq(formatMessage("object missing tag ${missing} space", {mising: "not here"}),
michael@0 465 'object missing tag ${missing} space: {"mising":"not here"}');
michael@0 466 // If we are given null text and no params, the resulting formatted message is empty.
michael@0 467 do_check_eq(formatMessage(null), '');
michael@0 468 });
michael@0 469
michael@0 470 /*
michael@0 471 * If we call a log function with a non-string object in place of the text
michael@0 472 * argument, and no parameters, treat that the same as logging empty text
michael@0 473 * with the object argument as parameters. This makes the log useful when the
michael@0 474 * caller does "catch(err) {logger.error(err)}"
michael@0 475 */
michael@0 476 add_task(function test_log_err_only() {
michael@0 477 let log = Log.repository.getLogger("error.only");
michael@0 478 let testFormatter = { format: msg => msg };
michael@0 479 let appender = new MockAppender(testFormatter);
michael@0 480 log.addAppender(appender);
michael@0 481
michael@0 482 /*
michael@0 483 * Check that log.error(err) is treated the same as
michael@0 484 * log.error(null, err) by the logMessage constructor; the formatMessage()
michael@0 485 * tests above ensure that the combination of null text and an error object
michael@0 486 * is formatted correctly.
michael@0 487 */
michael@0 488 try {
michael@0 489 eval("javascript syntax error");
michael@0 490 }
michael@0 491 catch (e) {
michael@0 492 log.error(e);
michael@0 493 msg = appender.messages.pop();
michael@0 494 do_check_eq(msg.message, null);
michael@0 495 do_check_eq(msg.params, e);
michael@0 496 }
michael@0 497 });
michael@0 498
michael@0 499 /*
michael@0 500 * Test logStructured() messages through basic formatter.
michael@0 501 */
michael@0 502 add_task(function test_structured_basic() {
michael@0 503 let log = Log.repository.getLogger("test.logger");
michael@0 504 let appender = new MockAppender(new Log.BasicFormatter());
michael@0 505
michael@0 506 log.level = Log.Level.Info;
michael@0 507 appender.level = Log.Level.Info;
michael@0 508 log.addAppender(appender);
michael@0 509
michael@0 510 // A structured entry with no _message is treated the same as log./level/(null, params)
michael@0 511 // except the 'action' field is added to the object.
michael@0 512 log.logStructured("action", {data: "structure"});
michael@0 513 do_check_eq(appender.messages.length, 1);
michael@0 514 do_check_true(appender.messages[0].contains('{"data":"structure","action":"action"}'));
michael@0 515
michael@0 516 // A structured entry with _message and substitution is treated the same as
michael@0 517 // log./level/(null, params).
michael@0 518 log.logStructured("action", {_message: "Structured sub ${data}", data: "structure"});
michael@0 519 do_check_eq(appender.messages.length, 2);
michael@0 520 do_print(appender.messages[1]);
michael@0 521 do_check_true(appender.messages[1].contains('Structured sub structure'));
michael@0 522 });
michael@0 523
michael@0 524 /*
michael@0 525 * Test that all the basic logger methods pass the message and params through to the appender.
michael@0 526 */
michael@0 527 add_task(function log_message_with_params() {
michael@0 528 let log = Log.repository.getLogger("error.logger");
michael@0 529 let testFormatter = { format: msg => msg };
michael@0 530 let appender = new MockAppender(testFormatter);
michael@0 531 log.addAppender(appender);
michael@0 532
michael@0 533 let testParams = {a:1, b:2};
michael@0 534 log.fatal("Test fatal", testParams);
michael@0 535 log.error("Test error", testParams);
michael@0 536 log.warn("Test warn", testParams);
michael@0 537 log.info("Test info", testParams);
michael@0 538 log.config("Test config", testParams);
michael@0 539 log.debug("Test debug", testParams);
michael@0 540 log.trace("Test trace", testParams);
michael@0 541 do_check_eq(appender.messages.length, 7);
michael@0 542 for (let msg of appender.messages) {
michael@0 543 do_check_true(msg.params === testParams);
michael@0 544 do_check_true(msg.message.startsWith("Test "));
michael@0 545 }
michael@0 546 });
michael@0 547
michael@0 548 /*
michael@0 549 * Check that we format JS Errors reasonably.
michael@0 550 */
michael@0 551 add_task(function format_errors() {
michael@0 552 let pFormat = new Log.ParameterFormatter();
michael@0 553
michael@0 554 // Test that subclasses of Error are recognized as errors.
michael@0 555 err = new ReferenceError("Ref Error", "ERROR_FILE", 28);
michael@0 556 str = pFormat.format(err);
michael@0 557 do_check_true(str.contains("ReferenceError"));
michael@0 558 do_check_true(str.contains("ERROR_FILE:28"));
michael@0 559 do_check_true(str.contains("Ref Error"));
michael@0 560
michael@0 561 // Test that JS-generated Errors are recognized and formatted.
michael@0 562 try {
michael@0 563 eval("javascript syntax error");
michael@0 564 }
michael@0 565 catch (e) {
michael@0 566 str = pFormat.format(e);
michael@0 567 do_check_true(str.contains("SyntaxError: missing ;"));
michael@0 568 // Make sure we identified it as an Error and formatted the error location as
michael@0 569 // lineNumber:columnNumber.
michael@0 570 do_check_true(str.contains(":1:11)"));
michael@0 571 }
michael@0 572 });

mercurial