blob: 3d51f24513541e107fdc18042befe974da7d8270 [file] [log] [blame]
/*
* Copyright (C) 2015 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;
}
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;
}
// 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"),
};