private Dictionary<KspPartObject, List<KspPartLinkProperty>> FindPartDependencies (KspCraftObject craft, RegexFilter filter)
        {
            ui.DisplayUserMessage ($"Entering craft '{craft.Name}'...");

            var partLookup = new PartLookup (craft);
            var dependencies = partLookup.LookupParts (filter).ToList ();

            var dependentParts = new HashSet<KspPartObject> ();
            Parallel.ForEach (dependencies, dependency => {
                foreach (var part in partLookup.LookupSoftDependencies (dependency)) {
                    lock (dependentParts) {
                        dependentParts.Add (part);
                    }
                }
            });

            ui.DisplayUserMessage ($"Found {dependentParts.Count} dependent parts");

            return dependentParts.ToDictionary (part => part, part => FindPartLinks (part, dependencies));
        }
        public void CanLookupSoftDependenciesOnAGivenPart ()
        {
            // given
            var craft = createTestCraft ();
            var parts = craft.Children<KspPartObject> ().ToArray ();

            var dependency = parts [0];
            var expectedDependencies = new[] {
                parts [3], // srfN to [0]
                parts [6], // link to [0]
                parts [7], // srfN to [0]
                parts [8], // attN to [0]
            };

            var target = new PartLookup (craft);

            // when
            var actualDependencies = target.LookupSoftDependencies (dependency);

            // then
            Assert.That (actualDependencies, Is.EqualTo (expectedDependencies));
        }