Keep exact wildcard, whitespace and aligned-mask validation in the shared domain refinement. Advertise descriptive string inputs instead of repeating regexes, and remove discarded byte/mask array materialization. Preserve caller spelling and error precedence; cover large public stdio queries, provider-boundary parsing and connection recovery. Fixes #5257
134 lines
3.8 KiB
TypeScript
134 lines
3.8 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { z } from "zod";
|
|
import {
|
|
bytePatternFields,
|
|
validateBytePattern,
|
|
byteSearchResultSchema,
|
|
} from "./bytePattern.js";
|
|
|
|
const patternSchema = z
|
|
.strictObject(bytePatternFields)
|
|
.superRefine(validateBytePattern);
|
|
|
|
it.each(["0x20", "overlay:0x10", "invalid"])(
|
|
"rejects a match outside reported searched coverage: %s",
|
|
(address) => {
|
|
const page = {
|
|
matches: [
|
|
{ address, segment: ".text", file_offset: null, procedure: null },
|
|
],
|
|
searched: {
|
|
blocks: [{ segment: ".text", start: "0x10", end: "0x1f", bytes: 16 }],
|
|
bytes: 16,
|
|
skipped_uninitialized: [],
|
|
skipped_not_loaded: [],
|
|
},
|
|
complete: true,
|
|
next_cursor: null,
|
|
};
|
|
expect(byteSearchResultSchema.safeParse(page).success).toBe(false);
|
|
page.matches[0]!.address = "0x10";
|
|
expect(byteSearchResultSchema.safeParse(page).success).toBe(true);
|
|
page.matches[0]!.segment = ".bss";
|
|
expect(byteSearchResultSchema.safeParse(page).success).toBe(false);
|
|
},
|
|
);
|
|
|
|
it.each(["", "overlay:"])(
|
|
"accepts matches at both inclusive coverage boundaries in address space %s",
|
|
(space) => {
|
|
const start = `${space}0x10`;
|
|
const end = `${space}0x1f`;
|
|
expect(
|
|
byteSearchResultSchema.safeParse({
|
|
matches: [start, end].map((address) => ({
|
|
address,
|
|
segment: ".text",
|
|
file_offset: null,
|
|
procedure: null,
|
|
})),
|
|
searched: {
|
|
blocks: [{ segment: ".text", start, end, bytes: 16 }],
|
|
bytes: 16,
|
|
skipped_uninitialized: [],
|
|
skipped_not_loaded: [],
|
|
},
|
|
complete: true,
|
|
next_cursor: null,
|
|
}).success,
|
|
).toBe(true);
|
|
},
|
|
);
|
|
|
|
describe("masked byte patterns", () => {
|
|
it("preserves whole-byte wildcards and combines explicit bit masks", () => {
|
|
for (const input of [
|
|
{ pattern: "48 8b\t??89", mask: "ff f0 ff 0f" },
|
|
{ pattern: "Aa\u00a000" },
|
|
{ pattern: "00" },
|
|
{ pattern: "????aa", mask: "000001" },
|
|
])
|
|
expect(patternSchema.parse(input)).toEqual(input);
|
|
});
|
|
|
|
it.each([
|
|
["", undefined, "pattern"],
|
|
["4", undefined, "pattern"],
|
|
["4?", undefined, "pattern"],
|
|
["gg", undefined, "pattern"],
|
|
["?? ??", undefined, "pattern"],
|
|
["ff", "00", "pattern"],
|
|
["ffff", "ff", "mask"],
|
|
["ff", "??", "mask"],
|
|
["aa ??", "00 ff", "pattern"],
|
|
["??", "0000", "mask"],
|
|
["aa", "00gg", "mask"],
|
|
["??gg", "00gg", "pattern"],
|
|
])("rejects invalid pattern %s with typed issues", (pattern, mask, field) => {
|
|
const parsed = patternSchema.safeParse({ pattern, mask });
|
|
expect(parsed.success).toBe(false);
|
|
if (parsed.success) throw new Error("Expected a rejected pattern");
|
|
expect(parsed.error.issues).toMatchObject([
|
|
{ path: [field], code: "custom" },
|
|
]);
|
|
});
|
|
|
|
it("accepts ECMAScript whitespace only between complete byte pairs", () => {
|
|
const whitespace = [
|
|
"\t",
|
|
"\n",
|
|
"\v",
|
|
"\f",
|
|
"\r",
|
|
" ",
|
|
"\u00a0",
|
|
"\u1680",
|
|
...Array.from({ length: 11 }, (_, i) => String.fromCharCode(0x2000 + i)),
|
|
"\u2028",
|
|
"\u2029",
|
|
"\u202f",
|
|
"\u205f",
|
|
"\u3000",
|
|
"\ufeff",
|
|
];
|
|
for (const separator of whitespace) {
|
|
expect(
|
|
patternSchema.safeParse({
|
|
pattern: `aa${separator}??`,
|
|
mask: `ff${separator}ff`,
|
|
}).success,
|
|
).toBe(true);
|
|
for (const pattern of [
|
|
`${separator}aa`,
|
|
`aa${separator}`,
|
|
`a${separator}a`,
|
|
])
|
|
expect(patternSchema.safeParse({ pattern }).success).toBe(false);
|
|
}
|
|
for (const separator of ["\u0085", "\u180e", "\u200b", "\u200c", "\u200d"])
|
|
expect(
|
|
patternSchema.safeParse({ pattern: `aa${separator}bb` }).success,
|
|
).toBe(false);
|
|
});
|
|
});
|