Skip to content

Commit

Permalink
Merge latest from master, resolve conflicts, add new project and fix …
Browse files Browse the repository at this point in the history
…build
  • Loading branch information
rprouse committed Apr 25, 2017
2 parents 00a32ce + 749594c commit c1a2fc8
Show file tree
Hide file tree
Showing 10 changed files with 312 additions and 55 deletions.
Binary file modified NUnit3TestAdapter.sln
Binary file not shown.
6 changes: 6 additions & 0 deletions build.cake
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ Task("NuGetRestore")
Information("Restoring NuGet Packages for the Adapter Solution");
DotNetCoreRestore(ADAPTER_SOLUTION);

Information("Restoring NuGet Packages for the VSIX project");
NuGetRestore(PROJECT_DIR + "src/NUnit3TestAdapterInstall/NUnit3TestAdapterInstall.csproj",
new NuGetRestoreSettings {
PackagesDirectory = PROJECT_DIR + "packages"
});

Information("Restoring NuGet Packages for the Demo Solution");
DotNetCoreRestore(DEMO_SOLUTION);
});
Expand Down
16 changes: 16 additions & 0 deletions src/NUnit3AdapterExternalTests/NUnit3AdapterExternalTests.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk" ToolsVersion="15.0">
<PropertyGroup>
<AssemblyName>NUnit.VisualStudio.TestAdapter.ExternalTests</AssemblyName>
<RootNamespace>NUnit.VisualStudio.TestAdapter.Tests</RootNamespace>
<TargetFrameworks>net45;netstandard1.6</TargetFrameworks>
<DebugType Condition="'$(TargetFramework)' != '' AND '$(TargetFramework)' != 'netstandard1.6'">Full</DebugType>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NUnit" Version="3.6.1" />
</ItemGroup>

<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
</Project>
16 changes: 16 additions & 0 deletions src/NUnit3AdapterExternalTests/NavigationTestData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace NUnit.VisualStudio.TestAdapter.Tests
{
public abstract class AbstractBaseClass
{
public void EmptyMethod_ThreeLines()
{ // expectedLineDebug = 6
} // expectedLineRelease = 7
}

public class ConcreteBaseClass
{
public void EmptyMethod_ThreeLines()
{ // expectedLineDebug = 13
} // expectedLineRelease = 14
}
}
8 changes: 7 additions & 1 deletion src/NUnitTestAdapter/NUnit3TestExecutor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -265,7 +265,13 @@ private void RunAssembly(string assemblyName, TestFilter filter)
}
}
else
TestLog.Info("NUnit failed to load " + assemblyName);
{
var msgNode = loadResult.SelectSingleNode("properties/property[@name='_SKIPREASON']");
if (msgNode != null && (new[] { "contains no tests", "Has no TestFixtures" }).Any(msgNode.GetAttribute("value").Contains))
TestLog.Info("NUnit couldn't find any tests in " + assemblyName);
else
TestLog.Info("NUnit failed to load " + assemblyName);
}
}
catch (BadImageFormatException)
{
Expand Down
160 changes: 108 additions & 52 deletions src/NUnitTestAdapter/NavigationDataProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,10 @@
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
Expand All @@ -33,6 +33,7 @@ namespace NUnit.VisualStudio.TestAdapter
public class NavigationDataProvider
{
readonly string _assemblyPath;
ICollection<string> _knownReferencedAssemblies;
IDictionary<string, TypeDefinition> _typeDefs;

public NavigationDataProvider(string assemblyPath)
Expand All @@ -43,82 +44,137 @@ public NavigationDataProvider(string assemblyPath)
public NavigationData GetNavigationData(string className, string methodName)
{
if (_typeDefs == null)
{
_typeDefs = CacheTypes(_assemblyPath);

#if true
TypeDefinition typeDef;
if (!TryGetTypeDefinition(className, out typeDef))
return NavigationData.Invalid;

MethodDefinition methodDef = null;
_knownReferencedAssemblies = new HashSet<string> { _assemblyPath };
}

// Through NUnit 3.2.1, the class name provided by NUnit is
// the reflected class - that is, the class of the fixture
// that is being run. To use for location data, we actually
// need the class where the method is defined.
// need the class where the method is defined (for example,
// a base class being used as an abstract test fixture).
// TODO: Once NUnit is changed, try to simplify this.
while (true)
{
methodDef = typeDef.Methods.FirstOrDefault(o => o.Name == methodName);

if (methodDef != null)
break;

var baseType = typeDef.BaseType;
if (baseType == null || baseType.FullName == "System.Object")
return NavigationData.Invalid;

typeDef = typeDef.BaseType.Resolve();
}

var sequencePoint = FirstOrDefaultSequencePoint(methodDef);
if (sequencePoint != null)
return new NavigationData(sequencePoint.Document.Url, sequencePoint.StartLine);

return NavigationData.Invalid;
#else
var navigationData = GetMethods(className)
.Where(m => m.Name == methodName)
.Select(FirstOrDefaultSequencePoint)
var sequencePoint = GetTypeLineage(className)
.Select(typeDef => typeDef.Methods.FirstOrDefault(o => o.Name == methodName))
.Where(x => x != null)
.OrderBy(x => x.StartLine)
.Select(x => new NavigationData(x.Document.Url, x.StartLine))
.Select(FirstOrDefaultSequencePoint)
.FirstOrDefault();

return navigationData ?? NavigationData.Invalid;
#endif
if (sequencePoint == null) { return NavigationData.Invalid; }
return new NavigationData(sequencePoint.Document.Url, sequencePoint.StartLine);
}

static bool DoesPdbFileExist(string filepath) => File.Exists(Path.ChangeExtension(filepath, ".pdb"));


static IDictionary<string, TypeDefinition> CacheTypes(string assemblyPath)
{
var readsymbols = DoesPdbFileExist(assemblyPath);
var readerParameters = new ReaderParameters { ReadSymbols = readsymbols};
var module = ModuleDefinition.ReadModule(assemblyPath, readerParameters);

var types = new Dictionary<string, TypeDefinition>();
CacheNewTypes(assemblyPath, types);
return types;
}

private static void CacheNewTypes(string assemblyPath, IDictionary<string, TypeDefinition> types)
{
#if NETCOREAPP1_0
var readerParameters = new ReaderParameters
{
ReadSymbols = DoesPdbFileExist(assemblyPath)
};

var module = ModuleDefinition.ReadModule(assemblyPath, readerParameters);

foreach (var type in module.GetTypes())
{
types[type.FullName] = type;
}
#else
var resolver = new DefaultAssemblyResolver();
var readerParameters = new ReaderParameters
{
ReadSymbols = DoesPdbFileExist(assemblyPath),
AssemblyResolver = resolver
};

return types;
}
var directory = Path.GetDirectoryName(assemblyPath);
resolver.AddSearchDirectory(directory);
var knownSearchDirectories = new HashSet<string> { directory };

IEnumerable<MethodDefinition> GetMethods(string className)
{
TypeDefinition type;
var module = ModuleDefinition.ReadModule(assemblyPath, readerParameters);

if (TryGetTypeDefinition(className, out type))
return type.Methods;
foreach (var type in module.GetTypes())
{
directory = Path.GetDirectoryName(type.Module.FullyQualifiedName);
if (!knownSearchDirectories.Contains(directory))
{
resolver.AddSearchDirectory(directory);
knownSearchDirectories.Add(directory);
}

return Enumerable.Empty<MethodDefinition>();
types[type.FullName] = type;
}
#endif
}

bool TryGetTypeDefinition(string className, out TypeDefinition typeDef)
IEnumerable<TypeDefinition> GetTypeLineage(string className)
{
return _typeDefs.TryGetValue(StandardizeTypeName(className), out typeDef);
var typeDef = GetTypeDefinitionOrDefault(className);
while (typeDef != default(TypeDefinition) && typeDef.FullName != "System.Object")
{
if (!_typeDefs.ContainsKey(typeDef.FullName))
{
_typeDefs[typeDef.FullName] = typeDef;
}

yield return typeDef;

// .Resolve() will resolve the type within all configured
// search-directories, but it will _not_ try to read any
// symbols, which we need for navigation data.
//
// So, first resolve the type to get the full-path, and
// then re-read and cache the module with our reader-settings.
//
// This feels like it should be handled by Mono.Cecil. A
// follow-up may be in order.
try
{
var newTypeDef = typeDef.BaseType.Resolve();
#if NETCOREAPP1_0
var newAssemblyPath = newTypeDef.Module.FileName;
#else
var newAssemblyPath = newTypeDef.Module.FullyQualifiedName;
#endif
if (!_knownReferencedAssemblies.Contains(newAssemblyPath))
{
CacheNewTypes(newAssemblyPath, _typeDefs);
_knownReferencedAssemblies.Add(newAssemblyPath);
if (_typeDefs.ContainsKey(newTypeDef.FullName))
{
newTypeDef = _typeDefs[newTypeDef.FullName];
}
}

typeDef = newTypeDef;
}
#if NETCOREAPP1_0
catch (Exception)
#else
catch (AssemblyResolutionException)
#endif
{
// when resolving the assembly for the base-type fails
// (assembly not found in the known search-directories)
// stop the type-lineage here ("best-effort").
typeDef = null;
}
}
}

TypeDefinition GetTypeDefinitionOrDefault(string className)
{
TypeDefinition typeDef;
return _typeDefs.TryGetValue(StandardizeTypeName(className), out typeDef) ? typeDef : default(TypeDefinition);
}

static SequencePoint FirstOrDefaultSequencePoint(MethodDefinition testMethod)
Expand Down
1 change: 1 addition & 0 deletions src/NUnitTestAdapterTests/NUnit.TestAdapter.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
<ProjectReference Include="..\empty-assembly\empty-assembly.csproj" />
<ProjectReference Include="..\mock-assembly\mock-assembly.csproj" />
<ProjectReference Include="..\NUnitTestAdapter\NUnit.TestAdapter.csproj" />
<ProjectReference Include="..\NUnit3AdapterExternalTests\NUnit3AdapterExternalTests.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
Loading

0 comments on commit c1a2fc8

Please sign in to comment.