1616
1717namespace Xtensive . Orm . Tests . Core . Helpers
1818{
19- [ TestFixture ]
20- public class TopologicalSorterTest
19+ [ TestFixture ]
20+ public class TopologicalSorterTest
21+ {
22+ [ Test , Explicit ]
23+ public void PerformanceTest ( )
2124 {
22- [ Test , Explicit ]
23- public void PerformanceTest ( )
24- {
25- using ( TestLog . InfoRegion ( "No loops" ) ) {
26- InternalPerformanceTest ( 10000 , 10 , false ) ;
27- InternalPerformanceTest ( 100 , 10 , false ) ;
28- InternalPerformanceTest ( 1000 , 10 , false ) ;
29- InternalPerformanceTest ( 10000 , 10 , false ) ;
30- InternalPerformanceTest ( 100000 , 10 , false ) ;
31- }
32- TestLog . Info ( "" ) ;
33- using ( TestLog . InfoRegion ( "With loop removal" ) ) {
34- InternalPerformanceTest ( 10000 , 10 , true ) ;
35- InternalPerformanceTest ( 100 , 10 , true ) ;
36- InternalPerformanceTest ( 1000 , 10 , true ) ;
37- InternalPerformanceTest ( 10000 , 10 , true ) ;
38- InternalPerformanceTest ( 100000 , 10 , true ) ;
39- }
40- }
25+ using ( TestLog . InfoRegion ( "No loops" ) ) {
26+ InternalPerformanceTest ( 10000 , 10 , false ) ;
27+ InternalPerformanceTest ( 100 , 10 , false ) ;
28+ InternalPerformanceTest ( 1000 , 10 , false ) ;
29+ InternalPerformanceTest ( 10000 , 10 , false ) ;
30+ InternalPerformanceTest ( 100000 , 10 , false ) ;
31+ }
32+ TestLog . Info ( "" ) ;
33+ using ( TestLog . InfoRegion ( "With loop removal" ) ) {
34+ InternalPerformanceTest ( 10000 , 10 , true ) ;
35+ InternalPerformanceTest ( 100 , 10 , true ) ;
36+ InternalPerformanceTest ( 1000 , 10 , true ) ;
37+ InternalPerformanceTest ( 10000 , 10 , true ) ;
38+ InternalPerformanceTest ( 100000 , 10 , true ) ;
39+ }
40+ }
4141
42- private static void InternalPerformanceTest ( int nodeCount , int averageConnectionCount , bool allowLoops )
43- {
44- TestLog . Info ( "Building graph: {0} nodes, {1} connections/node in average." , nodeCount , averageConnectionCount ) ;
45- var rnd = new Random ( ) ;
46- var nodes = new List < Node < int , int > > ( ) ;
47- for ( int i = 0 ; i < nodeCount ; i ++ )
48- nodes . Add ( new Node < int , int > ( i ) ) ;
49- int connectionCount = 0 ;
50- foreach ( var from in nodes ) {
51- int outgoingConnectionCount = rnd . Next ( averageConnectionCount ) ;
52- for ( int i = 0 ; i < outgoingConnectionCount ; i ++ ) {
53- var to = nodes [ rnd . Next ( allowLoops ? nodeCount : @from . Item ) ] ;
54- if ( from == to )
55- continue ;
56- var c = new NodeConnection < int , int > ( @from , to , connectionCount ++ ) ;
57- c . BindToNodes ( ) ;
58- }
59- }
60-
61- GC . GetTotalMemory ( true ) ;
62- using ( new Measurement ( "Sorting" , nodeCount + connectionCount ) ) {
63- List < Node < int , int > > removedEdges ;
64- var result = TopologicalSorter . Sort ( nodes , out removedEdges ) ;
65- if ( ! allowLoops )
66- Assert . AreEqual ( nodeCount , result . Count ) ;
67- }
68- GC . GetTotalMemory ( true ) ;
69- }
42+ private static void InternalPerformanceTest ( int nodeCount , int averageConnectionCount , bool allowLoops )
43+ {
44+ TestLog . Info ( "Building graph: {0} nodes, {1} connections/node in average." , nodeCount , averageConnectionCount ) ;
45+ var rnd = new Random ( ) ;
46+ var nodes = new List < Node < int , int > > ( ) ;
47+ for ( var i = 0 ; i < nodeCount ; i ++ ) {
48+ nodes . Add ( new Node < int , int > ( i ) ) ;
49+ }
7050
71- [ Test ]
72- public void SelfReferenceTest ( )
73- {
74- var node = new Node < int , string > ( 1 ) ;
75- var connection = new NodeConnection < int , string > ( node , node , "ConnectionItem" ) ;
76- connection . BindToNodes ( ) ;
77-
78- List < NodeConnection < int , string > > removedEdges ;
79- List < int > result = TopologicalSorter . Sort ( EnumerableUtils . One ( node ) , out removedEdges ) ;
80- Assert . AreEqual ( 1 , result . Count ) ;
81- Assert . AreEqual ( node . Item , result [ 0 ] ) ;
82- Assert . AreEqual ( 1 , removedEdges . Count ) ;
83- Assert . AreEqual ( connection , removedEdges [ 0 ] ) ;
51+ int connectionCount = 0 ;
52+ foreach ( var from in nodes ) {
53+ var outgoingConnectionCount = rnd . Next ( averageConnectionCount ) ;
54+ for ( var i = 0 ; i < outgoingConnectionCount ; i ++ ) {
55+ var to = nodes [ rnd . Next ( allowLoops ? nodeCount : @from . Item ) ] ;
56+ if ( from == to )
57+ continue ;
58+ var c = new NodeConnection < int , int > ( @from , to , connectionCount ++ ) ;
59+ c . BindToNodes ( ) ;
8460 }
61+ }
8562
86- [ Test ]
87- public void RemoveWholeNodeTest ( )
88- {
89- var node1 = new Node < int , string > ( 1 ) ;
90- var node2 = new Node < int , string > ( 2 ) ;
91- var connection12_1 = new NodeConnection < int , string > ( node1 , node2 , "ConnectionItem 1->2 1" ) ;
92- connection12_1 . BindToNodes ( ) ;
93- var connection12_2 = new NodeConnection < int , string > ( node1 , node2 , "ConnectionItem 1->2 2" ) ;
94- connection12_2 . BindToNodes ( ) ;
95- var connection21_1 = new NodeConnection < int , string > ( node2 , node1 , "ConnectionItem 2->1 1" ) ;
96- connection21_1 . BindToNodes ( ) ;
97-
98- // Remove edge by edge.
99-
100- List < NodeConnection < int , string > > removedEdges ;
101- List < int > result = TopologicalSorter . Sort ( new [ ] { node2 , node1 } , out removedEdges ) ;
102- Assert . AreEqual ( 2 , result . Count ) ;
103- Assert . AreEqual ( node1 . Item , result [ 0 ] ) ;
104- Assert . AreEqual ( node2 . Item , result [ 1 ] ) ;
105-
106- Assert . AreEqual ( 1 , removedEdges . Count ) ;
107- Assert . AreEqual ( connection21_1 , removedEdges [ 0 ] ) ;
108-
109- // Remove whole node
110- connection12_1 . BindToNodes ( ) ;
111- connection12_2 . BindToNodes ( ) ;
112- connection21_1 . BindToNodes ( ) ;
113-
114- result = TopologicalSorter . Sort ( new [ ] { node2 , node1 } , out removedEdges , true ) ;
115- Assert . AreEqual ( 2 , result . Count ) ;
116- Assert . AreEqual ( node1 . Item , result [ 1 ] ) ;
117- Assert . AreEqual ( node2 . Item , result [ 0 ] ) ;
118-
119- Assert . AreEqual ( 2 , removedEdges . Count ) ;
120- Assert . AreEqual ( 0 , removedEdges . Except ( new [ ] { connection12_1 , connection12_2 } ) . Count ( ) ) ;
121- }
63+ _ = GC . GetTotalMemory ( true ) ;
64+ using ( new Measurement ( "Sorting" , nodeCount + connectionCount ) ) {
65+ var result = TopologicalSorter . Sort ( nodes , out var _ ) ;
66+ if ( ! allowLoops )
67+ Assert . AreEqual ( nodeCount , result . Count ) ;
68+ }
69+ _ = GC . GetTotalMemory ( true ) ;
70+ }
12271
123- [ Test ]
124- public void CombinedTest ( )
125- {
126- TestSort ( new [ ] { 4 , 3 , 2 , 1 } , ( i1 , i2 ) => ! ( i1 == 3 || i2 == 3 ) , null , new [ ] { 4 , 2 , 1 } ) ;
127- TestSort ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => i1 >= i2 , new [ ] { 1 , 2 , 3 } , null ) ;
128- TestSort ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => true , null , new [ ] { 1 , 2 , 3 } ) ;
129- TestSort ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => false , new [ ] { 3 , 2 , 1 } , null ) ;
130- }
72+ [ Test ]
73+ public void SelfReferenceTest ( )
74+ {
75+ var node = new Node < int , string > ( 1 ) ;
76+ var connection = new NodeConnection < int , string > ( node , node , "ConnectionItem" ) ;
77+ connection . BindToNodes ( ) ;
78+
79+ var result = TopologicalSorter . Sort ( EnumerableUtils . One ( node ) , out var removedEdges ) ;
80+ Assert . AreEqual ( 1 , result . Count ) ;
81+ Assert . AreEqual ( node . Item , result [ 0 ] ) ;
82+ Assert . AreEqual ( 1 , removedEdges . Count ) ;
83+ Assert . AreEqual ( connection , removedEdges [ 0 ] ) ;
84+ }
85+
86+ [ Test ]
87+ public void NullNodeCollectionTest ( )
88+ {
89+ _ = Assert . Throws < ArgumentNullException > ( ( ) => TopologicalSorter . Sort ( ( IEnumerable < Node < int , string > > ) null , out var _ ) ) ;
90+ _ = Assert . Throws < ArgumentNullException > ( ( ) => TopologicalSorter . Sort ( ( IEnumerable < Node < int , string > > ) null , out var _ , false ) ) ;
91+ _ = Assert . Throws < ArgumentNullException > ( ( ) => TopologicalSorter . Sort ( ( IEnumerable < Node < int , string > > ) null , out var _ , true ) ) ;
92+ _ = Assert . Throws < ArgumentNullException > ( ( ) => TopologicalSorter . Sort ( ( List < Node < int , int > > ) null , out _ ) ) ;
93+ }
94+
95+ [ Test ]
96+ public void EmptyNodeCollectionTest ( )
97+ {
98+ _ = TopologicalSorter . Sort ( Enumerable . Empty < Node < int , string > > ( ) , out _ ) ;
99+ _ = TopologicalSorter . Sort ( Enumerable . Empty < Node < int , string > > ( ) , out _ , false ) ;
100+ _ = TopologicalSorter . Sort ( Enumerable . Empty < Node < int , string > > ( ) , out _ , true ) ;
101+ _ = TopologicalSorter . Sort ( new List < Node < int , int > > ( ) , out _ ) ;
102+ }
103+
104+ [ Test ]
105+ public void FullCircleTest ( )
106+ {
107+ var nodes = new List < Node < int , int > > ( ) ;
108+ for ( var i = 0 ; i < 3 ; i ++ ) {
109+ nodes . Add ( new Node < int , int > ( i ) ) ;
110+ }
111+
112+ var c = new NodeConnection < int , int > ( nodes [ 0 ] , nodes [ 1 ] , 1 ) ;
113+ c . BindToNodes ( ) ;
114+ c = new NodeConnection < int , int > ( nodes [ 1 ] , nodes [ 2 ] , 2 ) ;
115+ c . BindToNodes ( ) ;
116+ c = new NodeConnection < int , int > ( nodes [ 2 ] , nodes [ 0 ] , 3 ) ;
117+ c . BindToNodes ( ) ;
118+
119+ var result = TopologicalSorter . Sort ( nodes , out var removedEdges ) ;
120+ Assert . That ( result , Is . Null ) ;
121+ }
122+
123+ [ Test ]
124+ public void RemoveWholeNodeTest ( )
125+ {
126+ var node1 = new Node < int , string > ( 1 ) ;
127+ var node2 = new Node < int , string > ( 2 ) ;
128+ var connection12_1 = new NodeConnection < int , string > ( node1 , node2 , "ConnectionItem 1->2 1" ) ;
129+ connection12_1 . BindToNodes ( ) ;
130+ var connection12_2 = new NodeConnection < int , string > ( node1 , node2 , "ConnectionItem 1->2 2" ) ;
131+ connection12_2 . BindToNodes ( ) ;
132+ var connection21_1 = new NodeConnection < int , string > ( node2 , node1 , "ConnectionItem 2->1 1" ) ;
133+ connection21_1 . BindToNodes ( ) ;
134+
135+ // Remove edge by edge.
136+ var result = TopologicalSorter . Sort ( new [ ] { node2 , node1 } , out var removedEdges ) ;
137+ Assert . AreEqual ( 2 , result . Count ) ;
138+ Assert . AreEqual ( node1 . Item , result [ 0 ] ) ;
139+ Assert . AreEqual ( node2 . Item , result [ 1 ] ) ;
140+
141+ Assert . AreEqual ( 1 , removedEdges . Count ) ;
142+ Assert . AreEqual ( connection21_1 , removedEdges [ 0 ] ) ;
143+
144+ // Remove whole node
145+ connection12_1 . BindToNodes ( ) ;
146+ connection12_2 . BindToNodes ( ) ;
147+ connection21_1 . BindToNodes ( ) ;
148+
149+ result = TopologicalSorter . Sort ( new [ ] { node2 , node1 } , out removedEdges , true ) ;
150+ Assert . AreEqual ( 2 , result . Count ) ;
151+ Assert . AreEqual ( node1 . Item , result [ 1 ] ) ;
152+ Assert . AreEqual ( node2 . Item , result [ 0 ] ) ;
153+
154+ Assert . AreEqual ( 2 , removedEdges . Count ) ;
155+ Assert . AreEqual ( 0 , removedEdges . Except ( new [ ] { connection12_1 , connection12_2 } ) . Count ( ) ) ;
156+ }
157+
158+ [ Test ]
159+ public void CombinedTest ( )
160+ {
161+ TestSortLoopsCheck ( new [ ] { 4 , 3 , 2 , 1 } , ( i1 , i2 ) => ! ( i1 == 3 || i2 == 3 ) , null , new [ ] { 4 , 2 , 1 } ) ;
162+ TestSortLoopsCheck ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => i1 >= i2 , new [ ] { 3 , 2 , 1 } , null ) ;
163+ TestSortLoopsCheck ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => true , null , new [ ] { 1 , 2 , 3 } ) ;
164+ TestSortLoopsCheck ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => false , new [ ] { 3 , 2 , 1 } , null ) ;
165+ TestSortLoopsCheck ( Array . Empty < int > ( ) , ( i1 , i2 ) => true , Array . Empty < int > ( ) , null ) ;
166+ TestSortLoopsCheck ( Array . Empty < int > ( ) , ( i1 , i2 ) => false , Array . Empty < int > ( ) , null ) ;
167+ _ = Assert . Throws < ArgumentNullException > ( ( ) => TestSortLoopsCheck < int > ( null , ( i1 , i2 ) => true , null , null ) ) ;
168+ _ = Assert . Throws < ArgumentNullException > ( ( ) => TestSortLoopsCheck < int > ( null , ( i1 , i2 ) => false , null , null ) ) ;
169+
170+ TestSortNoLoopsCheck ( new [ ] { 4 , 3 , 2 , 1 } , ( i1 , i2 ) => ! ( i1 == 3 || i2 == 3 ) , null ) ;
171+ TestSortNoLoopsCheck ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => i1 >= i2 , new [ ] { 3 , 2 , 1 } ) ;
172+ TestSortNoLoopsCheck ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => true , null ) ;
173+ TestSortNoLoopsCheck ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => false , new [ ] { 3 , 2 , 1 } ) ;
174+ TestSortNoLoopsCheck ( Array . Empty < int > ( ) , ( i1 , i2 ) => true , Array . Empty < int > ( ) ) ;
175+ TestSortNoLoopsCheck ( Array . Empty < int > ( ) , ( i1 , i2 ) => false , Array . Empty < int > ( ) ) ;
176+ _ = Assert . Throws < ArgumentNullException > ( ( ) => TestSortNoLoopsCheck < int > ( null , ( i1 , i2 ) => true , null ) ) ;
177+ _ = Assert . Throws < ArgumentNullException > ( ( ) => TestSortNoLoopsCheck < int > ( null , ( i1 , i2 ) => false , null ) ) ;
178+
179+ TestEdgeRemoval ( new [ ] { 4 , 3 , 2 , 1 } , ( i1 , i2 ) => ! ( i1 == 3 || i2 == 3 ) , new [ ] { 3 , 1 , 2 , 4 } , new [ ] { ( 4 , 2 ) , ( 4 , 1 ) , ( 2 , 1 ) } ) ;
180+ TestEdgeRemoval ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => i1 >= i2 , new [ ] { 3 , 2 , 1 } , null ) ;
181+ TestEdgeRemoval ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => true , new [ ] { 1 , 2 , 3 } , new [ ] { ( 3 , 2 ) , ( 2 , 1 ) , ( 3 , 1 ) } ) ;
182+ TestEdgeRemoval ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => false , new [ ] { 3 , 2 , 1 } , null ) ;
183+
184+ TestEdgeRemovalWithNode ( new [ ] { 4 , 3 , 2 , 1 } , ( i1 , i2 ) => ! ( i1 == 3 || i2 == 3 ) , new [ ] { 3 , 1 , 2 , 4 } , new [ ] { ( 4 , 2 ) , ( 4 , 1 ) , ( 2 , 1 ) } ) ;
185+ TestEdgeRemovalWithNode ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => i1 >= i2 , new [ ] { 3 , 2 , 1 } , null ) ;
186+ TestEdgeRemovalWithNode ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => true , new [ ] { 1 , 2 , 3 } , new [ ] { ( 3 , 2 ) , ( 2 , 1 ) , ( 3 , 1 ) } ) ;
187+ TestEdgeRemovalWithNode ( new [ ] { 3 , 2 , 1 } , ( i1 , i2 ) => false , new [ ] { 3 , 2 , 1 } , null ) ;
188+ }
131189
132- private void TestSort < T > ( T [ ] data , Predicate < T , T > connector , T [ ] expected , T [ ] loops )
133- {
134- List < Node < T , object > > actualLoopNodes ;
135- List < T > actual = TopologicalSorter . Sort ( data , connector , out actualLoopNodes ) ;
136- T [ ] actualLoops = null ;
137- if ( actualLoopNodes != null )
138- actualLoops = actualLoopNodes
139- . Where ( n => n . OutgoingConnectionCount != 0 )
140- . Select ( n => n . Item )
141- . ToArray ( ) ;
142-
143- AssertEx . HasSameElements ( expected , actual ) ;
144- AssertEx . HasSameElements ( loops , actualLoops ) ;
145-
146- List < NodeConnection < T , object > > removedEdges ;
147- List < T > sortWithRemove = TopologicalSorter . Sort ( data , connector , out removedEdges ) ;
148- Assert . AreEqual ( sortWithRemove . Count , data . Length ) ;
149- if ( loops == null ) {
150- Assert . AreEqual ( sortWithRemove . Count , actual . Count ) ;
151- for ( int i = 0 ; i < actual . Count ; i ++ ) {
152- Assert . AreEqual ( sortWithRemove [ i ] , actual [ i ] ) ;
153- }
154- }
155- else {
156- TestLog . Debug ( "Loops detected" ) ;
157- }
190+ private void TestSortLoopsCheck < T > ( T [ ] data , Predicate < T , T > connector , T [ ] expected , T [ ] loops )
191+ {
192+ var actual = TopologicalSorter . Sort ( data , connector , out List < Node < T , object > > actualLoopNodes ) ;
193+
194+ if ( expected == null )
195+ Assert . That ( actual , Is . Null ) ;
196+ else if ( data . Length == 0 )
197+ Assert . That ( actual , Is . Empty ) ;
198+ else
199+ Assert . That ( expected . SequenceEqual ( actual ) ) ;
200+
201+ var actualLoops = actualLoopNodes != null
202+ ? actualLoopNodes
203+ . Where ( n => n . OutgoingConnectionCount != 0 )
204+ . Select ( n => n . Item )
205+ . ToArray ( )
206+ : null ;
207+
208+ AssertEx . HasSameElements ( loops , actualLoops ) ;
209+
210+ var sortWithRemove = TopologicalSorter . Sort ( data , connector , out List < NodeConnection < T , object > > removedEdges ) ;
211+ Assert . AreEqual ( sortWithRemove . Count , data . Length ) ;
212+
213+ if ( loops == null ) {
214+ Assert . AreEqual ( sortWithRemove . Count , actual . Count ) ;
215+ for ( var i = 0 ; i < actual . Count ; i ++ ) {
216+ Assert . AreEqual ( sortWithRemove [ i ] , actual [ i ] ) ;
158217 }
218+ }
219+ else {
220+ TestLog . Debug ( "Loops detected" ) ;
221+ }
222+ }
223+
224+ private void TestSortNoLoopsCheck < T > ( T [ ] data , Predicate < T , T > connector , T [ ] expected )
225+ {
226+ var actual = TopologicalSorter . Sort ( data , connector ) ;
227+
228+ if ( expected == null )
229+ Assert . That ( actual , Is . Null ) ;
230+ else if ( data . Length == 0 )
231+ Assert . That ( actual , Is . Empty ) ;
232+ else
233+ Assert . That ( expected . SequenceEqual ( actual ) ) ;
234+
235+ var sortWithRemove = TopologicalSorter . Sort ( data , connector , out List < NodeConnection < T , object > > _ ) ;
236+ Assert . AreEqual ( sortWithRemove . Count , data . Length ) ;
237+ }
238+
239+ private void TestEdgeRemoval < T > ( T [ ] data , Predicate < T , T > connector , T [ ] expected , ( T source , T target ) [ ] expectedRemovedEdges )
240+ {
241+ var sortWithRemove = TopologicalSorter . Sort ( data , connector , out List < NodeConnection < T , object > > removedEdges ) ;
242+ Assert . That ( sortWithRemove , Is . Not . Null ) ;
243+ Assert . AreEqual ( sortWithRemove . Count , data . Length ) ;
244+ Assert . That ( sortWithRemove . SequenceEqual ( expected ) , Is . True ) ;
245+
246+ if ( expectedRemovedEdges == null ) {
247+ Assert . That ( removedEdges , Is . Empty ) ;
248+ }
249+
250+ foreach ( var removedEdge in removedEdges ) {
251+ var s = removedEdge . Source . Item ;
252+ var t = removedEdge . Destination . Item ;
253+ ( T source , T target ) expectedTuple = ( s , t ) ;
254+ Assert . That ( expectedRemovedEdges . Contains ( expectedTuple ) , Is . True , $ "({ s } -> { t } ) is not represented in expected edges") ;
255+ }
256+ }
257+
258+ private void TestEdgeRemovalWithNode < T > ( T [ ] data , Predicate < T , T > connector , T [ ] expected , ( T source , T target ) [ ] expectedRemovedEdges )
259+ {
260+ var sortWithRemove = TopologicalSorter . Sort ( data , connector , out var removedEdges , true ) ;
261+ Assert . That ( sortWithRemove , Is . Not . Null ) ;
262+ Assert . AreEqual ( sortWithRemove . Count , data . Length ) ;
263+ Assert . That ( sortWithRemove . SequenceEqual ( expected ) , Is . True ) ;
264+
265+ if ( expectedRemovedEdges == null ) {
266+ Assert . That ( removedEdges , Is . Empty ) ;
267+ }
268+
269+ foreach ( var removedEdge in removedEdges ) {
270+ var s = removedEdge . Source . Item ;
271+ var t = removedEdge . Destination . Item ;
272+ ( T source , T target ) expectedTuple = ( s , t ) ;
273+ Assert . That ( expectedRemovedEdges . Contains ( expectedTuple ) , Is . True , $ "({ s } -> { t } ) is not represented in expected edges") ;
274+ }
159275 }
276+ }
160277}
0 commit comments