diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c782b131d..bc33a26ac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,15 +50,12 @@ jobs: build_params: ${{ steps.versions.outputs.build_params }} test_params: ${{ steps.versions.outputs.test_params }} specs_filter: ${{ steps.versions.outputs.specs_filter }} + gh_logger_settings: ${{ steps.versions.outputs.gh_logger_settings }} steps: - uses: actions/checkout@v4 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: 8.0.x - id: versions name: Calculate versions shell: pwsh @@ -115,22 +112,26 @@ jobs: } Write-Output "main_build_params=$mainBuildParams" >> $env:GITHUB_OUTPUT - - $testParams = "--no-build --verbosity normal -c $productConfig" + $gitHubActionsLoggerSettings = '"GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true;annotations.titleFormat=[@traits.Category] @test;annotations.messageFormat=@error\n@trace"' + $testParams = "--no-build --verbosity normal -c $productConfig --logger $gitHubActionsLoggerSettings -- RunConfiguration.CollectSourceInformation=true" Write-Output "test_params=$testParams" >> $env:GITHUB_OUTPUT Write-Output "Test Params: $testParams" + - name: Restore dependencies run: dotnet restore + - name: Install Test Report Dependencies + run: | + dotnet add ./Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj package GitHubActionsTestLogger + dotnet add ./Tests/Reqnroll.PluginTests/Reqnroll.PluginTests.csproj package GitHubActionsTestLogger + dotnet add ./Tests/Reqnroll.GeneratorTests/Reqnroll.GeneratorTests.csproj package GitHubActionsTestLogger - name: Build run: dotnet build --no-restore ${{ steps.versions.outputs.main_build_params }} - name: Runtime Tests - run: dotnet test ./Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj ${{ steps.versions.outputs.test_params }} -f net6.0 - - name: Plugin Tests - run: dotnet test ./Tests/Reqnroll.PluginTests/Reqnroll.PluginTests.csproj ${{ steps.versions.outputs.test_params }} -f net6.0 + run: dotnet test ./Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj --logger "trx;LogFileName=runtimetests-results.trx" ${{ steps.versions.outputs.test_params }} - name: Generator Tests - run: dotnet test ./Tests/Reqnroll.GeneratorTests/Reqnroll.GeneratorTests.csproj ${{ steps.versions.outputs.test_params }} -f net6.0 - - name: ExternalData Plugin Tests - run: dotnet test ./Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests.csproj ${{ steps.versions.outputs.test_params }} -f net6.0 + run: dotnet test ./Tests/Reqnroll.GeneratorTests/Reqnroll.GeneratorTests.csproj --logger "trx;LogFileName=generatortests-results.trx" ${{ steps.versions.outputs.test_params }} + - name: Plugin Tests + run: dotnet test ./Tests/Reqnroll.PluginTests/Reqnroll.PluginTests.csproj --logger "trx;LogFileName=plugintests-results.trx" ${{ steps.versions.outputs.test_params }} - name: Upload packages uses: actions/upload-artifact@v4 with: @@ -143,43 +144,7 @@ jobs: name: build-trx path: "**/*.trx" - specs-xunit: - runs-on: ubuntu-latest - needs: build - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: | - 6.0.x - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore ${{ needs.build.outputs.build_params }} - - name: Set .NET 6 SDK - run: dotnet new globaljson --sdk-version 6.0.418 - - name: xUnit Specs - shell: pwsh - run: dotnet test ./Tests/Reqnroll.Specs/Reqnroll.Specs.csproj ${{ needs.build.outputs.test_params }} -f net6.0 --filter "Category=xUnit&Category=Net60&Category!=requiresMsBuild${{ needs.build.outputs.specs_filter }}" --logger "trx;LogFileName=specs-xunit-results.trx" - - name: Publish xUnit Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - check_name: xUnit Specs - files: "**/specs-xunit-results.trx" - comment_mode: off - - name: Upload TRX files - uses: actions/upload-artifact@v4 - if: always() - with: - name: specs-xunit-trx - path: "**/specs-xunit-results.trx" - - specs-nunit: + specs: runs-on: ubuntu-latest needs: build @@ -187,69 +152,22 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: | - 6.0.x - - name: Restore dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore ${{ needs.build.outputs.build_params }} - - name: Set .NET 6 SDK - run: dotnet new globaljson --sdk-version 6.0.418 - - name: NUnit Specs - shell: pwsh - run: dotnet test ./Tests/Reqnroll.Specs/Reqnroll.Specs.csproj ${{ needs.build.outputs.test_params }} -f net6.0 --filter "Category=NUnit3&Category=Net60&Category!=requiresMsBuild${{ needs.build.outputs.specs_filter }}" --logger "trx;LogFileName=specs-nunit-results.trx" - - name: Publish NUnit Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - check_name: NUnit Specs - files: "**/specs-nunit-results.trx" - comment_mode: off - - name: Upload TRX files - uses: actions/upload-artifact@v4 - if: always() - with: - name: specs-nunit-trx - path: "**/specs-nunit-results.trx" - - specs-mstest: - runs-on: ubuntu-latest - needs: build - - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 # avoid shallow clone so nbgv can do its work. - - name: Setup .NET - uses: actions/setup-dotnet@v4 - with: - dotnet-version: | - 6.0.x - name: Restore dependencies run: dotnet restore + - name: Install Test Report Dependencies + run: | + dotnet add ./Tests/Reqnroll.Specs/Reqnroll.Specs.csproj package GitHubActionsTestLogger - name: Build run: dotnet build --no-restore ${{ needs.build.outputs.build_params }} - - name: Set .NET 6 SDK - run: dotnet new globaljson --sdk-version 6.0.418 - - name: MsTest Specs + - name: Specs shell: pwsh - run: dotnet test ./Tests/Reqnroll.Specs/Reqnroll.Specs.csproj ${{ needs.build.outputs.test_params }} -f net6.0 --filter "Category=MsTest&Category=Net60&Category!=requiresMsBuild${{ needs.build.outputs.specs_filter }}" --logger "trx;LogFileName=specs-mstest-results.trx" - - name: Publish MSTest Test Results - uses: EnricoMi/publish-unit-test-result-action@v2 - if: always() - with: - check_name: MSTest Specs - files: "**/specs-mstest-results.trx" - comment_mode: off + run: dotnet test ./Tests/Reqnroll.Specs/Reqnroll.Specs.csproj --filter "Category!=quarantaine{{ needs.build.outputs.specs_filter }}" --logger "trx;LogFileName=specs-results.trx" ${{ needs.build.outputs.test_params }} - name: Upload TRX files uses: actions/upload-artifact@v4 if: always() with: - name: specs-mstest-trx - path: "**/specs-mstest-results.trx" + name: specs-trx + path: "**/specs-results.trx" system-tests-windows: runs-on: windows-latest @@ -272,9 +190,7 @@ jobs: run: dotnet build --no-restore ${{ needs.build.outputs.build_params }} - name: System Tests shell: pwsh - run: | - $gitHubActionsLoggerSettings = '"GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true;annotations.titleFormat=[@traits.Category] @test;annotations.messageFormat=@error\n@trace"' - dotnet test ./Tests/Reqnroll.SystemTests/Reqnroll.SystemTests.csproj ${{ needs.build.outputs.test_params }} --logger "trx;LogFileName=systemtests-windows-results.trx" --logger $gitHubActionsLoggerSettings -- RunConfiguration.CollectSourceInformation=true + run: dotnet test ./Tests/Reqnroll.SystemTests/Reqnroll.SystemTests.csproj --logger "trx;LogFileName=systemtests-windows-results.trx" ${{ needs.build.outputs.test_params }} - name: Upload Test Result TRX Files uses: actions/upload-artifact@v4 if: always() @@ -304,9 +220,7 @@ jobs: run: dotnet build --no-restore ${{ needs.build.outputs.build_params }} - name: System Tests shell: pwsh - run: | - $gitHubActionsLoggerSettings = '"GitHubActions;summary.includePassedTests=true;summary.includeSkippedTests=true;annotations.titleFormat=[@traits.Category] @test;annotations.messageFormat=@error\n@trace"' - dotnet test ./Tests/Reqnroll.SystemTests/Reqnroll.SystemTests.csproj ${{ needs.build.outputs.test_params }} --filter "TestCategory!=MsBuild&TestCategory!=Net481" --logger "trx;LogFileName=systemtests-linux-results.trx" --logger $gitHubActionsLoggerSettings -- RunConfiguration.CollectSourceInformation=true + run: dotnet test ./Tests/Reqnroll.SystemTests/Reqnroll.SystemTests.csproj --filter "TestCategory!=MsBuild&TestCategory!=Net481" --logger "trx;LogFileName=systemtests-linux-results.trx" ${{ needs.build.outputs.test_params }} - name: Upload Test Result TRX Files uses: actions/upload-artifact@v4 if: always() diff --git a/.gitignore b/.gitignore index 9b65f193d..c42377080 100644 --- a/.gitignore +++ b/.gitignore @@ -385,3 +385,4 @@ docs/_build/* /docs/Lib/ /docs/Scripts/ /docs/pyvenv.cfg +nCrunchTemp*.csproj diff --git a/.ncrunch/Reqnroll.Autofac.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.Autofac.ReqnrollPlugin.v3.ncrunchproject index 6c4b990aa..95a483b43 100644 --- a/.ncrunch/Reqnroll.Autofac.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.Autofac.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,3 @@  - - - TargetFrameworks = net6.0 - - True - + \ No newline at end of file diff --git a/.ncrunch/Reqnroll.CustomPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.CustomPlugin.v3.ncrunchproject index 6c4b990aa..319cd523c 100644 --- a/.ncrunch/Reqnroll.CustomPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.CustomPlugin.v3.ncrunchproject @@ -1,8 +1,5 @@  - - TargetFrameworks = net6.0 - True \ No newline at end of file diff --git a/.ncrunch/Reqnroll.TestProjectGenerator.Tests.net48.v3.ncrunchproject b/.ncrunch/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest.v3.ncrunchproject similarity index 100% rename from .ncrunch/Reqnroll.TestProjectGenerator.Tests.net48.v3.ncrunchproject rename to .ncrunch/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest.v3.ncrunchproject diff --git a/.ncrunch/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests.v3.ncrunchproject b/.ncrunch/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests.v3.ncrunchproject deleted file mode 100644 index 6c4b990aa..000000000 --- a/.ncrunch/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - TargetFrameworks = net6.0 - - True - - \ No newline at end of file diff --git a/.ncrunch/Reqnroll.ExternalData.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.ExternalData.ReqnrollPlugin.v3.ncrunchproject index 6c4b990aa..95a483b43 100644 --- a/.ncrunch/Reqnroll.ExternalData.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.ExternalData.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,3 @@  - - - TargetFrameworks = net6.0 - - True - + \ No newline at end of file diff --git a/.ncrunch/Reqnroll.Generator.v3.ncrunchproject b/.ncrunch/Reqnroll.Generator.v3.ncrunchproject index 5448450ce..95a483b43 100644 --- a/.ncrunch/Reqnroll.Generator.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.Generator.v3.ncrunchproject @@ -1,8 +1,3 @@  - - - TargetFrameworks = net6.0 - - False - + \ No newline at end of file diff --git a/.ncrunch/Reqnroll.GeneratorTests.v3.ncrunchproject b/.ncrunch/Reqnroll.GeneratorTests.v3.ncrunchproject index 40d7f9349..5e6d62816 100644 --- a/.ncrunch/Reqnroll.GeneratorTests.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.GeneratorTests.v3.ncrunchproject @@ -1,9 +1,5 @@  - - TargetFrameworks = net6.0 - True - False \ No newline at end of file diff --git a/.ncrunch/Reqnroll.MSTest.Generator.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.MSTest.Generator.ReqnrollPlugin.v3.ncrunchproject index 5448450ce..e586040f3 100644 --- a/.ncrunch/Reqnroll.MSTest.Generator.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.MSTest.Generator.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,7 @@  - TargetFrameworks = net6.0 + TargetFrameworks = netstandard2.0 - False \ No newline at end of file diff --git a/.ncrunch/Reqnroll.MSTest.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.MSTest.ReqnrollPlugin.v3.ncrunchproject index 5448450ce..0bcc569d0 100644 --- a/.ncrunch/Reqnroll.MSTest.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.MSTest.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,5 @@  - - TargetFrameworks = net6.0 - False \ No newline at end of file diff --git a/.ncrunch/Reqnroll.MsBuildNetSdk.IntegrationTests.v3.ncrunchproject b/.ncrunch/Reqnroll.MsBuildNetSdk.IntegrationTests.v3.ncrunchproject deleted file mode 100644 index 6c4b990aa..000000000 --- a/.ncrunch/Reqnroll.MsBuildNetSdk.IntegrationTests.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - TargetFrameworks = net6.0 - - True - - \ No newline at end of file diff --git a/.ncrunch/Reqnroll.NUnit.Generator.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.NUnit.Generator.ReqnrollPlugin.v3.ncrunchproject index 5448450ce..e9934c101 100644 --- a/.ncrunch/Reqnroll.NUnit.Generator.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.NUnit.Generator.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,5 @@  - - TargetFrameworks = net6.0 - - False + False \ No newline at end of file diff --git a/.ncrunch/Reqnroll.NUnit.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.NUnit.ReqnrollPlugin.v3.ncrunchproject index 5448450ce..e9934c101 100644 --- a/.ncrunch/Reqnroll.NUnit.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.NUnit.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,5 @@  - - TargetFrameworks = net6.0 - - False + False \ No newline at end of file diff --git a/.ncrunch/Reqnroll.PluginTests.v3.ncrunchproject b/.ncrunch/Reqnroll.PluginTests.v3.ncrunchproject index 6c4b990aa..79ff1b6af 100644 --- a/.ncrunch/Reqnroll.PluginTests.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.PluginTests.v3.ncrunchproject @@ -1,8 +1,15 @@  - - TargetFrameworks = net6.0 - - True + + ExternalData\SampleFiles\**.* + + + + Reqnroll.PluginTests.Generator.GeneratorPluginLoaderTests.LoadPlugin_LoadXUnitSuccessfully + + + Reqnroll.PluginTests.Infrastructure.WindsorPluginTests.Can_load_Windsor_plugin + + \ No newline at end of file diff --git a/.ncrunch/Reqnroll.RuntimeTests.v3.ncrunchproject b/.ncrunch/Reqnroll.RuntimeTests.v3.ncrunchproject index 40d7f9349..9a0eea7aa 100644 --- a/.ncrunch/Reqnroll.RuntimeTests.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.RuntimeTests.v3.ncrunchproject @@ -1,8 +1,5 @@  - - TargetFrameworks = net6.0 - True False diff --git a/.ncrunch/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin.v3.ncrunchproject index 6c4b990aa..e586040f3 100644 --- a/.ncrunch/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,7 @@  - TargetFrameworks = net6.0 + TargetFrameworks = netstandard2.0 - True \ No newline at end of file diff --git a/.ncrunch/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin.v3.ncrunchproject index 6c4b990aa..e586040f3 100644 --- a/.ncrunch/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,7 @@  - TargetFrameworks = net6.0 + TargetFrameworks = netstandard2.0 - True \ No newline at end of file diff --git a/.ncrunch/Reqnroll.Specs.Generator.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.Specs.Generator.ReqnrollPlugin.v3.ncrunchproject deleted file mode 100644 index 6c4b990aa..000000000 --- a/.ncrunch/Reqnroll.Specs.Generator.ReqnrollPlugin.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - TargetFrameworks = net6.0 - - True - - \ No newline at end of file diff --git a/.ncrunch/Reqnroll.Specs.v3.ncrunchproject b/.ncrunch/Reqnroll.Specs.v3.ncrunchproject index 6c4b990aa..319cd523c 100644 --- a/.ncrunch/Reqnroll.Specs.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.Specs.v3.ncrunchproject @@ -1,8 +1,5 @@  - - TargetFrameworks = net6.0 - True \ No newline at end of file diff --git a/.ncrunch/Reqnroll.Templates.DotNet.v3.ncrunchproject b/.ncrunch/Reqnroll.Templates.DotNet.v3.ncrunchproject index 6c4b990aa..319cd523c 100644 --- a/.ncrunch/Reqnroll.Templates.DotNet.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.Templates.DotNet.v3.ncrunchproject @@ -1,8 +1,5 @@  - - TargetFrameworks = net6.0 - True \ No newline at end of file diff --git a/.ncrunch/Reqnroll.TestProjectGenerator.Cli.v3.ncrunchproject b/.ncrunch/Reqnroll.TestProjectGenerator.Cli.v3.ncrunchproject index 8cffa5048..d3fdf31e4 100644 --- a/.ncrunch/Reqnroll.TestProjectGenerator.Cli.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.TestProjectGenerator.Cli.v3.ncrunchproject @@ -1,7 +1,4 @@  - - TargetFrameworks = net6.0 - \ No newline at end of file diff --git a/.ncrunch/Reqnroll.TestProjectGenerator.Tests.net60.v3.ncrunchproject b/.ncrunch/Reqnroll.TestProjectGenerator.Tests.net60.v3.ncrunchproject deleted file mode 100644 index 319cd523c..000000000 --- a/.ncrunch/Reqnroll.TestProjectGenerator.Tests.net60.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Reqnroll.TestProjectGenerator.net471.v3.ncrunchproject b/.ncrunch/Reqnroll.TestProjectGenerator.net471.v3.ncrunchproject deleted file mode 100644 index 319cd523c..000000000 --- a/.ncrunch/Reqnroll.TestProjectGenerator.net471.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Reqnroll.TestProjectGenerator.net60.v3.ncrunchproject b/.ncrunch/Reqnroll.TestProjectGenerator.net60.v3.ncrunchproject deleted file mode 100644 index 319cd523c..000000000 --- a/.ncrunch/Reqnroll.TestProjectGenerator.net60.v3.ncrunchproject +++ /dev/null @@ -1,5 +0,0 @@ - - - True - - \ No newline at end of file diff --git a/.ncrunch/Reqnroll.Tools.MsBuild.Generation.v3.ncrunchproject b/.ncrunch/Reqnroll.Tools.MsBuild.Generation.v3.ncrunchproject index 5448450ce..e586040f3 100644 --- a/.ncrunch/Reqnroll.Tools.MsBuild.Generation.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.Tools.MsBuild.Generation.v3.ncrunchproject @@ -1,8 +1,7 @@  - TargetFrameworks = net6.0 + TargetFrameworks = netstandard2.0 - False \ No newline at end of file diff --git a/.ncrunch/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.v3.ncrunchproject b/.ncrunch/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.v3.ncrunchproject index 6c4b990aa..319cd523c 100644 --- a/.ncrunch/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.v3.ncrunchproject @@ -1,8 +1,5 @@  - - TargetFrameworks = net6.0 - True \ No newline at end of file diff --git a/.ncrunch/Reqnroll.Verify.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.Verify.ReqnrollPlugin.v3.ncrunchproject index 6c4b990aa..e586040f3 100644 --- a/.ncrunch/Reqnroll.Verify.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.Verify.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,7 @@  - TargetFrameworks = net6.0 + TargetFrameworks = netstandard2.0 - True \ No newline at end of file diff --git a/.ncrunch/Reqnroll.Windsor.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.Windsor.ReqnrollPlugin.v3.ncrunchproject index 6c4b990aa..95a483b43 100644 --- a/.ncrunch/Reqnroll.Windsor.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.Windsor.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,3 @@  - - - TargetFrameworks = net6.0 - - True - + \ No newline at end of file diff --git a/.ncrunch/Reqnroll.v3.ncrunchproject b/.ncrunch/Reqnroll.v3.ncrunchproject index e586040f3..95a483b43 100644 --- a/.ncrunch/Reqnroll.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.v3.ncrunchproject @@ -1,7 +1,3 @@  - - - TargetFrameworks = netstandard2.0 - - + \ No newline at end of file diff --git a/.ncrunch/Reqnroll.xUnit.Generator.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.xUnit.Generator.ReqnrollPlugin.v3.ncrunchproject index 5448450ce..cff5044ed 100644 --- a/.ncrunch/Reqnroll.xUnit.Generator.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.xUnit.Generator.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,5 @@  - - TargetFrameworks = net6.0 - - False + False \ No newline at end of file diff --git a/.ncrunch/Reqnroll.xUnit.ReqnrollPlugin.v3.ncrunchproject b/.ncrunch/Reqnroll.xUnit.ReqnrollPlugin.v3.ncrunchproject index 5448450ce..cff5044ed 100644 --- a/.ncrunch/Reqnroll.xUnit.ReqnrollPlugin.v3.ncrunchproject +++ b/.ncrunch/Reqnroll.xUnit.ReqnrollPlugin.v3.ncrunchproject @@ -1,8 +1,5 @@  - - TargetFrameworks = net6.0 - - False + False \ No newline at end of file diff --git a/.ncrunch/Specs.v3.ncrunchproject b/.ncrunch/Specs.v3.ncrunchproject deleted file mode 100644 index 6c4b990aa..000000000 --- a/.ncrunch/Specs.v3.ncrunchproject +++ /dev/null @@ -1,8 +0,0 @@ - - - - TargetFrameworks = net6.0 - - True - - \ No newline at end of file diff --git a/Build/Versioning.targets b/Build/Versioning.targets deleted file mode 100644 index ed47888b5..000000000 --- a/Build/Versioning.targets +++ /dev/null @@ -1,7 +0,0 @@ - - - - 3.5.0 - - - \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 56adc0b8b..78d557aa3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,16 @@ test run (global) context instead of the test thread context. * Support for PriorityAttribute in MsTest adapter * Support for Scenario Outline / DataRowAttribute in MsTest adapter +* Fix for #81 in which Cucumber Expressions fail when two enums or two custom types with the same short name (differing namespaces) are used as parameters +* Fix: Adding @ignore to an Examples block generates invalid code for NUnit v3+ (#103) +* Fix: #111 @ignore attribute is not inherited to the scenarios from Rule +* Support for JSON files added to SpecFlow.ExternalData +* Fix: #120 Capture ExecutionContext after every binding invoke +* Allow creating single target (netstandard2.0) plugins +* MsTest: Use ClassCleanupBehavior.EndOfClass instead of custom implementation (preparation for MsTest v4.0) +* Fix: #71 StackOverflowException when using [StepArgumentTransformation] with same input and output type (for example string) +* Fix: Autofac without hook does not run GlobalDependencies (#127) +* Fix: Reqnroll.Autofac shows wrongly ambiguous step definition (#56) # v1.0.1 - 2024-02-16 diff --git a/Directory.Build.props b/Directory.Build.props index 3d9127a80..0395695cd 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -36,32 +36,9 @@ - net462 - - net6.0 - - - $(Reqnroll_FullFramework_TFM) - netstandard2.0 - - $(Reqnroll_FullFramework_TFM) - netcoreapp3.1 - - $(Reqnroll_FullFramework_TFM) - netcoreapp3.1;net6.0 - - $(Reqnroll_FullFramework_TFM) - $(Reqnroll_Net6_TFM) - - $(Reqnroll_FullFramework_TFM) - netcoreapp3.1 - $(Reqnroll_Net6_TFM) - - $(Reqnroll_FullFramework_Runtime_TFM);$(Reqnroll_Core_Runtime_TFM);$(Reqnroll_Net6_TFM) - $(Reqnroll_FullFramework_Generator_TFM);$(Reqnroll_Core_Generator_TFM);$(Reqnroll_Net6_TFM) - $(Reqnroll_FullFramework_Test_TFM);$(Reqnroll_Core_Test_TFM) - $(Reqnroll_Net6_Specs_TFM) - $(Reqnroll_FullFramework_Tools_TFM);$(Reqnroll_Core_Tools_TFM);$(Reqnroll_Net6_Tools_TFM) + + net462 + netstandard2.0 true diff --git a/Directory.Build.targets b/Directory.Build.targets index c01395040..4d2d2a4c0 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -8,16 +8,8 @@ $(NuspecProperties);author=$(Reqnroll_Authors) $(NuspecProperties);owner=$(Reqnroll_Owners) - $(NuspecProperties);Reqnroll_Net6_TFM=$(Reqnroll_Net6_TFM) - - $(NuspecProperties);Reqnroll_FullFramework_Runtime_TFM=$(Reqnroll_FullFramework_Runtime_TFM) - $(NuspecProperties);Reqnroll_Core_Runtime_TFM=$(Reqnroll_Core_Runtime_TFM) - - $(NuspecProperties);Reqnroll_FullFramework_Generator_TFM=$(Reqnroll_FullFramework_Generator_TFM) - $(NuspecProperties);Reqnroll_Core_Generator_TFM=$(Reqnroll_Core_Generator_TFM) - + $(NuspecProperties);Reqnroll_FullFramework_Tools_TFM=$(Reqnroll_FullFramework_Tools_TFM) $(NuspecProperties);Reqnroll_Core_Tools_TFM=$(Reqnroll_Core_Tools_TFM) - $(NuspecProperties);Reqnroll_Net6_Tools_TFM=$(Reqnroll_Net6_Tools_TFM) diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index ddc00ccbc..000000000 --- a/Dockerfile +++ /dev/null @@ -1,32 +0,0 @@ -FROM mcr.microsoft.com/dotnet/core/sdk:3.1.100-buster - -RUN apt update \ - && apt install -y git mono-complete \ - && dotnet tool install --global PowerShell - -RUN wget -q https://packages.microsoft.com/config/ubuntu/18.04/packages-microsoft-prod.deb -O packages-microsoft-prod.deb \ - && dpkg -i packages-microsoft-prod.deb \ - && apt update \ - && apt install apt-transport-https -y \ - && apt update -RUN apt install dotnet-sdk-3.1 -y - -# RUN git clone --recurse-submodules--single-branch --branch updateLinuxBuild -j8 https://github.com/reqnroll/Reqnroll.git /src \ -# && ls -la - -WORKDIR /src - -COPY . . - -RUN git clean -fdx - -# build project -# RUN pwsh /src/build.ps1 - -RUN pwsh /src/build.ps1 - -#CMD /bin/sh -# CMD dotnet test /src/*.sln -v n --no-build --logger "trx;LogFileName=TestResults.trx" -# CMD dotnet test ./Tests/Reqnroll.Specs/Reqnroll.Specs.csproj -v n --no-build --logger "trx;LogFileName=TestResults.trx" --filter "BasicScenarioExecutionFeature_MSTest" -# CMD dotnet test Reqnroll.sln -v n --no-build --logger "trx;LogFileName=TestResults.trx" --filter "BasicScenarioExecutionFeature_MSTest" && /bin/sh -CMD /bin/sh diff --git a/Installer/Reqnroll.CustomPlugin/Reqnroll.CustomPlugin.csproj b/Installer/Reqnroll.CustomPlugin/Reqnroll.CustomPlugin.csproj index 497cd9a02..bc4b0f3d0 100644 --- a/Installer/Reqnroll.CustomPlugin/Reqnroll.CustomPlugin.csproj +++ b/Installer/Reqnroll.CustomPlugin/Reqnroll.CustomPlugin.csproj @@ -1,7 +1,7 @@ - $(Reqnroll_Generator_TFM) + netstandard2.0 $(MSBuildThisFileDirectory)Reqnroll.CustomPlugin.nuspec true diff --git a/Installer/Reqnroll.CustomPlugin/Reqnroll.CustomPlugin.nuspec b/Installer/Reqnroll.CustomPlugin/Reqnroll.CustomPlugin.nuspec index 2b424d2c9..d37fd9818 100644 --- a/Installer/Reqnroll.CustomPlugin/Reqnroll.CustomPlugin.nuspec +++ b/Installer/Reqnroll.CustomPlugin/Reqnroll.CustomPlugin.nuspec @@ -17,33 +17,15 @@ $copyright$ - - - - - - - - - - - - - - - - - - - - - - + + + + diff --git a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/AutofacPlugin.cs b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/AutofacPlugin.cs index f78c88cee..eeb13ea9c 100644 --- a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/AutofacPlugin.cs +++ b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/AutofacPlugin.cs @@ -1,183 +1,183 @@ using System; using Autofac; +using Reqnroll.BoDi; using Reqnroll.Autofac; -using Reqnroll; using Reqnroll.Infrastructure; using Reqnroll.Plugins; using Reqnroll.UnitTestProvider; [assembly: RuntimePlugin(typeof(AutofacPlugin))] -namespace Reqnroll.Autofac -{ - using Reqnroll.BoDi; +namespace Reqnroll.Autofac; - using Reqnroll; +public class AutofacPlugin : IRuntimePlugin +{ + private static readonly object _registrationLock = new(); - public class AutofacPlugin : IRuntimePlugin + public void Initialize(RuntimePluginEvents runtimePluginEvents, RuntimePluginParameters runtimePluginParameters, UnitTestProviderConfiguration unitTestProviderConfiguration) { - private static readonly Object _registrationLock = new Object(); - - public void Initialize(RuntimePluginEvents runtimePluginEvents, RuntimePluginParameters runtimePluginParameters, UnitTestProviderConfiguration unitTestProviderConfiguration) + runtimePluginEvents.CustomizeGlobalDependencies += (_, args) => { - runtimePluginEvents.CustomizeGlobalDependencies += (sender, args) => + // temporary fix for CustomizeGlobalDependencies called multiple times + // see https://github.com/SpecFlowOSS/SpecFlow/issues/948 + if (!args.ObjectContainer.IsRegistered()) { - // temporary fix for CustomizeGlobalDependencies called multiple times - // see https://github.com/reqnroll/Reqnroll/issues/948 - if (!args.ObjectContainer.IsRegistered()) + // an extra lock to ensure that there are not two fast threads re-registering the same stuff + lock (_registrationLock) { - // an extra lock to ensure that there are not two super fast threads re-registering the same stuff - lock (_registrationLock) + if (!args.ObjectContainer.IsRegistered()) { - if (!args.ObjectContainer.IsRegistered()) + var testRunContainer = args.ObjectContainer; + RegisterTestRunPluginTypes(testRunContainer); + + var containerBuilderFinder = args.ObjectContainer.Resolve(); + var configureGlobalContainer = containerBuilderFinder.GetConfigureGlobalContainer(); + if (configureGlobalContainer != null) { - args.ObjectContainer.RegisterTypeAs(); - args.ObjectContainer.RegisterTypeAs(); - - var containerBuilderFinder = args.ObjectContainer.Resolve(); - var configureGlobalContainer = containerBuilderFinder.GetConfigureGlobalContainer(); - if (configureGlobalContainer != null) - { - var containerBuilder = configureGlobalContainer(new global::Autofac.ContainerBuilder()); - args.ObjectContainer.RegisterFactoryAs(() => containerBuilder.Build()); - } + var containerBuilder = configureGlobalContainer(new global::Autofac.ContainerBuilder()); + args.ObjectContainer.RegisterFactoryAs(() => containerBuilder.Build()); } } - - // workaround for parallel execution issue - this should be rather a feature in BoDi? - args.ObjectContainer.Resolve(); } - }; - runtimePluginEvents.CustomizeTestThreadDependencies += (sender, args) => - { - if (args.ObjectContainer.BaseContainer.IsRegistered()) - { - var container = args.ObjectContainer.BaseContainer.Resolve(); - args.ObjectContainer.RegisterFactoryAs(() => container.BeginLifetimeScope(nameof(TestThreadContext))); - } - }; + // workaround for parallel execution issue - this should be rather a feature in BoDi? + args.ObjectContainer.Resolve(); + } + }; - runtimePluginEvents.CustomizeFeatureDependencies += (sender, args) => + runtimePluginEvents.CustomizeTestThreadDependencies += (_, args) => + { + if (args.ObjectContainer.BaseContainer.IsRegistered()) { - var containerBuilderFinder = args.ObjectContainer.Resolve(); + var container = args.ObjectContainer.BaseContainer.Resolve(); + args.ObjectContainer.RegisterFactoryAs(() => container.BeginLifetimeScope(nameof(TestThreadContext))); + } + }; - var featureScopeFinder = containerBuilderFinder.GetFeatureLifetimeScope(); + runtimePluginEvents.CustomizeFeatureDependencies += (_, args) => + { + var containerBuilderFinder = args.ObjectContainer.Resolve(); - ILifetimeScope featureScope = null; + var featureScopeFinder = containerBuilderFinder.GetFeatureLifetimeScope(); - if (featureScopeFinder != null) - { - featureScope = featureScopeFinder(); - } - else if (args.ObjectContainer.BaseContainer.IsRegistered()) - { - var testThreadScope = args.ObjectContainer.BaseContainer.Resolve(); + ILifetimeScope featureScope = null; - featureScope = testThreadScope.BeginLifetimeScope(nameof(FeatureContext)); - } + if (featureScopeFinder != null) + { + // if the user provided a feature scope creation method, we use that + featureScope = featureScopeFinder(); + } + else if (args.ObjectContainer.BaseContainer.IsRegistered()) + { + // if the TestThread container has a lifetime scope, we make a child scope for the feature + var testThreadScope = args.ObjectContainer.BaseContainer.Resolve(); + featureScope = testThreadScope.BeginLifetimeScope(nameof(FeatureContext)); + } - if (featureScope != null) - { - args.ObjectContainer.RegisterInstanceAs(featureScope); - } - }; + if (featureScope != null) + { + args.ObjectContainer.RegisterInstanceAs(featureScope); + } + }; - runtimePluginEvents.CustomizeScenarioDependencies += (sender, args) => + runtimePluginEvents.CustomizeScenarioDependencies += (_, args) => + { + args.ObjectContainer.RegisterFactoryAs(() => { - args.ObjectContainer.RegisterFactoryAs(() => - { - var containerBuilderFinder = args.ObjectContainer.Resolve(); + var containerBuilderFinder = args.ObjectContainer.Resolve(); - var featureScope = GetFeatureScope(args.ObjectContainer, containerBuilderFinder); + var featureScope = GetFeatureScope(args.ObjectContainer, containerBuilderFinder); - if (featureScope != null) + if (featureScope != null) + { + return featureScope.BeginLifetimeScope(nameof(ScenarioContext), containerBuilder => { - return featureScope.BeginLifetimeScope(nameof(ScenarioContext), containerBuilder => + var configureScenarioContainer = containerBuilderFinder.GetConfigureScenarioContainer(); + + if (configureScenarioContainer != null) { - var configureScenarioContainer = containerBuilderFinder.GetConfigureScenarioContainer(); + containerBuilder = configureScenarioContainer(containerBuilder); + } - if (configureScenarioContainer != null) - { - containerBuilder = configureScenarioContainer(containerBuilder); - } + RegisterReqnrollDependencies(args.ObjectContainer, containerBuilder); + }); + } - RegisterReqnrollDependecies(args.ObjectContainer, containerBuilder); - }); - } + var legacyCreateScenarioContainerBuilder = containerBuilderFinder.GetLegacyCreateScenarioContainerBuilder(); + if (legacyCreateScenarioContainerBuilder == null) + { + throw new Exception("Unable to find scenario dependencies! Mark a static method that has a ContainerBuilder parameter and returns void with [ScenarioDependencies]!"); + } - var createScenarioContainerBuilder = containerBuilderFinder.GetCreateScenarioContainerBuilder(); - if (createScenarioContainerBuilder == null) - { - throw new Exception("Unable to find scenario dependencies! Mark a static method that has a ContainerBuilder parameter and returns void with [ScenarioDependencies]!"); - } + var containerBuilder = legacyCreateScenarioContainerBuilder(null); + RegisterReqnrollDependencies(args.ObjectContainer, containerBuilder); + args.ObjectContainer.RegisterFactoryAs(() => containerBuilder.Build()); + return args.ObjectContainer.Resolve().BeginLifetimeScope(nameof(ScenarioContext)); + }); + }; + } - var containerBuilder = createScenarioContainerBuilder(null); - RegisterReqnrollDependecies(args.ObjectContainer, containerBuilder); - args.ObjectContainer.RegisterFactoryAs(() => containerBuilder.Build()); - return args.ObjectContainer.Resolve().BeginLifetimeScope(nameof(ScenarioContext)); - }); - }; - } + protected virtual void RegisterTestRunPluginTypes(ObjectContainer testRunContainer) + { + testRunContainer.RegisterTypeAs(); + testRunContainer.RegisterTypeAs(); + testRunContainer.RegisterTypeAs(); + } - private static ILifetimeScope GetFeatureScope(ObjectContainer objectContainer, IContainerBuilderFinder containerBuilderFinder) + private static ILifetimeScope GetFeatureScope(ObjectContainer objectContainer, IContainerBuilderFinder containerBuilderFinder) + { + if (objectContainer.BaseContainer.IsRegistered()) { - if (objectContainer.BaseContainer.IsRegistered()) - { - return objectContainer.BaseContainer.Resolve(); - } - - var configureScenarioContainer = containerBuilderFinder.GetConfigureScenarioContainer(); - - if (configureScenarioContainer != null) - { - var containerBuilder = new global::Autofac.ContainerBuilder(); - objectContainer.RegisterFactoryAs(() => containerBuilder.Build()); - return objectContainer.Resolve(); - } - - return null; + return objectContainer.BaseContainer.Resolve(); } + var configureScenarioContainer = containerBuilderFinder.GetConfigureScenarioContainer(); - /// - /// Fix for https://github.com/gasparnagy/Reqnroll.Autofac/issues/11 Cannot resolve ScenarioInfo - /// Extracted from - /// https://github.com/reqnroll/Reqnroll/blob/master/Reqnroll/Infrastructure/ITestObjectResolver.cs - /// The test objects might be dependent on particular Reqnroll infrastructure, therefore the implemented - /// resolution logic should support resolving the following objects (from the provided Reqnroll container): - /// , , and - /// (to be able to resolve any other Reqnroll infrastucture). So basically - /// the resolution of these classes has to be forwarded to the original container. - /// - /// Reqnroll DI container. - /// Autofac ContainerBuilder. - private void RegisterReqnrollDependecies( - IObjectContainer objectContainer, - global::Autofac.ContainerBuilder containerBuilder) + if (configureScenarioContainer != null) { - containerBuilder.Register(ctx => objectContainer).As(); - containerBuilder.Register( - ctx => - { - var reqnrollContainer = ctx.Resolve(); - var scenarioContext = reqnrollContainer.Resolve(); - return scenarioContext; - }).As(); - containerBuilder.Register( - ctx => - { - var reqnrollContainer = ctx.Resolve(); - var scenarioContext = reqnrollContainer.Resolve(); - return scenarioContext; - }).As(); - containerBuilder.Register( - ctx => - { - var reqnrollContainer = ctx.Resolve(); - var scenarioContext = reqnrollContainer.Resolve(); - return scenarioContext; - }).As(); + var containerBuilder = new global::Autofac.ContainerBuilder(); + objectContainer.RegisterFactoryAs(() => containerBuilder.Build()); + return objectContainer.Resolve(); } + + return null; + } + + + /// + /// The test objects might be dependent on particular Reqnroll infrastructure, therefore the implemented + /// resolution logic should support resolving the following objects (from the provided Reqnroll container): + /// , , and + /// (to be able to resolve any other Reqnroll infrastructure). So basically + /// the resolution of these classes has to be forwarded to the original container. + /// + /// Reqnroll DI container. + /// Autofac ContainerBuilder. + private void RegisterReqnrollDependencies( + IObjectContainer objectContainer, + global::Autofac.ContainerBuilder containerBuilder) + { + containerBuilder.Register(_ => objectContainer).As(); + containerBuilder.Register( + ctx => + { + var reqnrollContainer = ctx.Resolve(); + var scenarioContext = reqnrollContainer.Resolve(); + return scenarioContext; + }).As(); + containerBuilder.Register( + ctx => + { + var reqnrollContainer = ctx.Resolve(); + var scenarioContext = reqnrollContainer.Resolve(); + return scenarioContext; + }).As(); + containerBuilder.Register( + ctx => + { + var reqnrollContainer = ctx.Resolve(); + var scenarioContext = reqnrollContainer.Resolve(); + return scenarioContext; + }).As(); } -} +} \ No newline at end of file diff --git a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ConfigurationMethodsProvider.cs b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ConfigurationMethodsProvider.cs new file mode 100644 index 000000000..5272c893a --- /dev/null +++ b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ConfigurationMethodsProvider.cs @@ -0,0 +1,15 @@ +using Reqnroll.Bindings; +using Reqnroll.Infrastructure; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; + +namespace Reqnroll.Autofac; +public class ConfigurationMethodsProvider(ITestAssemblyProvider _testAssemblyProvider) : IConfigurationMethodsProvider +{ + public IEnumerable GetConfigurationMethods() + { + return _testAssemblyProvider.TestAssembly.GetTypes() + .SelectMany(t => t.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)); + } +} diff --git a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ContainerBuilderExtensions.cs b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ContainerBuilderExtensions.cs index b03f9f0de..e66c734f3 100644 --- a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ContainerBuilderExtensions.cs +++ b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ContainerBuilderExtensions.cs @@ -1,7 +1,6 @@ using System; -using System.Linq; +using System.Reflection; using Autofac; -using Reqnroll; namespace Reqnroll.Autofac.ReqnrollPlugin { @@ -13,7 +12,7 @@ public static class ContainerBuilderExtensions /// /// Add Reqnroll binding for classes in the assembly where typeof TAssemblyType resides. /// - /// The type in an assembly to search for bindings. + /// Any type in an assembly to search for bindings. /// The builder. /// The builder. public static ContainerBuilder AddReqnrollBindings(this ContainerBuilder builder) => builder.AddReqnrollBindings(typeof(TAssemblyType)); @@ -22,12 +21,20 @@ public static class ContainerBuilderExtensions /// Add Reqnroll binding for classes in the assembly where the type resides. /// /// The builder. - /// The type in an assembly to search for bindings. + /// Any type in an assembly to search for bindings. /// The builder. - public static ContainerBuilder AddReqnrollBindings(this ContainerBuilder builder, Type type) + public static ContainerBuilder AddReqnrollBindings(this ContainerBuilder builder, Type type) => builder.AddReqnrollBindings(type.Assembly); + + /// + /// Add Reqnroll binding for classes in an assembly. + /// + /// The builder. + /// The assembly to search for bindings. + /// The builder. + public static ContainerBuilder AddReqnrollBindings(this ContainerBuilder builder, Assembly assembly) { builder - .RegisterAssemblyTypes(type.Assembly) + .RegisterAssemblyTypes(assembly) .Where(t => Attribute.IsDefined(t, typeof(BindingAttribute))) .SingleInstance(); return builder; diff --git a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ContainerBuilderFinder.cs b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ContainerBuilderFinder.cs index 42b80db27..c9834f5ee 100644 --- a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ContainerBuilderFinder.cs +++ b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/ContainerBuilderFinder.cs @@ -3,85 +3,77 @@ using System.Reflection; using Autofac; using Reqnroll.Autofac.ReqnrollPlugin; -using Reqnroll.Bindings; -using Reqnroll.Bindings.Discovery; -using Reqnroll.Infrastructure; using ContainerBuilder = Autofac.ContainerBuilder; -namespace Reqnroll.Autofac +namespace Reqnroll.Autofac; + +public class ContainerBuilderFinder : IContainerBuilderFinder { - public class ContainerBuilderFinder : IContainerBuilderFinder + private readonly IConfigurationMethodsProvider _configurationMethodsProvider; + private readonly Lazy> _createConfigureGlobalContainer; + private readonly Lazy> _createConfigureScenarioContainer; + private readonly Lazy> _legacyCreateScenarioContainerBuilder; + private readonly Lazy> _getFeatureLifetimeScope; + + public ContainerBuilderFinder(IConfigurationMethodsProvider configurationMethodsProvider) { - private readonly IBindingRegistry bindingRegistry; - private readonly IRuntimeBindingRegistryBuilder bindingRegistryBuilder; - private readonly ITestAssemblyProvider testAssemblyProvider; - private readonly Lazy> createConfigureGlobalContainer; - private readonly Lazy> createConfigureScenarioContainer; - private readonly Lazy> createScenarioContainerBuilder; - private readonly Lazy> getFeatureLifetimeScope; + _configurationMethodsProvider = configurationMethodsProvider; - public ContainerBuilderFinder(IBindingRegistry bindingRegistry, IRuntimeBindingRegistryBuilder bindingRegistryBuilder, ITestAssemblyProvider testAssemblyProvider) + static ContainerBuilder InvokeVoidAndReturnBuilder(ContainerBuilder containerBuilder, MethodInfo methodInfo) { - this.bindingRegistry = bindingRegistry; - this.bindingRegistryBuilder = bindingRegistryBuilder; - this.testAssemblyProvider = testAssemblyProvider; - static ContainerBuilder invokeVoidAndReturnBuilder(ContainerBuilder containerBuilder, MethodInfo methodInfo) - { - methodInfo.Invoke(null, new[] { containerBuilder }); - return containerBuilder; - } - createConfigureGlobalContainer = new Lazy>(() => FindCreateScenarioContainerBuilder(typeof(GlobalDependenciesAttribute), typeof(void), invokeVoidAndReturnBuilder), true); - createConfigureScenarioContainer = new Lazy>(() => FindCreateScenarioContainerBuilder(typeof(ScenarioDependenciesAttribute), typeof(void), invokeVoidAndReturnBuilder), true); - createScenarioContainerBuilder = new Lazy>(() => FindCreateScenarioContainerBuilder(typeof(ScenarioDependenciesAttribute), typeof(ContainerBuilder), (c, m) => (ContainerBuilder)m.Invoke(null, null)), true); - getFeatureLifetimeScope = new Lazy>(() => FindLifetimeScope(typeof(FeatureDependenciesAttribute), typeof(ILifetimeScope), m => (ILifetimeScope)m.Invoke(null, null))); + methodInfo.Invoke(null, [ containerBuilder ]); + return containerBuilder; } + _createConfigureGlobalContainer = new Lazy>(() => FindCreateScenarioContainerBuilder(typeof(GlobalDependenciesAttribute), typeof(void), InvokeVoidAndReturnBuilder), true); + _createConfigureScenarioContainer = new Lazy>(() => FindCreateScenarioContainerBuilder(typeof(ScenarioDependenciesAttribute), typeof(void), InvokeVoidAndReturnBuilder), true); + _legacyCreateScenarioContainerBuilder = new Lazy>(() => FindCreateScenarioContainerBuilder(typeof(ScenarioDependenciesAttribute), typeof(ContainerBuilder), (_, m) => (ContainerBuilder)m.Invoke(null, null)), true); + _getFeatureLifetimeScope = new Lazy>(() => FindLifetimeScope(typeof(FeatureDependenciesAttribute), typeof(ILifetimeScope), m => (ILifetimeScope)m.Invoke(null, null))); + } - public Func GetConfigureGlobalContainer() - { - bindingRegistryBuilder.BuildBindingsFromAssembly(testAssemblyProvider.TestAssembly); - return createConfigureGlobalContainer.Value; - } + public Func GetConfigureGlobalContainer() + { + return _createConfigureGlobalContainer.Value; + } - public Func GetConfigureScenarioContainer() - { - return createConfigureScenarioContainer.Value; - } + public Func GetConfigureScenarioContainer() + { + return _createConfigureScenarioContainer.Value; + } - public Func GetCreateScenarioContainerBuilder() - { - return createScenarioContainerBuilder.Value; - } + // For legacy support: configuration methods that return a container builder. + // It is recommended to use the void methods that get a container builder as a parameter + public Func GetLegacyCreateScenarioContainerBuilder() + { + return _legacyCreateScenarioContainerBuilder.Value; + } - public Func GetFeatureLifetimeScope() - { - return getFeatureLifetimeScope.Value; - } + public Func GetFeatureLifetimeScope() + { + return _getFeatureLifetimeScope.Value; + } - protected virtual Func FindLifetimeScope(Type attributeType, Type returnType, Func invoke) - { - var method = GetMethod(attributeType, returnType); + protected virtual Func FindLifetimeScope(Type attributeType, Type returnType, Func invoke) + { + var method = GetMethod(attributeType, returnType); - return method == null - ? null - : () => invoke(method); - } + return method == null + ? null + : () => invoke(method); + } - protected virtual Func FindCreateScenarioContainerBuilder(Type attributeType, Type returnType, Func invoke) - { - var method = GetMethod(attributeType, returnType); + protected virtual Func FindCreateScenarioContainerBuilder(Type attributeType, Type returnType, Func invoke) + { + var method = GetMethod(attributeType, returnType); - return method == null - ? null - : containerBuilder => invoke(containerBuilder, method); - } + return method == null + ? null + : containerBuilder => invoke(containerBuilder, method); + } - private MethodInfo GetMethod(Type attributeType, Type returnType) - { - return bindingRegistry.GetBindingAssemblies() - .SelectMany(x => x.GetTypes()) - .SelectMany(x => x.GetMethods(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public)) - .Where(x => x.ReturnType == returnType) - .FirstOrDefault(x => Attribute.IsDefined(x, attributeType)); - } + private MethodInfo GetMethod(Type attributeType, Type returnType) + { + return _configurationMethodsProvider.GetConfigurationMethods() + .Where(x => x.ReturnType == returnType) + .FirstOrDefault(x => Attribute.IsDefined(x, attributeType)); } } \ No newline at end of file diff --git a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/IConfigurationMethodsProvider.cs b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/IConfigurationMethodsProvider.cs new file mode 100644 index 000000000..92a5d3848 --- /dev/null +++ b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/IConfigurationMethodsProvider.cs @@ -0,0 +1,9 @@ +using System.Collections.Generic; +using System.Reflection; + +namespace Reqnroll.Autofac; + +public interface IConfigurationMethodsProvider +{ + IEnumerable GetConfigurationMethods(); +} diff --git a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/IContainerBuilderFinder.cs b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/IContainerBuilderFinder.cs index c321b2d30..04e5d046a 100644 --- a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/IContainerBuilderFinder.cs +++ b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/IContainerBuilderFinder.cs @@ -9,7 +9,7 @@ public interface IContainerBuilderFinder Func GetConfigureGlobalContainer(); - Func GetCreateScenarioContainerBuilder(); + Func GetLegacyCreateScenarioContainerBuilder(); Func GetFeatureLifetimeScope(); } diff --git a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/Reqnroll.Autofac.ReqnrollPlugin.csproj b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/Reqnroll.Autofac.ReqnrollPlugin.csproj index 486be9ecb..c46ff72dc 100644 --- a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/Reqnroll.Autofac.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/Reqnroll.Autofac.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@ - $(Reqnroll_Runtime_TFM) + netstandard2.0 $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) $(Reqnroll_PublicSign) @@ -11,6 +11,8 @@ true true $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb + + Reqnroll.Autofac diff --git a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/Reqnroll.Autofac.nuspec b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/Reqnroll.Autofac.nuspec index 7b6bd05d4..20aaf3f77 100644 --- a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/Reqnroll.Autofac.nuspec +++ b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/Reqnroll.Autofac.nuspec @@ -16,29 +16,17 @@ LICENSE reqnroll autofac di dependency injection - - - - - - - - - - - - - - + + diff --git a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/build/Reqnroll.Autofac.targets b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/build/Reqnroll.Autofac.targets index c1700e37d..1ff367c1d 100644 --- a/Plugins/Reqnroll.Autofac.ReqnrollPlugin/build/Reqnroll.Autofac.targets +++ b/Plugins/Reqnroll.Autofac.ReqnrollPlugin/build/Reqnroll.Autofac.targets @@ -1,8 +1,7 @@ - <_Reqnroll_AutofacPluginFramework Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' ">netstandard2.0 - <_Reqnroll_AutofacPluginFramework Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">net462 + <_Reqnroll_AutofacPluginFramework>netstandard2.0 <_Reqnroll_AutofacPluginPath>$(MSBuildThisFileDirectory)\..\lib\$(_Reqnroll_AutofacPluginFramework)\Reqnroll.Autofac.ReqnrollPlugin.dll diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/AssemblyHooks.cs b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/AssemblyHooks.cs similarity index 92% rename from Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/AssemblyHooks.cs rename to Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/AssemblyHooks.cs index f3878e27f..6e3f1895a 100644 --- a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/AssemblyHooks.cs +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/AssemblyHooks.cs @@ -1,8 +1,7 @@ using System.Threading.Tasks; using NUnit.Framework; -using Reqnroll; -namespace Specs +namespace Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest { // This class is only required because in this sample application the generator // is not loaded from NuGet package. In a usual Reqnroll project it is not needed. diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Features/ExternalDataFromCSV.feature b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataFromCSV.feature similarity index 100% rename from Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Features/ExternalDataFromCSV.feature rename to Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataFromCSV.feature diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Features/ExternalDataFromExcel-HU.feature b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataFromExcel-HU.feature similarity index 100% rename from Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Features/ExternalDataFromExcel-HU.feature rename to Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataFromExcel-HU.feature diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Features/ExternalDataFromExcel.feature b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataFromExcel.feature similarity index 100% rename from Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Features/ExternalDataFromExcel.feature rename to Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataFromExcel.feature diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataFromJson.feature b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataFromJson.feature new file mode 100644 index 000000000..2c67ec647 --- /dev/null +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/ExternalDataFromJson.feature @@ -0,0 +1,38 @@ +Feature: External Data from Json file + +@DataSource:products.json +Scenario: The basket price is calculated correctly + The scenario will be treated as a scenario outline with the examples from the json file. + The first object array is used by default from the json file. + Given the price of is € + And the customer has put 1 pcs of to the basket + When the basket price is calculated + Then the basket price should be € + +@DataSource:products.json @DataSet:other_products +Scenario: The basket price is calculated correctly for other products + The scenario will be treated as a scenario outline with the examples from the json file. + The "other_products" object array is used from the json file. + Given the price of is € + And the customer has put 1 pcs of to the basket + When the basket price is calculated + Then the basket price should be € + + +@DataSource:products-nested-dataset.json +Scenario: The basket price is calculated correctly for products in nested products json + The scenario will be treated as a scenario outline with the examples from the json file. + The first object array is used by default from the json file. + Given the price of is € + And the customer has put 1 pcs of to the basket + When the basket price is calculated + Then the basket price should be € + +@DataSource:products-nested-dataset.json @DataSet:products.varieties +Scenario: The basket price is calculated correctly for products.varieties in nested products json + The scenario will be treated as a scenario outline with the examples from the json file. + The products.varieties is used from the json file. + Given the price of is € + And the customer has put 1 pcs of to the basket + When the basket price is calculated + Then the basket price should be € diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/products-nested-dataset.json b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/products-nested-dataset.json new file mode 100644 index 000000000..79afec351 --- /dev/null +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/products-nested-dataset.json @@ -0,0 +1,53 @@ +{ + "products": + [ + { + "product": "Chocolate", + "color": "brown", + "total price": "3.15", + "varieties": + [ + { + "name": "Dark Chocolate", + "price": "1.6" + }, + { + "name": "Milk Chocolate", + "price": "1.55" + } + ] + }, + { + "product": "Apple", + "color": "red", + "total price": "2.5", + "varieties": + [ + { + "name": "Pink Lady", + "price": "1.0" + }, + { + "name": "Fuji", + "price": "1.5" + } + ] + }, + { + "product": "Orange", + "color": "orange", + "total price": "2.9", + "varieties": + [ + { + "name": "Seville Orange", + "price": "1.3" + }, + { + "name": "Tangerine", + "price": "1.6" + } + ] + } + ] +} diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SampleFiles/products.csv b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/products.csv similarity index 100% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SampleFiles/products.csv rename to Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/products.csv diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/products.json b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/products.json new file mode 100644 index 000000000..7abf4b3f2 --- /dev/null +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/products.json @@ -0,0 +1,33 @@ +{ + "products": + [ + { + "product": "Chocolate", + "price": "2.5", + "color": "brown" + }, + { + "product": "Apple", + "price": "1.0", + "color": "red" + }, + { + "product": "Orange", + "price": "1.2", + "color": "orange" + } + ], + "other products": + [ + { + "product": "Cookie", + "price": "2.5", + "color": "brown" + }, + { + "product": "Bananas", + "price": "1.0", + "color": "yellow" + } + ] +} diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Features/products.xlsx b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/products.xlsx similarity index 100% rename from Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Features/products.xlsx rename to Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Features/products.xlsx diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest.csproj b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest.csproj new file mode 100644 index 000000000..abf210a9a --- /dev/null +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest.csproj @@ -0,0 +1,56 @@ + + + + + + + net8.0 + + XReqnroll.ExternalData.ReqnrollPlugin.IntegrationTest + + + + + + + + + + + + + + + + + + <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' == 'Core'">$(Reqnroll_Core_Tools_TFM) + <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' != 'Core'">$(Reqnroll_FullFramework_Tools_TFM) + + + + + + + + + + + + + <_Reqnroll_TaskAssembly>..\..\Reqnroll.Tools.MsBuild.Generation\bin\$(Configuration)\$(_Reqnroll_Needed_MSBuildGenerator)\tasks\Reqnroll.Tools.MsBuild.Generation.dll + + + + + + + PreBuild; + $(BuildDependsOn) + + + PreBuild; + $(RebuildDependsOn) + + + \ No newline at end of file diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Specs.sln b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Specs.sln similarity index 100% rename from Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Specs.sln rename to Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/Specs.sln diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/StepDefinitions/PricingStepDefinitions.cs b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/StepDefinitions/PricingStepDefinitions.cs similarity index 97% rename from Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/StepDefinitions/PricingStepDefinitions.cs rename to Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/StepDefinitions/PricingStepDefinitions.cs index e2e0a0215..928c9b061 100644 --- a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/StepDefinitions/PricingStepDefinitions.cs +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/StepDefinitions/PricingStepDefinitions.cs @@ -2,9 +2,8 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using Reqnroll; -namespace Specs.StepDefinitions +namespace Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest.StepDefinitions { [Binding] public class PricingStepDefinitions diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/testdata.json b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/testdata.json similarity index 100% rename from Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/testdata.json rename to Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest/testdata.json diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests.csproj b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests.csproj deleted file mode 100644 index 8d9941699..000000000 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests.csproj +++ /dev/null @@ -1,47 +0,0 @@ - - - - $(Reqnroll_Test_TFM) - $(Reqnroll_KeyFile) - $(Reqnroll_EnableStrongNameSigning) - $(Reqnroll_PublicSign) - false - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - PreserveNewest - - - - - diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/ExternalDataGeneratorPlugin.cs b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/ExternalDataGeneratorPlugin.cs index d22470efc..a39da3c18 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/ExternalDataGeneratorPlugin.cs +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/ExternalDataGeneratorPlugin.cs @@ -23,6 +23,7 @@ public void Initialize(GeneratorPluginEvents generatorPluginEvents, GeneratorPlu args.ObjectContainer.RegisterTypeAs(); args.ObjectContainer.RegisterTypeAs("CSV"); args.ObjectContainer.RegisterTypeAs("Excel"); + args.ObjectContainer.RegisterTypeAs("JSON"); }; } } diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Loaders/JsonDataTableGenerator.cs b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Loaders/JsonDataTableGenerator.cs new file mode 100644 index 000000000..e3baed6a4 --- /dev/null +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Loaders/JsonDataTableGenerator.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using System.Linq; +using Newtonsoft.Json.Linq; +using Reqnroll.ExternalData.ReqnrollPlugin.DataSources; + +namespace Reqnroll.ExternalData.ReqnrollPlugin.Loaders +{ + + public class JsonDataTableGenerator + { + public DataSources.DataTable FlattenDataSetToDataTable(JObject originalJson, string[] objectPath) + { + var header = GenerateHeader(originalJson, objectPath); + var dataTable = new DataSources.DataTable(header.ToArray()); + var records = GenerateRecordsFromNestedObjects(originalJson, objectPath, new List()); + foreach (var record in records) dataTable.Items.Add(record); + + return dataTable; + } + + private static List GenerateHeader(JObject originalJson, string[] objectPath) + { + var currentObject = originalJson; + + List header = new(); + while (objectPath.Length > 0) + { + var currentObjectArray = currentObject[objectPath.First()]?.ToObject(); + + if (currentObjectArray == null) + throw new ExternalDataPluginException($"Expected {objectPath.First()} to be in json object"); + + objectPath = objectPath.Skip(1).ToArray(); + + if (currentObjectArray.First == null) + throw new ExternalDataPluginException("Empty object arrays are not supported"); + + currentObject = currentObjectArray.First.ToObject(); + header.AddRange(GetPropertiesExcludingToBeFlattenedArrayObject(currentObject, objectPath).Select(p => p.Name)); + } + + return header; + } + + private List GenerateRecordsFromNestedObjects( + JObject currentObject, + string[] dataSetObjectPath, + List currentRecords) + { + if (dataSetObjectPath.Length == 0) + return currentRecords; + + var objectArrayPropertyName = dataSetObjectPath.First(); + var remainingObjectArrays = dataSetObjectPath.Skip(1).ToArray(); + + var objectArray = currentObject[objectArrayPropertyName]?.ToObject(); + if (objectArray == null) + throw new ExternalDataPluginException($"Expected object array property {objectArrayPropertyName} inside {currentObject}"); + + var result = new List(); + foreach (var jObject in objectArray.Select(i => i.ToObject())) + { + + var objectProperties = GetPropertiesExcludingToBeFlattenedArrayObject(jObject, remainingObjectArrays); + + var objectRecord = new DataRecord(); + foreach (var property in objectProperties) + objectRecord.Fields[property.Name] = new DataValue(property.Value); + + List intermediateResult; + if (currentRecords.Any()) + intermediateResult = AppendFieldsToCurrentDataRecords(currentRecords, objectRecord); + else + intermediateResult = new List { objectRecord }; + + result.AddRange(GenerateRecordsFromNestedObjects(jObject, remainingObjectArrays, intermediateResult)); + } + + return result; + } + + private static List AppendFieldsToCurrentDataRecords(List currentRecords, DataRecord objectRecord) + { + var result = new List(); + foreach (var record in currentRecords) + { + var updatedRecord = new DataRecord(record.Fields); + objectRecord.Fields.ToList().ForEach(x => updatedRecord.Fields.Add(x.Key, x.Value)); + result.Add(updatedRecord); + } + + return result; + } + + private static List GetPropertiesExcludingToBeFlattenedArrayObject( + JObject childJson, + string[] remainingDataSets) + { + return childJson.Properties().Where(p => p.Name != remainingDataSets.FirstOrDefault()).ToList(); + } + } +} \ No newline at end of file diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Loaders/JsonLoader.cs b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Loaders/JsonLoader.cs new file mode 100644 index 000000000..023b51e21 --- /dev/null +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Loaders/JsonLoader.cs @@ -0,0 +1,81 @@ +using System.Linq; +using System.Collections.Generic; +using Newtonsoft.Json.Linq; +using Reqnroll.ExternalData.ReqnrollPlugin.DataSources; +using Newtonsoft.Json; + +namespace Reqnroll.ExternalData.ReqnrollPlugin.Loaders +{ + public class JsonLoader : FileBasedLoader + { + public JsonLoader() : base("Json", ".json") + { + + } + + protected override DataSource LoadDataSourceFromFilePath(string filePath, string sourceFilePath) + { + var fileContent = ReadTextFileContent(filePath); + + return LoadJsonDataSource(fileContent, sourceFilePath); + } + + private List DetermineDataSets(JObject jsonObject, string dataSetPrepend) + { + var dataSets = new List(); + foreach (var array in GetArraysFromObject(jsonObject)) + { + var firstArrayEntry = array.Children().FirstOrDefault(); + + if (firstArrayEntry == null || firstArrayEntry.Type != JTokenType.Object) + continue; + + var firstArrayObject = firstArrayEntry.ToObject(); + + var dataSetPath = string.IsNullOrWhiteSpace(dataSetPrepend) ? ((JProperty)array.Parent!).Name : $"{dataSetPrepend}.{array.Path}"; + + dataSets.Add(dataSetPath); + + foreach (var nestedDataSetPath in DetermineDataSets(firstArrayObject, dataSetPath)) + dataSets.Add(nestedDataSetPath); + } + + return dataSets; + } + + private static IEnumerable GetArraysFromObject(JObject jsonObject) + { + return jsonObject.Properties().Where(p => p.Value.Type == JTokenType.Array).Select(a => a.Value); + } + + private DataSource LoadJsonDataSource(string fileContent, string sourceFilePath) + { + JObject fileJson = ParseJson(fileContent, sourceFilePath); + var dataSets = DetermineDataSets(fileJson, ""); + var dataSetsRecord = new DataRecord(); + var jsonDataTableGenerator = new JsonDataTableGenerator(); + foreach (var dataSetPath in dataSets) + { + var dataTable = jsonDataTableGenerator.FlattenDataSetToDataTable(fileJson, dataSetPath.Split('.')); + dataSetsRecord.Fields[dataSetPath] = new DataValue(dataTable); + } + + return new DataSource(dataSetsRecord, dataSets.First()); + } + + private static JObject ParseJson(string fileContent, string sourceFilePath) + { + JObject fileJson; + try + { + fileJson = JObject.Parse(fileContent); + } + catch (JsonReaderException jsonReaderException) + { + throw new ExternalDataPluginException($"Failed to parse json file {sourceFilePath}", jsonReaderException); + } + + return fileJson; + } + } +} \ No newline at end of file diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Reqnroll.ExternalData.ReqnrollPlugin.csproj b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Reqnroll.ExternalData.ReqnrollPlugin.csproj index 11c077ab3..37ba8799a 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Reqnroll.ExternalData.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Reqnroll.ExternalData.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@ - $(Reqnroll_Generator_TFM) + netstandard2.0 $(MSBuildThisFileDirectory)Reqnroll.ExternalData.nuspec $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) @@ -15,6 +15,7 @@ + diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Reqnroll.ExternalData.nuspec b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Reqnroll.ExternalData.nuspec index ba9370439..51ee2bd2b 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Reqnroll.ExternalData.nuspec +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/Reqnroll.ExternalData.nuspec @@ -21,9 +21,7 @@ - - - + diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/build/Reqnroll.ExternalData.targets b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/build/Reqnroll.ExternalData.targets index e2525b081..887d91c64 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/build/Reqnroll.ExternalData.targets +++ b/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin/build/Reqnroll.ExternalData.targets @@ -1,9 +1,7 @@ - <_ExternalDataGeneratorPluginFramework Condition=" '$(MSBuildRuntimeType)' == 'Core'">netcoreapp3.1 - <_ExternalDataGeneratorPluginFramework Condition=" '$(MSBuildRuntimeType)' != 'Core'">net462 + <_ExternalDataGeneratorPluginFramework>netstandard2.0 <_ExternalDataGeneratorPluginPath>$(MSBuildThisFileDirectory)$(_ExternalDataGeneratorPluginFramework)\Reqnroll.ExternalData.ReqnrollPlugin.dll - \ No newline at end of file diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Specs.csproj b/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Specs.csproj deleted file mode 100644 index b51b3fb20..000000000 --- a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Specs.csproj +++ /dev/null @@ -1,54 +0,0 @@ - - - - - - - net6.0 - - - - - - - - - - - - - - - - - - <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' == 'Core'">$(Reqnroll_Core_Generator_TFM) - <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' != 'Core'">$(Reqnroll_FullFramework_Generator_TFM) - - - - - - - - - - - - - <_Reqnroll_TaskAssembly>..\..\Reqnroll.Tools.MsBuild.Generation\bin\$(Configuration)\$(_Reqnroll_Needed_MSBuildGenerator)\Reqnroll.Tools.MsBuild.Generation.dll - - - - - - - PreBuild; - $(BuildDependsOn) - - - PreBuild; - $(RebuildDependsOn) - - - \ No newline at end of file diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Steps/CommentBindings.cs b/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Steps/CommentBindings.cs deleted file mode 100644 index 4a28b5f11..000000000 --- a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Steps/CommentBindings.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Reqnroll; -using Reqnroll.Infrastructure; - -namespace Specs.Steps -{ - [Binding] - public class CommentBindings - { - private readonly ScenarioContext _scenarioContext; - - public CommentBindings(ScenarioContext scenarioContext) - { - _scenarioContext = scenarioContext; - } - - [Given(@"a comment with text (.*)")] - public void GivenACommentWithText(string text) - { - } - - [Given(@"the screen height of (.*)px")] - public void GivenTheScreenHeightOfPx(int height) - { - } - - [When(@"the comment box is shown")] - public void WhenTheCommentBoxIsShown() - { - } - - [Then(@"the the comment size should not exceed (.*) lines")] - public void ThenTheTheCommentSizeShouldNotExceedLines(int lines) - { - } - } -} \ No newline at end of file diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Steps/RegistrationBindings.cs b/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Steps/RegistrationBindings.cs deleted file mode 100644 index 90eb31be6..000000000 --- a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Steps/RegistrationBindings.cs +++ /dev/null @@ -1,57 +0,0 @@ -using NUnit.Framework; -using Reqnroll; - -namespace Specs.Steps -{ - [Binding] - public class RegistrationBindings - { - private readonly ScenarioContext _scenarioContext; - - private string _name; - private string _email; - private string _errorMessage = ""; - - public RegistrationBindings(ScenarioContext scenarioContext) - { - _scenarioContext = scenarioContext; - } - [Given(@"a visitor registering as ""(.*)"" with email (.*)")] - public void GivenAVisitorRegisteringAsWithEmail(string name, string email) - { - this._email = email; - this._name = name; - } - - [When(@"the registration completes")] - public void WhenTheRegistrationCompletes() - { - //Simulate some email validation - if (!_email.Contains("@")) - { - _email = null; - _errorMessage = "Invalid Email"; - } - } - - [Then(@"the account system should record (.*) related to user ""(.*)""")] - public void ThenTheAccountSystemShouldRecordEmailToUser(string email, string name) - { - Assert.AreEqual(name, _name); - Assert.AreEqual(email, _email); - } - - [Then(@"the account system should not record (.*)")] - public void ThenTheAccountSystemShouldNotRecord(string p0) - { - Assert.IsNull(_email); - } - - [Then(@"the error response should be ""(.*)""")] - public void ThenTheErrorResponseShouldBe(string expectedErrorMessage) - { - Assert.AreEqual(expectedErrorMessage, _errorMessage); - } - - } -} \ No newline at end of file diff --git a/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/Reqnroll.MSTest.Generator.ReqnrollPlugin.csproj b/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/Reqnroll.MSTest.Generator.ReqnrollPlugin.csproj index 8b415f5d0..216d841d0 100644 --- a/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/Reqnroll.MSTest.Generator.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/Reqnroll.MSTest.Generator.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@ - $(Reqnroll_Generator_TFM) + netstandard2.0 $(MSBuildThisFileDirectory)Reqnroll.MSTest.nuspec $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) diff --git a/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/Reqnroll.MSTest.nuspec b/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/Reqnroll.MSTest.nuspec index 63360359a..accdf6f86 100644 --- a/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/Reqnroll.MSTest.nuspec +++ b/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/Reqnroll.MSTest.nuspec @@ -16,35 +16,20 @@ reqnroll mstest $copyright$ - - - - - - - - - - - + - - - - + - - - - + + diff --git a/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/build/MSTest.AssemblyHooks.template.cs b/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/build/MSTest.AssemblyHooks.template.cs index c67a9d7e7..93076d137 100644 --- a/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/build/MSTest.AssemblyHooks.template.cs +++ b/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/build/MSTest.AssemblyHooks.template.cs @@ -20,7 +20,15 @@ public static async Task AssemblyInitializeAsync(TestContext testContext) var currentAssembly = typeof(PROJECT_ROOT_NAMESPACE_MSTestAssemblyHooks).Assembly; var containerBuilder = new MsTestContainerBuilder(testContext); - await global::Reqnroll.TestRunnerManager.OnTestRunStartAsync(currentAssembly, containerBuilder: containerBuilder); + try + { + await global::Reqnroll.TestRunnerManager.OnTestRunStartAsync(currentAssembly, containerBuilder: containerBuilder); + } + catch (System.Exception ex) + { + // wrap the exception because MsTest swallows the outer exception + throw new System.AggregateException(ex); + } } [AssemblyCleanup] diff --git a/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/build/Reqnroll.MsTest.targets b/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/build/Reqnroll.MsTest.targets index b0e411f8c..7be4cb065 100644 --- a/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/build/Reqnroll.MsTest.targets +++ b/Plugins/Reqnroll.MSTest.Generator.ReqnrollPlugin/build/Reqnroll.MsTest.targets @@ -17,12 +17,10 @@ - <_Reqnroll_MsTestGeneratorPlugin Condition=" '$(MSBuildRuntimeType)' == 'Core'">netcoreapp3.1 - <_Reqnroll_MsTestGeneratorPlugin Condition=" '$(MSBuildRuntimeType)' != 'Core'">net462 + <_Reqnroll_MsTestGeneratorPlugin>netstandard2.0 <_Reqnroll_MsTestGeneratorPluginPath>$(MSBuildThisFileDirectory)\$(_Reqnroll_MsTestGeneratorPlugin)\Reqnroll.MSTest.Generator.ReqnrollPlugin.dll - <_Reqnroll_MsTestRuntimePlugin Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' ">netstandard2.0 - <_Reqnroll_MsTestRuntimePlugin Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">net462 + <_Reqnroll_MsTestRuntimePlugin>netstandard2.0 <_Reqnroll_MsTestRuntimePluginPath>$(MSBuildThisFileDirectory)\..\lib\$(_Reqnroll_MsTestRuntimePlugin)\Reqnroll.MSTest.ReqnrollPlugin.dll $(MSBuildThisFileDirectory)MSTest.AssemblyHooks$(DefaultLanguageSourceExtension) diff --git a/Plugins/Reqnroll.MSTest.ReqnrollPlugin/MsTestRuntimeProvider.cs b/Plugins/Reqnroll.MSTest.ReqnrollPlugin/MsTestRuntimeProvider.cs index ee8be201d..b6db73f7d 100644 --- a/Plugins/Reqnroll.MSTest.ReqnrollPlugin/MsTestRuntimeProvider.cs +++ b/Plugins/Reqnroll.MSTest.ReqnrollPlugin/MsTestRuntimeProvider.cs @@ -19,7 +19,5 @@ public void TestIgnore(string message) { TestInconclusive(message); // there is no dynamic "Ignore" in mstest } - - public bool DelayedFixtureTearDown => true; } } \ No newline at end of file diff --git a/Plugins/Reqnroll.MSTest.ReqnrollPlugin/Reqnroll.MSTest.ReqnrollPlugin.csproj b/Plugins/Reqnroll.MSTest.ReqnrollPlugin/Reqnroll.MSTest.ReqnrollPlugin.csproj index 1de872a2c..928684ecc 100644 --- a/Plugins/Reqnroll.MSTest.ReqnrollPlugin/Reqnroll.MSTest.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.MSTest.ReqnrollPlugin/Reqnroll.MSTest.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@ - $(Reqnroll_Runtime_TFM) + netstandard2.0 $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) $(Reqnroll_PublicSign) @@ -15,7 +15,6 @@ all runtime; build; native; contentfiles; analyzers - diff --git a/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/Reqnroll.NUnit.Generator.ReqnrollPlugin.csproj b/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/Reqnroll.NUnit.Generator.ReqnrollPlugin.csproj index 5cb39fec0..243b1dec7 100644 --- a/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/Reqnroll.NUnit.Generator.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/Reqnroll.NUnit.Generator.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@ - $(Reqnroll_Generator_TFM) + netstandard2.0 $(MSBuildThisFileDirectory)Reqnroll.NUnit.nuspec $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) diff --git a/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/Reqnroll.NUnit.nuspec b/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/Reqnroll.NUnit.nuspec index 5618ab73b..dc12ee964 100644 --- a/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/Reqnroll.NUnit.nuspec +++ b/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/Reqnroll.NUnit.nuspec @@ -16,35 +16,20 @@ reqnroll nunit $copyright$ - - - - - - - - - - - - - - + - - - - + + diff --git a/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/build/Reqnroll.NUnit.targets b/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/build/Reqnroll.NUnit.targets index c9273cf1d..30c4f4456 100644 --- a/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/build/Reqnroll.NUnit.targets +++ b/Plugins/Reqnroll.NUnit.Generator.ReqnrollPlugin/build/Reqnroll.NUnit.targets @@ -17,12 +17,10 @@ - <_Reqnroll_NUnitGeneratorPlugin Condition=" '$(MSBuildRuntimeType)' == 'Core'">netcoreapp3.1 - <_Reqnroll_NUnitGeneratorPlugin Condition=" '$(MSBuildRuntimeType)' != 'Core'">net462 + <_Reqnroll_NUnitGeneratorPlugin>netstandard2.0 <_Reqnroll_NUnitGeneratorPluginPath>$(MSBuildThisFileDirectory)\$(_Reqnroll_NUnitGeneratorPlugin)\Reqnroll.NUnit.Generator.ReqnrollPlugin.dll - <_Reqnroll_NUnitRuntimePlugin Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' ">netstandard2.0 - <_Reqnroll_NUnitRuntimePlugin Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">net462 + <_Reqnroll_NUnitRuntimePlugin>netstandard2.0 <_Reqnroll_NUnitRuntimePluginPath>$(MSBuildThisFileDirectory)\..\lib\$(_Reqnroll_NUnitRuntimePlugin)\Reqnroll.NUnit.ReqnrollPlugin.dll $(MSBuildThisFileDirectory)NUnit.AssemblyHooks$(DefaultLanguageSourceExtension) diff --git a/Plugins/Reqnroll.NUnit.ReqnrollPlugin/NUnitRuntimeProvider.cs b/Plugins/Reqnroll.NUnit.ReqnrollPlugin/NUnitRuntimeProvider.cs index 081fe7439..a54df615e 100644 --- a/Plugins/Reqnroll.NUnit.ReqnrollPlugin/NUnitRuntimeProvider.cs +++ b/Plugins/Reqnroll.NUnit.ReqnrollPlugin/NUnitRuntimeProvider.cs @@ -19,7 +19,5 @@ public void TestIgnore(string message) { Assert.Ignore(message); } - - public bool DelayedFixtureTearDown => false; } } \ No newline at end of file diff --git a/Plugins/Reqnroll.NUnit.ReqnrollPlugin/Reqnroll.NUnit.ReqnrollPlugin.csproj b/Plugins/Reqnroll.NUnit.ReqnrollPlugin/Reqnroll.NUnit.ReqnrollPlugin.csproj index 6842cfb26..9eb93ad40 100644 --- a/Plugins/Reqnroll.NUnit.ReqnrollPlugin/Reqnroll.NUnit.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.NUnit.ReqnrollPlugin/Reqnroll.NUnit.ReqnrollPlugin.csproj @@ -1,9 +1,6 @@ - $(Reqnroll_Runtime_TFM) - $(Reqnroll_KeyFile) - $(Reqnroll_EnableStrongNameSigning) - $(Reqnroll_PublicSign) + netstandard2.0 $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) $(Reqnroll_PublicSign) @@ -18,7 +15,6 @@ all runtime; build; native; contentfiles; analyzers - diff --git a/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/GeneratorPlugin.cs b/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/GeneratorPlugin.cs index 4887e2234..b724beca8 100644 --- a/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/GeneratorPlugin.cs +++ b/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/GeneratorPlugin.cs @@ -13,7 +13,6 @@ public class GeneratorPlugin : IGeneratorPlugin { public void Initialize(GeneratorPluginEvents generatorPluginEvents, GeneratorPluginParameters generatorPluginParameters, UnitTestProviderConfiguration unitTestProviderConfiguration) { -#if NETFRAMEWORK generatorPluginEvents.ConfigurationDefaults += (_, args) => { var configuration = args.ReqnrollProjectConfiguration.ReqnrollConfiguration; @@ -24,6 +23,5 @@ public void Initialize(GeneratorPluginEvents generatorPluginEvents, GeneratorPlu loader.UpdateFromAppConfig(configuration, configSection); } }; -#endif } } \ No newline at end of file diff --git a/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin.csproj b/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin.csproj index 440c2abbd..3d871baa8 100644 --- a/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@  - $(Reqnroll_Generator_TFM) + netstandard2.0;net462 $(MSBuildThisFileDirectory)Reqnroll.SpecFlowCompatibility.nuspec $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) @@ -17,22 +17,12 @@ all runtime; build; native; contentfiles; analyzers - - - false - - - - - - - AppConfig\%(RecursiveDir)%(Filename)%(Extension) - + diff --git a/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.nuspec b/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.nuspec index d6b91b9bc..ee051fbd3 100644 --- a/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.nuspec +++ b/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.nuspec @@ -22,23 +22,17 @@ - - - - - - - + + + - - - - + + diff --git a/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/build/Reqnroll.SpecFlowCompatibility.targets b/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/build/Reqnroll.SpecFlowCompatibility.targets index 76b58aad8..77cfcf43e 100644 --- a/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/build/Reqnroll.SpecFlowCompatibility.targets +++ b/Plugins/Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin/build/Reqnroll.SpecFlowCompatibility.targets @@ -2,7 +2,7 @@ - <_Reqnroll_SpecFlowCompatibilityGeneratorPlugin Condition=" '$(MSBuildRuntimeType)' == 'Core'">netcoreapp3.1 + <_Reqnroll_SpecFlowCompatibilityGeneratorPlugin Condition=" '$(MSBuildRuntimeType)' == 'Core'">netstandard2.0 <_Reqnroll_SpecFlowCompatibilityGeneratorPlugin Condition=" '$(MSBuildRuntimeType)' != 'Core'">net462 <_Reqnroll_SpecFlowCompatibilityGeneratorPluginPath>$(MSBuildThisFileDirectory)\$(_Reqnroll_SpecFlowCompatibilityGeneratorPlugin)\Reqnroll.SpecFlowCompatibility.Generator.ReqnrollPlugin.dll diff --git a/Plugins/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin.csproj b/Plugins/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin.csproj index 45e89d292..f36633d45 100644 --- a/Plugins/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin/Reqnroll.SpecFlowCompatibility.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@  - $(Reqnroll_Runtime_TFM) + netstandard2.0;net462 $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) $(Reqnroll_PublicSign) @@ -18,15 +18,11 @@ - - - - - + - - + + \ 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 8977b5fcb..fb0f900c3 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 @@ -1,10 +1,10 @@ - + - net6.0 + net8.0 true @@ -30,28 +30,30 @@ - <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' == 'Core'">$(Reqnroll_Core_Generator_TFM) - <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' != 'Core'">$(Reqnroll_FullFramework_Generator_TFM) + <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' == 'Core'">$(Reqnroll_Core_Tools_TFM) + <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' != 'Core'">$(Reqnroll_FullFramework_Tools_TFM) + + <_VerifyGeneratorPluginFramework Condition=" '$(MSBuildRuntimeType)' == 'Core'">netstandard2.0 + <_VerifyGeneratorPluginFramework Condition=" '$(MSBuildRuntimeType)' != 'Core'">net462 - - + + - - + + - - <_Reqnroll_TaskAssembly>..\..\Reqnroll.Tools.MsBuild.Generation\bin\$(Configuration)\$(_Reqnroll_Needed_MSBuildGenerator)\Reqnroll.Tools.MsBuild.Generation.dll + <_Reqnroll_TaskAssembly>..\..\Reqnroll.Tools.MsBuild.Generation\bin\$(Configuration)\$(_Reqnroll_Needed_MSBuildGenerator)\tasks\Reqnroll.Tools.MsBuild.Generation.dll diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Steps/Steps.cs b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/StepDefinitions/StepDefinitions.cs similarity index 79% rename from Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Steps/Steps.cs rename to Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/StepDefinitions/StepDefinitions.cs index 492023486..f5f56e96e 100644 --- a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/Steps/Steps.cs +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin.IntegrationTest/StepDefinitions/StepDefinitions.cs @@ -1,9 +1,7 @@ -using Reqnroll; - -namespace Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.Steps; +namespace Reqnroll.Verify.ReqnrollPlugin.IntegrationTest.StepDefinitions; [Binding] -internal class Steps +internal class StepDefinitions { [When("I try Verify with Reqnroll")] public async Task ITryVerifyWithReqnroll() diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/Reqnroll.Verify.ReqnrollPlugin.csproj b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/Reqnroll.Verify.ReqnrollPlugin.csproj index 58723e760..a6aec9b9e 100644 --- a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/Reqnroll.Verify.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/Reqnroll.Verify.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@ - + - $(Reqnroll_Generator_TFM) + net462;netstandard2.0 $(MSBuildThisFileDirectory)Reqnroll.Verify.nuspec $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/Reqnroll.Verify.nuspec b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/Reqnroll.Verify.nuspec index bf72de566..fb893ba03 100644 --- a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/Reqnroll.Verify.nuspec +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/Reqnroll.Verify.nuspec @@ -20,11 +20,7 @@ - - - - - + @@ -35,11 +31,10 @@ - + - - + diff --git a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/build/Reqnroll.Verify.targets b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/build/Reqnroll.Verify.targets index ebf38b7c2..1b5945aaf 100644 --- a/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/build/Reqnroll.Verify.targets +++ b/Plugins/Reqnroll.Verify/Reqnroll.Verify.ReqnrollPlugin/build/Reqnroll.Verify.targets @@ -1,9 +1,9 @@ - <_VerifyGeneratorPluginFramework Condition=" '$(MSBuildRuntimeType)' == 'Core'">netcoreapp2.1 + <_VerifyGeneratorPluginFramework Condition=" '$(MSBuildRuntimeType)' == 'Core'">netstandard2.0 <_VerifyGeneratorPluginFramework Condition=" '$(MSBuildRuntimeType)' != 'Core'">net462 <_VerifyGeneratorPluginPath>$(MSBuildThisFileDirectory)$(_VerifyGeneratorPluginFramework)\Reqnroll.Verify.ReqnrollPlugin.dll - \ No newline at end of file + diff --git a/Plugins/Reqnroll.Windsor.ReqnrollPlugin/Reqnroll.Windsor.ReqnrollPlugin.csproj b/Plugins/Reqnroll.Windsor.ReqnrollPlugin/Reqnroll.Windsor.ReqnrollPlugin.csproj index 9ce8dfaf1..b9e35ec99 100644 --- a/Plugins/Reqnroll.Windsor.ReqnrollPlugin/Reqnroll.Windsor.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.Windsor.ReqnrollPlugin/Reqnroll.Windsor.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@ - $(Reqnroll_Runtime_TFM) + netstandard2.0 $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) $(Reqnroll_PublicSign) diff --git a/Plugins/Reqnroll.Windsor.ReqnrollPlugin/Reqnroll.Windsor.nuspec b/Plugins/Reqnroll.Windsor.ReqnrollPlugin/Reqnroll.Windsor.nuspec index feb4de67a..bc9501d40 100644 --- a/Plugins/Reqnroll.Windsor.ReqnrollPlugin/Reqnroll.Windsor.nuspec +++ b/Plugins/Reqnroll.Windsor.ReqnrollPlugin/Reqnroll.Windsor.nuspec @@ -16,28 +16,17 @@ LICENSE reqnroll castle windsor di dependency injection - - - - - - - - - - - - - + + diff --git a/Plugins/Reqnroll.Windsor.ReqnrollPlugin/build/Reqnroll.Windsor.targets b/Plugins/Reqnroll.Windsor.ReqnrollPlugin/build/Reqnroll.Windsor.targets index 47de52b31..3f351247c 100644 --- a/Plugins/Reqnroll.Windsor.ReqnrollPlugin/build/Reqnroll.Windsor.targets +++ b/Plugins/Reqnroll.Windsor.ReqnrollPlugin/build/Reqnroll.Windsor.targets @@ -1,8 +1,6 @@ - - <_Reqnroll_WindsorPluginFramework Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' ">netstandard2.0 - <_Reqnroll_WindsorPluginFramework Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">net462 + <_Reqnroll_WindsorPluginFramework>netstandard2.0 <_Reqnroll_WindsorPluginPath>$(MSBuildThisFileDirectory)\..\lib\$(_Reqnroll_WindsorPluginFramework)\Reqnroll.Windsor.ReqnrollPlugin.dll diff --git a/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/Reqnroll.xUnit.Generator.ReqnrollPlugin.csproj b/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/Reqnroll.xUnit.Generator.ReqnrollPlugin.csproj index 66e6500e1..8a0e69ddb 100644 --- a/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/Reqnroll.xUnit.Generator.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/Reqnroll.xUnit.Generator.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@ - $(Reqnroll_Generator_TFM) + netstandard2.0 $(MSBuildThisFileDirectory)Reqnroll.xUnit.nuspec $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) diff --git a/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/Reqnroll.xUnit.nuspec b/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/Reqnroll.xUnit.nuspec index 421d0d21e..1aa3e7975 100644 --- a/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/Reqnroll.xUnit.nuspec +++ b/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/Reqnroll.xUnit.nuspec @@ -16,39 +16,21 @@ reqnroll xUnit $copyright$ - - - - - - - - - - - - - - - - - + - - - - + + diff --git a/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/build/Reqnroll.xUnit.targets b/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/build/Reqnroll.xUnit.targets index 0a47457ae..60ad81ef6 100644 --- a/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/build/Reqnroll.xUnit.targets +++ b/Plugins/Reqnroll.xUnit.Generator.ReqnrollPlugin/build/Reqnroll.xUnit.targets @@ -16,12 +16,10 @@ - <_Reqnroll_xUnitGeneratorPlugin Condition=" '$(MSBuildRuntimeType)' == 'Core'" >netcoreapp3.1 - <_Reqnroll_xUnitGeneratorPlugin Condition=" '$(MSBuildRuntimeType)' != 'Core'" >net462 + <_Reqnroll_xUnitGeneratorPlugin>netstandard2.0 <_Reqnroll_xUnitGeneratorPluginPath>$(MSBuildThisFileDirectory)\$(_Reqnroll_xUnitGeneratorPlugin)\Reqnroll.xUnit.Generator.ReqnrollPlugin.dll - <_Reqnroll_xUnitRuntimePlugin Condition=" '$(TargetFrameworkIdentifier)' == '.NETCoreApp' ">netstandard2.0 - <_Reqnroll_xUnitRuntimePlugin Condition=" '$(TargetFrameworkIdentifier)' == '.NETFramework' ">net462 + <_Reqnroll_xUnitRuntimePlugin>netstandard2.0 <_Reqnroll_xUnitRuntimePluginPath>$(MSBuildThisFileDirectory)\..\lib\$(_Reqnroll_xUnitRuntimePlugin)\Reqnroll.xUnit.ReqnrollPlugin.dll $(MSBuildThisFileDirectory)xUnit.AssemblyHooks$(DefaultLanguageSourceExtension) diff --git a/Plugins/Reqnroll.xUnit.ReqnrollPlugin/Reqnroll.xUnit.ReqnrollPlugin.csproj b/Plugins/Reqnroll.xUnit.ReqnrollPlugin/Reqnroll.xUnit.ReqnrollPlugin.csproj index 0056c313d..386177533 100644 --- a/Plugins/Reqnroll.xUnit.ReqnrollPlugin/Reqnroll.xUnit.ReqnrollPlugin.csproj +++ b/Plugins/Reqnroll.xUnit.ReqnrollPlugin/Reqnroll.xUnit.ReqnrollPlugin.csproj @@ -1,6 +1,6 @@ - $(Reqnroll_Runtime_TFM) + netstandard2.0 $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) $(Reqnroll_PublicSign) @@ -15,7 +15,9 @@ all runtime; build; native; contentfiles; analyzers + + diff --git a/Plugins/Reqnroll.xUnit.ReqnrollPlugin/XUnitRuntimeProvider.cs b/Plugins/Reqnroll.xUnit.ReqnrollPlugin/XUnitRuntimeProvider.cs index c84221bb0..99efacd30 100644 --- a/Plugins/Reqnroll.xUnit.ReqnrollPlugin/XUnitRuntimeProvider.cs +++ b/Plugins/Reqnroll.xUnit.ReqnrollPlugin/XUnitRuntimeProvider.cs @@ -19,7 +19,5 @@ public void TestIgnore(string message) { Skip.If(true, message); } - - public bool DelayedFixtureTearDown => false; } } \ No newline at end of file diff --git a/README.md b/README.md index 99329e310..224de1f8c 100644 --- a/README.md +++ b/README.md @@ -17,6 +17,7 @@ Reqnroll works on all major operating systems (Windows, Linux, macOS), on all co * [Quickstart guide](https://go.reqnroll.net/quickstart) * [Reqnroll website](https://reqnroll.net/) * [Reqnroll documentation](https://docs.reqnroll.net/) +* [Migrating from SpecFlow](https://docs.reqnroll.net/latest/guides/migrating-from-specflow.html) * [Release notes](https://go.reqnroll.net/release-notes) * [IDE setup instructions for Reqnroll](https://go.reqnroll.net/doc-setup-ide) @@ -34,4 +35,4 @@ Reqnroll for VisualStudio is licensed under the [BSD 3-Clause License](LICENSE). Copyright (c) 2024 Reqnroll -This project is based on the [SpecFlow](https://github.com/SpecFlowOSS/SpecFlow) framework. \ No newline at end of file +This project is based on the [SpecFlow](https://github.com/SpecFlowOSS/SpecFlow) framework. diff --git a/Reqnroll.BoDi/BoDi.cs b/Reqnroll.BoDi/BoDi.cs deleted file mode 100644 index ba657a233..000000000 --- a/Reqnroll.BoDi/BoDi.cs +++ /dev/null @@ -1,920 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Reflection; -using System.Runtime.Serialization; -using System.Threading; - -namespace Reqnroll.BoDi -{ - [Serializable] - public class ObjectContainerException : Exception - { - public ObjectContainerException(string message, Type[] resolutionPath) : base(GetMessage(message, resolutionPath)) - { - } - - protected ObjectContainerException( - SerializationInfo info, - StreamingContext context) : base(info, context) - { - } - - static private string GetMessage(string message, Type[] resolutionPath) - { - if (resolutionPath == null || resolutionPath.Length == 0) - return message; - - return string.Format("{0} (resolution path: {1})", message, string.Join("->", resolutionPath.Select(t => t.FullName).ToArray())); - } - } - - public interface IObjectContainer : IDisposable - { - /// - /// Fired when a new object is created directly by the container. It is not invoked for resolving instance and factory registrations. - /// - event Action ObjectCreated; - - /// - /// Registers a type as the desired implementation type of an interface. - /// - /// Implementation type - /// Interface will be resolved - /// An object which allows to change resolving strategy. - /// A name to register named instance, otherwise null. - /// If there was already a resolve for the . - /// - /// Previous registrations can be overridden before the first resolution for the . - /// - IStrategyRegistration RegisterTypeAs(string name = null) where TType : class, TInterface; - - /// - /// Registers an instance - /// - /// Interface will be resolved - /// The instance implements the interface. - /// A name to register named instance, otherwise null. - /// Whether the instance should be disposed on container dispose, otherwise false. - /// If is null. - /// If there was already a resolve for the . - /// - /// Previous registrations can be overridden before the first resolution for the . - /// The instance will be registered in the object pool, so if a (for another interface) would require an instance of the dynamic type of the , the will be returned. - /// - void RegisterInstanceAs(TInterface instance, string name = null, bool dispose = false) where TInterface : class; - - /// - /// Registers an instance - /// - /// The instance implements the interface. - /// Interface will be resolved - /// A name to register named instance, otherwise null. - /// Whether the instance should be disposed on container dispose, otherwise false. - /// If is null. - /// If there was already a resolve for the . - /// - /// Previous registrations can be overridden before the first resolution for the . - /// The instance will be registered in the object pool, so if a (for another interface) would require an instance of the dynamic type of the , the will be returned. - /// - void RegisterInstanceAs(object instance, Type interfaceType, string name = null, bool dispose = false); - - /// - /// Registers an instance produced by . The delegate will be called only once and the instance it returned will be returned in each resolution. - /// - /// Interface to register as. - /// The function to run to obtain the instance. - /// A name to resolve named instance, otherwise null. - IStrategyRegistration RegisterFactoryAs(Func factoryDelegate, string name = null); - - /// - /// Resolves an implementation object for an interface or type. - /// - /// The interface or type. - /// An object implementing . - /// - /// The container pools the objects, so if the interface is resolved twice or the same type is registered for multiple interfaces, a single instance is created and returned. - /// - T Resolve(); - - /// - /// Resolves an implementation object for an interface or type. - /// - /// A name to resolve named instance, otherwise null. - /// The interface or type. - /// An object implementing . - /// - /// The container pools the objects, so if the interface is resolved twice or the same type is registered for multiple interfaces, a single instance is created and returned. - /// - T Resolve(string name); - - /// - /// Resolves an implementation object for an interface or type. - /// - /// The interface or type. - /// A name to resolve named instance, otherwise null. - /// An object implementing . - /// - /// The container pools the objects, so if the interface is resolved twice or the same type is registered for multiple interfaces, a single instance is created and returned. - /// - object Resolve(Type typeToResolve, string name = null); - - /// - /// Resolves all implementations of an interface or type. - /// - /// The interface or type. - /// An object implementing . - IEnumerable ResolveAll() where T : class; - - /// - /// Determines whether the interface or type is registered. - /// - /// The interface or type. - /// true if the interface or type is registered; otherwise false. - bool IsRegistered(); - - /// - /// Determines whether the interface or type is registered with the specified name. - /// - /// The interface or type. - /// The name. - /// true if the interface or type is registered; otherwise false. - bool IsRegistered(string name); - } - public interface IContainedInstance - { - IObjectContainer Container { get; } - } - public interface IStrategyRegistration - { - /// - /// Changes resolving strategy to a new instance per each dependency. - /// - /// - IStrategyRegistration InstancePerDependency(); - /// - /// Changes resolving strategy to a single instance per object container. This strategy is a default behaviour. - /// - /// - IStrategyRegistration InstancePerContext(); - } - - public class ObjectContainer : IObjectContainer - { - private const string REGISTERED_NAME_PARAMETER_NAME = "registeredName"; - - /// - /// A very simple immutable linked list of . - /// - private class ResolutionList - { - private readonly RegistrationKey _currentRegistrationKey; - private readonly Type _currentResolvedType; - private readonly ResolutionList _nextNode; - private bool IsLast => _nextNode == null; - - public ResolutionList() - { - Debug.Assert(IsLast); - } - - private ResolutionList(RegistrationKey currentRegistrationKey, Type currentResolvedType, ResolutionList nextNode) - { - if (nextNode == null) throw new ArgumentNullException("nextNode"); - - _currentRegistrationKey = currentRegistrationKey; - _currentResolvedType = currentResolvedType; - _nextNode = nextNode; - } - - public ResolutionList AddToEnd(RegistrationKey registrationKey, Type resolvedType) - { - return new ResolutionList(registrationKey, resolvedType, this); - } - - // ReSharper disable once UnusedMember.Local - public bool Contains(Type resolvedType) - { - if (resolvedType == null) throw new ArgumentNullException("resolvedType"); - return GetReverseEnumerable().Any(i => i.Value == resolvedType); - } - - public bool Contains(RegistrationKey registrationKey) - { - return GetReverseEnumerable().Any(i => i.Key.Equals(registrationKey)); - } - - private IEnumerable> GetReverseEnumerable() - { - var node = this; - while (!node.IsLast) - { - yield return new KeyValuePair(node._currentRegistrationKey, node._currentResolvedType); - node = node._nextNode; - } - } - - public Type[] ToTypeList() - { - return GetReverseEnumerable().Select(i => i.Value ?? i.Key.Type).Reverse().ToArray(); - } - - public override string ToString() - { - return string.Join(",", GetReverseEnumerable().Select(n => string.Format("{0}:{1}", n.Key, n.Value))); - } - } - - private struct RegistrationKey - { - public readonly Type Type; - public readonly string Name; - - public RegistrationKey(Type type, string name) - { - if (type == null) throw new ArgumentNullException("type"); - - Type = type; - Name = name; - } - - private Type TypeGroup - { - get - { - if (Type.IsGenericType && !Type.IsGenericTypeDefinition) - return Type.GetGenericTypeDefinition(); - return Type; - } - } - - public override string ToString() - { - Debug.Assert(Type.FullName != null); - if (Name == null) - return Type.FullName; - - return string.Format("{0}('{1}')", Type.FullName, Name); - } - - bool Equals(RegistrationKey other) - { - var isInvertable = other.TypeGroup == Type || other.Type == TypeGroup || other.Type == Type; - return isInvertable && String.Equals(other.Name, Name, StringComparison.CurrentCultureIgnoreCase); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (obj.GetType() != typeof(RegistrationKey)) return false; - return Equals((RegistrationKey)obj); - } - - public override int GetHashCode() - { - return TypeGroup.GetHashCode(); - } - } - - #region Registration types - - private enum SolvingStrategy - { - PerContext, - PerDependency - } - - private interface IRegistration - { - object Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath); - } - - private class TypeRegistration : RegistrationWithStrategy, IRegistration - { - private readonly Type _implementationType; - private readonly object _syncRoot = new object(); - - public TypeRegistration(Type implementationType) - { - _implementationType = implementationType; - } - - protected override object ResolvePerContext(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) - { - var typeToConstruct = GetTypeToConstruct(keyToResolve); - - var pooledObjectKey = new RegistrationKey(typeToConstruct, keyToResolve.Name); - - var result = ExecuteWithLock(_syncRoot, () => container.GetPooledObject(pooledObjectKey), () => - { - if (typeToConstruct.IsInterface) - throw new ObjectContainerException("Interface cannot be resolved: " + keyToResolve, - resolutionPath.ToTypeList()); - - var obj = container.CreateObject(typeToConstruct, resolutionPath, keyToResolve); - container._objectPool.Add(pooledObjectKey, obj); - return obj; - }, resolutionPath, container.ConcurrentObjectResolutionTimeout); - - return result; - } - - - - protected override object ResolvePerDependency(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) - { - var typeToConstruct = GetTypeToConstruct(keyToResolve); - if (typeToConstruct.IsInterface) - throw new ObjectContainerException("Interface cannot be resolved: " + keyToResolve, resolutionPath.ToTypeList()); - return container.CreateObject(typeToConstruct, resolutionPath, keyToResolve); - } - - private Type GetTypeToConstruct(RegistrationKey keyToResolve) - { - var targetType = _implementationType; - if (targetType.IsGenericTypeDefinition) - { - var typeArgs = keyToResolve.Type.GetGenericArguments(); - targetType = targetType.MakeGenericType(typeArgs); - } - return targetType; - } - - public override string ToString() - { - return "Type: " + _implementationType.FullName; - } - } - - private class InstanceRegistration : IRegistration - { - private readonly object _instance; - - public InstanceRegistration(object instance) - { - _instance = instance; - } - - public object Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) - { - return _instance; - } - - public override string ToString() - { - string instanceText; - try - { - instanceText = _instance.ToString(); - } - catch (Exception ex) - { - instanceText = ex.Message; - } - - return "Instance: " + instanceText; - } - } - - private abstract class RegistrationWithStrategy : IStrategyRegistration - { - protected SolvingStrategy SolvingStrategy = SolvingStrategy.PerContext; - public virtual object Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) - { - if (SolvingStrategy == SolvingStrategy.PerDependency) - { - return ResolvePerDependency(container, keyToResolve, resolutionPath); - } - return ResolvePerContext(container, keyToResolve, resolutionPath); - } - - protected abstract object ResolvePerContext(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath); - protected abstract object ResolvePerDependency(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath); - - public IStrategyRegistration InstancePerDependency() - { - SolvingStrategy = SolvingStrategy.PerDependency; - return this; - } - - public IStrategyRegistration InstancePerContext() - { - SolvingStrategy = SolvingStrategy.PerContext; - return this; - } - - protected static object ExecuteWithLock(object lockObject, Func getter, Func factory, ResolutionList resolutionPath, TimeSpan timeout) - { - var obj = getter(); - - if (obj != null) - return obj; - - if (timeout == TimeSpan.Zero) - return factory(); - - if (Monitor.TryEnter(lockObject, timeout)) - { - try - { - obj = getter(); - - if (obj != null) - return obj; - - obj = factory(); - return obj; - } - finally - { - Monitor.Exit(lockObject); - } - } - - throw new ObjectContainerException("Concurrent object resolution timeout (potential circular dependency).", resolutionPath.ToTypeList()); - } - } - - private class FactoryRegistration : RegistrationWithStrategy, IRegistration - { - private readonly Delegate _factoryDelegate; - private readonly object _syncRoot = new object(); - public FactoryRegistration(Delegate factoryDelegate) - { - _factoryDelegate = factoryDelegate; - } - - protected override object ResolvePerContext(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) - { - var result = ExecuteWithLock(_syncRoot, () => container.GetPooledObject(keyToResolve), () => - { - var obj = container.InvokeFactoryDelegate(_factoryDelegate, resolutionPath, keyToResolve); - container._objectPool.Add(keyToResolve, obj); - return obj; - }, resolutionPath, container.ConcurrentObjectResolutionTimeout); - - return result; - } - protected override object ResolvePerDependency(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) - { - return container.InvokeFactoryDelegate(_factoryDelegate, resolutionPath, keyToResolve); - } - } - - private class NonDisposableWrapper - { - public object Object { get; private set; } - - public NonDisposableWrapper(object obj) - { - Object = obj; - } - } - - private class NamedInstanceDictionaryRegistration : IRegistration - { - public object Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) - { - var typeToResolve = keyToResolve.Type; - Debug.Assert(typeToResolve.IsGenericType && typeToResolve.GetGenericTypeDefinition() == typeof(IDictionary<,>)); - - var genericArguments = typeToResolve.GetGenericArguments(); - var keyType = genericArguments[0]; - var targetType = genericArguments[1]; - var result = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(genericArguments)); - - foreach (var namedRegistration in container._registrations.Where(r => r.Key.Name != null && r.Key.Type == targetType).Select(r => r.Key).ToList()) - { - var convertedKey = ChangeType(namedRegistration.Name, keyType); - Debug.Assert(convertedKey != null); - result.Add(convertedKey, container.Resolve(namedRegistration.Type, namedRegistration.Name)); - } - - return result; - } - - private object ChangeType(string name, Type keyType) - { - if (keyType.IsEnum) - return Enum.Parse(keyType, name, true); - - Debug.Assert(keyType == typeof(string)); - return name; - } - } - - #endregion - - public static TimeSpan DefaultConcurrentObjectResolutionTimeout { get; set; } = TimeSpan.FromSeconds(1); - private bool _isDisposed = false; - private readonly ObjectContainer _baseContainer; - private readonly ConcurrentDictionary _registrations = new(); - private readonly List _resolvedKeys = new(); - private readonly Dictionary _objectPool = new(); - - public event Action ObjectCreated; - public IObjectContainer BaseContainer => _baseContainer; - - /// - /// Sets the timeout for thread-safe object resolution. By default, it uses the value of that is initialized to 1 second. Setting it to disables thread-safe resolution. - /// - public TimeSpan ConcurrentObjectResolutionTimeout { get; set; } = DefaultConcurrentObjectResolutionTimeout; - - public ObjectContainer(IObjectContainer baseContainer = null) - { - if (baseContainer != null && !(baseContainer is ObjectContainer)) - throw new ArgumentException("Base container must be an ObjectContainer", "baseContainer"); - - _baseContainer = (ObjectContainer)baseContainer; - RegisterInstanceAs(this); - } - - #region Registration - - public IStrategyRegistration RegisterTypeAs(Type implementationType, string name = null) where TInterface : class - { - Type interfaceType = typeof(TInterface); - return RegisterTypeAsInternal(implementationType, interfaceType, name); - } - - public IStrategyRegistration RegisterTypeAs(string name = null) where TType : class, TInterface - { - Type interfaceType = typeof(TInterface); - Type implementationType = typeof(TType); - return RegisterTypeAsInternal(implementationType, interfaceType, name); - } - - public IStrategyRegistration RegisterTypeAs(Type implementationType, Type interfaceType, string name = null) - { - if (!IsValidTypeMapping(implementationType, interfaceType)) - throw new InvalidOperationException("type mapping is not valid"); - return RegisterTypeAsInternal(implementationType, interfaceType, name); - } - - private bool IsValidTypeMapping(Type implementationType, Type interfaceType) - { - if (interfaceType.IsAssignableFrom(implementationType)) - return true; - - if (interfaceType.IsGenericTypeDefinition && implementationType.IsGenericTypeDefinition) - { - var baseTypes = GetBaseTypes(implementationType).ToArray(); - return baseTypes.Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == interfaceType); - } - - return false; - } - - private static IEnumerable GetBaseTypes(Type type) - { - if (type.BaseType == null) return type.GetInterfaces(); - - return Enumerable.Repeat(type.BaseType, 1) - .Concat(type.GetInterfaces()) - .Concat(type.GetInterfaces().SelectMany(GetBaseTypes)) - .Concat(GetBaseTypes(type.BaseType)); - } - - - private RegistrationKey CreateNamedInstanceDictionaryKey(Type targetType) - { - return new RegistrationKey(typeof(IDictionary<,>).MakeGenericType(typeof(string), targetType), null); - } - - private void AddRegistration(RegistrationKey key, IRegistration registration) - { - _registrations[key] = registration; - - AddNamedDictionaryRegistration(key); - } - - private IRegistration EnsureImplicitRegistration(RegistrationKey key) - { - var registration = _registrations.GetOrAdd(key, (registrationKey => new TypeRegistration(registrationKey.Type))); - - AddNamedDictionaryRegistration(key); - - return registration; - } - - private void AddNamedDictionaryRegistration(RegistrationKey key) - { - if (key.Name != null) - { - var dictKey = CreateNamedInstanceDictionaryKey(key.Type); - _registrations.TryAdd(dictKey, new NamedInstanceDictionaryRegistration()); - } - } - - private IStrategyRegistration RegisterTypeAsInternal(Type implementationType, Type interfaceType, string name) - { - var registrationKey = new RegistrationKey(interfaceType, name); - AssertNotResolved(registrationKey); - - ClearRegistrations(registrationKey); - var typeRegistration = new TypeRegistration(implementationType); - AddRegistration(registrationKey, typeRegistration); - - return typeRegistration; - } - - public void RegisterInstanceAs(object instance, Type interfaceType, string name = null, bool dispose = false) - { - if (instance == null) - throw new ArgumentNullException("instance"); - var registrationKey = new RegistrationKey(interfaceType, name); - AssertNotResolved(registrationKey); - - ClearRegistrations(registrationKey); - AddRegistration(registrationKey, new InstanceRegistration(instance)); - _objectPool[new RegistrationKey(instance.GetType(), name)] = GetPoolableInstance(instance, dispose); - } - - private static object GetPoolableInstance(object instance, bool dispose) - { - return (instance is IDisposable) && !dispose ? new NonDisposableWrapper(instance) : instance; - } - - public void RegisterInstanceAs(TInterface instance, string name = null, bool dispose = false) where TInterface : class - { - RegisterInstanceAs(instance, typeof(TInterface), name, dispose); - } - - public IStrategyRegistration RegisterFactoryAs(Func factoryDelegate, string name = null) - { - return RegisterFactoryAs(factoryDelegate, typeof(TInterface), name); - } - - public IStrategyRegistration RegisterFactoryAs(Func factoryDelegate, string name = null) - { - return RegisterFactoryAs(factoryDelegate, typeof(TInterface), name); - } - - public void RegisterFactoryAs(Delegate factoryDelegate, string name = null) - { - RegisterFactoryAs(factoryDelegate, typeof(TInterface), name); - } - - public IStrategyRegistration RegisterFactoryAs(Delegate factoryDelegate, Type interfaceType, string name = null) - { - if (factoryDelegate == null) throw new ArgumentNullException("factoryDelegate"); - if (interfaceType == null) throw new ArgumentNullException("interfaceType"); - - var registrationKey = new RegistrationKey(interfaceType, name); - AssertNotResolved(registrationKey); - - ClearRegistrations(registrationKey); - var factoryRegistration = new FactoryRegistration(factoryDelegate); - AddRegistration(registrationKey, factoryRegistration); - - return factoryRegistration; - } - - public bool IsRegistered() - { - return IsRegistered(null); - } - - public bool IsRegistered(string name) - { - Type typeToResolve = typeof(T); - - var keyToResolve = new RegistrationKey(typeToResolve, name); - - return _registrations.ContainsKey(keyToResolve); - } - - // ReSharper disable once UnusedParameter.Local - private void AssertNotResolved(RegistrationKey interfaceType) - { - if (_resolvedKeys.Contains(interfaceType)) - throw new ObjectContainerException("An object has been resolved for this interface already.", null); - } - - private void ClearRegistrations(RegistrationKey registrationKey) - { - _registrations.TryRemove(registrationKey, out _); - } - - - #endregion - - #region Resolve - - public T Resolve() - { - return Resolve(null); - } - - public T Resolve(string name) - { - Type typeToResolve = typeof(T); - - object resolvedObject = Resolve(typeToResolve, name); - - return (T)resolvedObject; - } - - public object Resolve(Type typeToResolve, string name = null) - { - return Resolve(typeToResolve, new ResolutionList(), name); - } - - public IEnumerable ResolveAll() where T : class - { - return _registrations - .Where(x => x.Key.Type == typeof(T)) - .Select(x => Resolve(x.Key.Type, x.Key.Name) as T); - } - - private object Resolve(Type typeToResolve, ResolutionList resolutionPath, string name) - { - AssertNotDisposed(); - - var keyToResolve = new RegistrationKey(typeToResolve, name); - object resolvedObject = ResolveObject(keyToResolve, resolutionPath); - if (!_resolvedKeys.Contains(keyToResolve)) - { - _resolvedKeys.Add(keyToResolve); - } - Debug.Assert(typeToResolve.IsInstanceOfType(resolvedObject)); - return resolvedObject; - } - - private KeyValuePair? GetRegistrationResult(RegistrationKey keyToResolve) - { - IRegistration registration; - if (_registrations.TryGetValue(keyToResolve, out registration)) - { - return new KeyValuePair(this, registration); - } - - if (_baseContainer != null) - return _baseContainer.GetRegistrationResult(keyToResolve); - - if (IsSpecialNamedInstanceDictionaryKey(keyToResolve)) - { - var targetType = keyToResolve.Type.GetGenericArguments()[1]; - return GetRegistrationResult(CreateNamedInstanceDictionaryKey(targetType)); - } - - // if there was no named registration, we still return an empty dictionary - if (IsDefaultNamedInstanceDictionaryKey(keyToResolve)) - { - return new KeyValuePair(this, new NamedInstanceDictionaryRegistration()); - } - - return null; - } - - private bool IsDefaultNamedInstanceDictionaryKey(RegistrationKey keyToResolve) - { - return IsNamedInstanceDictionaryKey(keyToResolve) && - keyToResolve.Type.GetGenericArguments()[0] == typeof(string); - } - - private bool IsSpecialNamedInstanceDictionaryKey(RegistrationKey keyToResolve) - { - return IsNamedInstanceDictionaryKey(keyToResolve) && - keyToResolve.Type.GetGenericArguments()[0].IsEnum; - } - - private bool IsNamedInstanceDictionaryKey(RegistrationKey keyToResolve) - { - return keyToResolve.Name == null && keyToResolve.Type.IsGenericType && keyToResolve.Type.GetGenericTypeDefinition() == typeof(IDictionary<,>); - } - - private object GetPooledObject(RegistrationKey pooledObjectKey) - { - object obj; - if (GetObjectFromPool(pooledObjectKey, out obj)) - return obj; - - return null; - } - - private bool GetObjectFromPool(RegistrationKey pooledObjectKey, out object obj) - { - if (!_objectPool.TryGetValue(pooledObjectKey, out obj)) - return false; - - var nonDisposableWrapper = obj as NonDisposableWrapper; - if (nonDisposableWrapper != null) - obj = nonDisposableWrapper.Object; - - return true; - } - - private object ResolveObject(RegistrationKey keyToResolve, ResolutionList resolutionPath) - { - if (keyToResolve.Type.IsPrimitive || keyToResolve.Type == typeof(string) || keyToResolve.Type.IsValueType) - throw new ObjectContainerException("Primitive types or structs cannot be resolved: " + keyToResolve.Type.FullName, resolutionPath.ToTypeList()); - - var registrationResult = GetRegistrationResult(keyToResolve); - - var registrationToUse = registrationResult ?? - new KeyValuePair(this, EnsureImplicitRegistration(keyToResolve)); - - var resolutionPathForResolve = registrationToUse.Key == this ? - resolutionPath : new ResolutionList(); - var result = registrationToUse.Value.Resolve(registrationToUse.Key, keyToResolve, resolutionPathForResolve); - - return result; - } - - - private object CreateObject(Type type, ResolutionList resolutionPath, RegistrationKey keyToResolve) - { - var ctors = type.GetConstructors(); - if (ctors.Length == 0) - ctors = type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance); - - Debug.Assert(ctors.Length > 0, "Class must have a constructor!"); - - int maxParamCount = ctors.Max(ctor => ctor.GetParameters().Length); - var maxParamCountCtors = ctors.Where(ctor => ctor.GetParameters().Length == maxParamCount).ToArray(); - - object obj; - if (maxParamCountCtors.Length == 1) - { - ConstructorInfo ctor = maxParamCountCtors[0]; - if (resolutionPath.Contains(keyToResolve)) - throw new ObjectContainerException("Circular dependency found! " + type.FullName, resolutionPath.ToTypeList()); - - var args = ResolveArguments(ctor.GetParameters(), keyToResolve, resolutionPath.AddToEnd(keyToResolve, type)); - obj = ctor.Invoke(args); - } - else - { - throw new ObjectContainerException("Multiple public constructors with same maximum parameter count are not supported! " + type.FullName, resolutionPath.ToTypeList()); - } - - OnObjectCreated(obj); - - return obj; - } - - protected virtual void OnObjectCreated(object obj) - { - var eventHandler = ObjectCreated; - if (eventHandler != null) - eventHandler(obj); - } - - private object InvokeFactoryDelegate(Delegate factoryDelegate, ResolutionList resolutionPath, RegistrationKey keyToResolve) - { - if (resolutionPath.Contains(keyToResolve)) - throw new ObjectContainerException("Circular dependency found! " + factoryDelegate, resolutionPath.ToTypeList()); - - var args = ResolveArguments(factoryDelegate.Method.GetParameters(), keyToResolve, resolutionPath.AddToEnd(keyToResolve, null)); - return factoryDelegate.DynamicInvoke(args); - } - - private object[] ResolveArguments(IEnumerable parameters, RegistrationKey keyToResolve, ResolutionList resolutionPath) - { - return parameters.Select(p => IsRegisteredNameParameter(p) ? ResolveRegisteredName(keyToResolve) : Resolve(p.ParameterType, resolutionPath, null)).ToArray(); - } - - private object ResolveRegisteredName(RegistrationKey keyToResolve) - { - return keyToResolve.Name; - } - - private bool IsRegisteredNameParameter(ParameterInfo parameterInfo) - { - return parameterInfo.ParameterType == typeof(string) && - parameterInfo.Name.Equals(REGISTERED_NAME_PARAMETER_NAME); - } - - #endregion - - public override string ToString() - { - return string.Join(Environment.NewLine, - _registrations - .Where(r => !(r.Value is NamedInstanceDictionaryRegistration)) - .Select(r => string.Format("{0} -> {1}", r.Key, (r.Key.Type == typeof(IObjectContainer) && r.Key.Name == null) ? "" : r.Value.ToString()))); - } - - private void AssertNotDisposed() - { - if (_isDisposed) - throw new ObjectContainerException("Object container disposed", null); - } - - public void Dispose() - { - _isDisposed = true; - - foreach (var obj in _objectPool.Values.OfType().Where(o => !ReferenceEquals(o, this))) - obj.Dispose(); - - _objectPool.Clear(); - _registrations.Clear(); - _resolvedKeys.Clear(); - } - } -} diff --git a/Reqnroll.BoDi/Reqnroll.BoDi.csproj b/Reqnroll.BoDi/Reqnroll.BoDi.csproj deleted file mode 100644 index d0c0a5657..000000000 --- a/Reqnroll.BoDi/Reqnroll.BoDi.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - netstandard2.0 - Reqnroll.BoDi - Reqnroll.BoDi - $(Reqnroll_KeyFile) - $(Reqnroll_EnableStrongNameSigning) - $(Reqnroll_PublicSign) - - true - $(NoWarn);1591 - true - - true - true - $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - - - - - - diff --git a/Reqnroll.Generator/CodeDom/CodeDomHelper.cs b/Reqnroll.Generator/CodeDom/CodeDomHelper.cs index 7829c115f..9d649dbbb 100644 --- a/Reqnroll.Generator/CodeDom/CodeDomHelper.cs +++ b/Reqnroll.Generator/CodeDom/CodeDomHelper.cs @@ -250,7 +250,7 @@ public bool IsVoid(CodeTypeReference codeTypeReference) return typeof(void).FullName!.Equals(codeTypeReference.BaseType); } - public void MarkCodeMethodInvokeExpressionAsAwait(CodeMethodInvokeExpression expression) + public CodeMethodInvokeExpression MarkCodeMethodInvokeExpressionAsAwait(CodeMethodInvokeExpression expression) { if (expression.Method.TargetObject is CodeVariableReferenceExpression variableExpression) { @@ -276,6 +276,7 @@ public void MarkCodeMethodInvokeExpressionAsAwait(CodeMethodInvokeExpression exp { expression.Method.TargetObject = GetAwaitedMethodThisTargetObject(expression.Method.TargetObject); } + return expression; } private CodeExpression GetAwaitedMethodThisTargetObject(CodeExpression thisExpression) diff --git a/Reqnroll.Generator/DefaultDependencyProvider.cs b/Reqnroll.Generator/DefaultDependencyProvider.cs index a3bde7d1c..cf9a95e90 100644 --- a/Reqnroll.Generator/DefaultDependencyProvider.cs +++ b/Reqnroll.Generator/DefaultDependencyProvider.cs @@ -6,6 +6,8 @@ using Reqnroll.Generator.Plugins; using Reqnroll.Generator.UnitTestConverter; using Reqnroll.Parser; +using Reqnroll.PlatformCompatibility; +using Reqnroll.Plugins; using Reqnroll.Tracing; using Reqnroll.Utils; @@ -25,6 +27,7 @@ public virtual void RegisterDefaults(ObjectContainer container) container.RegisterTypeAs(); container.RegisterTypeAs(); + PlatformHelper.RegisterPluginAssemblyLoader(container); container.RegisterTypeAs(); container.RegisterTypeAs(); diff --git a/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs b/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs index 0eb430d51..baba0e0b8 100644 --- a/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs +++ b/Reqnroll.Generator/Generation/UnitTestFeatureGenerator.cs @@ -7,7 +7,6 @@ using Reqnroll.Generator.CodeDom; using Reqnroll.Generator.UnitTestConverter; using Reqnroll.Generator.UnitTestProvider; -using Reqnroll.Infrastructure; using Reqnroll.Parser; using Reqnroll.Tracing; @@ -127,7 +126,7 @@ private void SetupScenarioCleanupMethod(TestClassGenerationContext generationCon //await testRunner.CollectScenarioErrorsAsync(); var expression = new CodeMethodInvokeExpression( testRunnerField, - nameof(TestRunner.CollectScenarioErrorsAsync)); + nameof(ITestRunner.CollectScenarioErrorsAsync)); _codeDomHelper.MarkCodeMethodInvokeExpressionAsAwait(expression); @@ -296,7 +295,7 @@ private void SetupScenarioInitializeMethod(TestClassGenerationContext generation scenarioInitializeMethod.Statements.Add( new CodeMethodInvokeExpression( testRunnerField, - nameof(ITestExecutionEngine.OnScenarioInitialize), + nameof(ITestRunner.OnScenarioInitialize), new CodeVariableReferenceExpression("scenarioInfo"))); } @@ -313,7 +312,7 @@ private void SetupScenarioStartMethod(TestClassGenerationContext generationConte var testRunnerField = _scenarioPartHelper.GetTestRunnerExpression(); var expression = new CodeMethodInvokeExpression( testRunnerField, - nameof(ITestExecutionEngine.OnScenarioStartAsync)); + nameof(ITestRunner.OnScenarioStartAsync)); _codeDomHelper.MarkCodeMethodInvokeExpressionAsAwait(expression); diff --git a/Reqnroll.Generator/Generation/UnitTestMethodGenerator.cs b/Reqnroll.Generator/Generation/UnitTestMethodGenerator.cs index ba2ef7e3a..252c6ca34 100644 --- a/Reqnroll.Generator/Generation/UnitTestMethodGenerator.cs +++ b/Reqnroll.Generator/Generation/UnitTestMethodGenerator.cs @@ -277,8 +277,10 @@ 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 scenarioCombinedTagsPropertyExpression = new CodePropertyReferenceExpression(new CodeVariableReferenceExpression("scenarioInfo"), "CombinedTags"); + var tagHelperReference = new CodeTypeReferenceExpression(_codeDomHelper.GetGlobalizedTypeName(typeof(TagHelper))); - var scenarioTagIgnoredCheckStatement = new CodeMethodInvokeExpression(tagHelperReference, nameof(TagHelper.ContainsIgnoreTag), tagsOfScenarioVariableReferenceExpression); + var scenarioTagIgnoredCheckStatement = new CodeMethodInvokeExpression(tagHelperReference, nameof(TagHelper.ContainsIgnoreTag), scenarioCombinedTagsPropertyExpression); var featureTagIgnoredCheckStatement = new CodeMethodInvokeExpression(tagHelperReference, nameof(TagHelper.ContainsIgnoreTag), featureFileTagFieldReferenceExpression); var ifIsIgnoredStatement = new CodeConditionStatement( diff --git a/Reqnroll.Generator/Plugins/GeneratorPluginLoader.cs b/Reqnroll.Generator/Plugins/GeneratorPluginLoader.cs index d3018e390..3d2e42994 100644 --- a/Reqnroll.Generator/Plugins/GeneratorPluginLoader.cs +++ b/Reqnroll.Generator/Plugins/GeneratorPluginLoader.cs @@ -1,28 +1,23 @@ using System; -using System.IO; using System.Reflection; using Reqnroll.Infrastructure; +using Reqnroll.PlatformCompatibility; using Reqnroll.Plugins; namespace Reqnroll.Generator.Plugins { - public class GeneratorPluginLoader : IGeneratorPluginLoader + public class GeneratorPluginLoader(IPluginAssemblyLoader _pluginAssemblyLoader) : IGeneratorPluginLoader { public IGeneratorPlugin LoadPlugin(PluginDescriptor pluginDescriptor) { Assembly pluginAssembly; try { - -#if NETCOREAPP - pluginAssembly = PluginAssemblyResolver.Load(pluginDescriptor.Path); -#else - pluginAssembly = Assembly.LoadFrom(pluginDescriptor.Path); -#endif + pluginAssembly = _pluginAssemblyLoader.LoadAssembly(pluginDescriptor.Path); } catch(Exception ex) { - throw new ReqnrollException($"Unable to load plugin assembly: {pluginDescriptor.Path}. Please check https://go.reqnroll.net/doc-plugins for details.", ex); + throw new ReqnrollException($"Unable to load plugin assembly: {pluginDescriptor.Path}. Please check https://go.reqnroll.net/doc-plugins for details. (Framework: {PlatformInformation.DotNetFrameworkDescription})", ex); } var pluginAttribute = (GeneratorPluginAttribute)Attribute.GetCustomAttribute(pluginAssembly, typeof(GeneratorPluginAttribute)); diff --git a/Reqnroll.Generator/Reqnroll.Generator.csproj b/Reqnroll.Generator/Reqnroll.Generator.csproj index 6b17cde00..4938fbb2d 100644 --- a/Reqnroll.Generator/Reqnroll.Generator.csproj +++ b/Reqnroll.Generator/Reqnroll.Generator.csproj @@ -1,6 +1,6 @@ - + - $(Reqnroll_Generator_TFM) + netstandard2.0 Reqnroll.Generator $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) @@ -20,8 +20,6 @@ - - @@ -30,30 +28,4 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/Reqnroll.Generator/UnitTestProvider/MsTestGeneratorProvider.cs b/Reqnroll.Generator/UnitTestProvider/MsTestGeneratorProvider.cs index d38b6b0c8..beb0cb675 100644 --- a/Reqnroll.Generator/UnitTestProvider/MsTestGeneratorProvider.cs +++ b/Reqnroll.Generator/UnitTestProvider/MsTestGeneratorProvider.cs @@ -13,6 +13,8 @@ public class MsTestGeneratorProvider : IUnitTestGeneratorProvider protected internal const string PROPERTY_ATTR = "Microsoft.VisualStudio.TestTools.UnitTesting.TestPropertyAttribute"; protected internal const string TESTFIXTURESETUP_ATTR = "Microsoft.VisualStudio.TestTools.UnitTesting.ClassInitializeAttribute"; protected internal const string TESTFIXTURETEARDOWN_ATTR = "Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute"; + protected internal const string CLASSCLEANUPBEHAVIOR_ENUM = "Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior"; + protected internal const string CLASSCLEANUPBEHAVIOR_ENDOFCLASS = "EndOfClass"; protected internal const string TESTSETUP_ATTR = "Microsoft.VisualStudio.TestTools.UnitTesting.TestInitializeAttribute"; protected internal const string TESTTEARDOWN_ATTR = "Microsoft.VisualStudio.TestTools.UnitTesting.TestCleanupAttribute"; protected internal const string IGNORE_ATTR = "Microsoft.VisualStudio.TestTools.UnitTesting.IgnoreAttribute"; @@ -112,7 +114,9 @@ public virtual void SetTestClassInitializeMethod(TestClassGenerationContext gene public void SetTestClassCleanupMethod(TestClassGenerationContext generationContext) { generationContext.TestClassCleanupMethod.Attributes |= MemberAttributes.Static; - CodeDomHelper.AddAttribute(generationContext.TestClassCleanupMethod, TESTFIXTURETEARDOWN_ATTR); + // [Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupAttribute(Microsoft.VisualStudio.TestTools.UnitTesting.ClassCleanupBehavior.EndOfClass)] + var attribute = CodeDomHelper.AddAttribute(generationContext.TestClassCleanupMethod, TESTFIXTURETEARDOWN_ATTR); + attribute.Arguments.Add(new CodeAttributeArgument(new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(CLASSCLEANUPBEHAVIOR_ENUM), CLASSCLEANUPBEHAVIOR_ENDOFCLASS))); } diff --git a/Reqnroll.Generator/UnitTestProvider/NUnit3TestGeneratorProvider.cs b/Reqnroll.Generator/UnitTestProvider/NUnit3TestGeneratorProvider.cs index 898bb8035..66fc9568f 100644 --- a/Reqnroll.Generator/UnitTestProvider/NUnit3TestGeneratorProvider.cs +++ b/Reqnroll.Generator/UnitTestProvider/NUnit3TestGeneratorProvider.cs @@ -140,7 +140,7 @@ public void SetRow(TestClassGenerationContext generationContext, CodeMemberMetho } if (isIgnored) - args.Add(new CodeAttributeArgument("Ignored", new CodePrimitiveExpression(true))); + args.Add(new CodeAttributeArgument("IgnoreReason", new CodePrimitiveExpression("Ignored by @ignore tag"))); CodeDomHelper.AddAttribute(testMethod, ROW_ATTR, args.ToArray()); } diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Cli/Reqnroll.TestProjectGenerator.Cli.csproj b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Cli/Reqnroll.TestProjectGenerator.Cli.csproj index 280b93c8a..eb2310446 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Cli/Reqnroll.TestProjectGenerator.Cli.csproj +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Cli/Reqnroll.TestProjectGenerator.Cli.csproj @@ -2,7 +2,7 @@ Exe - net6.0 + net8.0 true reqnroll-tpg diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/AppConfigGeneratorTests.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/AppConfigGeneratorTests.cs index 8d7afcc27..730e35230 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/AppConfigGeneratorTests.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/AppConfigGeneratorTests.cs @@ -2,7 +2,6 @@ using Reqnroll.TestProjectGenerator.ConfigurationModel; using Reqnroll.TestProjectGenerator.Data; using Reqnroll.TestProjectGenerator.Factories.ConfigurationGenerator; -using Reqnroll.TestProjectGenerator.NewApi._1_Memory; using Xunit; namespace Reqnroll.TestProjectGenerator.Tests diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/FeatureFileGeneratorTests.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/FeatureFileGeneratorTests.cs index 4692c1fac..f4a23adf1 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/FeatureFileGeneratorTests.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/FeatureFileGeneratorTests.cs @@ -1,5 +1,4 @@ using FluentAssertions; -using Reqnroll.TestProjectGenerator.NewApi._1_Memory; using Xunit; namespace Reqnroll.TestProjectGenerator.Tests diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/JsonConfigGeneratorTests.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/JsonConfigGeneratorTests.cs index 5137bb094..57d2d6239 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/JsonConfigGeneratorTests.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/JsonConfigGeneratorTests.cs @@ -2,7 +2,6 @@ using Reqnroll.TestProjectGenerator.ConfigurationModel; using Reqnroll.TestProjectGenerator.Data; using Reqnroll.TestProjectGenerator.Factories.ConfigurationGenerator; -using Reqnroll.TestProjectGenerator.NewApi._1_Memory; using Xunit; namespace Reqnroll.TestProjectGenerator.Tests diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/ProjectTests.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/ProjectTests.cs index 1797b9360..1ec4cf57b 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/ProjectTests.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/ProjectTests.cs @@ -35,193 +35,220 @@ public ProjectTests() return (solution, project, folder); } - [Fact] + private SolutionWriter CreateSolutionWriter() => new SolutionWriter(new Mock().Object); + + private void RunSkippableTest(Action test) + { + try + { + test(); + } + catch (DotNetSdkNotInstalledException ex) + { + Skip.IfNot(new ConfigurationDriver().PipelineMode, ex.ToString()); + } + } + + [SkippableFact] public void AddNuGetPackageToProjectInNewFormat() { - - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp); - project.AddNuGetPackage("Reqnroll", "2.3.1", new NuGetPackageAssembly("Reqnroll, Version=2.3.1.0, Culture=neutral, PublicKeyToken=0778194805d6db41, processorArchitecture=MSIL", "net45\\Reqnroll.dll")); + project.AddNuGetPackage("Reqnroll", "2.3.1", new NuGetPackageAssembly("Reqnroll, Version=2.3.1.0, Culture=neutral, PublicKeyToken=0778194805d6db41, processorArchitecture=MSIL", "net45\\Reqnroll.dll")); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - var projectFileContent = GetProjectFileContent(solutionFolder, project); + var projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should().Contain(""); + projectFileContent.Should().Contain(""); + }); } - [Fact] + [SkippableFact] public void AddNuGetPackageToProjectInOldFormat() { - - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.Old, ProgrammingLanguage.CSharp); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.Old, ProgrammingLanguage.CSharp); - project.AddNuGetPackage("Reqnroll", "2.3.1", new NuGetPackageAssembly("Reqnroll, Version=2.3.1.0, Culture=neutral, PublicKeyToken=0778194805d6db41, processorArchitecture=MSIL", "net45\\Reqnroll.dll")); + project.AddNuGetPackage("Reqnroll", "2.3.1", new NuGetPackageAssembly("Reqnroll, Version=2.3.1.0, Culture=neutral, PublicKeyToken=0778194805d6db41, processorArchitecture=MSIL", "net45\\Reqnroll.dll")); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - var projectFileContent = GetProjectFileContent(solutionFolder, project); + var projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should().Contain(""); - projectFileContent.Should().Match("**..\\packages\\Reqnroll.2.3.1\\lib\\net45\\Reqnroll.dll**"); + projectFileContent.Should().Contain(""); + projectFileContent.Should().Match("**..\\packages\\Reqnroll.2.3.1\\lib\\net45\\Reqnroll.dll**"); + }); } - [Fact] + [SkippableFact] public void AddNuGetPackageWithMSBuildFilesToProjectInOldFormat() { + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.Old, ProgrammingLanguage.CSharp); - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.Old, ProgrammingLanguage.CSharp); + project.AddNuGetPackage( + "Reqnroll.Tools.MsBuild.Generation", + "2.3.2-preview20180328"); - project.AddNuGetPackage( - "Reqnroll.Tools.MsBuild.Generation", - "2.3.2-preview20180328"); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + var projectFileContent = GetProjectFileContent(solutionFolder, project); - var projectFileContent = GetProjectFileContent(solutionFolder, project); - - projectFileContent.Should().Contain(""); - projectFileContent.Should().Contain(""); - + projectFileContent.Should().Contain(""); + projectFileContent.Should().Contain(""); + }); } - [Fact] + [SkippableFact] public void AddReferenceToProjectInNewFormat() { - - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp); - project.AddReference("System.Configuration"); + project.AddReference("System.Configuration"); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - var projectFileContent = GetProjectFileContent(solutionFolder, project); + var projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should().Contain(""); + projectFileContent.Should().Contain(""); + }); } - [Fact] + [SkippableFact] public void AddReferenceToProjectInOldFormat() { + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.Old, ProgrammingLanguage.CSharp); - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.Old, ProgrammingLanguage.CSharp); + project.AddReference("System.Configuration"); - project.AddReference("System.Configuration"); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + var projectFileContent = GetProjectFileContent(solutionFolder, project); - var projectFileContent = GetProjectFileContent(solutionFolder, project); - - projectFileContent.Should().Contain(""); + projectFileContent.Should().Contain(""); + }); } - [Fact] + [SkippableFact] public void AddFileToProjectInOldFormat() { + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.Old, ProgrammingLanguage.CSharp); - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.Old, ProgrammingLanguage.CSharp); - - var projectFile = new ProjectFile("File.cs", "Compile", "//no code"); + var projectFile = new ProjectFile("File.cs", "Compile", "//no code"); - project.AddFile(projectFile); + project.AddFile(projectFile); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - var projectFileContent = GetProjectFileContent(solutionFolder, project); - var filePath = Path.Combine(GetProjectFolderPath(solutionFolder, project), "File.cs"); + var projectFileContent = GetProjectFileContent(solutionFolder, project); + var filePath = Path.Combine(GetProjectFolderPath(solutionFolder, project), "File.cs"); - projectFileContent.Should().Contain(""); - File.Exists(filePath).Should().BeTrue(); - File.ReadAllText(filePath).Should().Contain("//no code"); - + projectFileContent.Should().Contain(""); + File.Exists(filePath).Should().BeTrue(); + File.ReadAllText(filePath).Should().Contain("//no code"); + }); } - [Fact] + [SkippableFact] public void AddFileToProjectInNewFormat() { + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp); - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp); - - var projectFile = new ProjectFile("File.cs", "Compile", "//no code"); + var projectFile = new ProjectFile("File.cs", "Compile", "//no code"); - project.AddFile(projectFile); + project.AddFile(projectFile); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - var projectFileContent = GetProjectFileContent(solutionFolder, project); - var filePath = Path.Combine(GetProjectFolderPath(solutionFolder, project), "File.cs"); + var projectFileContent = GetProjectFileContent(solutionFolder, project); + var filePath = Path.Combine(GetProjectFolderPath(solutionFolder, project), "File.cs"); - projectFileContent.Should().NotContain(" + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.Old, ProgrammingLanguage.CSharp); - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.Old, ProgrammingLanguage.CSharp); - - var projectFile = new ProjectFile(Path.Combine("Folder","File.cs"), "Compile", "//no code"); + var projectFile = new ProjectFile(Path.Combine("Folder", "File.cs"), "Compile", "//no code"); - project.AddFile(projectFile); + project.AddFile(projectFile); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - var projectFileContent = GetProjectFileContent(solutionFolder, project); - var filePath = Path.Combine(GetProjectFolderPath(solutionFolder, project), "Folder", "File.cs"); + var projectFileContent = GetProjectFileContent(solutionFolder, project); + var filePath = Path.Combine(GetProjectFolderPath(solutionFolder, project), "Folder", "File.cs"); - projectFileContent.Should().Contain(""); - File.Exists(filePath).Should().BeTrue(); - File.ReadAllText(filePath).Should().Contain("//no code"); - + projectFileContent.Should().Contain(""); + File.Exists(filePath).Should().BeTrue(); + File.ReadAllText(filePath).Should().Contain("//no code"); + }); } - [Fact] + [SkippableFact] public void AddFileInFolderToProjectInNewFormat() { + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp); - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp); - - var projectFile = new ProjectFile(Path.Combine("Folder", "File.cs"), "Compile", "//no code"); + var projectFile = new ProjectFile(Path.Combine("Folder", "File.cs"), "Compile", "//no code"); - project.AddFile(projectFile); + project.AddFile(projectFile); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - var projectFileContent = GetProjectFileContent(solutionFolder, project); - var filePath = Path.Combine(GetProjectFolderPath(solutionFolder, project), "Folder", "File.cs"); + var projectFileContent = GetProjectFileContent(solutionFolder, project); + var filePath = Path.Combine(GetProjectFolderPath(solutionFolder, project), "Folder", "File.cs"); - projectFileContent.Should().NotContain(" + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); + string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should().Contain(""); + projectFileContent.Should().Contain(""); + }); } - [Fact] + [SkippableFact] public void CreateEmptyCSharpCore3_1ProjectInNewFormat() { - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp, TargetFramework.Netcoreapp31); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp, TargetFramework.Netcoreapp31); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); + string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should() - .Contain("\r\n \r\n netcoreapp3.1\r\n \r\n"); + projectFileContent.Should() + .Contain("\r\n \r\n netcoreapp3.1\r\n \r\n"); + }); } - [Fact] + [SkippableFact] public void CreateEmptyCSharpNet50ProjectInNewFormat() { - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp73, TargetFramework.Net50); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp73, TargetFramework.Net50); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); + string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should() - .Contain("\r\n \r\n net5.0\r\n 7.3\r\n \r\n"); + projectFileContent.Should() + .Contain("\r\n \r\n net5.0\r\n 7.3\r\n \r\n"); + }); } - [Fact] + [SkippableFact] public void CreateEmptyCSharpNet60ProjectInNewFormat() { - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp, TargetFramework.Net60); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp, TargetFramework.Net60); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); + string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should() - .Contain("\r\n \r\n net6.0\r\n enable\r\n enable\r\n \r\n"); + projectFileContent.Should() + .Contain("\r\n \r\n net6.0\r\n enable\r\n enable\r\n \r\n"); + }); } - [Fact] + [SkippableFact] public void CreateEmptyCSharpNet70ProjectInNewFormat() { - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp, TargetFramework.Net70); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp, TargetFramework.Net70); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); + string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should() - .Contain("\r\n \r\n net7.0\r\n enable\r\n enable\r\n \r\n"); + projectFileContent.Should() + .Contain("\r\n \r\n net7.0\r\n enable\r\n enable\r\n \r\n"); + }); } - [Fact] + [SkippableFact] public void CreateEmptyCSharpNet80ProjectInNewFormat() { - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp, TargetFramework.Net80); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp, TargetFramework.Net80); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); + string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should() - .Contain("\r\n \r\n net8.0\r\n enable\r\n enable\r\n \r\n"); + projectFileContent.Should() + .Contain("\r\n \r\n net8.0\r\n enable\r\n enable\r\n \r\n"); + }); } - [Fact] + [SkippableFact] public void CreateEmptyCSharpNet481ProjectInNewFormat() { - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp73, TargetFramework.Net481); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp73, TargetFramework.Net481); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); + string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should() - .Contain("\r\n \r\n net481\r\n 7.3\r\n \r\n"); + projectFileContent.Should() + .Contain("\r\n \r\n net481\r\n 7.3\r\n \r\n"); + }); } - [Fact] + [SkippableFact] public void CreateEmptyCSharpNet462ProjectInNewFormat() { - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp73, TargetFramework.Net462); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp73, TargetFramework.Net462); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); + string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should() - .Contain("\r\n \r\n net462\r\n 7.3\r\n \r\n"); + projectFileContent.Should() + .Contain("\r\n \r\n net462\r\n 7.3\r\n \r\n"); + }); } - [Fact] + [SkippableFact] public void CreateEmptyCSharpNet472ProjectInNewFormat() { - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp73, TargetFramework.Net472); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.CSharp73, TargetFramework.Net472); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); + string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should() - .Contain("\r\n \r\n net472\r\n 7.3\r\n \r\n"); + projectFileContent.Should() + .Contain("\r\n \r\n net472\r\n 7.3\r\n \r\n"); + }); } - [Fact] + [SkippableFact] public void CreateEmptyFSharpProjectInNewFormat() { - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.FSharp); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.FSharp); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should().Contain(""); - projectFileContent.Should().Contain("net462"); - projectFileContent.Should().Contain("\r\n \r\n "); + string projectFileContent = GetProjectFileContent(solutionFolder, project); + projectFileContent.Should().Contain(""); + projectFileContent.Should().Contain("net462"); + projectFileContent.Should().Contain("\r\n \r\n "); + }); } - [Fact] + [SkippableFact] public void CreateEmptyVbProjectInNewFormat() { - var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.VB); + RunSkippableTest(() => + { + var (solution, project, solutionFolder) = CreateEmptySolutionAndProject(ProjectFormat.New, ProgrammingLanguage.VB); - new SolutionWriter(new Mock().Object).WriteToFileSystem(solution, solutionFolder); + CreateSolutionWriter().WriteToFileSystem(solution, solutionFolder); - string projectFileContent = GetProjectFileContent(solutionFolder, project); + string projectFileContent = GetProjectFileContent(solutionFolder, project); - projectFileContent.Should() - .Contain("\r\n \r\n ProjectName\r\n net462\r\n \r\n"); + projectFileContent.Should() + .Contain("\r\n \r\n ProjectName\r\n net462\r\n \r\n"); + }); } } - - } diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/Reqnroll.TestProjectGenerator.Tests.csproj b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/Reqnroll.TestProjectGenerator.Tests.csproj index 12dae46f5..a20db0e0b 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/Reqnroll.TestProjectGenerator.Tests.csproj +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/Reqnroll.TestProjectGenerator.Tests.csproj @@ -1,6 +1,6 @@ - net6.0 + net8.0 false @@ -14,6 +14,7 @@ all runtime; build; native; contentfiles; analyzers + diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/SolutionTests.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/SolutionTests.cs index f1773e3d8..799de72fe 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/SolutionTests.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.Tests/SolutionTests.cs @@ -10,6 +10,8 @@ namespace Reqnroll.TestProjectGenerator.Tests { public class SolutionTests { + private SolutionWriter CreateSolutionWriter() => new SolutionWriter(new Mock().Object); + [Fact] public void CreateEmptySolution() { @@ -17,7 +19,7 @@ public void CreateEmptySolution() var solution = new Solution("SolutionName"); - var solutionWriter = new SolutionWriter(new Mock().Object); + var solutionWriter = CreateSolutionWriter(); solutionWriter.WriteToFileSystem(solution, folder); @@ -39,7 +41,7 @@ public void CreateSolutionWithProject(ProgrammingLanguage programmingLanguage, s solution.AddProject(project); - var solutionWriter = new SolutionWriter(new Mock().Object); + var solutionWriter = CreateSolutionWriter(); solutionWriter.WriteToFileSystem(solution, folder); diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ConfigurationDriver.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ConfigurationDriver.cs index 04182607d..bbb60346e 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ConfigurationDriver.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ConfigurationDriver.cs @@ -1,10 +1,12 @@ using System; using System.Configuration; +using System.IO; namespace Reqnroll.TestProjectGenerator; public class ConfigurationDriver { + public string TempFolderPath => GetConfigSetting("tempFolder", Path.GetTempPath()); public string TestProjectFolderName => GetConfigSetting("testProjectFolder", "RR"); public string VSTestPath => GetConfigSetting("vstestPath", "Common7\\IDE\\CommonExtensions\\Microsoft\\TestWindow"); public string MsBuildPath => GetConfigSetting(nameof(MsBuildPath)); diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ConfigurationModel/Configuration.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ConfigurationModel/Configuration.cs index 9e3414abd..ff316838f 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ConfigurationModel/Configuration.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ConfigurationModel/Configuration.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Globalization; using Reqnroll.TestProjectGenerator.Data; -using Reqnroll.TestProjectGenerator.NewApi._1_Memory; namespace Reqnroll.TestProjectGenerator.ConfigurationModel { diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/AppConfigSection.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/AppConfigSection.cs index b1f01a9ff..0c53ec045 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/AppConfigSection.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/AppConfigSection.cs @@ -1,4 +1,4 @@ -namespace Reqnroll.TestProjectGenerator.NewApi._1_Memory +namespace Reqnroll.TestProjectGenerator.Data { public class AppConfigSection { diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/ProjectFile.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/ProjectFile.cs index 312a06e7b..a8d37693a 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/ProjectFile.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/ProjectFile.cs @@ -4,12 +4,11 @@ namespace Reqnroll.TestProjectGenerator.Data { [DebuggerDisplay("{" + nameof(Path) + ("} [{" + nameof(BuildAction) + "}]"))] - public class ProjectFile :SolutionFile //FeatureFiles, Code, App.Config, NuGet.Config, packages.config, + public class ProjectFile: SolutionFile //FeatureFiles, Code, App.Config, NuGet.Config, packages.config, { public ProjectFile(string path, string buildAction, string content, CopyToOutputDirectory copyToOutputDirectory = CopyToOutputDirectory.DoNotCopy) : this(path, buildAction, content, copyToOutputDirectory, new Dictionary()) { - } public ProjectFile(string path, string buildAction, string content, CopyToOutputDirectory copyToOutputDirectory, IReadOnlyDictionary additionalMsBuildProperties) : base(path, content) diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/SolutionFile.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/SolutionFile.cs index 3be064212..e5bf1615a 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/SolutionFile.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Data/SolutionFile.cs @@ -1,14 +1,30 @@ +using System; + namespace Reqnroll.TestProjectGenerator.Data { - public class SolutionFile + public class SolutionFile(string path, string content) { - public SolutionFile(string path, string content) + private bool _isFrozen = false; + + public string Path { get; } = path; //relative from project + public string Content { get; private set; } = content; + + internal void Freeze() { - Path = path; - Content = content; + _isFrozen = true; } - public string Path { get; } //relative from project - public string Content { get; } + public void Append(string addedContent) + { + if (_isFrozen) + { + throw new InvalidOperationException("Cannot append to frozen file"); + } + + if (!Content.EndsWith(Environment.NewLine)) + Content += Environment.NewLine; + + Content += addedContent; + } } } \ No newline at end of file diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/DotNetSdkNotInstalledException.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/DotNetSdkNotInstalledException.cs new file mode 100644 index 000000000..ffb398484 --- /dev/null +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/DotNetSdkNotInstalledException.cs @@ -0,0 +1,24 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Reqnroll.TestProjectGenerator +{ + /// + /// Is thrown when a needed SDK/TargetFramework is not installed. + /// + /// + /// Is used to ignore tests, if the needed SDKs/TargetFrameworks are not installed on the local machine. + /// This helps to improve the (first time) developer experience. + /// + public class DotNetSdkNotInstalledException : ProjectCreationNotPossibleException + { + public DotNetSdkNotInstalledException(string message) : base(message) + { + } + + public DotNetSdkNotInstalledException(string message, Exception innerException) : base(message, innerException) + { + } + } +} diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/HooksDriver.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/BindingsDriver.cs similarity index 57% rename from Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/HooksDriver.cs rename to Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/BindingsDriver.cs index 749b3df18..fb9bb5ad0 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/HooksDriver.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/BindingsDriver.cs @@ -8,15 +8,8 @@ namespace Reqnroll.TestProjectGenerator.Driver { - public class HooksDriver + public class BindingsDriver(TestProjectFolders _testProjectFolders) { - private readonly TestProjectFolders _testProjectFolders; - - public HooksDriver(TestProjectFolders testProjectFolders) - { - _testProjectFolders = testProjectFolders; - } - public void CheckIsHookExecuted(string methodName, int expectedTimesExecuted) { int hookExecutionCount = GetHookExecutionCount(methodName); @@ -27,13 +20,12 @@ public int GetHookExecutionCount(string methodName) { _testProjectFolders.PathToSolutionDirectory.Should().NotBeNullOrWhiteSpace(); - string pathToHookLogFile = Path.Combine(_testProjectFolders.PathToSolutionDirectory, "steps.log"); - if (!File.Exists(pathToHookLogFile)) + if (!File.Exists(_testProjectFolders.LogFilePath)) { return 0; } - string content = File.ReadAllText(pathToHookLogFile); + string content = File.ReadAllText(_testProjectFolders.LogFilePath); content.Should().NotBeNull(); var regex = new Regex($@"-> hook: {methodName}"); @@ -41,13 +33,15 @@ public int GetHookExecutionCount(string methodName) return regex.Matches(content).Count; } + private string GetLogFileLockPath() => _testProjectFolders.LogFilePath + ".lock"; + public void AcquireHookLock() { _testProjectFolders.PathToSolutionDirectory.Should().NotBeNullOrWhiteSpace(); - var pathToHookLogFile = Path.Combine(_testProjectFolders.PathToSolutionDirectory, "steps.log.lock"); + var pathToHookLogFile = GetLogFileLockPath(); - Directory.CreateDirectory(Path.GetDirectoryName(pathToHookLogFile)); + Directory.CreateDirectory(Path.GetDirectoryName(pathToHookLogFile)!); using (File.Open(pathToHookLogFile, FileMode.CreateNew)) { } @@ -56,10 +50,7 @@ public void AcquireHookLock() public void ReleaseHookLock() { _testProjectFolders.PathToSolutionDirectory.Should().NotBeNullOrWhiteSpace(); - - var pathToHookLogFile = Path.Combine(_testProjectFolders.PathToSolutionDirectory, "steps.log.lock"); - - File.Delete(pathToHookLogFile); + File.Delete(GetLogFileLockPath()); } public async Task WaitForIsWaitingForHookLockAsync(string methodName) @@ -81,14 +72,12 @@ private bool CheckIsWaitingForHookLock(string methodName) { _testProjectFolders.PathToSolutionDirectory.Should().NotBeNullOrWhiteSpace(); - var pathToHookLogFile = Path.Combine(_testProjectFolders.PathToSolutionDirectory, "steps.log"); - - if (!File.Exists(pathToHookLogFile)) + if (!File.Exists(_testProjectFolders.LogFilePath)) { return false; } - var content = File.ReadAllText(pathToHookLogFile); + var content = File.ReadAllText(_testProjectFolders.LogFilePath); content.Should().NotBeNull(); var regex = new Regex($@"-> waiting for hook lock: {methodName}"); @@ -96,12 +85,19 @@ private bool CheckIsWaitingForHookLock(string methodName) return regex.Matches(content).Count == 1; } + public IEnumerable GetActualLogLines(string category) + { + _testProjectFolders.PathToSolutionDirectory.Should().NotBeNullOrWhiteSpace(); + + var lines = File.ReadAllLines(_testProjectFolders.LogFilePath); + return lines.Where(l => l.StartsWith($"-> {category}:")); + } + public void CheckIsNotHookExecuted(string methodName, int timesExecuted) { _testProjectFolders.PathToSolutionDirectory.Should().NotBeNullOrWhiteSpace(); - var pathToHookLogFile = Path.Combine(_testProjectFolders.PathToSolutionDirectory, "steps.log"); - var content = File.ReadAllText(pathToHookLogFile); + var content = File.ReadAllText(_testProjectFolders.LogFilePath); content.Should().NotBeNull(); var regex = new Regex($@"-> hook: {methodName}"); @@ -109,14 +105,40 @@ public void CheckIsNotHookExecuted(string methodName, int timesExecuted) regex.Matches(content).Count.Should().NotBe(timesExecuted); } - public void CheckIsHookExecutedInOrder(IEnumerable methodNames) + private IEnumerable GetActualHookLines() => GetActualLogLines("hook"); + + public void AssertHooksExecutedInOrder(params string[] methodNames) { - _testProjectFolders.PathToSolutionDirectory.Should().NotBeNullOrWhiteSpace(); + var hookLines = GetActualHookLines(); - var pathToHookLogFile = Path.Combine(_testProjectFolders.PathToSolutionDirectory, "steps.log"); - var lines = File.ReadAllLines(pathToHookLogFile); var methodNameLines = methodNames.Select(m => $"-> hook: {m}"); - lines.Should().ContainInOrder(methodNameLines); + hookLines.Should().ContainInOrder(methodNameLines); + } + + public void AssertExecutedHooksEqual(params string[] methodNames) + { + var hookLines = GetActualHookLines(); + + var methodNameLines = methodNames.Select(m => $"-> hook: {m}"); + hookLines.Should().Equal(methodNameLines); + } + + private IEnumerable GetActualStepLines() => GetActualLogLines("step"); + + public void AssertStepsExecutedInOrder(params string[] methodNames) + { + var stepLines = GetActualStepLines(); + + var methodNameLines = methodNames.Select(m => $"-> step: {m}"); + stepLines.Should().ContainInOrder(methodNameLines); + } + + public void AssertExecutedStepsEqual(params string[] methodNames) + { + var stepLines = GetActualStepLines(); + + var methodNameLines = methodNames.Select(m => $"-> step: {m}"); + stepLines.Should().Equal(methodNameLines); } } } diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/CompilationDriver.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/CompilationDriver.cs index 3cf6432e5..8eefe799e 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/CompilationDriver.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/CompilationDriver.cs @@ -1,4 +1,5 @@ using System; +using System.Text.RegularExpressions; using FluentAssertions; namespace Reqnroll.TestProjectGenerator.Driver @@ -36,8 +37,13 @@ public void CompileSolutionTimes(uint times, BuildTool buildTool = DefaultBuildT for (uint time = 0; time < times; time++) { _compilationResultDriver.CompileResult = _compiler.Run(usedBuildTool, treatWarningsAsErrors); - if (failOnError) + if (failOnError && !_compilationResultDriver.CompileResult.IsSuccessful) + { + var missingSdk = Regex.Match(_compilationResultDriver.CompileResult.Output, @"(MSB3644: .* not found\.)"); + if (missingSdk.Success) + throw new DotNetSdkNotInstalledException(missingSdk.Value); _compilationResultDriver.CompileResult.IsSuccessful.Should().BeTrue($"Compilation should succeed. Build errors: {Environment.NewLine}{_compilationResultDriver.CompileResult.ErrorLines}"); + } } } diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/ConfigurationDriver.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/ConfigurationFileDriver.cs similarity index 97% rename from Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/ConfigurationDriver.cs rename to Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/ConfigurationFileDriver.cs index 74115448d..9272f1ccc 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/ConfigurationDriver.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/ConfigurationFileDriver.cs @@ -4,11 +4,11 @@ namespace Reqnroll.TestProjectGenerator.Driver { - public class ConfigurationDriver + public class ConfigurationFileDriver { private readonly SolutionDriver _solutionDriver; - public ConfigurationDriver(SolutionDriver solutionDriver) + public ConfigurationFileDriver(SolutionDriver solutionDriver) { _solutionDriver = solutionDriver; } diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/JsonConfigurationLoaderDriver.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/JsonConfigurationLoaderDriver.cs index 5b97b7b85..f8b6799c6 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/JsonConfigurationLoaderDriver.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/JsonConfigurationLoaderDriver.cs @@ -5,17 +5,17 @@ namespace Reqnroll.TestProjectGenerator.Driver public class JsonConfigurationLoaderDriver { private readonly ProjectsDriver _projectsDriver; - private readonly ConfigurationDriver _configurationDriver; + private readonly ConfigurationFileDriver _configurationFileDriver; - public JsonConfigurationLoaderDriver(ProjectsDriver projectsDriver, ConfigurationDriver configurationDriver) + public JsonConfigurationLoaderDriver(ProjectsDriver projectsDriver, ConfigurationFileDriver configurationFileDriver) { _projectsDriver = projectsDriver; - _configurationDriver = configurationDriver; + _configurationFileDriver = configurationFileDriver; } public void AddReqnrollJson(string reqnrollJson) { - _configurationDriver.SetConfigurationFormat(ConfigurationFormat.None); + _configurationFileDriver.SetConfigurationFormat(ConfigurationFormat.None); _projectsDriver.AddFile("reqnroll.json", reqnrollJson); } } diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/ProjectsDriver.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/ProjectsDriver.cs index 07f1680eb..9eee89bd3 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/ProjectsDriver.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/ProjectsDriver.cs @@ -12,6 +12,7 @@ public class ProjectsDriver private readonly SolutionDriver _solutionDriver; private readonly ProjectBuilderFactory _projectBuilderFactory; private readonly TestProjectFolders _testProjectFolders; + public ProjectFile LastFeatureFile { get; private set; } public ProjectsDriver(SolutionDriver solutionDriver, ProjectBuilderFactory projectBuilderFactory, TestProjectFolders testProjectFolders) { @@ -59,9 +60,9 @@ public void AddHookBinding(string eventType, string name, string hookTypeAttribu AddHookBinding(_solutionDriver.DefaultProject, eventType, name, code, order, hookTypeAttributeTags, methodScopeAttributeTags, classScopeAttributeTags); } - public void AddHookBinding(string eventType, string name, string code = "", int? order = null, IList hookTypeAttributeTags = null, IList methodScopeAttributeTags = null, IList classScopeAttributeTags = null) + public void AddHookBinding(string eventType, string name = null, string code = "", int? order = null, IList hookTypeAttributeTags = null, IList methodScopeAttributeTags = null, IList classScopeAttributeTags = null) { - AddHookBinding(_solutionDriver.DefaultProject, eventType, name, code, order, hookTypeAttributeTags, methodScopeAttributeTags, classScopeAttributeTags); + AddHookBinding(_solutionDriver.DefaultProject, eventType, name ?? eventType, code, order, hookTypeAttributeTags, methodScopeAttributeTags, classScopeAttributeTags); } private void AddHookBinding(ProjectBuilder project, string eventType, string name, string code = "", int? order = null, IList hookTypeAttributeTags = null, IList methodScopeAttributeTags = null, IList classScopeAttributeTags = null) @@ -81,27 +82,34 @@ private void AddAsyncHookBindingIncludingLocking(ProjectBuilder project, string public void AddFeatureFile(string featureFileContent) { - _solutionDriver.DefaultProject.AddFeatureFile(featureFileContent); + LastFeatureFile = _solutionDriver.DefaultProject.AddFeatureFile(featureFileContent); } public void AddScenario(string scenarioContent) { - AddFeatureFile( - $$""" - Feature: Sample Feature - - {{scenarioContent}} - """); + if (LastFeatureFile != null) + { + LastFeatureFile.Append(scenarioContent); + } + else + { + AddFeatureFile( + $$""" + Feature: Sample Feature + + {{scenarioContent}} + """); + } } - public void AddStepBinding(string attributeName, string regex, string csharpcode, string vbnetcode) + public void AddStepBinding(string attributeName, string regex, string csharpcode, string vbnetcode = null) { _solutionDriver.DefaultProject.AddStepBinding(attributeName, regex, csharpcode, vbnetcode); } public void AddLoggingStepBinding(string attributeName, string methodName, string regex) { - _solutionDriver.DefaultProject.AddLoggingStepBinding(attributeName, methodName, Path.Combine(_testProjectFolders.PathToSolutionDirectory, "steps.log"), regex); + _solutionDriver.DefaultProject.AddLoggingStepBinding(attributeName, methodName, regex); } public void AddStepBinding(string projectName, string bindingCode) => AddStepBinding(_solutionDriver.Projects[projectName], bindingCode); @@ -157,7 +165,7 @@ public void AddNuGetPackage(string nugetPackage, string nugetVersion) _solutionDriver.DefaultProject.AddNuGetPackage(nugetPackage, nugetVersion); } - public void AddFailingStepBinding(string scenarioBlock, string stepRegex) + public void AddFailingStepBinding(string scenarioBlock = "StepDefinition", string stepRegex = ".*") { AddStepBinding(scenarioBlock, stepRegex, @"throw new System.Exception(""simulated failure"");", @"Throw New System.Exception(""simulated failure"")"); } diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/TestSuiteSetupDriver.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/TestSuiteSetupDriver.cs index 63f60f2f8..6ea0185af 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/TestSuiteSetupDriver.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Driver/TestSuiteSetupDriver.cs @@ -9,16 +9,16 @@ public class TestSuiteSetupDriver private readonly ProjectsDriver _projectsDriver; private readonly TestSuiteInitializationDriver _testSuiteInitializationDriver; private readonly JsonConfigurationLoaderDriver _jsonConfigurationLoaderDriver; - private readonly ConfigurationDriver _configurationDriver; + private readonly ConfigurationFileDriver _configurationFileDriver; private bool _isProjectCreated; public TestSuiteSetupDriver(ProjectsDriver projectsDriver, TestSuiteInitializationDriver testSuiteInitializationDriver, JsonConfigurationLoaderDriver jsonConfigurationLoaderDriver, - ConfigurationDriver configurationDriver) + ConfigurationFileDriver configurationFileDriver) { _projectsDriver = projectsDriver; _testSuiteInitializationDriver = testSuiteInitializationDriver; _jsonConfigurationLoaderDriver = jsonConfigurationLoaderDriver; - _configurationDriver = configurationDriver; + _configurationFileDriver = configurationFileDriver; } public void AddGenericWhenStepBinding() @@ -136,7 +136,7 @@ public void AddScenarioWithGivenStep(string step, string tags = "") public void AddAppConfigFromString(string appConfigContent) { - _configurationDriver.SetConfigurationFormat(ConfigurationFormat.None); + _configurationFileDriver.SetConfigurationFormat(ConfigurationFormat.None); _projectsDriver.AddFile("app.config", appConfigContent); } } diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/BaseBindingsGenerator.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/BaseBindingsGenerator.cs index 646145720..d799d3426 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/BaseBindingsGenerator.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/BaseBindingsGenerator.cs @@ -16,7 +16,7 @@ public ProjectFile GenerateStepDefinition(string methodName, string methodImplem return GenerateStepDefinition(method); } - public ProjectFile GenerateLoggingStepDefinition(string methodName, string pathToLogFile, string attributeName, string regex, ParameterType parameterType = ParameterType.Normal, string argumentName = null) + public ProjectFile GenerateLoggingStepDefinition(string methodName, string attributeName, string regex, ParameterType parameterType = ParameterType.Normal, string argumentName = null) { string method = GetLoggingStepDefinitionCode(methodName, attributeName, regex, parameterType, argumentName); return GenerateStepDefinition(method); diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/CSharp10BindingsGenerator.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/CSharp10BindingsGenerator.cs index f8cfd23dd..2f7809437 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/CSharp10BindingsGenerator.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/CSharp10BindingsGenerator.cs @@ -44,6 +44,11 @@ internal static void LogHook([CallerMemberName] string stepName = null!) Retry(5, () => WriteToFile($@"-> hook: {stepName}{Environment.NewLine}")); } + internal static void LogCustom(string category, string value, [CallerMemberName] string memberName = null) + { + Retry(5, () => WriteToFile($@"-> {category}: {value}:{memberName}{Environment.NewLine}")); + } + static void WriteToFile(string line) { using (FileStream fs = File.Open(LogFileLocation, FileMode.Append, FileAccess.Write, FileShare.None)) diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/CSharpBindingsGenerator.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/CSharpBindingsGenerator.cs index fd0a17861..40c0229a6 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/CSharpBindingsGenerator.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/BindingsGenerator/CSharpBindingsGenerator.cs @@ -109,6 +109,11 @@ internal static async Task LogHookIncludingLockingAsync([CallerMemberName] strin WriteToFile($@"-> hook: {stepName}{Environment.NewLine}"); } + internal static void LogCustom(string category, string value, [CallerMemberName] string memberName = null) + { + Retry(5, () => WriteToFile($@"-> {category}: {value}:{memberName}{Environment.NewLine}")); + } + static void WriteToFile(string line) { using (FileStream fs = File.Open(LogFileLocation, FileMode.Append, FileAccess.Write, FileShare.None)) diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/ConfigurationGenerator/AppConfigGenerator.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/ConfigurationGenerator/AppConfigGenerator.cs index f7a0329eb..1e790efba 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/ConfigurationGenerator/AppConfigGenerator.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/ConfigurationGenerator/AppConfigGenerator.cs @@ -9,7 +9,6 @@ using Reqnroll.TestProjectGenerator.Data; using Reqnroll.TestProjectGenerator.Extensions; using Reqnroll.TestProjectGenerator.Helpers; -using Reqnroll.TestProjectGenerator.NewApi._1_Memory; namespace Reqnroll.TestProjectGenerator.Factories.ConfigurationGenerator { diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/ProjectBuilderFactory.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/ProjectBuilderFactory.cs index 54437d9ec..500ac8098 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/ProjectBuilderFactory.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Factories/ProjectBuilderFactory.cs @@ -3,7 +3,6 @@ using Reqnroll.TestProjectGenerator.Factories.BindingsGenerator; using Reqnroll.TestProjectGenerator.Factories.ConfigurationGenerator; using Reqnroll.TestProjectGenerator.Helpers; -using Reqnroll.TestProjectGenerator.NewApi._1_Memory; namespace Reqnroll.TestProjectGenerator.Factories { diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FeatureFileGenerator.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FeatureFileGenerator.cs index 8e17c551f..984f99e52 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FeatureFileGenerator.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FeatureFileGenerator.cs @@ -1,17 +1,15 @@ using System; using Reqnroll.TestProjectGenerator.Data; -namespace Reqnroll.TestProjectGenerator.NewApi._1_Memory +namespace Reqnroll.TestProjectGenerator; + +public class FeatureFileGenerator { - public class FeatureFileGenerator + public ProjectFile Generate(string featureFileContent, string featureFileName = null) { - public ProjectFile Generate(string featureFileContent, string featureFileName = null) - { - featureFileName = featureFileName ?? $"FeatureFile{Guid.NewGuid():N}.feature"; - + featureFileName = featureFileName ?? $"FeatureFile{Guid.NewGuid():N}.feature"; - string fileContent = featureFileContent.Replace("'''", "\"\"\""); - return new ProjectFile(featureFileName, "None", fileContent); - } + string fileContent = featureFileContent.Replace("'''", "\"\"\""); + return new ProjectFile(featureFileName, "None", fileContent); } -} +} \ No newline at end of file diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FilesystemWriter/FileWriter.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FilesystemWriter/FileWriter.cs index b2ea4c261..bcdab71ec 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FilesystemWriter/FileWriter.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FilesystemWriter/FileWriter.cs @@ -26,6 +26,8 @@ public void Write(SolutionFile projectFile, string projectRootPath) { File.WriteAllText(absolutePath, projectFile.Content); } + + projectFile.Freeze(); } } } diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FilesystemWriter/SolutionWriter.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FilesystemWriter/SolutionWriter.cs index 34e646b4c..ad0932a43 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FilesystemWriter/SolutionWriter.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/FilesystemWriter/SolutionWriter.cs @@ -54,8 +54,12 @@ public string WriteToFileSystem(Solution solution, string outputPath) DisableUsingSdkFromEnvironmentVariable(); var createSolutionCommand = DotNet.New(_outputWriter).Solution().InFolder(outputPath).WithName(solution.Name).Build(); - createSolutionCommand.ExecuteWithRetry(1, TimeSpan.FromSeconds(1), - (innerException) => new ProjectCreationNotPossibleException("Could not create solution.", innerException) ); + createSolutionCommand.ExecuteWithRetry(1, TimeSpan.FromSeconds(1), (innerException) => + { + if (innerException is AggregateException aggregateException && aggregateException.InnerExceptions.Any(x => x.InnerException.Message.Contains("Install the [" + sdk.Version))) + return new DotNetSdkNotInstalledException($"Sdk Version \"{sdk.Version}\" is not installed", innerException); + return new ProjectCreationNotPossibleException("Could not create solution.", innerException); + }); string solutionFilePath = Path.Combine(outputPath, $"{solution.Name}.sln"); WriteProjects(solution, outputPath, solutionFilePath); diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Folders.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Folders.cs index ab12317cd..b0feb2c42 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Folders.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Folders.cs @@ -69,7 +69,9 @@ public string PackageFolder set => _packageFolder = value; } - public string SystemGlobalNuGetPackages => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); + public string SystemGlobalNuGetPackages => + Environment.GetEnvironmentVariable("NUGET_PACKAGES") ?? + Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".nuget", "packages"); public string Reqnroll { @@ -77,7 +79,7 @@ public string Reqnroll set => _reqnroll = value; } - public virtual string FolderToSaveGeneratedSolutions => Path.Combine(Path.GetTempPath(), _configurationDriver.TestProjectFolderName); + public virtual string FolderToSaveGeneratedSolutions => Path.Combine(_configurationDriver.TempFolderPath, _configurationDriver.TestProjectFolderName); public virtual string RunUniqueFolderToSaveGeneratedSolutions => Path.Combine(FolderToSaveGeneratedSolutions, _artifactNamingConvention.GetRunName(UniqueRunId)); public virtual string GlobalNuGetPackages => _configurationDriver.PipelineMode ? SystemGlobalNuGetPackages : _configurationDriver.GlobalNuGetPackages ?? Path.Combine(RunUniqueFolderToSaveGeneratedSolutions, ".nuget"); diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ProjectBuilder.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ProjectBuilder.cs index 48c1e3ea7..92f716480 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ProjectBuilder.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/ProjectBuilder.cs @@ -6,7 +6,6 @@ using Reqnroll.TestProjectGenerator.Driver; using Reqnroll.TestProjectGenerator.Factories.BindingsGenerator; using Reqnroll.TestProjectGenerator.Factories.ConfigurationGenerator; -using Reqnroll.TestProjectGenerator.NewApi._1_Memory; namespace Reqnroll.TestProjectGenerator { @@ -17,7 +16,7 @@ public class ProjectBuilder public const string NUnit3TestAdapterPackageName = "NUnit3TestAdapter"; public const string NUnit3TestAdapterPackageVersion = "3.17.0"; private const string XUnitPackageVersion = "2.4.2"; - private const string MSTestPackageVersion = "2.1.2"; + private const string MSTestPackageVersion = "2.2.8"; private const string InternalJsonPackageName = "SpecFlow.Internal.Json"; private const string InternalJsonVersion = "1.0.8"; private readonly BindingsGeneratorFactory _bindingsGeneratorFactory; @@ -79,13 +78,14 @@ public void AddFile(ProjectFile projectFile) _project.AddFile(projectFile ?? throw new ArgumentNullException(nameof(projectFile))); } - public void AddFeatureFile(string featureFileContent) + public ProjectFile AddFeatureFile(string featureFileContent) { EnsureProjectExists(); var featureFile = _featureFileGenerator.Generate(featureFileContent); _project.AddFile(featureFile); + return featureFile; } public void AddStepBinding(string attributeName, string regex, string csharpcode, string vbnetcode) @@ -100,15 +100,15 @@ public void AddStepBinding(string attributeName, string regex, string csharpcode _project.AddFile(bindingsGenerator.GenerateStepDefinition("StepBinding", methodImplementation, attributeName, regex, ParameterType.DocString, "docStringArg")); } - public void AddLoggingStepBinding(string attributeName, string methodName, string pathToLogFile, string regex) + public void AddLoggingStepBinding(string attributeName, string methodName, string regex) { EnsureProjectExists(); var bindingsGenerator = _bindingsGeneratorFactory.FromLanguage(_project.ProgrammingLanguage); - _project.AddFile(bindingsGenerator.GenerateLoggingStepDefinition(methodName, pathToLogFile, attributeName, regex)); - _project.AddFile(bindingsGenerator.GenerateLoggingStepDefinition(methodName, pathToLogFile, attributeName, regex, ParameterType.Table, "tableArg")); - _project.AddFile(bindingsGenerator.GenerateLoggingStepDefinition(methodName, pathToLogFile, attributeName, regex, ParameterType.DocString, "docStringArg")); + _project.AddFile(bindingsGenerator.GenerateLoggingStepDefinition(methodName, attributeName, regex)); + _project.AddFile(bindingsGenerator.GenerateLoggingStepDefinition(methodName, attributeName, regex, ParameterType.Table, "tableArg")); + _project.AddFile(bindingsGenerator.GenerateLoggingStepDefinition(methodName, attributeName, regex, ParameterType.DocString, "docStringArg")); } public void AddHookBinding(string eventType, string name, string code = "", int? order = null, IList hookTypeAttributeTags = null, IList methodScopeAttributeTags = null, @@ -245,7 +245,7 @@ private void EnsureProjectExists() _project.AddNuGetPackage("Microsoft.Bcl.AsyncInterfaces", "6.0.0", new NuGetPackageAssembly("Microsoft.Bcl.AsyncInterfaces", "netstandard2.0\\Microsoft.Bcl.AsyncInterfaces.dll")); var generator = _bindingsGeneratorFactory.FromLanguage(_project.ProgrammingLanguage); - _project.AddFile(generator.GenerateLoggerClass(Path.Combine(_testProjectFolders.PathToSolutionDirectory, "steps.log"))); + _project.AddFile(generator.GenerateLoggerClass(_testProjectFolders.LogFilePath)); switch (_project.ProgrammingLanguage) { diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.csproj b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.csproj index 9b6e8fcaf..66a584098 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.csproj +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator.csproj @@ -21,13 +21,6 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive - - - - - - - diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TRXParser.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TRXParser.cs index d4f19e435..44a7648f1 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TRXParser.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TRXParser.cs @@ -155,6 +155,7 @@ private List GetTestResultsInternal(XElement testRunResultsElement, var testResults = from unitTestResultElement in testRunResultsElement?.Elements(_unitTestResultElementName) ?? Enumerable.Empty() let outputElement = unitTestResultElement.Element(_unitTestResultOutputElementName) let idAttribute = unitTestResultElement.Attribute("executionId") + let testNameAttribute = unitTestResultElement.Attribute("testName") let outcomeAttribute = unitTestResultElement.Attribute("outcome") let stdOutElement = outputElement?.Element(_unitTestResultStdOutElementName) let errorInfoElement = outputElement?.Element(xmlns + "ErrorInfo") @@ -165,6 +166,7 @@ private List GetTestResultsInternal(XElement testRunResultsElement, select new TestResult { Id = idAttribute.Value, + TestName = testNameAttribute.Value, Outcome = outcomeAttribute.Value, StdOut = stdOutElement?.Value, ErrorMessage = errorMessage?.Value, diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TestExecutionResult.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TestExecutionResult.cs index 4385aa41c..e8e8522f0 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TestExecutionResult.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TestExecutionResult.cs @@ -32,7 +32,7 @@ public class TestResult public string Id { get; set; } public string Outcome { get; set; } public string StdOut { get; set; } - public string Title { get; set; } + public string TestName { get; set; } public string Feature { get; set; } public int ScheduleOrder { get; set; } public List Steps { get; set; } diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TestProjectFolders.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TestProjectFolders.cs index 61f07fae6..addc8d75f 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TestProjectFolders.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/TestProjectFolders.cs @@ -12,6 +12,7 @@ public class TestProjectFolders public string CompiledAssemblyPath { get; set; } public string PathToSolutionDirectory => Path.GetDirectoryName(PathToSolutionFile); public string TestAssemblyFileName { get; set; } + public string LogFilePath => Path.Combine(PathToSolutionDirectory, "steps.log"); public string PathToSolutionFile { diff --git a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/VSTestExecutionDriver.cs b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/VSTestExecutionDriver.cs index f53a95208..c1edcc62b 100644 --- a/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/VSTestExecutionDriver.cs +++ b/Reqnroll.TestProjectGenerator/Reqnroll.TestProjectGenerator/VSTestExecutionDriver.cs @@ -53,10 +53,9 @@ public VSTestExecutionDriver( public void CheckIsBindingMethodExecuted(string methodName, int timesExecuted) { - string pathToLogFile = Path.Combine(_testProjectFolders.PathToSolutionDirectory, "steps.log"); - string logFileContent = File.ReadAllText(pathToLogFile, Encoding.UTF8); + string logFileContent = File.ReadAllText(_testProjectFolders.LogFilePath, Encoding.UTF8); - var regex = new Regex($@"-> step: {methodName}"); + var regex = new Regex($"-> step: {methodName}"); regex.Match(logFileContent).Success.Should().BeTrue($"method {methodName} was not executed."); regex.Matches(logFileContent).Count.Should().Be(timesExecuted); diff --git a/Reqnroll.Tools.MsBuild.Generation/Reqnroll.Tools.MsBuild.Generation.csproj b/Reqnroll.Tools.MsBuild.Generation/Reqnroll.Tools.MsBuild.Generation.csproj index 1c06a1bf3..fab42b65a 100644 --- a/Reqnroll.Tools.MsBuild.Generation/Reqnroll.Tools.MsBuild.Generation.csproj +++ b/Reqnroll.Tools.MsBuild.Generation/Reqnroll.Tools.MsBuild.Generation.csproj @@ -1,6 +1,6 @@ - + - $(Reqnroll_Tools_TFM) + $(Reqnroll_FullFramework_Tools_TFM);$(Reqnroll_Core_Tools_TFM) $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) $(Reqnroll_PublicSign) @@ -56,19 +56,6 @@ - - - - - - - - - - - - - Microsoft.Build @@ -91,4 +78,19 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Reqnroll.Tools.MsBuild.Generation/Reqnroll.Tools.MsBuild.Generation.nuspec b/Reqnroll.Tools.MsBuild.Generation/Reqnroll.Tools.MsBuild.Generation.nuspec index 562aabac5..b16bb720d 100644 --- a/Reqnroll.Tools.MsBuild.Generation/Reqnroll.Tools.MsBuild.Generation.nuspec +++ b/Reqnroll.Tools.MsBuild.Generation/Reqnroll.Tools.MsBuild.Generation.nuspec @@ -22,13 +22,13 @@ - - - - - - + + + + diff --git a/Reqnroll.Tools.MsBuild.Generation/build/Reqnroll.Tools.MsBuild.Generation.props b/Reqnroll.Tools.MsBuild.Generation/build/Reqnroll.Tools.MsBuild.Generation.props index b8b91dca9..7569d70ad 100644 --- a/Reqnroll.Tools.MsBuild.Generation/build/Reqnroll.Tools.MsBuild.Generation.props +++ b/Reqnroll.Tools.MsBuild.Generation/build/Reqnroll.Tools.MsBuild.Generation.props @@ -67,7 +67,7 @@ - <_Reqnroll_TaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' And '$(_Reqnroll_TaskFolder)' == ''">netcoreapp3.1 + <_Reqnroll_TaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' And '$(_Reqnroll_TaskFolder)' == ''">netstandard2.0 <_Reqnroll_TaskFolder Condition=" '$(MSBuildRuntimeType)' != 'Core' And '$(_Reqnroll_TaskFolder)' == ''">net462 <_Reqnroll_TaskAssembly Condition=" '$(_Reqnroll_TaskAssembly)' == '' ">..\tasks\$(_Reqnroll_TaskFolder)\Reqnroll.Tools.MsBuild.Generation.dll diff --git a/Reqnroll.sln b/Reqnroll.sln index 0b7748ae1..6aac1cdec 100644 --- a/Reqnroll.sln +++ b/Reqnroll.sln @@ -10,17 +10,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject .editorconfig = .editorconfig .gitignore = .gitignore - build.ps1 = build.ps1 CHANGELOG.md = CHANGELOG.md - clean.ps1 = clean.ps1 CommonAssemblyInfo.cs = CommonAssemblyInfo.cs - global.json = global.json LICENSE = LICENSE nuget.config = nuget.config README.md = README.md reqnroll-config.json = reqnroll-config.json Reqnroll.sln.DotSettings = Reqnroll.sln.DotSettings - version.json = version.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "UnitTests", "UnitTests", "{0359B7D7-7E29-48E9-8DF9-7D1FACFA5CFA}" @@ -30,7 +26,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{D87E7021 Directory.Build.props = Directory.Build.props Directory.Build.rsp = Directory.Build.rsp Directory.Build.targets = Directory.Build.targets - Build\Versioning.targets = Build\Versioning.targets EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.Specs", "Tests\Reqnroll.Specs\Reqnroll.Specs.csproj", "{2D7D31B3-E8D0-445A-BB24-216ABA3CB778}" @@ -67,8 +62,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{8BE0 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.xUnit.ReqnrollPlugin", "Plugins\Reqnroll.xUnit.ReqnrollPlugin\Reqnroll.xUnit.ReqnrollPlugin.csproj", "{D649B28A-352C-4CB0-B6F2-0570DD181F60}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.Specs.Generator.ReqnrollPlugin", "Tests\Reqnroll.Specs.Generator.ReqnrollPlugin\Reqnroll.Specs.Generator.ReqnrollPlugin.csproj", "{03A229D6-BB43-47CC-AAEF-B7265652AD5A}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.NUnit.ReqnrollPlugin", "Plugins\Reqnroll.NUnit.ReqnrollPlugin\Reqnroll.NUnit.ReqnrollPlugin.csproj", "{D151832C-15D6-4869-9999-B245443BC23E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.MSTest.ReqnrollPlugin", "Plugins\Reqnroll.MSTest.ReqnrollPlugin\Reqnroll.MSTest.ReqnrollPlugin.csproj", "{EC77355C-D442-416A-AE67-E42D5FAD0FB9}" @@ -83,14 +76,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.PluginTests", "Tes EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.CustomPlugin", "Installer\Reqnroll.CustomPlugin\Reqnroll.CustomPlugin.csproj", "{FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.MsBuildNetSdk.IntegrationTests", "Tests\Reqnroll.MsBuildNetSdk.IntegrationTests\Reqnroll.MsBuildNetSdk.IntegrationTests.csproj", "{02A08F81-B90F-4EB3-9C30-CE7447DE2012}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AzurePipelines", "AzurePipelines", "{48E162BC-9431-450D-8353-66D396DCB5FE}" - ProjectSection(SolutionItems) = preProject - azure-pipelines.yml = azure-pipelines.yml - build.yml = build.yml - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.Autofac.ReqnrollPlugin", "Plugins\Reqnroll.Autofac.ReqnrollPlugin\Reqnroll.Autofac.ReqnrollPlugin.csproj", "{EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.Templates.DotNet", "Templates\Reqnroll.Templates.DotNet\Reqnroll.Templates.DotNet.csproj", "{B7F589EB-524F-4BAD-9419-6576F6527670}" @@ -103,11 +88,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ExternalData", "ExternalDat EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.ExternalData.ReqnrollPlugin", "Plugins\Reqnroll.ExternalData\Reqnroll.ExternalData.ReqnrollPlugin\Reqnroll.ExternalData.ReqnrollPlugin.csproj", "{AF7CACE6-1F26-454D-A814-19CFCF7C5810}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.ExternalData.ReqnrollPlugin.UnitTests", "Plugins\Reqnroll.ExternalData\Reqnroll.ExternalData.ReqnrollPlugin.UnitTests\Reqnroll.ExternalData.ReqnrollPlugin.UnitTests.csproj", "{C3B1DA26-4F12-443E-9FBA-898393520CE7}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Specs", "Plugins\Reqnroll.ExternalData\sample\ExternalDataSample\Specs.csproj", "{C681C731-49F2-4C93-A730-467251741457}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sample", "Sample", "{35D24F50-A29E-428A-8649-275C73C1E646}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest", "Plugins\Reqnroll.ExternalData\Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest\Reqnroll.ExternalData.ReqnrollPlugin.IntegrationTest.csproj", "{C681C731-49F2-4C93-A730-467251741457}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Verify", "Verify", "{229D5199-8A48-4174-AE8F-0B4C91170751}" EndProject @@ -129,8 +110,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHubWorkflows", "GitHubWo .github\workflows\lock.yml = .github\workflows\lock.yml EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.BoDi", "Reqnroll.BoDi\Reqnroll.BoDi.csproj", "{8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.SystemTests", "Tests\Reqnroll.SystemTests\Reqnroll.SystemTests.csproj", "{C658B37D-FD36-4868-9070-4EB452FAE526}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Reqnroll.Microsoft.Extensions.DependencyInjection.ReqnrollPlugin", "Plugins\Reqnroll.Microsoft.Extensions.DependencyInjection.ReqnrollPlugin\Reqnroll.Microsoft.Extensions.DependencyInjection.ReqnrollPlugin.csproj", "{6CBEB31D-5F98-460F-B193-578AEEA78CF9}" @@ -138,421 +117,129 @@ EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU - Debug-MSTest|Any CPU = Debug-MSTest|Any CPU - Debug-NUnit|Any CPU = Debug-NUnit|Any CPU - Debug-XUnit|Any CPU = Debug-XUnit|Any CPU Release|Any CPU = Release|Any CPU - VS2010IntegrationTest|Any CPU = VS2010IntegrationTest|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.Debug-MSTest|Any CPU.ActiveCfg = Debug-MSTest|Any CPU - {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.Debug-MSTest|Any CPU.Build.0 = Debug-MSTest|Any CPU - {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.Debug-NUnit|Any CPU.ActiveCfg = Debug-NUnit|Any CPU - {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.Debug-NUnit|Any CPU.Build.0 = Debug-NUnit|Any CPU - {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.Debug-XUnit|Any CPU.ActiveCfg = Debug-XUnit|Any CPU - {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.Debug-XUnit|Any CPU.Build.0 = Debug-XUnit|Any CPU {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.Release|Any CPU.ActiveCfg = Release|Any CPU {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.Release|Any CPU.Build.0 = Release|Any CPU - {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {2D7D31B3-E8D0-445A-BB24-216ABA3CB778}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.Release|Any CPU.ActiveCfg = Release|Any CPU {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.Release|Any CPU.Build.0 = Release|Any CPU - {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {48E0CB65-33D4-49CD-9E22-1AD49D8684B0}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {7E637287-6E34-4BEC-8226-B052062AEA1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {7E637287-6E34-4BEC-8226-B052062AEA1F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7E637287-6E34-4BEC-8226-B052062AEA1F}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {7E637287-6E34-4BEC-8226-B052062AEA1F}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {7E637287-6E34-4BEC-8226-B052062AEA1F}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {7E637287-6E34-4BEC-8226-B052062AEA1F}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {7E637287-6E34-4BEC-8226-B052062AEA1F}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {7E637287-6E34-4BEC-8226-B052062AEA1F}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {7E637287-6E34-4BEC-8226-B052062AEA1F}.Release|Any CPU.ActiveCfg = Release|Any CPU {7E637287-6E34-4BEC-8226-B052062AEA1F}.Release|Any CPU.Build.0 = Release|Any CPU - {7E637287-6E34-4BEC-8226-B052062AEA1F}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {7E637287-6E34-4BEC-8226-B052062AEA1F}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.Debug|Any CPU.Build.0 = Debug|Any CPU - {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.Release|Any CPU.ActiveCfg = Release|Any CPU {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.Release|Any CPU.Build.0 = Release|Any CPU - {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {ABA918DA-C436-4EE2-B23D-FD0B24403B27}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.Release|Any CPU.ActiveCfg = Release|Any CPU {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.Release|Any CPU.Build.0 = Release|Any CPU - {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {413EE28C-4F89-4C6F-BA1E-2CDEE4CD43B4}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {F0A50949-8FE1-4048-9A3E-BB0498843669}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F0A50949-8FE1-4048-9A3E-BB0498843669}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F0A50949-8FE1-4048-9A3E-BB0498843669}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {F0A50949-8FE1-4048-9A3E-BB0498843669}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {F0A50949-8FE1-4048-9A3E-BB0498843669}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {F0A50949-8FE1-4048-9A3E-BB0498843669}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {F0A50949-8FE1-4048-9A3E-BB0498843669}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {F0A50949-8FE1-4048-9A3E-BB0498843669}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {F0A50949-8FE1-4048-9A3E-BB0498843669}.Release|Any CPU.ActiveCfg = Release|Any CPU {F0A50949-8FE1-4048-9A3E-BB0498843669}.Release|Any CPU.Build.0 = Release|Any CPU - {F0A50949-8FE1-4048-9A3E-BB0498843669}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {F0A50949-8FE1-4048-9A3E-BB0498843669}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {D719B8EA-6C89-4B81-AABD-65ACF562F521}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D719B8EA-6C89-4B81-AABD-65ACF562F521}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D719B8EA-6C89-4B81-AABD-65ACF562F521}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {D719B8EA-6C89-4B81-AABD-65ACF562F521}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {D719B8EA-6C89-4B81-AABD-65ACF562F521}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {D719B8EA-6C89-4B81-AABD-65ACF562F521}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {D719B8EA-6C89-4B81-AABD-65ACF562F521}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {D719B8EA-6C89-4B81-AABD-65ACF562F521}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {D719B8EA-6C89-4B81-AABD-65ACF562F521}.Release|Any CPU.ActiveCfg = Release|Any CPU {D719B8EA-6C89-4B81-AABD-65ACF562F521}.Release|Any CPU.Build.0 = Release|Any CPU - {D719B8EA-6C89-4B81-AABD-65ACF562F521}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {D719B8EA-6C89-4B81-AABD-65ACF562F521}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.Release|Any CPU.ActiveCfg = Release|Any CPU {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.Release|Any CPU.Build.0 = Release|Any CPU - {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {FA04B445-D04C-4075-9EB0-4D0A70E47FC4}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.Release|Any CPU.ActiveCfg = Release|Any CPU {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.Release|Any CPU.Build.0 = Release|Any CPU - {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {AE2FCDBB-0933-4751-BF17-8D9BF9B2055A}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {1B51F6C9-226E-479B-9212-E6C02A1E6633}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {1B51F6C9-226E-479B-9212-E6C02A1E6633}.Debug|Any CPU.Build.0 = Debug|Any CPU - {1B51F6C9-226E-479B-9212-E6C02A1E6633}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {1B51F6C9-226E-479B-9212-E6C02A1E6633}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {1B51F6C9-226E-479B-9212-E6C02A1E6633}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {1B51F6C9-226E-479B-9212-E6C02A1E6633}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {1B51F6C9-226E-479B-9212-E6C02A1E6633}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {1B51F6C9-226E-479B-9212-E6C02A1E6633}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {1B51F6C9-226E-479B-9212-E6C02A1E6633}.Release|Any CPU.ActiveCfg = Release|Any CPU {1B51F6C9-226E-479B-9212-E6C02A1E6633}.Release|Any CPU.Build.0 = Release|Any CPU - {1B51F6C9-226E-479B-9212-E6C02A1E6633}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {1B51F6C9-226E-479B-9212-E6C02A1E6633}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {D649B28A-352C-4CB0-B6F2-0570DD181F60}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D649B28A-352C-4CB0-B6F2-0570DD181F60}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D649B28A-352C-4CB0-B6F2-0570DD181F60}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {D649B28A-352C-4CB0-B6F2-0570DD181F60}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {D649B28A-352C-4CB0-B6F2-0570DD181F60}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {D649B28A-352C-4CB0-B6F2-0570DD181F60}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {D649B28A-352C-4CB0-B6F2-0570DD181F60}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {D649B28A-352C-4CB0-B6F2-0570DD181F60}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {D649B28A-352C-4CB0-B6F2-0570DD181F60}.Release|Any CPU.ActiveCfg = Release|Any CPU {D649B28A-352C-4CB0-B6F2-0570DD181F60}.Release|Any CPU.Build.0 = Release|Any CPU - {D649B28A-352C-4CB0-B6F2-0570DD181F60}.VS2010IntegrationTest|Any CPU.ActiveCfg = Release|Any CPU - {D649B28A-352C-4CB0-B6F2-0570DD181F60}.VS2010IntegrationTest|Any CPU.Build.0 = Release|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.Debug-MSTest|Any CPU.ActiveCfg = Debug-MSTest|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.Debug-MSTest|Any CPU.Build.0 = Debug-MSTest|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.Debug-NUnit|Any CPU.ActiveCfg = Debug-NUnit|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.Debug-NUnit|Any CPU.Build.0 = Debug-NUnit|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.Debug-XUnit|Any CPU.ActiveCfg = Debug-XUnit|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.Debug-XUnit|Any CPU.Build.0 = Debug-XUnit|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.Release|Any CPU.Build.0 = Release|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {03A229D6-BB43-47CC-AAEF-B7265652AD5A}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {D151832C-15D6-4869-9999-B245443BC23E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D151832C-15D6-4869-9999-B245443BC23E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D151832C-15D6-4869-9999-B245443BC23E}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {D151832C-15D6-4869-9999-B245443BC23E}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {D151832C-15D6-4869-9999-B245443BC23E}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {D151832C-15D6-4869-9999-B245443BC23E}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {D151832C-15D6-4869-9999-B245443BC23E}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {D151832C-15D6-4869-9999-B245443BC23E}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {D151832C-15D6-4869-9999-B245443BC23E}.Release|Any CPU.ActiveCfg = Release|Any CPU {D151832C-15D6-4869-9999-B245443BC23E}.Release|Any CPU.Build.0 = Release|Any CPU - {D151832C-15D6-4869-9999-B245443BC23E}.VS2010IntegrationTest|Any CPU.ActiveCfg = Release|Any CPU - {D151832C-15D6-4869-9999-B245443BC23E}.VS2010IntegrationTest|Any CPU.Build.0 = Release|Any CPU {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.Release|Any CPU.ActiveCfg = Release|Any CPU {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.Release|Any CPU.Build.0 = Release|Any CPU - {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.VS2010IntegrationTest|Any CPU.ActiveCfg = Release|Any CPU - {EC77355C-D442-416A-AE67-E42D5FAD0FB9}.VS2010IntegrationTest|Any CPU.Build.0 = Release|Any CPU {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.Release|Any CPU.ActiveCfg = Release|Any CPU {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.Release|Any CPU.Build.0 = Release|Any CPU - {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {5CD2408D-6A5A-4824-8F28-A2421088BEF0}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {556F5CA5-5497-4F32-9926-77A9369AB09C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {556F5CA5-5497-4F32-9926-77A9369AB09C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {556F5CA5-5497-4F32-9926-77A9369AB09C}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {556F5CA5-5497-4F32-9926-77A9369AB09C}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {556F5CA5-5497-4F32-9926-77A9369AB09C}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {556F5CA5-5497-4F32-9926-77A9369AB09C}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {556F5CA5-5497-4F32-9926-77A9369AB09C}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {556F5CA5-5497-4F32-9926-77A9369AB09C}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {556F5CA5-5497-4F32-9926-77A9369AB09C}.Release|Any CPU.ActiveCfg = Release|Any CPU {556F5CA5-5497-4F32-9926-77A9369AB09C}.Release|Any CPU.Build.0 = Release|Any CPU - {556F5CA5-5497-4F32-9926-77A9369AB09C}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {556F5CA5-5497-4F32-9926-77A9369AB09C}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.Release|Any CPU.ActiveCfg = Release|Any CPU {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.Release|Any CPU.Build.0 = Release|Any CPU - {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {69473C7E-DBA7-478B-AE6D-C185AA910FD2}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {204B619C-6D4B-4384-B1E9-E231BA0081BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {204B619C-6D4B-4384-B1E9-E231BA0081BF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {204B619C-6D4B-4384-B1E9-E231BA0081BF}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {204B619C-6D4B-4384-B1E9-E231BA0081BF}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {204B619C-6D4B-4384-B1E9-E231BA0081BF}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {204B619C-6D4B-4384-B1E9-E231BA0081BF}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {204B619C-6D4B-4384-B1E9-E231BA0081BF}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {204B619C-6D4B-4384-B1E9-E231BA0081BF}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {204B619C-6D4B-4384-B1E9-E231BA0081BF}.Release|Any CPU.ActiveCfg = Release|Any CPU {204B619C-6D4B-4384-B1E9-E231BA0081BF}.Release|Any CPU.Build.0 = Release|Any CPU - {204B619C-6D4B-4384-B1E9-E231BA0081BF}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {204B619C-6D4B-4384-B1E9-E231BA0081BF}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.Release|Any CPU.ActiveCfg = Release|Any CPU {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.Release|Any CPU.Build.0 = Release|Any CPU - {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Debug|Any CPU.Build.0 = Debug|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Release|Any CPU.ActiveCfg = Release|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.Release|Any CPU.Build.0 = Release|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {02A08F81-B90F-4EB3-9C30-CE7447DE2012}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.Release|Any CPU.Build.0 = Release|Any CPU - {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.VS2010IntegrationTest|Any CPU.ActiveCfg = Release|Any CPU - {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86}.VS2010IntegrationTest|Any CPU.Build.0 = Release|Any CPU {B7F589EB-524F-4BAD-9419-6576F6527670}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B7F589EB-524F-4BAD-9419-6576F6527670}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B7F589EB-524F-4BAD-9419-6576F6527670}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {B7F589EB-524F-4BAD-9419-6576F6527670}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {B7F589EB-524F-4BAD-9419-6576F6527670}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {B7F589EB-524F-4BAD-9419-6576F6527670}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {B7F589EB-524F-4BAD-9419-6576F6527670}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {B7F589EB-524F-4BAD-9419-6576F6527670}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {B7F589EB-524F-4BAD-9419-6576F6527670}.Release|Any CPU.ActiveCfg = Release|Any CPU {B7F589EB-524F-4BAD-9419-6576F6527670}.Release|Any CPU.Build.0 = Release|Any CPU - {B7F589EB-524F-4BAD-9419-6576F6527670}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {B7F589EB-524F-4BAD-9419-6576F6527670}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {A3931239-01D7-4277-9A7C-44D751BA746A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A3931239-01D7-4277-9A7C-44D751BA746A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {A3931239-01D7-4277-9A7C-44D751BA746A}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {A3931239-01D7-4277-9A7C-44D751BA746A}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {A3931239-01D7-4277-9A7C-44D751BA746A}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {A3931239-01D7-4277-9A7C-44D751BA746A}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {A3931239-01D7-4277-9A7C-44D751BA746A}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {A3931239-01D7-4277-9A7C-44D751BA746A}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {A3931239-01D7-4277-9A7C-44D751BA746A}.Release|Any CPU.ActiveCfg = Release|Any CPU {A3931239-01D7-4277-9A7C-44D751BA746A}.Release|Any CPU.Build.0 = Release|Any CPU - {A3931239-01D7-4277-9A7C-44D751BA746A}.VS2010IntegrationTest|Any CPU.ActiveCfg = Release|Any CPU - {A3931239-01D7-4277-9A7C-44D751BA746A}.VS2010IntegrationTest|Any CPU.Build.0 = Release|Any CPU {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.Release|Any CPU.ActiveCfg = Release|Any CPU {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.Release|Any CPU.Build.0 = Release|Any CPU - {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.VS2010IntegrationTest|Any CPU.ActiveCfg = Release|Any CPU - {AF7CACE6-1F26-454D-A814-19CFCF7C5810}.VS2010IntegrationTest|Any CPU.Build.0 = Release|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.Release|Any CPU.Build.0 = Release|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {C3B1DA26-4F12-443E-9FBA-898393520CE7}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {C681C731-49F2-4C93-A730-467251741457}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C681C731-49F2-4C93-A730-467251741457}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C681C731-49F2-4C93-A730-467251741457}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {C681C731-49F2-4C93-A730-467251741457}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {C681C731-49F2-4C93-A730-467251741457}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {C681C731-49F2-4C93-A730-467251741457}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {C681C731-49F2-4C93-A730-467251741457}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {C681C731-49F2-4C93-A730-467251741457}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {C681C731-49F2-4C93-A730-467251741457}.Release|Any CPU.ActiveCfg = Release|Any CPU {C681C731-49F2-4C93-A730-467251741457}.Release|Any CPU.Build.0 = Release|Any CPU - {C681C731-49F2-4C93-A730-467251741457}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {C681C731-49F2-4C93-A730-467251741457}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.Release|Any CPU.ActiveCfg = Release|Any CPU {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.Release|Any CPU.Build.0 = Release|Any CPU - {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {93476F8A-EBF1-4DB4-929F-77F5B96D08E0}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.Debug|Any CPU.Build.0 = Debug|Any CPU - {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.Release|Any CPU.ActiveCfg = Release|Any CPU {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.Release|Any CPU.Build.0 = Release|Any CPU - {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {68E341C2-12C9-4DAC-AB04-A11C7CF94F30}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {56BECF00-09F0-463A-A720-BED4495E51E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {56BECF00-09F0-463A-A720-BED4495E51E8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {56BECF00-09F0-463A-A720-BED4495E51E8}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {56BECF00-09F0-463A-A720-BED4495E51E8}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {56BECF00-09F0-463A-A720-BED4495E51E8}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {56BECF00-09F0-463A-A720-BED4495E51E8}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {56BECF00-09F0-463A-A720-BED4495E51E8}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {56BECF00-09F0-463A-A720-BED4495E51E8}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {56BECF00-09F0-463A-A720-BED4495E51E8}.Release|Any CPU.ActiveCfg = Release|Any CPU {56BECF00-09F0-463A-A720-BED4495E51E8}.Release|Any CPU.Build.0 = Release|Any CPU - {56BECF00-09F0-463A-A720-BED4495E51E8}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {56BECF00-09F0-463A-A720-BED4495E51E8}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {2FCD6A28-2134-435E-A205-3D55A6F5A389}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2FCD6A28-2134-435E-A205-3D55A6F5A389}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2FCD6A28-2134-435E-A205-3D55A6F5A389}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {2FCD6A28-2134-435E-A205-3D55A6F5A389}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {2FCD6A28-2134-435E-A205-3D55A6F5A389}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {2FCD6A28-2134-435E-A205-3D55A6F5A389}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {2FCD6A28-2134-435E-A205-3D55A6F5A389}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {2FCD6A28-2134-435E-A205-3D55A6F5A389}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {2FCD6A28-2134-435E-A205-3D55A6F5A389}.Release|Any CPU.ActiveCfg = Release|Any CPU {2FCD6A28-2134-435E-A205-3D55A6F5A389}.Release|Any CPU.Build.0 = Release|Any CPU - {2FCD6A28-2134-435E-A205-3D55A6F5A389}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {2FCD6A28-2134-435E-A205-3D55A6F5A389}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.Release|Any CPU.ActiveCfg = Release|Any CPU {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.Release|Any CPU.Build.0 = Release|Any CPU - {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {C073A609-8A6A-4707-86B0-7BB32EF8ACEE}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.Release|Any CPU.Build.0 = Release|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {8E1AB4D0-98E8-46C5-97EC-7EF47B5DFD27}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {C658B37D-FD36-4868-9070-4EB452FAE526}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C658B37D-FD36-4868-9070-4EB452FAE526}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C658B37D-FD36-4868-9070-4EB452FAE526}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {C658B37D-FD36-4868-9070-4EB452FAE526}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {C658B37D-FD36-4868-9070-4EB452FAE526}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {C658B37D-FD36-4868-9070-4EB452FAE526}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {C658B37D-FD36-4868-9070-4EB452FAE526}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {C658B37D-FD36-4868-9070-4EB452FAE526}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {C658B37D-FD36-4868-9070-4EB452FAE526}.Release|Any CPU.ActiveCfg = Release|Any CPU {C658B37D-FD36-4868-9070-4EB452FAE526}.Release|Any CPU.Build.0 = Release|Any CPU - {C658B37D-FD36-4868-9070-4EB452FAE526}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {C658B37D-FD36-4868-9070-4EB452FAE526}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.Debug-MSTest|Any CPU.ActiveCfg = Debug|Any CPU - {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.Debug-MSTest|Any CPU.Build.0 = Debug|Any CPU - {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.Debug-NUnit|Any CPU.ActiveCfg = Debug|Any CPU - {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.Debug-NUnit|Any CPU.Build.0 = Debug|Any CPU - {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.Debug-XUnit|Any CPU.ActiveCfg = Debug|Any CPU - {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.Debug-XUnit|Any CPU.Build.0 = Debug|Any CPU {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.Release|Any CPU.Build.0 = Release|Any CPU - {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.VS2010IntegrationTest|Any CPU.ActiveCfg = Debug|Any CPU - {6CBEB31D-5F98-460F-B193-578AEEA78CF9}.VS2010IntegrationTest|Any CPU.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -567,7 +254,6 @@ Global {FA04B445-D04C-4075-9EB0-4D0A70E47FC4} = {974420E8-FEEA-4564-B4CE-BE06F7457E3B} {1B51F6C9-226E-479B-9212-E6C02A1E6633} = {974420E8-FEEA-4564-B4CE-BE06F7457E3B} {D649B28A-352C-4CB0-B6F2-0570DD181F60} = {8BE0FE31-6A52-452E-BE71-B8C64A3ED402} - {03A229D6-BB43-47CC-AAEF-B7265652AD5A} = {A10B5CD6-38EC-4D7E-9D1C-2EBA8017E437} {D151832C-15D6-4869-9999-B245443BC23E} = {8BE0FE31-6A52-452E-BE71-B8C64A3ED402} {EC77355C-D442-416A-AE67-E42D5FAD0FB9} = {8BE0FE31-6A52-452E-BE71-B8C64A3ED402} {5CD2408D-6A5A-4824-8F28-A2421088BEF0} = {8BE0FE31-6A52-452E-BE71-B8C64A3ED402} @@ -575,16 +261,12 @@ Global {69473C7E-DBA7-478B-AE6D-C185AA910FD2} = {8BE0FE31-6A52-452E-BE71-B8C64A3ED402} {204B619C-6D4B-4384-B1E9-E231BA0081BF} = {0359B7D7-7E29-48E9-8DF9-7D1FACFA5CFA} {FA9DFEFA-4F35-4A57-91AE-64C7B5D41EAA} = {DCE0C3C4-5BC6-4A30-86BE-3FEFF4677A01} - {02A08F81-B90F-4EB3-9C30-CE7447DE2012} = {A10B5CD6-38EC-4D7E-9D1C-2EBA8017E437} - {48E162BC-9431-450D-8353-66D396DCB5FE} = {577A0375-1436-446C-802B-3C75C8CEF94F} {EA16606D-6F1C-4A4D-B7A1-8A08B448CD86} = {8BE0FE31-6A52-452E-BE71-B8C64A3ED402} {B7F589EB-524F-4BAD-9419-6576F6527670} = {05275A0B-57E4-484C-B181-227697C7B89A} {A3931239-01D7-4277-9A7C-44D751BA746A} = {8BE0FE31-6A52-452E-BE71-B8C64A3ED402} {16EAF4F9-9860-4BB9-8992-98522E68E570} = {8BE0FE31-6A52-452E-BE71-B8C64A3ED402} {AF7CACE6-1F26-454D-A814-19CFCF7C5810} = {16EAF4F9-9860-4BB9-8992-98522E68E570} - {C3B1DA26-4F12-443E-9FBA-898393520CE7} = {16EAF4F9-9860-4BB9-8992-98522E68E570} - {C681C731-49F2-4C93-A730-467251741457} = {35D24F50-A29E-428A-8649-275C73C1E646} - {35D24F50-A29E-428A-8649-275C73C1E646} = {16EAF4F9-9860-4BB9-8992-98522E68E570} + {C681C731-49F2-4C93-A730-467251741457} = {16EAF4F9-9860-4BB9-8992-98522E68E570} {229D5199-8A48-4174-AE8F-0B4C91170751} = {8BE0FE31-6A52-452E-BE71-B8C64A3ED402} {93476F8A-EBF1-4DB4-929F-77F5B96D08E0} = {229D5199-8A48-4174-AE8F-0B4C91170751} {68E341C2-12C9-4DAC-AB04-A11C7CF94F30} = {229D5199-8A48-4174-AE8F-0B4C91170751} diff --git a/Reqnroll.sln.DotSettings b/Reqnroll.sln.DotSettings index 00558d182..00beadc2b 100644 --- a/Reqnroll.sln.DotSettings +++ b/Reqnroll.sln.DotSettings @@ -101,4 +101,5 @@ True True True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/Reqnroll/AssemblyAttributes.cs b/Reqnroll/AssemblyAttributes.cs index 44461642d..b14eb35dd 100644 --- a/Reqnroll/AssemblyAttributes.cs +++ b/Reqnroll/AssemblyAttributes.cs @@ -1,14 +1,5 @@ - using System.Runtime.CompilerServices; -//[assembly: AllowPartiallyTrustedCallers] - -#if REQNROLL_ENABLE_STRONG_NAME_SIGNING [assembly: InternalsVisibleTo("Reqnroll.RuntimeTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059bb085601a8b65a8a7b00f34e6d85df230f1e4913d3c0eaadcd13c1fd9cd15c3f01567c49d5f41f617dbda6f544ea3e2d5b5a042f307a7c8d21a079254509ba543efaefce96fac977abd0a76206b721abc33f84ee45ae5383cf50eeb8e234595656fd4af916e1dcde644ce20bb9a68bd72bc7957b759560524c63ca294585ca")] [assembly: InternalsVisibleTo("Reqnroll.PluginTests, PublicKey=002400000480000094000000060200000024000052534131000400000100010059bb085601a8b65a8a7b00f34e6d85df230f1e4913d3c0eaadcd13c1fd9cd15c3f01567c49d5f41f617dbda6f544ea3e2d5b5a042f307a7c8d21a079254509ba543efaefce96fac977abd0a76206b721abc33f84ee45ae5383cf50eeb8e234595656fd4af916e1dcde644ce20bb9a68bd72bc7957b759560524c63ca294585ca")] [assembly: InternalsVisibleTo("Reqnroll.SpecFlowCompatibility.ReqnrollPlugin, PublicKey=002400000480000094000000060200000024000052534131000400000100010059bb085601a8b65a8a7b00f34e6d85df230f1e4913d3c0eaadcd13c1fd9cd15c3f01567c49d5f41f617dbda6f544ea3e2d5b5a042f307a7c8d21a079254509ba543efaefce96fac977abd0a76206b721abc33f84ee45ae5383cf50eeb8e234595656fd4af916e1dcde644ce20bb9a68bd72bc7957b759560524c63ca294585ca")] -#else -[assembly: InternalsVisibleTo("Reqnroll.RuntimeTests")] -[assembly: InternalsVisibleTo("Reqnroll.PluginTests")] -[assembly: InternalsVisibleTo("Reqnroll.SpecFlowCompatibility.ReqnrollPlugin")] -#endif diff --git a/Reqnroll/Bindings/BindingDelegateInvoker.cs b/Reqnroll/Bindings/BindingDelegateInvoker.cs index bfbf10da1..3e9f770e5 100644 --- a/Reqnroll/Bindings/BindingDelegateInvoker.cs +++ b/Reqnroll/Bindings/BindingDelegateInvoker.cs @@ -22,16 +22,18 @@ public virtual async Task InvokeDelegateAsync(Delegate bindingDelegate, // The ExecutionContext only flows down, so async binding methods cannot directly change it, but even if all binding method // is async the constructor of the binding classes are run in sync, so they should be able to change the ExecutionContext. // (The binding classes are created as part of the 'bindingDelegate' this method receives. - - try - { - return await InvokeInExecutionContext(executionContext?.Value, () => CreateDelegateInvocationTask(bindingDelegate, invokeArgs)); - } - finally + return await InvokeInExecutionContext(executionContext?.Value, () => { - if (executionContext != null) - executionContext.Value = ExecutionContext.Capture(); - } + try + { + return CreateDelegateInvocationTask(bindingDelegate, invokeArgs); + } + finally + { + if (executionContext != null) + executionContext.Value = ExecutionContext.Capture(); + } + }); } private Task InvokeInExecutionContext(ExecutionContext executionContext, Func> callback) diff --git a/Reqnroll/Bindings/BindingInvoker.cs b/Reqnroll/Bindings/BindingInvoker.cs index 0fa34aba6..82273cabc 100644 --- a/Reqnroll/Bindings/BindingInvoker.cs +++ b/Reqnroll/Bindings/BindingInvoker.cs @@ -9,7 +9,6 @@ using System.Threading; using System.Threading.Tasks; using Reqnroll.Bindings.Reflection; -using Reqnroll.Compatibility; using Reqnroll.Configuration; using Reqnroll.ErrorHandling; using Reqnroll.Infrastructure; @@ -168,11 +167,7 @@ protected virtual Delegate CreateMethodDelegate(MethodInfo method) parameters.ToArray()); } -#if WINDOWS_PHONE - return ExpressionCompiler.ExpressionCompiler.Compile(lambda); -#else return lambda.Compile(); -#endif } protected virtual Expression GetBindingMethodCallExpression(Expression instance, MethodInfo method, Expression[] argumentsExpressions) diff --git a/Reqnroll/Bindings/CucumberExpressions/CucumberExpressionParameterTypeRegistry.cs b/Reqnroll/Bindings/CucumberExpressions/CucumberExpressionParameterTypeRegistry.cs index b46663a7a..182654df6 100644 --- a/Reqnroll/Bindings/CucumberExpressions/CucumberExpressionParameterTypeRegistry.cs +++ b/Reqnroll/Bindings/CucumberExpressions/CucumberExpressionParameterTypeRegistry.cs @@ -87,7 +87,7 @@ private Dictionary InitializeR // get custom user transformations (both for built-in types and for custom types) var userTransformations = _bindingRegistry.GetStepTransformations() - .SelectMany(t => GetUserTransformations(t, aliases)); + .SelectMany(t => GetUserTransformations(t, builtInTransformations, aliases)); var parameterTypes = builtInTransformations .Concat(enumTypes) @@ -100,9 +100,11 @@ private Dictionary InitializeR return parameterTypes; } - private IEnumerable GetUserTransformations(IStepArgumentTransformationBinding t, Dictionary aliases) + private IEnumerable GetUserTransformations(IStepArgumentTransformationBinding t, BuiltInCucumberExpressionParameterTypeTransformation[] builtInTransformations, Dictionary aliases) { - yield return new UserDefinedCucumberExpressionParameterTypeTransformation(t); + bool IsUserDefinedType(IBindingType type) => !builtInTransformations.Any(b => b.TargetType.Name == type.Name); + var name = IsUserDefinedType(t.Method.ReturnType) && (t.Name == t.Method.ReturnType.Name || t.Name == null) ? t.Method.ReturnType.FullName : t.Name; + yield return new UserDefinedCucumberExpressionParameterTypeTransformation(t, name); // If the custom user transformations is for a built-in type, we also expose it with the // short name (e.g {int}) and not only with the type name (e.g. {Int32}). @@ -120,7 +122,8 @@ private IEnumerable GetEnumTypes { yield return new BuiltInCucumberExpressionParameterTypeTransformation( CucumberExpressionParameterType.MatchAllRegex, - enumParameterType); + enumParameterType, + enumParameterType.Type.FullName); } } @@ -128,6 +131,19 @@ public IParameterType LookupByTypeName(string name) { if (_parameterTypesByName.Value.TryGetValue(name, out var parameterType)) return parameterType; + //enum keys contain the Fullname of the type, try matching on the short name: + var matchingEntries = _parameterTypesByName.Value.Where(kvp => kvp.Key.EndsWith("." + name) || kvp.Key.EndsWith("+" + name)).ToArray(); + if (matchingEntries.Length == 0) { return null; } + if (matchingEntries.Length == 1) { return matchingEntries[0].Value; } + if (matchingEntries.Length > 1) + { + if (matchingEntries[0].Value.ParameterType.IsEnum) + { + throw new ReqnrollException($"Ambiguous enum in cucumber expression. Multiple enums share the same short name '{name}'. Use the enum's full name in the cucumber expression or define a [StepArgumentTransformation] with the chosen type and the short name."); + } + else + throw new ReqnrollException($"Ambiguous type used in cucumber expressions. Multiple step method parameter types share the same short name '{name}'. Use a uniquely named StepArgumentTransformation for each. "); + } return null; } diff --git a/Reqnroll/Bindings/Discovery/BindingSourceProcessor.cs b/Reqnroll/Bindings/Discovery/BindingSourceProcessor.cs index d6c88d9ac..147acb129 100644 --- a/Reqnroll/Bindings/Discovery/BindingSourceProcessor.cs +++ b/Reqnroll/Bindings/Discovery/BindingSourceProcessor.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using Reqnroll.Bindings.Reflection; -using Reqnroll.Compatibility; +using Reqnroll.PlatformCompatibility; namespace Reqnroll.Bindings.Discovery { diff --git a/Reqnroll/Bindings/Discovery/RuntimeBindingRegistryBuilder.cs b/Reqnroll/Bindings/Discovery/RuntimeBindingRegistryBuilder.cs index 63f239e0c..a1f475816 100644 --- a/Reqnroll/Bindings/Discovery/RuntimeBindingRegistryBuilder.cs +++ b/Reqnroll/Bindings/Discovery/RuntimeBindingRegistryBuilder.cs @@ -4,9 +4,9 @@ using System.Reflection; using Reqnroll.BoDi; using Reqnroll.Bindings.Reflection; -using Reqnroll.Compatibility; using Reqnroll.Configuration; using Reqnroll.Infrastructure; +using Reqnroll.PlatformCompatibility; namespace Reqnroll.Bindings.Discovery { diff --git a/Reqnroll/Bindings/StepArgumentTypeConverter.cs b/Reqnroll/Bindings/StepArgumentTypeConverter.cs index 1d7a5cdc3..ea4d8a568 100644 --- a/Reqnroll/Bindings/StepArgumentTypeConverter.cs +++ b/Reqnroll/Bindings/StepArgumentTypeConverter.cs @@ -38,11 +38,16 @@ protected virtual IStepArgumentTransformationBinding GetMatchingStepTransformati } public async Task ConvertAsync(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo) + { + return await ConvertAsync(value, typeToConvertTo, cultureInfo, null); + } + + private async Task ConvertAsync(object value, IBindingType typeToConvertTo, CultureInfo cultureInfo, IStepArgumentTransformationBinding lastBindingUsed) { if (value == null) throw new ArgumentNullException(nameof(value)); var stepTransformation = GetMatchingStepTransformation(value, typeToConvertTo, true); - if (stepTransformation != null) + if (stepTransformation != null && lastBindingUsed != stepTransformation) return await DoTransformAsync(stepTransformation, value, cultureInfo); if (typeToConvertTo is RuntimeBindingType convertToType && convertToType.Type.IsInstanceOfType(value)) @@ -55,16 +60,16 @@ private async Task DoTransformAsync(IStepArgumentTransformationBinding s { object[] arguments; if (stepTransformation.Regex != null && value is string stringValue) - arguments = await GetStepTransformationArgumentsFromRegexAsync(stepTransformation, stringValue, cultureInfo); + arguments = await GetStepTransformationArgumentsFromRegexAsync(stepTransformation, stringValue, cultureInfo, stepTransformation); else - arguments = new[] { await ConvertAsync(value, stepTransformation.Method.Parameters.ElementAtOrDefault(0)?.Type ?? new RuntimeBindingType(typeof(object)), cultureInfo)}; + arguments = new[] { await ConvertAsync(value, stepTransformation.Method.Parameters.ElementAtOrDefault(0)?.Type ?? new RuntimeBindingType(typeof(object)), cultureInfo, stepTransformation) }; var result = await bindingInvoker.InvokeBindingAsync(stepTransformation, contextManager, arguments, testTracer, new DurationHolder()); return result; } - private async Task GetStepTransformationArgumentsFromRegexAsync(IStepArgumentTransformationBinding stepTransformation, string stepSnippet, CultureInfo cultureInfo) + private async Task GetStepTransformationArgumentsFromRegexAsync(IStepArgumentTransformationBinding stepTransformation, string stepSnippet, CultureInfo cultureInfo, IStepArgumentTransformationBinding lastBindingUsed) { var match = stepTransformation.Regex.Match(stepSnippet); var argumentStrings = match.Groups.Cast().Skip(1).Select(g => g.Value).ToList(); @@ -74,7 +79,7 @@ private async Task GetStepTransformationArgumentsFromRegexAsync(IStepA for (int i = 0; i < argumentStrings.Count; i++) { - result[i] = await ConvertAsync(argumentStrings[i], bindingParameters[i].Type, cultureInfo); + result[i] = await ConvertAsync(argumentStrings[i], bindingParameters[i].Type, cultureInfo, lastBindingUsed); } return result; diff --git a/Reqnroll/Bindings/StepContext.cs b/Reqnroll/Bindings/StepContext.cs index 7418becd0..c18425f12 100644 --- a/Reqnroll/Bindings/StepContext.cs +++ b/Reqnroll/Bindings/StepContext.cs @@ -1,8 +1,8 @@ using System; using System.Collections.Generic; using System.Globalization; -using Reqnroll.Compatibility; using Reqnroll.Configuration; +using Reqnroll.PlatformCompatibility; namespace Reqnroll.Bindings { diff --git a/Reqnroll/BoDi/IContainedInstance.cs b/Reqnroll/BoDi/IContainedInstance.cs new file mode 100644 index 000000000..87f0e9667 --- /dev/null +++ b/Reqnroll/BoDi/IContainedInstance.cs @@ -0,0 +1,6 @@ +namespace Reqnroll.BoDi; + +public interface IContainedInstance +{ + IObjectContainer Container { get; } +} diff --git a/Reqnroll/BoDi/IObjectContainer.cs b/Reqnroll/BoDi/IObjectContainer.cs new file mode 100644 index 000000000..d906e0638 --- /dev/null +++ b/Reqnroll/BoDi/IObjectContainer.cs @@ -0,0 +1,125 @@ +using System; +using System.Collections.Generic; + +namespace Reqnroll.BoDi; + +public interface IObjectContainer : IDisposable +{ + /// + /// Fired when a new object is created directly by the container. It is not invoked for resolving instance and factory registrations. + /// + event Action ObjectCreated; + + /// + /// Registers a type as the desired implementation type of an interface. + /// + /// Implementation type + /// Interface will be resolved + /// An object which allows to change resolving strategy. + /// A name to register named instance, otherwise null. + /// If there was already a resolve for the . + /// + /// Previous registrations can be overridden before the first resolution for the . + /// + IStrategyRegistration RegisterTypeAs(string name = null) where TType : class, TInterface; + + /// + /// Registers an instance + /// + /// Interface will be resolved + /// The instance implements the interface. + /// A name to register named instance, otherwise null. + /// Whether the instance should be disposed on container dispose, otherwise false. + /// If is null. + /// If there was already a resolve for the . + /// + /// Previous registrations can be overridden before the first resolution for the . + /// The instance will be registered in the object pool, so if a (for another interface) would require an instance of the dynamic type of the , the will be returned. + /// + void RegisterInstanceAs(TInterface instance, string name = null, bool dispose = false) where TInterface : class; + + /// + /// Registers an instance + /// + /// The instance implements the interface. + /// Interface will be resolved + /// A name to register named instance, otherwise null. + /// Whether the instance should be disposed on container dispose, otherwise false. + /// If is null. + /// If there was already a resolve for the . + /// + /// Previous registrations can be overridden before the first resolution for the . + /// The instance will be registered in the object pool, so if a (for another interface) would require an instance of the dynamic type of the , the will be returned. + /// + void RegisterInstanceAs(object instance, Type interfaceType, string name = null, bool dispose = false); + + /// + /// Registers an instance produced by . The delegate will be called only once and the instance it returned will be returned in each resolution. + /// + /// Interface to register as. + /// The function to run to obtain the instance. + /// A name to resolve named instance, otherwise null. + IStrategyRegistration RegisterFactoryAs(Func factoryDelegate, string name = null); + + /// + /// Registers an instance produced by . The delegate will be called only once and the instance it returned will be returned in each resolution. + /// + /// Interface to register as. + /// The function to run to obtain the instance. + /// A name to resolve named instance, otherwise null. + IStrategyRegistration RegisterFactoryAs(Func factoryDelegate, string name = null); + + /// + /// Resolves an implementation object for an interface or type. + /// + /// The interface or type. + /// An object implementing . + /// + /// The container pools the objects, so if the interface is resolved twice or the same type is registered for multiple interfaces, a single instance is created and returned. + /// + T Resolve(); + + /// + /// Resolves an implementation object for an interface or type. + /// + /// A name to resolve named instance, otherwise null. + /// The interface or type. + /// An object implementing . + /// + /// The container pools the objects, so if the interface is resolved twice or the same type is registered for multiple interfaces, a single instance is created and returned. + /// + T Resolve(string name); + + /// + /// Resolves an implementation object for an interface or type. + /// + /// The interface or type. + /// A name to resolve named instance, otherwise null. + /// An object implementing . + /// + /// The container pools the objects, so if the interface is resolved twice or the same type is registered for multiple interfaces, a single instance is created and returned. + /// + object Resolve(Type typeToResolve, string name = null); + + /// + /// Resolves all implementations of an interface or type. + /// + /// The interface or type. + /// An object implementing . + IEnumerable ResolveAll() where T : class; + + /// + /// Determines whether the interface or type is registered. + /// + /// The interface or type. + /// true if the interface or type is registered; otherwise false. + bool IsRegistered(); + + /// + /// Determines whether the interface or type is registered with the specified name. + /// + /// The interface or type. + /// The name. + /// true if the interface or type is registered; otherwise false. + bool IsRegistered(string name); +} diff --git a/Reqnroll/BoDi/IStrategyRegistration.cs b/Reqnroll/BoDi/IStrategyRegistration.cs new file mode 100644 index 000000000..b69fb20ee --- /dev/null +++ b/Reqnroll/BoDi/IStrategyRegistration.cs @@ -0,0 +1,15 @@ +namespace Reqnroll.BoDi; + +public interface IStrategyRegistration +{ + /// + /// Changes resolving strategy to a new instance per each dependency. + /// + /// + IStrategyRegistration InstancePerDependency(); + /// + /// Changes resolving strategy to a single instance per object container. This strategy is a default behaviour. + /// + /// + IStrategyRegistration InstancePerContext(); +} diff --git a/Reqnroll/BoDi/ObjectContainer.cs b/Reqnroll/BoDi/ObjectContainer.cs new file mode 100644 index 000000000..4e7ab8603 --- /dev/null +++ b/Reqnroll/BoDi/ObjectContainer.cs @@ -0,0 +1,730 @@ +using System; +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Threading; + +namespace Reqnroll.BoDi; + +public class ObjectContainer : IObjectContainer +{ + private const string REGISTERED_NAME_PARAMETER_NAME = "registeredName"; + + /// + /// A very simple immutable linked list of . + /// + private class ResolutionList + { + private readonly RegistrationKey _currentRegistrationKey; + private readonly Type _currentResolvedType; + private readonly ResolutionList _nextNode; + private bool IsLast => _nextNode == null; + + public ResolutionList() + { + Debug.Assert(IsLast); + } + + private ResolutionList(RegistrationKey currentRegistrationKey, Type currentResolvedType, ResolutionList nextNode) + { + _currentRegistrationKey = currentRegistrationKey; + _currentResolvedType = currentResolvedType; + _nextNode = nextNode ?? throw new ArgumentNullException(nameof(nextNode)); + } + + public ResolutionList AddToEnd(RegistrationKey registrationKey, Type resolvedType) + { + return new ResolutionList(registrationKey, resolvedType, this); + } + + // ReSharper disable once UnusedMember.Local + public bool Contains(Type resolvedType) + { + if (resolvedType == null) throw new ArgumentNullException(nameof(resolvedType)); + return GetReverseEnumerable().Any(i => i.Value == resolvedType); + } + + public bool Contains(RegistrationKey registrationKey) + { + return GetReverseEnumerable().Any(i => i.Key.Equals(registrationKey)); + } + + private IEnumerable> GetReverseEnumerable() + { + var node = this; + while (!node.IsLast) + { + yield return new KeyValuePair(node._currentRegistrationKey, node._currentResolvedType); + node = node._nextNode; + } + } + + public Type[] ToTypeList() + { + return GetReverseEnumerable().Select(i => i.Value ?? i.Key.Type).Reverse().ToArray(); + } + + public override string ToString() + { + return string.Join(",", GetReverseEnumerable().Select(n => $"{n.Key}:{n.Value}")); + } + } + + private readonly struct RegistrationKey(Type type, string name) + { + public readonly Type Type = type ?? throw new ArgumentNullException(nameof(type)); + public readonly string Name = name; + + private Type TypeGroup + { + get + { + if (Type.IsGenericType && !Type.IsGenericTypeDefinition) + return Type.GetGenericTypeDefinition(); + return Type; + } + } + + public override string ToString() + { + Debug.Assert(Type.FullName != null); + if (Name == null) + return Type.FullName; + + return $"{Type.FullName}('{Name}')"; + } + + bool Equals(RegistrationKey other) + { + var isInvertible = other.TypeGroup == Type || other.Type == TypeGroup || other.Type == Type; + return isInvertible && String.Equals(other.Name, Name, StringComparison.CurrentCultureIgnoreCase); + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (obj.GetType() != typeof(RegistrationKey)) return false; + return Equals((RegistrationKey)obj); + } + + public override int GetHashCode() + { + return TypeGroup.GetHashCode(); + } + } + + #region Registration types + + private enum SolvingStrategy + { + PerContext, + PerDependency + } + + private interface IRegistration + { + object Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath); + } + + private class TypeRegistration(Type _implementationType) : RegistrationWithStrategy, IRegistration + { + private readonly object _syncRoot = new(); + + protected override object ResolvePerContext(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) + { + var typeToConstruct = GetTypeToConstruct(keyToResolve); + + var pooledObjectKey = new RegistrationKey(typeToConstruct, keyToResolve.Name); + + var result = ExecuteWithLock(_syncRoot, () => container.GetPooledObject(pooledObjectKey), () => + { + if (typeToConstruct.IsInterface) + throw new ObjectContainerException("Interface cannot be resolved: " + keyToResolve, + resolutionPath.ToTypeList()); + + var obj = container.CreateObject(typeToConstruct, resolutionPath, keyToResolve); + container._objectPool.Add(pooledObjectKey, obj); + return obj; + }, resolutionPath, container.ConcurrentObjectResolutionTimeout); + + return result; + } + + + + protected override object ResolvePerDependency(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) + { + var typeToConstruct = GetTypeToConstruct(keyToResolve); + if (typeToConstruct.IsInterface) + throw new ObjectContainerException("Interface cannot be resolved: " + keyToResolve, resolutionPath.ToTypeList()); + return container.CreateObject(typeToConstruct, resolutionPath, keyToResolve); + } + + private Type GetTypeToConstruct(RegistrationKey keyToResolve) + { + var targetType = _implementationType; + if (targetType.IsGenericTypeDefinition) + { + var typeArgs = keyToResolve.Type.GetGenericArguments(); + targetType = targetType.MakeGenericType(typeArgs); + } + return targetType; + } + + public override string ToString() + { + return "Type: " + _implementationType.FullName; + } + } + + private class InstanceRegistration(object _instance) : IRegistration + { + public object Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) + { + return _instance; + } + + public override string ToString() + { + string instanceText; + try + { + instanceText = _instance.ToString(); + } + catch (Exception ex) + { + instanceText = ex.Message; + } + + return "Instance: " + instanceText; + } + } + + private abstract class RegistrationWithStrategy : IStrategyRegistration + { + protected SolvingStrategy SolvingStrategy = SolvingStrategy.PerContext; + public virtual object Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) + { + if (SolvingStrategy == SolvingStrategy.PerDependency) + { + return ResolvePerDependency(container, keyToResolve, resolutionPath); + } + return ResolvePerContext(container, keyToResolve, resolutionPath); + } + + protected abstract object ResolvePerContext(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath); + protected abstract object ResolvePerDependency(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath); + + public IStrategyRegistration InstancePerDependency() + { + SolvingStrategy = SolvingStrategy.PerDependency; + return this; + } + + public IStrategyRegistration InstancePerContext() + { + SolvingStrategy = SolvingStrategy.PerContext; + return this; + } + + protected static object ExecuteWithLock(object lockObject, Func getter, Func factory, ResolutionList resolutionPath, TimeSpan timeout) + { + var obj = getter(); + + if (obj != null) + return obj; + + if (timeout == TimeSpan.Zero) + return factory(); + + if (Monitor.TryEnter(lockObject, timeout)) + { + try + { + obj = getter(); + + if (obj != null) + return obj; + + obj = factory(); + return obj; + } + finally + { + Monitor.Exit(lockObject); + } + } + + throw new ObjectContainerException("Concurrent object resolution timeout (potential circular dependency).", resolutionPath.ToTypeList()); + } + } + + private class FactoryRegistration(Delegate _factoryDelegate) : RegistrationWithStrategy, IRegistration + { + private readonly object _syncRoot = new(); + + protected override object ResolvePerContext(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) + { + var result = ExecuteWithLock(_syncRoot, () => container.GetPooledObject(keyToResolve), () => + { + var obj = container.InvokeFactoryDelegate(_factoryDelegate, resolutionPath, keyToResolve); + container._objectPool.Add(keyToResolve, obj); + return obj; + }, resolutionPath, container.ConcurrentObjectResolutionTimeout); + + return result; + } + protected override object ResolvePerDependency(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) + { + return container.InvokeFactoryDelegate(_factoryDelegate, resolutionPath, keyToResolve); + } + } + + private class NonDisposableWrapper(object obj) + { + public object Object { get; } = obj; + } + + private class NamedInstanceDictionaryRegistration : IRegistration + { + public object Resolve(ObjectContainer container, RegistrationKey keyToResolve, ResolutionList resolutionPath) + { + var typeToResolve = keyToResolve.Type; + Debug.Assert(typeToResolve.IsGenericType && typeToResolve.GetGenericTypeDefinition() == typeof(IDictionary<,>)); + + var genericArguments = typeToResolve.GetGenericArguments(); + var keyType = genericArguments[0]; + var targetType = genericArguments[1]; + var result = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(genericArguments))!; + + foreach (var namedRegistration in container._registrations.Where(r => r.Key.Name != null && r.Key.Type == targetType).Select(r => r.Key).ToList()) + { + var convertedKey = ChangeType(namedRegistration.Name, keyType); + Debug.Assert(convertedKey != null); + result.Add(convertedKey, container.Resolve(namedRegistration.Type, namedRegistration.Name)); + } + + return result; + } + + private object ChangeType(string name, Type keyType) + { + if (keyType.IsEnum) + return Enum.Parse(keyType, name, true); + + Debug.Assert(keyType == typeof(string)); + return name; + } + } + + #endregion + + public static TimeSpan DefaultConcurrentObjectResolutionTimeout { get; set; } = TimeSpan.FromSeconds(1); + private bool _isDisposed = false; + private readonly ObjectContainer _baseContainer; + private readonly ConcurrentDictionary _registrations = new(); + private readonly List _resolvedKeys = new(); + private readonly Dictionary _objectPool = new(); + + public event Action ObjectCreated; + public IObjectContainer BaseContainer => _baseContainer; + + /// + /// Sets the timeout for thread-safe object resolution. By default, it uses the value of that is initialized to 1 second. Setting it to disables thread-safe resolution. + /// + public TimeSpan ConcurrentObjectResolutionTimeout { get; set; } = DefaultConcurrentObjectResolutionTimeout; + + public ObjectContainer(IObjectContainer baseContainer = null) + { + if (baseContainer != null && !(baseContainer is ObjectContainer)) + throw new ArgumentException("Base container must be an ObjectContainer", nameof(baseContainer)); + + _baseContainer = (ObjectContainer)baseContainer; + RegisterInstanceAs(this); + } + + #region Registration + + public IStrategyRegistration RegisterTypeAs(Type implementationType, string name = null) where TInterface : class + { + Type interfaceType = typeof(TInterface); + return RegisterTypeAsInternal(implementationType, interfaceType, name); + } + + public IStrategyRegistration RegisterTypeAs(string name = null) where TType : class, TInterface + { + Type interfaceType = typeof(TInterface); + Type implementationType = typeof(TType); + return RegisterTypeAsInternal(implementationType, interfaceType, name); + } + + public IStrategyRegistration RegisterTypeAs(Type implementationType, Type interfaceType, string name = null) + { + if (!IsValidTypeMapping(implementationType, interfaceType)) + throw new InvalidOperationException("type mapping is not valid"); + return RegisterTypeAsInternal(implementationType, interfaceType, name); + } + + private bool IsValidTypeMapping(Type implementationType, Type interfaceType) + { + if (interfaceType.IsAssignableFrom(implementationType)) + return true; + + if (interfaceType.IsGenericTypeDefinition && implementationType!.IsGenericTypeDefinition) + { + var baseTypes = GetBaseTypes(implementationType).ToArray(); + return baseTypes.Any(t => t.IsGenericType && t.GetGenericTypeDefinition() == interfaceType); + } + + return false; + } + + private static IEnumerable GetBaseTypes(Type type) + { + if (type.BaseType == null) return type.GetInterfaces(); + + return Enumerable.Repeat(type.BaseType, 1) + .Concat(type.GetInterfaces()) + .Concat(type.GetInterfaces().SelectMany(GetBaseTypes)) + .Concat(GetBaseTypes(type.BaseType)); + } + + + private RegistrationKey CreateNamedInstanceDictionaryKey(Type targetType) + { + return new RegistrationKey(typeof(IDictionary<,>).MakeGenericType(typeof(string), targetType), null); + } + + private void AddRegistration(RegistrationKey key, IRegistration registration) + { + _registrations[key] = registration; + + AddNamedDictionaryRegistration(key); + } + + private IRegistration EnsureImplicitRegistration(RegistrationKey key) + { + var registration = _registrations.GetOrAdd(key, (registrationKey => new TypeRegistration(registrationKey.Type))); + + AddNamedDictionaryRegistration(key); + + return registration; + } + + private void AddNamedDictionaryRegistration(RegistrationKey key) + { + if (key.Name != null) + { + var dictKey = CreateNamedInstanceDictionaryKey(key.Type); + _registrations.TryAdd(dictKey, new NamedInstanceDictionaryRegistration()); + } + } + + private IStrategyRegistration RegisterTypeAsInternal(Type implementationType, Type interfaceType, string name) + { + var registrationKey = new RegistrationKey(interfaceType, name); + AssertNotResolved(registrationKey); + + ClearRegistrations(registrationKey); + var typeRegistration = new TypeRegistration(implementationType); + AddRegistration(registrationKey, typeRegistration); + + return typeRegistration; + } + + public void RegisterInstanceAs(object instance, Type interfaceType, string name = null, bool dispose = false) + { + if (instance == null) + throw new ArgumentNullException(nameof(instance)); + var registrationKey = new RegistrationKey(interfaceType, name); + AssertNotResolved(registrationKey); + + ClearRegistrations(registrationKey); + AddRegistration(registrationKey, new InstanceRegistration(instance)); + _objectPool[new RegistrationKey(instance.GetType(), name)] = GetPoolableInstance(instance, dispose); + } + + private static object GetPoolableInstance(object instance, bool dispose) + { + return (instance is IDisposable) && !dispose ? new NonDisposableWrapper(instance) : instance; + } + + public void RegisterInstanceAs(TInterface instance, string name = null, bool dispose = false) where TInterface : class + { + RegisterInstanceAs(instance, typeof(TInterface), name, dispose); + } + + public IStrategyRegistration RegisterFactoryAs(Func factoryDelegate, string name = null) + { + return RegisterFactoryAs(factoryDelegate, typeof(TInterface), name); + } + + public IStrategyRegistration RegisterFactoryAs(Func factoryDelegate, string name = null) + { + return RegisterFactoryAs(factoryDelegate, typeof(TInterface), name); + } + + public void RegisterFactoryAs(Delegate factoryDelegate, string name = null) + { + RegisterFactoryAs(factoryDelegate, typeof(TInterface), name); + } + + public IStrategyRegistration RegisterFactoryAs(Delegate factoryDelegate, Type interfaceType, string name = null) + { + if (factoryDelegate == null) throw new ArgumentNullException(nameof(factoryDelegate)); + if (interfaceType == null) throw new ArgumentNullException(nameof(interfaceType)); + + var registrationKey = new RegistrationKey(interfaceType, name); + AssertNotResolved(registrationKey); + + ClearRegistrations(registrationKey); + var factoryRegistration = new FactoryRegistration(factoryDelegate); + AddRegistration(registrationKey, factoryRegistration); + + return factoryRegistration; + } + + public bool IsRegistered() + { + return IsRegistered(null); + } + + public bool IsRegistered(string name) + { + Type typeToResolve = typeof(T); + + var keyToResolve = new RegistrationKey(typeToResolve, name); + + return _registrations.ContainsKey(keyToResolve); + } + + // ReSharper disable once UnusedParameter.Local + private void AssertNotResolved(RegistrationKey interfaceType) + { + if (_resolvedKeys.Contains(interfaceType)) + throw new ObjectContainerException("An object has been resolved for this interface already.", null); + } + + private void ClearRegistrations(RegistrationKey registrationKey) + { + _registrations.TryRemove(registrationKey, out _); + } + + + #endregion + + #region Resolve + + public T Resolve() + { + return Resolve(null); + } + + public T Resolve(string name) + { + Type typeToResolve = typeof(T); + + object resolvedObject = Resolve(typeToResolve, name); + + return (T)resolvedObject; + } + + public object Resolve(Type typeToResolve, string name = null) + { + return Resolve(typeToResolve, new ResolutionList(), name); + } + + public IEnumerable ResolveAll() where T : class + { + return _registrations + .Where(x => x.Key.Type == typeof(T)) + .Select(x => Resolve(x.Key.Type, x.Key.Name) as T); + } + + private object Resolve(Type typeToResolve, ResolutionList resolutionPath, string name) + { + AssertNotDisposed(); + + var keyToResolve = new RegistrationKey(typeToResolve, name); + object resolvedObject = ResolveObject(keyToResolve, resolutionPath); + if (!_resolvedKeys.Contains(keyToResolve)) + { + _resolvedKeys.Add(keyToResolve); + } + Debug.Assert(typeToResolve.IsInstanceOfType(resolvedObject)); + return resolvedObject; + } + + private KeyValuePair? GetRegistrationResult(RegistrationKey keyToResolve) + { + if (_registrations.TryGetValue(keyToResolve, out var registration)) + { + return new KeyValuePair(this, registration); + } + + if (_baseContainer != null) + return _baseContainer.GetRegistrationResult(keyToResolve); + + if (IsSpecialNamedInstanceDictionaryKey(keyToResolve)) + { + var targetType = keyToResolve.Type.GetGenericArguments()[1]; + return GetRegistrationResult(CreateNamedInstanceDictionaryKey(targetType)); + } + + // if there was no named registration, we still return an empty dictionary + if (IsDefaultNamedInstanceDictionaryKey(keyToResolve)) + { + return new KeyValuePair(this, new NamedInstanceDictionaryRegistration()); + } + + return null; + } + + private bool IsDefaultNamedInstanceDictionaryKey(RegistrationKey keyToResolve) + { + return IsNamedInstanceDictionaryKey(keyToResolve) && + keyToResolve.Type.GetGenericArguments()[0] == typeof(string); + } + + private bool IsSpecialNamedInstanceDictionaryKey(RegistrationKey keyToResolve) + { + return IsNamedInstanceDictionaryKey(keyToResolve) && + keyToResolve.Type.GetGenericArguments()[0].IsEnum; + } + + private bool IsNamedInstanceDictionaryKey(RegistrationKey keyToResolve) + { + return keyToResolve.Name == null && keyToResolve.Type.IsGenericType && keyToResolve.Type.GetGenericTypeDefinition() == typeof(IDictionary<,>); + } + + private object GetPooledObject(RegistrationKey pooledObjectKey) + { + if (GetObjectFromPool(pooledObjectKey, out object obj)) + return obj; + + return null; + } + + private bool GetObjectFromPool(RegistrationKey pooledObjectKey, out object obj) + { + if (!_objectPool.TryGetValue(pooledObjectKey, out obj)) + return false; + + if (obj is NonDisposableWrapper nonDisposableWrapper) + obj = nonDisposableWrapper.Object; + + return true; + } + + private object ResolveObject(RegistrationKey keyToResolve, ResolutionList resolutionPath) + { + if (keyToResolve.Type.IsPrimitive || keyToResolve.Type == typeof(string) || keyToResolve.Type.IsValueType) + throw new ObjectContainerException("Primitive types or structs cannot be resolved: " + keyToResolve.Type.FullName, resolutionPath.ToTypeList()); + + var registrationResult = GetRegistrationResult(keyToResolve); + + var registrationToUse = registrationResult ?? + new KeyValuePair(this, EnsureImplicitRegistration(keyToResolve)); + + var resolutionPathForResolve = registrationToUse.Key == this ? + resolutionPath : new ResolutionList(); + var result = registrationToUse.Value.Resolve(registrationToUse.Key, keyToResolve, resolutionPathForResolve); + + return result; + } + + + private object CreateObject(Type type, ResolutionList resolutionPath, RegistrationKey keyToResolve) + { + var constructors = type.GetConstructors(); + if (constructors.Length == 0) + constructors = type.GetConstructors(BindingFlags.NonPublic | BindingFlags.Instance); + + Debug.Assert(constructors.Length > 0, "Class must have a constructor!"); + + int maxParamCount = constructors.Max(ctor => ctor.GetParameters().Length); + var maxParamCountConstructors = constructors.Where(ctor => ctor.GetParameters().Length == maxParamCount).ToArray(); + + object obj; + if (maxParamCountConstructors.Length == 1) + { + ConstructorInfo ctor = maxParamCountConstructors[0]; + if (resolutionPath.Contains(keyToResolve)) + throw new ObjectContainerException("Circular dependency found! " + type.FullName, resolutionPath.ToTypeList()); + + var args = ResolveArguments(ctor.GetParameters(), keyToResolve, resolutionPath.AddToEnd(keyToResolve, type)); + obj = ctor.Invoke(args); + } + else + { + throw new ObjectContainerException("Multiple public constructors with same maximum parameter count are not supported! " + type.FullName, resolutionPath.ToTypeList()); + } + + OnObjectCreated(obj); + + return obj; + } + + protected virtual void OnObjectCreated(object obj) + { + var eventHandler = ObjectCreated; + if (eventHandler != null) + eventHandler(obj); + } + + private object InvokeFactoryDelegate(Delegate factoryDelegate, ResolutionList resolutionPath, RegistrationKey keyToResolve) + { + if (resolutionPath.Contains(keyToResolve)) + throw new ObjectContainerException("Circular dependency found! " + factoryDelegate, resolutionPath.ToTypeList()); + + var args = ResolveArguments(factoryDelegate.Method.GetParameters(), keyToResolve, resolutionPath.AddToEnd(keyToResolve, null)); + return factoryDelegate.DynamicInvoke(args); + } + + private object[] ResolveArguments(IEnumerable parameters, RegistrationKey keyToResolve, ResolutionList resolutionPath) + { + return parameters.Select(p => IsRegisteredNameParameter(p) ? ResolveRegisteredName(keyToResolve) : Resolve(p.ParameterType, resolutionPath, null)).ToArray(); + } + + private object ResolveRegisteredName(RegistrationKey keyToResolve) + { + return keyToResolve.Name; + } + + private bool IsRegisteredNameParameter(ParameterInfo parameterInfo) + { + return parameterInfo.ParameterType == typeof(string) && + parameterInfo!.Name!.Equals(REGISTERED_NAME_PARAMETER_NAME); + } + + #endregion + + public override string ToString() + { + return string.Join(Environment.NewLine, + _registrations + .Where(r => !(r.Value is NamedInstanceDictionaryRegistration)) + .Select(r => $"{r.Key} -> {((r.Key.Type == typeof(IObjectContainer) && r.Key.Name == null) ? "" : r.Value.ToString())}")); + } + + private void AssertNotDisposed() + { + if (_isDisposed) + throw new ObjectContainerException("Object container disposed", null); + } + + public void Dispose() + { + _isDisposed = true; + + foreach (var obj in _objectPool.Values.OfType().Where(o => !ReferenceEquals(o, this))) + obj.Dispose(); + + _objectPool.Clear(); + _registrations.Clear(); + _resolvedKeys.Clear(); + } +} diff --git a/Reqnroll/BoDi/ObjectContainerException.cs b/Reqnroll/BoDi/ObjectContainerException.cs new file mode 100644 index 000000000..a0f7b20d3 --- /dev/null +++ b/Reqnroll/BoDi/ObjectContainerException.cs @@ -0,0 +1,27 @@ +using System; +using System.Linq; +using System.Runtime.Serialization; + +namespace Reqnroll.BoDi; + +[Serializable] +public class ObjectContainerException : Exception +{ + public ObjectContainerException(string message, Type[] resolutionPath) : base(GetMessage(message, resolutionPath)) + { + } + + protected ObjectContainerException( + SerializationInfo info, + StreamingContext context) : base(info, context) + { + } + + private static string GetMessage(string message, Type[] resolutionPath) + { + if (resolutionPath == null || resolutionPath.Length == 0) + return message; + + return $"{message} (resolution path: {string.Join("->", resolutionPath.Select(t => t.FullName).ToArray())})"; + } +} diff --git a/Reqnroll/Configuration/ConfigurationLoader.cs b/Reqnroll/Configuration/ConfigurationLoader.cs index 1bd1f9dd4..469018523 100644 --- a/Reqnroll/Configuration/ConfigurationLoader.cs +++ b/Reqnroll/Configuration/ConfigurationLoader.cs @@ -4,8 +4,8 @@ using System.IO; using Reqnroll.BoDi; using Reqnroll.BindingSkeletons; -using Reqnroll.Compatibility; using Reqnroll.Configuration.JsonConfig; +using Reqnroll.PlatformCompatibility; using Reqnroll.Tracing; namespace Reqnroll.Configuration diff --git a/Reqnroll/Infrastructure/DefaultDependencyProvider.cs b/Reqnroll/Infrastructure/DefaultDependencyProvider.cs index 54b48f8bc..fc05fa24a 100644 --- a/Reqnroll/Infrastructure/DefaultDependencyProvider.cs +++ b/Reqnroll/Infrastructure/DefaultDependencyProvider.cs @@ -15,6 +15,7 @@ using Reqnroll.TestFramework; using Reqnroll.Time; using Reqnroll.Tracing; +using Reqnroll.PlatformCompatibility; namespace Reqnroll.Infrastructure { @@ -56,6 +57,7 @@ public virtual void RegisterGlobalContainerDefaults(ObjectContainer container) container.RegisterTypeAs(); container.RegisterTypeAs(); + PlatformHelper.RegisterPluginAssemblyLoader(container); container.RegisterTypeAs(); container.RegisterTypeAs(); container.RegisterTypeAs(); diff --git a/Reqnroll/Infrastructure/TestExecutionEngine.cs b/Reqnroll/Infrastructure/TestExecutionEngine.cs index b201dc037..c0af86828 100644 --- a/Reqnroll/Infrastructure/TestExecutionEngine.cs +++ b/Reqnroll/Infrastructure/TestExecutionEngine.cs @@ -7,10 +7,10 @@ using Reqnroll.Analytics; using Reqnroll.Bindings; using Reqnroll.Bindings.Reflection; -using Reqnroll.Compatibility; using Reqnroll.Configuration; using Reqnroll.ErrorHandling; using Reqnroll.Events; +using Reqnroll.PlatformCompatibility; using Reqnroll.Plugins; using Reqnroll.Tracing; using Reqnroll.UnitTestProvider; @@ -141,15 +141,6 @@ public virtual async Task OnTestRunEndAsync() public virtual async Task OnFeatureStartAsync(FeatureInfo featureInfo) { - // if the unit test provider would execute the fixture teardown code - // only delayed (at the end of the execution), we automatically close - // the current feature if necessary - if (_unitTestRuntimeProvider.DelayedFixtureTearDown && FeatureContext != null) - { - await OnFeatureEndAsync(); - } - - _contextManager.InitializeFeatureContext(featureInfo); _testThreadExecutionEventPublisher.PublishEvent(new FeatureStartedEvent(FeatureContext)); @@ -159,13 +150,6 @@ public virtual async Task OnFeatureStartAsync(FeatureInfo featureInfo) public virtual async Task OnFeatureEndAsync() { - // if the unit test provider would execute the fixture teardown code - // only delayed (at the end of the execution), we ignore the - // feature-end call, if the feature has been closed already - if (_unitTestRuntimeProvider.DelayedFixtureTearDown && - FeatureContext == null) - return; - await FireEventsAsync(HookType.AfterFeature); if (_reqnrollConfiguration.TraceTimings) diff --git a/Reqnroll/PlatformCompatibility/CultureInfoHelper.cs b/Reqnroll/PlatformCompatibility/CultureInfoHelper.cs index 9effe95b9..6c674a165 100644 --- a/Reqnroll/PlatformCompatibility/CultureInfoHelper.cs +++ b/Reqnroll/PlatformCompatibility/CultureInfoHelper.cs @@ -1,6 +1,6 @@ using System.Globalization; -namespace Reqnroll.Compatibility +namespace Reqnroll.PlatformCompatibility { internal static class CultureInfoHelper { diff --git a/Reqnroll/PlatformCompatibility/EnumHelper.cs b/Reqnroll/PlatformCompatibility/EnumHelper.cs index 43d9b34d6..4d0efcff3 100644 --- a/Reqnroll/PlatformCompatibility/EnumHelper.cs +++ b/Reqnroll/PlatformCompatibility/EnumHelper.cs @@ -1,6 +1,6 @@ using System; -namespace Reqnroll.Compatibility +namespace Reqnroll.PlatformCompatibility { internal static class EnumHelper { diff --git a/Reqnroll/PlatformCompatibility/ExceptionHelper.cs b/Reqnroll/PlatformCompatibility/ExceptionHelper.cs index 6ae159750..5dc90c12a 100644 --- a/Reqnroll/PlatformCompatibility/ExceptionHelper.cs +++ b/Reqnroll/PlatformCompatibility/ExceptionHelper.cs @@ -1,7 +1,7 @@ using System; using System.Reflection; -namespace Reqnroll.Compatibility +namespace Reqnroll.PlatformCompatibility { internal static class ExceptionHelper { diff --git a/Reqnroll/PlatformCompatibility/MonoHelper.cs b/Reqnroll/PlatformCompatibility/MonoHelper.cs index 08025054f..30d47b355 100644 --- a/Reqnroll/PlatformCompatibility/MonoHelper.cs +++ b/Reqnroll/PlatformCompatibility/MonoHelper.cs @@ -1,7 +1,7 @@ using System; using System.Reflection; -namespace Reqnroll.Compatibility +namespace Reqnroll.PlatformCompatibility { internal class MonoHelper { diff --git a/Reqnroll/PlatformCompatibility/PlatformHelper.cs b/Reqnroll/PlatformCompatibility/PlatformHelper.cs new file mode 100644 index 000000000..ca573b5ce --- /dev/null +++ b/Reqnroll/PlatformCompatibility/PlatformHelper.cs @@ -0,0 +1,14 @@ +using Reqnroll.BoDi; +using Reqnroll.Plugins; + +namespace Reqnroll.PlatformCompatibility; +public static class PlatformHelper +{ + public static void RegisterPluginAssemblyLoader(IObjectContainer container) + { + if (PlatformInformation.IsDotNetFramework) + container.RegisterTypeAs(); + else + container.RegisterTypeAs(); + } +} diff --git a/Reqnroll/PlatformCompatibility/PlatformInformation.cs b/Reqnroll/PlatformCompatibility/PlatformInformation.cs new file mode 100644 index 000000000..eb0eaad0c --- /dev/null +++ b/Reqnroll/PlatformCompatibility/PlatformInformation.cs @@ -0,0 +1,26 @@ +using System.Diagnostics; +using System.Runtime.InteropServices; +using System; +using System.Text.RegularExpressions; + +namespace Reqnroll.PlatformCompatibility; +public class PlatformInformation +{ + public static Architecture ProcessArchitecture => RuntimeInformation.ProcessArchitecture; + public static string DotNetFullVersion { get; } = FileVersionInfo.GetVersionInfo(typeof(Uri).Assembly.Location).ProductVersion; + public static string DotNetFrameworkDescription => RuntimeInformation.FrameworkDescription; + public static string DotNetFrameworkMainDescription { get; } = GetMainDotNetFrameworkDescription(); + public static bool IsDotNetFramework { get; } = GetIsDotNetFramework(); + + private static bool GetIsDotNetFramework() + { + string frameworkDescription = DotNetFrameworkDescription; + return frameworkDescription.StartsWith(".NET Framework", StringComparison.OrdinalIgnoreCase) || + frameworkDescription.StartsWith("Mono", StringComparison.OrdinalIgnoreCase); + } + + private static string GetMainDotNetFrameworkDescription() + { + return Regex.Replace(RuntimeInformation.FrameworkDescription, @"(?\d+\.\d+).*", "${mver}"); + } +} diff --git a/Reqnroll/Plugins/DotNetFrameworkPluginAssemblyLoader.cs b/Reqnroll/Plugins/DotNetFrameworkPluginAssemblyLoader.cs new file mode 100644 index 000000000..7ee805f63 --- /dev/null +++ b/Reqnroll/Plugins/DotNetFrameworkPluginAssemblyLoader.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace Reqnroll.Plugins; + +public class DotNetFrameworkPluginAssemblyLoader : IPluginAssemblyLoader +{ + public Assembly LoadAssembly(string assemblyName) => Assembly.LoadFrom(assemblyName); +} diff --git a/Reqnroll/Plugins/IPluginAssemblyLoader.cs b/Reqnroll/Plugins/IPluginAssemblyLoader.cs new file mode 100644 index 000000000..7d46da021 --- /dev/null +++ b/Reqnroll/Plugins/IPluginAssemblyLoader.cs @@ -0,0 +1,7 @@ +using System.Reflection; + +namespace Reqnroll.Plugins; +public interface IPluginAssemblyLoader +{ + Assembly LoadAssembly(string assemblyName); +} \ No newline at end of file diff --git a/Reqnroll/Plugins/PluginAssemblyLoader.cs b/Reqnroll/Plugins/PluginAssemblyLoader.cs new file mode 100644 index 000000000..9129d406f --- /dev/null +++ b/Reqnroll/Plugins/PluginAssemblyLoader.cs @@ -0,0 +1,8 @@ +using System.Reflection; + +namespace Reqnroll.Plugins; + +public class PluginAssemblyLoader : IPluginAssemblyLoader +{ + public Assembly LoadAssembly(string assemblyName) => PluginAssemblyResolver.Load(assemblyName); +} diff --git a/Reqnroll/Plugins/PluginAssemblyResolver.cs b/Reqnroll/Plugins/PluginAssemblyResolver.cs index 294ea8f4f..b7ef4817f 100644 --- a/Reqnroll/Plugins/PluginAssemblyResolver.cs +++ b/Reqnroll/Plugins/PluginAssemblyResolver.cs @@ -1,5 +1,3 @@ -#if NETSTANDARD - using System; using System.Collections.Generic; using System.IO; @@ -77,5 +75,3 @@ public static Assembly Load(string path) } } } - -#endif \ No newline at end of file diff --git a/Reqnroll/Plugins/RuntimePluginLoader.cs b/Reqnroll/Plugins/RuntimePluginLoader.cs index e0e4b187f..1d61da1f0 100644 --- a/Reqnroll/Plugins/RuntimePluginLoader.cs +++ b/Reqnroll/Plugins/RuntimePluginLoader.cs @@ -1,25 +1,22 @@ using System; using System.Reflection; +using Reqnroll.PlatformCompatibility; using Reqnroll.Tracing; namespace Reqnroll.Plugins { - public class RuntimePluginLoader : IRuntimePluginLoader + public class RuntimePluginLoader(IPluginAssemblyLoader _pluginAssemblyLoader) : IRuntimePluginLoader { public IRuntimePlugin LoadPlugin(string pluginAssemblyName, ITraceListener traceListener, bool traceMissingPluginAttribute) { Assembly assembly; try { -#if NETSTANDARD - assembly = PluginAssemblyResolver.Load(pluginAssemblyName); -#else - assembly = Assembly.LoadFrom(pluginAssemblyName); -#endif + assembly = _pluginAssemblyLoader.LoadAssembly(pluginAssemblyName); } catch (Exception ex) { - throw new ReqnrollException($"Unable to load plugin: {pluginAssemblyName}. Please check https://go.reqnroll.net/doc-plugins for details.", ex); + throw new ReqnrollException($"Unable to load plugin: {pluginAssemblyName}. Please check https://go.reqnroll.net/doc-plugins for details. (Framework: {PlatformInformation.DotNetFrameworkDescription})", ex); } var pluginAttribute = (RuntimePluginAttribute)Attribute.GetCustomAttribute(assembly, typeof(RuntimePluginAttribute)); diff --git a/Reqnroll/Plugins/RuntimePluginLocationMerger.cs b/Reqnroll/Plugins/RuntimePluginLocationMerger.cs index 6056e0ab2..6057a3e7e 100644 --- a/Reqnroll/Plugins/RuntimePluginLocationMerger.cs +++ b/Reqnroll/Plugins/RuntimePluginLocationMerger.cs @@ -8,12 +8,7 @@ public class RuntimePluginLocationMerger : IRuntimePluginLocationMerger public IReadOnlyList Merge(IReadOnlyList pluginPaths) { // Idea is to filter out the same assemblies stored on different paths. Shortcut: check if we even have duplicated assemblies - var hashset = new HashSet( -#if NETCOREAPP2_1_OR_GREATER - // initialize with expected size when available - pluginPaths.Count -#endif - ); + var hashset = new HashSet(); List modifiedList = null; for (var i = 0; i < pluginPaths.Count; i++) diff --git a/Reqnroll/Reqnroll.csproj b/Reqnroll/Reqnroll.csproj index 53fec9a3f..023f1c76d 100644 --- a/Reqnroll/Reqnroll.csproj +++ b/Reqnroll/Reqnroll.csproj @@ -1,6 +1,6 @@  - $(Reqnroll_Runtime_TFM) + netstandard2.0 Reqnroll $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) @@ -18,12 +18,13 @@ true + all runtime; build; native; contentfiles; analyzers - + @@ -31,29 +32,13 @@ - - - - - - - - - - - - - + - - $(DefineConstants);NETSTANDARD - - 3fd018ff-819d-4685-a6e1-6f09bc98d20b @@ -64,29 +49,13 @@ - - - - - - + + - + diff --git a/Reqnroll/Reqnroll.nuspec b/Reqnroll/Reqnroll.nuspec index c7ff03075..fd0d550d4 100644 --- a/Reqnroll/Reqnroll.nuspec +++ b/Reqnroll/Reqnroll.nuspec @@ -16,21 +16,12 @@ reqnroll bdd gherkin cucumber $copyright$ - - + - - - - - - - - @@ -39,13 +30,11 @@ - - - - - - - + + + + + diff --git a/Reqnroll/Table.cs b/Reqnroll/Table.cs index 3584cf680..826f65d7f 100644 --- a/Reqnroll/Table.cs +++ b/Reqnroll/Table.cs @@ -10,9 +10,7 @@ namespace Reqnroll /// /// An alias for the class for backwards compatibility. /// -#if !BODI_LIMITEDRUNTIME [Serializable] -#endif public class Table { internal const string ERROR_NO_CELLS_TO_ADD = "No cells to add"; @@ -175,9 +173,7 @@ private void AddTableRow(StringBuilder builder, IEnumerable cells, int[] } } -#if !BODI_LIMITEDRUNTIME [Serializable] -#endif public class DataTableRows : IEnumerable { private readonly List _innerList = new(); @@ -207,9 +203,7 @@ internal string[][] ToArray() } } -#if !BODI_LIMITEDRUNTIME [Serializable] -#endif public class DataTableRow : IDictionary { private readonly Table _table; diff --git a/Reqnroll/Tracing/AnsiColor/AnsiColor.cs b/Reqnroll/Tracing/AnsiColor/AnsiColor.cs index c92487a24..e33e81746 100644 --- a/Reqnroll/Tracing/AnsiColor/AnsiColor.cs +++ b/Reqnroll/Tracing/AnsiColor/AnsiColor.cs @@ -23,9 +23,6 @@ public readonly struct AnsiColor public static AnsiColor Background(int r, int g, int b) => new(TerminalControlSequences.SetBackgroundColor, new TerminalRgbColor(r, g, b)); public static AnsiColor Composite(params AnsiColor[] codes) => new(codes); -#if NET3_0_OR_GREATER - [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNull("text")] -#endif public static string? ColorizeText(string? text, AnsiColor code) { if (text == null) return null; @@ -70,7 +67,7 @@ public AnsiColor(TerminalControlSequences controlSequence, TerminalRgbColor rgbC private TerminalControlSequences GetResetSequence(TerminalControlSequences controlSequence) { - switch (ControlSequence) + switch (controlSequence) { case TerminalControlSequences.SetForegroundColor: return TerminalControlSequences.DefaultForegroundColor; case TerminalControlSequences.SetBackgroundColor: return TerminalControlSequences.DefaultBackgroundColor; diff --git a/Reqnroll/Tracing/LanguageHelper.cs b/Reqnroll/Tracing/LanguageHelper.cs index c22c5ab0a..dd9b0498f 100644 --- a/Reqnroll/Tracing/LanguageHelper.cs +++ b/Reqnroll/Tracing/LanguageHelper.cs @@ -4,9 +4,8 @@ using System.Linq; using Gherkin; using Reqnroll.Bindings; -using Reqnroll.Compatibility; using Reqnroll.Parser; - +using Reqnroll.PlatformCompatibility; namespace Reqnroll.Tracing { diff --git a/Reqnroll/UnitTestProvider/IUnitTestRuntimeProvider.cs b/Reqnroll/UnitTestProvider/IUnitTestRuntimeProvider.cs index 702bba205..4cebfa4b0 100644 --- a/Reqnroll/UnitTestProvider/IUnitTestRuntimeProvider.cs +++ b/Reqnroll/UnitTestProvider/IUnitTestRuntimeProvider.cs @@ -5,6 +5,5 @@ public interface IUnitTestRuntimeProvider void TestPending(string message); void TestInconclusive(string message); void TestIgnore(string message); - bool DelayedFixtureTearDown { get; } } } \ No newline at end of file diff --git a/Tests/Reqnroll.GeneratorTests/Reqnroll.GeneratorTests.csproj b/Tests/Reqnroll.GeneratorTests/Reqnroll.GeneratorTests.csproj index fed139e41..e89529de3 100644 --- a/Tests/Reqnroll.GeneratorTests/Reqnroll.GeneratorTests.csproj +++ b/Tests/Reqnroll.GeneratorTests/Reqnroll.GeneratorTests.csproj @@ -1,6 +1,6 @@ - + - $(Reqnroll_Test_TFM) + net8.0 Reqnroll.GeneratorTests $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) @@ -40,20 +40,7 @@ all runtime; build; native; contentfiles; analyzers - - - - - - - - - - - - - diff --git a/Tests/Reqnroll.GeneratorTests/nCrunchTemp_38b0de49-90e4-4313-9c7d-e1fe55c4c33d.csproj b/Tests/Reqnroll.GeneratorTests/nCrunchTemp_38b0de49-90e4-4313-9c7d-e1fe55c4c33d.csproj new file mode 100644 index 000000000..5018cc575 --- /dev/null +++ b/Tests/Reqnroll.GeneratorTests/nCrunchTemp_38b0de49-90e4-4313-9c7d-e1fe55c4c33d.csproj @@ -0,0 +1,125 @@ + + + $(Reqnroll_Test_TFM) + Reqnroll.GeneratorTests + $(Reqnroll_KeyFile) + $(Reqnroll_EnableStrongNameSigning) + $(Reqnroll_PublicSign) + Reqnroll.GeneratorTests + true + false + + + + + PreserveNewest + + + + + + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers + + + all + runtime; build; native; contentfiles; analyzers + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/App.config b/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/App.config deleted file mode 100644 index 41fc85e03..000000000 --- a/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/App.config +++ /dev/null @@ -1,17 +0,0 @@ - - - -
- - - - - - - - - - - diff --git a/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/Bindings/DoNothingBinding.cs b/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/Bindings/DoNothingBinding.cs deleted file mode 100644 index c1146ea26..000000000 --- a/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/Bindings/DoNothingBinding.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace Reqnroll.MsBuildNetSdk.IntegrationTests.Features -{ - [Binding] - public class DoNothingBinding - { - [Given(".*"), When(".*"), Then(".*")] - public void EmptyStep() - { - } - - [Given(".*"), When(".*"), Then(".*")] - public void EmptyStep(string multiLineStringParam) - { - } - - [Given(".*"), When(".*"), Then(".*")] - public void EmptyStep(DataTable tableParam) - { - } - } -} \ No newline at end of file diff --git a/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/CodeBehindFileGenerationTests.cs b/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/CodeBehindFileGenerationTests.cs deleted file mode 100644 index 985b1053c..000000000 --- a/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/CodeBehindFileGenerationTests.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Reflection; - -using Reqnroll.MsBuildNetSdk.IntegrationTests.Features; -using Xunit; - -namespace Reqnroll.MsBuildNetSdk.IntegrationTests -{ - - public class CodeBehindFileGenerationTests - { - [Fact] - public void TestIfCodeBehindFilesWasGeneratedAndCompiled() - { - var assemblyHoldingThisClass = Assembly.GetExecutingAssembly(); - var typeOfGeneratedFeatureFile = assemblyHoldingThisClass.GetType(typeof(DummyFeatureFileToTestMSBuildNetsdkCodebehindFileGenerationFeature).FullName); - Assert.NotNull(typeOfGeneratedFeatureFile); - } - } -} diff --git a/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/Features/dummy.feature b/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/Features/dummy.feature deleted file mode 100644 index 2f1cf31c0..000000000 --- a/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/Features/dummy.feature +++ /dev/null @@ -1,7 +0,0 @@ -Feature: Dummy feature file to test MSBuild netsdk codebehind file generation - -Scenario: Dummy scenario - Given I have a net.sdk style project - When the project is build - Then msbuild integration will generate code behind files - And nest generated codebehind files under corresponding feature file diff --git a/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/Reqnroll.MsBuildNetSdk.IntegrationTests.csproj b/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/Reqnroll.MsBuildNetSdk.IntegrationTests.csproj deleted file mode 100644 index 87568b7d9..000000000 --- a/Tests/Reqnroll.MsBuildNetSdk.IntegrationTests/Reqnroll.MsBuildNetSdk.IntegrationTests.csproj +++ /dev/null @@ -1,95 +0,0 @@ - - - - - - - - $(Reqnroll_Specs_TFM) - Always - false - true - true - true - - - - - - - - false - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers - - - all - runtime; build; native; contentfiles; analyzers - - - - - - - - Always - - - - - - - - <_Reqnroll_PluginTFM Condition=" '$(MSBuildRuntimeType)' == 'Core'">netcoreapp3.1 - <_Reqnroll_PluginTFM Condition=" '$(MSBuildRuntimeType)' != 'Core'">net462 - - - - - - - - - - - - - - <_Reqnroll_TaskFolder Condition=" '$(MSBuildRuntimeType)' == 'Core' And '$(_Reqnroll_TaskFolder)' == ''">netcoreapp3.1 - <_Reqnroll_TaskFolder Condition=" '$(MSBuildRuntimeType)' != 'Core' And '$(_Reqnroll_TaskFolder)' == ''">net462 - <_Reqnroll_TaskAssembly>..\..\Reqnroll.Tools.MsBuild.Generation\bin\$(Configuration)\$(_Reqnroll_TaskFolder)\Reqnroll.Tools.MsBuild.Generation.dll - - - - - - - - - - PreBuild; - $(BuildDependsOn) - - - PreBuild; - $(RebuildDependsOn) - - - - diff --git a/Tests/Reqnroll.PluginTests/Autofac/AutofacPluginTests.cs b/Tests/Reqnroll.PluginTests/Autofac/AutofacPluginTests.cs new file mode 100644 index 000000000..a40ce4bab --- /dev/null +++ b/Tests/Reqnroll.PluginTests/Autofac/AutofacPluginTests.cs @@ -0,0 +1,291 @@ +using System; +using System.Collections.Specialized; +using System.Globalization; +using System.Linq; +using System.Reflection; +using Autofac; +using Autofac.Core.Registration; +using FluentAssertions; +using Moq; +using Reqnroll.Autofac; +using Reqnroll.Autofac.ReqnrollPlugin; +using Reqnroll.BoDi; +using Reqnroll.Configuration; +using Reqnroll.Infrastructure; +using Reqnroll.Plugins; +using Reqnroll.UnitTestProvider; +using Xunit; + +namespace Reqnroll.PluginTests.Autofac; +public class AutofacPluginTests +{ + class TestableAutofacPlugin : AutofacPlugin + { + private readonly IConfigurationMethodsProvider _configurationMethodsProvider; + + public TestableAutofacPlugin(Type configType, params string[] configMethodNames) + { + var configMethods = configType.GetMethods().AsEnumerable(); + if (configMethodNames.Any()) + { + configMethods = configMethods.Where(m => configMethodNames.Contains(m.Name)); + } + var configurationMethodsProviderMock = new Mock(); + configurationMethodsProviderMock.Setup(m => m.GetConfigurationMethods()).Returns(configMethods.ToArray()); + _configurationMethodsProvider = configurationMethodsProviderMock.Object; + } + + protected override void RegisterTestRunPluginTypes(ObjectContainer testRunContainer) + { + base.RegisterTestRunPluginTypes(testRunContainer); + testRunContainer.RegisterInstanceAs(_configurationMethodsProvider); + } + } + + interface IScenarioDependency1; + class ScenarioDependency1 : IScenarioDependency1; + class ScenarioDependency2(IGlobalDependency1 globalDependency1) : IScenarioDependency1 + { + public IGlobalDependency1 CurrentGlobalDependency1 { get; } = globalDependency1; + } + + interface IGlobalDependency1; + class GlobalDependency1 : IGlobalDependency1; + + public class ContainerSetup1 + { + [GlobalDependencies] + public static void SetupGlobalContainer(global::Autofac.ContainerBuilder containerBuilder) + { + containerBuilder + .RegisterType() + .As() + .SingleInstance(); + } + + [ScenarioDependencies] + public static void SetupScenarioContainer(global::Autofac.ContainerBuilder containerBuilder) + { + containerBuilder + .RegisterType() + .As() + .SingleInstance(); + } + + [ScenarioDependencies] + public static void SetupScenarioContainerWithGlobalDep(global::Autofac.ContainerBuilder containerBuilder) + { + containerBuilder + .RegisterType() + .As() + .SingleInstance(); + } + } + + public class ContainerSetup2 + { + private static readonly ILifetimeScope _existingGlobalScope; + + static ContainerSetup2() + { + var containerBuilder = new global::Autofac.ContainerBuilder(); + ContainerSetup1.SetupGlobalContainer(containerBuilder); + _existingGlobalScope = containerBuilder.Build(); + } + + [FeatureDependencies] + public static ILifetimeScope GetFeatureLifetimeScope() + { + return _existingGlobalScope.BeginLifetimeScope(); + } + + [ScenarioDependencies] + public static void SetupScenarioContainer(global::Autofac.ContainerBuilder containerBuilder) + { + ContainerSetup1.SetupScenarioContainer(containerBuilder); + } + } + +private readonly RuntimePluginEvents _runtimePluginEvents; + private readonly ObjectContainer _testRunContainer; + private readonly ObjectContainer _testThreadContainer; + private readonly ObjectContainer _featureContainer; + + public AutofacPluginTests() + { + _runtimePluginEvents = new RuntimePluginEvents(); + _testRunContainer = new ObjectContainer(); + var testAssemblyProviderMock = new Mock(); + testAssemblyProviderMock.SetupGet(tap => tap.TestAssembly).Returns(Assembly.GetExecutingAssembly()); + _testRunContainer.RegisterInstanceAs(testAssemblyProviderMock.Object); + + _testThreadContainer = new ObjectContainer(_testRunContainer); + _featureContainer = new ObjectContainer(_testThreadContainer); + } + + [Fact] + public void Should_register_AutofacTestObjectResolver() + { + // Arrange + var sut = new AutofacPlugin(); + + // Act + sut.Initialize(_runtimePluginEvents, new RuntimePluginParameters(), new UnitTestProviderConfiguration()); + var reqnrollConfiguration = ConfigurationLoader.GetDefault(); + _runtimePluginEvents.RaiseCustomizeGlobalDependencies(_testRunContainer, reqnrollConfiguration); + + // Assert + _testRunContainer.IsRegistered().Should().BeTrue(); + _testRunContainer.Resolve().Should().BeOfType(); + } + + [Fact] + public void Should_allow_global_registrations_to_TestThreadContainer() + { + // Arrange + var sut = new TestableAutofacPlugin(typeof(ContainerSetup1), nameof(ContainerSetup1.SetupGlobalContainer)); + + // Act + sut.Initialize(_runtimePluginEvents, new RuntimePluginParameters(), new UnitTestProviderConfiguration()); + var reqnrollConfiguration = ConfigurationLoader.GetDefault(); + _runtimePluginEvents.RaiseCustomizeGlobalDependencies(_testRunContainer, reqnrollConfiguration); + + var testThreadContainer = new ObjectContainer(_testRunContainer); + _runtimePluginEvents.RaiseCustomizeTestThreadDependencies(testThreadContainer); + + // Assert + var resolver = _testRunContainer.Resolve(); + var globalDep1 = resolver.ResolveBindingInstance(typeof(IGlobalDependency1), testThreadContainer); + globalDep1.Should().NotBeNull(); + } + + private ObjectContainer InitializeToScenarioContainer(TestableAutofacPlugin sut) + { + sut.Initialize(_runtimePluginEvents, new RuntimePluginParameters(), new UnitTestProviderConfiguration()); + var reqnrollConfiguration = ConfigurationLoader.GetDefault(); + _runtimePluginEvents.RaiseCustomizeGlobalDependencies(_testRunContainer, reqnrollConfiguration); + _runtimePluginEvents.RaiseCustomizeTestThreadDependencies(_testThreadContainer); + _runtimePluginEvents.RaiseCustomizeFeatureDependencies(_featureContainer); + + var scenarioContainer = new ObjectContainer(_featureContainer); + _runtimePluginEvents.RaiseCustomizeScenarioDependencies(scenarioContainer); + + return scenarioContainer; + } + + [Fact] + public void Should_allow_scenario_specific_registrations() + { + // Arrange + var sut = new TestableAutofacPlugin(typeof(ContainerSetup1), + nameof(ContainerSetup1.SetupGlobalContainer), + nameof(ContainerSetup1.SetupScenarioContainer)); + + // Act + var scenarioContainer = InitializeToScenarioContainer(sut); + + // Assert + var resolver = _testRunContainer.Resolve(); + var scenarioDep1 = resolver.ResolveBindingInstance(typeof(IScenarioDependency1), scenarioContainer); + scenarioDep1.Should().NotBeNull(); + + var globalDep1 = resolver.ResolveBindingInstance(typeof(IGlobalDependency1), scenarioContainer); + globalDep1.Should().NotBeNull(); + } + + [Fact] + public void Should_allow_scenario_specific_registrations_without_global_registrations() + { + // Arrange + var sut = new TestableAutofacPlugin(typeof(ContainerSetup1), + nameof(ContainerSetup1.SetupScenarioContainer)); + + // Act + var scenarioContainer = InitializeToScenarioContainer(sut); + + // Assert + var resolver = _testRunContainer.Resolve(); + var scenarioDep1 = resolver.ResolveBindingInstance(typeof(IScenarioDependency1), scenarioContainer); + scenarioDep1.Should().NotBeNull(); + + FluentActions.Invoking(() => resolver.ResolveBindingInstance(typeof(IGlobalDependency1), scenarioContainer)) + .Should() + .Throw(); + } + + + [Fact] + public void Should_allow_resolving_common_reqnroll_objects() + { + // Arrange + var sut = new TestableAutofacPlugin(typeof(ContainerSetup1), + nameof(ContainerSetup1.SetupGlobalContainer), + nameof(ContainerSetup1.SetupScenarioContainer)); + + // Act + var scenarioContainer = InitializeToScenarioContainer(sut); + var resolver = _testRunContainer.Resolve(); + var scenarioContext = new ScenarioContext(scenarioContainer, new ScenarioInfo("", "", Array.Empty(), new OrderedDictionary()), resolver); + scenarioContainer.RegisterInstanceAs(scenarioContext); + var featureContext = new FeatureContext(scenarioContainer, new FeatureInfo(CultureInfo.CurrentCulture, "", "", ""), ConfigurationLoader.GetDefault()); + _featureContainer.RegisterInstanceAs(featureContext); + var testThreadContext = new TestThreadContext(_testThreadContainer); + _testThreadContainer.RegisterInstanceAs(testThreadContext); + + // Assert + var resolvedContainer = resolver.ResolveBindingInstance(typeof(IObjectContainer), scenarioContainer); + resolvedContainer.Should().BeSameAs(scenarioContainer); + + var resolvedScenarioContext = resolver.ResolveBindingInstance(typeof(ScenarioContext), scenarioContainer); + resolvedScenarioContext.Should().BeSameAs(scenarioContext); + + var resolvedFeatureContext = resolver.ResolveBindingInstance(typeof(FeatureContext), scenarioContainer); + resolvedFeatureContext.Should().BeSameAs(featureContext); + + var resolvedTestThreadContext = resolver.ResolveBindingInstance(typeof(TestThreadContext), scenarioContainer); + resolvedTestThreadContext.Should().BeSameAs(testThreadContext); + } + + + [Fact] + public void Should_allow_having_scenario_dependency_that_depends_on_a_global() + { + // Arrange + var sut = new TestableAutofacPlugin(typeof(ContainerSetup1), + nameof(ContainerSetup1.SetupGlobalContainer), + nameof(ContainerSetup1.SetupScenarioContainerWithGlobalDep)); + + // Act + var scenarioContainer = InitializeToScenarioContainer(sut); + + // Assert + + + var resolver = _testRunContainer.Resolve(); + var scenarioDep1 = resolver.ResolveBindingInstance(typeof(IScenarioDependency1), scenarioContainer); + var dep2 = scenarioDep1.Should().BeOfType().Subject; + + var globalDep1 = resolver.ResolveBindingInstance(typeof(IGlobalDependency1), scenarioContainer); + globalDep1.Should().NotBeNull(); + + dep2.CurrentGlobalDependency1.Should().BeSameAs(globalDep1); + } + + [Fact] + public void Should_allow_using_existing_global_scope() + { + // Arrange + var sut = new TestableAutofacPlugin(typeof(ContainerSetup2)); + + // Act + var scenarioContainer = InitializeToScenarioContainer(sut); + + // Assert + var resolver = _testRunContainer.Resolve(); + var scenarioDep1 = resolver.ResolveBindingInstance(typeof(IScenarioDependency1), scenarioContainer); + scenarioDep1.Should().NotBeNull(); + + var globalDep1 = resolver.ResolveBindingInstance(typeof(IGlobalDependency1), scenarioContainer); + globalDep1.Should().NotBeNull(); + } +} diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/CsvLoaderTests.cs b/Tests/Reqnroll.PluginTests/ExternalData/CsvLoaderTests.cs similarity index 96% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/CsvLoaderTests.cs rename to Tests/Reqnroll.PluginTests/ExternalData/CsvLoaderTests.cs index 39973d95a..4e66d7c5b 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/CsvLoaderTests.cs +++ b/Tests/Reqnroll.PluginTests/ExternalData/CsvLoaderTests.cs @@ -1,15 +1,15 @@ using System; -using System.Globalization; using System.IO; using System.Reflection; +using Reqnroll.ExternalData.ReqnrollPlugin; using Reqnroll.ExternalData.ReqnrollPlugin.Loaders; using Xunit; -namespace Reqnroll.ExternalData.ReqnrollPlugin.UnitTests +namespace Reqnroll.PluginTests.ExternalData { public class CsvLoaderTests { - private static readonly string SampleFilesFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "SampleFiles"); + private static readonly string SampleFilesFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "ExternalData", "SampleFiles"); private string _productsSampleFilePath = Path.Combine(SampleFilesFolder, "products.csv"); private string SampleFeatureFilePathInSampleFileFolder => diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/DataSourceLoaderFactoryTests.cs b/Tests/Reqnroll.PluginTests/ExternalData/DataSourceLoaderFactoryTests.cs similarity index 97% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/DataSourceLoaderFactoryTests.cs rename to Tests/Reqnroll.PluginTests/ExternalData/DataSourceLoaderFactoryTests.cs index 20e80086f..f574f71c6 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/DataSourceLoaderFactoryTests.cs +++ b/Tests/Reqnroll.PluginTests/ExternalData/DataSourceLoaderFactoryTests.cs @@ -1,10 +1,11 @@ using System; using Reqnroll.BoDi; +using Reqnroll.ExternalData.ReqnrollPlugin; using Reqnroll.ExternalData.ReqnrollPlugin.DataSources; using Reqnroll.ExternalData.ReqnrollPlugin.Loaders; using Xunit; -namespace Reqnroll.ExternalData.ReqnrollPlugin.UnitTests +namespace Reqnroll.PluginTests.ExternalData { public class DataSourceLoaderFactoryTests { diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/DataSources/Selectors/DataSourceSelectorParserTests.cs b/Tests/Reqnroll.PluginTests/ExternalData/DataSources/Selectors/DataSourceSelectorParserTests.cs similarity index 88% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/DataSources/Selectors/DataSourceSelectorParserTests.cs rename to Tests/Reqnroll.PluginTests/ExternalData/DataSources/Selectors/DataSourceSelectorParserTests.cs index d742f5589..2615edf6e 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/DataSources/Selectors/DataSourceSelectorParserTests.cs +++ b/Tests/Reqnroll.PluginTests/ExternalData/DataSources/Selectors/DataSourceSelectorParserTests.cs @@ -2,7 +2,7 @@ using Reqnroll.ExternalData.ReqnrollPlugin.DataSources.Selectors; using Xunit; -namespace Reqnroll.ExternalData.ReqnrollPlugin.UnitTests.DataSources.Selectors +namespace Reqnroll.PluginTests.ExternalData.DataSources.Selectors { public class DataSourceSelectorParserTests { diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/DataSources/Selectors/DataSourceSelectorTests.cs b/Tests/Reqnroll.PluginTests/ExternalData/DataSources/Selectors/DataSourceSelectorTests.cs similarity index 94% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/DataSources/Selectors/DataSourceSelectorTests.cs rename to Tests/Reqnroll.PluginTests/ExternalData/DataSources/Selectors/DataSourceSelectorTests.cs index 27bbd88c5..7d4b6a969 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/DataSources/Selectors/DataSourceSelectorTests.cs +++ b/Tests/Reqnroll.PluginTests/ExternalData/DataSources/Selectors/DataSourceSelectorTests.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; +using Reqnroll.ExternalData.ReqnrollPlugin; using Reqnroll.ExternalData.ReqnrollPlugin.DataSources; using Reqnroll.ExternalData.ReqnrollPlugin.DataSources.Selectors; using Xunit; -namespace Reqnroll.ExternalData.ReqnrollPlugin.UnitTests.DataSources.Selectors +namespace Reqnroll.PluginTests.ExternalData.DataSources.Selectors { public class DataSourceSelectorTests { diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/ExcelLoaderTests.cs b/Tests/Reqnroll.PluginTests/ExternalData/ExcelLoaderTests.cs similarity index 97% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/ExcelLoaderTests.cs rename to Tests/Reqnroll.PluginTests/ExternalData/ExcelLoaderTests.cs index 5d81919e4..a5e3aac98 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/ExcelLoaderTests.cs +++ b/Tests/Reqnroll.PluginTests/ExternalData/ExcelLoaderTests.cs @@ -5,11 +5,11 @@ using Reqnroll.ExternalData.ReqnrollPlugin.Loaders; using Xunit; -namespace Reqnroll.ExternalData.ReqnrollPlugin.UnitTests +namespace Reqnroll.PluginTests.ExternalData { public class ExcelLoaderTests { - private static readonly string SampleFilesFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "SampleFiles"); + private static readonly string SampleFilesFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "ExternalData", "SampleFiles"); private readonly string _productsSampleFilePath = Path.Combine(SampleFilesFolder, "products.xlsx"); private ExcelLoader CreateSut() => new(); diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/ExternalDataSpecificationTests.cs b/Tests/Reqnroll.PluginTests/ExternalData/ExternalDataSpecificationTests.cs similarity index 96% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/ExternalDataSpecificationTests.cs rename to Tests/Reqnroll.PluginTests/ExternalData/ExternalDataSpecificationTests.cs index 86857ecd2..ef6ea0af8 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/ExternalDataSpecificationTests.cs +++ b/Tests/Reqnroll.PluginTests/ExternalData/ExternalDataSpecificationTests.cs @@ -5,11 +5,11 @@ using Reqnroll.ExternalData.ReqnrollPlugin.DataSources.Selectors; using Xunit; -namespace Reqnroll.ExternalData.ReqnrollPlugin.UnitTests +namespace Reqnroll.PluginTests.ExternalData { public class ExternalDataSpecificationTests { - private ReqnrollPlugin.DataSources.DataTable CreateProductDataTable() + private Reqnroll.ExternalData.ReqnrollPlugin.DataSources.DataTable CreateProductDataTable() { return new(new []{ "product", "price", "color"}) { @@ -22,7 +22,7 @@ private ReqnrollPlugin.DataSources.DataTable CreateProductDataTable() }; } - private ReqnrollPlugin.DataSources.DataTable CreateUserDataTable() + private Reqnroll.ExternalData.ReqnrollPlugin.DataSources.DataTable CreateUserDataTable() { return new(new []{ "name" }) { diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/IncludeExternalDataTransformationTests.cs b/Tests/Reqnroll.PluginTests/ExternalData/IncludeExternalDataTransformationTests.cs similarity index 98% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/IncludeExternalDataTransformationTests.cs rename to Tests/Reqnroll.PluginTests/ExternalData/IncludeExternalDataTransformationTests.cs index 3271e9530..0b8b81918 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/IncludeExternalDataTransformationTests.cs +++ b/Tests/Reqnroll.PluginTests/ExternalData/IncludeExternalDataTransformationTests.cs @@ -8,7 +8,7 @@ using Reqnroll.Parser; using Xunit; -namespace Reqnroll.ExternalData.ReqnrollPlugin.UnitTests +namespace Reqnroll.PluginTests.ExternalData { public class IncludeExternalDataTransformationTests { @@ -25,7 +25,7 @@ public IncludeExternalDataTransformationTests() private IncludeExternalDataTransformation CreateSut() => new(_specificationProviderMock.Object); - private ReqnrollPlugin.DataSources.DataTable CreateProductDataTable() + private Reqnroll.ExternalData.ReqnrollPlugin.DataSources.DataTable CreateProductDataTable() { return new(new []{"product", "price"}) { diff --git a/Tests/Reqnroll.PluginTests/ExternalData/JsonLoaderTests.cs b/Tests/Reqnroll.PluginTests/ExternalData/JsonLoaderTests.cs new file mode 100644 index 000000000..80c188323 --- /dev/null +++ b/Tests/Reqnroll.PluginTests/ExternalData/JsonLoaderTests.cs @@ -0,0 +1,136 @@ +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using Newtonsoft.Json; +using Reqnroll.ExternalData.ReqnrollPlugin; +using Reqnroll.ExternalData.ReqnrollPlugin.Loaders; +using Xunit; + +namespace Reqnroll.PluginTests.ExternalData +{ + public class JsonLoaderTests + { + private static readonly string SampleFilesFolder = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location) ?? ".", "ExternalData", "SampleFiles"); + private readonly string _productsSampleFilePath = Path.Combine(SampleFilesFolder, "products.json"); + private readonly string _productsInvalidSampleFilePath = Path.Combine(SampleFilesFolder, "products-invalid.json"); + private readonly string _nestedProductsSampleFilePath = Path.Combine(SampleFilesFolder, "products-nested-dataset.json"); + + private string SampleFeatureFilePathInSampleFileFolder => + Path.Combine(Path.GetDirectoryName(_productsSampleFilePath) ?? ".", "Sample.feature"); + + private JsonLoader CreateSut() => new(); + + [Fact] + public void Can_read_simple_json_file() + { + var sut = CreateSut(); + var result = sut.LoadDataSource(_productsSampleFilePath, null); + + Assert.NotNull(result); + Assert.True(result.IsDataRecord); + Assert.True(result.AsDataRecord.Fields.ContainsKey("products")); + var worksheetResult = result.AsDataRecord.Fields["products"]; + Assert.True(worksheetResult.IsDataTable); + Assert.Equal(3, worksheetResult.AsDataTable.Items.Count); + Assert.Equal("Chocolate", worksheetResult.AsDataTable.Items[0].Fields["product"].AsString()); + Assert.Equal("2.5", worksheetResult.AsDataTable.Items[0].Fields["price"].AsString(CultureInfo.GetCultureInfo("en-us"))); + Assert.Equal("brown", worksheetResult.AsDataTable.Items[0].Fields["color"].AsString()); + } + + [Fact] + public void Can_handle_properties_with_spaces() + { + var sut = CreateSut(); + var result = sut.LoadDataSource(_productsSampleFilePath, null); + + Assert.NotNull(result); + Assert.True(result.IsDataRecord); + Assert.True(result.AsDataRecord.Fields.ContainsKey("other products")); + var worksheetResult = result.AsDataRecord.Fields["other products"]; + Assert.True(worksheetResult.IsDataTable); + Assert.Equal(2, worksheetResult.AsDataTable.Items.Count); + Assert.Equal("Cookie", worksheetResult.AsDataTable.Items[0].Fields["product"].AsString()); + Assert.Equal("2.5", worksheetResult.AsDataTable.Items[0].Fields["price"].AsString(CultureInfo.GetCultureInfo("en-us"))); + Assert.Equal("brown", worksheetResult.AsDataTable.Items[0].Fields["color"].AsString()); + } + + [Fact] + public void Can_handle_relative_path() + { + var sut = CreateSut(); + var result = sut.LoadDataSource( + Path.GetFileName(_productsSampleFilePath), + SampleFeatureFilePathInSampleFileFolder); + + Assert.True(result.IsDataRecord); + } + + + [Fact] + public void Returns_name_of_first_object_array_as_default_data_set() + { + var sut = CreateSut(); + var result = sut.LoadDataSource(_productsSampleFilePath, null); + + Assert.NotNull(result); + Assert.Equal("products", result.DefaultDataSet); + } + + [Fact] + public void Can_read_products_from_json_with_nested_dataset() + { + var sut = CreateSut(); + var result = sut.LoadDataSource(_nestedProductsSampleFilePath, null); + + Assert.NotNull(result); + Assert.True(result.IsDataRecord); + Assert.True(result.AsDataRecord.Fields.ContainsKey("products")); + var worksheetResult = result.AsDataRecord.Fields["products"]; + Assert.True(worksheetResult.IsDataTable); + Assert.Equal(3, worksheetResult.AsDataTable.Items.Count); + var firstItem = worksheetResult.AsDataTable.Items[0]; + Assert.Equal(3, firstItem.Fields.Count); + Assert.Equal("Chocolate", firstItem.Fields["product"].AsString()); + Assert.Equal("brown", firstItem.Fields["color"].AsString()); + Assert.Equal($"[{Environment.NewLine} {{{Environment.NewLine} \"name\": \"Dark Chocolate\",{Environment.NewLine} \"price\": \"1.6\"{Environment.NewLine} }},{Environment.NewLine} {{{Environment.NewLine} \"name\": \"Milk Chocolate\",{Environment.NewLine} \"price\": \"1.55\"{Environment.NewLine} }}{Environment.NewLine}]", firstItem.Fields["varieties"].AsString()); + } + + [Fact] + public void Reads_nested_dataset() + { + var sut = CreateSut(); + var result = sut.LoadDataSource(_nestedProductsSampleFilePath, null); + + Assert.NotNull(result); + Assert.True(result.IsDataRecord); + Assert.True(result.AsDataRecord.Fields.ContainsKey("products.varieties")); + var worksheetResult = result.AsDataRecord.Fields["products.varieties"]; + Assert.True(worksheetResult.IsDataTable); + Assert.Equal(6, worksheetResult.AsDataTable.Items.Count); + var firstItem = worksheetResult.AsDataTable.Items.First(); + Assert.Equal(4, firstItem.Fields.Count); + Assert.Equal("Chocolate", firstItem.Fields["product"].AsString()); + Assert.Equal("brown", firstItem.Fields["color"].AsString()); + Assert.Equal("1.6", firstItem.Fields["price"].AsString(CultureInfo.GetCultureInfo("en-us"))); + Assert.Equal("Dark Chocolate", firstItem.Fields["name"].AsString()); + + var lastItem = worksheetResult.AsDataTable.Items.Last(); + Assert.Equal(4, lastItem.Fields.Count); + Assert.Equal("Orange", lastItem.Fields["product"].AsString()); + Assert.Equal("orange", lastItem.Fields["color"].AsString()); + Assert.Equal("1.6", lastItem.Fields["price"].AsString(CultureInfo.GetCultureInfo("en-us"))); + Assert.Equal("Tangerine", lastItem.Fields["name"].AsString()); + } + + [Fact] + public void Can_handle_invalid_json_file_format() + { + var sut = CreateSut(); + + var externalDataPluginException = Assert.Throws(() => sut.LoadDataSource(_productsInvalidSampleFilePath, null)); + Assert.IsType(externalDataPluginException.InnerException); + } + } +} diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SampleFiles/products-empty.csv b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-empty.csv similarity index 100% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SampleFiles/products-empty.csv rename to Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-empty.csv diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SampleFiles/products-invalid.csv b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-invalid.csv similarity index 100% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SampleFiles/products-invalid.csv rename to Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-invalid.csv diff --git a/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-invalid.json b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-invalid.json new file mode 100644 index 000000000..a35073557 --- /dev/null +++ b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-invalid.json @@ -0,0 +1,32 @@ +{ + "products": + [ + { + "product": "Chocolate", + "price": "2.5", + "color": "brown", + { + "product": "Apple", + "price": "1.0", + "color": "red" + }, + { + "product": "Orange", + "price": "1.2", + "color": "orange" + } + ], + "other products": + [ + { + "product": "Cookie", + "price": "2.5", + "color": "brown" + }, + { + "product": "Bananas", + "price": "1.0", + "color": "yellow" + } + ] +} diff --git a/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-nested-dataset.json b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-nested-dataset.json new file mode 100644 index 000000000..f6d290d4b --- /dev/null +++ b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-nested-dataset.json @@ -0,0 +1,50 @@ +{ + "products": + [ + { + "product": "Chocolate", + "color": "brown", + "varieties": + [ + { + "name": "Dark Chocolate", + "price": "1.6" + }, + { + "name": "Milk Chocolate", + "price": "1.55" + } + ] + }, + { + "product": "Apple", + "color": "red", + "varieties": + [ + { + "name": "Pink Lady", + "price": "1.0" + }, + { + "name": "Fuji", + "price": "1.5" + } + ] + }, + { + "product": "Orange", + "color": "orange", + "varieties": + [ + { + "name": "Seville Orange", + "price": "1.3" + }, + { + "name": "Tangerine", + "price": "1.6" + } + ] + } + ] +} diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SampleFiles/products-special.csv b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-special.csv similarity index 100% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SampleFiles/products-special.csv rename to Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products-special.csv diff --git a/Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Features/products.csv b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products.csv similarity index 100% rename from Plugins/Reqnroll.ExternalData/sample/ExternalDataSample/Features/products.csv rename to Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products.csv diff --git a/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products.json b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products.json new file mode 100644 index 000000000..7abf4b3f2 --- /dev/null +++ b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products.json @@ -0,0 +1,33 @@ +{ + "products": + [ + { + "product": "Chocolate", + "price": "2.5", + "color": "brown" + }, + { + "product": "Apple", + "price": "1.0", + "color": "red" + }, + { + "product": "Orange", + "price": "1.2", + "color": "orange" + } + ], + "other products": + [ + { + "product": "Cookie", + "price": "2.5", + "color": "brown" + }, + { + "product": "Bananas", + "price": "1.0", + "color": "yellow" + } + ] +} diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SampleFiles/products.xlsx b/Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products.xlsx similarity index 100% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SampleFiles/products.xlsx rename to Tests/Reqnroll.PluginTests/ExternalData/SampleFiles/products.xlsx diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/ScenarioTransformationTests.cs b/Tests/Reqnroll.PluginTests/ExternalData/ScenarioTransformationTests.cs similarity index 99% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/ScenarioTransformationTests.cs rename to Tests/Reqnroll.PluginTests/ExternalData/ScenarioTransformationTests.cs index 0913eefbb..d1f65c0d2 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/ScenarioTransformationTests.cs +++ b/Tests/Reqnroll.PluginTests/ExternalData/ScenarioTransformationTests.cs @@ -6,7 +6,7 @@ using Reqnroll.Parser; using Xunit; -namespace Reqnroll.ExternalData.ReqnrollPlugin.UnitTests +namespace Reqnroll.PluginTests.ExternalData { public class ScenarioTransformationTests { diff --git a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SpecificationProviderTests.cs b/Tests/Reqnroll.PluginTests/ExternalData/SpecificationProviderTests.cs similarity index 98% rename from Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SpecificationProviderTests.cs rename to Tests/Reqnroll.PluginTests/ExternalData/SpecificationProviderTests.cs index ab80774c2..892b77d8e 100644 --- a/Plugins/Reqnroll.ExternalData/Reqnroll.ExternalData.ReqnrollPlugin.UnitTests/SpecificationProviderTests.cs +++ b/Tests/Reqnroll.PluginTests/ExternalData/SpecificationProviderTests.cs @@ -1,12 +1,13 @@ using System; using Gherkin.Ast; using Moq; +using Reqnroll.ExternalData.ReqnrollPlugin; using Reqnroll.ExternalData.ReqnrollPlugin.DataSources; using Reqnroll.ExternalData.ReqnrollPlugin.DataSources.Selectors; using Reqnroll.ExternalData.ReqnrollPlugin.Loaders; using Xunit; -namespace Reqnroll.ExternalData.ReqnrollPlugin.UnitTests +namespace Reqnroll.PluginTests.ExternalData { public class SpecificationProviderTests { diff --git a/Tests/Reqnroll.PluginTests/Generator/GeneratorPluginLoaderTests.cs b/Tests/Reqnroll.PluginTests/Generator/GeneratorPluginLoaderTests.cs index 4a57cb101..2c65090a2 100644 --- a/Tests/Reqnroll.PluginTests/Generator/GeneratorPluginLoaderTests.cs +++ b/Tests/Reqnroll.PluginTests/Generator/GeneratorPluginLoaderTests.cs @@ -1,6 +1,3 @@ -using System; -using System.IO; -using System.Reflection; using FluentAssertions; using Reqnroll.Generator.Plugins; using Reqnroll.Plugins; @@ -15,7 +12,7 @@ public class GeneratorPluginLoaderTests public void LoadPlugin_LoadXUnitSuccessfully() { //ARRANGE - var generatorPluginLoader = new GeneratorPluginLoader(); + var generatorPluginLoader = new GeneratorPluginLoader(new PluginAssemblyLoader()); //ACT var pluginDescriptor = new PluginDescriptor("Reqnroll.xUnit.Generator.ReqnrollPlugin", "Reqnroll.xUnit.Generator.ReqnrollPlugin.dll", PluginType.Generator, ""); diff --git a/Tests/Reqnroll.PluginTests/Infrastructure/WindsorPluginTests.cs b/Tests/Reqnroll.PluginTests/Infrastructure/WindsorPluginTests.cs index f6b0140df..3910edf0a 100644 --- a/Tests/Reqnroll.PluginTests/Infrastructure/WindsorPluginTests.cs +++ b/Tests/Reqnroll.PluginTests/Infrastructure/WindsorPluginTests.cs @@ -21,7 +21,7 @@ public class WindsorPluginTests [Fact] public void Can_load_Windsor_plugin() { - var loader = new RuntimePluginLoader(); + var loader = new RuntimePluginLoader(new PluginAssemblyLoader()); var listener = new Mock(); var plugin = loader.LoadPlugin("Reqnroll.Windsor.ReqnrollPlugin.dll", listener.Object, It.IsAny()); diff --git a/Tests/Reqnroll.PluginTests/Reqnroll.PluginTests.csproj b/Tests/Reqnroll.PluginTests/Reqnroll.PluginTests.csproj index 11a33c4e1..9547d9520 100644 --- a/Tests/Reqnroll.PluginTests/Reqnroll.PluginTests.csproj +++ b/Tests/Reqnroll.PluginTests/Reqnroll.PluginTests.csproj @@ -1,6 +1,6 @@ - $(Reqnroll_Test_TFM) + net8.0 Reqnroll.PluginTests $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) @@ -11,6 +11,8 @@ + + @@ -34,4 +36,31 @@ + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + diff --git a/Tests/Reqnroll.RuntimeTests/Bindings/BindingInvokerTests.cs b/Tests/Reqnroll.RuntimeTests/Bindings/BindingInvokerTests.cs index af6bbfbb4..e965c7f1f 100644 --- a/Tests/Reqnroll.RuntimeTests/Bindings/BindingInvokerTests.cs +++ b/Tests/Reqnroll.RuntimeTests/Bindings/BindingInvokerTests.cs @@ -283,35 +283,35 @@ class StepDefClassWithAsyncLocal public string LoadedValue { get; set; } // ReSharper disable once UnusedMember.Local - public void SetAsyncLocal_Sync(AsyncLocalType asyncLocalType) + public void SetAsyncLocal_Sync(AsyncLocalType asyncLocalType, string content) { switch (asyncLocalType) { case AsyncLocalType.Uninitialized: - _uninitializedAsyncLocal.Value = "42"; + _uninitializedAsyncLocal.Value = content; break; case AsyncLocalType.CtorInitialized: - _ctorInitializedAsyncLocal.Value = "42"; + _ctorInitializedAsyncLocal.Value = content; break; case AsyncLocalType.Boxed: - _boxedAsyncLocal.Value!.Value = "42"; + _boxedAsyncLocal.Value!.Value = content; break; } } // ReSharper disable once UnusedMember.Local - public async Task SetAsyncLocal_Async(AsyncLocalType asyncLocalType) + public async Task SetAsyncLocal_Async(AsyncLocalType asyncLocalType, string content) { switch (asyncLocalType) { case AsyncLocalType.Uninitialized: - _uninitializedAsyncLocal.Value = "42"; + _uninitializedAsyncLocal.Value = content; break; case AsyncLocalType.CtorInitialized: - _ctorInitializedAsyncLocal.Value = "42"; + _ctorInitializedAsyncLocal.Value = content; break; case AsyncLocalType.Boxed: - _boxedAsyncLocal.Value!.Value = "42"; + _boxedAsyncLocal.Value!.Value = content; break; } await Task.Delay(1); @@ -352,13 +352,26 @@ public async Task ExecutionContext_is_flowing_down_correctly_to_step_definitions var contextManager = CreateContextManagerWith(); if (setAs != null) - await InvokeBindingAsync(sut, contextManager, typeof(StepDefClassWithAsyncLocal), "SetAsyncLocal_" + setAs, asyncLocalType); + await InvokeBindingAsync(sut, contextManager, typeof(StepDefClassWithAsyncLocal), "SetAsyncLocal_" + setAs, asyncLocalType, "42"); await InvokeBindingAsync(sut, contextManager, typeof(StepDefClassWithAsyncLocal), nameof(StepDefClassWithAsyncLocal.GetAsyncLocal_Async), asyncLocalType, expectedResult); var stepDefClass = contextManager.ScenarioContext.ScenarioContainer.Resolve(); stepDefClass.LoadedValue.Should().Be(expectedResult, $"Error was not propagated for {description}"); } + [Fact] + public async Task ExecutionContext_can_be_changed_multiple_times() + { + var sut = CreateSut(); + var contextManager = CreateContextManagerWith(); + + await InvokeBindingAsync(sut, contextManager, typeof(StepDefClassWithAsyncLocal), "SetAsyncLocal_Sync", AsyncLocalType.Uninitialized, "14"); + await InvokeBindingAsync(sut, contextManager, typeof(StepDefClassWithAsyncLocal), "SetAsyncLocal_Sync", AsyncLocalType.Uninitialized, "42"); + await InvokeBindingAsync(sut, contextManager, typeof(StepDefClassWithAsyncLocal), nameof(StepDefClassWithAsyncLocal.GetAsyncLocal_Async), AsyncLocalType.Uninitialized, "42"); + var stepDefClass = contextManager.ScenarioContext.ScenarioContainer.Resolve(); + stepDefClass.LoadedValue.Should().Be("42", $"Error was not propagated"); + } + #endregion #region Exception Handling related tests - regression tests for SF2649 diff --git a/Tests/Reqnroll.RuntimeTests/Bindings/CucumberExpressions/CucumberExpressionIntegrationTests.cs b/Tests/Reqnroll.RuntimeTests/Bindings/CucumberExpressions/CucumberExpressionIntegrationTests.cs index 7e1095ee8..c81fb54aa 100644 --- a/Tests/Reqnroll.RuntimeTests/Bindings/CucumberExpressions/CucumberExpressionIntegrationTests.cs +++ b/Tests/Reqnroll.RuntimeTests/Bindings/CucumberExpressions/CucumberExpressionIntegrationTests.cs @@ -57,6 +57,11 @@ public override bool Equals(object obj) public override int GetHashCode() => UserName.GetHashCode(); } +public record SampleComplexUser(string Firstname, string Surname, int Age, int Height) +{ + public static SampleComplexUser Create(string firstname, string surname, int age, int height) => new(firstname, surname, age, height); +} + public class CucumberExpressionIntegrationTests { public class SampleBindings @@ -100,10 +105,25 @@ public void StepDefWithCustomClassParam(SampleUser userParam) ExecutedParams.Add((userParam, typeof(SampleUser))); } + public void StepDefWithCustomComplexClassParam(SampleComplexUser userParam) + { + ExecutedParams.Add((userParam, typeof(SampleComplexUser))); + } + public int ConvertFortyTwo() { return 42; } + + public string ConvertToStringLowercase(string str) + { + return str.ToLower(); + } + + public int ConvertToIntPlus100(int nr) + { + return nr + 100; + } } public class TestDependencyProvider : DefaultDependencyProvider @@ -423,4 +443,64 @@ public async void Should_match_step_with_customized_built_in_parameter_with_simp sampleBindings.ExecutedParams.Should().Contain(expectedParam); } + + [Fact] + public async void Should_match_step_with_customized_built_in_parameter_without_recursion_string() + { + var expression = "there is a user {string} registered"; + var stepText = "there is a user 'Marvin' registered"; + var expectedParam = ("marvin", typeof(string)); + var methodName = nameof(SampleBindings.StepDefWithStringParam); + + IStepArgumentTransformationBinding transformation = new StepArgumentTransformationBinding( + (string)null, + new RuntimeBindingMethod(typeof(SampleBindings).GetMethod(nameof(SampleBindings.ConvertToStringLowercase)))); + + var sampleBindings = await PerformStepExecution(methodName, expression, stepText, new[] { transformation }); + + sampleBindings.ExecutedParams.Should().Contain(expectedParam); + } + + [Fact] + public async void Should_match_step_with_customized_built_in_parameter_without_recursion_int32() + { + var expression = "I have {int} cucumbers in my belly"; + var stepText = "I have 43 cucumbers in my belly"; + var expectedParam = (143, typeof(int)); + var methodName = nameof(SampleBindings.StepDefWithIntParam); + + IStepArgumentTransformationBinding transformation = new StepArgumentTransformationBinding( + (string)null, + new RuntimeBindingMethod(typeof(SampleBindings).GetMethod(nameof(SampleBindings.ConvertToIntPlus100)))); + + var sampleBindings = await PerformStepExecution(methodName, expression, stepText, new[] { transformation }); + + sampleBindings.ExecutedParams.Should().Contain(expectedParam); + } + + [Fact] + public async void Should_match_step_with_custom_parameter_with_additional_step_arguments() + { + var expression = "there is a {user} registered"; + var stepText = "there is a user Marvin Smith he is 27 years old and 175 height registered"; + var expectedParam = (new SampleComplexUser("marvin", "smith", 127, 275), typeof(SampleComplexUser)); + var methodName = nameof(SampleBindings.StepDefWithCustomComplexClassParam); + + IStepArgumentTransformationBinding transformationStringWithouthBlanks = new StepArgumentTransformationBinding( + (string)null, + new RuntimeBindingMethod(typeof(SampleBindings).GetMethod(nameof(SampleBindings.ConvertToStringLowercase)))); + + IStepArgumentTransformationBinding transformationIntPlus100 = new StepArgumentTransformationBinding( + (string)null, + new RuntimeBindingMethod(typeof(SampleBindings).GetMethod(nameof(SampleBindings.ConvertToIntPlus100)))); + + IStepArgumentTransformationBinding transformationSampleComplexUser = new StepArgumentTransformationBinding( + "user ([A-Za-z]+) ([A-Za-z]+) he is ([0-9]+) years old and ([0-9]+) height", + new RuntimeBindingMethod(typeof(SampleComplexUser).GetMethod(nameof(SampleComplexUser.Create))), + "user"); + + var sampleBindings = await PerformStepExecution(methodName, expression, stepText, new[] { transformationStringWithouthBlanks, transformationIntPlus100, transformationSampleComplexUser }); + + sampleBindings.ExecutedParams.Should().Contain(expectedParam); + } } diff --git a/Tests/Reqnroll.RuntimeTests/Bindings/CucumberExpressions/CucumberExpressionParameterTypeRegistryTests.cs b/Tests/Reqnroll.RuntimeTests/Bindings/CucumberExpressions/CucumberExpressionParameterTypeRegistryTests.cs index ccc3e6e67..72781052a 100644 --- a/Tests/Reqnroll.RuntimeTests/Bindings/CucumberExpressions/CucumberExpressionParameterTypeRegistryTests.cs +++ b/Tests/Reqnroll.RuntimeTests/Bindings/CucumberExpressions/CucumberExpressionParameterTypeRegistryTests.cs @@ -1,34 +1,182 @@ using System; +using System.Linq; +using System.Xml.Linq; using FluentAssertions; using Reqnroll.Bindings; using Reqnroll.Bindings.CucumberExpressions; +using Reqnroll.Bindings.Discovery; +using Reqnroll.Bindings.Reflection; +using Reqnroll.Infrastructure; using Xunit; -namespace Reqnroll.RuntimeTests.Bindings.CucumberExpressions; - -public class CucumberExpressionParameterTypeRegistryTests +namespace Reqnroll.RuntimeTests.Bindings.CucumberExpressions { - // Most of the logic in CucumberExpressionParameterTypeRegistry can only be tested in integration of a cucumber expression match, - // so those are tested in the CucumberExpressionIntegrationTests class. - private BindingRegistry _bindingRegistry; - private CucumberExpressionParameterTypeRegistry CreateSut() + public class CucumberExpressionParameterTypeRegistryTests + { + // Most of the logic in CucumberExpressionParameterTypeRegistry can only be tested in integration of a cucumber expression match, + // so those are tested in the CucumberExpressionIntegrationTests class. + + private BindingRegistry _bindingRegistry; + private CucumberExpressionParameterTypeRegistry CreateSut() + { + _bindingRegistry = new BindingRegistry(); + return new CucumberExpressionParameterTypeRegistry(_bindingRegistry); + } + + [Fact] + public void Should_provide_string_type() + { + var sut = CreateSut(); + var paramType = sut.LookupByTypeName("string"); + + // The regex '.*' provided by the CucumberExpressionParameterTypeRegistry is fake and + // will be ignored because of the special string type handling implemented in ReqnrollCucumberExpression. + // See ReqnrollCucumberExpression.HandleStringType for detailed explanation. + paramType.Should().NotBeNull(); + paramType.RegexStrings.Should().HaveCount(1); + paramType.RegexStrings[0].Should().Be(".*"); + } + + public class SampleEnumUsingClass + { + public void MethodUsingSampleColorEnum1(SampleColorEnum color) { } + } + [Fact] + public void Should_not_error_on_multiple_enums_of_the_same_name() + { + var sut = CreateSut(); + IBindingMethod enumUsingBindingMethod1 = new RuntimeBindingMethod(typeof(SampleEnumUsingClass).GetMethod(nameof(SampleEnumUsingClass.MethodUsingSampleColorEnum1))); + sut.OnBindingMethodProcessed(enumUsingBindingMethod1); + IBindingMethod enumUsingBindingMethod2 = new RuntimeBindingMethod(typeof(CucumberAddtionalExpressions.KlasWithCucumberExpressions).GetMethod(nameof(CucumberAddtionalExpressions.KlasWithCucumberExpressions.MethodUsingSampleColorEnum2))); + sut.OnBindingMethodProcessed(enumUsingBindingMethod2); + var paramTypes = sut.GetParameterTypes().Where(pt => pt.ParameterType.IsEnum).ToList(); + + paramTypes.Should().HaveCount(2); + } + + [Fact] + public void ParameterTypeRegistry_should_identify_error_when_given_multiple_bindings_with_an_ambiguous_enum_as_a_parameter() + { + var expression = "I have {SampleColorEnum} cucumbers in my belly"; + var containerBuilder = new ContainerBuilder(new CucumberExpressionIntegrationTests.TestDependencyProvider()); + var globalContainer = containerBuilder.CreateGlobalContainer(GetType().Assembly); + + var bindingSourceProcessor = globalContainer.Resolve(); + + var bindingRegistry = globalContainer.Resolve(); + + // set up first method binding that uses an ambiguous enum parameter + SetupBoundMethod(expression, bindingRegistry, bindingSourceProcessor, typeof(SampleEnumUsingClass), nameof(SampleEnumUsingClass.MethodUsingSampleColorEnum1)); + + // set up second method binding that uses an ambiguous enum parameter + SetupBoundMethod(expression, bindingRegistry, bindingSourceProcessor, typeof(CucumberAddtionalExpressions.KlasWithCucumberExpressions), nameof(CucumberAddtionalExpressions.KlasWithCucumberExpressions.MethodUsingSampleColorEnum2)); + + bindingSourceProcessor.BuildingCompleted(); + + bindingRegistry.IsValid.Should().BeFalse(); + var stepDefs = bindingRegistry.GetStepDefinitions().ToArray(); + stepDefs.Count().Should().Be(2); + stepDefs.All(sd => sd.SourceExpression == expression).Should().BeTrue(); + stepDefs.All(sd => sd.IsValid == false).Should().BeTrue(); + stepDefs.All(sd => sd.ErrorMessage.StartsWith("Ambiguous enum")).Should().BeTrue(); + } + + [Fact] + public void ParameterTypeRegistry_should_identify_error_when_given_multiple_bindings_with_an_ambiguous_type_as_a_parameter() + { + var expression = "a user {SampleUser} is registered"; + var containerBuilder = new ContainerBuilder(new CucumberExpressionIntegrationTests.TestDependencyProvider()); + var globalContainer = containerBuilder.CreateGlobalContainer(GetType().Assembly); + + var bindingSourceProcessor = globalContainer.Resolve(); + + var bindingRegistry = globalContainer.Resolve(); + + IStepArgumentTransformationBinding transformation1 = new StepArgumentTransformationBinding( + "user ([A-Z][a-z]+)", + new RuntimeBindingMethod(typeof(Reqnroll.RuntimeTests.Bindings.CucumberExpressions.SampleUser).GetMethod(nameof(Reqnroll.RuntimeTests.Bindings.CucumberExpressions.SampleUser.Create)))); + + // set up first method binding that uses an ambiguous enum parameter + SetupBoundMethod(expression, bindingRegistry, bindingSourceProcessor, typeof(CucumberExpressionIntegrationTests.SampleBindings), nameof(CucumberExpressionIntegrationTests.SampleBindings.StepDefWithCustomClassParam), transformation1); + + IStepArgumentTransformationBinding transformation2 = new StepArgumentTransformationBinding( + "user ([A-Z][a-z]+)", + new RuntimeBindingMethod(typeof(Reqnroll.RuntimeTests.Bindings.CucumberAddtionalExpressions.SampleUser).GetMethod(nameof(Reqnroll.RuntimeTests.Bindings.CucumberAddtionalExpressions.SampleUser.Create)))); + + // set up second method binding that uses an ambiguous enum parameter + SetupBoundMethod(expression, bindingRegistry, bindingSourceProcessor, typeof(CucumberAddtionalExpressions.KlasWithCucumberExpressions), nameof(CucumberAddtionalExpressions.KlasWithCucumberExpressions.MethodUsingSampleUser), transformation2); + + bindingSourceProcessor.BuildingCompleted(); + + bindingRegistry.IsValid.Should().BeFalse(); + var stepDefs = bindingRegistry.GetStepDefinitions().ToArray(); + stepDefs.Count().Should().Be(2); + stepDefs.All(sd => sd.SourceExpression == expression).Should().BeTrue(); + stepDefs.All(sd => sd.IsValid == false).Should().BeTrue(); + stepDefs.All(sd => sd.ErrorMessage.StartsWith("Ambiguous type used in cucumber expressions")).Should().BeTrue(); + } + + + private static void SetupBoundMethod(string expression, IBindingRegistry bindingRegistry, IRuntimeBindingSourceProcessor bindingSourceProcessor, Type testType, string methodName, IStepArgumentTransformationBinding transformation = null) + { + if (transformation != null) + { + bindingRegistry.RegisterStepArgumentTransformationBinding(transformation); + } + var bindingSourceMethod = new BindingSourceMethod + { + BindingMethod = new RuntimeBindingMethod(testType.GetMethod(methodName)), + IsPublic = true, + Attributes = new[] + { + new BindingSourceAttribute + { + AttributeType = new RuntimeBindingType(typeof(GivenAttribute)), + AttributeValues = new IBindingSourceAttributeValueProvider[] + { + new BindingSourceAttributeValueProvider(expression) + } + } + } + }; + bindingSourceProcessor.ProcessType( + new BindingSourceType + { + BindingType = new RuntimeBindingType(typeof(CucumberExpressionIntegrationTests.SampleBindings)), + Attributes = new[] + { + new BindingSourceAttribute + { + AttributeType = new RuntimeBindingType(typeof(BindingAttribute)) + } + }, + IsPublic = true, + IsClass = true + }); + bindingSourceProcessor.ProcessMethod(bindingSourceMethod); + } + } +} +namespace Reqnroll.RuntimeTests.Bindings.CucumberAddtionalExpressions +{ + public class KlasWithCucumberExpressions { - _bindingRegistry = new BindingRegistry(); - return new CucumberExpressionParameterTypeRegistry(_bindingRegistry); + public enum SampleColorEnum { Yellow, Brown }; + + public void MethodUsingSampleColorEnum2(SampleColorEnum color) { } + + public void MethodUsingSampleUser(SampleUser user) { } } - [Fact] - public void Should_provide_string_type() + public class SampleUser { - var sut = CreateSut(); - var paramType = sut.LookupByTypeName("string"); - - // The regex '.*' provided by the CucumberExpressionParameterTypeRegistry is fake and - // will be ignored because of the special string type handling implemented in ReqnrollCucumberExpression. - // See ReqnrollCucumberExpression.HandleStringType for detailed explanation. - paramType.Should().NotBeNull(); - paramType.RegexStrings.Should().HaveCount(1); - paramType.RegexStrings[0].Should().Be(".*"); + public string UserName { get; } + + public SampleUser(string userName) + { + UserName = userName; + } + public static SampleUser Create(string userName) => new(userName); } } \ No newline at end of file diff --git a/Tests/Reqnroll.RuntimeTests/Infrastructure/TestRunContainerBuilderTests.cs b/Tests/Reqnroll.RuntimeTests/Infrastructure/TestRunContainerBuilderTests.cs index 8a5b9fc97..fe91167ce 100644 --- a/Tests/Reqnroll.RuntimeTests/Infrastructure/TestRunContainerBuilderTests.cs +++ b/Tests/Reqnroll.RuntimeTests/Infrastructure/TestRunContainerBuilderTests.cs @@ -111,11 +111,6 @@ public void TestIgnore(string message) { throw new NotImplementedException(); } - - public bool DelayedFixtureTearDown - { - get { throw new NotImplementedException(); } - } } public void Dispose() diff --git a/Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj b/Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj index c2943172e..e1972e83a 100644 --- a/Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj +++ b/Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj @@ -1,6 +1,6 @@ - $(Reqnroll_Test_TFM) + net8.0 Reqnroll.RuntimeTests $(Reqnroll_KeyFile) $(Reqnroll_EnableStrongNameSigning) @@ -34,29 +34,13 @@ all runtime; build; native; contentfiles; analyzers - - - - - - - - - - - - - - - - PreserveNewest diff --git a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/Combination.cs b/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/Combination.cs deleted file mode 100644 index 5493e269a..000000000 --- a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/Combination.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Reqnroll.Specs.Generator.ReqnrollPlugin -{ - public class Combination - { - public string ProjectFormat { get; set; } - public string TargetFramework { get; set; } - public string ProgrammingLanguage { get; set; } - public string UnitTestProvider { get; set; } - public string ConfigFormat { get; set; } - } -} \ No newline at end of file diff --git a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/CombinationFeatureGenerator.cs b/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/CombinationFeatureGenerator.cs deleted file mode 100644 index a42d85073..000000000 --- a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/CombinationFeatureGenerator.cs +++ /dev/null @@ -1,19 +0,0 @@ -using Reqnroll.Configuration; -using Reqnroll.Generator; -using Reqnroll.Generator.CodeDom; -using Reqnroll.Generator.Generation; -using Reqnroll.Generator.Interfaces; -using Reqnroll.Generator.UnitTestConverter; - -namespace Reqnroll.Specs.Generator.ReqnrollPlugin -{ - public class CombinationFeatureGenerator : UnitTestFeatureGenerator - { - - public CombinationFeatureGenerator(CodeDomHelper codeDomHelper, ReqnrollConfiguration reqnrollConfiguration, IDecoratorRegistry decoratorRegistry, Combination combination, ProjectSettings projectSettings) : - base(new CustomXUnitGeneratorProvider(codeDomHelper, combination, projectSettings), codeDomHelper, reqnrollConfiguration, decoratorRegistry) - { - - } - } -} diff --git a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/CustomXUnitGeneratorProvider.cs b/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/CustomXUnitGeneratorProvider.cs deleted file mode 100644 index 9094aefa0..000000000 --- a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/CustomXUnitGeneratorProvider.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.CodeDom; -using Reqnroll.Generator; -using Reqnroll.Generator.CodeDom; -using Reqnroll.Generator.Interfaces; -using Reqnroll.Generator.UnitTestProvider; - -namespace Reqnroll.Specs.Generator.ReqnrollPlugin -{ - class CustomXUnitGeneratorProvider : XUnit2TestGeneratorProvider - { - private readonly Combination _combination; - - public CustomXUnitGeneratorProvider(CodeDomHelper codeDomHelper, Combination combination, ProjectSettings projectSettings) : base(codeDomHelper) - { - _combination = combination; - } - - public override void FinalizeTestClass(TestClassGenerationContext generationContext) - { - base.FinalizeTestClass(generationContext); - - if (_combination != null) - { - string programminLanguageEnum = $"Reqnroll.TestProjectGenerator.ProgrammingLanguage.{_combination.ProgrammingLanguage}"; - string projectFormatEnum = $"Reqnroll.TestProjectGenerator.Data.ProjectFormat.{_combination.ProjectFormat}"; - string targetFrameworkEnum = $"Reqnroll.TestProjectGenerator.Data.TargetFramework.{_combination.TargetFramework}"; - string unitTestProviderEnum = $"Reqnroll.TestProjectGenerator.UnitTestProvider.{_combination.UnitTestProvider}"; - string configFormat = $"Reqnroll.TestProjectGenerator.ConfigurationFormat.{_combination.ConfigFormat}"; - - generationContext.ScenarioInitializeMethod.Statements.Add( - new CodeMethodInvokeExpression( - new CodeMethodReferenceExpression( - new CodePropertyReferenceExpression( - new CodePropertyReferenceExpression( - new CodeFieldReferenceExpression(null, generationContext.TestRunnerField.Name), - "ScenarioContext"), - "ScenarioContainer"), - "RegisterInstanceAs", - new CodeTypeReference("Reqnroll.TestProjectGenerator.TestRunConfiguration")), - new CodeVariableReferenceExpression( - $"new Reqnroll.TestProjectGenerator.TestRunConfiguration(){{ ProgrammingLanguage = {programminLanguageEnum}, ProjectFormat = {projectFormatEnum}, TargetFramework = {targetFrameworkEnum}, UnitTestProvider = {unitTestProviderEnum}, ConfigurationFormat = {configFormat} }}"))); - } - } - } -} \ No newline at end of file diff --git a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/GeneratorPlugin.cs b/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/GeneratorPlugin.cs deleted file mode 100644 index 09c6c0f6e..000000000 --- a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/GeneratorPlugin.cs +++ /dev/null @@ -1,30 +0,0 @@ -using Reqnroll.Generator.Plugins; -using Reqnroll.Generator.UnitTestConverter; -using Reqnroll.Infrastructure; -using Reqnroll.Specs.Generator.ReqnrollPlugin; -using Reqnroll.UnitTestProvider; - -[assembly: GeneratorPlugin(typeof(GeneratorPlugin))] - - -namespace Reqnroll.Specs.Generator.ReqnrollPlugin -{ - public class GeneratorPlugin : IGeneratorPlugin - { - public void Initialize(GeneratorPluginEvents generatorPluginEvents, GeneratorPluginParameters generatorPluginParameters, UnitTestProviderConfiguration unitTestProviderConfiguration) - { - //unitTestProviderConfiguration.UseUnitTestProvider("specs-multiple-configurations"); - generatorPluginEvents.RegisterDependencies += GeneratorPluginEvents_RegisterDependencies; - generatorPluginEvents.CustomizeDependencies += GeneratorPluginEvents_CustomizeDependencies; - } - - private void GeneratorPluginEvents_CustomizeDependencies(object sender, CustomizeDependenciesEventArgs e) - { - e.ObjectContainer.RegisterTypeAs("specs-multiple-configurations"); - } - - private void GeneratorPluginEvents_RegisterDependencies(object sender, RegisterDependenciesEventArgs e) - { - } - } -} diff --git a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/MultiFeatureGenerator.cs b/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/MultiFeatureGenerator.cs deleted file mode 100644 index a9860366c..000000000 --- a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/MultiFeatureGenerator.cs +++ /dev/null @@ -1,207 +0,0 @@ -using Gherkin.Ast; -using System; -using System.CodeDom; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.InteropServices; -using Reqnroll.Generator; -using Reqnroll.Generator.Generation; -using Reqnroll.Generator.UnitTestConverter; -using Reqnroll.Parser; - -namespace Reqnroll.Specs.Generator.ReqnrollPlugin -{ - internal class MultiFeatureGenerator : IFeatureGenerator - { - private readonly IFeatureGenerator _defaultFeatureGenerator; - private readonly KeyValuePair[] _featureGenerators; - private readonly List _unitTestProviderTags = new List { "xunit", "mstest", "nunit3" }; - - public MultiFeatureGenerator(IEnumerable> featureGenerators, IFeatureGenerator defaultFeatureGenerator) - { - _defaultFeatureGenerator = defaultFeatureGenerator; - _featureGenerators = featureGenerators.ToArray(); - - foreach (var featureGenerator in _featureGenerators) - { - if (featureGenerator.Value is UnitTestFeatureGenerator unitTestFeatureGenerator) - { - unitTestFeatureGenerator.TestClassNameFormat += $"_{featureGenerator.Key.UnitTestProvider}_{featureGenerator.Key.TargetFramework}_{featureGenerator.Key.ProjectFormat}_{featureGenerator.Key.ProgrammingLanguage}"; - } - } - } - - public CodeNamespace GenerateUnitTestFixture(ReqnrollDocument reqnrollDocument, string testClassName, string targetNamespace) - { - CodeNamespace result = null; - bool onlyFullframework = false; - - var reqnrollFeature = reqnrollDocument.ReqnrollFeature; - bool onlyDotNetCore = false; - if (reqnrollFeature.HasTags()) - { - if (reqnrollFeature.Tags.Where(t => t.Name == "@SingleTestConfiguration").Any()) - { - return _defaultFeatureGenerator.GenerateUnitTestFixture(reqnrollDocument, testClassName, targetNamespace); - } - - onlyFullframework = HasFeatureTag(reqnrollFeature, "@fullframework"); - onlyDotNetCore = HasFeatureTag(reqnrollFeature, "@dotnetcore"); - } - - if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) - { - onlyFullframework = false; - onlyDotNetCore = true; - } - - var tagsOfFeature = reqnrollFeature.Tags.Select(t => t.Name); - var unitTestProviders = tagsOfFeature.Where(t => _unitTestProviderTags.Where(utpt => string.Compare(t, "@" + utpt, StringComparison.CurrentCultureIgnoreCase) == 0).Any()); - - foreach (var featureGenerator in GetFilteredFeatureGenerator(unitTestProviders, onlyFullframework, onlyDotNetCore)) - { - var clonedDocument = CloneDocumentAndAddTags(reqnrollDocument, featureGenerator.Key); - - - var featureGeneratorResult = featureGenerator.Value.GenerateUnitTestFixture(clonedDocument, testClassName, targetNamespace); - - if (result == null) - { - result = featureGeneratorResult; - } - else - { - foreach (CodeTypeDeclaration type in featureGeneratorResult.Types) - { - result.Types.Add(type); - } - } - } - - if (result == null) - { - result = new CodeNamespace(targetNamespace); - } - - return result; - } - - private ReqnrollDocument CloneDocumentAndAddTags(ReqnrollDocument reqnrollDocument, Combination combination) - { - var tags = new List(); - var reqnrollFeature = reqnrollDocument.ReqnrollFeature; - tags.AddRange(reqnrollFeature.Tags); - if (!HasFeatureTag(reqnrollDocument.ReqnrollFeature, "@" + combination.UnitTestProvider)) - tags.Add(new Tag(null, "@" + combination.UnitTestProvider)); - foreach (string otherUnitTestProvider in _unitTestProviderTags.Where(utp => !utp.Equals(combination.UnitTestProvider, StringComparison.InvariantCultureIgnoreCase))) - { - tags.RemoveAll(t => ("@" + otherUnitTestProvider).Equals(t.Name, StringComparison.InvariantCultureIgnoreCase)); - } - if (!HasFeatureTag(reqnrollDocument.ReqnrollFeature, "@" + combination.TargetFramework)) - tags.Add(new Tag(null, "@" + combination.TargetFramework)); - var feature = new ReqnrollFeature(tags.ToArray(), - reqnrollFeature.Location, - reqnrollFeature.Language, - reqnrollFeature.Keyword, - reqnrollFeature.Name, - reqnrollFeature.Description, - reqnrollFeature.Children.ToArray()); - - return new ReqnrollDocument(feature, reqnrollDocument.Comments.ToArray(), reqnrollDocument.DocumentLocation); - } - - private IEnumerable> GetFilteredFeatureGenerator(IEnumerable unitTestProviders, bool onlyFullframework, bool onlyDotNetCore) - { - if (!unitTestProviders.Any()) - { - foreach (var featureGenerator in _featureGenerators) - { - if (onlyFullframework) - { - if (featureGenerator.Key.TargetFramework == TestRunCombinations.TfmEnumValuenet462) - { - yield return featureGenerator; - } - } - else - { - if (onlyDotNetCore) - { - if (ShouldCompileForNetCore21(featureGenerator.Key) - || ShouldCompileForNetCore31(featureGenerator.Key) || ShouldCompileForNet50(featureGenerator.Key) || ShouldCompileForNet60(featureGenerator.Key)) - { - yield return featureGenerator; - } - } - else - { - yield return featureGenerator; - } - } - } - } - - foreach (string unitTestProvider in unitTestProviders) - { - foreach (var featureGenerator in _featureGenerators) - { - if (IsForUnitTestProvider(featureGenerator, unitTestProvider)) - { - if (onlyFullframework) - { - if (featureGenerator.Key.TargetFramework == TestRunCombinations.TfmEnumValuenet462) - { - yield return featureGenerator; - } - } - else - { - if (onlyDotNetCore) - { - if (ShouldCompileForNetCore21(featureGenerator.Key) - || ShouldCompileForNetCore31(featureGenerator.Key) || ShouldCompileForNet50(featureGenerator.Key) || ShouldCompileForNet60(featureGenerator.Key)) - { - yield return featureGenerator; - } - } - else - { - yield return featureGenerator; - } - } - } - } - } - } - public bool ShouldCompileForNetCore21(Combination combination) - { - return combination.TargetFramework == TestRunCombinations.TfmEnumValueNetCore21 - && RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - } - - public bool ShouldCompileForNetCore31(Combination combination) - { - return combination.TargetFramework == TestRunCombinations.TfmEnumValueNetCore31; - } - - public bool ShouldCompileForNet50(Combination combination) - { - return combination.TargetFramework == TestRunCombinations.TfmEnumValueNet50; - } - - public bool ShouldCompileForNet60(Combination combination) - { - return combination.TargetFramework == TestRunCombinations.TfmEnumValueNet60; - } - - private bool IsForUnitTestProvider(KeyValuePair featureGenerator, string unitTestProvider) - { - return string.Compare("@" + featureGenerator.Key.UnitTestProvider, unitTestProvider, StringComparison.CurrentCultureIgnoreCase) == 0; - } - - private bool HasFeatureTag(ReqnrollFeature reqnrollFeature, string tag) - { - return reqnrollFeature.Tags.Any(t => string.Compare(t.Name, tag, StringComparison.CurrentCultureIgnoreCase) == 0); - } - } -} diff --git a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/MultiFeatureGeneratorProvider.cs b/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/MultiFeatureGeneratorProvider.cs deleted file mode 100644 index f029726e7..000000000 --- a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/MultiFeatureGeneratorProvider.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Collections.Generic; -using Reqnroll.BoDi; -using Reqnroll.Configuration; -using Reqnroll.Generator.CodeDom; -using Reqnroll.Generator.Interfaces; -using Reqnroll.Generator.UnitTestConverter; -using Reqnroll.Parser; - -namespace Reqnroll.Specs.Generator.ReqnrollPlugin -{ - internal class MultiFeatureGeneratorProvider : IFeatureGeneratorProvider - { - - - private readonly MultiFeatureGenerator _multiFeatureGenerator; - - public MultiFeatureGeneratorProvider(IObjectContainer container) - { - var featureGenerators = new List>(); - - foreach (var combination in TestRunCombinations.List) - { - var combinationFeatureGenerator = new CombinationFeatureGenerator(container.Resolve(), container.Resolve(), container.Resolve(), combination, container.Resolve()); - featureGenerators.Add(new KeyValuePair(combination, combinationFeatureGenerator)); - } - - _multiFeatureGenerator = new MultiFeatureGenerator(featureGenerators, new CombinationFeatureGenerator(container.Resolve(), container.Resolve(), container.Resolve(), null, container.Resolve())); - } - - - public bool CanGenerate(ReqnrollDocument reqnrollDocument) - { - return true; - } - - public IFeatureGenerator CreateGenerator(ReqnrollDocument reqnrollDocument) - { - return _multiFeatureGenerator; - } - - public int Priority => PriorityValues.Normal; - } -} \ No newline at end of file diff --git a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/Reqnroll.Specs.Generator.ReqnrollPlugin.csproj b/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/Reqnroll.Specs.Generator.ReqnrollPlugin.csproj deleted file mode 100644 index 5a409dd19..000000000 --- a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/Reqnroll.Specs.Generator.ReqnrollPlugin.csproj +++ /dev/null @@ -1,43 +0,0 @@ - - - $(Reqnroll_Tools_TFM) - false - true - $(TargetsForTfmSpecificContentInPackage);PackPublishOutput - - - Debug;Release;Debug-XUnit;Debug-MSTest;Debug-NUnit - - - - $(DefineConstants);XUNIT_SPECS;MSTEST_SPECS;NUNIT_SPECS - - - - $(DefineConstants);NUNIT_SPECS - bin\Debug - - - - $(DefineConstants);MSTEST_SPECS - bin\Debug - - - - $(DefineConstants);XUNIT_SPECS - bin\Debug - - - - - All - - - - - - - - - - diff --git a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/TestRunCombinations.cs b/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/TestRunCombinations.cs deleted file mode 100644 index 3c3fa35f0..000000000 --- a/Tests/Reqnroll.Specs.Generator.ReqnrollPlugin/TestRunCombinations.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; - -namespace Reqnroll.Specs.Generator.ReqnrollPlugin -{ - public class TestRunCombinations - { - public const string TfmEnumValuenet462 = "Net462"; - public const string TfmEnumValueNetCore21 = "Netcoreapp21"; - public const string TfmEnumValueNetCore31 = "Netcoreapp31"; - public const string TfmEnumValueNet50 = "Net50"; - public const string TfmEnumValueNet60 = "Net60"; - - public static List List { get; } = new List() - { -#if XUNIT_SPECS - new Combination() {ProgrammingLanguage = "CSharp73", ProjectFormat = "Old", TargetFramework = TfmEnumValuenet462, UnitTestProvider = "xUnit", ConfigFormat = "Json"}, - new Combination() {ProgrammingLanguage = "CSharp73", ProjectFormat = "New", TargetFramework = TfmEnumValuenet462, UnitTestProvider = "xUnit", ConfigFormat = "Json"}, - new Combination() {ProgrammingLanguage = "CSharp73", ProjectFormat = "New", TargetFramework = TfmEnumValueNetCore31, UnitTestProvider = "xUnit", ConfigFormat = "Json"}, - new Combination() {ProgrammingLanguage = "CSharp", ProjectFormat = "New", TargetFramework = TfmEnumValueNet60, UnitTestProvider = "xUnit", ConfigFormat = "Json"}, -#endif - -#if MSTEST_SPECS - new Combination() {ProgrammingLanguage = "CSharp73", ProjectFormat = "New", TargetFramework = TfmEnumValuenet462, UnitTestProvider = "MSTest", ConfigFormat = "Json"}, - new Combination() {ProgrammingLanguage = "CSharp73", ProjectFormat = "New", TargetFramework = TfmEnumValueNetCore31, UnitTestProvider = "MSTest", ConfigFormat = "Json"}, - new Combination() {ProgrammingLanguage = "CSharp", ProjectFormat = "New", TargetFramework = TfmEnumValueNet60, UnitTestProvider = "MSTest", ConfigFormat = "Json"}, -#endif - -#if NUNIT_SPECS - new Combination() {ProgrammingLanguage = "CSharp73", ProjectFormat = "New", TargetFramework = TfmEnumValuenet462, UnitTestProvider = "NUnit3", ConfigFormat = "Json"}, - new Combination() {ProgrammingLanguage = "CSharp73", ProjectFormat = "New", TargetFramework = TfmEnumValueNetCore31, UnitTestProvider = "NUnit3", ConfigFormat = "Json"}, - new Combination() {ProgrammingLanguage = "CSharp", ProjectFormat = "New", TargetFramework = TfmEnumValueNet60, UnitTestProvider = "NUnit3", ConfigFormat = "Json"}, -#endif - }; - } -} diff --git a/Tests/Reqnroll.Specs/Drivers/ConfigurationLoaderDriver.cs b/Tests/Reqnroll.Specs/Drivers/ConfigurationLoaderDriver.cs index 94824f804..9b6033f5a 100644 --- a/Tests/Reqnroll.Specs/Drivers/ConfigurationLoaderDriver.cs +++ b/Tests/Reqnroll.Specs/Drivers/ConfigurationLoaderDriver.cs @@ -6,12 +6,12 @@ namespace Reqnroll.Specs.Drivers { public class ConfigurationLoaderDriver { - private readonly ConfigurationDriver _configurationDriver; + private readonly ConfigurationFileDriver _configurationFileDriver; private readonly SolutionDriver _solutionDriver; - public ConfigurationLoaderDriver(ConfigurationDriver configurationDriver, SolutionDriver solutionDriver) + public ConfigurationLoaderDriver(ConfigurationFileDriver configurationFileDriver, SolutionDriver solutionDriver) { - _configurationDriver = configurationDriver; + _configurationFileDriver = configurationFileDriver; _solutionDriver = solutionDriver; } @@ -21,11 +21,11 @@ public void SetFromReqnrollConfiguration(ReqnrollConfiguration reqnrollConfigura foreach (string stepAssemblyName in reqnrollConfiguration.AdditionalStepAssemblies) { - _configurationDriver.AddStepAssembly(project, new BindingAssembly(stepAssemblyName)); + _configurationFileDriver.AddStepAssembly(project, new BindingAssembly(stepAssemblyName)); } - _configurationDriver.SetBindingCulture(project, reqnrollConfiguration.BindingCulture); - _configurationDriver.SetFeatureLanguage(project, reqnrollConfiguration.FeatureLanguage); + _configurationFileDriver.SetBindingCulture(project, reqnrollConfiguration.BindingCulture); + _configurationFileDriver.SetFeatureLanguage(project, reqnrollConfiguration.FeatureLanguage); } } } diff --git a/Tests/Reqnroll.Specs/Drivers/Parser/ParserDriver.cs b/Tests/Reqnroll.Specs/Drivers/Parser/ParserDriver.cs index be3084a8a..4583f1333 100644 --- a/Tests/Reqnroll.Specs/Drivers/Parser/ParserDriver.cs +++ b/Tests/Reqnroll.Specs/Drivers/Parser/ParserDriver.cs @@ -44,9 +44,10 @@ public void AssertParsedFeatureEqualTo(string parsedFeatureXml) { const string ns1 = "xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\""; const string ns2 = "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\""; - - string expected = parsedFeatureXml.Replace("\r", "").Replace(ns1, "").Replace(ns2, ""); - string got = SerializeDocument(ParsedDocument).Replace("\r", "").Replace(ns1, "").Replace(ns2, ""); + string Normalize(string value) + => value.Replace("\r", "").Replace(@"\r", "").Replace(ns1, "").Replace(ns2, ""); + string expected = Normalize(parsedFeatureXml); + string got = Normalize(SerializeDocument(ParsedDocument)); got.Should().Be(expected); } diff --git a/Tests/Reqnroll.Specs/Features/Build systems.feature b/Tests/Reqnroll.Specs/Features/Build systems.feature deleted file mode 100644 index faa987ff5..000000000 --- a/Tests/Reqnroll.Specs/Features/Build systems.feature +++ /dev/null @@ -1,33 +0,0 @@ -@dotnetcore -Feature: Build systems - -@quarantaine -@requiresMsBuild -@globalusingdirective #MSBuild for VS2019 and Mono throws error CS8652: The feature 'global using directive' is currently in Preview and unsupported. -Scenario: Use MSBuild for compiling - Given there is a scenario in a feature file - And all steps are bound and pass - - When I compile the solution using 'MSBuild' - - Then no compilation errors are reported - -Scenario: Use dotnet build for compiling - Given there is a scenario in a feature file - And all steps are bound and pass - - When I compile the solution using 'dotnet build' - - Then no compilation errors are reported - - -Scenario: Use dotnet msbuild for compiling - Given there is a scenario in a feature file - And all steps are bound and pass - - When I compile the solution using 'dotnet msbuild' - - Then no compilation errors are reported - - - diff --git a/Tests/Reqnroll.Specs/Features/LanguageSupport.feature b/Tests/Reqnroll.Specs/Features/LanguageSupport.feature deleted file mode 100644 index e4bfb1deb..000000000 --- a/Tests/Reqnroll.Specs/Features/LanguageSupport.feature +++ /dev/null @@ -1,55 +0,0 @@ -Feature: .NET Language Code-Behind Generation Support - -@quarantaine -Scenario Outline: A project with a single scenario should compile successfully in all supported languages and build systems - Given I have a '' test project - And there is a feature file in the project as - """ - Feature: Simple Feature - Scenario: Simple Scenario - When I do something - """ - And all steps are bound and pass - - When I compile the solution using '' - And I execute the tests - - Then the execution summary should contain - | Total | Succeeded | - | 1 | 1 | - -Examples: - | Description | Language | Build Command | - | C# with dotnet build | C# | dotnet build | - | VB.NET with dotnet build | VB.NET | dotnet build | - -Examples: - | Description | Language | Build Command | - | C# with dotnet msbuild | C# | dotnet msbuild | - | VB.NET with dotnet msuild | VB.NET | dotnet msbuild | - -# duplicated scenario to be able to filter it out on CI build -@quarantaine -@globalusingdirective #MSBuild for VS2019 and Mono throws error CS8652: The feature 'global using directive' is currently in Preview and unsupported. -@requiresMsBuild -Scenario Outline: A project with a single scenario should compile successfully in all supported languages and build systems (MsBuild) - Given I have a '' test project - And there is a feature file in the project as - """ - Feature: Simple Feature - Scenario: Simple Scenario - When I do something - """ - And all steps are bound and pass - - When I compile the solution using '' - And I execute the tests - - Then the execution summary should contain - | Total | Succeeded | - | 1 | 1 | - -Examples: - | Description | Language | Build Command | - | C# with MSBuild | C# | MSBuild | - | VB.NET with MSBuild | VB.NET | MSBuild | diff --git a/Tests/Reqnroll.Specs/Features/MultipleSpecsProjects.feature b/Tests/Reqnroll.Specs/Features/MultipleSpecsProjects.feature deleted file mode 100644 index bac6830e7..000000000 --- a/Tests/Reqnroll.Specs/Features/MultipleSpecsProjects.feature +++ /dev/null @@ -1,27 +0,0 @@ -Feature: Multiple Specs Projects - -@quarantaine -Scenario Outline: Two projects with the same unit test provider - Given I have Specs.Project.A and Specs.Project.B using the same unit test provider - And Specs.Project.B references Specs.Project.A - When I build the solution using '' - Then the build should succeed - - Examples: - | Build Tool | - | dotnet build | - | dotnet msbuild | - -# duplicated scenario to be able to filter it out on CI build -@quarantaine -@requiresMsBuild -@globalusingdirective #MSBuild for VS2019 and Mono throws error CS8652: The feature 'global using directive' is currently in Preview and unsupported. -Scenario Outline: Two projects with the same unit test provider (MsBuild) - Given I have Specs.Project.A and Specs.Project.B using the same unit test provider - And Specs.Project.B references Specs.Project.A - When I build the solution using '' - Then the build should succeed - - Examples: - | Build Tool | - | MSBuild | diff --git a/Tests/Reqnroll.Specs/Features/RegressionTests/GH2571.feature b/Tests/Reqnroll.Specs/Features/RegressionTests/GH2571.feature deleted file mode 100644 index a43e14b6a..000000000 --- a/Tests/Reqnroll.Specs/Features/RegressionTests/GH2571.feature +++ /dev/null @@ -1,76 +0,0 @@ -Feature: GH2571 - -Parameters of Scenario Outlines contained in multi-line text that are nested inside of XML are not handled properly - https://github.com/reqnroll/Reqnroll/issues/2571 - - -Scenario: GH2571 - - Given there is a feature file in the project as - """ - Feature: parameters in DocStrings - Scenario Outline: nested angle brackets surround parameter names - Given the the package contains the web.config file of the following content - \"\"\" - - - - - - - \"\"\" - When the user gets the debug status - Then the customErrors element should be - - Examples: - | Mode | customErrors | - | Off | | - | RemoteOnly | | - """ - - And the following binding class - """ - using FluentAssertions; - using Reqnroll; - - [Binding] - public class ScenarioOutlineBug2571StepDefinitions - { - private string _docString; - private string _customErrorString; - - public ScenarioOutlineBug2571StepDefinitions() - { - - } - - - [Given("the the package contains the web.config file of the following content")] - public void GivenMyXMLIs(string scenarioText) - { - - _docString = scenarioText; - } - - - [When("the user gets the debug status")] - public void WhenIProcessXML() - { - - int x = _docString.IndexOf(" + - $(Reqnroll_Specs_TFM) + net8.0 Reqnroll.Specs $(Reqnroll_KeyFile) false $(Reqnroll_PublicSign) Reqnroll.Specs true - Debug;Release;Debug-XUnit;Debug-MSTest;Debug-NUnit false true Always @@ -34,8 +33,8 @@ - + @@ -58,8 +57,8 @@ - <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' == 'Core'">$(Reqnroll_Core_Generator_TFM) - <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' != 'Core'">$(Reqnroll_FullFramework_Generator_TFM) + <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' == 'Core'">$(Reqnroll_Core_Tools_TFM) + <_Reqnroll_Needed_MSBuildGenerator Condition=" '$(MSBuildRuntimeType)' != 'Core'">$(Reqnroll_FullFramework_Tools_TFM) @@ -74,15 +73,15 @@ - + - + - <_Reqnroll_TaskAssembly>..\..\Reqnroll.Tools.MsBuild.Generation\bin\$(Configuration)\$(_Reqnroll_Needed_MSBuildGenerator)\Reqnroll.Tools.MsBuild.Generation.dll + <_Reqnroll_TaskAssembly>..\..\Reqnroll.Tools.MsBuild.Generation\bin\$(Configuration)\$(_Reqnroll_Needed_MSBuildGenerator)\tasks\Reqnroll.Tools.MsBuild.Generation.dll diff --git a/Tests/Reqnroll.Specs/StepDefinitions/ConfigurationSteps.cs b/Tests/Reqnroll.Specs/StepDefinitions/ConfigurationSteps.cs index 8c133a16c..921b41ee8 100644 --- a/Tests/Reqnroll.Specs/StepDefinitions/ConfigurationSteps.cs +++ b/Tests/Reqnroll.Specs/StepDefinitions/ConfigurationSteps.cs @@ -5,12 +5,12 @@ namespace Reqnroll.Specs.StepDefinitions [Binding] public class ConfigurationSteps { - private readonly ConfigurationDriver _configurationDriver; + private readonly ConfigurationFileDriver _configurationFileDriver; private readonly CompilationResultDriver _compilationResultDriver; - public ConfigurationSteps(ConfigurationDriver configurationDriver, CompilationResultDriver compilationResultDriver) + public ConfigurationSteps(ConfigurationFileDriver configurationFileDriver, CompilationResultDriver compilationResultDriver) { - _configurationDriver = configurationDriver; + _configurationFileDriver = configurationFileDriver; _compilationResultDriver = compilationResultDriver; } @@ -29,7 +29,7 @@ public void ThenTheReqnroll_JsonIsUsedForConfiguration() [Given(@"the feature language is '(.*)'")] public void GivenTheFeatureLanguageIs(string featureLanguage) { - _configurationDriver.SetFeatureLanguage(featureLanguage); + _configurationFileDriver.SetFeatureLanguage(featureLanguage); } } } diff --git a/Tests/Reqnroll.Specs/StepDefinitions/ExecutionResultSteps.cs b/Tests/Reqnroll.Specs/StepDefinitions/ExecutionResultSteps.cs index b55537bda..9c0ee1b43 100644 --- a/Tests/Reqnroll.Specs/StepDefinitions/ExecutionResultSteps.cs +++ b/Tests/Reqnroll.Specs/StepDefinitions/ExecutionResultSteps.cs @@ -13,14 +13,14 @@ namespace Reqnroll.Specs.StepDefinitions [Binding] public class ExecutionResultSteps { - private readonly HooksDriver _hooksDriver; + private readonly BindingsDriver _bindingsDriver; private readonly VSTestExecutionDriver _vsTestExecutionDriver; private readonly TestProjectFolders _testProjectFolders; private readonly TestRunLogDriver _testRunLogDriver; - public ExecutionResultSteps(HooksDriver hooksDriver, VSTestExecutionDriver vsTestExecutionDriver, TestProjectFolders testProjectFolders, TestRunLogDriver testRunLogDriver) + public ExecutionResultSteps(BindingsDriver bindingsDriver, VSTestExecutionDriver vsTestExecutionDriver, TestProjectFolders testProjectFolders, TestRunLogDriver testRunLogDriver) { - _hooksDriver = hooksDriver; + _bindingsDriver = bindingsDriver; _vsTestExecutionDriver = vsTestExecutionDriver; _testProjectFolders = testProjectFolders; _testRunLogDriver = testRunLogDriver; @@ -66,13 +66,13 @@ public void ThenTheBindingMethodIsExecuted(string methodName, int times) [Then(@"the hook '(.*)' is executed (\d+) times")] public void ThenTheHookIsExecuted(string methodName, int times) { - _hooksDriver.CheckIsHookExecuted(methodName, times); + _bindingsDriver.CheckIsHookExecuted(methodName, times); } [Then(@"the hooks are executed in the order")] public void ThenTheHooksAreExecutedInTheOrder(Table table) { - _hooksDriver.CheckIsHookExecutedInOrder(table.Rows.Select(r => r[0])); + _bindingsDriver.AssertHooksExecutedInOrder(table.Rows.Select(r => r[0]).ToArray()); } [Then(@"the execution log should contain text '(.*)'")] diff --git a/Tests/Reqnroll.Specs/StepDefinitions/ReqnrollConfigurationSteps.cs b/Tests/Reqnroll.Specs/StepDefinitions/ReqnrollConfigurationSteps.cs index 1ecab808b..f40a732ee 100644 --- a/Tests/Reqnroll.Specs/StepDefinitions/ReqnrollConfigurationSteps.cs +++ b/Tests/Reqnroll.Specs/StepDefinitions/ReqnrollConfigurationSteps.cs @@ -1,25 +1,24 @@ using Reqnroll.Specs.Drivers; using Reqnroll.TestProjectGenerator; using Reqnroll.TestProjectGenerator.Driver; -using ConfigurationDriver = Reqnroll.TestProjectGenerator.Driver.ConfigurationDriver; namespace Reqnroll.Specs.StepDefinitions { [Binding] public class ReqnrollConfigurationSteps { - private readonly ConfigurationDriver _configurationDriver; + private readonly ConfigurationFileDriver _configurationFileDriver; private readonly JsonConfigurationParserDriver _jsonConfigurationParserDriver; private readonly ConfigurationLoaderDriver _configurationLoaderDriver; private readonly TestSuiteSetupDriver _testSuiteSetupDriver; public ReqnrollConfigurationSteps( - ConfigurationDriver configurationDriver, + ConfigurationFileDriver configurationFileDriver, JsonConfigurationParserDriver jsonConfigurationParserDriver, ConfigurationLoaderDriver configurationLoaderDriver, TestSuiteSetupDriver testSuiteSetupDriver) { - _configurationDriver = configurationDriver; + _configurationFileDriver = configurationFileDriver; _jsonConfigurationParserDriver = jsonConfigurationParserDriver; _configurationLoaderDriver = configurationLoaderDriver; _testSuiteSetupDriver = testSuiteSetupDriver; @@ -28,7 +27,7 @@ public ReqnrollConfigurationSteps( [Given(@"the project has no reqnroll\.json configuration")] public void GivenTheProjectHasNoReqnroll_JsonConfiguration() { - _configurationDriver.SetConfigurationFormat(ConfigurationFormat.None); + _configurationFileDriver.SetConfigurationFormat(ConfigurationFormat.None); } [Given(@"there is a project with this reqnroll\.json configuration")] @@ -47,31 +46,31 @@ public void GivenTheReqnrollConfigurationIs(string reqnrollJsonConfig) [Given(@"the project is configured to use the (.+) provider")] public void GivenTheProjectIsConfiguredToUseTheUnitTestProvider(string providerName) { - _configurationDriver.SetUnitTestProvider(providerName); + _configurationFileDriver.SetUnitTestProvider(providerName); } [Given(@"Reqnroll is configured in the reqnroll\.json")] public void GivenReqnrollIsConfiguredInTheReqnrollJson() { - _configurationDriver.SetConfigurationFormat(ConfigurationFormat.Json); + _configurationFileDriver.SetConfigurationFormat(ConfigurationFormat.Json); } [Given(@"obsoleteBehavior configuration value is set to (.*)")] public void GivenObsoleteBehaviorConfigurationValueIsSetTo(string obsoleteBehaviorValue) { - _configurationDriver.SetRuntimeObsoleteBehavior(obsoleteBehaviorValue); + _configurationFileDriver.SetRuntimeObsoleteBehavior(obsoleteBehaviorValue); } [Given(@"row testing is (.+)")] public void GivenRowTestingIsRowTest(bool enabled) { - _configurationDriver.SetIsRowTestsAllowed(enabled); + _configurationFileDriver.SetIsRowTestsAllowed(enabled); } [Given(@"the type '(.*)' is registered as '(.*)' in Reqnroll runtime configuration")] public void GivenTheTypeIsRegisteredAsInReqnrollRuntimeConfiguration(string typeName, string interfaceName) { - _configurationDriver.AddRuntimeRegisterDependency(typeName, interfaceName); + _configurationFileDriver.AddRuntimeRegisterDependency(typeName, interfaceName); } [Given(@"there is a scenario")] diff --git a/Tests/Reqnroll.Specs/Support/Hooks.cs b/Tests/Reqnroll.Specs/Support/Hooks.cs index a407bc287..a20729e6b 100644 --- a/Tests/Reqnroll.Specs/Support/Hooks.cs +++ b/Tests/Reqnroll.Specs/Support/Hooks.cs @@ -1,5 +1,6 @@ using System; using Reqnroll.TestProjectGenerator; +using Reqnroll.TestProjectGenerator.Data; using Reqnroll.TestProjectGenerator.Helpers; namespace Reqnroll.Specs.Support; @@ -22,6 +23,16 @@ public Hooks(ScenarioContext scenarioContext, CurrentVersionDriver currentVersio [BeforeScenario] public void BeforeScenario() { + _scenarioContext.ScenarioContainer.RegisterInstanceAs( + new TestRunConfiguration + { + ProgrammingLanguage = TestProjectGenerator.ProgrammingLanguage.CSharp, + ProjectFormat = ProjectFormat.New, + TargetFramework = TargetFramework.Net80, + UnitTestProvider = TestProjectGenerator.UnitTestProvider.MSTest, + ConfigurationFormat = ConfigurationFormat.Json + }); + _currentVersionDriver.NuGetVersion = NuGetPackageVersion.Version; _currentVersionDriver.ReqnrollNuGetVersion = NuGetPackageVersion.Version; _scenarioContext.ScenarioContainer.RegisterTypeAs(); diff --git a/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs b/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs index f76a3a41a..1df2aff18 100644 --- a/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs +++ b/Tests/Reqnroll.SystemTests/Generation/GenerationTestBase.cs @@ -1,9 +1,11 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using FluentAssertions; namespace Reqnroll.SystemTests.Generation; [TestCategory("Generation")] -public class GenerationTestBase : SystemTestBase +public abstract class GenerationTestBase : SystemTestBase { [TestMethod] public void GeneratorAllIn_sample_can_be_handled() @@ -19,29 +21,273 @@ public void GeneratorAllIn_sample_can_be_handled() public void Handles_simple_scenarios_without_namespace_collisions() { _projectsDriver.CreateProject("CollidingNamespace.Reqnroll", "C#"); - + AddSimpleScenarioAndOutline(); AddScenario( """ - Scenario: Sample Scenario - When something happens - Scenario: Scenario with DataTable When something happens with | who | when | | me | today | | someone else | tomorrow | """); - _projectsDriver.AddPassingStepBinding(); + 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) - //TODO: test hooks: before/after test feature & scenario (require special handling by test frameworks) - //TODO: test scenario outlines (nr of examples, params are available in ScenarioContext, allowRowTests=false, examples tags) + #region Test different outcomes: success, failure, pending, undefined, ignored (scenario & scenario outline) + protected virtual string GetExpectedPendingOutcome() => "NotExecuted"; + protected virtual string GetExpectedUndefinedOutcome() => "NotExecuted"; + protected virtual string GetExpectedIgnoredOutcome() => "NotExecuted"; + + [TestMethod] + public void Handles_different_scenario_and_scenario_outline_outcomes() + { + AddFeatureFile( + """ + Feature: Sample Feature + + Scenario: Passing scenario + When the step passes + + Scenario: Failing scenario + When the step fails + + Scenario: Pending scenario + When the step is pending + + Scenario: Undefined scenario + When the step is undefined + + @ignore + Scenario: Ignored scenario + When the step fails + + Scenario Outline: SO + When the step + Examples: + | result | + | passes | + | fails | + | is pending | + | is undefined | + @ignore + Examples: + | result | + | ignored | + + @ignore + Scenario Outline: ExampleIgnored + When the step + Examples: + | result | + | fails | + + @ignore + Rule: Scenario in this Rule should be Ignored + Scenario: Ruleignored scenario + When the step passes + """); + _projectsDriver.AddPassingStepBinding(stepRegex: "the step passes"); + _projectsDriver.AddFailingStepBinding(stepRegex: "the step fails"); + _projectsDriver.AddStepBinding("StepDefinition", regex: "the step is pending", "throw new PendingStepException();", "Throw New PendingStepException()"); + ExecuteTests(); + + // handles PASSED + var expectedPassedOutcome = "Passed"; + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("Passing")) + .Which.Outcome.Should().Be(expectedPassedOutcome); + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("SO") && tr.TestName.Contains("passes")) + .Which.Outcome.Should().Be(expectedPassedOutcome); + + // handles FAILED + var expectedFailedOutcome = "Failed"; + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("Failing")) + .Which.Outcome.Should().Be(expectedFailedOutcome); + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("SO") && tr.TestName.Contains("fails")) + .Which.Outcome.Should().Be(expectedFailedOutcome); + + // handles PENDING + var expectedPendingOutcome = GetExpectedPendingOutcome(); + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("Pending")) + .Which.Outcome.Should().Be(expectedPendingOutcome); + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("SO") && tr.TestName.Contains("pending")) + .Which.Outcome.Should().Be(expectedPendingOutcome); + + // handles UNDEFINED + var expectedUndefinedOutcome = GetExpectedUndefinedOutcome(); + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("Undefined")) + .Which.Outcome.Should().Be(expectedUndefinedOutcome); + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("SO") && tr.TestName.Contains("undefined")) + .Which.Outcome.Should().Be(expectedUndefinedOutcome); + + // handles IGNORED + var expectedIgnoredOutcome = GetExpectedIgnoredOutcome(); + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("Ignored")) + .Which.Outcome.Should().Be(expectedIgnoredOutcome); + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("ExampleIgnored")) + .Which.Outcome.Should().Be(expectedIgnoredOutcome); + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("Ruleignored")) + .Which.Outcome.Should().Be(expectedIgnoredOutcome); + AssertIgnoredScenarioOutlineExampleHandled(); + } + + protected virtual void AssertIgnoredScenarioOutlineExampleHandled() + { + // the scenario outline examples ignored by a tag on the examples block are not generated + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().NotContain(tr => tr.TestName.StartsWith("SO") && tr.TestName.Contains("ignored")) + .And.Subject.Where(tr => tr.TestName.StartsWith("SO")).Should().HaveCount(4); + + } + #endregion + + #region Test async steps (async steps are executed in order) + [TestMethod] + public void Async_steps_are_executed_in_order() + { + AddScenario( + """ + Scenario: Async Scenario Steps + When Async step 1 is called + When Async step 2 is called + When Async step 3 is called + """); + + AddBindingClass( + """ + namespace AsyncSequence.StepDefinitions + { + [Binding] + public class AsyncSequenceStepDefinitions + { + [When("Async step 1 is called")] + public async Task WhenStep1IsTaken() + { + await Task.Run(() => global::Log.LogStep() ); + } + [When("Async step 2 is called")] + public async Task WhenStep2IsTaken() + { + await Task.Run(() => global::Log.LogStep() ); + } + [When("Async step 3 is called")] + public async Task WhenStep3IsTaken() + { + await Task.Run(() => global::Log.LogStep() ); + } + } + } + """); + + ExecuteTests(); + _bindingDriver.AssertExecutedStepsEqual("WhenStep1IsTaken", "WhenStep2IsTaken", "WhenStep3IsTaken"); + + ShouldAllScenariosPass(); + } + #endregion + + #region Test hooks: before/after run, feature & scenario hook (require special handling by test frameworks) + [TestMethod] + public void TestRun_Feature_and_Scenario_hooks_are_executed_in_right_order() + { + var testsInFeatureFile = 3; + AddSimpleScenario(); + AddSimpleScenarioOutline(testsInFeatureFile - 1); + AddPassingStepBinding(); + AddHookBinding("BeforeTestRun"); + AddHookBinding("AfterTestRun"); + AddHookBinding("BeforeFeature"); + AddHookBinding("AfterFeature"); + AddHookBinding("BeforeScenario"); + AddHookBinding("AfterScenario"); + + ExecuteTests(); + + _bindingDriver.AssertExecutedHooksEqual( + "BeforeTestRun", + "BeforeFeature", + "BeforeScenario", + "AfterScenario", + "BeforeScenario", + "AfterScenario", + "BeforeScenario", + "AfterScenario", + "AfterFeature", + "AfterTestRun"); + ShouldAllScenariosPass(); + } + #endregion + + #region Test scenario outlines (nr of examples, params are available in ScenarioContext, allowRowTests=false, examples tags) + + [TestMethod] + public void Scenario_outline_examples_gather_tags_and_parameters() + { + AddFeatureFile( + """ + @feature_tag + Feature: Sample Feature + + @rule_tag + Rule: Sample Rule + + @so1_tag + Scenario Outline: SO1 + When happens to + Examples: + | what1 | person | + | something | me | + | nothing | you | + + @so2_tag + Scenario Outline: SO2 + When happens to + Examples: + | what2 | person | + | something | me | + @e3_tag + Examples: E3 + | what2 | person | + | nothing | you | + """); + _projectsDriver.AddStepBinding("StepDefinition", regex: ".*", + """ + global::Log.LogCustom("tags", string.Join(",", _scenarioContext.ScenarioInfo.CombinedTags)); + global::Log.LogCustom("parameters", string.Join(",", _scenarioContext.ScenarioInfo.Arguments.OfType().Select(kv => $"{kv.Key}={kv.Value}"))); + """); + + ExecuteTests(); + + ShouldAllScenariosPass(4); + + _bindingDriver.GetActualLogLines("tags").Should().BeEquivalentTo( + "-> tags: so1_tag,feature_tag,rule_tag:StepBinding", + "-> tags: so1_tag,feature_tag,rule_tag:StepBinding", + "-> tags: so2_tag,feature_tag,rule_tag:StepBinding", + "-> tags: so2_tag,e3_tag,feature_tag,rule_tag:StepBinding"); + + _bindingDriver.GetActualLogLines("parameters").Should().BeEquivalentTo( + "-> parameters: what1=something,person=me:StepBinding", + "-> parameters: what1=nothing,person=you:StepBinding", + "-> parameters: what2=something,person=me:StepBinding", + "-> parameters: what2=nothing,person=you:StepBinding"); + } + + #endregion + //TODO: test parallel execution (details TBD) - maybe this should be in a separate test class } diff --git a/Tests/Reqnroll.SystemTests/Generation/NUnitGenerationTest.cs b/Tests/Reqnroll.SystemTests/Generation/NUnitGenerationTest.cs index 9d4b15b4a..f5f8238ef 100644 --- a/Tests/Reqnroll.SystemTests/Generation/NUnitGenerationTest.cs +++ b/Tests/Reqnroll.SystemTests/Generation/NUnitGenerationTest.cs @@ -1,3 +1,4 @@ +using FluentAssertions; using Microsoft.VisualStudio.TestTools.UnitTesting; using Reqnroll.TestProjectGenerator; @@ -16,4 +17,11 @@ protected override void TestInitialize() base.TestInitialize(); _testRunConfiguration.UnitTestProvider = UnitTestProvider.NUnit3; } + + protected override void AssertIgnoredScenarioOutlineExampleHandled() + { + _vsTestExecutionDriver.LastTestExecutionResult.LeafTestResults + .Should().ContainSingle(tr => tr.TestName.StartsWith("SO") && tr.TestName.Contains("ignored")) + .Which.Outcome.Should().Be(GetExpectedIgnoredOutcome()); + } } \ No newline at end of file diff --git a/Tests/Reqnroll.SystemTests/Generation/XUnitGenerationTest.cs b/Tests/Reqnroll.SystemTests/Generation/XUnitGenerationTest.cs index adf2a569f..086f2ab37 100644 --- a/Tests/Reqnroll.SystemTests/Generation/XUnitGenerationTest.cs +++ b/Tests/Reqnroll.SystemTests/Generation/XUnitGenerationTest.cs @@ -16,4 +16,7 @@ protected override void TestInitialize() base.TestInitialize(); _testRunConfiguration.UnitTestProvider = UnitTestProvider.xUnit; } + + protected override string GetExpectedPendingOutcome() => "Failed"; + protected override string GetExpectedUndefinedOutcome() => "Failed"; } \ No newline at end of file diff --git a/Tests/Reqnroll.SystemTests/Portability/PortabilityTestBase.cs b/Tests/Reqnroll.SystemTests/Portability/PortabilityTestBase.cs index 7ff9c6efd..2d3de7740 100644 --- a/Tests/Reqnroll.SystemTests/Portability/PortabilityTestBase.cs +++ b/Tests/Reqnroll.SystemTests/Portability/PortabilityTestBase.cs @@ -1,5 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; +using Reqnroll.TestProjectGenerator; using Reqnroll.TestProjectGenerator.Driver; +using System; using System.Runtime.InteropServices; namespace Reqnroll.SystemTests.Portability; @@ -10,37 +12,85 @@ namespace Reqnroll.SystemTests.Portability; [TestCategory("Portability")] public abstract class PortabilityTestBase : SystemTestBase { + private void RunSkippableTest(Action test) + { + try + { + test(); + } + catch (DotNetSdkNotInstalledException ex) + { + if (!new ConfigurationDriver().PipelineMode) + Assert.Inconclusive(ex.ToString()); + } + } + [TestMethod] - public void GeneratorAllIn_sample_can_be_handled() + [DataRow(UnitTestProvider.MSTest)] + [DataRow(UnitTestProvider.NUnit3)] + [DataRow(UnitTestProvider.xUnit)] + public void GeneratorAllIn_sample_can_be_handled(UnitTestProvider unitTestProvider) { - PrepareGeneratorAllInSamples(); + RunSkippableTest(() => + { + //TODO: Temporarily disabled tests until https://github.com/reqnroll/Reqnroll/issues/132 is resolved + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && + unitTestProvider == UnitTestProvider.xUnit) + Assert.Inconclusive("Temporarily disabled tests until https://github.com/reqnroll/Reqnroll/issues/132 is resolved"); - ExecuteTests(); + _testRunConfiguration.UnitTestProvider = unitTestProvider; - ShouldAllScenariosPass(); + PrepareGeneratorAllInSamples(); + + ExecuteTests(); + + ShouldAllScenariosPass(); + }); } [TestMethod] [TestCategory("MsBuild")] public void GeneratorAllIn_sample_can_be_compiled_with_MsBuild() { - PrepareGeneratorAllInSamples(); - _compilationDriver.SetBuildTool(BuildTool.MSBuild); - - _compilationDriver.CompileSolution(); + RunSkippableTest(() => + { + PrepareGeneratorAllInSamples(); + _compilationDriver.SetBuildTool(BuildTool.MSBuild); + _compilationDriver.CompileSolution(); + }); } [TestMethod] [TestCategory("DotnetMSBuild")] public void GeneratorAllIn_sample_can_be_compiled_with_DotnetMSBuild() { - PrepareGeneratorAllInSamples(); - _compilationDriver.SetBuildTool(BuildTool.DotnetMSBuild); + RunSkippableTest(() => + { + PrepareGeneratorAllInSamples(); + _compilationDriver.SetBuildTool(BuildTool.DotnetMSBuild); - _compilationDriver.CompileSolution(); + _compilationDriver.CompileSolution(); + }); } - //TODO: test different outcomes: success, failure, pending, undefined, ignored (scenario & scenario outline) - //TODO: test async steps (async steps are executed in order) - //TODO: test before/after test run hooks (.NET Framework version of Reqnroll is subscribed to assembly unload) + #region Test before/after test run hooks (.NET Framework version of Reqnroll is subscribed to assembly unload) + [TestMethod] + public void TestRun_hooks_are_executed() + { + RunSkippableTest(() => + { + AddSimpleScenario(); + AddPassingStepBinding(); + AddHookBinding("BeforeTestRun"); + AddHookBinding("AfterTestRun"); + + ExecuteTests(); + + _bindingDriver.AssertExecutedHooksEqual( + "BeforeTestRun", + "AfterTestRun"); + ShouldAllScenariosPass(); + }); + } + #endregion } diff --git a/Tests/Reqnroll.SystemTests/Reqnroll.SystemTests.csproj b/Tests/Reqnroll.SystemTests/Reqnroll.SystemTests.csproj index 4ef30b6fe..00b6cf412 100644 --- a/Tests/Reqnroll.SystemTests/Reqnroll.SystemTests.csproj +++ b/Tests/Reqnroll.SystemTests/Reqnroll.SystemTests.csproj @@ -18,11 +18,12 @@ + + - false diff --git a/Tests/Reqnroll.SystemTests/Smoke/SmokeTest.cs b/Tests/Reqnroll.SystemTests/Smoke/SmokeTest.cs index d64f4d6ab..5f098744f 100644 --- a/Tests/Reqnroll.SystemTests/Smoke/SmokeTest.cs +++ b/Tests/Reqnroll.SystemTests/Smoke/SmokeTest.cs @@ -9,11 +9,7 @@ public class SmokeTest : SystemTestBase [TestMethod] public void Handles_the_simplest_scenario() { - AddScenario( - """ - Scenario: Sample Scenario - When something happens - """); + AddSimpleScenario(); _projectsDriver.AddPassingStepBinding(); ExecuteTests(); diff --git a/Tests/Reqnroll.SystemTests/SystemTestBase.cs b/Tests/Reqnroll.SystemTests/SystemTestBase.cs index 61dd12c7b..e0b8d9013 100644 --- a/Tests/Reqnroll.SystemTests/SystemTestBase.cs +++ b/Tests/Reqnroll.SystemTests/SystemTestBase.cs @@ -1,13 +1,15 @@ using System; +using System.Linq; using System.Text.RegularExpressions; using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Reqnroll.BoDi; using Reqnroll.SystemTests.Drivers; using Reqnroll.TestProjectGenerator; using Reqnroll.TestProjectGenerator.Data; using Reqnroll.TestProjectGenerator.Driver; using Reqnroll.TestProjectGenerator.Helpers; +using Scrutor; namespace Reqnroll.SystemTests; public abstract class SystemTestBase @@ -17,10 +19,12 @@ public abstract class SystemTestBase protected VSTestExecutionDriver _vsTestExecutionDriver = null!; protected TestFileManager _testFileManager = new(); protected FolderCleaner _folderCleaner = null!; - protected ObjectContainer _testContainer = null!; + protected IServiceProvider _testContainer = null!; protected TestRunConfiguration _testRunConfiguration = null!; protected CurrentVersionDriver _currentVersionDriver = null!; protected CompilationDriver _compilationDriver = null!; + protected BindingsDriver _bindingDriver = null!; + protected TestProjectFolders _testProjectFolders = null!; protected int _preparedTests = 0; @@ -32,29 +36,67 @@ public void TestInitializeMethod() TestInitialize(); } + protected virtual IServiceCollection ConfigureServices() + { + var services = new ServiceCollection(); + + services.AddSingleton(); + + services.Scan(scan => scan + .FromAssemblyOf() + .AddClasses() + .UsingRegistrationStrategy(RegistrationStrategy.Skip) + .AsSelf() + .WithScopedLifetime()); + + services.Scan(scan => scan + .FromAssemblyOf() + .AddClasses(c => c.InNamespaceOf()) + .UsingRegistrationStrategy(RegistrationStrategy.Skip) + .AsSelf() + .WithScopedLifetime()); + + return services; + } + protected virtual void TestInitialize() { - _testContainer = new ObjectContainer(); - _testContainer.RegisterTypeAs(); + var services = ConfigureServices(); + _testContainer = services.BuildServiceProvider(); - _testRunConfiguration = _testContainer.Resolve(); + _testRunConfiguration = _testContainer.GetService(); _testRunConfiguration.ProgrammingLanguage = ProgrammingLanguage.CSharp; _testRunConfiguration.ProjectFormat = ProjectFormat.New; _testRunConfiguration.ConfigurationFormat = ConfigurationFormat.Json; _testRunConfiguration.TargetFramework = TargetFramework.Net80; _testRunConfiguration.UnitTestProvider = UnitTestProvider.MSTest; - _currentVersionDriver = _testContainer.Resolve(); + _currentVersionDriver = _testContainer.GetService(); _currentVersionDriver.NuGetVersion = NuGetPackageVersion.Version; _currentVersionDriver.ReqnrollNuGetVersion = NuGetPackageVersion.Version; - _folderCleaner = _testContainer.Resolve(); + _folderCleaner = _testContainer.GetService(); _folderCleaner.EnsureOldRunFoldersCleaned(); - _projectsDriver = _testContainer.Resolve(); - _executionDriver = _testContainer.Resolve(); - _vsTestExecutionDriver = _testContainer.Resolve(); - _compilationDriver = _testContainer.Resolve(); + _projectsDriver = _testContainer.GetService(); + _executionDriver = _testContainer.GetService(); + _vsTestExecutionDriver = _testContainer.GetService(); + _compilationDriver = _testContainer.GetService(); + _testProjectFolders = _testContainer.GetService(); + _bindingDriver = _testContainer.GetService(); + } + + + [TestCleanup] + public void TestCleanupMethod() + { + TestCleanup(); + } + + protected virtual void TestCleanup() + { + if (TestContext.CurrentTestOutcome == UnitTestOutcome.Passed) + _folderCleaner.CleanSolutionFolder(); } protected void AddFeatureFileFromResource(string fileName, int? preparedTests = null) @@ -110,6 +152,41 @@ protected void AddScenario(string scenarioContent, int? preparedTests = null) UpdatePreparedTests(scenarioContent, preparedTests); } + protected void AddSimpleScenario() + { + AddScenario( + """ + Scenario: Sample Scenario + When something happens + """); + } + + protected void AddSimpleScenarioOutline(int numberOfExamples = 2) + { + var examples = numberOfExamples == 2 + ? """ + | me | + | you | + """ + : string.Join( + Environment.NewLine, + Enumerable.Range(1, numberOfExamples).Select(i => $" | example {i} |")); + AddScenario( + $""" + Scenario Outline: Scenario outline with examples + When something happens to + Examples: + | person | + {examples} + """); + } + + protected void AddSimpleScenarioAndOutline() + { + AddSimpleScenario(); + AddSimpleScenarioOutline(); + } + private void UpdatePreparedTests(string gherkinContent, int? preparedTests) { if (preparedTests == null) @@ -137,13 +214,33 @@ protected void ExecuteTests() protected void ShouldAllScenariosPass(int? expectedNrOfTestsSpec = null) { - if (expectedNrOfTestsSpec == null && _preparedTests == 0) + int expectedNrOfTests = ConfirmAllTestsRan(expectedNrOfTestsSpec); + _vsTestExecutionDriver.LastTestExecutionResult.Succeeded.Should().Be(expectedNrOfTests, "all tests should pass"); + } + + protected int ConfirmAllTestsRan(int? expectedNrOfTestsSpec) + { + if (expectedNrOfTestsSpec == null && _preparedTests == 0) throw new ArgumentException($"If {nameof(_preparedTests)} is not set, the {nameof(expectedNrOfTestsSpec)} is mandatory.", nameof(expectedNrOfTestsSpec)); var expectedNrOfTests = expectedNrOfTestsSpec ?? _preparedTests; _vsTestExecutionDriver.LastTestExecutionResult.Should().NotBeNull(); _vsTestExecutionDriver.LastTestExecutionResult.Total.Should().Be(expectedNrOfTests, $"the run should contain {expectedNrOfTests} tests"); - _vsTestExecutionDriver.LastTestExecutionResult.Succeeded.Should().Be(expectedNrOfTests, "all tests should pass"); - _folderCleaner.CleanSolutionFolder(); + return expectedNrOfTests; + } + + protected void AddHookBinding(string eventType, string? name = null, string code = "") + { + _projectsDriver.AddHookBinding(eventType, name, code: code); + } + + protected void AddPassingStepBinding(string scenarioBlock = "StepDefinition", string stepRegex = ".*") + { + _projectsDriver.AddPassingStepBinding(scenarioBlock, stepRegex); + } + + protected void AddBindingClass(string content) + { + _projectsDriver.AddBindingClass(content); } } diff --git a/azure-pipelines.yml b/azure-pipelines.yml deleted file mode 100644 index b76243dec..000000000 --- a/azure-pipelines.yml +++ /dev/null @@ -1,48 +0,0 @@ -name: $(GitAssemblyInformationalVersion) - - -resources: -- repo: self - clean: "true" - -trigger: - batch: true - branches: - include: - - master - paths: - exclude: - - docs/* - - .readthedocs.yml - -pr: - branches: - include: - - '*' - paths: - exclude: - - docs/* - - .readthedocs.yml - -jobs: -#- template: build.yml -# parameters: -# name: macOS -# artifactFileName: '$(Build.ArtifactStagingDirectory)/Reqnroll-macOS.zip' -# pool: -# name: 'Hosted macOS' - -- template: build.yml - parameters: - name: Windows - artifactFileName: '$(Build.ArtifactStagingDirectory)/Reqnroll-Windows.zip' - appInsightsInstrumentationKey: $(AppInsightsInstrumentationKey) - pool: - vmImage: 'windows-latest' - -- template: build.yml - parameters: - name: Linux - artifactFileName: '$(Build.ArtifactStagingDirectory)/Reqnroll-Linux.zip' - pool: - vmImage: 'ubuntu-latest' diff --git a/build.ps1 b/build.ps1 deleted file mode 100644 index d8b810f87..000000000 --- a/build.ps1 +++ /dev/null @@ -1,23 +0,0 @@ -param ( - [string]$Configuration = "Debug", - [string]$appInsightsInstrumentationKey = "" -) - -$additionalOptions = "" - -if ($IsLinux) { - $additionalOptions = "-p:EnableSourceControlManagerQueries=false -p:EnableSourceLink=false -p:DeterministicSourcePaths=false" -} - -if ($appInsightsInstrumentationKey) { - if ($additionalOptions){ - $additionalOptions = "$($additionalOptions) -property:AppInsightsInstrumentationKey=$($appInsightsInstrumentationKey)" - } - else { - $additionalOptions = "-property:AppInsightsInstrumentationKey=$($appInsightsInstrumentationKey)" - } -} - -Write-Host "dotnet build ./Reqnroll.sln --no-restore -property:Configuration=$Configuration -bl:msbuild.$Configuration.binlog -nodeReuse:false -v n --no-incremental $additionalOptions" - -& dotnet build ./Reqnroll.sln --no-restore -property:Configuration=$Configuration -bl:msbuild.$Configuration.binlog -nodeReuse:false -v n $additionalOptions diff --git a/build.yml b/build.yml deleted file mode 100644 index 3b2c6339c..000000000 --- a/build.yml +++ /dev/null @@ -1,93 +0,0 @@ -parameters: - name: '' - pool: '' - nugetVersion: 5.8.0 - net6SdkVersion: 6.0.x - artifactFileName: '' - appInsightsInstrumentationKey: '' - publishArtifacts: true - -jobs: -- job: ${{ parameters.name }} - pool: ${{ parameters.pool }} - steps: - - checkout: self - submodules: true - clean: true - - - task: CmdLine@2 - displayName: 'git log' - inputs: - script: 'git log -2' - - - task: NuGetToolInstaller@0 - displayName: 'Use NuGet' - inputs: - versionSpec: ${{ parameters.nugetVersion }} - - - task: UseDotNet@2 - displayName: 'Use .NET 6 SDK' - inputs: - packageType: sdk - version: ${{ parameters.net6SdkVersion }} - includePreviewVersions: true - - - task: PowerShell@2 - displayName: 'dotnet --info' - inputs: - targetType: Inline - pwsh: true - script: dotnet --info - - - task: PowerShell@2 - inputs: - targetType: Inline - pwsh: true - script: dotnet restore --configfile $(Build.SourcesDirectory)\nuget.config - displayName: 'dotnet restore' - - - task: PowerShell@2 - inputs: - filePath: build.ps1 - workingDirectory: ./ - arguments: Debug -appInsightsInstrumentationKey '${{ parameters.appInsightsInstrumentationKey }}' - pwsh: true - displayName: 'build Debug' - - - task: PowerShell@2 - inputs: - filePath: build.ps1 - workingDirectory: ./ - arguments: Release -appInsightsInstrumentationKey '${{ parameters.appInsightsInstrumentationKey }}' - pwsh: true - displayName: 'build Release' - - - task: ArchiveFiles@2 - displayName: 'Archive $(Build.SourcesDirectory)' - condition: and(succeeded(), ${{ parameters.publishArtifacts}}) - inputs: - rootFolderOrFile: '$(Build.SourcesDirectory)' - archiveFile: ${{ parameters.artifactFileName }} - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: Reqnroll.CI-Sources' - condition: and(succeeded(), ${{ parameters.publishArtifacts}}) - inputs: - PathtoPublish: ${{ parameters.artifactFileName }} - artifactType: container - ArtifactName: 'Reqnroll.CI-BuildResult-${{ parameters.name }}' - - - task: CopyFiles@2 - displayName: 'Copy binlogs' - condition: ${{ parameters.publishArtifacts}} - inputs: - contents: '$(Build.SourcesDirectory)/*.binlog' - targetFolder: '$(Build.SourcesDirectory)/binlogs' - - - task: PublishBuildArtifacts@1 - displayName: 'Publish Artifact: Reqnroll.CI-Binlogs' - condition: ${{ parameters.publishArtifacts}} - inputs: - PathtoPublish: '$(Build.SourcesDirectory)/binlogs' - artifactType: container - ArtifactName: 'Reqnroll.CI-Binlogs-${{ parameters.name }}' diff --git a/clean.ps1 b/clean.ps1 deleted file mode 100644 index ef74c3228..000000000 --- a/clean.ps1 +++ /dev/null @@ -1 +0,0 @@ -git clean -fdx . \ No newline at end of file diff --git a/docs/_static/images/jsonDatasetWithNestedObjectArrays.png b/docs/_static/images/jsonDatasetWithNestedObjectArrays.png new file mode 100644 index 000000000..9dc674e8a Binary files /dev/null and b/docs/_static/images/jsonDatasetWithNestedObjectArrays.png differ diff --git a/docs/_static/images/jsonDatasetWithSpace.png b/docs/_static/images/jsonDatasetWithSpace.png new file mode 100644 index 000000000..2ee1e6db7 Binary files /dev/null and b/docs/_static/images/jsonDatasetWithSpace.png differ diff --git a/docs/extend/plugins.md b/docs/extend/plugins.md index e72c66646..fe6f7a437 100644 --- a/docs/extend/plugins.md +++ b/docs/extend/plugins.md @@ -9,7 +9,9 @@ All types of plugins are created in a similar way. ## Runtime plugins -Runtime plugins need to target .NET Framework 4.6.2 and .NET Standard 2.0. +Runtime plugins should target .NET Standard 2.0 to be compatible with all .NET versions supported +by Reqnroll. Targetting a more specific version will limit the compatibility of your plugin. + Reqnroll searches for files that end with `.ReqnrollPlugin.dll` in the following locations: * The folder containing your `Reqnroll.dll` file @@ -44,7 +46,15 @@ Mandatory: ## Generator plugins -Generator plugins need to target .NET Framework 4.7.1 and .NET Core 3.1. +Runtime plugins should target .NET Standard 2.0 to be compatible with all scenarios supported by +Reqnroll. + +The generator plugins are invoked during build. They are usually invoked in a .NET environment +according to your .NET SDK (e.g. .NET 8.0), but in some cases (when built using MSBuild or in Visual Studio) +they might be invoked in a .NET 4.8 environment. Therefore, you have to make sure that your plugin +works in both environments. If necessary, you can multi-target your plugin, but using the right compiled +version of your plugin is the responsibility of the plugin itself. + The MSBuild task needs to know which generator plugins it should use. You therefore have to add your generator plugin to the `ReqnrollGeneratorPlugins` ItemGroup. This is passed to the MSBuild task as a parameter and later used to load the plugins. @@ -65,28 +75,31 @@ This is passed to the MSBuild task as a parameter and later used to load the plu ## Combined Package with both plugins -If you need to update generator and runtime plugins with a single NuGet package (as we are doing with the `Reqnroll.xUnit`, `Reqnroll.NUnit` and `Reqnroll.xUnit` packages), you can do so. +You can have a single NuGet package that contains both the runtime and generator plugins. +We use this approach for the `Reqnroll.xUnit`, `Reqnroll.NUnit` and `Reqnroll.xUnit` packages +for example. -As with the separate plugins, you need two projects. One for the runtime plugin, and one for the generator plugin. As you only want one NuGet package, the **NuSpec files must only be present in the generator project**. -This is because the generator plugin is built with a higher .NET Framework version (.NET 4.7.1), meaning you can add a dependency on the Runtime plugin (which is only .NET 4.6.1). This will not working the other way around. +The combined package can be built from a single project, or from two projects. The latter +allows you to have different dependencies for the runtime and generator plugins. -You can simply combine the contents of the `.targets` and `.props` file to a single one. +If you use two projects for the combined package, the **NuSpec files should only be present +in the generator project**. This is because the generators typically have more dependencies. +You can simply combine the contents of the `.targets` and `.props` file to a single one. ## Tips & Tricks ### Building Plugins on non-Windows machines -For building .NET 4.6.2 projects on non- Windows machines, the .NET Framework reference assemblies are needed. +For building .NET 4.6.2 projects on non-Windows machines, the .NET Framework reference assemblies are needed. You can add them with following PackageReference to your project: -``` xml +```xml all runtime; build; native; contentfiles; analyzers - ``` diff --git a/docs/integrations/autofac.md b/docs/integrations/autofac.md index 2fe5024ca..242cd1e31 100644 --- a/docs/integrations/autofac.md +++ b/docs/integrations/autofac.md @@ -48,41 +48,47 @@ using Reqnroll.Autofac.ReqnrollPlugin; ``` Then ```csharp -containerBuilder.AddReqnrollBindings(typeof(YourClassInTheReqnrollProject)) +containerBuilder.AddReqnrollBindings() ``` Or overload ```csharp -containerBuilder.AddReqnrollBindings() +containerBuilder.AddReqnrollBindings(Assembly.GetExecutingAssembly()) ``` Or manually register like so: ```csharp builder - .RegisterAssemblyTypes(typeof(TestDependencies).Assembly) + .RegisterAssemblyTypes(typeof(AnyClassInTheReqnrollProject).Assembly) .Where(t => Attribute.IsDefined(t, typeof(BindingAttribute))) .SingleInstance(); ``` ### 3. A typical dependency builder method for `[GlobalDependencies]` with `[ScenarioDependencies]` probably looks like this: ```csharp -[GlobalDependencies] -public static void CreateGlobalContainer(ContainerBuilder containerBuilder) +public class SetupTestDependencies { + [GlobalDependencies] + public static void SetupGlobalContainer(ContainerBuilder containerBuilder) + { // Register globally scoped runtime dependencies - Dependencies.RegisterGlobalDependencies(containerBuilder); - - //TODO: add Services that are shared globally. -} - -[ScenarioDependencies] -public static void CreateContainerBuilder(ContainerBuilder containerBuilder) -{ - // Register scenario scoped runtime dependencies - Dependencies.RegisterScenarioDependencies(containerBuilder); - - //TODO: add customizations, stubs required for testing - - containerBuilder.AddReqnrollBindings() + containerBuilder + .RegisterType() + .As() + .SingleInstance(); + } + + [ScenarioDependencies] + public static void SetupScenarioContainer(ContainerBuilder containerBuilder) + { + // Register scenario scoped runtime dependencies + containerBuilder + .RegisterType() + .As() + .SingleInstance(); + + // register binding classes + containerBuilder.AddReqnrollBindings(); + } } ``` @@ -109,8 +115,7 @@ public static ILifetimeScope GetFeatureLifetimeScope() public static void ConfigureContainerBuilder(ContainerBuilder containerBuilder) { //TODO: add customizations, stubs required for testing - - containerBuilder.AddReqnrollBindings(); + containerBuilder.AddReqnrollBindings(); } ``` diff --git a/docs/integrations/externaldata.md b/docs/integrations/externaldata.md index 993aad73c..ebe524bf7 100644 --- a/docs/integrations/externaldata.md +++ b/docs/integrations/externaldata.md @@ -23,6 +23,12 @@ Standard RFC 4180 CSV format is supported with a header line (plugin uses [CsvHe Both XLSX and XLS is supported (plugin uses [ExcelDataReader](https://github.com/ExcelDataReader/ExcelDataReader) to parse the files). ``` +- JSON files (format 'JSON', extension .json) + +```{note} +Object arrays and nested object arrays are supported (plugin uses [JObject.Parse](https://github.com/JamesNK/Newtonsoft.Json/blob/master/Src/Newtonsoft.Json/Linq/JObject.cs) to parse the files). +``` + ## Tags The following tags can be used to specify the external source: @@ -38,7 +44,7 @@ The path is a relative path to the folder of the **feature files**. - `@DataFormat:format` - This tag only needs to be used if the format cannot be identified from the file extension. -- `@DataSet:data-set-name` - This tag is applicable to *Excel files only*. It is used to select the worksheet of the Excel file you wish to use. By **default**, the first worksheet in an Excel file is targeted. +- `@DataSet:data-set-name` - This tag is applicable to *Excel and Json files only*. For Excel it is used to select the worksheet of the Excel file you wish to use. By **default**, the first worksheet in an Excel file is targeted. For Json it is used to select the object array you wish to use. By **default**, the first object array in a Json file is targeted. - `@DataField:name-in-feature-file=name-in-source-file` - This tag can be used to "rename" columns of the external data source. @@ -189,3 +195,45 @@ Forgatókönyv: The basket price is calculated correctly Akkor the basket price should be € ```` + +### Json files + +You can use Json files the same way as you do with Excel files with some minor differences: + +- Only Object arrays and nested Object are supported. Arrays of simple types, empty arrays, etc. are not supported. + +- Json files with multiple object arrays are supported, you can use the `@DataSet:array-property-name` to select the object array you wish to target. The plugin uses the **first** object array by **default**. Nested object arrays can be specified by appending property names using a '.'. + +- Use underscores in the `@DataSet` tag instead of spaces if the array property name name contains spaces. + +The below example shows a Json file with object arrays and we wish to target the last array labelled *"other products"*. We do this by using the `@DataSet:other_products` tag. Note the use of (_) instead of space: + +![product json](/_static/images/jsonDatasetWithSpace.png) + +````Gherkin + +@DataSource:products.json @DataSet:other_products +Scenario: The basket price is calculated correctly for other products + Given the price of is € + And the customer has put 1 pcs of to the basket + When the basket price is calculated + Then the basket price should be € + +```` + +The below example shows a Json file with nested object arrays and we wish to target the inner array labelled *"varieties"*. We do this by using the `@DataSet:products.varieties` tag. Note the use of . to append nested property names: + +![product.varieties json](/_static/images/jsonDatasetWithNestedObjectArrays.png) + +````Gherkin + +@DataSource:products-nested-dataset.json @DataSet:products.varieties +Scenario: The basket price is calculated correctly for products.varieties in nested products json + Given the price of is € + And the customer has put 1 pcs of to the basket + When the basket price is calculated + Then the basket price should be € + + +```` + diff --git a/docs/integrations/mstest.md b/docs/integrations/mstest.md index e108f8aaa..664466ad6 100644 --- a/docs/integrations/mstest.md +++ b/docs/integrations/mstest.md @@ -1,6 +1,6 @@ # MSTest -Reqnroll supports MsTest V2. +Reqnroll supports MsTest V2 (NuGet Version 2.2.8 or higher). Documentation for MSTest can be found [here](https://docs.microsoft.com/en-us/visualstudio/test/unit-test-your-code?view=vs-2019). diff --git a/docs/quickstart/index.md b/docs/quickstart/index.md index 6c802a6ea..df36ff447 100644 --- a/docs/quickstart/index.md +++ b/docs/quickstart/index.md @@ -33,13 +33,13 @@ namespace ReqnrollQuickstart.App; public class PriceCalculator { // the item prices are hard coded for now - private readonly Dictionary _priceTable = new() + private readonly Dictionary _priceTable = new() { { "Electric guitar", 180.0 }, { "Guitar pick", 1.5 } }; - public double CalculatePrice(Dictionary basket) + public decimal CalculatePrice(Dictionary basket) { throw new NotImplementedException(); } diff --git a/dotnet_sdk_validation_build.yml b/dotnet_sdk_validation_build.yml deleted file mode 100644 index e757d44c7..000000000 --- a/dotnet_sdk_validation_build.yml +++ /dev/null @@ -1,45 +0,0 @@ -name: $(GitAssemblyInformationalVersion) - -resources: -- repo: self - clean: true - -trigger: none -pr: none -schedules: -- cron: "0 0 * * *" - displayName: Daily midnight build - branches: - include: - - master - -jobs: -- template: build.yml - parameters: - name: SDK_3_1_102 - artifactFileName: '$(Build.ArtifactStagingDirectory)/Reqnroll-Windows.zip' - appInsightsInstrumentationKey: $(AppInsightsInstrumentationKey) - sdkVersion: 3.1.102 - publishArtifacts: false - pool: - vmImage: 'windows-latest' - -- template: build.yml - parameters: - name: SDK_3_1_200 - artifactFileName: '$(Build.ArtifactStagingDirectory)/Reqnroll-Windows.zip' - appInsightsInstrumentationKey: $(AppInsightsInstrumentationKey) - sdkVersion: 3.1.x - publishArtifacts: false - pool: - vmImage: 'windows-latest' - -- template: build.yml - parameters: - name: SDK_5_0_100_preview_1 - artifactFileName: '$(Build.ArtifactStagingDirectory)/Reqnroll-Windows.zip' - appInsightsInstrumentationKey: $(AppInsightsInstrumentationKey) - sdkVersion: 5.x - publishArtifacts: false - pool: - vmImage: 'windows-latest' \ No newline at end of file diff --git a/nightly-build.yml b/nightly-build.yml deleted file mode 100644 index 49f029fb0..000000000 --- a/nightly-build.yml +++ /dev/null @@ -1,58 +0,0 @@ -# ASP.NET -# Build and test ASP.NET projects. -# Add steps that publish symbols, save build artifacts, deploy, and more: -# https://docs.microsoft.com/azure/devops/pipelines/apps/aspnet/build-aspnet-4 - -name: 0.2.$(Rev:r) - -trigger: none -pr: none - -# The time zone for cron schedules is UTC -# Cron expression is a space-delimited expression with five entries: mm HH DD MM DW -schedules: -- cron: "00 4 * * Mon-Fri" - displayName: Reqnroll Nightly build - branches: - include: - - master - always: true - -pool: - vmImage: 'windows-latest' - -workspace: - clean: 'all' - -variables: - sdkVersion: 3.1.201 - net5SdkVersion: 5.0.102 - net6SdkVersion: 6.x - -steps: -- checkout: self - submodules: true - -- task: UseDotNet@2 - displayName: 'Use .NET Core SDK: $(sdkVersion)' - inputs: - version: '$(sdkVersion)' - -- task: UseDotNet@2 - displayName: 'Use .NET 5 SDK: $(net5SdkVersion)' - inputs: - version: '$(net5SdkVersion)' - -- task: UseDotNet@2 - displayName: 'Use .NET 6 SDK: $(net6SdkVersion)' - inputs: - version: '$(net6SdkVersion)' - -- task: DotNetCoreCLI@2 - displayName: 'dotnet build Release' - inputs: - projects: Reqnroll.sln - arguments: --configuration Release - -- task: WhiteSource Bolt@20 - displayName: 'WhiteSource Bolt - Reqnroll' diff --git a/nuget.config b/nuget.config index 235d37c05..27a5cefad 100644 --- a/nuget.config +++ b/nuget.config @@ -2,15 +2,7 @@ - - - - - - - - \ No newline at end of file diff --git a/reqnroll.yml b/reqnroll.yml deleted file mode 100644 index 50aedb548..000000000 --- a/reqnroll.yml +++ /dev/null @@ -1,9 +0,0 @@ -name: Reqnroll -desc: Reqnroll is a pragmatic BDD solution for .NET -site: https://www.reqnroll.net -tags: -- bdd -- testing -upforgrabs: - name: up-for-grabs - link: https://github.com/reqnroll/Reqnroll/issues?q=is%3Aopen+is%3Aissue+label%3Aup-for-grabs \ No newline at end of file diff --git a/runtests.ps1 b/runtests.ps1 deleted file mode 100644 index 07e1e711b..000000000 --- a/runtests.ps1 +++ /dev/null @@ -1,14 +0,0 @@ - -$errorCode = dotnet test --no-build ./Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj -f net471 - -if ($errorCode -gt 0) { - Write-Host "Runtime Tests for .NET 4.7.1 failed" - exit -} - -$errorCode = dotnet test --no-build ./Tests/Reqnroll.RuntimeTests/Reqnroll.RuntimeTests.csproj -f netcoreapp2.0 - -if ($errorCode -gt 0) { - Write-Host "Runtime Tests for .NET Core 2.0 failed" - exit -} \ No newline at end of file