1/* 2 * Copyright (c) 2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16import { DeepTypeUtils } from './DeepTypeUtils'; 17import { AnyType } from '../../types/common'; 18import { AssertResult } from '../../modal/assertModel'; 19 20export function assertDeepEquals(actualValue: AnyType, expected: AnyType[]): AssertResult { 21 let result = eq(actualValue, expected[0]); 22 let msg = result ? '' : logMsg(actualValue, expected[0]); 23 return { 24 pass: result, 25 message: msg, 26 }; 27} 28 29/** 30 * 获取失败显示日志 31 * @param actualValue 实际对象 32 * @param expected 期待比较对象 33 */ 34function logMsg(actualValue: AnyType, expected: AnyType): string { 35 const actualMsg = getObjectDesc(actualValue); 36 const expectMsg = getObjectDesc(expected); 37 return actualMsg + ' is not deep equal ' + expectMsg; 38} 39 40function getObjectDesc(value: AnyType) { 41 const aClassName = DeepTypeUtils.getType(value); 42 let message = ''; 43 if (aClassName === 'Function' || aClassName === 'Promise') { 44 message = 'actualValue ' + aClassName; 45 } else if (aClassName === 'Set') { 46 const typeValue = value as Set<AnyType>; 47 message = 'SET(' + typeValue.size + ') ' + JSON.stringify(Array.from(typeValue)); 48 } else if (aClassName === 'Map') { 49 const typeValue = value as Map<AnyType, AnyType>; 50 message = 'Map(' + typeValue.size + ') '; 51 } else if (aClassName === 'RegExp') { 52 message = 'RegExp ' + JSON.stringify((value as RegExp).source.replace('\\', '')); 53 } else if (aClassName === 'Bigint') { 54 message = 'Bigint ' + String(value as BigInt); 55 } else if (aClassName == 'Error') { 56 message = 'Error ' + (value as Error).message; 57 } else if (aClassName == 'ArrayBuffer') { 58 } else { 59 message = 'Bigint ' + JSON.stringify(value); 60 } 61 return message; 62} 63 64function eq(a: AnyType, b: AnyType): boolean { 65 const aType = DeepTypeUtils.getType(a); 66 const bType = DeepTypeUtils.getType(b); 67 68 if (aType !== bType) { 69 return false; 70 } 71 if (!a || !b) { 72 return a === b; 73 } 74 75 if (aType === 'Date') { 76 return eqOfDate(a as Date, b as Date); 77 } else if (aType === 'ArrayBuffer') { 78 return eqOfArrayBuffer(a as ArrayBuffer, b as ArrayBuffer); 79 } else if (aType === 'RegExp') { 80 return eqOfRegExp(a as RegExp, b as RegExp); 81 } else if (aType === 'Error') { 82 return eqOfError(a as Error, b as Error); 83 } else if (aType === 'Map') { 84 return eqOfMap(a as Map<AnyType, AnyType>, b as Map<AnyType, AnyType>); 85 } else if (aType === 'Set') { 86 return eqOfSet(a as Set<AnyType>, b as Set<AnyType>); 87 } else if (aType === 'Array') { 88 return eqOfArray(a as Array<AnyType>, b as Array<AnyType>); 89 } else if (aType === 'Object') { 90 return eqOfObj(a as object, b as object); 91 } else { 92 return a === b; 93 } 94} 95 96function eqOfDate(a: Date, b: Date): boolean { 97 return a.getTime() === b.getTime(); 98} 99 100function eqOfArrayBuffer(a: ArrayBuffer, b: ArrayBuffer): boolean { 101 if (a.byteLength !== b.byteLength) { 102 return false; 103 } 104 const view1 = new Uint8Array(a); 105 const view2 = new Uint8Array(b); 106 for (let i = 0; i < view1.length; i++) { 107 if (view1[i] !== view2[i]) { 108 return false; 109 } 110 } 111 return true; 112} 113 114function eqOfRegExp(a: RegExp, b: RegExp): boolean { 115 return a.source === b.source && a.global === b.global && a.multiline === b.multiline && a.ignoreCase === b.ignoreCase; 116} 117 118function eqOfError(a: Error, b: Error): boolean { 119 return a.message === b.message; 120} 121 122function eqOfMap<k, v>(a: Map<k, v>, b: Map<k, v>): boolean { 123 if (a.size !== b.size) { 124 return false; 125 } 126 for (const key of a.keys()) { 127 if (!b.has(key) || !eq(b.get(key) as AnyType, a.get(key) as AnyType)) { 128 return false; 129 } 130 } 131 132 return true; 133} 134 135function eqOfSet<T>(a: Set<T>, b: Set<T>): boolean { 136 if (a.size !== b.size) { 137 return false; 138 } 139 for (const item of a) { 140 if (!b.has(item)) { 141 return false; 142 } 143 } 144 return true; 145} 146 147function eqOfArray<T>(a: Array<T>, b: Array<T>): boolean { 148 const aLength = a.length; 149 const bLength = b.length; 150 if (aLength !== bLength) { 151 return false; 152 } 153 for (let i = 0; i < aLength; i++) { 154 if (!eq(a[i] as AnyType, b[i] as AnyType)) { 155 return false; 156 } 157 } 158 return true; 159} 160 161function eqOfObj(a: object, b: object): boolean { 162 const aKeys = DeepTypeUtils.getAllKeys(a); 163 const bKeys = DeepTypeUtils.getAllKeys(b); 164 if (aKeys.length !== bKeys.length) { 165 return false; 166 } 167 for (const entries of Object.entries(a)) { 168 if(!entries) { 169 continue 170 } 171 const aKey = entries[0] 172 const aValue = entries[1] 173 if(!bKeys.includes(aKey)) { 174 return false 175 } 176 if(!eq(aValue as object, Reflect.get(b, aKey) as object)) { 177 return false 178 } 179 } 180 return true; 181} 182