diff options
| author | 2026-10-12 08:46:23 +0330 | |
|---|---|---|
| committer | 2026-10-12 08:46:23 +0330 | |
| commit | ecc350ca5c2eb50be8309c5f14f8bf0d87db9a02 (patch) | |
| tree | 70e0af76a1b13affff7c6cc8aac7b3e6ef960478 /tests/drivers/diagcache_test.js | |
a
Diffstat (limited to 'tests/drivers/diagcache_test.js')
| -rw-r--r-- | tests/drivers/diagcache_test.js | 140 |
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); |
