dep_graph_spec.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. var DepGraph = require('../lib/dep_graph').DepGraph;
  2. describe('DepGraph', function () {
  3. it('should be able to add/remove nodes', function () {
  4. var graph = new DepGraph();
  5. graph.addNode('Foo');
  6. graph.addNode('Bar');
  7. expect(graph.hasNode('Foo')).toBe(true);
  8. expect(graph.hasNode('Bar')).toBe(true);
  9. expect(graph.hasNode('NotThere')).toBe(false);
  10. graph.removeNode('Bar');
  11. expect(graph.hasNode('Bar')).toBe(false);
  12. });
  13. it('should calculate its size', function () {
  14. var graph = new DepGraph();
  15. expect(graph.size()).toEqual(0);
  16. graph.addNode('Foo');
  17. graph.addNode('Bar');
  18. expect(graph.size()).toEqual(2);
  19. graph.removeNode('Bar');
  20. expect(graph.size()).toEqual(1);
  21. });
  22. it('should treat the node data parameter as optional and use the node name as data if node data was not given', function () {
  23. var graph = new DepGraph();
  24. graph.addNode('Foo');
  25. expect(graph.getNodeData('Foo')).toBe('Foo');
  26. });
  27. it('should be able to associate a node name with data on node add', function () {
  28. var graph = new DepGraph();
  29. graph.addNode('Foo', 'data');
  30. expect(graph.getNodeData('Foo')).toBe('data');
  31. });
  32. it('should be able to add undefined as node data', function () {
  33. var graph = new DepGraph();
  34. graph.addNode('Foo', undefined);
  35. expect(graph.getNodeData('Foo')).toBe(undefined);
  36. });
  37. it('should return true when using hasNode with a node which has falsy data', function () {
  38. var graph = new DepGraph();
  39. var falsyData = ['', 0, null, undefined, false];
  40. graph.addNode('Foo');
  41. falsyData.forEach(function(data) {
  42. graph.setNodeData('Foo', data);
  43. expect(graph.hasNode('Foo')).toBe(true);
  44. // Just an extra check to make sure that the saved data is correct
  45. expect(graph.getNodeData('Foo')).toBe(data);
  46. });
  47. });
  48. it('should be able to set data after a node was added', function () {
  49. var graph = new DepGraph();
  50. graph.addNode('Foo', 'data');
  51. graph.setNodeData('Foo', 'data2');
  52. expect(graph.getNodeData('Foo')).toBe('data2');
  53. });
  54. it('should throw an error if we try to set data for a non-existing node', function () {
  55. var graph = new DepGraph();
  56. expect(function () {
  57. graph.setNodeData('Foo', 'data');
  58. }).toThrow(new Error('Node does not exist: Foo'));
  59. });
  60. it('should throw an error if the node does not exists and we try to get data', function () {
  61. var graph = new DepGraph();
  62. expect(function () {
  63. graph.getNodeData('Foo');
  64. }).toThrow(new Error('Node does not exist: Foo'));
  65. });
  66. it('should do nothing if creating a node that already exists', function () {
  67. var graph = new DepGraph();
  68. graph.addNode('a');
  69. graph.addNode('b');
  70. graph.addDependency('a','b');
  71. graph.addNode('a');
  72. expect(graph.dependenciesOf('a')).toEqual(['b']);
  73. });
  74. it('should do nothing if removing a node that does not exist', function () {
  75. var graph = new DepGraph();
  76. graph.addNode('a');
  77. expect(graph.hasNode('a')).toBe(true);
  78. graph.removeNode('a');
  79. expect(graph.hasNode('Foo')).toBe(false);
  80. graph.removeNode('a');
  81. expect(graph.hasNode('Foo')).toBe(false);
  82. });
  83. it('should be able to add dependencies between nodes', function () {
  84. var graph = new DepGraph();
  85. graph.addNode('a');
  86. graph.addNode('b');
  87. graph.addNode('c');
  88. graph.addDependency('a','b');
  89. graph.addDependency('a','c');
  90. expect(graph.dependenciesOf('a')).toEqual(['b', 'c']);
  91. });
  92. it('should throw an error if a node does not exist and a dependency is added', function () {
  93. var graph = new DepGraph();
  94. graph.addNode('a');
  95. expect(function () {
  96. graph.addDependency('a','b');
  97. }).toThrow(new Error('Node does not exist: b'));
  98. });
  99. it('should detect cycles', function () {
  100. var graph = new DepGraph();
  101. graph.addNode('a');
  102. graph.addNode('b');
  103. graph.addNode('c');
  104. graph.addNode('d');
  105. graph.addDependency('a', 'b');
  106. graph.addDependency('b', 'c');
  107. graph.addDependency('c', 'a');
  108. graph.addDependency('d', 'a');
  109. expect(function () {
  110. graph.dependenciesOf('b');
  111. }).toThrow(new Error('Dependency Cycle Found: b -> c -> a -> b'));
  112. });
  113. it('should allow cycles when configured', function () {
  114. var graph = new DepGraph({ circular: true });
  115. graph.addNode('a');
  116. graph.addNode('b');
  117. graph.addNode('c');
  118. graph.addNode('d');
  119. graph.addDependency('a', 'b');
  120. graph.addDependency('b', 'c');
  121. graph.addDependency('c', 'a');
  122. graph.addDependency('d', 'a');
  123. expect(graph.dependenciesOf('b')).toEqual(['a', 'c']);
  124. expect(graph.overallOrder()).toEqual(['c', 'b', 'a', 'd']);
  125. });
  126. it('should detect cycles in overall order', function () {
  127. var graph = new DepGraph();
  128. graph.addNode('a');
  129. graph.addNode('b');
  130. graph.addNode('c');
  131. graph.addNode('d');
  132. graph.addDependency('a', 'b');
  133. graph.addDependency('b', 'c');
  134. graph.addDependency('c', 'a');
  135. graph.addDependency('d', 'a');
  136. expect(function () {
  137. graph.overallOrder();
  138. }).toThrow(new Error('Dependency Cycle Found: a -> b -> c -> a'));
  139. });
  140. it('should detect cycles in overall order when all nodes have dependants (incoming edges)', function () {
  141. var graph = new DepGraph();
  142. graph.addNode('a');
  143. graph.addNode('b');
  144. graph.addNode('c');
  145. graph.addDependency('a', 'b');
  146. graph.addDependency('b', 'c');
  147. graph.addDependency('c', 'a');
  148. expect(function () {
  149. graph.overallOrder();
  150. }).toThrow(new Error('Dependency Cycle Found: a -> b -> c -> a'));
  151. });
  152. it('should detect cycles in overall order when there are several ' +
  153. 'disconnected subgraphs (with one that does not have a cycle', function () {
  154. var graph = new DepGraph();
  155. graph.addNode('a_1');
  156. graph.addNode('a_2');
  157. graph.addNode('b_1');
  158. graph.addNode('b_2');
  159. graph.addNode('b_3');
  160. graph.addDependency('a_1', 'a_2');
  161. graph.addDependency('b_1', 'b_2');
  162. graph.addDependency('b_2', 'b_3');
  163. graph.addDependency('b_3', 'b_1');
  164. expect(function () {
  165. graph.overallOrder();
  166. }).toThrow(new Error('Dependency Cycle Found: b_1 -> b_2 -> b_3 -> b_1'));
  167. });
  168. it('should retrieve dependencies and dependants in the correct order', function () {
  169. var graph = new DepGraph();
  170. graph.addNode('a');
  171. graph.addNode('b');
  172. graph.addNode('c');
  173. graph.addNode('d');
  174. graph.addDependency('a', 'd');
  175. graph.addDependency('a', 'b');
  176. graph.addDependency('b', 'c');
  177. graph.addDependency('d', 'b');
  178. expect(graph.dependenciesOf('a')).toEqual(['c', 'b', 'd']);
  179. expect(graph.dependenciesOf('b')).toEqual(['c']);
  180. expect(graph.dependenciesOf('c')).toEqual([]);
  181. expect(graph.dependenciesOf('d')).toEqual(['c', 'b']);
  182. expect(graph.dependantsOf('a')).toEqual([]);
  183. expect(graph.dependantsOf('b')).toEqual(['a','d']);
  184. expect(graph.dependantsOf('c')).toEqual(['a','d','b']);
  185. expect(graph.dependantsOf('d')).toEqual(['a']);
  186. });
  187. it('should be able to resolve the overall order of things', function () {
  188. var graph = new DepGraph();
  189. graph.addNode('a');
  190. graph.addNode('b');
  191. graph.addNode('c');
  192. graph.addNode('d');
  193. graph.addNode('e');
  194. graph.addDependency('a', 'b');
  195. graph.addDependency('a', 'c');
  196. graph.addDependency('b', 'c');
  197. graph.addDependency('c', 'd');
  198. expect(graph.overallOrder()).toEqual(['d', 'c', 'b', 'a', 'e']);
  199. });
  200. it('should be able to only retrieve the "leaves" in the overall order', function () {
  201. var graph = new DepGraph();
  202. graph.addNode('a');
  203. graph.addNode('b');
  204. graph.addNode('c');
  205. graph.addNode('d');
  206. graph.addNode('e');
  207. graph.addDependency('a', 'b');
  208. graph.addDependency('a', 'c');
  209. graph.addDependency('b', 'c');
  210. graph.addDependency('c', 'd');
  211. expect(graph.overallOrder(true)).toEqual(['d', 'e']);
  212. });
  213. it('should be able to give the overall order for a graph with several disconnected subgraphs', function () {
  214. var graph = new DepGraph();
  215. graph.addNode('a_1');
  216. graph.addNode('a_2');
  217. graph.addNode('b_1');
  218. graph.addNode('b_2');
  219. graph.addNode('b_3');
  220. graph.addDependency('a_1', 'a_2');
  221. graph.addDependency('b_1', 'b_2');
  222. graph.addDependency('b_2', 'b_3');
  223. expect(graph.overallOrder()).toEqual(['a_2', 'a_1', 'b_3', 'b_2', 'b_1']);
  224. });
  225. it('should give an empty overall order for an empty graph', function () {
  226. var graph = new DepGraph();
  227. expect(graph.overallOrder()).toEqual([]);
  228. });
  229. it('should still work after nodes are removed', function () {
  230. var graph = new DepGraph();
  231. graph.addNode('a');
  232. graph.addNode('b');
  233. graph.addNode('c');
  234. graph.addDependency('a', 'b');
  235. graph.addDependency('b', 'c');
  236. expect(graph.dependenciesOf('a')).toEqual(['c', 'b']);
  237. graph.removeNode('c');
  238. expect(graph.dependenciesOf('a')).toEqual(['b']);
  239. });
  240. it('should clone an empty graph', function () {
  241. var graph = new DepGraph();
  242. expect(graph.size()).toEqual(0);
  243. var cloned = graph.clone();
  244. expect(cloned.size()).toEqual(0);
  245. expect(graph === cloned).toBe(false);
  246. });
  247. it('should clone a non-empty graph', function () {
  248. var graph = new DepGraph();
  249. graph.addNode('a');
  250. graph.addNode('b');
  251. graph.addNode('c');
  252. graph.addDependency('a', 'b');
  253. graph.addDependency('b', 'c');
  254. var cloned = graph.clone();
  255. expect(graph === cloned).toBe(false);
  256. expect(cloned.hasNode('a')).toBe(true);
  257. expect(cloned.hasNode('b')).toBe(true);
  258. expect(cloned.hasNode('c')).toBe(true);
  259. expect(cloned.dependenciesOf('a')).toEqual(['c', 'b']);
  260. expect(cloned.dependantsOf('c')).toEqual(['a', 'b']);
  261. // Changes to the original graph shouldn't affect the clone
  262. graph.removeNode('c');
  263. expect(graph.dependenciesOf('a')).toEqual(['b']);
  264. expect(cloned.dependenciesOf('a')).toEqual(['c', 'b']);
  265. graph.addNode('d');
  266. graph.addDependency('b', 'd');
  267. expect(graph.dependenciesOf('a')).toEqual(['d', 'b']);
  268. expect(cloned.dependenciesOf('a')).toEqual(['c', 'b']);
  269. });
  270. it('should only be a shallow clone', function () {
  271. var graph = new DepGraph();
  272. var data = {a: 42};
  273. graph.addNode('a', data);
  274. var cloned = graph.clone();
  275. expect(graph === cloned).toBe(false);
  276. expect(graph.getNodeData('a') === cloned.getNodeData('a')).toBe(true);
  277. graph.getNodeData('a').a = 43;
  278. expect(cloned.getNodeData('a').a).toEqual(43);
  279. cloned.setNodeData('a', {a: 42});
  280. expect(cloned.getNodeData('a').a).toEqual(42);
  281. expect(graph.getNodeData('a') === cloned.getNodeData('a')).toBe(false);
  282. });
  283. });