summaryrefslogtreecommitdiffstats
path: root/toolkit/modules/tests/xpcshell/test_Log.js
diff options
context:
space:
mode:
Diffstat (limited to 'toolkit/modules/tests/xpcshell/test_Log.js')
-rw-r--r--toolkit/modules/tests/xpcshell/test_Log.js592
1 files changed, 0 insertions, 592 deletions
diff --git a/toolkit/modules/tests/xpcshell/test_Log.js b/toolkit/modules/tests/xpcshell/test_Log.js
deleted file mode 100644
index 6aee99c93..000000000
--- a/toolkit/modules/tests/xpcshell/test_Log.js
+++ /dev/null
@@ -1,592 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/* eslint-disable block-spacing */
-
-var {utils: Cu} = Components;
-
-Cu.import("resource://gre/modules/NetUtil.jsm");
-Cu.import("resource://gre/modules/osfile.jsm");
-
-Cu.import("resource://gre/modules/Promise.jsm")
-Cu.import("resource://gre/modules/Log.jsm");
-
-var testFormatter = {
- format: function format(message) {
- return message.loggerName + "\t" +
- message.levelDesc + "\t" +
- message.message;
- }
-};
-
-function MockAppender(formatter) {
- Log.Appender.call(this, formatter);
- this.messages = [];
-}
-MockAppender.prototype = {
- __proto__: Log.Appender.prototype,
-
- doAppend: function DApp_doAppend(message) {
- this.messages.push(message);
- }
-};
-
-function run_test() {
- run_next_test();
-}
-
-add_task(function test_Logger() {
- let log = Log.repository.getLogger("test.logger");
- let appender = new MockAppender(new Log.BasicFormatter());
-
- log.level = Log.Level.Debug;
- appender.level = Log.Level.Info;
- log.addAppender(appender);
- log.info("info test");
- log.debug("this should be logged but not appended.");
-
- do_check_eq(appender.messages.length, 1);
-
- let msgRe = /\d+\ttest.logger\t\INFO\tinfo test/;
- do_check_true(msgRe.test(appender.messages[0]));
-});
-
-add_task(function test_Logger_parent() {
- // Check whether parenting is correct
- let grandparentLog = Log.repository.getLogger("grandparent");
- let childLog = Log.repository.getLogger("grandparent.parent.child");
- do_check_eq(childLog.parent.name, "grandparent");
-
- let parentLog = Log.repository.getLogger("grandparent.parent");
- do_check_eq(childLog.parent.name, "grandparent.parent");
-
- // Check that appends are exactly in scope
- let gpAppender = new MockAppender(new Log.BasicFormatter());
- gpAppender.level = Log.Level.Info;
- grandparentLog.addAppender(gpAppender);
- childLog.info("child info test");
- Log.repository.rootLogger.info("this shouldn't show up in gpAppender");
-
- do_check_eq(gpAppender.messages.length, 1);
- do_check_true(gpAppender.messages[0].indexOf("child info test") > 0);
-});
-
-add_test(function test_LoggerWithMessagePrefix() {
- let log = Log.repository.getLogger("test.logger.prefix");
- let appender = new MockAppender(new Log.MessageOnlyFormatter());
- log.addAppender(appender);
-
- let prefixed = Log.repository.getLoggerWithMessagePrefix(
- "test.logger.prefix", "prefix: ");
-
- log.warn("no prefix");
- prefixed.warn("with prefix");
-
- Assert.equal(appender.messages.length, 2, "2 messages were logged.");
- Assert.deepEqual(appender.messages, [
- "no prefix",
- "prefix: with prefix",
- ], "Prefix logger works.");
-
- run_next_test();
-});
-
-/*
- * A utility method for checking object equivalence.
- * Fields with a reqular expression value in expected will be tested
- * against the corresponding value in actual. Otherwise objects
- * are expected to have the same keys and equal values.
- */
-function checkObjects(expected, actual) {
- do_check_true(expected instanceof Object);
- do_check_true(actual instanceof Object);
- for (let key in expected) {
- do_check_neq(actual[key], undefined);
- if (expected[key] instanceof RegExp) {
- do_check_true(expected[key].test(actual[key].toString()));
- } else if (expected[key] instanceof Object) {
- checkObjects(expected[key], actual[key]);
- } else {
- do_check_eq(expected[key], actual[key]);
- }
- }
-
- for (let key in actual) {
- do_check_neq(expected[key], undefined);
- }
-}
-
-add_task(function test_StructuredLogCommands() {
- let appender = new MockAppender(new Log.StructuredFormatter());
- let logger = Log.repository.getLogger("test.StructuredOutput");
- logger.addAppender(appender);
- logger.level = Log.Level.Info;
-
- logger.logStructured("test_message", {_message: "message string one"});
- logger.logStructured("test_message", {_message: "message string two",
- _level: "ERROR",
- source_file: "test_Log.js"});
- logger.logStructured("test_message");
- logger.logStructured("test_message", {source_file: "test_Log.js",
- message_position: 4});
-
- let messageOne = {"_time": /\d+/,
- "_namespace": "test.StructuredOutput",
- "_level": "INFO",
- "_message": "message string one",
- "action": "test_message"};
-
- let messageTwo = {"_time": /\d+/,
- "_namespace": "test.StructuredOutput",
- "_level": "ERROR",
- "_message": "message string two",
- "action": "test_message",
- "source_file": "test_Log.js"};
-
- let messageThree = {"_time": /\d+/,
- "_namespace": "test.StructuredOutput",
- "_level": "INFO",
- "action": "test_message"};
-
- let messageFour = {"_time": /\d+/,
- "_namespace": "test.StructuredOutput",
- "_level": "INFO",
- "action": "test_message",
- "source_file": "test_Log.js",
- "message_position": 4};
-
- checkObjects(messageOne, JSON.parse(appender.messages[0]));
- checkObjects(messageTwo, JSON.parse(appender.messages[1]));
- checkObjects(messageThree, JSON.parse(appender.messages[2]));
- checkObjects(messageFour, JSON.parse(appender.messages[3]));
-
- let errored = false;
- try {
- logger.logStructured("", {_message: "invalid message"});
- } catch (e) {
- errored = true;
- do_check_eq(e, "An action is required when logging a structured message.");
- } finally {
- do_check_true(errored);
- }
-
- errored = false;
- try {
- logger.logStructured("message_action", "invalid params");
- } catch (e) {
- errored = true;
- do_check_eq(e, "The params argument is required to be an object.");
- } finally {
- do_check_true(errored);
- }
-
- // Logging with unstructured interface should produce the same messages
- // as the structured interface for these cases.
- appender = new MockAppender(new Log.StructuredFormatter());
- logger = Log.repository.getLogger("test.StructuredOutput1");
- messageOne._namespace = "test.StructuredOutput1";
- messageTwo._namespace = "test.StructuredOutput1";
- logger.addAppender(appender);
- logger.level = Log.Level.All;
- logger.info("message string one", {action: "test_message"});
- logger.error("message string two", {action: "test_message",
- source_file: "test_Log.js"});
-
- checkObjects(messageOne, JSON.parse(appender.messages[0]));
- checkObjects(messageTwo, JSON.parse(appender.messages[1]));
-});
-
-add_task(function test_StorageStreamAppender() {
- let appender = new Log.StorageStreamAppender(testFormatter);
- do_check_eq(appender.getInputStream(), null);
-
- // Log to the storage stream and verify the log was written and can be
- // read back.
- let logger = Log.repository.getLogger("test.StorageStreamAppender");
- logger.addAppender(appender);
- logger.info("OHAI");
- let inputStream = appender.getInputStream();
- let data = NetUtil.readInputStreamToString(inputStream,
- inputStream.available());
- do_check_eq(data, "test.StorageStreamAppender\tINFO\tOHAI\n");
-
- // We can read it again even.
- let sndInputStream = appender.getInputStream();
- let sameData = NetUtil.readInputStreamToString(sndInputStream,
- sndInputStream.available());
- do_check_eq(data, sameData);
-
- // Reset the appender and log some more.
- appender.reset();
- do_check_eq(appender.getInputStream(), null);
- logger.debug("wut?!?");
- inputStream = appender.getInputStream();
- data = NetUtil.readInputStreamToString(inputStream,
- inputStream.available());
- do_check_eq(data, "test.StorageStreamAppender\tDEBUG\twut?!?\n");
-});
-
-function fileContents(path) {
- let decoder = new TextDecoder();
- return OS.File.read(path).then(array => {
- return decoder.decode(array);
- });
-}
-
-add_task(function* test_FileAppender() {
- // This directory does not exist yet
- let dir = OS.Path.join(do_get_profile().path, "test_Log");
- do_check_false(yield OS.File.exists(dir));
- let path = OS.Path.join(dir, "test_FileAppender");
- let appender = new Log.FileAppender(path, testFormatter);
- let logger = Log.repository.getLogger("test.FileAppender");
- logger.addAppender(appender);
-
- // Logging to a file that can't be created won't do harm.
- do_check_false(yield OS.File.exists(path));
- logger.info("OHAI!");
-
- yield OS.File.makeDir(dir);
- logger.info("OHAI");
- yield appender._lastWritePromise;
-
- do_check_eq((yield fileContents(path)),
- "test.FileAppender\tINFO\tOHAI\n");
-
- logger.info("OHAI");
- yield appender._lastWritePromise;
-
- do_check_eq((yield fileContents(path)),
- "test.FileAppender\tINFO\tOHAI\n" +
- "test.FileAppender\tINFO\tOHAI\n");
-
- // Reset the appender and log some more.
- yield appender.reset();
- do_check_false(yield OS.File.exists(path));
-
- logger.debug("O RLY?!?");
- yield appender._lastWritePromise;
- do_check_eq((yield fileContents(path)),
- "test.FileAppender\tDEBUG\tO RLY?!?\n");
-
- yield appender.reset();
- logger.debug("1");
- logger.info("2");
- logger.info("3");
- logger.info("4");
- logger.info("5");
- // Waiting on only the last promise should account for all of these.
- yield appender._lastWritePromise;
-
- // Messages ought to be logged in order.
- do_check_eq((yield fileContents(path)),
- "test.FileAppender\tDEBUG\t1\n" +
- "test.FileAppender\tINFO\t2\n" +
- "test.FileAppender\tINFO\t3\n" +
- "test.FileAppender\tINFO\t4\n" +
- "test.FileAppender\tINFO\t5\n");
-});
-
-add_task(function* test_BoundedFileAppender() {
- let dir = OS.Path.join(do_get_profile().path, "test_Log");
-
- if (!(yield OS.File.exists(dir))) {
- yield OS.File.makeDir(dir);
- }
-
- let path = OS.Path.join(dir, "test_BoundedFileAppender");
- // This appender will hold about two lines at a time.
- let appender = new Log.BoundedFileAppender(path, testFormatter, 40);
- let logger = Log.repository.getLogger("test.BoundedFileAppender");
- logger.addAppender(appender);
-
- logger.info("ONE");
- logger.info("TWO");
- yield appender._lastWritePromise;
-
- do_check_eq((yield fileContents(path)),
- "test.BoundedFileAppender\tINFO\tONE\n" +
- "test.BoundedFileAppender\tINFO\tTWO\n");
-
- logger.info("THREE");
- logger.info("FOUR");
-
- do_check_neq(appender._removeFilePromise, undefined);
- yield appender._removeFilePromise;
- yield appender._lastWritePromise;
-
- do_check_eq((yield fileContents(path)),
- "test.BoundedFileAppender\tINFO\tTHREE\n" +
- "test.BoundedFileAppender\tINFO\tFOUR\n");
-
- yield appender.reset();
- logger.info("ONE");
- logger.info("TWO");
- logger.info("THREE");
- logger.info("FOUR");
-
- do_check_neq(appender._removeFilePromise, undefined);
- yield appender._removeFilePromise;
- yield appender._lastWritePromise;
-
- do_check_eq((yield fileContents(path)),
- "test.BoundedFileAppender\tINFO\tTHREE\n" +
- "test.BoundedFileAppender\tINFO\tFOUR\n");
-
-});
-
-/*
- * Test parameter formatting.
- */
-add_task(function* log_message_with_params() {
- let formatter = new Log.BasicFormatter();
-
- function formatMessage(text, params) {
- let full = formatter.format(new Log.LogMessage("test.logger", Log.Level.Warn, text, params));
- return full.split("\t")[3];
- }
-
- // Strings are substituted directly.
- do_check_eq(formatMessage("String is ${foo}", {foo: "bar"}),
- "String is bar");
-
- // Numbers are substituted.
- do_check_eq(formatMessage("Number is ${number}", {number: 47}),
- "Number is 47")
-
- // The entire params object is JSON-formatted and substituted.
- do_check_eq(formatMessage("Object is ${}", {foo: "bar"}),
- 'Object is {"foo":"bar"}');
-
- // An object nested inside params is JSON-formatted and substituted.
- do_check_eq(formatMessage("Sub object is ${sub}", {sub: {foo: "bar"}}),
- 'Sub object is {"foo":"bar"}');
-
- // The substitution field is missing from params. Leave the placeholder behind
- // to make the mistake obvious.
- do_check_eq(formatMessage("Missing object is ${missing}", {}),
- 'Missing object is ${missing}');
-
- // Make sure we don't treat the parameter name 'false' as a falsey value.
- do_check_eq(formatMessage("False is ${false}", {false: true}),
- 'False is true');
-
- // If an object has a .toJSON method, the formatter uses it.
- let ob = function() {};
- ob.toJSON = function() {return {sneaky: "value"}};
- do_check_eq(formatMessage("JSON is ${sub}", {sub: ob}),
- 'JSON is {"sneaky":"value"}');
-
- // Fall back to .toSource() if JSON.stringify() fails on an object.
- ob = function() {};
- ob.toJSON = function() {throw "oh noes JSON"};
- do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
- 'Fail is (function() {})');
-
- // Fall back to .toString if both .toJSON and .toSource fail.
- ob.toSource = function() {throw "oh noes SOURCE"};
- do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
- 'Fail is function() {}');
-
- // Fall back to '[object]' if .toJSON, .toSource and .toString fail.
- ob.toString = function() {throw "oh noes STRING"};
- do_check_eq(formatMessage("Fail is ${sub}", {sub: ob}),
- 'Fail is [object]');
-
- // If params are passed but there are no substitution in the text
- // we JSON format and append the entire parameters object.
- do_check_eq(formatMessage("Text with no subs", {a: "b", c: "d"}),
- 'Text with no subs: {"a":"b","c":"d"}');
-
- // If we substitute one parameter but not the other,
- // we ignore any params that aren't substituted.
- do_check_eq(formatMessage("Text with partial sub ${a}", {a: "b", c: "d"}),
- 'Text with partial sub b');
-
- // We don't format internal fields stored in params.
- do_check_eq(formatMessage("Params with _ ${}", {a: "b", _c: "d", _level:20, _message:"froo",
- _time:123456, _namespace:"here.there"}),
- 'Params with _ {"a":"b","_c":"d"}');
-
- // Don't print an empty params holder if all params are internal.
- do_check_eq(formatMessage("All params internal", {_level:20, _message:"froo",
- _time:123456, _namespace:"here.there"}),
- 'All params internal');
-
- // Format params with null and undefined values.
- do_check_eq(formatMessage("Null ${n} undefined ${u}", {n: null, u: undefined}),
- 'Null null undefined undefined');
-
- // Format params with number, bool, and Object/String type.
- do_check_eq(formatMessage("number ${n} boolean ${b} boxed Boolean ${bx} String ${s}",
- {n: 45, b: false, bx: new Boolean(true), s: new String("whatevs")}),
- 'number 45 boolean false boxed Boolean true String whatevs');
-
- /*
- * Check that errors get special formatting if they're formatted directly as
- * a named param or they're the only param, but not if they're a field in a
- * larger structure.
- */
- let err = Components.Exception("test exception", Components.results.NS_ERROR_FAILURE);
- let str = formatMessage("Exception is ${}", err);
- do_check_true(str.includes('Exception is [Exception... "test exception"'));
- do_check_true(str.includes("(NS_ERROR_FAILURE)"));
- str = formatMessage("Exception is", err);
- do_check_true(str.includes('Exception is: [Exception... "test exception"'));
- str = formatMessage("Exception is ${error}", {error: err});
- do_check_true(str.includes('Exception is [Exception... "test exception"'));
- str = formatMessage("Exception is", {_error: err});
- do_print(str);
- // Exceptions buried inside objects are formatted badly.
- do_check_true(str.includes('Exception is: {"_error":{}'));
- // If the message text is null, the message contains only the formatted params object.
- str = formatMessage(null, err);
- do_check_true(str.startsWith('[Exception... "test exception"'));
- // If the text is null and 'params' is a String object, the message is exactly that string.
- str = formatMessage(null, new String("String in place of params"));
- do_check_eq(str, "String in place of params");
-
- // We use object.valueOf() internally; make sure a broken valueOf() method
- // doesn't cause the logger to fail.
- let vOf = {a: 1, valueOf: function() {throw "oh noes valueOf"}};
- do_check_eq(formatMessage("Broken valueOf ${}", vOf),
- 'Broken valueOf ({a:1, valueOf:(function() {throw "oh noes valueOf"})})');
-
- // Test edge cases of bad data to formatter:
- // If 'params' is not an object, format it as a basic type.
- do_check_eq(formatMessage("non-object no subst", 1),
- 'non-object no subst: 1');
- do_check_eq(formatMessage("non-object all subst ${}", 2),
- 'non-object all subst 2');
- do_check_eq(formatMessage("false no subst", false),
- 'false no subst: false');
- do_check_eq(formatMessage("null no subst", null),
- 'null no subst: null');
- // If 'params' is undefined and there are no substitutions expected,
- // the message should still be output.
- do_check_eq(formatMessage("undefined no subst", undefined),
- 'undefined no subst');
- // If 'params' is not an object, no named substitutions can succeed;
- // therefore we leave the placeholder and append the formatted params.
- do_check_eq(formatMessage("non-object named subst ${junk} space", 3),
- 'non-object named subst ${junk} space: 3');
- // If there are no params, we leave behind the placeholders in the text.
- do_check_eq(formatMessage("no params ${missing}", undefined),
- 'no params ${missing}');
- // If params doesn't contain any of the tags requested in the text,
- // we leave them all behind and append the formatted params.
- do_check_eq(formatMessage("object missing tag ${missing} space", {mising: "not here"}),
- 'object missing tag ${missing} space: {"mising":"not here"}');
- // If we are given null text and no params, the resulting formatted message is empty.
- do_check_eq(formatMessage(null), '');
-});
-
-/*
- * If we call a log function with a non-string object in place of the text
- * argument, and no parameters, treat that the same as logging empty text
- * with the object argument as parameters. This makes the log useful when the
- * caller does "catch(err) {logger.error(err)}"
- */
-add_task(function* test_log_err_only() {
- let log = Log.repository.getLogger("error.only");
- let mockFormatter = { format: msg => msg };
- let appender = new MockAppender(mockFormatter);
- log.addAppender(appender);
-
- /*
- * Check that log.error(err) is treated the same as
- * log.error(null, err) by the logMessage constructor; the formatMessage()
- * tests above ensure that the combination of null text and an error object
- * is formatted correctly.
- */
- try {
- eval("javascript syntax error");
- }
- catch (e) {
- log.error(e);
- msg = appender.messages.pop();
- do_check_eq(msg.message, null);
- do_check_eq(msg.params, e);
- }
-});
-
-/*
- * Test logStructured() messages through basic formatter.
- */
-add_task(function* test_structured_basic() {
- let log = Log.repository.getLogger("test.logger");
- let appender = new MockAppender(new Log.BasicFormatter());
-
- log.level = Log.Level.Info;
- appender.level = Log.Level.Info;
- log.addAppender(appender);
-
- // A structured entry with no _message is treated the same as log./level/(null, params)
- // except the 'action' field is added to the object.
- log.logStructured("action", {data: "structure"});
- do_check_eq(appender.messages.length, 1);
- do_check_true(appender.messages[0].includes('{"data":"structure","action":"action"}'));
-
- // A structured entry with _message and substitution is treated the same as
- // log./level/(null, params).
- log.logStructured("action", {_message: "Structured sub ${data}", data: "structure"});
- do_check_eq(appender.messages.length, 2);
- do_print(appender.messages[1]);
- do_check_true(appender.messages[1].includes('Structured sub structure'));
-});
-
-/*
- * Test that all the basic logger methods pass the message and params through to the appender.
- */
-add_task(function* log_message_with_params() {
- let log = Log.repository.getLogger("error.logger");
- let mockFormatter = { format: msg => msg };
- let appender = new MockAppender(mockFormatter);
- log.addAppender(appender);
-
- let testParams = {a:1, b:2};
- log.fatal("Test fatal", testParams);
- log.error("Test error", testParams);
- log.warn("Test warn", testParams);
- log.info("Test info", testParams);
- log.config("Test config", testParams);
- log.debug("Test debug", testParams);
- log.trace("Test trace", testParams);
- do_check_eq(appender.messages.length, 7);
- for (let msg of appender.messages) {
- do_check_true(msg.params === testParams);
- do_check_true(msg.message.startsWith("Test "));
- }
-});
-
-/*
- * Check that we format JS Errors reasonably.
- */
-add_task(function* format_errors() {
- let pFormat = new Log.ParameterFormatter();
-
- // Test that subclasses of Error are recognized as errors.
- err = new ReferenceError("Ref Error", "ERROR_FILE", 28);
- str = pFormat.format(err);
- do_check_true(str.includes("ReferenceError"));
- do_check_true(str.includes("ERROR_FILE:28"));
- do_check_true(str.includes("Ref Error"));
-
- // Test that JS-generated Errors are recognized and formatted.
- try {
- yield Promise.resolve(); // Scrambles the stack
- eval("javascript syntax error");
- }
- catch (e) {
- str = pFormat.format(e);
- do_check_true(str.includes("SyntaxError: missing ;"));
- // Make sure we identified it as an Error and formatted the error location as
- // lineNumber:columnNumber.
- do_check_true(str.includes(":1:11)"));
- // Make sure that we use human-readable stack traces
- // Check that the error doesn't contain any reference to "Promise.jsm" or "Task.jsm"
- do_check_false(str.includes("Promise.jsm"));
- do_check_false(str.includes("Task.jsm"));
- do_check_true(str.includes("format_errors"));
- }
-});