serialize_child.html (7763B)
1 <!doctype html> 2 <script src="/resources/channel.sub.js"></script> 3 <script src="serialize-data.js"></script> 4 <script> 5 6 let lastData; 7 8 // Hack: these will be converted into testharness AssertionError instances in the test 9 // This means they will be treated identically to asserts raised by `assert_` in the harness 10 // In the long term we want to be able to use parts of testharness.js directly in remote 11 // contexts and automatically send the results over a channel. 12 function AssertionError(message) { 13 this.message = message; 14 } 15 AssertionError.prototype = Object.create(Error.prototype); 16 17 function compareResult(name, actual) { 18 let obj = objects[name]; 19 // If there's an output property use that, otherwise assume the output is equal to the input 20 let expected = obj.hasOwnProperty("output") ? obj.output : obj.input; 21 seen = new Set(); 22 try { 23 compareValue(actual, expected, seen); 24 } catch(e) { 25 throw new AssertionError(e.message); 26 } 27 return true; 28 } 29 30 function compareValue(actualValue, expectedValue, seen) { 31 let seenActual; 32 if (typeof actualValue != typeof expectedValue) { 33 throw new Error(`Types differ, expected ${typeof expectedValue}, got ${typeof actualValue}`); 34 } 35 if (["undefined", "string", "boolean", "number", "bigint"].includes(typeof expectedValue) || 36 actualValue === null) { 37 if (!Object.is(actualValue, expectedValue)) { 38 throw new Error(`Expected ${typeof expected} ${expected}, got ${actual}`); 39 } 40 return; 41 } 42 43 if (expectedValue.constructor && actualValue.constructor && expectedValue.constructor.name !== actualValue.constructor.name) { 44 throw new Error(`Constructors differ, expected ${expectedValue.constructor.name}, got ${actualValue.constructor.name}`); 45 } 46 if (expectedValue.constructor && expectedValue.constructor.name === "SendChannel") { 47 if (expectedValue.uuid !== actualValue.uuid) { 48 throw new Error(`SendChannels differ, expected uuid ${expectedValue.uuid}, got ${actualValue.uuid}`); 49 } 50 } 51 else if (expectedValue.constructor && expectedValue.constructor.name === "RegExp") { 52 if (expectedValue.source !== actualValue.source || 53 expectedValue.flags !== actualValue.flags) { 54 throw new Error(`RegExps differ, expected ${expectedValue}, got ${actualValue}`); 55 } 56 } else if (expectedValue.constructor && expectedValue.constructor.name == "Date") { 57 if (expectedValue.valueOf() !== actualValue.valueOf()) { 58 throw new Error(`Dates differ, expected ${expectedValue.valueOf()} (${expectedValue.toDateString()}), ` 59 `got ${actualValue.valueOf()} (${actualValue.toDateString()})`); 60 } 61 } else if (expectedValue instanceof Error) { 62 if (expectedValue.message !== actualValue.message || 63 expectedValue.lineNumber !== actualValue.lineNumber || 64 expectedValue.columnNumber !== actualValue.columnNumber || 65 expectedValue.fileName !== actualValue.fileName) { 66 throw new Error(`Errors differ, expected ${expectedValue}, got ${actualValue}`); 67 } 68 } else if (Array.isArray(expectedValue)) { 69 seenActual = seen.has(actualValue); 70 seenExpected = seen.has(expectedValue) 71 if (seenActual && seenExpected) { 72 return; 73 } else if (seenExpected && !seenActual) { 74 throw new Error(`Expected cyclic array`); 75 } else if (!seenExpected && seenActual) { 76 throw new Error(`Got unexpected cyclic array`); 77 } 78 seen.add(actualValue); 79 seen.add(expectedValue); 80 81 if (actualValue.length !== expectedValue.length) { 82 throw new Error(`Array lengths differ, expected ${expectedValue.length}, got ${actualValue.length}`); 83 } 84 for (let i=0; i<actualValue.length; i++) { 85 compareValue(actualValue[i], expectedValue[i], seen); 86 } 87 } else if (expectedValue.constructor && expectedValue.constructor.name === "Set") { 88 seenActual = seen.has(actualValue); 89 seenExpected = seen.has(expectedValue) 90 if (seenActual && seenExpected) { 91 return; 92 } else if (seenExpected && !seenActual) { 93 throw new Error(`Expected cyclic set`); 94 } else if (!seenExpected && seenActual) { 95 throw new Error(`Got unexpected cyclic set`); 96 } 97 seen.add(actualValue); 98 seen.add(expectedValue); 99 100 101 if (actualValue.size !== expectedValue.size) { 102 throw new Error(`Set sizes differ, expected ${expectedValue.size}, got ${actualValue.size}`); 103 } 104 // For an arbitary set it's complex to check if two sets are equivalent, since 105 // we'd need to compare every object in one set with every object in the 106 // other set, so we end up with quadratic complexity. Instead, just support sets 107 // containing primitives and rely on the other tests for correct handling of 108 // objects. 109 for (let entry of expectedValue) { 110 if (["undefined", "string", "boolean", "number", "bigint"].includes(typeof entry) || entry === null) { 111 if(!actualValue.has(entry)) { 112 throw new Error(`Set missing entry, expected ${entry}`); 113 } 114 } else { 115 throw new Error(`Can't compare non-primitive value ${entry} inside sets`); 116 } 117 } 118 } else if (expectedValue.constructor && expectedValue.constructor.name === "Map") { 119 seenActual = seen.has(actualValue); 120 seenExpected = seen.has(expectedValue) 121 if (seenActual && seenExpected) { 122 return; 123 } else if (seenExpected && !seenActual) { 124 throw new Error(`Expected cyclic map`); 125 } else if (!seenExpected && seenActual) { 126 throw new Error(`Got unexpected cyclic map`); 127 } 128 seen.add(actualValue); 129 seen.add(expectedValue); 130 131 if (actualValue.size !== expectedValue.size) { 132 throw new Error(`Map sizes differ, expected ${expectedValue.size}, got ${actualValue.size}`); 133 } 134 // So for a set we can't really check if the values are the same 135 // except where they're primitives 136 for (let [key, value] of expectedValue.entries()) { 137 if(!actualValue.has(key)) { 138 throw new Error(`Map missing key, expected key ${key} with value ${value}`); 139 } 140 compareValue(actualValue.get(key), value, seen); 141 } 142 } else { 143 seenActual = seen.has(actualValue); 144 seenExpected = seen.has(expectedValue) 145 if (seenActual && seenExpected) { 146 return; 147 } else if (seenExpected && !seenActual) { 148 throw new Error(`Expected cyclic object`); 149 } else if (!seenExpected && seenActual) { 150 throw new Error(`Got unexpected cyclic object`); 151 } 152 seen.add(actualValue); 153 seen.add(expectedValue); 154 155 156 // Compare as a general Object 157 let expectedEntries = Object.entries(expectedValue); 158 if (Object.keys(actualValue).length !== expectedEntries.length) { 159 throw new Error(`Object keys differ, expected [${Object.keys(expectedValue).join(",")}], got [${Object.keys(actualValue).join(",")}]`); 160 } 161 // So for a set we can't really check if the values are the same 162 // except where they're primitives 163 for (let [name, entry] of expectedEntries) { 164 if(!actualValue.hasOwnProperty(name)) { 165 throw new Error(`Object missing key ${name}`); 166 } 167 compareValue(actualValue[name], entry, seen); 168 } 169 } 170 } 171 172 ctx = start_global_channel(); 173 </script>