@ -48,7 +48,6 @@ using ILSpy.Metadata.CorTables;
using ILSpy.Search ;
using ILSpy.Search ;
using ILSpy.TreeNodes ;
using ILSpy.TreeNodes ;
using ILSpy.ViewModels ;
using ILSpy.ViewModels ;
using ILSpy.Views ;
using NUnit.Framework ;
using NUnit.Framework ;
@ -66,22 +65,16 @@ public class AssemblyTreeTests
// ResourceTreeNode — both are ILSpyTreeNodes).
// ResourceTreeNode — both are ILSpyTreeNodes).
// Arrange — boot, wait for assemblies.
// Arrange — boot, wait for assemblies.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
// Act — pick CoreLib (always ships localised error-message tables), expand it.
// Act — pick CoreLib (always ships localised error-message tables), expand it.
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var assemblyNode = vm . AssemblyTreeModel . FindCoreLib ( ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( coreLibName ) ;
assemblyNode . Expand ( ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . IsExpanded = true ;
// Assert — exactly one Resources folder, named, non-empty, every child an ILSpyTreeNode.
// Assert — exactly one Resources folder, named, non-empty, every child an ILSpyTreeNode.
var resources = assemblyNode . Children . OfType < ResourceListTreeNode > ( ) . Single ( ) ;
var resources = assemblyNode . Children . OfType < ResourceListTreeNode > ( ) . Single ( ) ;
resources . Text . Should ( ) . Be ( Resources . _ Resources ) ;
resources . Text . Should ( ) . Be ( Resources . _ Resources ) ;
resources . EnsureLazyChildren ( ) ;
resources . Expand ( ) ;
resources . IsExpanded = true ;
resources . Children . Should ( ) . NotBeEmpty ( ) ;
resources . Children . Should ( ) . NotBeEmpty ( ) ;
resources . Children . Should ( ) . AllBeAssignableTo < ILSpyTreeNode > ( ) ;
resources . Children . Should ( ) . AllBeAssignableTo < ILSpyTreeNode > ( ) ;
}
}
@ -93,10 +86,7 @@ public class AssemblyTreeTests
// Ctrl-click to multi-select rows.
// Ctrl-click to multi-select rows.
// Arrange — boot, wait for assemblies + AssemblyListPane to materialise.
// Arrange — boot, wait for assemblies + AssemblyListPane to materialise.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( window , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
// Act — locate the realised DataGrid inside the AssemblyListPane.
// Act — locate the realised DataGrid inside the AssemblyListPane.
var pane = await window . WaitForComponent < AssemblyListPane > ( ) ;
var pane = await window . WaitForComponent < AssemblyListPane > ( ) ;
@ -115,15 +105,11 @@ public class AssemblyTreeTests
// flip its IsSelected back without affecting siblings.
// flip its IsSelected back without affecting siblings.
// Arrange — boot, wait for assemblies, expand Enumerable, grab two methods.
// Arrange — boot, wait for assemblies, expand Enumerable, grab two methods.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
"System.Linq" , "System.Linq" , "System.Linq.Enumerable" ) ;
"System.Linq" , "System.Linq" , "System.Linq.Enumerable" ) ;
typeNode . EnsureLazyChildren ( ) ;
typeNode . Expand ( ) ;
typeNode . IsExpanded = true ;
var first = typeNode . Children . OfType < MethodTreeNode > ( )
var first = typeNode . Children . OfType < MethodTreeNode > ( )
. First ( m = > m . MethodDefinition . Name = = "ElementAt" ) ;
. First ( m = > m . MethodDefinition . Name = = "ElementAt" ) ;
var second = typeNode . Children . OfType < MethodTreeNode > ( )
var second = typeNode . Children . OfType < MethodTreeNode > ( )
@ -155,10 +141,7 @@ public class AssemblyTreeTests
// Arrange — boot, wait for assemblies, build a fixture zip with a flat file + two
// Arrange — boot, wait for assemblies, build a fixture zip with a flat file + two
// folders containing one file each.
// folders containing one file each.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var tempDir = Path . Combine ( Path . GetTempPath ( ) , "ILSpy.Tests" , System . Guid . NewGuid ( ) . ToString ( "N" ) ) ;
var tempDir = Path . Combine ( Path . GetTempPath ( ) , "ILSpy.Tests" , System . Guid . NewGuid ( ) . ToString ( "N" ) ) ;
Directory . CreateDirectory ( tempDir ) ;
Directory . CreateDirectory ( tempDir ) ;
@ -174,22 +157,10 @@ public class AssemblyTreeTests
}
}
// Act — open the zip via the OpenCommand and wait for it to load.
// Act — open the zip via the OpenCommand and wait for it to load.
var registry = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) ;
var loaded = await vm . OpenAssemblyAsync ( zipPath ) ;
var openCommand = registry . Commands
. Single ( c = > c . Metadata . Header = = nameof ( Resources . _ Open ) )
. CreateExport ( ) . Value ;
openCommand . Execute ( zipPath ) ;
await Waiters . WaitForAsync ( ( ) = >
vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( ) . Any ( a = >
string . Equals ( a . FileName , zipPath , System . StringComparison . OrdinalIgnoreCase ) ) ) ;
var loaded = vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( )
. First ( a = > string . Equals ( a . FileName , zipPath , System . StringComparison . OrdinalIgnoreCase ) ) ;
await loaded . GetLoadResultAsync ( ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( loaded . ShortName ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( loaded . ShortName ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . Expand ( ) ;
assemblyNode . IsExpanded = true ;
// Assert — both nested folders show as PackageFolderTreeNodes; the flat file shows as
// Assert — both nested folders show as PackageFolderTreeNodes; the flat file shows as
// a ResourceTreeNode with its original entry name.
// a ResourceTreeNode with its original entry name.
@ -212,18 +183,12 @@ public class AssemblyTreeTests
// string-table or inner ResourceEntryNode children (depending on payload type).
// string-table or inner ResourceEntryNode children (depending on payload type).
// Arrange — boot, wait for assemblies, expand CoreLib's Resources folder.
// Arrange — boot, wait for assemblies, expand CoreLib's Resources folder.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var assemblyNode = vm . AssemblyTreeModel . FindCoreLib ( ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( coreLibName ) ;
assemblyNode . Expand ( ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . IsExpanded = true ;
var resources = assemblyNode . Children . OfType < ResourceListTreeNode > ( ) . Single ( ) ;
var resources = assemblyNode . Children . OfType < ResourceListTreeNode > ( ) . Single ( ) ;
resources . EnsureLazyChildren ( ) ;
resources . Expand ( ) ;
resources . IsExpanded = true ;
// Act — look up the first .resources file and select it.
// Act — look up the first .resources file and select it.
var resourceFileNode = resources . Children . OfType < ResourcesFileTreeNode > ( ) . FirstOrDefault ( ) ;
var resourceFileNode = resources . Children . OfType < ResourcesFileTreeNode > ( ) . FirstOrDefault ( ) ;
@ -246,14 +211,10 @@ public class AssemblyTreeTests
// AssemblyReferenceTreeNodes — one per AssemblyRef row.
// AssemblyReferenceTreeNodes — one per AssemblyRef row.
// Arrange — boot, wait for assemblies, expand System.Linq.
// Arrange — boot, wait for assemblies, expand System.Linq.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . Expand ( ) ;
assemblyNode . IsExpanded = true ;
// Act — find the References folder.
// Act — find the References folder.
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
@ -261,8 +222,7 @@ public class AssemblyTreeTests
// Assert — folder is named, expands to non-empty AssemblyReferenceTreeNode children.
// Assert — folder is named, expands to non-empty AssemblyReferenceTreeNode children.
refFolder . Text . Should ( ) . Be ( Resources . References ) ;
refFolder . Text . Should ( ) . Be ( Resources . References ) ;
refFolder . EnsureLazyChildren ( ) ;
refFolder . Expand ( ) ;
refFolder . IsExpanded = true ;
refFolder . Children . Should ( ) . NotBeEmpty ( ) ;
refFolder . Children . Should ( ) . NotBeEmpty ( ) ;
refFolder . Children . Should ( ) . AllBeAssignableTo < AssemblyReferenceTreeNode > ( ) ;
refFolder . Children . Should ( ) . AllBeAssignableTo < AssemblyReferenceTreeNode > ( ) ;
}
}
@ -277,14 +237,10 @@ public class AssemblyTreeTests
// Arrange — boot, wait for assemblies, force lazy children on assembly + References.
// Arrange — boot, wait for assemblies, force lazy children on assembly + References.
// Stay model-only (no IsExpanded = true) — visually expanding rows would render the
// Stay model-only (no IsExpanded = true) — visually expanding rows would render the
// icons, triggering the resolver and losing the lazy "loading" state we're asserting.
// icons, triggering the resolver and losing the lazy "loading" state we're asserting.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
assemblyNode . EnsureLazyChildren ( ) ;
var refFolder = assemblyNode . GetChild < ReferenceFolderTreeNode > ( ) ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
refFolder . EnsureLazyChildren ( ) ;
refFolder . EnsureLazyChildren ( ) ;
var refNode = refFolder . Children . OfType < AssemblyReferenceTreeNode > ( )
var refNode = refFolder . Children . OfType < AssemblyReferenceTreeNode > ( )
@ -309,27 +265,20 @@ public class AssemblyTreeTests
// TypeRef rows the host assembly imports from that reference.
// TypeRef rows the host assembly imports from that reference.
// Arrange — boot, wait for assemblies, expand System.Linq → References → System.Runtime.
// Arrange — boot, wait for assemblies, expand System.Linq → References → System.Runtime.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . Expand ( ) ;
assemblyNode . IsExpanded = true ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
refFolder . EnsureLazyChildren ( ) ;
refFolder . Expand ( ) ;
refFolder . IsExpanded = true ;
var refNode = refFolder . Children . OfType < AssemblyReferenceTreeNode > ( )
var refNode = refFolder . Children . OfType < AssemblyReferenceTreeNode > ( )
. First ( n = > n . AssemblyReference . Name = = "System.Runtime" ) ;
. First ( n = > n . AssemblyReference . Name = = "System.Runtime" ) ;
refNode . EnsureLazyChildren ( ) ;
refNode . Expand ( ) ;
refNode . IsExpanded = true ;
// Act — find the ReferencedTypes subnode and expand it.
// Act — find the ReferencedTypes subnode and expand it.
var typesNode = refNode . Children . OfType < AssemblyReferenceReferencedTypesTreeNode > ( ) . Single ( ) ;
var typesNode = refNode . Children . OfType < AssemblyReferenceReferencedTypesTreeNode > ( ) . Single ( ) ;
typesNode . EnsureLazyChildren ( ) ;
typesNode . Expand ( ) ;
typesNode . IsExpanded = true ;
// Assert — header text contains the expected label, and the children include at least
// Assert — header text contains the expected label, and the children include at least
// one TypeReferenceTreeNode.
// one TypeReferenceTreeNode.
@ -345,14 +294,10 @@ public class AssemblyTreeTests
// search to land on a specific namespace.
// search to land on a specific namespace.
// Arrange — boot, wait for assemblies, expand System.Linq.
// Arrange — boot, wait for assemblies, expand System.Linq.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . Expand ( ) ;
assemblyNode . IsExpanded = true ;
// Act — look up the System.Linq namespace.
// Act — look up the System.Linq namespace.
var ns = assemblyNode . FindNamespaceNode ( "System.Linq" ) ;
var ns = assemblyNode . FindNamespaceNode ( "System.Linq" ) ;
@ -370,14 +315,10 @@ public class AssemblyTreeTests
// to land on a specific type by ITypeDefinition.
// to land on a specific type by ITypeDefinition.
// Arrange — boot, wait for assemblies, expand System.Linq.
// Arrange — boot, wait for assemblies, expand System.Linq.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . Expand ( ) ;
assemblyNode . IsExpanded = true ;
// Act — resolve System.Linq.Enumerable's ITypeDefinition, then look up its tree node.
// Act — resolve System.Linq.Enumerable's ITypeDefinition, then look up its tree node.
var module = assemblyNode . LoadedAssembly . GetMetadataFileOrNull ( ) ! ;
var module = assemblyNode . LoadedAssembly . GetMetadataFileOrNull ( ) ! ;
@ -399,17 +340,12 @@ public class AssemblyTreeTests
// top of the underlying Method/Field glyph — verifies that overlay routing works.
// top of the underlying Method/Field glyph — verifies that overlay routing works.
// Arrange — boot, wait for assemblies, expand System.Linq → References.
// Arrange — boot, wait for assemblies, expand System.Linq → References.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . Expand ( ) ;
assemblyNode . IsExpanded = true ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
refFolder . EnsureLazyChildren ( ) ;
refFolder . Expand ( ) ;
refFolder . IsExpanded = true ;
// Act — walk every reference's ReferencedTypes subtree until we find a TypeRef with
// Act — walk every reference's ReferencedTypes subtree until we find a TypeRef with
// at least one MemberReference. System.Linq imports plenty (delegates, Func ctors)
// at least one MemberReference. System.Linq imports plenty (delegates, Func ctors)
@ -417,17 +353,14 @@ public class AssemblyTreeTests
MemberReferenceTreeNode ? memberRefNode = null ;
MemberReferenceTreeNode ? memberRefNode = null ;
foreach ( var refNode in refFolder . Children . OfType < AssemblyReferenceTreeNode > ( ) )
foreach ( var refNode in refFolder . Children . OfType < AssemblyReferenceTreeNode > ( ) )
{
{
refNode . EnsureLazyChildren ( ) ;
refNode . Expand ( ) ;
refNode . IsExpanded = true ;
var typesNode = refNode . Children . OfType < AssemblyReferenceReferencedTypesTreeNode > ( ) . FirstOrDefault ( ) ;
var typesNode = refNode . Children . OfType < AssemblyReferenceReferencedTypesTreeNode > ( ) . FirstOrDefault ( ) ;
if ( typesNode = = null )
if ( typesNode = = null )
continue ;
continue ;
typesNode . EnsureLazyChildren ( ) ;
typesNode . Expand ( ) ;
typesNode . IsExpanded = true ;
foreach ( var typeRef in typesNode . Children . OfType < TypeReferenceTreeNode > ( ) )
foreach ( var typeRef in typesNode . Children . OfType < TypeReferenceTreeNode > ( ) )
{
{
typeRef . EnsureLazyChildren ( ) ;
typeRef . Expand ( ) ;
typeRef . IsExpanded = true ;
memberRefNode = typeRef . Children . OfType < MemberReferenceTreeNode > ( ) . FirstOrDefault ( ) ;
memberRefNode = typeRef . Children . OfType < MemberReferenceTreeNode > ( ) . FirstOrDefault ( ) ;
if ( memberRefNode ! = null )
if ( memberRefNode ! = null )
break ;
break ;
@ -452,10 +385,7 @@ public class AssemblyTreeTests
// Arrange — boot, wait for assemblies. Open mscorlib explicitly: it's the canonical
// Arrange — boot, wait for assemblies. Open mscorlib explicitly: it's the canonical
// type-forwarder facade and we need a known forwarder regardless of default startup.
// type-forwarder facade and we need a known forwarder regardless of default startup.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var coreLibDir = System . IO . Path . GetDirectoryName ( typeof ( object ) . Assembly . Location ) ! ;
var coreLibDir = System . IO . Path . GetDirectoryName ( typeof ( object ) . Assembly . Location ) ! ;
var mscorlibPath = System . IO . Path . Combine ( coreLibDir , "mscorlib.dll" ) ;
var mscorlibPath = System . IO . Path . Combine ( coreLibDir , "mscorlib.dll" ) ;
@ -464,37 +394,22 @@ public class AssemblyTreeTests
Assert . Inconclusive ( $"mscorlib.dll not present next to System.Private.CoreLib at {coreLibDir} — runtime layout differs." ) ;
Assert . Inconclusive ( $"mscorlib.dll not present next to System.Private.CoreLib at {coreLibDir} — runtime layout differs." ) ;
return ;
return ;
}
}
var registry = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) ;
var loaded = await vm . OpenAssemblyAsync ( mscorlibPath ) ;
var openCommand = registry . Commands
. Single ( c = > c . Metadata . Header = = nameof ( Resources . _ Open ) )
. CreateExport ( ) . Value ;
openCommand . Execute ( mscorlibPath ) ;
await Waiters . WaitForAsync ( ( ) = >
vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( ) . Any ( a = >
string . Equals ( a . FileName , mscorlibPath , System . StringComparison . OrdinalIgnoreCase ) ) ) ;
var loaded = vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( )
. First ( a = > string . Equals ( a . FileName , mscorlibPath , System . StringComparison . OrdinalIgnoreCase ) ) ;
await loaded . GetLoadResultAsync ( ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( loaded . ShortName ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( loaded . ShortName ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . Expand ( ) ;
assemblyNode . IsExpanded = true ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
refFolder . EnsureLazyChildren ( ) ;
refFolder . Expand ( ) ;
refFolder . IsExpanded = true ;
// Act — walk every reference's ReferencedTypes for an ExportedTypeTreeNode.
// Act — walk every reference's ReferencedTypes for an ExportedTypeTreeNode.
ExportedTypeTreeNode ? exportedNode = null ;
ExportedTypeTreeNode ? exportedNode = null ;
foreach ( var refNode in refFolder . Children . OfType < AssemblyReferenceTreeNode > ( ) )
foreach ( var refNode in refFolder . Children . OfType < AssemblyReferenceTreeNode > ( ) )
{
{
refNode . EnsureLazyChildren ( ) ;
refNode . Expand ( ) ;
refNode . IsExpanded = true ;
var typesNode = refNode . Children . OfType < AssemblyReferenceReferencedTypesTreeNode > ( ) . FirstOrDefault ( ) ;
var typesNode = refNode . Children . OfType < AssemblyReferenceReferencedTypesTreeNode > ( ) . FirstOrDefault ( ) ;
if ( typesNode = = null )
if ( typesNode = = null )
continue ;
continue ;
typesNode . EnsureLazyChildren ( ) ;
typesNode . Expand ( ) ;
typesNode . IsExpanded = true ;
exportedNode = typesNode . Children . OfType < ExportedTypeTreeNode > ( ) . FirstOrDefault ( ) ;
exportedNode = typesNode . Children . OfType < ExportedTypeTreeNode > ( ) . FirstOrDefault ( ) ;
if ( exportedNode ! = null )
if ( exportedNode ! = null )
break ;
break ;
@ -514,25 +429,19 @@ public class AssemblyTreeTests
// — building a transitive reference tree the user can drill into.
// — building a transitive reference tree the user can drill into.
// Arrange — boot, wait for assemblies, expand System.Linq → References.
// Arrange — boot, wait for assemblies, expand System.Linq → References.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . Expand ( ) ;
assemblyNode . IsExpanded = true ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
refFolder . EnsureLazyChildren ( ) ;
refFolder . Expand ( ) ;
refFolder . IsExpanded = true ;
// Act — many BCL refs resolve to facade assemblies with zero transitive references;
// Act — many BCL refs resolve to facade assemblies with zero transitive references;
// iterate until we find one that has its own AssemblyRefs.
// iterate until we find one that has its own AssemblyRefs.
bool foundTransitive = false ;
bool foundTransitive = false ;
foreach ( var refNode in refFolder . Children . OfType < AssemblyReferenceTreeNode > ( ) )
foreach ( var refNode in refFolder . Children . OfType < AssemblyReferenceTreeNode > ( ) )
{
{
refNode . EnsureLazyChildren ( ) ;
refNode . Expand ( ) ;
refNode . IsExpanded = true ;
if ( refNode . Children . OfType < AssemblyReferenceTreeNode > ( ) . Any ( ) )
if ( refNode . Children . OfType < AssemblyReferenceTreeNode > ( ) . Any ( ) )
{
{
foundTransitive = true ;
foundTransitive = true ;
@ -551,17 +460,12 @@ public class AssemblyTreeTests
// assembly's AssemblyTreeNode in the tree — auto-loading it if necessary.
// assembly's AssemblyTreeNode in the tree — auto-loading it if necessary.
// Arrange — boot, wait for assemblies, expand System.Linq → References.
// Arrange — boot, wait for assemblies, expand System.Linq → References.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( "System.Linq" ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . Expand ( ) ;
assemblyNode . IsExpanded = true ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
var refFolder = assemblyNode . Children . OfType < ReferenceFolderTreeNode > ( ) . Single ( ) ;
refFolder . EnsureLazyChildren ( ) ;
refFolder . Expand ( ) ;
refFolder . IsExpanded = true ;
var refNode = refFolder . Children . OfType < AssemblyReferenceTreeNode > ( )
var refNode = refFolder . Children . OfType < AssemblyReferenceTreeNode > ( )
. Single ( n = > n . AssemblyReference . Name = = "System.Runtime" ) ;
. Single ( n = > n . AssemblyReference . Name = = "System.Runtime" ) ;
@ -591,25 +495,10 @@ public class AssemblyTreeTests
// Arrange — boot, wait for assemblies, open System.Net.Http (its CancelPendingRequests
// Arrange — boot, wait for assemblies, open System.Net.Http (its CancelPendingRequests
// pulls in plenty of currently-unreferenced types).
// pulls in plenty of currently-unreferenced types).
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var newAsmPath = typeof ( System . Net . Http . HttpClient ) . Assembly . Location ;
var newAsmPath = typeof ( System . Net . Http . HttpClient ) . Assembly . Location ;
var registry = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) ;
var loaded = await vm . OpenAssemblyAsync ( newAsmPath ) ;
var openCommand = registry . Commands
. Single ( c = > c . Metadata . Header = = nameof ( Resources . _ Open ) )
. CreateExport ( ) . Value ;
openCommand . Execute ( newAsmPath ) ;
await Waiters . WaitForAsync ( ( ) = >
vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( ) . Any ( a = >
string . Equals ( a . FileName , newAsmPath , System . StringComparison . OrdinalIgnoreCase ) ) ) ;
var loaded = vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( )
. First ( a = > string . Equals ( a . FileName , newAsmPath , System . StringComparison . OrdinalIgnoreCase ) ) ;
await loaded . GetLoadResultAsync ( ) ;
var initialFiles = vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( )
var initialFiles = vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( )
. Select ( a = > a . FileName )
. Select ( a = > a . FileName )
@ -619,8 +508,7 @@ public class AssemblyTreeTests
// assemblies to appear.
// assemblies to appear.
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
loaded . ShortName , "System.Net.Http" , "System.Net.Http.HttpClient" ) ;
loaded . ShortName , "System.Net.Http" , "System.Net.Http.HttpClient" ) ;
typeNode . EnsureLazyChildren ( ) ;
typeNode . Expand ( ) ;
typeNode . IsExpanded = true ;
var methodNode = typeNode . Children . OfType < MethodTreeNode > ( )
var methodNode = typeNode . Children . OfType < MethodTreeNode > ( )
. First ( m = > m . MethodDefinition . Name = = "CancelPendingRequests" ) ;
. First ( m = > m . MethodDefinition . Name = = "CancelPendingRequests" ) ;
@ -653,30 +541,14 @@ public class AssemblyTreeTests
// user actually opened.
// user actually opened.
// Arrange — boot, wait for assemblies, open + select a method that triggers auto-load.
// Arrange — boot, wait for assemblies, open + select a method that triggers auto-load.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var newAsmPath = typeof ( System . Net . Http . HttpClient ) . Assembly . Location ;
var newAsmPath = typeof ( System . Net . Http . HttpClient ) . Assembly . Location ;
var registry = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) ;
var loaded = await vm . OpenAssemblyAsync ( newAsmPath ) ;
var openCommand = registry . Commands
. Single ( c = > c . Metadata . Header = = nameof ( Resources . _ Open ) )
. CreateExport ( ) . Value ;
openCommand . Execute ( newAsmPath ) ;
await Waiters . WaitForAsync ( ( ) = >
vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( ) . Any ( a = >
string . Equals ( a . FileName , newAsmPath , System . StringComparison . OrdinalIgnoreCase ) ) ) ;
var loaded = vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( )
. First ( a = > string . Equals ( a . FileName , newAsmPath , System . StringComparison . OrdinalIgnoreCase ) ) ;
await loaded . GetLoadResultAsync ( ) ;
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
loaded . ShortName , "System.Net.Http" , "System.Net.Http.HttpClient" ) ;
loaded . ShortName , "System.Net.Http" , "System.Net.Http.HttpClient" ) ;
typeNode . EnsureLazyChildren ( ) ;
typeNode . Expand ( ) ;
typeNode . IsExpanded = true ;
var methodNode = typeNode . Children . OfType < MethodTreeNode > ( )
var methodNode = typeNode . Children . OfType < MethodTreeNode > ( )
. First ( m = > m . MethodDefinition . Name = = "CancelPendingRequests" ) ;
. First ( m = > m . MethodDefinition . Name = = "CancelPendingRequests" ) ;
@ -717,28 +589,13 @@ public class AssemblyTreeTests
// matching tree node, and select that node so the user sees their newly-opened file.
// matching tree node, and select that node so the user sees their newly-opened file.
// Arrange — boot, wait for assemblies, snapshot the initial count.
// Arrange — boot, wait for assemblies, snapshot the initial count.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var newAsmPath = typeof ( System . Net . Http . HttpClient ) . Assembly . Location ;
var newAsmPath = typeof ( System . Net . Http . HttpClient ) . Assembly . Location ;
var initialCount = vm . AssemblyTreeModel . AssemblyList ! . Count ;
var initialCount = vm . AssemblyTreeModel . AssemblyList ! . Count ;
// Act — fire OpenCommand on a new path and wait for the load to complete.
// Act — fire OpenCommand on a new path and wait for the load to complete.
var registry = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) ;
var loaded = await vm . OpenAssemblyAsync ( newAsmPath ) ;
var openCommand = registry . Commands
. Single ( c = > c . Metadata . Header = = nameof ( Resources . _ Open ) )
. CreateExport ( ) . Value ;
openCommand . Execute ( newAsmPath ) ;
await Waiters . WaitForAsync ( ( ) = >
vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( ) . Any ( a = >
string . Equals ( a . FileName , newAsmPath , System . StringComparison . OrdinalIgnoreCase ) ) ) ;
var loaded = vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( )
. First ( a = > string . Equals ( a . FileName , newAsmPath , System . StringComparison . OrdinalIgnoreCase ) ) ;
await loaded . GetLoadResultAsync ( ) ;
// Assert — count incremented by one; the corresponding tree node points at the
// Assert — count incremented by one; the corresponding tree node points at the
// freshly-loaded assembly and is selected.
// freshly-loaded assembly and is selected.
@ -758,24 +615,18 @@ public class AssemblyTreeTests
// Arrange — boot, wait for assemblies, expand a sub-tree large enough to scroll. Park
// Arrange — boot, wait for assemblies, expand a sub-tree large enough to scroll. Park
// the viewport mid-list so clicks are at non-zero offset.
// the viewport mid-list so clicks are at non-zero offset.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( window , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var enumerable = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var enumerable = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
"System.Linq" , "System.Linq" , "System.Linq.Enumerable" ) ;
"System.Linq" , "System.Linq" , "System.Linq.Enumerable" ) ;
enumerable . EnsureLazyChildren ( ) ;
enumerable . Expand ( ) ;
enumerable . IsExpanded = true ;
var ns = ( NamespaceTreeNode ) enumerable . Parent ! ;
var ns = ( NamespaceTreeNode ) enumerable . Parent ! ;
ns . EnsureLazyChildren ( ) ;
ns . Expand ( ) ;
ns . IsExpanded = true ;
var asm = ( AssemblyTreeNode ) ns . Parent ! ;
var asm = ( AssemblyTreeNode ) ns . Parent ! ;
asm . IsExpanded = true ;
asm . IsExpanded = true ;
foreach ( var child in ns . Children . OfType < TypeTreeNode > ( ) )
foreach ( var child in ns . Children . OfType < TypeTreeNode > ( ) )
{
{
child . EnsureLazyChildren ( ) ;
child . Expand ( ) ;
child . IsExpanded = true ;
}
}
var pane = await window . WaitForComponent < AssemblyListPane > ( ) ;
var pane = await window . WaitForComponent < AssemblyListPane > ( ) ;
@ -838,19 +689,13 @@ public class AssemblyTreeTests
// method — that's how nodes (e.g. resources) opt into a custom save dialog/format.
// method — that's how nodes (e.g. resources) opt into a custom save dialog/format.
// Arrange — boot, wait for assemblies, plant a probe node that records when Save runs.
// Arrange — boot, wait for assemblies, plant a probe node that records when Save runs.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var probe = new SaveProbeNode ( ) ;
var probe = new SaveProbeNode ( ) ;
vm . AssemblyTreeModel . SelectNode ( probe ) ;
vm . AssemblyTreeModel . SelectNode ( probe ) ;
// Act — fire the Save Code main-menu command.
// Act — fire the Save Code main-menu command.
var registry = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) ;
var saveCmd = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) . GetCommand ( nameof ( Resources . _ SaveCode ) ) ;
var saveCmd = registry . Commands
. Single ( c = > c . Metadata . Header = = nameof ( Resources . _ SaveCode ) )
. CreateExport ( ) . Value ;
saveCmd . Execute ( null ) ;
saveCmd . Execute ( null ) ;
// Assert — probe's Save() ran.
// Assert — probe's Save() ran.
@ -877,15 +722,11 @@ public class AssemblyTreeTests
// write the resulting C# to the user-picked path.
// write the resulting C# to the user-picked path.
// Arrange — boot, wait for assemblies, select a method, wait for the in-tab decompile.
// Arrange — boot, wait for assemblies, select a method, wait for the in-tab decompile.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
"System.Linq" , "System.Linq" , "System.Linq.Enumerable" ) ;
"System.Linq" , "System.Linq" , "System.Linq.Enumerable" ) ;
typeNode . EnsureLazyChildren ( ) ;
typeNode . Expand ( ) ;
typeNode . IsExpanded = true ;
var method = typeNode . Children . OfType < MethodTreeNode > ( )
var method = typeNode . Children . OfType < MethodTreeNode > ( )
. First ( m = > m . MethodDefinition . Name = = "AsEnumerable" ) ;
. First ( m = > m . MethodDefinition . Name = = "AsEnumerable" ) ;
vm . AssemblyTreeModel . SelectNode ( method ) ;
vm . AssemblyTreeModel . SelectNode ( method ) ;
@ -929,10 +770,7 @@ public class AssemblyTreeTests
// Arrange — boot, wait for assemblies, pick the assembly to evict + a sibling we
// Arrange — boot, wait for assemblies, pick the assembly to evict + a sibling we
// expect to survive, select the sacrificial one, focus the grid.
// expect to survive, select the sacrificial one, focus the grid.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( window , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var sacrificialName = typeof ( System . Linq . Enumerable ) . Assembly . GetName ( ) . Name ! ;
var sacrificialName = typeof ( System . Linq . Enumerable ) . Assembly . GetName ( ) . Name ! ;
var survivorName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var survivorName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
@ -980,17 +818,11 @@ public class AssemblyTreeTests
// The Clear Assembly List menu command must drop every entry from the active list.
// The Clear Assembly List menu command must drop every entry from the active list.
// Arrange — boot, wait for assemblies, baseline the non-empty count.
// Arrange — boot, wait for assemblies, baseline the non-empty count.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
vm . AssemblyTreeModel . AssemblyList ! . Count . Should ( ) . BeGreaterThan ( 0 ) ;
vm . AssemblyTreeModel . AssemblyList ! . Count . Should ( ) . BeGreaterThan ( 0 ) ;
// Act — fire the Clear command.
// Act — fire the Clear command.
var registry = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) ;
var clearCmd = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) . GetCommand ( nameof ( Resources . ClearAssemblyList ) ) ;
var clearCmd = registry . Commands
. Single ( c = > c . Metadata . Header = = nameof ( Resources . ClearAssemblyList ) )
. CreateExport ( ) . Value ;
clearCmd . CanExecute ( null ) . Should ( ) . BeTrue ( "non-empty list must enable Clear" ) ;
clearCmd . CanExecute ( null ) . Should ( ) . BeTrue ( "non-empty list must enable Clear" ) ;
clearCmd . Execute ( null ) ;
clearCmd . Execute ( null ) ;
@ -1008,10 +840,7 @@ public class AssemblyTreeTests
// Arrange — boot, wait for assemblies, plant a "broken" entry: a real file that isn't
// Arrange — boot, wait for assemblies, plant a "broken" entry: a real file that isn't
// a PE binary. AssemblyList records it but the load fails — that's the state the
// a PE binary. AssemblyList records it but the load fails — that's the state the
// command targets.
// command targets.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var brokenPath = System . IO . Path . Combine (
var brokenPath = System . IO . Path . Combine (
System . IO . Path . GetTempPath ( ) ,
System . IO . Path . GetTempPath ( ) ,
@ -1035,10 +864,7 @@ public class AssemblyTreeTests
validBefore . Should ( ) . NotBeEmpty ( "at least one real assembly must remain to verify it's spared" ) ;
validBefore . Should ( ) . NotBeEmpty ( "at least one real assembly must remain to verify it's spared" ) ;
// Act — fire the command.
// Act — fire the command.
var registry = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) ;
var cmd = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) . GetCommand ( nameof ( Resources . _ RemoveAssembliesWithLoadErrors ) ) ;
var cmd = registry . Commands
. Single ( c = > c . Metadata . Header = = nameof ( Resources . _ RemoveAssembliesWithLoadErrors ) )
. CreateExport ( ) . Value ;
cmd . CanExecute ( null ) . Should ( ) . BeTrue ( "a broken entry must enable the command" ) ;
cmd . CanExecute ( null ) . Should ( ) . BeTrue ( "a broken entry must enable the command" ) ;
cmd . Execute ( null ) ;
cmd . Execute ( null ) ;
@ -1069,10 +895,7 @@ public class AssemblyTreeTests
// Arrange — boot, wait for assemblies, locate the realised DataGrid and the
// Arrange — boot, wait for assemblies, locate the realised DataGrid and the
// HierarchicalModel wrapper for the assembly node.
// HierarchicalModel wrapper for the assembly node.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( window , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var pane = await window . WaitForComponent < AssemblyListPane > ( ) ;
var pane = await window . WaitForComponent < AssemblyListPane > ( ) ;
var grid = await pane . WaitForComponent < DataGrid > ( ) ;
var grid = await pane . WaitForComponent < DataGrid > ( ) ;
@ -1084,8 +907,7 @@ public class AssemblyTreeTests
hNode ! . IsExpanded . Should ( ) . BeFalse ( "baseline: top-level assembly rows start collapsed" ) ;
hNode ! . IsExpanded . Should ( ) . BeFalse ( "baseline: top-level assembly rows start collapsed" ) ;
// Act — production-shaped action: just toggle the model property.
// Act — production-shaped action: just toggle the model property.
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . Expand ( ) ;
assemblyNode . IsExpanded = true ;
// Drain the dispatcher so the wiring (PropertyChanged → hm.Expand) settles.
// Drain the dispatcher so the wiring (PropertyChanged → hm.Expand) settles.
for ( int i = 0 ; i < 5 ; i + + )
for ( int i = 0 ; i < 5 ; i + + )
@ -1106,10 +928,7 @@ public class AssemblyTreeTests
// tab opens with the supplied node decompiled, the existing tab keeps its content,
// tab opens with the supplied node decompiled, the existing tab keeps its content,
// and the assembly-tree selection is pulled across to the new tab's source node
// and the assembly-tree selection is pulled across to the new tab's source node
// (the active tab and the tree are kept in lockstep).
// (the active tab and the tree are kept in lockstep).
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( window , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
"System.Linq" , "System.Linq" , "System.Linq.Enumerable" ) ;
"System.Linq" , "System.Linq" , "System.Linq.Enumerable" ) ;
@ -1147,10 +966,7 @@ public class AssemblyTreeTests
{
{
// Activate two tabs (each carrying a different SourceNode), then flip the active
// Activate two tabs (each carrying a different SourceNode), then flip the active
// dockable back to the first one and confirm the assembly-tree selection follows.
// dockable back to the first one and confirm the assembly-tree selection follows.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
"System.Linq" , "System.Linq" , "System.Linq.Enumerable" ) ;
"System.Linq" , "System.Linq" , "System.Linq.Enumerable" ) ;
@ -1188,19 +1004,12 @@ public class AssemblyTreeTests
// DecompilerTabPageModel. Exercises the regression behind "there can't be multiple
// DecompilerTabPageModel. Exercises the regression behind "there can't be multiple
// metadata views": before the OpenNodeInNewTab consolidation, the new-tab path
// metadata views": before the OpenNodeInNewTab consolidation, the new-tab path
// always built a decompiler tab and the metadata page-type couldn't be carved out.
// always built a decompiler tab and the metadata page-type couldn't be carved out.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 1 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var tablesNode = vm . AssemblyTreeModel . FindCoreLib ( )
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( coreLibName ) ;
. GetChild < MetadataTreeNode > ( )
assemblyNode . EnsureLazyChildren ( ) ;
. GetChild < MetadataTablesTreeNode > ( ) ;
var metadataNode = assemblyNode . Children . OfType < MetadataTreeNode > ( ) . Single ( ) ;
var typeDefNode = tablesNode . GetChild < TypeDefTableTreeNode > ( ) ;
metadataNode . EnsureLazyChildren ( ) ;
var tablesNode = metadataNode . Children . OfType < MetadataTablesTreeNode > ( ) . Single ( ) ;
tablesNode . EnsureLazyChildren ( ) ;
var typeDefNode = tablesNode . Children . OfType < TypeDefTableTreeNode > ( ) . Single ( ) ;
// First metadata view via tree-selection (active tab gets MetadataTablePageModel content).
// First metadata view via tree-selection (active tab gets MetadataTablePageModel content).
vm . AssemblyTreeModel . SelectNode ( typeDefNode ) ;
vm . AssemblyTreeModel . SelectNode ( typeDefNode ) ;
@ -1229,10 +1038,7 @@ public class AssemblyTreeTests
// ILSpy. Without this, tree-based navigation can't reach get_X / set_X — and
// ILSpy. Without this, tree-based navigation can't reach get_X / set_X — and
// MMB on a property-accessor row in the metadata grid silently no-ops because
// MMB on a property-accessor row in the metadata grid silently no-ops because
// FindTreeNode can't resolve the IMethod to a tree node.
// FindTreeNode can't resolve the IMethod to a tree node.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 1 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var stringTypeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var stringTypeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
@ -1257,10 +1063,7 @@ public class AssemblyTreeTests
// Events surface add_X / remove_X (and invoke_X if present) as MethodTreeNode
// Events surface add_X / remove_X (and invoke_X if present) as MethodTreeNode
// children, mirroring WPF ILSpy. Same regression-protection as the property
// children, mirroring WPF ILSpy. Same regression-protection as the property
// case above.
// case above.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 1 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
// AppDomain.AssemblyLoad is a public, well-known event in CoreLib.
// AppDomain.AssemblyLoad is a public, well-known event in CoreLib.
@ -1285,10 +1088,7 @@ public class AssemblyTreeTests
// the per-node Filter must hide them under the default ShowApiLevel — they're
// the per-node Filter must hide them under the default ShowApiLevel — they're
// rendered as part of the parent property in the C# surface, so the tree should
// rendered as part of the parent property in the C# surface, so the tree should
// not duplicate them as standalone rows. ShowAll is the explicit opt-in.
// not duplicate them as standalone rows. ShowAll is the explicit opt-in.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 1 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var stringTypeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var stringTypeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
@ -1317,10 +1117,7 @@ public class AssemblyTreeTests
// an IMethod whose AccessorOwner is the IProperty. Without the AccessorOwner-aware
// an IMethod whose AccessorOwner is the IProperty. Without the AccessorOwner-aware
// branch in FindMemberNode, the lookup falls through to "MethodTreeNode child of the
// branch in FindMemberNode, the lookup falls through to "MethodTreeNode child of the
// type" and returns null because accessors live under the property, not the type.
// type" and returns null because accessors live under the property, not the type.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 1 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var stringTypeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var stringTypeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
@ -1346,10 +1143,7 @@ public class AssemblyTreeTests
// expect a single BaseTypesEntryNode whose label ends in "Object".
// expect a single BaseTypesEntryNode whose label ends in "Object".
// Arrange — boot, wait for assemblies, drill into System.Exception.
// Arrange — boot, wait for assemblies, drill into System.Exception.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
@ -1371,10 +1165,7 @@ public class AssemblyTreeTests
// Activating a BaseTypesEntryNode jumps the assembly-tree selection to the underlying
// Activating a BaseTypesEntryNode jumps the assembly-tree selection to the underlying
// type node — same gesture the WPF tree uses to walk an inheritance chain.
// type node — same gesture the WPF tree uses to walk an inheritance chain.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
@ -1404,10 +1195,7 @@ public class AssemblyTreeTests
// scan the assembly list synchronously — for the small fixture loaded in tests this
// scan the assembly list synchronously — for the small fixture loaded in tests this
// yields a few hits per common base (e.g. SystemException is derived from Exception).
// yields a few hits per common base (e.g. SystemException is derived from Exception).
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var typeNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
@ -1425,10 +1213,7 @@ public class AssemblyTreeTests
{
{
// Sealed types can't be derived from, so the DerivedTypes sub-tree must not appear.
// Sealed types can't be derived from, so the DerivedTypes sub-tree must not appear.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var stringNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var stringNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
@ -1444,10 +1229,7 @@ public class AssemblyTreeTests
{
{
// System.Object has no base types, so the BaseTypes sub-tree must not appear.
// System.Object has no base types, so the BaseTypes sub-tree must not appear.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var objectNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
var objectNode = vm . AssemblyTreeModel . FindNode < TypeTreeNode > (
@ -1498,10 +1280,7 @@ public class AssemblyTreeTests
// must NOT return Hidden purely because the field's name doesn't contain the active
// must NOT return Hidden purely because the field's name doesn't contain the active
// SearchTerm — only ShowApiLevel + ShowMember remain valid hiding criteria.
// SearchTerm — only ShowApiLevel + ShowMember remain valid hiding criteria.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 1 ) ;
var settings = AppComposition . Current . GetExport < SettingsService > ( )
var settings = AppComposition . Current . GetExport < SettingsService > ( )
. SessionSettings . LanguageSettings ;
. SessionSettings . LanguageSettings ;
@ -1533,13 +1312,9 @@ public class AssemblyTreeTests
// NOT as bare TypeTreeNodes hanging directly off the assembly node.
// NOT as bare TypeTreeNodes hanging directly off the assembly node.
// Arrange — boot, wait for assemblies, locate CoreLib.
// Arrange — boot, wait for assemblies, locate CoreLib.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var coreLibName = typeof ( object ) . Assembly . GetName ( ) . Name ! ;
var assemblyNode = vm . AssemblyTreeModel . FindCoreLib ( ) ;
var assemblyNode = vm . AssemblyTreeModel . FindNode < AssemblyTreeNode > ( coreLibName ) ;
assemblyNode . EnsureLazyChildren ( ) ;
assemblyNode . EnsureLazyChildren ( ) ;
// Assert — exactly one global-namespace node, labelled "-", non-empty contents.
// Assert — exactly one global-namespace node, labelled "-", non-empty contents.
@ -1570,10 +1345,7 @@ public class AssemblyTreeTests
// the current cell to the last row (the user reported having to press it twice).
// the current cell to the last row (the user reported having to press it twice).
// Arrange — boot, wait for assemblies, realise the grid.
// Arrange — boot, wait for assemblies, realise the grid.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( window , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var pane = await window . WaitForComponent < AssemblyListPane > ( ) ;
var pane = await window . WaitForComponent < AssemblyListPane > ( ) ;
var grid = await pane . WaitForComponent < DataGrid > ( ) ;
var grid = await pane . WaitForComponent < DataGrid > ( ) ;
@ -1619,10 +1391,7 @@ public class AssemblyTreeTests
// one row must collapse the selection down to just that row.
// one row must collapse the selection down to just that row.
// Arrange — boot, wait for assemblies, realise the grid, select everything.
// Arrange — boot, wait for assemblies, realise the grid, select everything.
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( window , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var pane = await window . WaitForComponent < AssemblyListPane > ( ) ;
var pane = await window . WaitForComponent < AssemblyListPane > ( ) ;
var grid = await pane . WaitForComponent < DataGrid > ( ) ;
var grid = await pane . WaitForComponent < DataGrid > ( ) ;
@ -1670,16 +1439,10 @@ public class AssemblyTreeTests
// to do nothing.
// to do nothing.
// Arrange -- boot, open System.Net.Http (it references many assemblies not in the list).
// Arrange -- boot, open System.Net.Http (it references many assemblies not in the list).
var window = AppComposition . Current . GetExport < MainWindow > ( ) ;
var ( _ , vm ) = await TestHarness . BootAsync ( 3 ) ;
window . Show ( ) ;
var vm = ( MainWindowViewModel ) window . DataContext ! ;
await vm . AssemblyTreeModel . WaitForAssembliesAsync ( minimumCount : 3 ) ;
var newAsmPath = typeof ( System . Net . Http . HttpClient ) . Assembly . Location ;
var newAsmPath = typeof ( System . Net . Http . HttpClient ) . Assembly . Location ;
var registry = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) ;
var openCommand = AppComposition . Current . GetExport < MainMenuCommandRegistry > ( ) . GetCommand ( nameof ( Resources . _ Open ) ) ;
var openCommand = registry . Commands
. Single ( c = > c . Metadata . Header = = nameof ( Resources . _ Open ) )
. CreateExport ( ) . Value ;
openCommand . Execute ( newAsmPath ) ;
openCommand . Execute ( newAsmPath ) ;
await Waiters . WaitForAsync ( ( ) = >
await Waiters . WaitForAsync ( ( ) = >
vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( ) . Any ( a = >
vm . AssemblyTreeModel . AssemblyList ! . GetAssemblies ( ) . Any ( a = >