summaryrefslogtreecommitdiff
path: root/ext/node/polyfills/_util/std_asserts.ts
diff options
context:
space:
mode:
authorBartek IwaƄczuk <biwanczuk@gmail.com>2023-02-14 17:38:45 +0100
committerGitHub <noreply@github.com>2023-02-14 17:38:45 +0100
commitd47147fb6ad229b1c039aff9d0959b6e281f4df5 (patch)
tree6e9e790f2b9bc71b5f0c9c7e64b95cae31579d58 /ext/node/polyfills/_util/std_asserts.ts
parent1d00bbe47e2ca14e2d2151518e02b2324461a065 (diff)
feat(ext/node): embed std/node into the snapshot (#17724)
This commit moves "deno_std/node" in "ext/node" crate. The code is transpiled and snapshotted during the build process. During the first pass a minimal amount of work was done to create the snapshot, a lot of code in "ext/node" depends on presence of "Deno" global. This code will be gradually fixed in the follow up PRs to migrate it to import relevant APIs from "internal:" modules. Currently the code from snapshot is not used in any way, and all Node/npm compatibility still uses code from "https://deno.land/std/node" (or from the location specified by "DENO_NODE_COMPAT_URL"). This will also be handled in a follow up PRs. --------- Co-authored-by: crowlkats <crowlkats@toaxl.com> Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com> Co-authored-by: Yoshiya Hinosawa <stibium121@gmail.com>
Diffstat (limited to 'ext/node/polyfills/_util/std_asserts.ts')
-rw-r--r--ext/node/polyfills/_util/std_asserts.ts293
1 files changed, 293 insertions, 0 deletions
diff --git a/ext/node/polyfills/_util/std_asserts.ts b/ext/node/polyfills/_util/std_asserts.ts
new file mode 100644
index 000000000..8c4c80078
--- /dev/null
+++ b/ext/node/polyfills/_util/std_asserts.ts
@@ -0,0 +1,293 @@
+// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
+// vendored from std/testing/asserts.ts
+
+import { red } from "internal:deno_node/polyfills/_util/std_fmt_colors.ts";
+import {
+ buildMessage,
+ diff,
+ diffstr,
+} from "internal:deno_node/polyfills/_util/std_testing_diff.ts";
+
+/** Converts the input into a string. Objects, Sets and Maps are sorted so as to
+ * make tests less flaky */
+export function format(v: unknown): string {
+ // deno-lint-ignore no-explicit-any
+ const { Deno } = globalThis as any;
+ return typeof Deno?.inspect === "function"
+ ? Deno.inspect(v, {
+ depth: Infinity,
+ sorted: true,
+ trailingComma: true,
+ compact: false,
+ iterableLimit: Infinity,
+ // getters should be true in assertEquals.
+ getters: true,
+ })
+ : `"${String(v).replace(/(?=["\\])/g, "\\")}"`;
+}
+
+const CAN_NOT_DISPLAY = "[Cannot display]";
+
+export class AssertionError extends Error {
+ override name = "AssertionError";
+ constructor(message: string) {
+ super(message);
+ }
+}
+
+function isKeyedCollection(x: unknown): x is Set<unknown> {
+ return [Symbol.iterator, "size"].every((k) => k in (x as Set<unknown>));
+}
+
+/** Deep equality comparison used in assertions */
+export function equal(c: unknown, d: unknown): boolean {
+ const seen = new Map();
+ return (function compare(a: unknown, b: unknown): boolean {
+ // Have to render RegExp & Date for string comparison
+ // unless it's mistreated as object
+ if (
+ a &&
+ b &&
+ ((a instanceof RegExp && b instanceof RegExp) ||
+ (a instanceof URL && b instanceof URL))
+ ) {
+ return String(a) === String(b);
+ }
+ if (a instanceof Date && b instanceof Date) {
+ const aTime = a.getTime();
+ const bTime = b.getTime();
+ // Check for NaN equality manually since NaN is not
+ // equal to itself.
+ if (Number.isNaN(aTime) && Number.isNaN(bTime)) {
+ return true;
+ }
+ return aTime === bTime;
+ }
+ if (typeof a === "number" && typeof b === "number") {
+ return Number.isNaN(a) && Number.isNaN(b) || a === b;
+ }
+ if (Object.is(a, b)) {
+ return true;
+ }
+ if (a && typeof a === "object" && b && typeof b === "object") {
+ if (a && b && !constructorsEqual(a, b)) {
+ return false;
+ }
+ if (a instanceof WeakMap || b instanceof WeakMap) {
+ if (!(a instanceof WeakMap && b instanceof WeakMap)) return false;
+ throw new TypeError("cannot compare WeakMap instances");
+ }
+ if (a instanceof WeakSet || b instanceof WeakSet) {
+ if (!(a instanceof WeakSet && b instanceof WeakSet)) return false;
+ throw new TypeError("cannot compare WeakSet instances");
+ }
+ if (seen.get(a) === b) {
+ return true;
+ }
+ if (Object.keys(a || {}).length !== Object.keys(b || {}).length) {
+ return false;
+ }
+ seen.set(a, b);
+ if (isKeyedCollection(a) && isKeyedCollection(b)) {
+ if (a.size !== b.size) {
+ return false;
+ }
+
+ let unmatchedEntries = a.size;
+
+ for (const [aKey, aValue] of a.entries()) {
+ for (const [bKey, bValue] of b.entries()) {
+ /* Given that Map keys can be references, we need
+ * to ensure that they are also deeply equal */
+ if (
+ (aKey === aValue && bKey === bValue && compare(aKey, bKey)) ||
+ (compare(aKey, bKey) && compare(aValue, bValue))
+ ) {
+ unmatchedEntries--;
+ break;
+ }
+ }
+ }
+
+ return unmatchedEntries === 0;
+ }
+ const merged = { ...a, ...b };
+ for (
+ const key of [
+ ...Object.getOwnPropertyNames(merged),
+ ...Object.getOwnPropertySymbols(merged),
+ ]
+ ) {
+ type Key = keyof typeof merged;
+ if (!compare(a && a[key as Key], b && b[key as Key])) {
+ return false;
+ }
+ if (((key in a) && (!(key in b))) || ((key in b) && (!(key in a)))) {
+ return false;
+ }
+ }
+ if (a instanceof WeakRef || b instanceof WeakRef) {
+ if (!(a instanceof WeakRef && b instanceof WeakRef)) return false;
+ return compare(a.deref(), b.deref());
+ }
+ return true;
+ }
+ return false;
+ })(c, d);
+}
+
+// deno-lint-ignore ban-types
+function constructorsEqual(a: object, b: object) {
+ return a.constructor === b.constructor ||
+ a.constructor === Object && !b.constructor ||
+ !a.constructor && b.constructor === Object;
+}
+
+/** Make an assertion, error will be thrown if `expr` does not have truthy value. */
+export function assert(expr: unknown, msg = ""): asserts expr {
+ if (!expr) {
+ throw new AssertionError(msg);
+ }
+}
+
+/** Make an assertion that `actual` and `expected` are equal, deeply. If not
+ * deeply equal, then throw. */
+export function assertEquals<T>(actual: T, expected: T, msg?: string) {
+ if (equal(actual, expected)) {
+ return;
+ }
+ let message = "";
+ const actualString = format(actual);
+ const expectedString = format(expected);
+ try {
+ const stringDiff = (typeof actual === "string") &&
+ (typeof expected === "string");
+ const diffResult = stringDiff
+ ? diffstr(actual as string, expected as string)
+ : diff(actualString.split("\n"), expectedString.split("\n"));
+ const diffMsg = buildMessage(diffResult, { stringDiff }).join("\n");
+ message = `Values are not equal:\n${diffMsg}`;
+ } catch {
+ message = `\n${red(red(CAN_NOT_DISPLAY))} + \n\n`;
+ }
+ if (msg) {
+ message = msg;
+ }
+ throw new AssertionError(message);
+}
+
+/** Make an assertion that `actual` and `expected` are not equal, deeply.
+ * If not then throw. */
+export function assertNotEquals<T>(actual: T, expected: T, msg?: string) {
+ if (!equal(actual, expected)) {
+ return;
+ }
+ let actualString: string;
+ let expectedString: string;
+ try {
+ actualString = String(actual);
+ } catch {
+ actualString = "[Cannot display]";
+ }
+ try {
+ expectedString = String(expected);
+ } catch {
+ expectedString = "[Cannot display]";
+ }
+ if (!msg) {
+ msg = `actual: ${actualString} expected not to be: ${expectedString}`;
+ }
+ throw new AssertionError(msg);
+}
+
+/** Make an assertion that `actual` and `expected` are strictly equal. If
+ * not then throw. */
+export function assertStrictEquals<T>(
+ actual: unknown,
+ expected: T,
+ msg?: string,
+): asserts actual is T {
+ if (Object.is(actual, expected)) {
+ return;
+ }
+
+ let message: string;
+
+ if (msg) {
+ message = msg;
+ } else {
+ const actualString = format(actual);
+ const expectedString = format(expected);
+
+ if (actualString === expectedString) {
+ const withOffset = actualString
+ .split("\n")
+ .map((l) => ` ${l}`)
+ .join("\n");
+ message =
+ `Values have the same structure but are not reference-equal:\n\n${
+ red(withOffset)
+ }\n`;
+ } else {
+ try {
+ const stringDiff = (typeof actual === "string") &&
+ (typeof expected === "string");
+ const diffResult = stringDiff
+ ? diffstr(actual as string, expected as string)
+ : diff(actualString.split("\n"), expectedString.split("\n"));
+ const diffMsg = buildMessage(diffResult, { stringDiff }).join("\n");
+ message = `Values are not strictly equal:\n${diffMsg}`;
+ } catch {
+ message = `\n${CAN_NOT_DISPLAY} + \n\n`;
+ }
+ }
+ }
+
+ throw new AssertionError(message);
+}
+
+/** Make an assertion that `actual` and `expected` are not strictly equal.
+ * If the values are strictly equal then throw. */
+export function assertNotStrictEquals<T>(
+ actual: T,
+ expected: T,
+ msg?: string,
+) {
+ if (!Object.is(actual, expected)) {
+ return;
+ }
+
+ throw new AssertionError(
+ msg ?? `Expected "actual" to be strictly unequal to: ${format(actual)}\n`,
+ );
+}
+
+/** Make an assertion that `actual` match RegExp `expected`. If not
+ * then throw. */
+export function assertMatch(
+ actual: string,
+ expected: RegExp,
+ msg?: string,
+) {
+ if (!expected.test(actual)) {
+ if (!msg) {
+ msg = `actual: "${actual}" expected to match: "${expected}"`;
+ }
+ throw new AssertionError(msg);
+ }
+}
+
+/** Make an assertion that `actual` not match RegExp `expected`. If match
+ * then throw. */
+export function assertNotMatch(
+ actual: string,
+ expected: RegExp,
+ msg?: string,
+) {
+ if (expected.test(actual)) {
+ if (!msg) {
+ msg = `actual: "${actual}" expected to not match: "${expected}"`;
+ }
+ throw new AssertionError(msg);
+ }
+}