16
16
17
17
namespace Xtensive . Orm . Tests . Core . Helpers
18
18
{
19
- [ TestFixture ]
20
- public class TopologicalSorterTest
19
+ [ TestFixture ]
20
+ public class TopologicalSorterTest
21
+ {
22
+ [ Test , Explicit ]
23
+ public void PerformanceTest ( )
21
24
{
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
+ }
41
41
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
+ }
70
50
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 ( ) ;
84
60
}
61
+ }
85
62
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
+ }
122
71
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
+ }
131
189
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 ] ) ;
158
217
}
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
+ }
159
275
}
276
+ }
160
277
}
0 commit comments