aboutsummaryrefslogtreecommitdiff
path: root/tests/drivers/diagcache_test.js
diff options
context:
space:
mode:
authorGravatar Moon Flower <moonflower@elhenney.local> 2026-10-12 08:46:23 +0330
committerGravatar Moon Flower <moonflower@elhenney.local> 2026-10-12 08:46:23 +0330
commitecc350ca5c2eb50be8309c5f14f8bf0d87db9a02 (patch)
tree70e0af76a1b13affff7c6cc8aac7b3e6ef960478 /tests/drivers/diagcache_test.js
a
Diffstat (limited to 'tests/drivers/diagcache_test.js')
-rw-r--r--tests/drivers/diagcache_test.js140
1 files changed, 140 insertions, 0 deletions
diff --git a/tests/drivers/diagcache_test.js b/tests/drivers/diagcache_test.js
new file mode 100644
index 0000000..43966cf
--- /dev/null
+++ b/tests/drivers/diagcache_test.js
@@ -0,0 +1,140 @@
+// the rules of the diagnostics cache (editors/vscode/client/diagcache.js), with
+// plain node and no vscode: a key covers the server build, the text, and every
+// file the text reads through include, transitively.
+//
+// node tests/drivers/diagcache_test.js
+
+const fs = require('fs');
+const os = require('os');
+const path = require('path');
+const assert = require('assert');
+const dc = require(path.join(__dirname, '..', '..', 'editors', 'vscode', 'client', 'diagcache.js'));
+
+let passed = 0, failed = 0;
+function check(name, fn) {
+ try { fn(); passed++; console.log('ok ' + name); }
+ catch (e) { failed++; console.log('FAIL ' + name + ': ' + e.message); }
+}
+
+const root = fs.mkdtempSync(path.join(os.tmpdir(), 'zc-diagcache-'));
+const write = (rel, text) => { const p = path.join(root, rel); fs.mkdirSync(path.dirname(p), { recursive: true }); fs.writeFileSync(p, text); return p; };
+const A = write('a.zc', 'include "b.zc";\nfn main() { return helper(); }\n');
+const B = write('b.zc', 'include "c.zc";\n');
+const C = write('c.zc', 'fn helper() { return 1; }\n');
+const U = write('unrelated.zc', 'fn other() { return 2; }\n');
+const aText = fs.readFileSync(A, 'utf8');
+
+const keyA = (stamp = 'S1', text = aText, p = A) => dc.diagKey(stamp, text, p, root).key;
+
+check('includeNames reads every include, however it is spaced', () => {
+ assert.deepStrictEqual(dc.includeNames('include "a.zc";\ninclude"b/c.zc";\nfn f() {}'), ['a.zc', 'b/c.zc']);
+ assert.deepStrictEqual(dc.includeNames('fn f() { return 1; }'), []);
+});
+
+check('includeNames ignores comments and literals while allowing comments between tokens', () => {
+ assert.deepStrictEqual(dc.includeNames('// include "fake.zc"\n/* include "also-fake.zc" */\nlet s = "include \\"string.zc\\"";\ninclude /* dependency */ "real.zc";'), ['real.zc']);
+});
+
+check('the same tree gives the same key', () => {
+ assert.strictEqual(keyA(), keyA());
+ assert.match(keyA(), /^[0-9a-f]{64}$/);
+});
+
+check('a change to the document text changes the key', () => {
+ assert.notStrictEqual(keyA('S1', aText + ' '), keyA());
+});
+
+check('a change to the server build changes the key', () => {
+ assert.notStrictEqual(keyA('S2'), keyA('S1'));
+});
+
+check('a change to a file the document includes, two steps down, changes the key', () => {
+ const before = keyA();
+ fs.writeFileSync(C, 'fn helper() { return 3; }\n');
+ const after = keyA();
+ assert.notStrictEqual(before, after);
+ fs.writeFileSync(C, 'fn helper() { return 1; }\n');
+ assert.strictEqual(keyA(), before, 'putting it back gives the old key again');
+});
+
+check('a file nothing includes does not change the key', () => {
+ const before = keyA();
+ fs.writeFileSync(U, 'fn other() { return 9; }\n');
+ assert.strictEqual(keyA(), before);
+});
+
+check('creating a missing include changes the key', () => {
+ const M = write('m.zc', 'include "later.zc";\nfn f() { return 0; }\n');
+ const mText = fs.readFileSync(M, 'utf8');
+ const before = dc.diagKey('S1', mText, M, root);
+ assert.ok(before.deps.some(p => p.endsWith('later.zc')), 'the missing name is still a dependency');
+ write('later.zc', 'fn g() { return 1; }\n');
+ assert.notStrictEqual(dc.diagKey('S1', mText, M, root).key, before.key);
+});
+
+check('an include is found from the workspace folder as well as the including file', () => {
+ const X = write('sub/x.zc', 'include "b.zc";\nfn x() { return 0; }\n');
+ const tree = dc.sourceTree(fs.readFileSync(X, 'utf8'), X, root);
+ assert.ok(tree.files.has(path.join(root, 'b.zc')), 'b.zc is read from the workspace root');
+ assert.ok(tree.files.has(path.join(root, 'sub', 'b.zc')), 'and the directory of x.zc is tried too');
+ assert.ok(tree.complete);
+});
+
+check('a tree too large to cover has no key, so nothing is cached for it', () => {
+ let many = '';
+ for (let i = 0; i < dc.MAX_FILES + 5; i++) many += 'include "nope' + i + '.zc";\n';
+ const big = write('big.zc', many);
+ const r = dc.diagKey('S1', many, big, root);
+ assert.strictEqual(r.key, null);
+ const cache = new dc.DiagCache(null);
+ assert.strictEqual(cache.put(r.key, []), false);
+ assert.strictEqual(cache.size(), 0);
+});
+
+check('the cache answers only for the key it was given', () => {
+ const cache = new dc.DiagCache(null);
+ const d = [{ message: 'x' }];
+ assert.ok(cache.put('k1', d));
+ assert.deepStrictEqual(cache.get('k1'), d);
+ assert.strictEqual(cache.get('k2'), null);
+ assert.strictEqual(cache.get(null), null);
+ assert.strictEqual(cache.put(null, []), false, 'a tree without a key is not kept');
+ assert.strictEqual(cache.size(), 1);
+});
+
+check('two states of one document are both kept, so going back is a hit', () => {
+ const cache = new dc.DiagCache(null);
+ cache.put('state-a', []);
+ cache.put('state-b', [{ message: 'b' }]);
+ assert.deepStrictEqual(cache.get('state-a'), []);
+ assert.deepStrictEqual(cache.get('state-b'), [{ message: 'b' }]);
+});
+
+check('the least recently used entries go first past the limit', () => {
+ const cache = new dc.DiagCache(null, 2);
+ cache.put('1', []);
+ cache.put('2', []);
+ cache.get('1');
+ cache.put('3', []);
+ assert.strictEqual(cache.size(), 2);
+ assert.strictEqual(cache.get('2'), null, 'two was the least recently used');
+ assert.deepStrictEqual(cache.get('1'), []);
+ assert.deepStrictEqual(cache.get('3'), []);
+});
+
+check('the cache carries over a save and load as plain JSON', () => {
+ const cache = new dc.DiagCache(null);
+ cache.put('k1', [{ message: 'z', range: { start: { line: 1, character: 2 } } }]);
+ const back = new dc.DiagCache(JSON.parse(JSON.stringify(cache)));
+ assert.deepStrictEqual(back.get('k1'), [{ message: 'z', range: { start: { line: 1, character: 2 } } }]);
+});
+
+check('a saved entry that is not well formed is dropped, not trusted', () => {
+ const back = new dc.DiagCache({ bad: { key: 'bad' }, good: [] });
+ assert.strictEqual(back.get('bad'), null);
+ assert.deepStrictEqual(back.get('good'), []);
+});
+
+fs.rmSync(root, { recursive: true, force: true });
+console.log(passed + ' passed, ' + failed + ' failed');
+process.exit(failed ? 1 : 0);