Skip to content

Commit 00d75f5

Browse files
Improve: NodeJS groundwork & corner-case tests (#151)
Co-authored-by: Mark Reed <mark@chattyr.com> Co-authored-by: Mark Reed <5108907+MarkReedZ@users.noreply.github.com> Co-authored-by: Ash Vardanian <1983160+ashvardanian@users.noreply.github.com>
1 parent 53623d5 commit 00d75f5

File tree

1 file changed

+168
-0
lines changed

1 file changed

+168
-0
lines changed

scripts/test.js

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,3 +214,171 @@ test("Zero-Copy Performance Test", () => {
214214
const byteResult = stringzilla.findByte(largeHaystack, 97); // 'a'
215215
assert.strictEqual(byteResult, 0n);
216216
});
217+
218+
test("Edge Cases - Empty Buffers", () => {
219+
const haystack = Buffer.from("hello world");
220+
const empty = Buffer.alloc(0);
221+
222+
// Finding empty in non-empty should return 0
223+
assert.strictEqual(stringzilla.find(haystack, empty), 0n);
224+
assert.strictEqual(stringzilla.findLast(haystack, empty), BigInt(haystack.length));
225+
226+
// Finding non-empty in empty should return -1
227+
assert.strictEqual(stringzilla.find(empty, haystack), -1n);
228+
assert.strictEqual(stringzilla.findLast(empty, haystack), -1n);
229+
230+
// Empty in empty
231+
assert.strictEqual(stringzilla.find(empty, empty), 0n);
232+
assert.strictEqual(stringzilla.count(empty, empty), 0n);
233+
});
234+
235+
test("Find Byte - Boundary Values", () => {
236+
const buffer = Buffer.from([0, 127, 128, 255]);
237+
238+
// Test boundary byte values
239+
assert.strictEqual(stringzilla.findByte(buffer, 0), 0n);
240+
assert.strictEqual(stringzilla.findByte(buffer, 127), 1n);
241+
assert.strictEqual(stringzilla.findByte(buffer, 128), 2n);
242+
assert.strictEqual(stringzilla.findByte(buffer, 255), 3n);
243+
244+
// Test not found
245+
assert.strictEqual(stringzilla.findByte(buffer, 1), -1n);
246+
});
247+
248+
test("UTF-8 Multi-byte Character Handling", () => {
249+
const haystack = Buffer.from("Hello 世界 World");
250+
const needle = Buffer.from("世界");
251+
252+
// Should work at byte level, not character level
253+
const result = stringzilla.find(haystack, needle);
254+
assert(result > 0n);
255+
256+
// Test with emoji
257+
const emojiBuffer = Buffer.from("Hello 👋 World");
258+
const emoji = Buffer.from("👋");
259+
assert(stringzilla.find(emojiBuffer, emoji) > 0n);
260+
});
261+
262+
test("Pattern at Buffer Boundaries", () => {
263+
const haystack = Buffer.from("abcdefghijk");
264+
265+
// Pattern at start
266+
assert.strictEqual(stringzilla.find(haystack, Buffer.from("abc")), 0n);
267+
268+
// Pattern at end
269+
assert.strictEqual(stringzilla.find(haystack, Buffer.from("ijk")), 8n);
270+
assert.strictEqual(stringzilla.findLast(haystack, Buffer.from("ijk")), 8n);
271+
272+
// Pattern spans entire buffer
273+
assert.strictEqual(stringzilla.find(haystack, haystack), 0n);
274+
});
275+
276+
test("Repeated Patterns", () => {
277+
const haystack = Buffer.from("aaaaaaaaaa");
278+
const needle = Buffer.from("aa");
279+
280+
// Test first and last occurrence
281+
assert.strictEqual(stringzilla.find(haystack, needle), 0n);
282+
assert.strictEqual(stringzilla.findLast(haystack, needle), 8n);
283+
284+
// Count with and without overlap
285+
assert.strictEqual(stringzilla.count(haystack, needle, false), 5n);
286+
assert.strictEqual(stringzilla.count(haystack, needle, true), 9n);
287+
});
288+
289+
test("Find Byte From - Edge Cases", () => {
290+
const haystack = Buffer.from("1234567890");
291+
292+
// Empty charset
293+
const emptyCharset = Buffer.alloc(0);
294+
assert.strictEqual(stringzilla.findByteFrom(haystack, emptyCharset), -1n);
295+
296+
// Charset with all possible bytes
297+
const allBytes = Buffer.alloc(256);
298+
for (let i = 0; i < 256; i++) allBytes[i] = i;
299+
assert.strictEqual(stringzilla.findByteFrom(haystack, allBytes), 0n);
300+
301+
// Charset with duplicates
302+
const duplicates = Buffer.from("1111");
303+
assert.strictEqual(stringzilla.findByteFrom(haystack, duplicates), 0n);
304+
});
305+
306+
test("Binary Data Handling", () => {
307+
// Test with null bytes and binary data
308+
const binaryData = Buffer.from([0x00, 0x01, 0x02, 0x00, 0x03, 0x00]);
309+
const nullByte = Buffer.from([0x00]);
310+
311+
assert.strictEqual(stringzilla.find(binaryData, nullByte), 0n);
312+
assert.strictEqual(stringzilla.findLast(binaryData, nullByte), 5n);
313+
assert.strictEqual(stringzilla.count(binaryData, nullByte), 3n);
314+
315+
// Test hash consistency with binary data
316+
const hash1 = stringzilla.hash(binaryData);
317+
const hash2 = stringzilla.hash(binaryData);
318+
assert.strictEqual(hash1, hash2);
319+
});
320+
321+
test("Large Buffer Operations", () => {
322+
const size = 100000; // 100KB (smaller than 1MB for faster tests)
323+
const largeBuffer = Buffer.alloc(size);
324+
325+
// Fill with pattern
326+
for (let i = 0; i < size; i++) {
327+
largeBuffer[i] = i % 256;
328+
}
329+
330+
// Test operations on large buffer
331+
const pattern = Buffer.from([0, 1, 2, 3]);
332+
assert(stringzilla.count(largeBuffer, pattern) > 0n);
333+
334+
// Test hash performance
335+
const start = Date.now();
336+
const hash = stringzilla.hash(largeBuffer);
337+
const duration = Date.now() - start;
338+
assert(duration < 100); // Should be fast
339+
assert(typeof hash === "bigint");
340+
});
341+
342+
test("Hasher - Incremental vs Single Shot", () => {
343+
const data = Buffer.from("a".repeat(1000));
344+
345+
// Single shot
346+
const hashSingle = stringzilla.hash(data);
347+
348+
// Progressive hashing with different chunk sizes should be consistent
349+
const hasher1 = new stringzilla.Hasher();
350+
hasher1.update(data.subarray(0, 100));
351+
hasher1.update(data.subarray(100, 500));
352+
hasher1.update(data.subarray(500));
353+
const hashProgressive1 = hasher1.digest();
354+
355+
const hasher2 = new stringzilla.Hasher();
356+
hasher2.update(data.subarray(0, 300));
357+
hasher2.update(data.subarray(300));
358+
const hashProgressive2 = hasher2.digest();
359+
360+
// Progressive hashing with same data should be consistent
361+
assert.strictEqual(hashProgressive1, hashProgressive2);
362+
363+
// Test that single-shot and progressive produce valid hashes
364+
assert.strictEqual(typeof hashSingle, "bigint");
365+
assert.strictEqual(typeof hashProgressive1, "bigint");
366+
assert(hashSingle > 0n);
367+
assert(hashProgressive1 > 0n);
368+
});
369+
370+
test("Compare - Special Cases", () => {
371+
// Different lengths
372+
assert(stringzilla.compare(Buffer.from("a"), Buffer.from("aa")) < 0);
373+
assert(stringzilla.compare(Buffer.from("aa"), Buffer.from("a")) > 0);
374+
375+
// Empty buffers
376+
assert.strictEqual(stringzilla.compare(Buffer.alloc(0), Buffer.alloc(0)), 0);
377+
assert(stringzilla.compare(Buffer.alloc(0), Buffer.from("a")) < 0);
378+
assert(stringzilla.compare(Buffer.from("a"), Buffer.alloc(0)) > 0);
379+
380+
// Binary data comparison
381+
const binary1 = Buffer.from([0x00, 0x01, 0x02]);
382+
const binary2 = Buffer.from([0x00, 0x01, 0x03]);
383+
assert(stringzilla.compare(binary1, binary2) < 0);
384+
});

0 commit comments

Comments
 (0)