353 lines
10 KiB
TypeScript
353 lines
10 KiB
TypeScript
import { expect } from 'vitest';
|
|
import { Vector4 } from '../lib/classes/Vector4';
|
|
import { Quaternion } from '../lib/classes/Quaternion';
|
|
import { LLSDReal } from '../lib/classes/llsd/LLSDReal';
|
|
import { LLSDURI } from '../lib/classes/llsd/LLSDURI';
|
|
import { Vector3 } from '../lib/classes/Vector3';
|
|
import { Vector2 } from '../lib/classes/Vector2';
|
|
import { UUID } from '../lib';
|
|
import { LLSDInteger } from '../lib/classes/llsd/LLSDInteger';
|
|
import { isInt } from 'validator';
|
|
|
|
function compareObject(path: string, one: unknown, two: unknown, visited = new Set<unknown>())
|
|
{
|
|
if (one === null)
|
|
{
|
|
expect(one, path + ' (null)').toBe(two);
|
|
if (two !== null)
|
|
{
|
|
console.error(path + ': Mismatch - One is NULL, two is ' + typeof two);
|
|
return;
|
|
}
|
|
return;
|
|
}
|
|
else if (typeof one === 'string')
|
|
{
|
|
expect(one, path + ' (string)').toBe(two);
|
|
return;
|
|
}
|
|
else if (typeof one === 'number')
|
|
{
|
|
if (isInt(String(one)) && isInt(String(two)))
|
|
{
|
|
expect(one, path + ' (number)').toBe(two);
|
|
}
|
|
else if (isNaN(one))
|
|
{
|
|
expect(isNaN(Number(two)), path + ' (number)').toBeTruthy;
|
|
}
|
|
else
|
|
{
|
|
expect(one, path + ' (number)').toBeCloseTo(Number(two), 6);
|
|
}
|
|
}
|
|
else if (typeof one === 'boolean')
|
|
{
|
|
expect(typeof two, path + ' (Boolean)').toBe('boolean');
|
|
expect(one, path + ' (Boolean)').toBe(two);
|
|
}
|
|
else if (one instanceof Buffer)
|
|
{
|
|
expect(two, path).toBeInstanceOf(Buffer);
|
|
if (two instanceof Buffer)
|
|
{
|
|
expect(one.length, path + ' (Buffer)').toBe(two.length);
|
|
expect(one.toString('base64'), path + ' (Buffer)').toBe(two.toString('base64'));
|
|
}
|
|
}
|
|
else if (one instanceof Date)
|
|
{
|
|
expect(two, path + ' (Date)').toBeInstanceOf(Date);
|
|
if (two instanceof Date)
|
|
{
|
|
if (isNaN(one.getTime()))
|
|
{
|
|
expect(one.getTime(), path + ' (Date)').toBe(two.getTime());
|
|
}
|
|
else
|
|
{
|
|
expect(one.getTime(), path + ' (Date)').toBeGreaterThanOrEqual(two.getTime() - 1);
|
|
expect(one.getTime(), path + ' (Date)').toBeLessThanOrEqual(two.getTime() + 1);
|
|
}
|
|
}
|
|
}
|
|
else if (Array.isArray(one))
|
|
{
|
|
expect(Array.isArray(two), path).toBeTruthy();
|
|
if (Array.isArray(two))
|
|
{
|
|
expect(one.length, path).toBe(two.length);
|
|
for (let x = 0; x < one.length; x++)
|
|
{
|
|
compareObject(path + '[' + String(x) + ']', one[x], two[x], visited);
|
|
}
|
|
}
|
|
}
|
|
else if (one instanceof Map)
|
|
{
|
|
expect(two).toBeInstanceOf(Map);
|
|
if (two instanceof Map)
|
|
{
|
|
for (const k of one.keys())
|
|
{
|
|
const v = one.get(k);
|
|
const v2 = two.get(k);
|
|
compareObject(path + '<' + String(v) + '>', v, v2, visited);
|
|
}
|
|
for (const k of two.keys())
|
|
{
|
|
expect(one.has(k)).toBeTruthy();
|
|
}
|
|
}
|
|
}
|
|
else if (one instanceof LLSDInteger)
|
|
{
|
|
expect(two, path).toBeInstanceOf(LLSDInteger);
|
|
if (two instanceof LLSDInteger)
|
|
{
|
|
expect(one.valueOf(), path + ' (LLSDInteger)').toBe(two.valueOf());
|
|
}
|
|
}
|
|
else if (one instanceof LLSDReal)
|
|
{
|
|
expect(two, path).toBeInstanceOf(LLSDReal);
|
|
if (two instanceof LLSDReal)
|
|
{
|
|
const oneVal = one.valueOf();
|
|
const twoVal = two.valueOf();
|
|
if (isNaN(oneVal))
|
|
{
|
|
expect(isNaN(twoVal), path + ' (LLSDReal)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(oneVal, path + ' (LLSDReal)').toBeCloseTo(twoVal);
|
|
}
|
|
}
|
|
}
|
|
else if (one instanceof LLSDURI)
|
|
{
|
|
expect(two, path).toBeInstanceOf(LLSDURI);
|
|
if (two instanceof LLSDURI)
|
|
{
|
|
expect(one.valueOf(), path + ' (LLSDURI)').toBe(two.valueOf());
|
|
}
|
|
}
|
|
/*else if (one instanceof Matrix4)
|
|
{
|
|
expect(two).toBeInstanceOf(Matrix4);
|
|
if (two instanceof Matrix4)
|
|
{
|
|
const arr1 = one.all();
|
|
const arr2 = two.all();
|
|
expect(arr1.length, path + ' (Matrix4)').toBe(arr2.length);
|
|
for(let x = 0; x < arr1.length; x++)
|
|
{
|
|
if (isNaN(arr1[x]))
|
|
{
|
|
expect(isNaN(arr2[x]), path + ' (Matrix4)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(arr1[x], path + ' (Matrix4)').toBeCloseTo(arr2[x], 6);
|
|
}
|
|
}
|
|
}
|
|
}*/
|
|
else if (one instanceof Quaternion)
|
|
{
|
|
expect(two).toBeInstanceOf(Quaternion);
|
|
if (two instanceof Quaternion)
|
|
{
|
|
if (isNaN(one.x))
|
|
{
|
|
expect(isNaN(two.x), path + ' (Quaternion)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.x, path + ' (Quaternion)').toBeCloseTo(two.x, 6);
|
|
}
|
|
if (isNaN(one.y))
|
|
{
|
|
expect(isNaN(two.y), path + ' (Quaternion)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.y, path + ' (Quaternion)').toBeCloseTo(two.y, 6);
|
|
}
|
|
if (isNaN(one.z))
|
|
{
|
|
expect(isNaN(two.z), path + ' (Quaternion)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.z, path + ' (Quaternion)').toBeCloseTo(two.z, 6);
|
|
}
|
|
if (isNaN(one.w))
|
|
{
|
|
expect(isNaN(two.w), path + ' (Quaternion)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.w, path + ' (Quaternion)').toBeCloseTo(two.w, 6);
|
|
}
|
|
}
|
|
}
|
|
else if (one instanceof Vector4)
|
|
{
|
|
expect(two).toBeInstanceOf(Vector4);
|
|
if (two instanceof Vector4)
|
|
{
|
|
if (isNaN(one.x))
|
|
{
|
|
expect(isNaN(two.x), path + ' (Vector4)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.x, path + ' (Vector4)').toBeCloseTo(two.x, 6);
|
|
}
|
|
if (isNaN(one.y))
|
|
{
|
|
expect(isNaN(two.y), path + ' (Vector4)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.y, path + ' (Vector4)').toBeCloseTo(two.y, 6);
|
|
}
|
|
if (isNaN(one.z))
|
|
{
|
|
expect(isNaN(two.z), path + ' (Vector4)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.z, path + ' (Vector4)').toBeCloseTo(two.z, 6);
|
|
}
|
|
if (isNaN(one.w))
|
|
{
|
|
expect(isNaN(two.w), path + ' (Vector4)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.w, path + ' (Vector4)').toBeCloseTo(two.w, 6);
|
|
}
|
|
}
|
|
}
|
|
else if (one instanceof Vector3)
|
|
{
|
|
expect(two).toBeInstanceOf(Vector3);
|
|
if (two instanceof Vector3)
|
|
{
|
|
if (isNaN(one.x))
|
|
{
|
|
expect(isNaN(two.x), path + ' (Vector3)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.x, path + ' (Vector3)').toBeCloseTo(two.x, 6);
|
|
}
|
|
if (isNaN(one.y))
|
|
{
|
|
expect(isNaN(two.y), path + ' (Vector3)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.y, path + ' (Vector3)').toBeCloseTo(two.y, 6);
|
|
}
|
|
if (isNaN(one.z))
|
|
{
|
|
expect(isNaN(two.z), path + ' (Vector3)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.z, path + ' (Vector3)').toBeCloseTo(two.z, 6);
|
|
}
|
|
}
|
|
}
|
|
else if (one instanceof Vector2)
|
|
{
|
|
expect(two).toBeInstanceOf(Vector2);
|
|
if (two instanceof Vector2)
|
|
{
|
|
if (isNaN(one.x))
|
|
{
|
|
expect(isNaN(two.x), path + ' (Vector2)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.x, path + ' (Vector2)').toBeCloseTo(two.x, 6);
|
|
}
|
|
if (isNaN(one.y))
|
|
{
|
|
expect(isNaN(two.y), path + ' (Vector2)').toBeTruthy();
|
|
}
|
|
else
|
|
{
|
|
expect(one.y, path + ' (Vector2)').toBeCloseTo(two.y, 6);
|
|
}
|
|
}
|
|
}
|
|
else if (one instanceof UUID)
|
|
{
|
|
expect(two).toBeInstanceOf(UUID);
|
|
if (two instanceof UUID)
|
|
{
|
|
expect(one.toString(), path + ' (UUID)').toBe(two.toString());
|
|
}
|
|
}
|
|
else if (typeof one === 'object')
|
|
{
|
|
if (visited.has(one))
|
|
{
|
|
return;
|
|
}
|
|
visited.add(one);
|
|
|
|
expect(typeof two, path + ' (object)').toBe('object');
|
|
expect(two, path + ' (object)').not.toBeNull();
|
|
if (one.constructor.name !== 'Object' && one.constructor.name !== 'LLMesh' && one.constructor.name !== 'LLSettings')
|
|
{
|
|
throw new Error('Unhandled object ' + one.constructor.name);
|
|
}
|
|
if (typeof two === 'object' && two !== null)
|
|
{
|
|
expect(one.constructor.name, path + ' (object)').toBe(two.constructor.name);
|
|
|
|
const keys = Object.keys(one);
|
|
for (const k of keys)
|
|
{
|
|
const subPath = path + '[\'' + k + '\']';
|
|
if ((one as any)[k] !== undefined)
|
|
{
|
|
expect((two as any)[k], subPath + ' (object)').toBeDefined();
|
|
compareObject(subPath, (one as any)[k], (two as any)[k], visited);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
throw new Error('Unknown object type: ' + one);
|
|
}
|
|
}
|
|
|
|
export function toDeeplyMatch(received: unknown, expected: unknown)
|
|
{
|
|
const visited = new Set<unknown>();
|
|
|
|
try
|
|
{
|
|
compareObject('', received, expected, visited);
|
|
return {
|
|
pass: true,
|
|
message: () => `Expected objects to not deeply match, but they did.`,
|
|
};
|
|
}
|
|
catch (error: any)
|
|
{
|
|
return {
|
|
pass: false,
|
|
message: () => error.message || `Objects do not deeply match.`,
|
|
};
|
|
}
|
|
}
|