| // Copyright 2008 the V8 project authors. All rights reserved. |
| // Redistribution and use in source and binary forms, with or without |
| // modification, are permitted provided that the following conditions are |
| // met: |
| // |
| // * Redistributions of source code must retain the above copyright |
| // notice, this list of conditions and the following disclaimer. |
| // * 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. |
| // * Neither the name of Google Inc. nor the names of its |
| // contributors may be used to endorse or promote products derived |
| // from this software without specific prior written permission. |
| // |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND 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 THE COPYRIGHT |
| // OWNER OR 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. |
| |
| function MjsUnitAssertionError(message) { |
| this.message = message; |
| // Temporarily install a custom stack trace formatter and restore the |
| // previous value. |
| let prevPrepareStackTrace = Error.prepareStackTrace; |
| try { |
| Error.prepareStackTrace = MjsUnitAssertionError.prepareStackTrace; |
| // This allows fetching the stack trace using TryCatch::StackTrace. |
| this.stack = new Error("MjsUnitAssertionError").stack; |
| } finally { |
| Error.prepareStackTrace = prevPrepareStackTrace; |
| } |
| } |
| |
| /* |
| * This file is included in all mini jsunit test cases. The test |
| * framework expects lines that signal failed tests to start with |
| * the f-word and ignore all other lines. |
| */ |
| |
| MjsUnitAssertionError.prototype.toString = function () { |
| return this.message + "\n\nStack: " + this.stack; |
| }; |
| |
| |
| // Expected and found values the same objects, or the same primitive |
| // values. |
| // For known primitive values, please use assertEquals. |
| var assertSame; |
| |
| // Expected and found values are identical primitive values or functions |
| // or similarly structured objects (checking internal properties |
| // of, e.g., Number and Date objects, the elements of arrays |
| // and the properties of non-Array objects). |
| var assertEquals; |
| |
| // Expected and found values are not identical primitive values or functions |
| // or similarly structured objects (checking internal properties |
| // of, e.g., Number and Date objects, the elements of arrays |
| // and the properties of non-Array objects). |
| var assertNotEquals; |
| |
| // The difference between expected and found value is within certain tolerance. |
| var assertEqualsDelta; |
| |
| // The found object is an Array with the same length and elements |
| // as the expected object. The expected object doesn't need to be an Array, |
| // as long as it's "array-ish". |
| var assertArrayEquals; |
| |
| // The found object must have the same enumerable properties as the |
| // expected object. The type of object isn't checked. |
| var assertPropertiesEqual; |
| |
| // Assert that the string conversion of the found value is equal to |
| // the expected string. Only kept for backwards compatibility, please |
| // check the real structure of the found value. |
| var assertToStringEquals; |
| |
| // Checks that the found value is true. Use with boolean expressions |
| // for tests that doesn't have their own assertXXX function. |
| var assertTrue; |
| |
| // Checks that the found value is false. |
| var assertFalse; |
| |
| // Checks that the found value is null. Kept for historical compatibility, |
| // please just use assertEquals(null, expected). |
| var assertNull; |
| |
| // Checks that the found value is *not* null. |
| var assertNotNull; |
| |
| // Assert that the passed function or eval code throws an exception. |
| // The optional second argument is an exception constructor that the |
| // thrown exception is checked against with "instanceof". |
| // The optional third argument is a message type string that is compared |
| // to the type property on the thrown exception. |
| var assertThrows; |
| |
| // Assert that the passed function throws an exception. |
| // The exception is checked against the second argument using assertEquals. |
| var assertThrowsEquals; |
| |
| // Assert that the passed function or eval code does not throw an exception. |
| var assertDoesNotThrow; |
| |
| // Asserts that the found value is an instance of the constructor passed |
| // as the second argument. |
| var assertInstanceof; |
| |
| // Assert that this code is never executed (i.e., always fails if executed). |
| var assertUnreachable; |
| |
| // Assert that the function code is (not) optimized. If "no sync" is passed |
| // as second argument, we do not wait for the concurrent optimization thread to |
| // finish when polling for optimization status. |
| // Only works with --allow-natives-syntax. |
| var assertOptimized; |
| var assertUnoptimized; |
| |
| // Assert that a string contains another expected substring. |
| var assertContains; |
| |
| // Assert that a string matches a given regex. |
| var assertMatches; |
| |
| // Assert the result of a promise. |
| var assertPromiseResult; |
| |
| var promiseTestChain; |
| var promiseTestCount = 0; |
| |
| // These bits must be in sync with bits defined in Runtime_GetOptimizationStatus |
| var V8OptimizationStatus = { |
| kIsFunction: 1 << 0, |
| kNeverOptimize: 1 << 1, |
| kAlwaysOptimize: 1 << 2, |
| kMaybeDeopted: 1 << 3, |
| kOptimized: 1 << 4, |
| kTurboFanned: 1 << 5, |
| kInterpreted: 1 << 6, |
| kMarkedForOptimization: 1 << 7, |
| kMarkedForConcurrentOptimization: 1 << 8, |
| kOptimizingConcurrently: 1 << 9, |
| kIsExecuting: 1 << 10, |
| kTopmostFrameIsTurboFanned: 1 << 11, |
| }; |
| |
| // Returns true if --no-opt mode is on. |
| var isNeverOptimize; |
| |
| // Returns true if --always-opt mode is on. |
| var isAlwaysOptimize; |
| |
| // Returns true if given function in interpreted. |
| var isInterpreted; |
| |
| // Returns true if given function is optimized. |
| var isOptimized; |
| |
| // Returns true if given function is compiled by Crankshaft. |
| var isCrankshafted; |
| |
| // Returns true if given function is compiled by TurboFan. |
| var isTurboFanned; |
| |
| // Used for async tests. See definition below for more documentation. |
| var testAsync; |
| |
| // Monkey-patchable all-purpose failure handler. |
| var failWithMessage; |
| |
| |
| (function () { // Scope for utility functions. |
| |
| var ObjectPrototypeToString = Object.prototype.toString; |
| var NumberPrototypeValueOf = Number.prototype.valueOf; |
| var BooleanPrototypeValueOf = Boolean.prototype.valueOf; |
| var StringPrototypeValueOf = String.prototype.valueOf; |
| var DatePrototypeValueOf = Date.prototype.valueOf; |
| var RegExpPrototypeToString = RegExp.prototype.toString; |
| var ArrayPrototypeForEach = Array.prototype.forEach; |
| var ArrayPrototypeJoin = Array.prototype.join; |
| var ArrayPrototypeMap = Array.prototype.map; |
| var ArrayPrototypePush = Array.prototype.push; |
| |
| function classOf(object) { |
| // Argument must not be null or undefined. |
| var string = ObjectPrototypeToString.call(object); |
| // String has format [object <ClassName>]. |
| return string.substring(8, string.length - 1); |
| } |
| |
| |
| function ValueOf(value) { |
| switch (classOf(value)) { |
| case "Number": |
| return NumberPrototypeValueOf.call(value); |
| case "String": |
| return StringPrototypeValueOf.call(value); |
| case "Boolean": |
| return BooleanPrototypeValueOf.call(value); |
| case "Date": |
| return DatePrototypeValueOf.call(value); |
| default: |
| return value; |
| } |
| } |
| |
| |
| function PrettyPrint(value) { |
| switch (typeof value) { |
| case "string": |
| return JSON.stringify(value); |
| case "number": |
| if (value === 0 && (1 / value) < 0) return "-0"; |
| // FALLTHROUGH. |
| case "boolean": |
| case "undefined": |
| case "function": |
| case "symbol": |
| return String(value); |
| case "object": |
| if (value === null) return "null"; |
| var objectClass = classOf(value); |
| switch (objectClass) { |
| case "Number": |
| case "String": |
| case "Boolean": |
| case "Date": |
| return objectClass + "(" + PrettyPrint(ValueOf(value)) + ")"; |
| case "RegExp": |
| return RegExpPrototypeToString.call(value); |
| case "Array": |
| var mapped = ArrayPrototypeMap.call(value, PrettyPrintArrayElement); |
| var joined = ArrayPrototypeJoin.call(mapped, ","); |
| return "[" + joined + "]"; |
| case "Uint8Array": |
| case "Int8Array": |
| case "Int16Array": |
| case "Uint16Array": |
| case "Uint32Array": |
| case "Int32Array": |
| case "Float32Array": |
| case "Float64Array": |
| var joined = ArrayPrototypeJoin.call(value, ","); |
| return objectClass + "([" + joined + "])"; |
| case "Object": |
| break; |
| default: |
| return objectClass + "()"; |
| } |
| // [[Class]] is "Object". |
| var name = value.constructor.name; |
| if (name) return name + "()"; |
| return "Object()"; |
| default: |
| return "-- unknown value --"; |
| } |
| } |
| |
| |
| function PrettyPrintArrayElement(value, index, array) { |
| if (value === undefined && !(index in array)) return ""; |
| return PrettyPrint(value); |
| } |
| |
| |
| failWithMessage = function failWithMessage(message) { |
| throw new MjsUnitAssertionError(message); |
| } |
| |
| function formatFailureText(expectedText, found, name_opt) { |
| var message = "Fail" + "ure"; |
| if (name_opt) { |
| // Fix this when we ditch the old test runner. |
| message += " (" + name_opt + ")"; |
| } |
| |
| var foundText = PrettyPrint(found); |
| if (expectedText.length <= 40 && foundText.length <= 40) { |
| message += ": expected <" + expectedText + "> found <" + foundText + ">"; |
| } else { |
| message += ":\nexpected:\n" + expectedText + "\nfound:\n" + foundText; |
| } |
| return message; |
| } |
| |
| function fail(expectedText, found, name_opt) { |
| return failWithMessage(formatFailureText(expectedText, found, name_opt)); |
| } |
| |
| |
| function deepObjectEquals(a, b) { |
| var aProps = Object.keys(a); |
| aProps.sort(); |
| var bProps = Object.keys(b); |
| bProps.sort(); |
| if (!deepEquals(aProps, bProps)) { |
| return false; |
| } |
| for (var i = 0; i < aProps.length; i++) { |
| if (!deepEquals(a[aProps[i]], b[aProps[i]])) { |
| return false; |
| } |
| } |
| return true; |
| } |
| |
| |
| function deepEquals(a, b) { |
| if (a === b) { |
| // Check for -0. |
| if (a === 0) return (1 / a) === (1 / b); |
| return true; |
| } |
| if (typeof a !== typeof b) return false; |
| if (typeof a === "number") return isNaN(a) && isNaN(b); |
| if (typeof a !== "object" && typeof a !== "function") return false; |
| // Neither a nor b is primitive. |
| var objectClass = classOf(a); |
| if (objectClass !== classOf(b)) return false; |
| if (objectClass === "RegExp") { |
| // For RegExp, just compare pattern and flags using its toString. |
| return RegExpPrototypeToString.call(a) === |
| RegExpPrototypeToString.call(b); |
| } |
| // Functions are only identical to themselves. |
| if (objectClass === "Function") return false; |
| if (objectClass === "Array") { |
| var elementCount = 0; |
| if (a.length !== b.length) { |
| return false; |
| } |
| for (var i = 0; i < a.length; i++) { |
| if (!deepEquals(a[i], b[i])) return false; |
| } |
| return true; |
| } |
| if (objectClass === "String" || objectClass === "Number" || |
| objectClass === "Boolean" || objectClass === "Date") { |
| if (ValueOf(a) !== ValueOf(b)) return false; |
| } |
| return deepObjectEquals(a, b); |
| } |
| |
| assertSame = function assertSame(expected, found, name_opt) { |
| // TODO(mstarzinger): We should think about using Harmony's egal operator |
| // or the function equivalent Object.is() here. |
| if (found === expected) { |
| if (expected !== 0 || (1 / expected) === (1 / found)) return; |
| } else if ((expected !== expected) && (found !== found)) { |
| return; |
| } |
| fail(PrettyPrint(expected), found, name_opt); |
| }; |
| |
| |
| assertEquals = function assertEquals(expected, found, name_opt) { |
| if (!deepEquals(found, expected)) { |
| fail(PrettyPrint(expected), found, name_opt); |
| } |
| }; |
| |
| assertNotEquals = function assertNotEquals(expected, found, name_opt) { |
| if (deepEquals(found, expected)) { |
| fail("not equals to " + PrettyPrint(expected), found, name_opt); |
| } |
| }; |
| |
| |
| assertEqualsDelta = |
| function assertEqualsDelta(expected, found, delta, name_opt) { |
| if (Math.abs(expected - found) > delta) { |
| fail(PrettyPrint(expected) + " +- " + PrettyPrint(delta), found, name_opt); |
| } |
| }; |
| |
| |
| assertArrayEquals = function assertArrayEquals(expected, found, name_opt) { |
| var start = ""; |
| if (name_opt) { |
| start = name_opt + " - "; |
| } |
| assertEquals(expected.length, found.length, start + "array length"); |
| if (expected.length === found.length) { |
| for (var i = 0; i < expected.length; ++i) { |
| assertEquals(expected[i], found[i], |
| start + "array element at index " + i); |
| } |
| } |
| }; |
| |
| |
| assertPropertiesEqual = function assertPropertiesEqual(expected, found, |
| name_opt) { |
| // Check properties only. |
| if (!deepObjectEquals(expected, found)) { |
| fail(expected, found, name_opt); |
| } |
| }; |
| |
| |
| assertToStringEquals = function assertToStringEquals(expected, found, |
| name_opt) { |
| if (expected !== String(found)) { |
| fail(expected, found, name_opt); |
| } |
| }; |
| |
| |
| assertTrue = function assertTrue(value, name_opt) { |
| assertEquals(true, value, name_opt); |
| }; |
| |
| |
| assertFalse = function assertFalse(value, name_opt) { |
| assertEquals(false, value, name_opt); |
| }; |
| |
| |
| assertNull = function assertNull(value, name_opt) { |
| if (value !== null) { |
| fail("null", value, name_opt); |
| } |
| }; |
| |
| |
| assertNotNull = function assertNotNull(value, name_opt) { |
| if (value === null) { |
| fail("not null", value, name_opt); |
| } |
| }; |
| |
| |
| assertThrows = function assertThrows(code, type_opt, cause_opt) { |
| try { |
| if (typeof code === 'function') { |
| code(); |
| } else { |
| eval(code); |
| } |
| } catch (e) { |
| if (typeof type_opt === 'function') { |
| assertInstanceof(e, type_opt); |
| } else if (type_opt !== void 0) { |
| failWithMessage( |
| 'invalid use of assertThrows, maybe you want assertThrowsEquals'); |
| } |
| if (arguments.length >= 3) { |
| if (cause_opt instanceof RegExp) { |
| assertMatches(cause_opt, e.message, "Error message"); |
| } else { |
| assertEquals(cause_opt, e.message, "Error message"); |
| } |
| } |
| // Success. |
| return; |
| } |
| failWithMessage("Did not throw exception"); |
| }; |
| |
| |
| assertThrowsEquals = function assertThrowsEquals(fun, val) { |
| try { |
| fun(); |
| } catch(e) { |
| assertEquals(val, e); |
| return; |
| } |
| failWithMessage("Did not throw exception"); |
| }; |
| |
| |
| assertInstanceof = function assertInstanceof(obj, type) { |
| if (!(obj instanceof type)) { |
| var actualTypeName = null; |
| var actualConstructor = Object.getPrototypeOf(obj).constructor; |
| if (typeof actualConstructor === "function") { |
| actualTypeName = actualConstructor.name || String(actualConstructor); |
| } |
| failWithMessage("Object <" + PrettyPrint(obj) + "> is not an instance of <" + |
| (type.name || type) + ">" + |
| (actualTypeName ? " but of <" + actualTypeName + ">" : "")); |
| } |
| }; |
| |
| |
| assertDoesNotThrow = function assertDoesNotThrow(code, name_opt) { |
| try { |
| if (typeof code === 'function') { |
| return code(); |
| } else { |
| return eval(code); |
| } |
| } catch (e) { |
| failWithMessage("threw an exception: " + (e.message || e)); |
| } |
| }; |
| |
| assertUnreachable = function assertUnreachable(name_opt) { |
| // Fix this when we ditch the old test runner. |
| var message = "Fail" + "ure: unreachable"; |
| if (name_opt) { |
| message += " - " + name_opt; |
| } |
| failWithMessage(message); |
| }; |
| |
| assertContains = function(sub, value, name_opt) { |
| if (value == null ? (sub != null) : value.indexOf(sub) == -1) { |
| fail("contains '" + String(sub) + "'", value, name_opt); |
| } |
| }; |
| |
| assertMatches = function(regexp, str, name_opt) { |
| if (!(regexp instanceof RegExp)) { |
| regexp = new RegExp(regexp); |
| } |
| if (!str.match(regexp)) { |
| fail("should match '" + regexp + "'", str, name_opt); |
| } |
| }; |
| |
| assertPromiseResult = function(promise, success, fail) { |
| // Use --allow-natives-syntax to use this function. Note that this function |
| // overwrites {failWithMessage} permanently with %AbortJS. |
| |
| // We have to patch mjsunit because normal assertion failures just throw |
| // exceptions which are swallowed in a then clause. |
| // We use eval here to avoid parsing issues with the natives syntax. |
| if (!success) success = () => {}; |
| |
| failWithMessage = (msg) => eval("%AbortJS(msg)"); |
| if (!fail) { |
| fail = result => failWithMessage("assertPromiseResult failed: " + result); |
| } |
| |
| var test_promise = |
| promise.then( |
| result => { |
| try { |
| success(result); |
| } catch (e) { |
| failWithMessage(e); |
| } |
| }, |
| result => { |
| fail(result); |
| } |
| ) |
| .then((x)=> { |
| if (--promiseTestCount == 0) testRunner.notifyDone(); |
| }); |
| |
| if (!promiseTestChain) promiseTestChain = Promise.resolve(); |
| // waitUntilDone is idempotent. |
| testRunner.waitUntilDone(); |
| ++promiseTestCount; |
| return promiseTestChain.then(test_promise); |
| }; |
| |
| var OptimizationStatusImpl = undefined; |
| |
| var OptimizationStatus = function(fun, sync_opt) { |
| if (OptimizationStatusImpl === undefined) { |
| try { |
| OptimizationStatusImpl = new Function( |
| "fun", "sync", "return %GetOptimizationStatus(fun, sync);"); |
| } catch (e) { |
| throw new Error("natives syntax not allowed"); |
| } |
| } |
| return OptimizationStatusImpl(fun, sync_opt); |
| } |
| |
| assertUnoptimized = function assertUnoptimized(fun, sync_opt, name_opt) { |
| if (sync_opt === undefined) sync_opt = ""; |
| var opt_status = OptimizationStatus(fun, sync_opt); |
| // Tests that use assertUnoptimized() do not make sense if --always-opt |
| // option is provided. Such tests must add --no-always-opt to flags comment. |
| assertFalse((opt_status & V8OptimizationStatus.kAlwaysOptimize) !== 0, |
| "test does not make sense with --always-opt"); |
| assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, name_opt); |
| if ((opt_status & V8OptimizationStatus.kMaybeDeopted) !== 0) { |
| // When --deopt-every-n-times flag is specified it's no longer guaranteed |
| // that particular function is still deoptimized, so keep running the test |
| // to stress test the deoptimizer. |
| return; |
| } |
| assertFalse((opt_status & V8OptimizationStatus.kOptimized) !== 0, name_opt); |
| } |
| |
| assertOptimized = function assertOptimized(fun, sync_opt, name_opt) { |
| if (sync_opt === undefined) sync_opt = ""; |
| var opt_status = OptimizationStatus(fun, sync_opt); |
| // Tests that use assertOptimized() do not make sense if --no-opt |
| // option is provided. Such tests must add --opt to flags comment. |
| assertFalse((opt_status & V8OptimizationStatus.kNeverOptimize) !== 0, |
| "test does not make sense with --no-opt"); |
| assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, name_opt); |
| if ((opt_status & V8OptimizationStatus.kMaybeDeopted) !== 0) { |
| // When --deopt-every-n-times flag is specified it's no longer guaranteed |
| // that particular function is still optimized, so keep running the test |
| // to stress test the deoptimizer. |
| return; |
| } |
| assertTrue((opt_status & V8OptimizationStatus.kOptimized) !== 0, name_opt); |
| } |
| |
| isNeverOptimize = function isNeverOptimize() { |
| var opt_status = OptimizationStatus(undefined, ""); |
| return (opt_status & V8OptimizationStatus.kNeverOptimize) !== 0; |
| } |
| |
| isAlwaysOptimize = function isAlwaysOptimize() { |
| var opt_status = OptimizationStatus(undefined, ""); |
| return (opt_status & V8OptimizationStatus.kAlwaysOptimize) !== 0; |
| } |
| |
| isInterpreted = function isInterpreted(fun) { |
| var opt_status = OptimizationStatus(fun, ""); |
| assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, |
| "not a function"); |
| return (opt_status & V8OptimizationStatus.kOptimized) === 0 && |
| (opt_status & V8OptimizationStatus.kInterpreted) !== 0; |
| } |
| |
| isOptimized = function isOptimized(fun) { |
| var opt_status = OptimizationStatus(fun, ""); |
| assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, |
| "not a function"); |
| return (opt_status & V8OptimizationStatus.kOptimized) !== 0; |
| } |
| |
| isCrankshafted = function isCrankshafted(fun) { |
| var opt_status = OptimizationStatus(fun, ""); |
| assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, |
| "not a function"); |
| return (opt_status & V8OptimizationStatus.kOptimized) !== 0 && |
| (opt_status & V8OptimizationStatus.kTurboFanned) === 0; |
| } |
| |
| isTurboFanned = function isTurboFanned(fun) { |
| var opt_status = OptimizationStatus(fun, ""); |
| assertTrue((opt_status & V8OptimizationStatus.kIsFunction) !== 0, |
| "not a function"); |
| return (opt_status & V8OptimizationStatus.kOptimized) !== 0 && |
| (opt_status & V8OptimizationStatus.kTurboFanned) !== 0; |
| } |
| |
| // Custom V8-specific stack trace formatter that is temporarily installed on |
| // the Error object. |
| MjsUnitAssertionError.prepareStackTrace = function(error, stack) { |
| // Trigger default formatting with recursion. |
| try { |
| // Filter-out all but the first mjsunit frame. |
| let filteredStack = []; |
| let inMjsunit = true; |
| for (let i = 0; i < stack.length; i++) { |
| let frame = stack[i]; |
| if (inMjsunit) { |
| let file = frame.getFileName(); |
| if (!file || !file.endsWith("mjsunit.js")) { |
| inMjsunit = false; |
| // Push the last mjsunit frame, typically containing the assertion |
| // function. |
| if (i > 0) ArrayPrototypePush.call(filteredStack, stack[i-1]); |
| ArrayPrototypePush.call(filteredStack, stack[i]); |
| } |
| continue; |
| } |
| ArrayPrototypePush.call(filteredStack, frame); |
| } |
| stack = filteredStack; |
| |
| // Infer function names and calculate {max_name_length} |
| let max_name_length = 0; |
| ArrayPrototypeForEach.call(stack, each => { |
| let name = each.getFunctionName(); |
| if (name == null) name = ""; |
| if (each.isEval()) { |
| name = name; |
| } else if (each.isConstructor()) { |
| name = "new " + name; |
| } else if (each.isNative()) { |
| name = "native " + name; |
| } else if (!each.isToplevel()) { |
| name = each.getTypeName() + "." + name; |
| } |
| each.name = name; |
| max_name_length = Math.max(name.length, max_name_length) |
| }); |
| |
| // Format stack frames. |
| stack = ArrayPrototypeMap.call(stack, each => { |
| let frame = " at " + each.name.padEnd(max_name_length); |
| let fileName = each.getFileName(); |
| if (each.isEval()) return frame + " " + each.getEvalOrigin(); |
| frame += " " + (fileName ? fileName : ""); |
| let line= each.getLineNumber(); |
| frame += " " + (line ? line : ""); |
| let column = each.getColumnNumber(); |
| frame += (column ? ":" + column : ""); |
| return frame; |
| }); |
| return "" + error.message + "\n" + ArrayPrototypeJoin.call(stack, "\n"); |
| } catch(e) {}; |
| return error.stack; |
| } |
| |
| /** |
| * This is to be used through the testAsync helper function defined |
| * below. |
| * |
| * This requires the --allow-natives-syntax flag to allow calling |
| * runtime functions. |
| * |
| * There must be at least one assertion in an async test. A test |
| * with no assertions will fail. |
| * |
| * @example |
| * testAsync(assert => { |
| * assert.plan(1) // There should be one assertion in this test. |
| * Promise.resolve(1) |
| * .then(val => assert.equals(1, val), |
| * assert.unreachable); |
| * }) |
| */ |
| class AsyncAssertion { |
| constructor(test, name) { |
| this.expectedAsserts_ = -1; |
| this.actualAsserts_ = 0; |
| this.test_ = test; |
| this.name_ = name || ''; |
| } |
| |
| /** |
| * Sets the number of expected asserts in the test. The test fails |
| * if the number of asserts computed after running the test is not |
| * equal to this specified value. |
| * @param {number} expectedAsserts |
| */ |
| plan(expectedAsserts) { |
| this.expectedAsserts_ = expectedAsserts; |
| } |
| |
| fail(expectedText, found) { |
| let message = formatFailureText(expectedText, found); |
| message += "\nin test:" + this.name_ |
| message += "\n" + Function.prototype.toString.apply(this.test_); |
| eval("%AbortJS(message)"); |
| } |
| |
| equals(expected, found, name_opt) { |
| this.actualAsserts_++; |
| if (!deepEquals(expected, found)) { |
| this.fail(PrettyPrint(expected), found, name_opt); |
| } |
| } |
| |
| unreachable() { |
| let message = "Failure: unreachable in test: " + this.name_; |
| message += "\n" + Function.prototype.toString.apply(this.test_); |
| eval("%AbortJS(message)"); |
| } |
| |
| unexpectedRejection(details) { |
| return (error) => { |
| let message = |
| "Failure: unexpected Promise rejection in test: " + this.name_; |
| if (details) message += "\n @" + details; |
| if (error instanceof Error) { |
| message += "\n" + String(error.stack); |
| } else { |
| message += "\n" + String(error); |
| } |
| message += "\n\n" + Function.prototype.toString.apply(this.test_); |
| eval("%AbortJS(message)"); |
| }; |
| } |
| |
| drainMicrotasks() { |
| eval("%RunMicrotasks()"); |
| } |
| |
| done_() { |
| if (this.expectedAsserts_ === -1) { |
| let message = "Please call t.plan(count) to initialize test harness " + |
| "with correct assert count (Note: count > 0)"; |
| eval("%AbortJS(message)"); |
| } |
| |
| if (this.expectedAsserts_ !== this.actualAsserts_) { |
| let message = "Expected asserts: " + this.expectedAsserts_; |
| message += ", Actual asserts: " + this.actualAsserts_; |
| message += "\nin test: " + this.name_; |
| message += "\n" + Function.prototype.toString.apply(this.test_); |
| eval("%AbortJS(message)"); |
| } |
| } |
| } |
| |
| /** This is used to test async functions and promises. |
| * @param {testCallback} test - test function |
| * @param {string} [name] - optional name of the test |
| * |
| * |
| * @callback testCallback |
| * @param {AsyncAssertion} assert |
| */ |
| testAsync = function(test, name) { |
| let assert = new AsyncAssertion(test, name); |
| test(assert); |
| eval("%RunMicrotasks()"); |
| assert.done_(); |
| } |
| })(); |