Skip to content

Commit

Permalink
Fix for #44 Reqnroll in RootNamespace breaks generated Files (#85)
Browse files Browse the repository at this point in the history
* Fix for #44 Reqnroll in RootNamespace breaks generated Files
This fix changes all references to Reqnroll types in the generated code as global:: references.

* Moved test to GenerationTestBase.
Modified Xunit2TestGenerationProvider so that it's reference to Reqnroll.xUnit classes were prefixed with global::
Modified GeneratorTests unit test to include the global:: prefix when looking for the FeatureInfo constructor in generated source.

* Adjusted handling of Xunit generator's handling of references to Reqnroll types in order to not break VB generation.

* Revised comment in CodeDomHelper regarding support for global:: namespace resolution for VB.NET.
  • Loading branch information
clrudolphi authored Mar 28, 2024
1 parent 45f6d9c commit 864fe0e
Show file tree
Hide file tree
Showing 8 changed files with 54 additions and 15 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# [vNext]

* Fix for #44 in which user code namespaces that included "Reqnroll" within them caused the code generation to fail
* Include built-in dependency injection framework (BoDi) to the main repository as "Reqnroll.BoDi" based on v1.5 of [BoDi](https://github.com/SpecFlowOSS/BoDi/)
* Resolve dependencies of [BeforeTestRun] / [AfterTestRun] hooks from the
test run (global) context instead of the test thread context.
Expand Down
10 changes: 10 additions & 0 deletions Reqnroll.Generator/CodeDom/CodeDomHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,16 @@ private CodeExpression GetAwaitedMethodThisTargetObject(CodeExpression thisExpre
_ => thisExpression
};
}

public string GetGlobalizedTypeName(Type type)
{
if (TargetLanguage == CodeDomProviderLanguage.CSharp)
{
return "global::" + type.FullName!;
}
// Global namespaces not yet supported in VB
return type.FullName!;
}
}
}

Expand Down
6 changes: 3 additions & 3 deletions Reqnroll.Generator/Generation/ScenarioPartHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ private CodeExpression GetTableArgExpression(Gherkin.Ast.DataTable tableArg, Lis
{
if (tableArg == null)
{
return new CodeCastExpression(typeof(Table), new CodePrimitiveExpression(null));
return new CodeCastExpression(_codeDomHelper.GetGlobalizedTypeName(typeof(Table)), new CodePrimitiveExpression(null));
}

_tableCounter++;
Expand All @@ -145,9 +145,9 @@ private CodeExpression GetTableArgExpression(Gherkin.Ast.DataTable tableArg, Lis
//Table table0 = new Table(header...);
var tableVar = new CodeVariableReferenceExpression("table" + _tableCounter);
statements.Add(
new CodeVariableDeclarationStatement(typeof(Table), tableVar.VariableName,
new CodeVariableDeclarationStatement(_codeDomHelper.GetGlobalizedTypeName(typeof(Table)), tableVar.VariableName,
new CodeObjectCreateExpression(
typeof(Table),
_codeDomHelper.GetGlobalizedTypeName(typeof(Table)),
GetStringArrayExpression(header.Cells.Select(c => c.Value), paramToIdentifier))));

foreach (var row in body)
Expand Down
12 changes: 6 additions & 6 deletions Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ private void SetupTestClass(TestClassGenerationContext generationContext)

private CodeMemberField DeclareTestRunnerMember(CodeTypeDeclaration type)
{
var testRunnerField = new CodeMemberField(typeof(ITestRunner), GeneratorConstants.TESTRUNNER_FIELD);
var testRunnerField = new CodeMemberField(_codeDomHelper.GetGlobalizedTypeName(typeof(ITestRunner)), GeneratorConstants.TESTRUNNER_FIELD);
type.Members.Add(testRunnerField);
return testRunnerField;
}
Expand All @@ -185,7 +185,7 @@ private void SetupTestClassInitializeMethod(TestClassGenerationContext generatio
};

var getTestRunnerExpression = new CodeMethodInvokeExpression(
new CodeTypeReferenceExpression(typeof(TestRunnerManager)),
new CodeTypeReferenceExpression(_codeDomHelper.GetGlobalizedTypeName(typeof(TestRunnerManager))),
nameof(TestRunnerManager.GetTestRunnerForAssembly), testRunnerParameters);

testClassInitializeMethod.Statements.Add(
Expand All @@ -195,15 +195,15 @@ private void SetupTestClassInitializeMethod(TestClassGenerationContext generatio

//FeatureInfo featureInfo = new FeatureInfo("xxxx");
testClassInitializeMethod.Statements.Add(
new CodeVariableDeclarationStatement(typeof(FeatureInfo), "featureInfo",
new CodeObjectCreateExpression(typeof(FeatureInfo),
new CodeVariableDeclarationStatement(_codeDomHelper.GetGlobalizedTypeName(typeof(FeatureInfo)), "featureInfo",
new CodeObjectCreateExpression(_codeDomHelper.GetGlobalizedTypeName(typeof(FeatureInfo)),
new CodeObjectCreateExpression(typeof(CultureInfo),
new CodePrimitiveExpression(generationContext.Feature.Language)),
new CodePrimitiveExpression(generationContext.Document.DocumentLocation?.FeatureFolderPath),
new CodePrimitiveExpression(generationContext.Feature.Name),
new CodePrimitiveExpression(generationContext.Feature.Description),
new CodeFieldReferenceExpression(
new CodeTypeReferenceExpression("ProgrammingLanguage"),
new CodeTypeReferenceExpression(_codeDomHelper.GetGlobalizedTypeName(typeof(Reqnroll.ProgrammingLanguage))),
_codeDomHelper.TargetLanguage.ToString()),
new CodeFieldReferenceExpression(null, GeneratorConstants.FEATURE_TAGS_VARIABLE_NAME))));

Expand Down Expand Up @@ -289,7 +289,7 @@ private void SetupScenarioInitializeMethod(TestClassGenerationContext generation
scenarioInitializeMethod.Attributes = MemberAttributes.Public | MemberAttributes.Final;
scenarioInitializeMethod.Name = GeneratorConstants.SCENARIO_INITIALIZE_NAME;
scenarioInitializeMethod.Parameters.Add(
new CodeParameterDeclarationExpression(typeof(ScenarioInfo), "scenarioInfo"));
new CodeParameterDeclarationExpression(_codeDomHelper.GetGlobalizedTypeName(typeof(ScenarioInfo)), "scenarioInfo"));

//testRunner.OnScenarioInitialize(scenarioInfo);
var testRunnerField = _scenarioPartHelper.GetTestRunnerExpression();
Expand Down
6 changes: 3 additions & 3 deletions Reqnroll.Generator/Generation/UnitTestMethodGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -191,8 +191,8 @@ private void GenerateTestBody(
AddVariableForArguments(testMethod, paramToIdentifier);

testMethod.Statements.Add(
new CodeVariableDeclarationStatement(typeof(ScenarioInfo), "scenarioInfo",
new CodeObjectCreateExpression(typeof(ScenarioInfo),
new CodeVariableDeclarationStatement(_codeDomHelper.GetGlobalizedTypeName(typeof(ScenarioInfo)), "scenarioInfo",
new CodeObjectCreateExpression(_codeDomHelper.GetGlobalizedTypeName(typeof(ScenarioInfo)),
new CodePrimitiveExpression(scenarioDefinition.Name),
new CodePrimitiveExpression(scenarioDefinition.Description),
new CodeVariableReferenceExpression(GeneratorConstants.SCENARIO_TAGS_VARIABLE_NAME),
Expand Down Expand Up @@ -277,7 +277,7 @@ internal void GenerateTestMethodBody(TestClassGenerationContext generationContex
var tagsOfScenarioVariableReferenceExpression = new CodeVariableReferenceExpression(GeneratorConstants.SCENARIO_TAGS_VARIABLE_NAME);
var featureFileTagFieldReferenceExpression = new CodeFieldReferenceExpression(null, GeneratorConstants.FEATURE_TAGS_VARIABLE_NAME);

var tagHelperReference = new CodeTypeReferenceExpression(nameof(TagHelper));
var tagHelperReference = new CodeTypeReferenceExpression(_codeDomHelper.GetGlobalizedTypeName(typeof(TagHelper)));
var scenarioTagIgnoredCheckStatement = new CodeMethodInvokeExpression(tagHelperReference, nameof(TagHelper.ContainsIgnoreTag), tagsOfScenarioVariableReferenceExpression);
var featureTagIgnoredCheckStatement = new CodeMethodInvokeExpression(tagHelperReference, nameof(TagHelper.ContainsIgnoreTag), featureFileTagFieldReferenceExpression);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ public virtual void FinalizeTestClass(TestClassGenerationContext generationConte
new CodePropertyReferenceExpression(new CodeVariableReferenceExpression(generationContext.TestRunnerField.Name), "TestWorkerId")));
generationContext.TestClassCleanupMethod.Statements.Add(
new CodeMethodInvokeExpression(
new CodeVariableReferenceExpression(XUNITPARALLELWORKERTRACKER_INSTANCE),
new CodeVariableReferenceExpression(GlobalNamespaceIfCSharp(XUNITPARALLELWORKERTRACKER_INSTANCE)),
"ReleaseWorker",
new CodeVariableReferenceExpression("testWorkerId")));
}
Expand Down Expand Up @@ -418,8 +418,13 @@ public CodeExpression GetTestWorkerIdExpression()
{
// XUnitParallelWorkerTracker.Instance.GetWorkerId()
return new CodeMethodInvokeExpression(
new CodeVariableReferenceExpression(XUNITPARALLELWORKERTRACKER_INSTANCE),
new CodeVariableReferenceExpression(GlobalNamespaceIfCSharp(XUNITPARALLELWORKERTRACKER_INSTANCE)),
"GetWorkerId");
}

private string GlobalNamespaceIfCSharp(string typeName)
{
return CodeDomHelper.TargetLanguage == CodeDomProviderLanguage.CSharp ? "global::" + typeName : typeName;
}
}
}
2 changes: 1 addition & 1 deletion Tests/Reqnroll.GeneratorTests/TestGeneratorBasicsTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ public void Should_detect_outdated_test_file_based_on_context_based_up_to_date_c

private static string AssertFolderPathArgument(string outputFile)
{
var match = Regex.Match(outputFile, @"new Reqnroll.FeatureInfo\([^;]*");
var match = Regex.Match(outputFile, @"new global::Reqnroll\.FeatureInfo\([^;]*");
match.Success.Should().BeTrue("FeatureInfo ctor should be found in output");
var folderPathArgument = match.Value.Split(',')[1].Trim();
folderPathArgument.Should().StartWith("\"").And.EndWith("\"", "the folderPath argument should be a string");
Expand Down
23 changes: 23 additions & 0 deletions Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,29 @@ public void GeneratorAllIn_sample_can_be_handled()
ShouldAllScenariosPass();
}

[TestMethod]
public void Handles_simple_scenarios_without_namespace_collisions()
{
_projectsDriver.CreateProject("CollidingNamespace.Reqnroll", "C#");

AddScenario(
"""
Scenario: Sample Scenario
When something happens
Scenario: Scenario with DataTable
When something happens with
| who | when |
| me | today |
| someone else | tomorrow |
""");
_projectsDriver.AddPassingStepBinding();

ExecuteTests();

ShouldAllScenariosPass();
}

//TODO: test different outcomes: success, failure, pending, undefined, ignored (scenario & scenario outline)
//TODO: test async steps (async steps are executed in order)
//TODO: test hooks: before/after test run (require special handling by test frameworks)
Expand Down

0 comments on commit 864fe0e

Please sign in to comment.