webxr_test_asserts.js (12553B)
1 // Utility assert functions. 2 // Relies on resources/testharness.js to be included before this file. 3 // Relies on webxr_test_constants.js to be included before this file. 4 // Relies on webxr_math_utils.js to be included before this file. 5 6 7 // |p1|, |p2| - objects with x, y, z, w components that are floating point numbers. 8 // Returns the name of mismatching component between p1 and p2. 9 const get_mismatched_component = function(p1, p2, epsilon = FLOAT_EPSILON) { 10 for (const v of ['x', 'y', 'z', 'w']) { 11 if (Math.abs(p1[v] - p2[v]) > epsilon) { 12 return v; 13 } 14 } 15 16 return null; 17 } 18 19 // Internal helper to find the mismatched component for orientations. 20 // Considers that q and -q represent the same orientation. 21 // Returns the component name ('x', 'y', 'z', 'w') of the first mismatch 22 // Returns null if q1 is approximately equal to q2 or -q2. 23 const get_mismatched_orientation_component = function(q1, q2, epsilon) { 24 const direct_mismatch = get_mismatched_component(q1, q2, epsilon); 25 // q1 == q2, for our purposes there is no mismatched component. 26 if (direct_mismatch === null) { 27 return null; 28 } 29 // q1 != q2, but check q1 vs -q2 30 const q2_flipped = flip_quaternion(q2); 31 if (get_mismatched_component(q1, q2_flipped, epsilon) === null) { 32 return null; 33 } 34 // q1 is not approx equal to q2 or -q2. 35 // both q2 and q2_flipped have non-null mismatchecs, but for ease of debugging 36 // return the mismatch from the direct comparison. 37 return direct_mismatch; 38 }; 39 40 // Internal helper to find the index of the first mismatched matrix element. 41 // Returns the index (0-15) or -1 if matrices are approximately equal. 42 const get_mismatched_matrix_element_index = function(m1, m2, epsilon, prefix="") { 43 assert_equals(m1.length, 16, prefix + "m1 must have length of 16"); 44 assert_equals(m2.length, 16, prefix + "m2 must have length of 16"); 45 46 for (let i = 0; i < 16; ++i) { 47 if (Math.abs(m1[i] - m2[i]) > epsilon) { 48 return i; 49 } 50 } 51 return -1; 52 } 53 54 // |p1|, |p2| - objects with x, y, z, w components that are floating point numbers. 55 // |epsilon| - float specifying precision 56 // |prefix| - string used as a prefix for logging 57 const assert_point_approx_equals = function(p1, p2, epsilon = FLOAT_EPSILON, prefix = "") { 58 if (p1 == null && p2 == null) { 59 return; 60 } 61 62 assert_not_equals(p1, null, prefix + "p1 must be non-null"); 63 assert_not_equals(p2, null, prefix + "p2 must be non-null"); 64 65 const mismatched_component = get_mismatched_component(p1, p2, epsilon); 66 67 if (mismatched_component !== null) { 68 let error_message = prefix + ' Point comparison failed.\n'; 69 error_message += ` p1: {x: ${p1.x}, y: ${p1.y}, z: ${p1.z}, w: ${p1.w}}\n`; 70 error_message += ` p2: {x: ${p2.x}, y: ${p2.y}, z: ${p2.z}, w: ${p2.w}}\n`; 71 error_message += ` Difference in component ${mismatched_component} exceeded the given epsilon.\n`; 72 assert_approx_equals(p2[mismatched_component], p1[mismatched_component], epsilon, error_message); 73 } 74 }; 75 76 // |p1|, |p2| - objects with x, y, z, w components that are floating point numbers. 77 // |epsilon| - float specifying precision 78 // |prefix| - string used as a prefix for logging 79 const assert_point_significantly_not_equals = function(p1, p2, epsilon = FLOAT_EPSILON, prefix = "") { 80 if (p1 == null || p2 == null) { 81 assert_not_equals(p2, p1, prefix + "p1 and p2 are both null"); 82 return; 83 } 84 85 const mismatched_component = get_mismatched_component(p1, p2, epsilon); 86 if (mismatched_component === null) { 87 let error_message = prefix + ' Point comparison failed (expected significant difference).\n'; 88 error_message += ` p1: {x: ${p1.x}, y: ${p1.y}, z: ${p1.z}, w: ${p1.w}}\n`; 89 error_message += ` p2: {x: ${p2.x}, y: ${p2.y}, z: ${p2.z}, w: ${p2.w}}\n`; 90 error_message += ` Difference in components did not exceeded the given epsilon.\n`; 91 assert_unreached(error_message); 92 } 93 }; 94 95 // |q1|, |q2| - objects with x, y, z, w components that are floating point numbers. 96 // |epsilon| - float specifying precision 97 // |prefix| - string used as a prefix for logging 98 const assert_orientation_approx_equals = function(q1, q2, epsilon = FLOAT_EPSILON, prefix = "") { 99 if (q1 == null && q2 == null) { 100 return; 101 } 102 103 assert_not_equals(q1, null, prefix + "q1 must be non-null"); 104 assert_not_equals(q2, null, prefix + "q2 must be non-null"); 105 106 const mismatched_component = get_mismatched_orientation_component(q1, q2, epsilon); 107 // q1 doesn't match neither q2 nor -q2, so it definitely does not represent the same orientations, 108 // log an assert failure. 109 if (mismatched_component !== null) { 110 let error_message = prefix + ' Orientation comparison failed.\n'; 111 error_message += ` q1: {x: ${q1.x}, y: ${q1.y}, z: ${q1.z}, w: ${q1.w}}\n`; 112 error_message += ` q2: {x: ${q2.x}, y: ${q2.y}, z: ${q2.z}, w: ${q2.w}}\n`; 113 error_message += ` Neither q2 nor -q2 are approximately equal to q1.\n`; 114 error_message += ` For q1 vs q2, difference in component ${mismatched_component} exceeded the given epsilon.\n`; 115 assert_approx_equals(q2[mismatched_component], q1[mismatched_component], epsilon, error_message); 116 } 117 }; 118 119 // |q1|, |q2| - objects with x, y, z, w components that are floating point numbers. 120 // |epsilon| - float specifying precision 121 // |prefix| - string used as a prefix for logging 122 const assert_orientation_significantly_not_equals = function(q1, q2, epsilon = FLOAT_EPSILON, prefix = "") { 123 if (q1 == null || q2 == null) { 124 assert_not_equals(q2, q1, prefix + "q1 and q2 are both null"); 125 return; 126 } 127 128 const mismatched_component = get_mismatched_orientation_component(q1, q2, epsilon); 129 // IF there is no mismatch q1 matches either q2 or -q2 (which are equivalent). 130 if (mismatched_component === null) { 131 let error_message = prefix + ' Orientation comparison failed (expected significant difference).\n'; 132 error_message += ` q1: {x: ${q1.x}, y: ${q1.y}, z: ${q1.z}, w: ${q1.w}}\n`; 133 error_message += ` q2: {x: ${q2.x}, y: ${q2.y}, z: ${q2.z}, w: ${q2.w}}\n`; 134 error_message += ` q1 is approximately equal to q2 or -q2, but a significant difference was expected.\n`; 135 assert_unreached(error_message); 136 } 137 }; 138 139 // |t1|, |t2| - objects containing position and orientation. 140 // |epsilon| - float specifying precision 141 // |prefix| - string used as a prefix for logging 142 const assert_transform_approx_equals = function(t1, t2, epsilon = FLOAT_EPSILON, prefix = "") { 143 if (t1 == null && t2 == null) { 144 return; 145 } 146 147 assert_not_equals(t1, null, prefix + "t1 must be non-null"); 148 assert_not_equals(t2, null, prefix + "t2 must be non-null"); 149 150 assert_point_approx_equals(t1.position, t2.position, epsilon, prefix + "positions must be equal"); 151 assert_orientation_approx_equals(t1.orientation, t2.orientation, epsilon, prefix + "orientations must be equal"); 152 }; 153 154 // |t1|, |t2| - objects containing position and orientation. 155 // |epsilon| - float specifying precision 156 // |prefix| - string used as a prefix for logging 157 const assert_transform_significantly_not_equals = function(t1, t2, epsilon = FLOAT_EPSILON, prefix = "") { 158 if (t1 == null || t2 == null) { 159 assert_not_equals(t1, t2, prefix + "t1 and t2 cannot both be null."); 160 return; 161 } 162 163 // It is okay for one of position or orientation to be equal; but not for both 164 // to be equal in order for the transform to not be equal. 165 let mismatched_position = get_mismatched_component(t1.position, t2.position, epsilon); 166 let mismatched_orientation = get_mismatched_orientation_component(t1.orientation, t2.orientation, epsilon); 167 if (mismatched_position === null && mismatched_orientation === null) { 168 assert_point_significantly_not_equals(t1.position, t2.position, epsilon, prefix + "positions must not be equal"); 169 assert_orientation_significantly_not_equals(t1.orientation, t2.orientation, epsilon, prefix + "orientations must not be equal"); 170 } 171 } 172 173 // |m1|, |m2| - arrays of floating point numbers 174 // |epsilon| - float specifying precision 175 // |prefix| - string used as a prefix for logging 176 const assert_matrix_approx_equals = function(m1, m2, epsilon = FLOAT_EPSILON, prefix = "") { 177 if (m1 == null && m2 == null) { 178 return; 179 } 180 181 assert_not_equals(m1, null, prefix + "m1 must be non-null"); 182 assert_not_equals(m2, null, prefix + "m2 must be non-null"); 183 184 assert_equals(m1.length, 16, prefix + "m1 must have length of 16"); 185 assert_equals(m2.length, 16, prefix + "m2 must have length of 16"); 186 187 const mismatched_element = get_mismatched_matrix_element_index(m1, m2, epsilon, prefix); 188 if (mismatched_element !== -1) { 189 let error_message = prefix + 'Matrix comparison failed.\n'; 190 error_message += ' Difference in element ' + mismatched_element + 191 ' exceeded the given epsilon.\n'; 192 193 error_message += ' Matrix 1: [' + m1.join(',') + ']\n'; 194 error_message += ' Matrix 2: [' + m2.join(',') + ']\n'; 195 196 assert_approx_equals( 197 m1[mismatched_element], m2[mismatched_element], epsilon, 198 error_message); 199 } 200 }; 201 202 // |m1|, |m2| - arrays of floating point numbers. 203 // |epsilon| - float specifying precision 204 // |prefix| - string used as a prefix for logging 205 const assert_matrix_significantly_not_equals = function(m1, m2, epsilon = FLOAT_EPSILON, prefix = "") { 206 if (m1 == null || m2 == null) { 207 assert_not_equals(m1, m2, prefix + "m1 and m2 must not both be null"); 208 return; 209 } 210 211 assert_equals(m1.length, 16, prefix + "m1 must have length of 16"); 212 assert_equals(m2.length, 16, prefix + "m2 must have length of 16"); 213 214 const mismatched_index = get_mismatched_matrix_element_index(m1, m2, epsilon, prefix); 215 if (mismatched_index === -1) { 216 let error_message = prefix + ' Matrix comparison failed (expected significant difference).\n'; 217 error_message += 218 ' No element exceeded the given epsilon ' + epsilon + '.\n'; 219 220 error_message += ' Matrix 1: [' + m1.join(',') + ']\n'; 221 error_message += ' Matrix 2: [' + m2.join(',') + ']\n'; 222 223 assert_unreached(error_message); 224 } 225 }; 226 227 // |r1|, |r2| - XRRay objects 228 // |epsilon| - float specifying precision 229 // |prefix| - string used as a prefix for logging 230 const assert_ray_approx_equals = function(r1, r2, epsilon = FLOAT_EPSILON, prefix = "") { 231 assert_point_approx_equals(r1.origin, r2.origin, epsilon, prefix + "origin:"); 232 assert_point_approx_equals(r1.direction, r2.direction, epsilon, prefix + "direction:"); 233 assert_matrix_approx_equals(r1.matrix, r2.matrix, epsilon, prefix + "matrix:"); 234 }; 235 236 // |actualBuffer|, |expectedBuffer| - ArrayBuffer objects 237 // |message| - string used as a prefix for logging 238 const assert_array_buffer_equals = function(actualBuffer, expectedBuffer, message = "ArrayBuffers should be equal") { 239 if (actualBuffer == null && expectedBuffer == null) { 240 return; 241 } 242 243 assert_not_equals(actualBuffer, null, message + " (actualBuffer is null)"); 244 assert_not_equals(expectedBuffer, null, message + " (expectedBuffer is null)"); 245 246 assert_equals(actualBuffer.byteLength, expectedBuffer.byteLength, message + " (byteLength mismatch)"); 247 248 const actualView = new Uint8Array(actualBuffer); 249 const expectedView = new Uint8Array(expectedBuffer); 250 251 for (let i = 0; i < actualView.length; i++) { 252 // Check each byte. If a mismatch is found, assert_equals will fail the test 253 // and provide a detailed message. 254 if (actualView[i] !== expectedView[i]) { 255 assert_equals(actualView[i], expectedView[i], `${message} (mismatch at byte ${i})`); 256 return; 257 } 258 } 259 // If the loop completes without an assert_equals failure, the buffers are identical. 260 }; 261 262 // |actualBuffer|, |expectedBuffer| - ArrayBuffer objects 263 // |message| - string used as a prefix for logging 264 const assert_array_buffer_not_equals = function(actualBuffer, expectedBuffer, message = "ArrayBuffers should not be equal") { 265 if (actualBuffer == null || expectedBuffer == null) { 266 assert_not_equals(actualBuffer, expectedBuffer, message+ " (actualBuffer and expectedBuffer both null)"); 267 return; 268 } 269 270 assert_not_equals(actualBuffer, null, message + " (actualBuffer is null)"); 271 assert_not_equals(expectedBuffer, null, message + " (expectedBuffer is null)"); 272 273 if (actualBuffer.byteLength !== expectedBuffer.byteLength) { 274 return; 275 } 276 277 const actualView = new Uint8Array(actualBuffer); 278 const expectedView = new Uint8Array(expectedBuffer); 279 280 for (let i = 0; i < actualView.length; i++) { 281 // Once one byte is different, then the two buffers aren't the same and we 282 // can return. 283 if (actualView[i] !== expectedView[i]) { 284 return; 285 } 286 } 287 assert_unreached(`${message} (buffers are identical`); 288 };