Skip to content

Commit

Permalink
separate typeinfo and testclass interfaces to avoid hack
Browse files Browse the repository at this point in the history
  • Loading branch information
gasparnagy committed May 25, 2018
1 parent 4d98319 commit 9ff9a4c
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.Framework
public class SpecFlowTestDiscoverer : XunitTestFrameworkDiscoverer
{
public SpecFlowTestDiscoverer(IAssemblyInfo assemblyInfo, ISourceInformationProvider sourceProvider, IMessageSink diagnosticMessageSink, IXunitTestCollectionFactory collectionFactory = null) :
base(CreateSpecFlowProjectAssemblyInfo(assemblyInfo), sourceProvider, diagnosticMessageSink)
base(CreateSpecFlowProjectAssemblyInfo(assemblyInfo), sourceProvider, diagnosticMessageSink, collectionFactory)
{
}

Expand All @@ -21,18 +21,21 @@ private static SpecFlowProjectAssemblyInfo CreateSpecFlowProjectAssemblyInfo(IAs
return new SpecFlowProjectAssemblyInfo(assemblyInfo);
}

private bool IsSpecFlowTest(object test)
private bool IsSpecFlowTypeInfo(ITypeInfo typeInfo)
{
return test is SpecFlowFeatureTestClass;
return typeInfo is SpecFlowFeatureTypeInfo;
}

private bool IsSpecFlowTest(ITestClass testClass)
{
return testClass is SpecFlowFeatureTestClass;
}

protected override ITestClass CreateTestClass(ITypeInfo typeInfo)
{
if (IsSpecFlowTest(typeInfo))
if (IsSpecFlowTypeInfo(typeInfo))
{
//TODO: return (ITestClass)new SpecFlowTestClass(this.TestCollectionFactory.Get(typeInfo), typeInfo);
((SpecFlowFeatureTestClass)typeInfo).Hack_SetTestCollection(this.TestCollectionFactory.Get(typeInfo));
return (ITestClass) typeInfo;
return new SpecFlowFeatureTestClass(TestCollectionFactory.Get(typeInfo), typeInfo);
}
return base.CreateTestClass(typeInfo);
}
Expand All @@ -43,11 +46,11 @@ protected override bool FindTestsForType(ITestClass testClass, bool includeSourc
if (!IsSpecFlowTest(testClass))
return base.FindTestsForType(testClass, includeSourceInformation, messageBus, discoveryOptions);

var featureTestClass = (SpecFlowFeatureTestClass)testClass;
var gherkinDocument = featureTestClass.GetDocument();
var featureTestClass = ((SpecFlowFeatureTestClass)testClass);
var gherkinDocument = featureTestClass.FeatureTypeInfo.GetDocument();
if (gherkinDocument?.SpecFlowFeature != null)
{
featureTestClass.FeatureName = gherkinDocument.SpecFlowFeature.Name;
featureTestClass.FeatureTypeInfo.FeatureName = gherkinDocument.SpecFlowFeature.Name;
var featureTags = gherkinDocument.SpecFlowFeature.Tags.GetTags().ToArray();
foreach (var scenarioDefinition in gherkinDocument.SpecFlowFeature.ScenarioDefinitions.Where(sd => !(sd is Background)))
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Xunit.Abstractions;
using Xunit.Sdk;

// ReSharper disable once CheckNamespace
namespace SpecFlow.xUnitAdapter.SpecFlowPlugin
{
public class SpecFlowTestFramework : XunitTestFramework
Expand All @@ -10,14 +11,10 @@ public SpecFlowTestFramework(IMessageSink diagnosticMessageSink) : base(diagnost
{
}

// we only override the discoverer, we can use the built-in executor as it is anyway delegates the actual exectution to the test cases (ScenarioTestCase)
protected override ITestFrameworkDiscoverer CreateDiscoverer(IAssemblyInfo assemblyInfo)
{
return new SpecFlowTestDiscoverer(assemblyInfo, SourceInformationProvider, DiagnosticMessageSink);
}

//protected override ITestFrameworkExecutor CreateExecutor(AssemblyName assemblyName)
//{
// return new SpecFlowTestFrameworkExecutor(assemblyName, SourceInformationProvider, DiagnosticMessageSink);
//}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public void FeatureSetup(GherkinDocument gherkinDocument)
Debug.Assert(gherkinDocument.Feature != null);
var feature = gherkinDocument.Feature;

var assembly = Assembly.LoadFrom(TestCase.FeatureFile.SpecFlowProject.AssemblyPath);
var assembly = Assembly.LoadFrom(TestCase.FeatureTypeInfo.SpecFlowProject.AssemblyPath);
testRunner = TestRunnerManager.GetTestRunner(assembly);
var featureInfo = new FeatureInfo(GetFeatureCulture(feature.Language), feature.Name, feature.Description, ProgrammingLanguage.CSharp, feature.Tags.GetTags().ToArray());
testRunner.OnFeatureStart(featureInfo);
Expand Down Expand Up @@ -79,7 +79,7 @@ protected override async Task<RunSummary> RunTestAsync()
var summary = new RunSummary() { Total = 1 };
string output = "";

var gherkinDocument = await this.TestCase.FeatureFile.GetDocumentAsync();
var gherkinDocument = await this.TestCase.FeatureTypeInfo.GetDocumentAsync();


Scenario scenario = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@
<ItemGroup>
<Compile Include="Runners\XUnitTraceListener.cs" />
<Compile Include="TestArtifacts\SpecFlowFeatureTestClass.cs" />
<Compile Include="TestArtifacts\EmbeddedFeatureFileTestClass.cs" />
<Compile Include="TestArtifacts\FeatureFileTestClass.cs" />
<Compile Include="TestArtifacts\SpecFlowFeatureTypeInfo.cs" />
<Compile Include="TestArtifacts\EmbeddedFeatureTypeInfo.cs" />
<Compile Include="TestArtifacts\FeatureFileTypeInfo.cs" />
<Compile Include="Runners\ScenarioTestCaseRunner.cs" />
<Compile Include="TestArtifacts\SpecFlowGenericFixtureType.cs" />
<Compile Include="SpecFlowParserHelper.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@

namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.TestArtifacts
{
public class EmbeddedFeatureFileTestClass : SpecFlowFeatureTestClass
public class EmbeddedFeatureTypeInfo : SpecFlowFeatureTypeInfo
{
public EmbeddedFeatureFileTestClass()
: base()
public EmbeddedFeatureTypeInfo()
{
}

public EmbeddedFeatureFileTestClass(SpecFlowProjectAssemblyInfo specFlowProject, string resourceName)
public EmbeddedFeatureTypeInfo(SpecFlowProjectAssemblyInfo specFlowProject, string resourceName)
: base(specFlowProject, resourceName)
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,13 @@

namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.TestArtifacts
{
public class FeatureFileTestClass : SpecFlowFeatureTestClass
public class FeatureFileTypeInfo : SpecFlowFeatureTypeInfo
{
public FeatureFileTestClass()
public FeatureFileTypeInfo()
{
}

public FeatureFileTestClass(SpecFlowProjectAssemblyInfo specFlowProject, string relativePath)
public FeatureFileTypeInfo(SpecFlowProjectAssemblyInfo specFlowProject, string relativePath)
: base(specFlowProject, relativePath)
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.TestArtifacts
{
public class ScenarioTestCase : LongLivedMarshalByRefObject, ITestMethod, IXunitTestCase, IReflectionMethodInfo
{
public SpecFlowFeatureTestClass FeatureFile { get; private set; }
public SpecFlowFeatureTestClass FeatureTestClass { get; private set; }
public SpecFlowFeatureTypeInfo FeatureTypeInfo => FeatureTestClass.FeatureTypeInfo;
public string Name { get; private set; }

public string DisplayName => GetDisplayName();
public string UniqueID => $"{FeatureFile.RelativePath};{Name};{ExampleId}";
public string UniqueID => $"{FeatureTypeInfo.RelativePath};{Name};{ExampleId}";

public ISourceInformation SourceInformation { get; set; }
public string SkipReason { get; set; }
Expand All @@ -29,7 +30,7 @@ public class ScenarioTestCase : LongLivedMarshalByRefObject, ITestMethod, IXunit
public bool IsScenarioOutline => !string.IsNullOrEmpty(ExampleId);

object[] ITestCase.TestMethodArguments => ScenarioOutlineParameters?.Cast<object>().ToArray();
public ITestClass TestClass => FeatureFile;
public ITestClass TestClass => FeatureTestClass;
public ITestMethod TestMethod => this;
public IMethodInfo Method => this;

Expand All @@ -40,9 +41,9 @@ public ScenarioTestCase()

private ScenarioTestCase(SpecFlowFeatureTestClass featureTestClass, ScenarioDefinition scenario, string[] featureTags, Location location)
{
FeatureFile = featureTestClass;
FeatureTestClass = featureTestClass;
Name = scenario.Name;
SourceInformation = new SourceInformation { FileName = featureTestClass.FeatureFilePath, LineNumber = location?.Line };
SourceInformation = new SourceInformation { FileName = FeatureTypeInfo.FeatureFilePath, LineNumber = location?.Line };
Traits = new Dictionary<string, List<string>>();
Traits.Add("Category", featureTags.Concat(((IHasTags)scenario).Tags.GetTags()).ToList());
}
Expand Down Expand Up @@ -70,14 +71,14 @@ private string GetDisplayName()

public void Deserialize(IXunitSerializationInfo data)
{
FeatureFile = data.GetValue<SpecFlowFeatureTestClass>("FeatureFile");
FeatureTestClass = data.GetValue<SpecFlowFeatureTestClass>("FeatureTestClass");
Name = data.GetValue<string>("Name");
ExampleId = data.GetValue<string>("ExampleId");
}

public void Serialize(IXunitSerializationInfo data)
{
data.AddValue("FeatureFile", FeatureFile);
data.AddValue("FeatureTestClass", FeatureTestClass);
data.AddValue("Name", Name);
data.AddValue("ExampleId", ExampleId);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,84 +1,48 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using TechTalk.SpecFlow.Parser;
using System;
using Xunit;
using Xunit.Abstractions;
using Xunit.Sdk;

namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.TestArtifacts
{
public abstract class SpecFlowFeatureTestClass : LongLivedMarshalByRefObject, ITypeInfo, IReflectionTypeInfo, ITestClass
public class SpecFlowFeatureTestClass : LongLivedMarshalByRefObject, ITestClass
{
public string FeatureName { get; set; }
public string RelativePath { get; private set; }
public SpecFlowProjectAssemblyInfo SpecFlowProject { get; private set; }

public virtual string FeatureFilePath { get; protected set; }

IAssemblyInfo ITypeInfo.Assembly => SpecFlowProject;
string ITypeInfo.Name => (FeatureName ?? RelativePath).Replace(".", "");
Type IReflectionTypeInfo.Type { get { return typeof(SpecFlowGenericFixtureType); } }

public ITypeInfo Class => this;
public ITypeInfo Class { get; private set; }
public ITestCollection TestCollection { get; private set; }

#region ITypeInfo default implementation
IEnumerable<IAttributeInfo> ITypeInfo.GetCustomAttributes(string assemblyQualifiedAttributeTypeName) => Enumerable.Empty<IAttributeInfo>();
IEnumerable<ITypeInfo> ITypeInfo.GetGenericArguments() => Enumerable.Empty<ITypeInfo>();
IMethodInfo ITypeInfo.GetMethod(string methodName, bool includePrivateMethod) => null;
IEnumerable<IMethodInfo> ITypeInfo.GetMethods(bool includePrivateMethods) => Enumerable.Empty<IMethodInfo>();
ITypeInfo ITypeInfo.BaseType => null;
IEnumerable<ITypeInfo> ITypeInfo.Interfaces => Enumerable.Empty<ITypeInfo>();
bool ITypeInfo.IsAbstract => false;
bool ITypeInfo.IsGenericParameter => false;
bool ITypeInfo.IsGenericType => false;
bool ITypeInfo.IsSealed => false;
bool ITypeInfo.IsValueType => false;
#endregion

protected SpecFlowFeatureTestClass() { }
public SpecFlowFeatureTypeInfo FeatureTypeInfo => (SpecFlowFeatureTypeInfo)Class;

protected SpecFlowFeatureTestClass(SpecFlowProjectAssemblyInfo specFlowProject, string relativePath)
[Obsolete("Called by the de-serializer; should only be called by deriving classes for de-serialization purposes")]
public SpecFlowFeatureTestClass()
{
SpecFlowProject = specFlowProject;
RelativePath = relativePath;
}

internal void Hack_SetTestCollection(ITestCollection testCollection)
public SpecFlowFeatureTestClass(ITestCollection testCollection, ITypeInfo typeInfo)
{
TestCollection = testCollection;
}

protected ISpecFlowSourceMapper SpecFlowSourceMapper { get; } = new SpecFlowSourceMapperV1();
if (!(typeInfo is SpecFlowFeatureTypeInfo))
throw new ArgumentException($"Must me an instance of {nameof(SpecFlowFeatureTypeInfo)}", "typeInfo");

public virtual void Deserialize(IXunitSerializationInfo data)
{
SpecFlowProject = data.GetValue<SpecFlowProjectAssemblyInfo>("SpecFlowProject");
RelativePath = data.GetValue<string>("RelativePath");
TestCollection = data.GetValue<ITestCollection>("TestCollection");
TestCollection = testCollection;
Class = typeInfo;
}

public virtual void Serialize(IXunitSerializationInfo data)
public void Serialize(IXunitSerializationInfo info)
{
data.AddValue("SpecFlowProject", SpecFlowProject);
data.AddValue("RelativePath", RelativePath);
data.AddValue("TestCollection", TestCollection);
info.AddValue("TestCollection", TestCollection);
info.AddValue("SpecFlowProject", FeatureTypeInfo.SpecFlowProject);
info.AddValue("RelativePath", FeatureTypeInfo.RelativePath);
info.AddValue("TypeInfoKind", Class.GetType().Name);
}

protected SpecFlowDocument ParseDocument(string content, string path, SpecFlowGherkinParser parser)
public void Deserialize(IXunitSerializationInfo info)
{
var sourceMap = this.SpecFlowSourceMapper.ReadSourceMap(content);

this.FeatureFilePath = sourceMap?.SourcePath ?? path;

return parser.Parse(new StringReader(content), this.FeatureFilePath);
TestCollection = info.GetValue<ITestCollection>("TestCollection");
var specFlowProject = info.GetValue<SpecFlowProjectAssemblyInfo>("SpecFlowProject");
var relativePath = info.GetValue<string>("RelativePath");
var typeInfoKind = info.GetValue<string>("TypeInfoKind");
if (typeInfoKind == nameof(FeatureFileTypeInfo))
Class = new FeatureFileTypeInfo(specFlowProject, relativePath);
else if (typeInfoKind == nameof(EmbeddedFeatureTypeInfo))
Class = new EmbeddedFeatureTypeInfo(specFlowProject, relativePath);
}

public abstract SpecFlowDocument GetDocument();

public abstract Task<SpecFlowDocument> GetDocumentAsync();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using TechTalk.SpecFlow.Parser;
using Xunit;
using Xunit.Abstractions;

namespace SpecFlow.xUnitAdapter.SpecFlowPlugin.TestArtifacts
{
public abstract class SpecFlowFeatureTypeInfo : LongLivedMarshalByRefObject, ITypeInfo, IReflectionTypeInfo
{
public string FeatureName { get; set; }
public string RelativePath { get; }
public SpecFlowProjectAssemblyInfo SpecFlowProject { get; }

public virtual string FeatureFilePath { get; protected set; }

IAssemblyInfo ITypeInfo.Assembly => SpecFlowProject;
string ITypeInfo.Name => (FeatureName ?? RelativePath).Replace(".", "");
Type IReflectionTypeInfo.Type => typeof(SpecFlowGenericFixtureType);

#region ITypeInfo default implementation
IEnumerable<IAttributeInfo> ITypeInfo.GetCustomAttributes(string assemblyQualifiedAttributeTypeName) => Enumerable.Empty<IAttributeInfo>();
IEnumerable<ITypeInfo> ITypeInfo.GetGenericArguments() => Enumerable.Empty<ITypeInfo>();
IMethodInfo ITypeInfo.GetMethod(string methodName, bool includePrivateMethod) => null;
IEnumerable<IMethodInfo> ITypeInfo.GetMethods(bool includePrivateMethods) => Enumerable.Empty<IMethodInfo>();
ITypeInfo ITypeInfo.BaseType => null;
IEnumerable<ITypeInfo> ITypeInfo.Interfaces => Enumerable.Empty<ITypeInfo>();
bool ITypeInfo.IsAbstract => false;
bool ITypeInfo.IsGenericParameter => false;
bool ITypeInfo.IsGenericType => false;
bool ITypeInfo.IsSealed => false;
bool ITypeInfo.IsValueType => false;
#endregion

protected SpecFlowFeatureTypeInfo() { }

protected SpecFlowFeatureTypeInfo(SpecFlowProjectAssemblyInfo specFlowProject, string relativePath)
{
SpecFlowProject = specFlowProject;
RelativePath = relativePath;
}

protected ISpecFlowSourceMapper SpecFlowSourceMapper { get; } = new SpecFlowSourceMapperV1();

protected SpecFlowDocument ParseDocument(string content, string path, SpecFlowGherkinParser parser)
{
var sourceMap = this.SpecFlowSourceMapper.ReadSourceMap(content);

this.FeatureFilePath = sourceMap?.SourcePath ?? path;

return parser.Parse(new StringReader(content), this.FeatureFilePath);
}

public abstract SpecFlowDocument GetDocument();

public abstract Task<SpecFlowDocument> GetDocumentAsync();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -43,15 +43,15 @@ public IEnumerable<ITypeInfo> GetTypes(bool includePrivateTypes)
foreach (var resourceName in assembly.GetManifestResourceNames().Where(x => x.EndsWith(".feature")))
{
Console.WriteLine($" {resourceName}");
yield return new EmbeddedFeatureFileTestClass(this, resourceName);
yield return new EmbeddedFeatureTypeInfo(this, resourceName);
}

Console.WriteLine($" Discovering feature files from folder {FeatureFilesFolder}");
foreach (var featureFilePath in Directory.GetFiles(FeatureFilesFolder, "*.feature", SearchOption.AllDirectories))
{
var relativePath = featureFilePath.Substring(FeatureFilesFolder.Length).TrimStart(Path.DirectorySeparatorChar);
Console.WriteLine($" {relativePath}");
yield return new FeatureFileTestClass(this, relativePath);
yield return new FeatureFileTypeInfo(this, relativePath);
}

// discovering "standard" xunit tests to allow incremental transition from the generated style to the new style
Expand Down

0 comments on commit 9ff9a4c

Please sign in to comment.