summaryrefslogtreecommitdiff
path: root/Libraries/LibJS/Tests/test-common.js
blob: 4c7e23ac8b42172adf68907c23bb3523e40a393e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/**
 * Custom error for failed assertions.
 * @constructor
 * @param {string} message Error message
 * @returns Error
 */
function AssertionError(message) {
    var instance = new Error(message);
    instance.name = 'AssertionError';
    Object.setPrototypeOf(instance, Object.getPrototypeOf(this));
    return instance;
}

/**
 * Throws an `AssertionError` if `value` is not truthy.
 * @param {*} value Value to be tested
 */
function assert(value) {
    if (!value)
        throw new AssertionError("The assertion failed!");
}

/**
 * Throws an `AssertionError` when called.
 * @throws {AssertionError}
 */
function assertNotReached() {
    throw new AssertionError("assertNotReached() was reached!");
}

/**
 * Ensures the provided functions throws a specific error.
 * @param {Function} testFunction Function executing the throwing code
 * @param {object} [options]
 * @param {Error} [options.error] Expected error type
 * @param {string} [options.name] Expected error name
 * @param {string} [options.message] Expected error message
 */
function assertThrowsError(testFunction, options) {
    try {
        testFunction();
        assertNotReached();
    } catch (e) {
        if (options.error !== undefined)
            assert(e instanceof options.error);
        if (options.name !== undefined)
            assert(e.name === options.name);
        if (options.message !== undefined)
            assert(e.message === options.message);
    }
}

/**
 * Ensures the provided JavaScript source code results in a SyntaxError
 * @param {string} source The JavaScript source code to compile
 */
function assertIsSyntaxError(source) {
    assertThrowsError(() => {
        new Function(source)();
    }, {
        error: SyntaxError,
    });
}

/**
 * Ensures the provided arrays contain exactly the same items.
 * @param {Array} a First array
 * @param {Array} b Second array
 */
function assertArrayEquals(a, b) {
    if (a.length != b.length)
        throw new AssertionError("Array lengths do not match");
    
    for (var i = 0; i < a.length; i++) {
        if (a[i] !== b[i])
            throw new AssertionError("Elements do not match");
    }
}

const assertVisitsAll = (testFunction, expectedOutput) => {
    const visited = [];
    testFunction(value => visited.push(value));
    assert(visited.length === expectedOutput.length);
    expectedOutput.forEach((value, i) => assert(visited[i] === value));
};

/**
 * Check whether the difference between two numbers is less than 0.000001.
 * @param {Number} a First number
 * @param {Number} b Second number
 */
function isClose(a, b) {
    return Math.abs(a - b) < 0.000001;
}

/**
 * Quick and dirty deep equals method.
 * @param {*} a First value
 * @param {*} b Second value
 */
function assertDeepEquals(a, b) {
    assert(deepEquals(a, b));
}

function deepEquals(a, b) {
    if (Array.isArray(a))
        return Array.isArray(b) && deepArrayEquals(a, b);
    if (typeof a === "object")
        return typeof b === "object" && deepObjectEquals(a, b);
    return a === b;
}

function deepArrayEquals(a, b) {
    if (a.length !== b.length)
        return false;
    for (let i = 0; i < a.length; ++i) {
        if (!deepEquals(a[i], b[i]))
            return false;
    }
    return true;
}

function deepObjectEquals(a, b) {
    if (a === null)
        return b === null;
    for (let key of Reflect.ownKeys(a)) {
        if (!deepEquals(a[key], b[key]))
            return false;
    }
    return true;
}