Skip to content

Commit 4e96d39

Browse files
committed
test: fix MutexState edge-case assertions and add lockfile
1 parent ba9ea46 commit 4e96d39

2 files changed

Lines changed: 150 additions & 0 deletions

File tree

package-lock.json

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

tests/40_edge_cases.test.mjs

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
import test from 'node:test';
2+
import assert from 'node:assert/strict';
3+
import * as core from '../mutex_core.js';
4+
import { stepN, lastTrace } from './helpers.mjs';
5+
6+
test('TokenRing edge branches: no alive processes, unknown/crashed holder, token remains', () => {
7+
const model = core.makeModel(2, 'TokenRing');
8+
9+
core.crashProcess(model, 'P2');
10+
let r = core.stepOnce(model);
11+
assert.equal(r.type, 'token_pass');
12+
assert.match(lastTrace(model), /Token remains at P1/);
13+
14+
model.token.holder = 'PX';
15+
r = core.stepOnce(model);
16+
assert.equal(r.reason, 'unknown_holder');
17+
18+
model.token.holder = 'P2';
19+
model.token.lost = false;
20+
r = core.stepOnce(model);
21+
assert.equal(r.reason, 'token_lost');
22+
23+
core.crashProcess(model, 'P1');
24+
const regen = core.regenerateToken(model);
25+
assert.equal(regen.reason, 'no_alive_processes');
26+
27+
model.ring = [];
28+
assert.equal(core.ringNextAlive(model, 'P1'), 'P1');
29+
});
30+
31+
test('comparePid fallback path via RA script with non-numeric process IDs', () => {
32+
const demo = {
33+
type: 'MutexDemo',
34+
algorithm: 'RA',
35+
processes: ['PA', 'PB'],
36+
events: [
37+
{ t: 0, op: 'requestCS', on: 'PA' },
38+
{ t: 1, op: 'requestCS', on: 'PB' },
39+
{ t: 2, op: 'deliverNext' },
40+
{ t: 3, op: 'deliverNext' },
41+
{ t: 4, op: 'deliverNext' }
42+
]
43+
};
44+
45+
const model = core.loadFromJsonObject(demo);
46+
stepN(core, model, 5);
47+
assert.ok(model.trace.some(e => /enters the critical section/.test(e.text)));
48+
});
49+
50+
test('RA edge branches: crashed receiver drop and unsupported message type', () => {
51+
const model = core.makeModel(3, 'RA');
52+
53+
core.requestCS(model, 'P1');
54+
core.crashProcess(model, 'P1');
55+
56+
model.network.queue.unshift({ id: 999, type: 'REPLY', from: 'P2', to: 'P1', ts: 1 });
57+
let r = core.stepOnce(model);
58+
assert.equal(r.type, 'message_dropped');
59+
assert.match(lastTrace(model), /Message dropped: REPLY #999 P2 -> P1 \(receiver crashed\)/);
60+
61+
const model2 = core.makeModel(2, 'RA');
62+
model2.network.queue.push({ id: 1, type: 'BOGUS', from: 'P1', to: 'P2', ts: 1 });
63+
r = core.stepOnce(model2);
64+
assert.equal(r.type, 'message_delivered');
65+
assert.match(lastTrace(model2), /Unsupported message type/);
66+
});
67+
68+
test('script bad-event branches for TokenRing and RA are covered', () => {
69+
const trDemo = {
70+
type: 'MutexDemo',
71+
algorithm: 'TokenRing',
72+
processes: ['P1', 'P2'],
73+
events: [
74+
{},
75+
{ t: 1, op: 'holdToken' },
76+
{ t: 2, op: 'passToken' },
77+
{ t: 3, op: 'requestCS' },
78+
{ t: 4, op: 'requestCS', on: 'PX' },
79+
{ t: 5, op: 'releaseCS' },
80+
{ t: 6, op: 'releaseCS', on: 'PX' },
81+
{ t: 7, op: 'crash' },
82+
{ t: 8, op: 'recover' }
83+
]
84+
};
85+
const tr = core.loadFromJsonObject(trDemo);
86+
stepN(core, tr, trDemo.events.length);
87+
const trTrace = tr.trace.map(e => e.text).join('\n');
88+
assert.match(
89+
trTrace,
90+
/missing op|holdToken skipped|passToken skipped|requestCS skipped|releaseCS skipped|crash skipped|recover skipped/
91+
);
92+
93+
const raDemo = {
94+
type: 'MutexDemo',
95+
algorithm: 'RA',
96+
processes: ['P1', 'P2'],
97+
events: [
98+
{},
99+
{ t: 1, op: 'requestCS' },
100+
{ t: 2, op: 'releaseCS' },
101+
{ t: 3, op: 'crash' },
102+
{ t: 4, op: 'recover' }
103+
]
104+
};
105+
const ra = core.loadFromJsonObject(raDemo);
106+
stepN(core, ra, raDemo.events.length);
107+
const raTrace = ra.trace.map(e => e.text).join('\n');
108+
assert.match(
109+
raTrace,
110+
/missing op|requestCS skipped|releaseCS skipped|crash skipped|recover skipped/
111+
);
112+
});
113+
114+
test('exportStateObject default branch and load MutexState defaults/optional branches', () => {
115+
const weird = { algorithm: 'Other', mode: 'interactive', processes: [] };
116+
const obj = core.exportStateObject(weird);
117+
assert.equal(obj.type, 'MutexState');
118+
assert.equal(obj.algorithm, 'Other');
119+
assert.equal(obj.mode, 'interactive');
120+
121+
const trState = {
122+
type: 'MutexState',
123+
algorithm: 'TokenRing',
124+
ring: ['P1', 'P2'],
125+
token: { holder: 'P2', lost: true }
126+
};
127+
const tr = core.loadFromJsonObject(trState);
128+
assert.equal(tr.token.holder, 'P2');
129+
assert.equal(tr.token.lost, true);
130+
131+
const raState = {
132+
type: 'MutexState',
133+
algorithm: 'RA',
134+
ring: ['P1', 'P2'],
135+
network: { nextMsgId: 7, dropNextSend: true, queue: [] }
136+
};
137+
const ra = core.loadFromJsonObject(raState);
138+
assert.equal(ra.network.nextMsgId, 7);
139+
assert.equal(ra.network.dropNextSend, true);
140+
});

0 commit comments

Comments
 (0)