]> git.ardis.lu - csvio/commitdiff
Add initial tests for memory leaks
authorArdis Lu <ardisglu@gmail.com>
Tue, 15 Jul 2025 05:07:56 +0000 (22:07 -0700)
committerArdis Lu <ardisglu@gmail.com>
Tue, 15 Jul 2025 05:07:56 +0000 (22:07 -0700)
Future tests should be dedicated for finding memory leaks only, but as an initial step it makes sense to add a simple sanity check to the large file tests.

package.json
test/large.test.js

index 83d22a5ef4513c3e3bd5289172139ad1286e3c23..8f01a9af4c24b2f8bfc84224ba85b4d7ff490aad 100644 (file)
@@ -30,7 +30,7 @@
   "scripts": {
     "types": "tsc",
     "test": "node --test --test-skip-pattern=\"/^large:/\"",
-    "test:large": "node --test --test-name-pattern=\"/^large:/\"",
+    "test:large": "node --test --test-name-pattern=\"/^large:/\" --expose-gc",
     "test:coverage": "npm run test -- --experimental-test-coverage",
     "bench": "node ./benchmark/core.bench.js",
     "prepack": "node ./npm/prepack.js",
index cb35b667ea5617f3dac2c1836c07ff7bd1b79805..3d2252bf6c4a0fd1b75588b111632d666ab82acc 100644 (file)
@@ -1,22 +1,36 @@
 import { suite, test, before, after } from 'node:test';
 import { ok, deepStrictEqual } from 'node:assert/strict';
 import { unlink, stat } from 'node:fs/promises';
+import { memoryUsage } from 'node:process';
 
 import { createRandomCSV, createTempFile } from './utils.js';
 import { CSVReader, CSVTransformer, CSVWriter } from '../src/core.js';
 
+/** Maximum variance allowed for memory leak testing. */
+const MEMORY_THRESHOLD = 10 * 1024 * 1024; // 10 MB, or 10% of the CSV file size
+
 suite('large: 100000 row x 100 column CSV (~100 MB)', { concurrency: 1 }, async () => {
   let temp;
   before(async () => { temp = await createTempFile() });
   after(async () => await unlink(temp));
 
   test('CSVWriter writes file', async () => {
+    global.gc?.();
+    const memoryBefore = memoryUsage().heapUsed;
+
     await createRandomCSV(100000, 100, 1).pipeTo(new CSVWriter(temp));
     const { size } = await stat(temp, { bigint: true });
     ok(size > 100_000_000n);
+
+    global.gc?.();
+    const memoryAfter = memoryUsage().heapUsed;
+    ok(memoryAfter - memoryBefore < MEMORY_THRESHOLD);
   });
 
   test('CSVReader reads file', async () => {
+    global.gc?.();
+    const memoryBefore = memoryUsage().heapUsed;
+
     const reader = new CSVReader(temp);
     let rowCount = 0;
     for await (const row of reader) {
@@ -38,9 +52,16 @@ suite('large: 100000 row x 100 column CSV (~100 MB)', { concurrency: 1 }, async
       }
       rowCount++;
     }
+
+    global.gc?.();
+    const memoryAfter = memoryUsage().heapUsed;
+    ok(memoryAfter - memoryBefore < MEMORY_THRESHOLD);
   });
 
   test('CSVTransformer transforms file', async () => {
+    global.gc?.();
+    const memoryBefore = memoryUsage().heapUsed;
+
     const reader = new CSVReader(temp)
       .pipeThrough(new CSVTransformer(r => r.map(f => Number(f) + 1), { handleHeaders: true }));
     let rowCount = 0;
@@ -63,6 +84,10 @@ suite('large: 100000 row x 100 column CSV (~100 MB)', { concurrency: 1 }, async
       }
       rowCount++;
     }
+
+    global.gc?.();
+    const memoryAfter = memoryUsage().heapUsed;
+    ok(memoryAfter - memoryBefore < MEMORY_THRESHOLD);
   });
 });
 
@@ -72,12 +97,22 @@ suite('large: 100 row x 100000 column CSV (~100 MB)', { concurrency: 1 }, async
   after(async () => await unlink(temp));
 
   test('CSVWriter writes file', async () => {
+    global.gc?.();
+    const memoryBefore = memoryUsage().heapUsed;
+
     await createRandomCSV(100, 100000, 1).pipeTo(new CSVWriter(temp));
     const { size } = await stat(temp, { bigint: true });
     ok(size > 100_000_000n);
+
+    global.gc?.();
+    const memoryAfter = memoryUsage().heapUsed;
+    ok(memoryAfter - memoryBefore < MEMORY_THRESHOLD);
   });
 
   test('CSVReader reads file', async () => {
+    global.gc?.();
+    const memoryBefore = memoryUsage().heapUsed;
+
     const reader = new CSVReader(temp);
     let rowCount = 0;
     for await (const row of reader) {
@@ -99,9 +134,16 @@ suite('large: 100 row x 100000 column CSV (~100 MB)', { concurrency: 1 }, async
       }
       rowCount++;
     }
+
+    global.gc?.();
+    const memoryAfter = memoryUsage().heapUsed;
+    ok(memoryAfter - memoryBefore < MEMORY_THRESHOLD);
   });
 
   test('CSVTransformer transforms file', async () => {
+    global.gc?.();
+    const memoryBefore = memoryUsage().heapUsed;
+
     const reader = new CSVReader(temp)
       .pipeThrough(new CSVTransformer(r => r.map(f => Number(f) + 1), { handleHeaders: true }));
     let rowCount = 0;
@@ -124,5 +166,9 @@ suite('large: 100 row x 100000 column CSV (~100 MB)', { concurrency: 1 }, async
       }
       rowCount++;
     }
+
+    global.gc?.();
+    const memoryAfter = memoryUsage().heapUsed;
+    ok(memoryAfter - memoryBefore < MEMORY_THRESHOLD);
   });
 });
\ No newline at end of file
This page took 0.047159 seconds and 4 git commands to generate.