• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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