Son CV dans un terminal web en Javascript!
https://terminal-cv.gregandev.fr
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
281 lines
9.0 KiB
281 lines
9.0 KiB
2 years ago
|
// @flow strict-local
|
||
|
|
||
|
import assert from 'assert';
|
||
|
import path from 'path';
|
||
|
import {Worker} from 'worker_threads';
|
||
|
|
||
|
import AdjacencyList, {NodeTypeMap, EdgeTypeMap} from '../src/AdjacencyList';
|
||
|
import {toNodeId} from '../src/types';
|
||
|
|
||
|
describe('AdjacencyList', () => {
|
||
|
it('constructor should initialize an empty graph', () => {
|
||
|
let stats = new AdjacencyList().stats;
|
||
|
assert(stats.nodes === 0);
|
||
|
assert(stats.edges === 0);
|
||
|
});
|
||
|
|
||
|
it('addNode should add a node to the graph', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let id = graph.addNode();
|
||
|
assert.equal(id, 0);
|
||
|
assert.equal(graph.stats.nodes, 1);
|
||
|
});
|
||
|
|
||
|
it('addNode should resize nodes array when necessary', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let size = graph.serialize().nodes.byteLength;
|
||
|
let a = graph.addNode();
|
||
|
let b = graph.addNode();
|
||
|
assert(size < (size = graph.serialize().nodes.byteLength));
|
||
|
graph.addEdge(a, b, 1);
|
||
|
graph.addEdge(a, b, 2);
|
||
|
graph.addEdge(a, b, 3);
|
||
|
graph.addEdge(a, b, 4);
|
||
|
assert(size < graph.serialize().nodes.byteLength);
|
||
|
});
|
||
|
|
||
|
it('removeEdge should remove an edge from the graph', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let node0 = graph.addNode();
|
||
|
let node1 = graph.addNode();
|
||
|
let node2 = graph.addNode();
|
||
|
let node3 = graph.addNode();
|
||
|
let node4 = graph.addNode();
|
||
|
let node5 = graph.addNode();
|
||
|
let node6 = graph.addNode();
|
||
|
graph.addEdge(node0, node1);
|
||
|
graph.addEdge(node2, node1);
|
||
|
// this will get removed
|
||
|
graph.addEdge(node3, node1);
|
||
|
graph.addEdge(node4, node1);
|
||
|
graph.addEdge(node5, node1);
|
||
|
graph.addEdge(node6, node1);
|
||
|
|
||
|
assert.deepEqual(graph.getNodeIdsConnectedTo(node1), [0, 2, 3, 4, 5, 6]);
|
||
|
|
||
|
graph.removeEdge(node3, node1);
|
||
|
assert.deepEqual(graph.getNodeIdsConnectedTo(node1), [0, 2, 4, 5, 6]);
|
||
|
});
|
||
|
|
||
|
it('removeEdge should remove an edge of a specific type from the graph', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let a = graph.addNode();
|
||
|
let b = graph.addNode();
|
||
|
let c = graph.addNode();
|
||
|
let d = graph.addNode();
|
||
|
graph.addEdge(a, b);
|
||
|
graph.addEdge(a, b, 2);
|
||
|
graph.addEdge(a, b, 3);
|
||
|
graph.addEdge(a, c);
|
||
|
graph.addEdge(a, d, 3);
|
||
|
assert.equal(graph.stats.edges, 5);
|
||
|
assert.ok(graph.hasEdge(a, b));
|
||
|
assert.ok(graph.hasEdge(a, b, 2));
|
||
|
assert.ok(graph.hasEdge(a, b, 3));
|
||
|
assert.ok(graph.hasEdge(a, c));
|
||
|
assert.ok(graph.hasEdge(a, d, 3));
|
||
|
assert.deepEqual(Array.from(graph.getAllEdges()), [
|
||
|
{from: a, to: b, type: 1},
|
||
|
{from: a, to: b, type: 2},
|
||
|
{from: a, to: b, type: 3},
|
||
|
{from: a, to: c, type: 1},
|
||
|
{from: a, to: d, type: 3},
|
||
|
]);
|
||
|
|
||
|
graph.removeEdge(a, b, 2);
|
||
|
assert.equal(graph.stats.edges, 4);
|
||
|
assert.ok(graph.hasEdge(a, b));
|
||
|
assert.equal(graph.hasEdge(a, b, 2), false);
|
||
|
assert.ok(graph.hasEdge(a, b, 3));
|
||
|
assert.ok(graph.hasEdge(a, c));
|
||
|
assert.ok(graph.hasEdge(a, d, 3));
|
||
|
assert.deepEqual(Array.from(graph.getAllEdges()), [
|
||
|
{from: a, to: b, type: 1},
|
||
|
{from: a, to: b, type: 3},
|
||
|
{from: a, to: c, type: 1},
|
||
|
{from: a, to: d, type: 3},
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
it('addEdge should add an edge to the graph', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let a = graph.addNode();
|
||
|
let b = graph.addNode();
|
||
|
graph.addEdge(a, b);
|
||
|
assert.equal(graph.stats.nodes, 2);
|
||
|
assert.equal(graph.stats.edges, 1);
|
||
|
assert.ok(graph.hasEdge(a, b));
|
||
|
});
|
||
|
|
||
|
it('addEdge should add multiple edges from a node in order', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let a = graph.addNode();
|
||
|
let b = graph.addNode();
|
||
|
let c = graph.addNode();
|
||
|
let d = graph.addNode();
|
||
|
graph.addEdge(a, b);
|
||
|
graph.addEdge(a, d);
|
||
|
graph.addEdge(a, c);
|
||
|
assert.deepEqual(graph.getNodeIdsConnectedFrom(a), [b, d, c]);
|
||
|
});
|
||
|
|
||
|
it('addEdge should add multiple edges to a node in order', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let a = graph.addNode();
|
||
|
let b = graph.addNode();
|
||
|
let c = graph.addNode();
|
||
|
let d = graph.addNode();
|
||
|
graph.addEdge(a, b);
|
||
|
graph.addEdge(d, b);
|
||
|
graph.addEdge(a, d);
|
||
|
graph.addEdge(c, b);
|
||
|
assert.deepEqual(graph.getNodeIdsConnectedTo(b), [a, d, c]);
|
||
|
});
|
||
|
|
||
|
it('addEdge should add multiple edges of different types in order', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let a = graph.addNode();
|
||
|
let b = graph.addNode();
|
||
|
graph.addEdge(a, b);
|
||
|
graph.addEdge(a, b, 1);
|
||
|
graph.addEdge(a, b, 4);
|
||
|
graph.addEdge(a, b, 3);
|
||
|
assert.deepEqual(graph.getNodeIdsConnectedFrom(a), [b]);
|
||
|
assert.deepEqual(Array.from(graph.getAllEdges()), [
|
||
|
{from: a, to: b, type: 1},
|
||
|
{from: a, to: b, type: 4},
|
||
|
{from: a, to: b, type: 3},
|
||
|
]);
|
||
|
});
|
||
|
|
||
|
it('addEdge should return false if an edge is already added', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let a = graph.addNode();
|
||
|
let b = graph.addNode();
|
||
|
assert.equal(graph.addEdge(a, b), true);
|
||
|
assert.equal(graph.addEdge(a, b), false);
|
||
|
});
|
||
|
|
||
|
it('addEdge should resize edges array when necessary', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let size = graph.serialize().edges.byteLength;
|
||
|
let a = graph.addNode();
|
||
|
let b = graph.addNode();
|
||
|
graph.addEdge(a, b, 1);
|
||
|
graph.addEdge(a, b, 2);
|
||
|
graph.addEdge(a, b, 3);
|
||
|
assert(size < graph.serialize().edges.byteLength);
|
||
|
});
|
||
|
|
||
|
it('addEdge should error when a node has not been added to the graph', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
assert.throws(() => graph.addEdge(toNodeId(0), toNodeId(1)));
|
||
|
graph.addNode();
|
||
|
assert.throws(() => graph.addEdge(toNodeId(0), toNodeId(1)));
|
||
|
graph.addNode();
|
||
|
assert.doesNotThrow(() => graph.addEdge(toNodeId(0), toNodeId(1)));
|
||
|
assert.throws(() => graph.addEdge(toNodeId(0), toNodeId(2)));
|
||
|
});
|
||
|
|
||
|
it('addEdge should error when an unsupported edge type is provided', () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let a = graph.addNode();
|
||
|
let b = graph.addNode();
|
||
|
assert.throws(() => graph.addEdge(a, b, 0));
|
||
|
assert.throws(() => graph.addEdge(a, b, -1));
|
||
|
assert.doesNotThrow(() => graph.addEdge(a, b, 1));
|
||
|
});
|
||
|
|
||
|
it('addEdge should not replace a deleted edge if the edge was already added', () => {
|
||
|
// Mock hash fn to generate collisions
|
||
|
// $FlowFixMe[prop-missing]
|
||
|
let originalHash = AdjacencyList.prototype.hash;
|
||
|
// $FlowFixMe[prop-missing]
|
||
|
AdjacencyList.prototype.hash = () => 1;
|
||
|
|
||
|
let graph = new AdjacencyList();
|
||
|
let n0 = graph.addNode();
|
||
|
let n1 = graph.addNode();
|
||
|
let n2 = graph.addNode();
|
||
|
graph.addEdge(n0, n1, 1);
|
||
|
graph.addEdge(n1, n2, 1);
|
||
|
graph.removeEdge(n1, n2, 1);
|
||
|
assert(graph.addEdge(n0, n1, 1) === false);
|
||
|
assert(graph.stats.edges === 1);
|
||
|
|
||
|
// $FlowFixMe[prop-missing]
|
||
|
AdjacencyList.prototype.hash = originalHash;
|
||
|
});
|
||
|
|
||
|
it('addEdge should replace a deleted edge', () => {
|
||
|
// Mock hash fn to generate collisions
|
||
|
// $FlowFixMe[prop-missing]
|
||
|
let originalHash = AdjacencyList.prototype.hash;
|
||
|
// $FlowFixMe[prop-missing]
|
||
|
AdjacencyList.prototype.hash = () => 1;
|
||
|
|
||
|
let graph = new AdjacencyList();
|
||
|
let n0 = graph.addNode();
|
||
|
let n1 = graph.addNode();
|
||
|
graph.addEdge(n0, n1, 2);
|
||
|
graph.removeEdge(n0, n1, 2);
|
||
|
assert(graph.addEdge(n0, n1, 2));
|
||
|
assert(graph.stats.edges === 1);
|
||
|
assert(graph.stats.deleted === 1);
|
||
|
// Resize to reclaim deleted edge space.
|
||
|
graph.resizeEdges(4);
|
||
|
assert(graph.stats.edges === 1);
|
||
|
assert(graph.stats.deleted === 0);
|
||
|
|
||
|
// $FlowFixMe[prop-missing]
|
||
|
AdjacencyList.prototype.hash = originalHash;
|
||
|
});
|
||
|
|
||
|
describe('deserialize', function () {
|
||
|
this.timeout(10000);
|
||
|
|
||
|
it('should share the underlying data across worker threads', async () => {
|
||
|
let graph = new AdjacencyList();
|
||
|
let n0 = graph.addNode();
|
||
|
let n1 = graph.addNode();
|
||
|
graph.addEdge(n0, n1, 1);
|
||
|
graph.addEdge(n0, n1, 2);
|
||
|
|
||
|
let worker = new Worker(
|
||
|
path.join(__dirname, 'integration/adjacency-list-shared-array.js'),
|
||
|
);
|
||
|
|
||
|
let originalSerialized = graph.serialize();
|
||
|
let originalNodes = [...originalSerialized.nodes];
|
||
|
let originalEdges = [...originalSerialized.edges];
|
||
|
let work = new Promise(resolve => worker.on('message', resolve));
|
||
|
worker.postMessage(originalSerialized);
|
||
|
let received = AdjacencyList.deserialize(await work);
|
||
|
await worker.terminate();
|
||
|
|
||
|
assert.deepEqual(received.serialize().nodes, graph.serialize().nodes);
|
||
|
assert.deepEqual(received.serialize().edges, graph.serialize().edges);
|
||
|
|
||
|
originalNodes.forEach((v, i) => {
|
||
|
if (i < NodeTypeMap.HEADER_SIZE) {
|
||
|
assert.equal(v, received.serialize().nodes[i]);
|
||
|
assert.equal(v, graph.serialize().nodes[i]);
|
||
|
} else {
|
||
|
assert.equal(v * 2, received.serialize().nodes[i]);
|
||
|
assert.equal(v * 2, graph.serialize().nodes[i]);
|
||
|
}
|
||
|
});
|
||
|
|
||
|
originalEdges.forEach((v, i) => {
|
||
|
if (i < EdgeTypeMap.HEADER_SIZE) {
|
||
|
assert.equal(v, received.serialize().edges[i]);
|
||
|
assert.equal(v, graph.serialize().edges[i]);
|
||
|
} else {
|
||
|
assert.equal(v * 2, received.serialize().edges[i]);
|
||
|
assert.equal(v * 2, graph.serialize().edges[i]);
|
||
|
}
|
||
|
});
|
||
|
});
|
||
|
});
|
||
|
});
|