diff --git a/CHANGELOG.md b/CHANGELOG.md index 09bae5894..56adc0b8b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/Reqnroll.Generator/CodeDom/CodeDomHelper.cs b/Reqnroll.Generator/CodeDom/CodeDomHelper.cs index 97f8e5426..7829c115f 100644 --- a/Reqnroll.Generator/CodeDom/CodeDomHelper.cs +++ b/Reqnroll.Generator/CodeDom/CodeDomHelper.cs @@ -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!; + } } } diff --git a/Reqnroll.Generator/Generation/ScenarioPartHelper.cs b/Reqnroll.Generator/Generation/ScenarioPartHelper.cs index 67f54627d..307b7d8a6 100644 --- a/Reqnroll.Generator/Generation/ScenarioPartHelper.cs +++ b/Reqnroll.Generator/Generation/ScenarioPartHelper.cs @@ -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++; @@ -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) diff --git a/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs b/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs index 252447b2a..0eb430d51 100644 --- a/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs +++ b/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs @@ -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; } @@ -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( @@ -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)))); @@ -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(); diff --git a/Reqnroll.Generator/Generation/UnitTestMethodGenerator.cs b/Reqnroll.Generator/Generation/UnitTestMethodGenerator.cs index c21d17b2a..ba2ef7e3a 100644 --- a/Reqnroll.Generator/Generation/UnitTestMethodGenerator.cs +++ b/Reqnroll.Generator/Generation/UnitTestMethodGenerator.cs @@ -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), @@ -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); diff --git a/Reqnroll.Generator/UnitTestProvider/XUnit2TestGeneratorProvider.cs b/Reqnroll.Generator/UnitTestProvider/XUnit2TestGeneratorProvider.cs index 56bb2714e..1355cdffd 100644 --- a/Reqnroll.Generator/UnitTestProvider/XUnit2TestGeneratorProvider.cs +++ b/Reqnroll.Generator/UnitTestProvider/XUnit2TestGeneratorProvider.cs @@ -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"))); } @@ -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; + } } } \ No newline at end of file diff --git a/Tests/Reqnroll.GeneratorTests/TestGeneratorBasicsTests.cs b/Tests/Reqnroll.GeneratorTests/TestGeneratorBasicsTests.cs index 446b71229..ee736d19e 100644 --- a/Tests/Reqnroll.GeneratorTests/TestGeneratorBasicsTests.cs +++ b/Tests/Reqnroll.GeneratorTests/TestGeneratorBasicsTests.cs @@ -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"); diff --git a/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs b/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs index 1afb62a73..f76a3a41a 100644 --- a/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs +++ b/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs @@ -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)