diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-06-27 06:07:23 +0000 |
commit | 1bf1084f2b10c3b47fd1a588d85d21ed0eb41d0c (patch) | |
tree | 46dcd36c86e7fbc6e5df36deb463b33e9967a6f7 /Source/WebInspectorUI/UserInterface/Test/TestHarness.js | |
parent | 32761a6cee1d0dee366b885b7b9c777e67885688 (diff) | |
download | WebKitGtk-tarball-master.tar.gz |
webkitgtk-2.16.5HEADwebkitgtk-2.16.5master
Diffstat (limited to 'Source/WebInspectorUI/UserInterface/Test/TestHarness.js')
-rw-r--r-- | Source/WebInspectorUI/UserInterface/Test/TestHarness.js | 374 |
1 files changed, 374 insertions, 0 deletions
diff --git a/Source/WebInspectorUI/UserInterface/Test/TestHarness.js b/Source/WebInspectorUI/UserInterface/Test/TestHarness.js new file mode 100644 index 000000000..53bee4a4b --- /dev/null +++ b/Source/WebInspectorUI/UserInterface/Test/TestHarness.js @@ -0,0 +1,374 @@ +/* + * Copyright (C) 2015, 2016 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +TestHarness = class TestHarness extends WebInspector.Object +{ + constructor() + { + super(); + + this._logCount = 0; + this._failureObjects = new Map; + this._failureObjectIdentifier = 1; + + // Options that are set per-test for debugging purposes. + this.forceDebugLogging = false; + + // Options that are set per-test to ensure deterministic output. + this.suppressStackTraces = false; + } + + completeTest() + { + throw new Error("Must be implemented by subclasses."); + } + + addResult() + { + throw new Error("Must be implemented by subclasses."); + } + + debugLog() + { + throw new Error("Must be implemented by subclasses."); + } + + evaluateInPage(string, callback) + { + throw new Error("Must be implemented by subclasses."); + } + + debug() + { + throw new Error("Must be implemented by subclasses."); + } + + createAsyncSuite(name) + { + return new AsyncTestSuite(this, name); + } + + createSyncSuite(name) + { + return new SyncTestSuite(this, name); + } + + get logCount() + { + return this._logCount; + } + + log(message) + { + ++this._logCount; + + if (this.forceDebugLogging) + this.debugLog(message); + else + this.addResult(message); + } + + assert(condition, message) + { + if (condition) + return; + + let stringifiedMessage = TestHarness.messageAsString(message); + this.log("ASSERT: " + stringifiedMessage); + } + + expectThat(actual, message) + { + this._expect(TestHarness.ExpectationType.True, !!actual, message, actual); + } + + expectFalse(actual, message) + { + this._expect(TestHarness.ExpectationType.False, !actual, message, actual); + } + + expectNull(actual, message) + { + this._expect(TestHarness.ExpectationType.Null, actual === null, message, actual, null); + } + + expectNotNull(actual, message) + { + this._expect(TestHarness.ExpectationType.NotNull, actual !== null, message, actual); + } + + expectEqual(actual, expected, message) + { + this._expect(TestHarness.ExpectationType.Equal, expected === actual, message, actual, expected); + } + + expectNotEqual(actual, expected, message) + { + this._expect(TestHarness.ExpectationType.NotEqual, expected !== actual, message, actual, expected); + } + + expectShallowEqual(actual, expected, message) + { + this._expect(TestHarness.ExpectationType.ShallowEqual, Object.shallowEqual(actual, expected), message, actual, expected); + } + + expectNotShallowEqual(actual, expected, message) + { + this._expect(TestHarness.ExpectationType.NotShallowEqual, !Object.shallowEqual(actual, expected), message, actual, expected); + } + + expectEqualWithAccuracy(actual, expected, accuracy, message) + { + console.assert(typeof expected === "number"); + console.assert(typeof actual === "number"); + + this._expect(TestHarness.ExpectationType.EqualWithAccuracy, Math.abs(expected - actual) <= accuracy, message, actual, expected, accuracy); + } + + expectLessThan(actual, expected, message) + { + this._expect(TestHarness.ExpectationType.LessThan, actual < expected, message, actual, expected); + } + + expectLessThanOrEqual(actual, expected, message) + { + this._expect(TestHarness.ExpectationType.LessThanOrEqual, actual <= expected, message, actual, expected); + } + + expectGreaterThan(actual, expected, message) + { + this._expect(TestHarness.ExpectationType.GreaterThan, actual > expected, message, actual, expected); + } + + expectGreaterThanOrEqual(actual, expected, message) + { + this._expect(TestHarness.ExpectationType.GreaterThanOrEqual, actual >= expected, message, actual, expected); + } + + pass(message) + { + let stringifiedMessage = TestHarness.messageAsString(message); + this.log("PASS: " + stringifiedMessage); + } + + fail(message) + { + let stringifiedMessage = TestHarness.messageAsString(message); + this.log("FAIL: " + stringifiedMessage); + } + + // Protected + + static messageAsString(message) + { + if (message instanceof Element) + return message.textContent; + + return (typeof message !== "string") ? JSON.stringify(message) : message; + } + + static sanitizeURL(url) + { + if (!url) + return "(unknown)"; + + let lastPathSeparator = Math.max(url.lastIndexOf("/"), url.lastIndexOf("\\")); + let location = (lastPathSeparator > 0) ? url.substr(lastPathSeparator + 1) : url; + if (!location.length) + location = "(unknown)"; + + // Clean up the location so it is bracketed or in parenthesis. + if (url.indexOf("[native code]") !== -1) + location = "[native code]"; + + return location; + } + + static sanitizeStackFrame(frame, i) + { + // Most frames are of the form "functionName@file:///foo/bar/File.js:345". + // But, some frames do not have a functionName. Get rid of the file path. + let nameAndURLSeparator = frame.indexOf("@"); + let frameName = (nameAndURLSeparator > 0) ? frame.substr(0, nameAndURLSeparator) : "(anonymous)"; + + let lastPathSeparator = Math.max(frame.lastIndexOf("/"), frame.lastIndexOf("\\")); + let frameLocation = (lastPathSeparator > 0) ? frame.substr(lastPathSeparator + 1) : frame; + if (!frameLocation.length) + frameLocation = "unknown"; + + // Clean up the location so it is bracketed or in parenthesis. + if (frame.indexOf("[native code]") !== -1) + frameLocation = "[native code]"; + else + frameLocation = "(" + frameLocation + ")"; + + return `#${i}: ${frameName} ${frameLocation}`; + } + + sanitizeStack(stack) + { + if (this.suppressStackTraces) + return "(suppressed)"; + + if (!stack || typeof stack !== "string") + return "(unknown)"; + + return stack.split("\n").map(TestHarness.sanitizeStackFrame).join("\n"); + } + + // Private + + _expect(type, condition, message, ...values) + { + console.assert(values.length > 0, "Should have an 'actual' value."); + + if (!message || !condition) { + values = values.map(this._expectationValueAsString.bind(this)); + message = message || this._expectationMessageFormat(type).format(...values); + } + + if (condition) { + this.pass(message); + return; + } + + message += "\n Expected: " + this._expectedValueFormat(type).format(...values.slice(1)); + message += "\n Actual: " + values[0]; + + this.fail(message); + } + + _expectationValueAsString(value) + { + let instanceIdentifier = (object) => { + let id = this._failureObjects.get(object); + if (!id) { + id = this._failureObjectIdentifier++; + this._failureObjects.set(object, id); + } + return "#" + id; + }; + + const maximumValueStringLength = 200; + const defaultValueString = String(new Object); // [object Object] + + // Special case for numbers, since JSON.stringify converts Infinity and NaN to null. + if (typeof value === "number") + return value; + + try { + let valueString = JSON.stringify(value); + if (valueString.length <= maximumValueStringLength) + return valueString; + } catch (e) {} + + try { + let valueString = String(value); + if (valueString === defaultValueString && value.constructor && value.constructor.name !== "Object") + return value.constructor.name + " instance " + instanceIdentifier(value); + return valueString; + } catch (e) { + return defaultValueString; + } + } + + _expectationMessageFormat(type) + { + switch (type) { + case TestHarness.ExpectationType.True: + return "expectThat(%s)"; + case TestHarness.ExpectationType.False: + return "expectFalse(%s)"; + case TestHarness.ExpectationType.Null: + return "expectNull(%s)"; + case TestHarness.ExpectationType.NotNull: + return "expectNotNull(%s)"; + case TestHarness.ExpectationType.Equal: + return "expectEqual(%s, %s)"; + case TestHarness.ExpectationType.NotEqual: + return "expectNotEqual(%s, %s)"; + case TestHarness.ExpectationType.ShallowEqual: + return "expectShallowEqual(%s, %s)"; + case TestHarness.ExpectationType.NotShallowEqual: + return "expectNotShallowEqual(%s, %s)"; + case TestHarness.ExpectationType.EqualWithAccuracy: + return "expectEqualWithAccuracy(%s, %s, %s)"; + case TestHarness.ExpectationType.LessThan: + return "expectLessThan(%s, %s)"; + case TestHarness.ExpectationType.LessThanOrEqual: + return "expectLessThanOrEqual(%s, %s)"; + case TestHarness.ExpectationType.GreaterThan: + return "expectGreaterThan(%s, %s)"; + case TestHarness.ExpectationType.GreaterThanOrEqual: + return "expectGreaterThanOrEqual(%s, %s)"; + default: + console.error("Unknown TestHarness.ExpectationType type: " + type); + return null; + } + } + + _expectedValueFormat(type) + { + switch (type) { + case TestHarness.ExpectationType.True: + return "truthy"; + case TestHarness.ExpectationType.False: + return "falsey"; + case TestHarness.ExpectationType.NotNull: + return "not null"; + case TestHarness.ExpectationType.NotEqual: + case TestHarness.ExpectationType.NotShallowEqual: + return "not %s"; + case TestHarness.ExpectationType.EqualWithAccuracy: + return "%s +/- %s"; + case TestHarness.ExpectationType.LessThan: + return "less than %s"; + case TestHarness.ExpectationType.LessThanOrEqual: + return "less than or equal to %s"; + case TestHarness.ExpectationType.GreaterThan: + return "greater than %s"; + case TestHarness.ExpectationType.GreaterThanOrEqual: + return "greater than or equal to %s"; + default: + return "%s"; + } + } +}; + +TestHarness.ExpectationType = { + True: Symbol("expect-true"), + False: Symbol("expect-false"), + Null: Symbol("expect-null"), + NotNull: Symbol("expect-not-null"), + Equal: Symbol("expect-equal"), + NotEqual: Symbol("expect-not-equal"), + ShallowEqual: Symbol("expect-shallow-equal"), + NotShallowEqual: Symbol("expect-not-shallow-equal"), + EqualWithAccuracy: Symbol("expect-equal-with-accuracy"), + LessThan: Symbol("expect-less-than"), + LessThanOrEqual: Symbol("expect-less-than-or-equal"), + GreaterThan: Symbol("expect-greater-than"), + GreaterThanOrEqual: Symbol("expect-greater-than-or-equal"), +}; |