Path: blob/main/src/vs/editor/contrib/inlineCompletions/test/browser/graph.test.ts
4798 views
/*---------------------------------------------------------------------------------------------1* Copyright (c) Microsoft Corporation. All rights reserved.2* Licensed under the MIT License. See License.txt in the project root for license information.3*--------------------------------------------------------------------------------------------*/45import assert from 'assert';6import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js';7import { DirectedGraph } from '../../browser/model/graph.js';89suite('DirectedGraph', () => {10ensureNoDisposablesAreLeakedInTestSuite();1112test('from - creates empty graph', () => {13const graph = DirectedGraph.from<string>([], () => []);14assert.deepStrictEqual(graph.getOutgoing('a'), []);15});1617test('from - creates graph with single node', () => {18const graph = DirectedGraph.from(['a'], () => []);19assert.deepStrictEqual(graph.getOutgoing('a'), []);20});2122test('from - creates graph with nodes and edges', () => {23const nodes = ['a', 'b', 'c'];24const getOutgoing = (node: string) => {25switch (node) {26case 'a':27return ['b', 'c'];28case 'b':29return ['c'];30case 'c':31return [];32default:33return [];34}35};3637const graph = DirectedGraph.from(nodes, getOutgoing);3839assert.deepStrictEqual([...graph.getOutgoing('a')].sort(), ['b', 'c']);40assert.deepStrictEqual(graph.getOutgoing('b'), ['c']);41assert.deepStrictEqual(graph.getOutgoing('c'), []);42});4344test('from - handles duplicate edges', () => {45const nodes = ['a', 'b'];46const getOutgoing = (node: string) => {47switch (node) {48case 'a':49return ['b', 'b']; // Duplicate edge50case 'b':51return [];52default:53return [];54}55};5657const graph = DirectedGraph.from(nodes, getOutgoing);5859assert.deepStrictEqual(graph.getOutgoing('a'), ['b']);60assert.deepStrictEqual(graph.getOutgoing('b'), []);61});6263test('removeCycles - no cycles', () => {64const nodes = ['a', 'b', 'c'];65const getOutgoing = (node: string) => {66switch (node) {67case 'a':68return ['b'];69case 'b':70return ['c'];71case 'c':72return [];73default:74return [];75}76};7778const graph = DirectedGraph.from(nodes, getOutgoing);79const result = graph.removeCycles();8081assert.deepStrictEqual(result.foundCycles, []);82assert.deepStrictEqual(graph.getOutgoing('a'), ['b']);83assert.deepStrictEqual(graph.getOutgoing('b'), ['c']);84assert.deepStrictEqual(graph.getOutgoing('c'), []);85});8687test('removeCycles - simple cycle', () => {88const nodes = ['a', 'b'];89const getOutgoing = (node: string) => {90switch (node) {91case 'a':92return ['b'];93case 'b':94return ['a']; // Creates cycle95default:96return [];97}98};99100const graph = DirectedGraph.from(nodes, getOutgoing);101const result = graph.removeCycles();102103assert.strictEqual(result.foundCycles.length, 1);104assert.ok(105result.foundCycles.includes('a') || result.foundCycles.includes('b')106);107108// After removing cycles, one of the edges should be removed109const aOutgoing = graph.getOutgoing('a');110const bOutgoing = graph.getOutgoing('b');111assert.ok(112(aOutgoing.length === 0 && bOutgoing.length === 1) ||113(aOutgoing.length === 1 && bOutgoing.length === 0)114);115});116117test('removeCycles - self loop', () => {118const nodes = ['a'];119const getOutgoing = (node: string) => {120switch (node) {121case 'a':122return ['a']; // Self loop123default:124return [];125}126};127128const graph = DirectedGraph.from(nodes, getOutgoing);129const result = graph.removeCycles();130131assert.deepStrictEqual(result.foundCycles, ['a']);132assert.deepStrictEqual(graph.getOutgoing('a'), []);133});134135test('removeCycles - complex cycle', () => {136const nodes = ['a', 'b', 'c', 'd'];137const getOutgoing = (node: string) => {138switch (node) {139case 'a':140return ['b'];141case 'b':142return ['c'];143case 'c':144return ['d', 'a']; // Creates cycle back to 'a'145case 'd':146return [];147default:148return [];149}150};151152const graph = DirectedGraph.from(nodes, getOutgoing);153const result = graph.removeCycles();154155assert.ok(result.foundCycles.length >= 1);156157// After removing cycles, there should be no path back to 'a' from 'c'158const cOutgoing = graph.getOutgoing('c');159assert.ok(!cOutgoing.includes('a'));160});161162test('removeCycles - multiple disconnected cycles', () => {163const nodes = ['a', 'b', 'c', 'd'];164const getOutgoing = (node: string) => {165switch (node) {166case 'a':167return ['b'];168case 'b':169return ['a']; // Cycle 1: a <-> b170case 'c':171return ['d'];172case 'd':173return ['c']; // Cycle 2: c <-> d174default:175return [];176}177};178179const graph = DirectedGraph.from(nodes, getOutgoing);180const result = graph.removeCycles();181182assert.ok(result.foundCycles.length >= 2);183184// After removing cycles, each pair should have only one direction185const aOutgoing = graph.getOutgoing('a');186const bOutgoing = graph.getOutgoing('b');187const cOutgoing = graph.getOutgoing('c');188const dOutgoing = graph.getOutgoing('d');189190assert.ok(191(aOutgoing.length === 0 && bOutgoing.length === 1) ||192(aOutgoing.length === 1 && bOutgoing.length === 0)193);194assert.ok(195(cOutgoing.length === 0 && dOutgoing.length === 1) ||196(cOutgoing.length === 1 && dOutgoing.length === 0)197);198});199200test('getOutgoing - non-existent node', () => {201const graph = DirectedGraph.from(['a'], () => []);202assert.deepStrictEqual(graph.getOutgoing('b'), []);203});204205test('with number nodes', () => {206const nodes = [1, 2, 3];207const getOutgoing = (node: number) => {208switch (node) {209case 1:210return [2, 3];211case 2:212return [3];213case 3:214return [];215default:216return [];217}218};219220const graph = DirectedGraph.from(nodes, getOutgoing);221222assert.deepStrictEqual([...graph.getOutgoing(1)].sort(), [2, 3]);223assert.deepStrictEqual(graph.getOutgoing(2), [3]);224assert.deepStrictEqual(graph.getOutgoing(3), []);225});226});227228229