diff --git a/packages/utils/dag.js b/packages/utils/dag.js index 22369b4..d85ce64 100644 --- a/packages/utils/dag.js +++ b/packages/utils/dag.js @@ -1,7 +1,18 @@ export default class DAG { constructor(nodes) { this.nodes = new Map(nodes.map((node) => [node._id, node])) - this.edges = [] + this.edges = new Set() + } + + format() { + this.nodes.forEach((_, id) => { + this.setLayer(id) + this.addEdges(id) + }) + } + + get(id) { + return this.nodes.get(id) } setLayer(id) { @@ -18,13 +29,13 @@ export default class DAG { return node.layer } - format() { - this.nodes.forEach((_, id) => { - this.setLayer(id) + addEdges(id) { + const node = this.nodes.get(id) + if (!node) { + throw new Error(`Node ${id} not found`) + } + node.requirements.forEach((rId) => { + this.edges.add([rId, id]) }) } - - get(id) { - return this.nodes.get(id) - } } diff --git a/packages/utils/dag.test.js b/packages/utils/dag.test.js index 272eeea..7dd3398 100644 --- a/packages/utils/dag.test.js +++ b/packages/utils/dag.test.js @@ -1,50 +1,133 @@ import DAG from './dag.js' -describe('dag', () => { - const data = [ - { - _id: 'helloworld', - title: 'Hello, Linux World!', - requirements: [], - }, - { - _id: 'spell', - title: 'Learn to spell', - requirements: ['helloworld'], - }, - { - _id: 'discover', - title: 'Discover the World!', - requirements: ['spell'], - }, - { - _id: 'read', - title: 'Read the File', - requirements: ['spell'], - }, - { - _id: 'remove', - requirements: ['read', 'discover'], - }, - { - _id: 'move', - requirements: ['helloworld'], - }, - { - _id: 'copy', - requirements: ['remove', 'move'], - }, - ] - - it('should set layers', () => { +describe('dag layers', () => { + it('two layer', () => { + const data = [ + { + _id: 'helloworld', + requirements: [], + }, + { + _id: 'spell', + requirements: ['helloworld'], + }, + ] const dag = new DAG(data) dag.format() expect(dag.get('helloworld').layer).toBe(1) expect(dag.get('spell').layer).toBe(2) - expect(dag.get('move').layer).toBe(2) - expect(dag.get('read').layer).toBe(3) - expect(dag.get('discover').layer).toBe(3) - expect(dag.get('remove').layer).toBe(4) - expect(dag.get('copy').layer).toBe(5) + }) + it('branch', () => { + const data = [ + { + _id: 'helloworld', + requirements: [], + }, + { + _id: 'spell', + requirements: ['helloworld'], + }, + { + _id: 'discover', + requirements: ['helloworld'], + }, + ] + const dag = new DAG(data) + dag.format() + expect(dag.get('helloworld').layer).toBe(1) + expect(dag.get('spell').layer).toBe(2) + expect(dag.get('discover').layer).toBe(2) + }) + it('merge', () => { + const data = [ + { + _id: 'A', + requirements: [], + }, + { + _id: 'B', + requirements: [], + }, + { + _id: 'C', + requirements: ['A', 'B'], + }, + ] + const dag = new DAG(data) + dag.format() + expect(dag.get('A').layer).toBe(1) + expect(dag.get('B').layer).toBe(1) + expect(dag.get('C').layer).toBe(2) + }) + it('skip', () => { + const data = [ + { + _id: 'A', + requirements: [], + }, + { + _id: 'B', + requirements: [], + }, + { + _id: 'C', + requirements: ['A'], + }, + { + _id: 'D', + requirements: ['B', 'C'], + }, + ] + const dag = new DAG(data) + dag.format() + expect(dag.get('A').layer).toBe(1) + expect(dag.get('B').layer).toBe(1) + expect(dag.get('C').layer).toBe(2) + expect(dag.get('D').layer).toBe(3) + }) +}) + +describe('dag edges', () => { + it('one edge', () => { + const data = [ + { + _id: 'helloworld', + requirements: [], + }, + { + _id: 'spell', + requirements: ['helloworld'], + }, + ] + + const dag = new DAG(data) + dag.format() + expect(dag.edges).toEqual(new Set([['helloworld', 'spell']])) + }) + + it('branch edges', () => { + const data = [ + { + _id: 'helloworld', + requirements: [], + }, + { + _id: 'spell', + requirements: ['helloworld'], + }, + { + _id: 'discover', + requirements: ['helloworld'], + }, + ] + + const dag = new DAG(data) + dag.format() + expect(dag.edges).toEqual( + new Set([ + ['helloworld', 'discover'], + ['helloworld', 'spell'], + ]) + ) }) })