diff --git a/.gitignore b/.gitignore index e5440cfd0..2845d4764 100644 --- a/.gitignore +++ b/.gitignore @@ -221,7 +221,7 @@ ClientBin/ *.publishsettings orleans.codegen.cs -# Including strong name files can present a security risk +# Including strong name files can present a security risk # (https://github.com/github/gitignore/pull/2483#issue-259490424) #*.snk @@ -315,7 +315,7 @@ __pycache__/ # OpenCover UI analysis results OpenCover/ -# Azure Stream Analytics local run output +# Azure Stream Analytics local run output ASALocalRun/ # MSBuild Binary and Structured Log @@ -384,3 +384,5 @@ docs/_build/* /docs/Scripts/ /docs/pyvenv.cfg nCrunchTemp*.csproj + +*.received.* diff --git a/CHANGELOG.md b/CHANGELOG.md index 3db9844f8..73984face 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,22 @@ # [vNext] +## Improvements: + ## Bug fixes: -* Modified VersionInfo class to force it to pull version information from the Reqnroll assembly + +*Contributors of this release (in alphabetical order):* + +# v2.1.1 - 2024-10-08 + +## Bug fixes: + +* Fix: Rule Backgounds cause External Data Plugin to fail (#271) +* Fix: VersionInfo class might provide the version of the runner instead of the version of Reqnroll (#248) * Fix: Reqnroll.CustomPlugin NuGet package has a version mismatch for the System.CodeDom dependency (#244) +* Fix: Reqnroll.Verify fails to run parallel tests determinately (#254). See our [verify documentation](docs/integrations/verify.md) on how to set up your test code to enable parallel testing. +* Fix: Reqnroll generates invalid code for rule backgrounds in Visual Basic (#283) -*Contributors of this release (in alphabetical order):* @clrudolphi, @UL-ChrisGlew +*Contributors of this release (in alphabetical order):* @ajeckmans, @clrudolphi, @gasparnagy, @UL-ChrisGlew # v2.1.0 - 2024-08-30 diff --git a/Directory.Build.props b/Directory.Build.props index 6b98e2f3a..f4be4af73 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -10,7 +10,7 @@ </PropertyGroup> <PropertyGroup> - <VersionPrefix>2.1.1</VersionPrefix> + <VersionPrefix>2.1.2</VersionPrefix> <VersionSuffix>local</VersionSuffix> <AssemblyVersion>2.0.0</AssemblyVersion> <CompatibilityVersionUpperRange>3</CompatibilityVersionUpperRange> diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataWithRuleBackgroundFromCSV.feature b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataWithRuleBackgroundFromCSV.feature new file mode 100644 index 000000000..eb1df2fb0 --- /dev/null +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataWithRuleBackgroundFromCSV.feature @@ -0,0 +1,17 @@ +Feature: ExternalDataWithRuleBackgroundFromCSV + +This feature demonstrates that Rule Background steps are handled appropriately when using the External Data Plugin. +This test validates a regression. + +Rule: Steps in Background are properly executed + Background: + Given my favorite color is blue +@DataSource:products.csv +Scenario: The basket price is calculated correctly + The scenario will be treated as a scenario outline with the examples from the CSV file. + The CSV file contains multile fields, including product and price. + Given the price of <product> is €<price> + And the customer has put 1 pcs of <product> to the basket + When the basket price is calculated + Then the basket price should be €<price> + And the color given as my favorite was blue diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/StepDefinitions/BackgroundStepsDefinitions.cs b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/StepDefinitions/BackgroundStepsDefinitions.cs new file mode 100644 index 000000000..85fe363a6 --- /dev/null +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/StepDefinitions/BackgroundStepsDefinitions.cs @@ -0,0 +1,28 @@ +using NUnit.Framework; +using Reqnroll; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace XReqnroll.ExternalData.ReqnrollPlugin.IntegrationTest.StepDefinitions +{ + [Binding] + internal class BackgroundStepsDefinitions + { + [Given("my favorite color is {word}")] + public void GivenMyFavoriteColorIs(string color) + { + _color = color; + } + + private string _color; + + [Then("the color given as my favorite was {word}")] + public void ThenTheColorGivenAsMyFavoriteWas(string color) + { + Assert.That(_color, Is.EqualTo(color)); + } + } +} diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Transformation/GherkinDocumentVisitor.cs b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Transformation/GherkinDocumentVisitor.cs index ef25d8a7f..5c043bf0a 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Transformation/GherkinDocumentVisitor.cs +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Transformation/GherkinDocumentVisitor.cs @@ -69,7 +69,8 @@ protected virtual void AcceptRule(Rule rule) OnRuleVisiting(rule); foreach (var ruleChild in rule.Children) { - if (ruleChild is ScenarioOutline scenarioOutline) AcceptScenarioOutline(scenarioOutline); + if (ruleChild is Background background) AcceptBackground(background); + else if (ruleChild is ScenarioOutline scenarioOutline) AcceptScenarioOutline(scenarioOutline); else if (ruleChild is Scenario scenario) AcceptScenario(scenario); } OnRuleVisited(rule); diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Transformation/ScenarioTransformation.cs b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Transformation/ScenarioTransformation.cs index d38fbd2f7..da6a8425f 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Transformation/ScenarioTransformation.cs +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Transformation/ScenarioTransformation.cs @@ -66,7 +66,7 @@ private void OnScenarioVisitedInternal(Scenario scenario, Scenario transformedSc protected override void OnBackgroundVisited(Background background) { - _featureChildren.Add(background); + _currentChildren.Add(background); } protected override void OnRuleVisiting(Rule rule) diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Feature.feature b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Feature.feature index 0d9ac25f0..7177a9a72 100644 --- a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Feature.feature +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Feature.feature @@ -1,14 +1,28 @@ +@DoNotParallelize Feature: Verify Test -Scenario: Check if Verify is working - When I try Verify with Reqnroll - Then it works - -Scenario Outline: Check if Verify is working with Example Tables - When I try Verify with Reqnroll for Parameter '<Parameter>' - Then it works - -Examples: - | Parameter | - | 1 | - | 2 | + Scenario: Check if Verify is working + When I try Verify with Reqnroll + Then it works + + Scenario Outline: Check if Verify is working with Example Tables + When I try Verify with Reqnroll for Parameter '<Parameter>' + Then it works + + Examples: + | Parameter | + | 1 | + | 2 | + + Scenario: Check if Verify is working with multiple scenario parameters + When I try Verify with Reqnroll for Parameter '<Parameter>' and some additional parameter '<Additional Parameter>' + Then it works + + Examples: + | Parameter | Additional Parameter | + | 1 | a | + | 2 | b | + + Scenario: Check if Verify is working with global registered path info + When I try Verify with Reqnroll with global registered path info + Then it works \ No newline at end of file diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Parallel Feature 1.feature b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Parallel Feature 1.feature new file mode 100644 index 000000000..fdcba0691 --- /dev/null +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Parallel Feature 1.feature @@ -0,0 +1,6 @@ +Feature: Verify Parallel feature #1 + + Scenario: Check if Verify uses the correct paths when ran in parallel 1 + When I try Verify with Reqnroll in parallel + Then it works in parallel with contents `Verify Parallel feature #1` + And the verified file is `Verify Parallel feature #1.Check if Verify uses the correct paths when ran in parallel 1.verified.txt` with contents `Verify Parallel feature #1` \ No newline at end of file diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Parallel Feature 2.feature b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Parallel Feature 2.feature new file mode 100644 index 000000000..f07b88325 --- /dev/null +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Parallel Feature 2.feature @@ -0,0 +1,6 @@ +Feature: Verify Parallel feature #2 + + Scenario: Check if Verify uses the correct paths when ran in parallel 2 + When I try Verify with Reqnroll in parallel + Then it works in parallel with contents `Verify Parallel feature #2` + And the verified file is `Verify Parallel feature #2.Check if Verify uses the correct paths when ran in parallel 2.verified.txt` with contents `Verify Parallel feature #2` diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Parallel feature #1.Check if Verify uses the correct paths when ran in parallel 1.verified.txt b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Parallel feature #1.Check if Verify uses the correct paths when ran in parallel 1.verified.txt new file mode 100644 index 000000000..f2569bc79 --- /dev/null +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Parallel feature #1.Check if Verify uses the correct paths when ran in parallel 1.verified.txt @@ -0,0 +1 @@ +Verify Parallel feature #1 \ No newline at end of file diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Parallel feature #2.Check if Verify uses the correct paths when ran in parallel 2.verified.txt b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Parallel feature #2.Check if Verify uses the correct paths when ran in parallel 2.verified.txt new file mode 100644 index 000000000..541a2a529 --- /dev/null +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Parallel feature #2.Check if Verify uses the correct paths when ran in parallel 2.verified.txt @@ -0,0 +1 @@ +Verify Parallel feature #2 \ No newline at end of file diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Test.Check if Verify is working with global registered path info.verified.txt b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Test.Check if Verify is working with global registered path info.verified.txt new file mode 100644 index 000000000..3c877c57a --- /dev/null +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Test.Check if Verify is working with global registered path info.verified.txt @@ -0,0 +1 @@ +value \ No newline at end of file diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Test.Check if Verify is working with multiple scenario parameters_1_a.verified.txt b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Test.Check if Verify is working with multiple scenario parameters_1_a.verified.txt new file mode 100644 index 000000000..63035262b --- /dev/null +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Test.Check if Verify is working with multiple scenario parameters_1_a.verified.txt @@ -0,0 +1 @@ +1 .. a \ No newline at end of file diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Test.Check if Verify is working with multiple scenario parameters_2_b.verified.txt b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Test.Check if Verify is working with multiple scenario parameters_2_b.verified.txt new file mode 100644 index 000000000..a848bd483 --- /dev/null +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Features/Verify Test.Check if Verify is working with multiple scenario parameters_2_b.verified.txt @@ -0,0 +1 @@ +2 .. b \ No newline at end of file diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.csproj b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.csproj index 97bdb3819..3def15624 100644 --- a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.csproj +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.csproj @@ -34,10 +34,6 @@ <ReqnrollGeneratorPlugins Include="$(MSBuildThisFileDirectory)..\..\Reqnroll.xUnit.Generator.ReqnrollPlugin\bin\$(Configuration)\netstandard2.0\Reqnroll.xUnit.Generator.ReqnrollPlugin.dll" /> </ItemGroup> - <ItemGroup> - <Folder Include="Features\" /> - </ItemGroup> - <Import Project="..\..\..\Reqnroll.Tools.MsBuild.Generation\build\Reqnroll.Tools.MsBuild.Generation.targets" /> <!-- has to be before the PropertyGroup change--> <PropertyGroup> diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/StepDefinitions/StepDefinitions.cs b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/StepDefinitions/StepDefinitions.cs index f5f56e96e..9c0dac825 100644 --- a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/StepDefinitions/StepDefinitions.cs +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/StepDefinitions/StepDefinitions.cs @@ -3,20 +3,79 @@ namespace Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.StepDefinitions; [Binding] internal class StepDefinitions { + private readonly VerifySettings _settings; + private VerifyResult _verifyResult; + + public StepDefinitions(VerifySettings settings) + { + _settings = settings; + } + [When("I try Verify with Reqnroll")] - public async Task ITryVerifyWithReqnroll() + public async Task WhenITryVerifyWithReqnroll() { - await Verifier.Verify("value"); + await Verifier.Verify("value", _settings); } [When(@"I try Verify with Reqnroll for Parameter '([^']*)'")] public async Task WhenITryVerifyWithReqnrollForParameter(string p0) + { + await Verifier.Verify("value", _settings); + } + + [When(@"I try Verify with Reqnroll for Parameter '(.*)' and some additional parameter '(.*)'")] + public async Task WhenITryVerifyWithReqnrollForParameterAndSomeAdditionalParameter(string p0, string p1) + { + await Verifier.Verify($"{p0} .. {p1}", _settings); + } + + [When(@"I try Verify with Reqnroll with global registered path info")] + public async Task WhenITryVerifyWithReqnrollWithGlobalRegisteredPathInfo() { await Verifier.Verify("value"); } [Then("it works")] - public void ItWorks() + public void ThenItWorks() { + // no-op + } + + private static readonly object Lock = new(); + private static bool _isFirstCallComplete; + + [When(@"I try Verify with Reqnroll in parallel")] + public static void WhenITryVerifyWithReqnrollInParallel() + { + // lock so the first execution will always finish after the second execution + + if (!_isFirstCallComplete) + { + lock (Lock) + { + if (!_isFirstCallComplete) + { + Thread.Sleep(500); + _isFirstCallComplete = true; + } + } + } + } + + [Then(@"it works in parallel with contents `(.*)`")] + public async Task ThenItWorksInParallelWithVerifyContents(string contents) + { + _verifyResult = await Verifier.Verify(contents, _settings); + } + + [Then(@"the verified file is `(.*)` with contents `(.*)`")] + public void ThenTheVerifiedFileIsWithContents(string fileName, string contents) + { + var actualFilePath = _verifyResult.Files.First(); + var actualFileName = Path.GetFileName(actualFilePath); + Assert.Equal(fileName, actualFileName); + + var actualContents = File.ReadAllText(actualFilePath); + Assert.Equal(contents, actualContents); } } diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/reqnroll.json b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/reqnroll.json new file mode 100644 index 000000000..2968e98b6 --- /dev/null +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/reqnroll.json @@ -0,0 +1,8 @@ +{ + "$schema": "https://schemas.reqnroll.net/reqnroll-config-latest.json", + "generator": { + "addNonParallelizableMarkerForTags": [ + "DoNotParallelize" + ] + } +} \ No newline at end of file diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/VerifyRuntimePlugin.cs b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/VerifyRuntimePlugin.cs index d9bba2ed3..8c358f328 100644 --- a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/VerifyRuntimePlugin.cs +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/VerifyRuntimePlugin.cs @@ -1,5 +1,9 @@ +using System; using System.Collections; using System.IO; +using System.Linq; +using System.Text; +using System.Threading; using Reqnroll.Plugins; using Reqnroll.UnitTestProvider; using Reqnroll.Verify.ReqnrollPlugin; @@ -15,33 +19,59 @@ public class VerifyRuntimePlugin : IRuntimePlugin public void Initialize(RuntimePluginEvents runtimePluginEvents, RuntimePluginParameters runtimePluginParameters, UnitTestProviderConfiguration unitTestProviderConfiguration) { runtimePluginEvents.CustomizeGlobalDependencies += RuntimePluginEvents_CustomizeGlobalDependencies; + runtimePluginEvents.CustomizeScenarioDependencies += RuntimePluginEvents_CustomizeScenarioDependencies; } private void RuntimePluginEvents_CustomizeGlobalDependencies(object sender, CustomizeGlobalDependenciesEventArgs e) { var runtimePluginTestExecutionLifecycleEvents = e.ObjectContainer.Resolve<RuntimePluginTestExecutionLifecycleEvents>(); - runtimePluginTestExecutionLifecycleEvents.BeforeScenario += RuntimePluginTestExecutionLifecycleEvents_BeforeScenario; + runtimePluginTestExecutionLifecycleEvents.BeforeScenario += (_, runtimePluginBeforeScenarioEventArgs) => + { + var scenarioContext = runtimePluginBeforeScenarioEventArgs.ObjectContainer.Resolve<ScenarioContext>(); + var featureContext = runtimePluginBeforeScenarioEventArgs.ObjectContainer.Resolve<FeatureContext>(); + + Verifier.DerivePathInfo( + (_, projectDirectory, _, _) => + { + string scenarioInfoTitle = scenarioContext.ScenarioInfo.Title; + + foreach (DictionaryEntry scenarioInfoArgument in scenarioContext.ScenarioInfo.Arguments) + { + scenarioInfoTitle += "_" + scenarioInfoArgument.Value; + } + + return new PathInfo( + Path.Combine(projectDirectory, featureContext.FeatureInfo.FolderPath), + featureContext.FeatureInfo.Title, + scenarioInfoTitle); + }); + }; } - private void RuntimePluginTestExecutionLifecycleEvents_BeforeScenario(object sender, RuntimePluginBeforeScenarioEventArgs e) + private void RuntimePluginEvents_CustomizeScenarioDependencies(object sender, CustomizeScenarioDependenciesEventArgs e) { - var scenarioContext = e.ObjectContainer.Resolve<ScenarioContext>(); - var featureContext = e.ObjectContainer.Resolve<FeatureContext>(); - - Verifier.DerivePathInfo( - (sourceFile, projectDirectory, type, method) => + e.ObjectContainer.RegisterFactoryAs( + container => { - string scenarioInfoTitle = scenarioContext.ScenarioInfo.Title; + var featureContext = container.Resolve<FeatureContext>(); + var scenarioContext = container.Resolve<ScenarioContext>(); - foreach (DictionaryEntry scenarioInfoArgument in scenarioContext.ScenarioInfo.Arguments) + var settings = new VerifySettings(); + string projectDirectory = Directory.GetCurrentDirectory().Split([@"\bin\"], StringSplitOptions.RemoveEmptyEntries).First(); + + settings.UseDirectory(Path.Combine(projectDirectory, featureContext.FeatureInfo.FolderPath)); + settings.UseTypeName(featureContext.FeatureInfo.Title); + + var methodNameBuilder = new StringBuilder(scenarioContext.ScenarioInfo.Title); + + foreach (DictionaryEntry entry in scenarioContext.ScenarioInfo.Arguments) { - scenarioInfoTitle += "_" + scenarioInfoArgument.Value; + methodNameBuilder.AppendFormat("_{0}", entry.Value); } - return new PathInfo( - Path.Combine(projectDirectory, featureContext.FeatureInfo.FolderPath), - featureContext.FeatureInfo.Title, - scenarioInfoTitle); + settings.UseMethodName(methodNameBuilder.ToString()); + + return settings; }); } } diff --git a/Reqnroll.Generator/CodeDom/CodeDomHelper.cs b/Reqnroll.Generator/CodeDom/CodeDomHelper.cs index 9d649dbbb..407d49280 100644 --- a/Reqnroll.Generator/CodeDom/CodeDomHelper.cs +++ b/Reqnroll.Generator/CodeDom/CodeDomHelper.cs @@ -107,7 +107,7 @@ public CodeStatement GetDisableWarningsPragma() case CodeDomProviderLanguage.CSharp: return new CodeSnippetStatement("#pragma warning disable"); case CodeDomProviderLanguage.VB: - return new CodeCommentStatement("#pragma warning disable"); //not supported in VB + return new CodeSnippetStatement("#Disable Warning BC42356"); //in VB warning codes must be listed explicitly } return new CodeCommentStatement("#pragma warning disable"); } @@ -119,7 +119,7 @@ public CodeStatement GetEnableWarningsPragma() case CodeDomProviderLanguage.CSharp: return new CodeSnippetStatement("#pragma warning restore"); case CodeDomProviderLanguage.VB: - return new CodeCommentStatement("#pragma warning restore"); //not supported in VB + return new CodeSnippetStatement("#Enable Warning BC42356"); //in VB warning codes must be listed explicitly } return new CodeCommentStatement("#pragma warning restore"); } diff --git a/Reqnroll.Generator/Generation/ScenarioPartHelper.cs b/Reqnroll.Generator/Generation/ScenarioPartHelper.cs index 307b7d8a6..e8395f841 100644 --- a/Reqnroll.Generator/Generation/ScenarioPartHelper.cs +++ b/Reqnroll.Generator/Generation/ScenarioPartHelper.cs @@ -68,12 +68,9 @@ private IEnumerable<CodeStatement> GenerateBackgroundStatementsForRule(TestClass if (background == null) return new List<CodeStatement>(); var statements = new List<CodeStatement>(); - using (new SourceLineScope(_reqnrollConfiguration, _codeDomHelper, statements, context.Document.SourceFilePath, background.Location)) + foreach (var step in background.Steps) { - foreach (var step in background.Steps) - { - GenerateStep(context, statements, step, null); - } + GenerateStep(context, statements, step, null); } return statements; diff --git a/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs b/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs index c145706cb..04cd07cdf 100644 --- a/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs +++ b/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs @@ -21,6 +21,18 @@ public void GeneratorAllIn_sample_can_be_handled() ShouldAllScenariosPass(); } + [TestMethod] + public void GeneratorAllIn_sample_can_be_handled_with_VisualBasic() + { + _testRunConfiguration.ProgrammingLanguage = ProgrammingLanguage.VB; + + PrepareGeneratorAllInSamples(); + + ExecuteTests(); + + ShouldAllScenariosPass(); + } + [TestMethod] public void Handles_simple_scenarios_without_namespace_collisions() { diff --git a/Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/SolutionTests.cs b/Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/SolutionTests.cs index 799de72fe..c89a02b9b 100644 --- a/Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/SolutionTests.cs +++ b/Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/SolutionTests.cs @@ -27,26 +27,33 @@ public void CreateEmptySolution() } - [Theory] + [SkippableTheory] [InlineData(ProgrammingLanguage.CSharp, "csproj")] [InlineData(ProgrammingLanguage.FSharp, "fsproj")] [InlineData(ProgrammingLanguage.VB, "vbproj")] public void CreateSolutionWithProject(ProgrammingLanguage programmingLanguage, string expectedEnding) { - string folder = Path.Combine(Path.GetTempPath(), "Reqnroll.TestProjectGenerator.Tests", Guid.NewGuid().ToString("N")); + try + { + string folder = Path.Combine(Path.GetTempPath(), "Reqnroll.TestProjectGenerator.Tests", Guid.NewGuid().ToString("N")); - var solution = new Solution("SolutionName"); - var project = new Project("ProjectName", Guid.NewGuid(), programmingLanguage, TargetFramework.Net462, ProjectFormat.New); + var solution = new Solution("SolutionName"); + var project = new Project("ProjectName", Guid.NewGuid(), programmingLanguage, TargetFramework.Net462, ProjectFormat.New); - solution.AddProject(project); + solution.AddProject(project); - var solutionWriter = CreateSolutionWriter(); + var solutionWriter = CreateSolutionWriter(); - solutionWriter.WriteToFileSystem(solution, folder); + solutionWriter.WriteToFileSystem(solution, folder); - File.Exists(Path.Combine(folder, "SolutionName.sln")).Should().BeTrue(); - File.Exists(Path.Combine(folder, "ProjectName", $"ProjectName.{expectedEnding}")).Should().BeTrue(); + File.Exists(Path.Combine(folder, "SolutionName.sln")).Should().BeTrue(); + File.Exists(Path.Combine(folder, "ProjectName", $"ProjectName.{expectedEnding}")).Should().BeTrue(); + } + catch (DotNetSdkNotInstalledException ex) + { + Skip.IfNot(new ConfigurationDriver().PipelineMode, ex.ToString()); + } } } } diff --git a/Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/VbBindingsGenerator.cs b/Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/VbBindingsGenerator.cs index c6c0808e1..d21e1e102 100644 --- a/Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/VbBindingsGenerator.cs +++ b/Tests/TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/VbBindingsGenerator.cs @@ -20,52 +20,66 @@ Public Class {0} public override ProjectFile GenerateLoggerClass(string pathToLogFile) { - string fileContent = $@" -Imports System -Imports System.IO -Imports System.Runtime.CompilerServices -Imports System.Threading.Tasks - -Friend Module Log - Private Const LogFileLocation As String = ""{pathToLogFile}"" - - Friend Sub LogStep(<CallerMemberName()> Optional stepName As String = Nothing) - File.AppendAllText(LogFileLocation, $""-> step: {{stepName}}{{Environment.NewLine}}"") - End Sub - - Friend Sub LogHook(<CallerMemberName()> Optional stepName As String = Nothing) - File.AppendAllText(LogFileLocation, $""-> hook: {{stepName}}{{Environment.NewLine}}"") - End Sub - - Friend Async Function LogHookIncludingLockingAsync( - <CallerMemberName> ByVal Optional stepName As String = Nothing) As Task - File.AppendAllText(LogFileLocation, $""->waiting for hook lock: {{stepName}}{{Environment.NewLine}}"") - Await WaitForLockAsync() - File.AppendAllText(LogFileLocation, $""-> hook: {{stepName}}{{Environment.NewLine}}"") - End Function - - Private Async Function WaitForLockAsync() As Task - Dim lockFile = LogFileLocation & "".lock"" - - While True - - Dim succeeded = True - Try - Using File.Open(lockFile, FileMode.CreateNew) - End Using - Exit While - Catch __unusedIOException1__ As IOException - succeeded = False - End Try - If Not succeeded Then - Await Task.Delay(1000) - End If - End While - - File.Delete(lockFile) - End Function -End Module -"; + string fileContent = + $$""" + Imports System + Imports System.IO + Imports System.Runtime.CompilerServices + Imports System.Threading.Tasks + + Friend Module Log + Private Const LogFileLocation As String = "{{pathToLogFile}}" + + Private Sub Retry(number As Integer, action As Action) + Try + action + Catch ex As Exception + Dim i = number - 1 + If (i = 0) + Throw + End If + System.Threading.Thread.Sleep(500) + Retry(i, action) + End Try + End Sub + + Friend Sub LogStep(<CallerMemberName()> Optional stepName As String = Nothing) + Retry(5, sub() File.AppendAllText(LogFileLocation, $"-> step: {stepName}{Environment.NewLine}")) + End Sub + + Friend Sub LogHook(<CallerMemberName()> Optional stepName As String = Nothing) + Retry(5, sub() File.AppendAllText(LogFileLocation, $"-> hook: {stepName}{Environment.NewLine}")) + End Sub + + Friend Async Function LogHookIncludingLockingAsync( + <CallerMemberName> ByVal Optional stepName As String = Nothing) As Task + File.AppendAllText(LogFileLocation, $"->waiting for hook lock: {stepName}{Environment.NewLine}") + Await WaitForLockAsync() + File.AppendAllText(LogFileLocation, $"-> hook: {stepName}{Environment.NewLine}") + End Function + + Private Async Function WaitForLockAsync() As Task + Dim lockFile = LogFileLocation & ".lock" + + While True + + Dim succeeded = True + Try + Using File.Open(lockFile, FileMode.CreateNew) + End Using + Exit While + Catch __unusedIOException1__ As IOException + succeeded = False + End Try + If Not succeeded Then + Await Task.Delay(1000) + End If + End While + + File.Delete(lockFile) + End Function + End Module + """; return new ProjectFile("Log.vb", "Compile", fileContent); } diff --git a/docs/ide-integrations/vscode/index.md b/docs/ide-integrations/vscode/index.md index f0fe4930a..3ed96e983 100644 --- a/docs/ide-integrations/vscode/index.md +++ b/docs/ide-integrations/vscode/index.md @@ -1,4 +1,4 @@ -# Reqnroll Visual Studio integration +# Reqnroll Visual Studio Code integration For setting up Reqnroll Visual Studio Code integration using the VSCode Cucumber plugin, please check the related [Setup Visual Studio Code](../../installation/setup-ide.md#setup-vscode) section of our IDE setup guide. diff --git a/docs/integrations/available-plugins.md b/docs/integrations/available-plugins.md index cec3178c2..444b3c918 100644 --- a/docs/integrations/available-plugins.md +++ b/docs/integrations/available-plugins.md @@ -4,12 +4,13 @@ | Name | Description | Download | |---|---|---| -|[Reqnroll.Autofac](https://github.com/reqnroll/Reqnroll)|Reqnroll plugin for using Autofac as a dependency injection framework for step definitions. [Read more...](autofac.md)|<a href="https://www.nuget.org/packages/Reqnroll.Autofac/"></a>| -|[Reqnroll.Windsor](https://github.com/reqnroll/Reqnroll)|Reqnroll plugin for using Castle Windsor as a dependency injection framework for step definitions. [Read more...](windsor.md)|<a href="https://www.nuget.org/packages/Reqnroll.Windsor/"></a>| -|[Reqnroll.Microsoft.Extensions.DependencyInjection](https://github.com/reqnroll/Reqnroll)|Reqnroll plugin for using Microsoft.Extensions.DependencyInjection as a dependency injection framework for step definitions. [Read more...](https://github.com/reqnroll/Reqnroll)|<a href="https://www.nuget.org/packages/Reqnroll.Microsoft.Extensions.DependencyInjection/"></a>| +| [Reqnroll.Autofac](https://github.com/reqnroll/Reqnroll) | Reqnroll plugin for using Autofac as a dependency injection framework for step definitions. [Read more...](autofac.md)| <a href="https://www.nuget.org/packages/Reqnroll.Autofac/"></a> | +| [Reqnroll.Microsoft.Extensions.DependencyInjection](https://github.com/reqnroll/Reqnroll) | Reqnroll plugin for using Microsoft.Extensions.DependencyInjection as a dependency injection framework for step definitions. [Read more...](dependency-injection.md) | <a href="https://www.nuget.org/packages/Reqnroll.Microsoft.Extensions.DependencyInjection/"></a> | +| [Reqnroll.Windsor](https://github.com/reqnroll/Reqnroll) | Reqnroll plugin for using Castle Windsor as a dependency injection framework for step definitions. [Read more...](windsor.md) | <a href="https://www.nuget.org/packages/Reqnroll.Windsor/"></a> | ## Other Plugins | Name | Description | Download | |---|---|---| -| [Reqnroll.External Data](https://www.nuget.org/packages/Reqnroll.ExternalData/) | Package to use external data in Gherkin scenarios. [Read more...](https://go.reqnroll.net/doc-externaldata) | <a href="https://www.nuget.org/packages/Reqnroll.ExternalData/"></a>| +| [Reqnroll.External Data](https://www.nuget.org/packages/Reqnroll.ExternalData/) | Package to use external data in Gherkin scenarios. [Read more...](https://go.reqnroll.net/doc-externaldata) | <a href="https://www.nuget.org/packages/Reqnroll.ExternalData/"></a> | +| [Reqnroll.Verify](https://github.com/reqnroll/Reqnroll/tree/main/Plugins/Reqnroll.Verify) | Reqnroll plugin for using Verify in scenario. [Read more...](verify.md) | <a href="https://www.nuget.org/packages/Reqnroll.Verify/"></a> | \ No newline at end of file diff --git a/docs/integrations/index.md b/docs/integrations/index.md index 773c43e82..1df6e41fd 100644 --- a/docs/integrations/index.md +++ b/docs/integrations/index.md @@ -8,10 +8,10 @@ This part contains details of the following topics. available-plugins autofac dependency-injection -windsor -fsharp externaldata +fsharp mstest nunit +windsor xunit ``` diff --git a/docs/integrations/nunit.md b/docs/integrations/nunit.md index 047d0f1a4..5c96947de 100644 --- a/docs/integrations/nunit.md +++ b/docs/integrations/nunit.md @@ -2,7 +2,7 @@ Reqnroll supports NUnit 3.13.1 or later. -Documentation for NUnit can be found [here](https://github.com/nunit/docs/wiki/NUnit-Documentation). +Documentation for NUnit can be found [here](https://docs.nunit.org/articles/nunit/intro.html). ## Needed NuGet Packages diff --git a/docs/integrations/verify.md b/docs/integrations/verify.md new file mode 100644 index 000000000..b22100c29 --- /dev/null +++ b/docs/integrations/verify.md @@ -0,0 +1,50 @@ +# Verify + +Reqnroll supports Verify 24.2.0 or later. + +Documentation for Verify can be found [here](https://github.com/VerifyTests/Verify). + +## Needed NuGet Packages + +* [Reqnroll.xUnit](https://www.nuget.org/packages/Reqnroll.xUnit/) and its [dependencies](xunit.md#Needed%20NuGet%20Packages) +* [Reqnroll.Verify](https://www.nuget.org/packages/Reqnroll.Verify/) + +## How it works + +This plugin adds a VerifySettings instance to Reqnroll's scenario container, which can be used to set the correct path for the tests' verified files. + +### Example + +```Gherkin +Feature: Verify feature + + Scenario: Verify scenario + When I calculate 1 + 2 + Then I expect the result is correct +``` +```csharp +[Binding] +internal class StepDefinitions +{ + private readonly VerifySettings _settings; + + public StepDefinitions(VerifySettings settings) + { + _settings = settings; + } + + [When("I calculate (\d+) + (\d+)")] + public void WhenICalculate(int value, int value2) + { + _settings.Verify(value + value, _settings); + } + + [Then("I expect the result is correct")] + public void ThenIExpectTheResultIsCorrect() + { + } +} + +``` + +**Note:** in a single-threaded environment, the plugin will work without the injected VerifySettings instance. However, in a multithreaded environment, the VerifySettings instance must be injected into the step definition class for a deterministic outcome.