@@ -216,20 +216,106 @@ func TestCalculateDependencyDepth(t *testing.T) {
216216}
217217
218218// TestSortPackagesByDependencyDepth_Stability tests that sorting is stable
219+ // A stable sort preserves the relative order of elements with equal keys
219220func TestSortPackagesByDependencyDepth_Stability (t * testing.T ) {
220- // Create packages with same depth - order should be preserved
221- packages := []* Package {
222- {fullNameOverride : "pkg1" , dependencies : []* Package {}},
223- {fullNameOverride : "pkg2" , dependencies : []* Package {}},
224- {fullNameOverride : "pkg3" , dependencies : []* Package {}},
221+ // Create a shared leaf dependency
222+ leaf := & Package {fullNameOverride : "leaf" , dependencies : []* Package {}}
223+
224+ // Create multiple packages at depth 1 (all depend on leaf)
225+ depth1Packages := []* Package {
226+ {fullNameOverride : "d1-alpha" , dependencies : []* Package {leaf }},
227+ {fullNameOverride : "d1-beta" , dependencies : []* Package {leaf }},
228+ {fullNameOverride : "d1-gamma" , dependencies : []* Package {leaf }},
229+ {fullNameOverride : "d1-delta" , dependencies : []* Package {leaf }},
225230 }
226231
227- sorted := sortPackagesByDependencyDepth (packages )
232+ // Create multiple packages at depth 0 (no dependencies)
233+ depth0Packages := []* Package {
234+ {fullNameOverride : "d0-alpha" , dependencies : []* Package {}},
235+ {fullNameOverride : "d0-beta" , dependencies : []* Package {}},
236+ {fullNameOverride : "d0-gamma" , dependencies : []* Package {}},
237+ }
228238
229- // All have depth 0, so order should be preserved
230- require .Equal (t , "pkg1" , sorted [0 ].FullName ())
231- require .Equal (t , "pkg2" , sorted [1 ].FullName ())
232- require .Equal (t , "pkg3" , sorted [2 ].FullName ())
239+ // Test with different input orderings to verify stability
240+ // The key insight: within each depth group, relative order must be preserved
241+ testCases := []struct {
242+ name string
243+ input []* Package
244+ }{
245+ {
246+ name : "depth1 first, then depth0" ,
247+ input : []* Package {
248+ depth1Packages [0 ], depth1Packages [1 ], depth1Packages [2 ], depth1Packages [3 ],
249+ depth0Packages [0 ], depth0Packages [1 ], depth0Packages [2 ],
250+ },
251+ },
252+ {
253+ name : "depth0 first, then depth1" ,
254+ input : []* Package {
255+ depth0Packages [0 ], depth0Packages [1 ], depth0Packages [2 ],
256+ depth1Packages [0 ], depth1Packages [1 ], depth1Packages [2 ], depth1Packages [3 ],
257+ },
258+ },
259+ {
260+ name : "interleaved" ,
261+ input : []* Package {
262+ depth1Packages [0 ], depth0Packages [0 ], depth1Packages [1 ], depth0Packages [1 ],
263+ depth1Packages [2 ], depth0Packages [2 ], depth1Packages [3 ],
264+ },
265+ },
266+ {
267+ name : "reverse interleaved" ,
268+ input : []* Package {
269+ depth0Packages [2 ], depth1Packages [3 ], depth0Packages [1 ], depth1Packages [2 ],
270+ depth0Packages [0 ], depth1Packages [1 ], depth1Packages [0 ],
271+ },
272+ },
273+ }
274+
275+ for _ , tc := range testCases {
276+ t .Run (tc .name , func (t * testing.T ) {
277+ // Record the input order of packages at each depth
278+ inputOrderDepth0 := []string {}
279+ inputOrderDepth1 := []string {}
280+ for _ , pkg := range tc .input {
281+ if len (pkg .dependencies ) == 0 {
282+ inputOrderDepth0 = append (inputOrderDepth0 , pkg .FullName ())
283+ } else {
284+ inputOrderDepth1 = append (inputOrderDepth1 , pkg .FullName ())
285+ }
286+ }
287+
288+ sorted := sortPackagesByDependencyDepth (tc .input )
289+
290+ // Extract the output order at each depth
291+ outputOrderDepth0 := []string {}
292+ outputOrderDepth1 := []string {}
293+ for _ , pkg := range sorted {
294+ if len (pkg .dependencies ) == 0 {
295+ outputOrderDepth0 = append (outputOrderDepth0 , pkg .FullName ())
296+ } else {
297+ outputOrderDepth1 = append (outputOrderDepth1 , pkg .FullName ())
298+ }
299+ }
300+
301+ // Depth 1 packages should come before depth 0 packages
302+ require .Equal (t , 7 , len (sorted ), "should have all 7 packages" )
303+
304+ // First 4 should be depth 1, last 3 should be depth 0
305+ for i := 0 ; i < 4 ; i ++ {
306+ require .Equal (t , 1 , len (sorted [i ].dependencies ), "first 4 should be depth 1" )
307+ }
308+ for i := 4 ; i < 7 ; i ++ {
309+ require .Equal (t , 0 , len (sorted [i ].dependencies ), "last 3 should be depth 0" )
310+ }
311+
312+ // Stability check: relative order within each depth group must match input order
313+ require .Equal (t , inputOrderDepth1 , outputOrderDepth1 ,
314+ "depth 1 packages should maintain relative input order (stability)" )
315+ require .Equal (t , inputOrderDepth0 , outputOrderDepth0 ,
316+ "depth 0 packages should maintain relative input order (stability)" )
317+ })
318+ }
233319}
234320
235321// TestSortPackagesByDependencyDepth_Performance tests with larger graphs
0 commit comments