toolkit/modules/tests/xpcshell/test_Log.js

changeset 0
6474c204b198
     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 +});

mercurial