diff --git a/.editorconfig b/.editorconfig index a042fda14d..11fc80a9e9 100644 --- a/.editorconfig +++ b/.editorconfig @@ -12,6 +12,9 @@ indent_style = space insert_final_newline = true charset = utf-8 +[*.{sln,*proj}] +charset = utf-8-bom + [*.{cs,cake}] indent_size = 4 @@ -113,3 +116,6 @@ dotnet_sort_system_directives_first = true # https://github.com/nunit/docs/wiki/Coding-Standards#use-of-the-var-keyword # Would be true:warning, except that that so much existing code is not consistent with the coding standard. csharp_style_var_when_type_is_apparent = true:suggestion + +# DOC100: Place text in paragraphs +dotnet_diagnostic.DOC100.severity = silent diff --git a/.gitignore b/.gitignore index dac5e01856..aadd9730b3 100644 --- a/.gitignore +++ b/.gitignore @@ -184,4 +184,7 @@ MockAssemblyResult.xml PortabilityAnalysis*.html project.lock.json *.project.lock.json -.dotnet \ No newline at end of file +.dotnet +.config/dotnet-tools.json +*.err +*.out diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 41fe67b720..0000000000 --- a/.travis.yml +++ /dev/null @@ -1,25 +0,0 @@ -language: csharp -sudo: false - -dotnet: 2.1.4 # SDK version; includes runtime version 2.0.5. Need 2.0+ for netcoreapp2.0 tests. - -mono: 5.10.0 # Need 5.2+ for the included MSBuild. - -matrix: - include: - - dist: xenial - install: - - sudo apt-get install dotnet-sharedframework-microsoft.netcore.app-1.1.6 - - - os: osx - install: - - curl https://download.microsoft.com/download/A/7/E/A7EF2AFF-F77B-4F77-A21B-0F7BD09A4065/dotnet-osx-x64.1.1.6.pkg -O - - sudo installer -pkg dotnet-osx-x64.1.1.6.pkg -target / - - # .NET Core 2 requires OSX 10.12+ - # https://github.com/dotnet/core/blob/master/release-notes/2.0/2.0-supported-os.md#macos - # https://docs.travis-ci.com/user/reference/osx/#OS-X-Version - osx_image: xcode9.1 - -script: - - ./build.sh --target=Travis --configuration=Release diff --git a/BUILDING.md b/BUILDING.md index 3e0ac05e2e..765623385a 100644 --- a/BUILDING.md +++ b/BUILDING.md @@ -6,9 +6,9 @@ There are two ways to build NUnit: using the solution file in an IDE or through ## Solution Build -The framework is built using a single Visual Studio solution, `nunit.sln`, which may be built with [Visual Studio 2017](https://www.visualstudio.com/vs/) on Windows and [Visual Studio for Mac](https://www.visualstudio.com/vs/) on macOS. Currently, MonoDevelop does not support the new multi-targeted `csproj` project format. Once MonoDevelop is updated, it should start working again. Until then, we recommend [Visual Studio Code](https://code.visualstudio.com/) and compiling using the build scripts on non-Windows platforms. +The framework is built using a single Visual Studio solution, `nunit.sln`, which may be built with [Visual Studio 2019 16.8](https://www.visualstudio.com/vs/) or newer on Windows and [Visual Studio for Mac](https://www.visualstudio.com/vs/) on macOS. Currently, MonoDevelop does not support the new multi-targeted `csproj` project format. Once MonoDevelop is updated, it should start working again. Until then, we recommend [Visual Studio Code](https://code.visualstudio.com/) and compiling using the build scripts on non-Windows platforms. -On all platforms, you will need to install [.NET Core 2.0.3 SDK](https://www.microsoft.com/net/download/windows) or newer. On Mac or Linux, you will need to install [Mono 5.2.0](https://www.mono-project.com/download/). Currently (as of 5.4.1), newer versions of Mono are broken and crash during the compile. +On all platforms, you will need to install [.NET 5.0 SDK](https://www.microsoft.com/net/download/windows) or newer. On Mac or Linux, you will need to install [Mono 5.2.0](https://www.mono-project.com/download/). Currently (as of 5.4.1), newer versions of Mono are broken and crash during the compile. The solutions all place their output in a common bin directory under the solution root. @@ -53,16 +53,6 @@ For a full list of tasks, run `build.cmd -ShowDescription`. 1. By design, the Package target does not depend on Build. This is to allow re-packaging when necessary without changing the binaries themselves. Of course, this means that you have to be very careful that the build is up to date before packaging. 2. For additional targets, refer to the build.cake script itself. -### Building and testing for Linux on a Windows machine - -Most of the time, it's not necessary to build or run tests on platforms other than your primary platform. The continuous integration which runs on every PR is enough to catch any problems. - -Once in a while you may find it desirable to be primarily developing the repository on a Windows machine but to run Linux tests on the same set of files while you edit them in Windows. One convenient way to do this is to pass the same arguments to [build-mono-docker.ps1](.\build-mono-docker.ps1) that you would pass to build.ps1. It requires [Docker](https://docs.docker.com/docker-for-windows/install/) to be installed. - -For example, to build and test everything: `.\build-mono-docker.ps1 -t test` - -This will run a temporary container using the latest [Mono image](https://hub.docker.com/r/library/mono/), mounting the repo inside the container and executing the [build.sh](build.sh) Cake bootstrapper with the arguments you specify. - ### Defined constants NUnit often uses conditional preprocessor to light up APIs and behaviors per platform. @@ -73,20 +63,17 @@ This brings clarity to the code and makes it easy to change the mapping between Feature constants are defined in [Directory.Build.props](src/NUnitFramework/Directory.Build.props): - `TASK_PARALLEL_LIBRARY_API` exposes NUnit APIs which depend on the TPL framework types - - `PARALLEL` enables running tests in parallel - - `PLATFORM_DETECTION` enables platform detection - `THREAD_ABORT` enables timeouts and forcible cancellation - - `APARTMENT_STATE` enables control of the thread apartment state Platform constants are defined by convention by the csproj SDK, one per target framework. -For example, `NET45`, `NETSTANDARD1_6`, `NETCOREAPP2_0`, and so on. +For example, `NET45`, `NETSTANDARD2_0`, `NETCOREAPP2_1`, and so on. It is most helpful to call out which platforms are the exception in rather than the rule in a given scenario. Keep in mind the effect the preprocessor would have on a newly added platform. For example, rather than this code: ```cs -#if NET45 || NETSTANDARD1_6 || NETSTANDARD2_0 +#if NET45 || NETSTANDARD2_0 || NETSTANDARD2_1 // Something that .NET Framework 4.0 can't do #endif ``` diff --git a/CHANGES.md b/CHANGES.md index b0a2028d73..471dd60651 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,118 @@ +## NUnit 3.13 - January 7, 2021 + +The [`FixtureLifeCycle`](https://docs.nunit.org/articles/nunit/writing-tests/attributes/fixturelifecycle.html) attribute has been added to indicate that an instance for a test fixture or all test fixtures in an assembly should be constructed for each test within the fixture or assembly. + +This attribute may be applied to a test fixture (class) or to a test assembly. It is useful in combination with the [Parallelizable Attribute](https://docs.nunit.org/articles/nunit/writing-tests/attributes/parallelizable.html) so that a new instance of a test fixture is constructed for every test within the test fixture. This allows tests to run in isolation without sharing instance fields and properties during parallel test runs. This make running parallel tests easier because it is easier to make your tests thread safe. + +This release also fixes several issues running tests in .NET 5.0. If your tests target .NET 5.0, we recommend updating to this release. + +#### Issues Resolved + +* 34 Async testing with F# +* 52 Self-contained item in array causes stack overflow +* 1394 Has.Property cannot see explicit interface implementation properties +* 1491 Add a CLA to the project +* 1546 NUnitEqualityComparer.GetEquatableGenericArguments should explicitly order arguments +* 1809 Assert.AreEqual fails for Complex on Linux +* 1897 EqualTo().Using() prevents caller from comparing strings to anything else +* 2211 Request: Add support of indexers to the PropertyConstraint +* 2477 Parameterized fixture with Explicit attribute can not be run when selected by name +* 2574 Instance-per-test-case feature +* 2680 Deprecate the DebugWriter class +* 3611 Properties are shown when --explore:nunit3 is run on entire project, but omitted when using the --where clause +* 3054 Don't enforce `[Timeout]` when debugger is attached +* 3075 Complete RunAsyncAction tests in FrameworkControllerTests +* 3228 Modulo bias is present in Randomizer.NextDecimal(decimal) +* 3240 Automate uploading of test results to Azure Pipelines +* 3243 Azure DevOps does not build release branch +* 3249 Pin GitLink version to speed up Cake script +* 3251 RawInt32() can't use Next since the maximum is always exclusive and it would never return int.MaxValue +* 3252 Timeout of 100 ms in TestTimeoutDoesNotStopCompletion occasionally fails the macOS build +* 3253 Chance of failure in random bias tests is not sufficiently low for CI +* 3256 Building under VS2019 +* 3257 Running under mono +* 3259 The type of an Array isn't inferred from properly +* 3264 Test that IRepeatTest only gets attributes via the IMethodInfo interface +* 3275 Enable setting IgnoreUntilDate in TestCaseData.Ignore +* 3279 Improve failure message from UniqueItemsConstraint +* 3282 TimeoutAttribute makes all Assertions count as failure +* 3283 ExecutionContext is flowed between unrelated tests +* 3286 Testing for equality using a predicate throws exception for collections +* 3290 'Good first issue' or 'help wanted' issue count badge +* 3296 ExceptionHelper.GetExceptionMessage(Exception ex) should tolerate exceptions from exceptions +* 3302 Incorrect formatting of failure message if test fails with Assert.Multiple +* 3303 Check type of actual argument using consistent helper method +* 3304 CheckString should not be a generic method +* 3305 Remove unused methods +* 3307 Sporadic GetResultIsNotCalledUntilContinued failure +* 3308 Fix disposal in EnumerablesComparer +* 3309 Simplify code in EventListenerTextWriter +* 3311 Minimal unit of DateTime in the report when Test was started/ended +* 3312 Simplify ProviderCache and make it instantiable since it is intentionally not thread safe +* 3315 Assert.DoesNotThrow() stopped working as it was previously +* 3318 Fix AwaitAdapter terminology +* 3321 Keep dependencies up to date +* 3322 Speed up build script by removing unnecessary builds +* 3324 Broken link in CHANGES.md +* 3328 Problems when using a mixture of Not and Or filters in NUnit framework 3.12.0 +* 3331 Contains.Key no longer working for IDictionary +* 3338 Azure Pipelines is failing on Linux for both netstandard 1.4 and 2.0 +* 3356 SetUpFixture not run +* 3368 Tests with warnings are not added to console TestResult.xml's total count +* 3383 Drop netstandard1.4 and stop testing on end-of-life versions of .NET Core +* 3389 Show names of parameters +* 3390 SetUpFixture not being triggered when running tests using --testlist +* 3392 Use of Thread.CurrentPrincipal in Blazor/WASM +* 3393 Nuget Package Not Signed +* 3395 Randomizer.NextString() can probably be sped up +* 3408 Save test results as build artifacts +* 3411 Update nuspec file to mention support for NET Standard 2.0+ +* 3414 Azure pipelines are failing on Linux +* 3415 Azure CI: Still publish test results on failure +* 3423 TestResult.cs casts ITestResult to TestResult +* 3447 Is.EqualTo(...).Using(StructuralComparisons.StructuralEqualityComparer or StructuralComparer) not working +* 3452 Assertions that use an existing Regex +* 3453 Visibility of SetUp/TearDown Methods +* 3454 Pre-Filtering in NUnitLite has problems +* 3464 Improve debugging experience +* 3470 Assertion for key-value-pair +* 3475 Our XML comments are using `` (block element) instead of `` (inline element) +* 3485 Should we make MultipleAssertException.TestResult maybe-null or obsolete two constructors? +* 3496 Adding data dictionary should not add a trailing newline +* 3497 Fix mixed line endings in Git +* 3503 Remove implicit cast from ITestResult to TestResult +* 3505 Better failure messages for Subset and Superset constraints +* 3506 ValueTuple tests now running if not targeting NET35 +* 3536 Reduce newly added API surface +* 3542 Update NuGet Package Icons +* 3547 DelayedConstraint constrains does not preserve original result additional information +* 3551 Add PrivateAssets="all" to analyzer dependency +* 3552 MessagePumpStrategy does not work for WPF on netcoreapp3.0 and upwards +* 3559 Disables the DOC100 suggestion and reverts the added paragraph elements +* 3563 `[Suggestion]` Improve TextMessageWriter output for numeric values +* 3565 .NET 5 issue with PlatformAttribute +* 3583 Avoid using a culture-sensitive EndsWith in common code +* 3592 Add classname and methodname to the start-test event +* 3594 Reduce memory overhead of TestNameGenerator +* 3596 AreAlmostEqualUlps throws OverflowException for -0 +* 3598 Fix typo +* 3608 `[Platform]` attribute fails with DllNotFoundException in WASM +* 3616 Extend Is.Empty to work for Guid.Empty +* 3618 NUnit has a P/Invoke whose native function doesn't exist on all platforms +* 3622 EmptyDirectoryConstraint doesn't need to enumerate entire directory contents +* 3632 Assert.Inconclusive() reports failed when timeout used +* 3636 NUnitLite filtering fails if space in test name before ( +* 3641 Type implementing `IComparable` (or any `IComparable`) fails comparison. +* 3647 Fix exception under blazor 5 +* 3650 Build issue with the latest .NET SDK 5.0.100-rc.2 +* 3657 Add Framework Version to the XML +* 3662 TestContext.CurrentContext.CurrentRepeatCount only contains retry count not the repeat count +* 3667 Create FrameworkPackageSetting to set CurrentCulture and CurrentUICulture +* 3676 Parallelizeable tests sometimes shares memory +* 3679 Issue 3390: Do not prefilter relevant SetUpFixtures +* 3694 Async tests causes double failure messages +* 3699 Compilation of netcoreapp3.1 targets fails on CI (both AppVeyor and Azure Pipelines) + ### NUnit 3.12 - May 14, 2019 This release of NUnit finally drops support for .NET 2.0. If your application still @@ -1071,7 +1186,7 @@ use the NUnit NuGet packages for the framework, but a ZIP file with the binaries ### NUnit 3.0.0 Beta 2 - May 12, 2015 -####Framework +#### Framework * The Compact Framework version of the framework is now packaged separately and will be distributed as a ZIP file and as a NuGet package. @@ -1089,8 +1204,7 @@ use the NUnit NuGet packages for the framework, but a ZIP file with the binaries * Added a core engine which is a non-extensible, minimal engine for use by devices and similar situations where reduced functionality is compensated - for by reduced size and simplicity of usage. See - https://github.com/nunit/dev/wiki/Core-Engine for more information. + for by reduced size and simplicity of usage. #### Issues Resolved @@ -1658,7 +1772,7 @@ NOTE: Bug Fixes below this point refer to the number of the bug in Launchpad. * 561436 SetCulture broken with 2.5.4 * 563532 DatapointsAttribute should be allowed on properties and methods -###NUnit 2.9.3 - October 26, 2009 +### NUnit 2.9.3 - October 26, 2009 #### Main Features @@ -1672,29 +1786,29 @@ NOTE: Bug Fixes below this point refer to the number of the bug in Launchpad. * 432805 Some Framework Tests don't run on Linux * 440109 Full Framework does not support "Contains" -###NUnit 2.9.2 - September 19, 2009 +### NUnit 2.9.2 - September 19, 2009 -####Main Features +#### Main Features * NUnitLite code is now merged with NUnit * Added NUnitLite runner to the framework code * Added Compact framework builds -####Bug Fixes +#### Bug Fixes * 430100 `Assert.Catch` should return T * 432566 NUnitLite shows empty string as argument * 432573 Mono test should be at runtime -###NUnit 2.9.1 - August 27, 2009 +### NUnit 2.9.1 - August 27, 2009 -####General +#### General * Created a separate project for the framework and framework tests * Changed license to MIT / X11 * Created Windows installer for the framework -####Bug Fixes +#### Bug Fixes * 400502 NUnitEqualityComparer.StreamsE­qual fails for same stream * 400508 TestCaseSource attirbute is not working when Type is given diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index e9846d6434..a351d1343b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -34,9 +34,7 @@ Issues that need confirmation will have the **confirm** label or be unlabeled an - Create unit tests to demonstrate the issue - Test issues and provide feedback -As of version 3.10, the NUnit and NUnitLite NuGet packages support **debugger source-stepping** for each binary from the repository. Debuggers can step into the source code, set breakpoints, watch variables, etc. If you’re getting ready to report a bug in NUnit, figuring out how to create a minimal repro is much easier since you aren’t dealing with a black box! - -To easily drop into NUnit code from your project any time, [follow these steps](https://github.com/nunit/docs/wiki/Debugger-Source-Stepping). +If you’re getting ready to report a bug in NUnit, figuring out how to create a minimal repro is easier if you temporarily disable the debugger’s [Just My Code](https://docs.microsoft.com/en-us/visualstudio/debugger/just-my-code) setting. This allows you to step into NUnit's source code, set breakpoints, watch variables, etc. ## Documentation diff --git a/LICENSE.txt b/LICENSE.txt index b029a77c34..29f0e2ea4c 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2019 Charlie Poole, Rob Prouse +Copyright (c) 2021 Charlie Poole, Rob Prouse Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/NUnit.sln.DotSettings b/NUnit.sln.DotSettings index d7fe019de8..c6f985f818 100644 --- a/NUnit.sln.DotSettings +++ b/NUnit.sln.DotSettings @@ -202,4 +202,46 @@ namespace $NAMESPACE$ $END$ } } -} +} + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True + True diff --git a/NuGet.config b/NuGet.config index ac89f19943..ff59585293 100644 --- a/NuGet.config +++ b/NuGet.config @@ -1,8 +1,6 @@ - - - + diff --git a/README.md b/README.md index d044e2aeca..0b7a0b0fe7 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ [![AppVeyor Build status](https://ci.appveyor.com/api/projects/status/3xfkxtnkrts1x06q/branch/master?svg=true)](https://ci.appveyor.com/project/CharliePoole/nunit/branch/master) [![Travis Build Status](https://travis-ci.org/nunit/nunit.svg?branch=master)](https://travis-ci.org/nunit/nunit) [![Azure Pipelines Build Status](https://nunit.visualstudio.com/NUnit/_apis/build/status/NUnit%20Framework/NUnit%20Framework%20CI?branchName=master)](https://nunit.visualstudio.com/NUnit/_build/latest?definitionId=11?branchName=master) [![NuGet Version and Downloads count](https://buildstats.info/nuget/NUnit)](https://www.nuget.org/packages/NUnit) -[![Follow NUnit](https://img.shields.io/twitter/follow/nunit.svg?style=social)](https://twitter.com/nunit) [![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/nunit/nunit) [![nunit-discuss Google Groups](https://img.shields.io/badge/mailing%20list-nunit--discuss-blue.svg)](https://groups.google.com/forum/#!forum/nunit-discuss) +[![Follow NUnit](https://img.shields.io/twitter/follow/nunit.svg?style=social)](https://twitter.com/nunit) [![Gitter](https://img.shields.io/gitter/room/nwjs/nw.js.svg)](https://gitter.im/nunit/nunit) [![nunit-discuss Google Groups](https://img.shields.io/badge/mailing%20list-nunit--discuss-blue.svg)](https://groups.google.com/forum/#!forum/nunit-discuss) [![NUnit issues marked with "help wanted" label](https://img.shields.io/github/issues/nunit/nunit/help%20wanted.svg)](https://github.com/nunit/nunit/issues?q=is%3Aopen+is%3Aissue+label%3A%22help+wanted%22) [![NUnit issues marked with "good first issue" label](https://img.shields.io/github/issues/nunit/nunit/good%20first%20issue.svg)](https://github.com/nunit/nunit/issues?q=is%3Aopen+is%3Aissue+label%3A%22good+first+issue%22) NUnit is a unit-testing framework for all .NET languages. Initially ported from JUnit, the current production release, version 3, has been completely rewritten with many new features and support for a wide range of .NET platforms. @@ -11,7 +11,6 @@ NUnit is a unit-testing framework for all .NET languages. Initially ported from - [Downloads](#downloads) - [Documentation](#documentation) - [Contributing](#contributing) -- [Debugger source-stepping](#debugger-source-stepping) - [License](#license) - [NUnit Projects](#nunit-projects) @@ -36,12 +35,6 @@ NUnit 3.0 was created by [Charlie Poole](https://github.com/CharliePoole), [Rob Earlier versions of NUnit were developed by Charlie Poole, James W. Newkirk, Alexei A. Vorontsov, Michael C. Two and Philip A. Craig. -## Debugger source-stepping ## - -The NUnit and NUnitLite NuGet packages contain a source-indexed PDB for each binary from this repository. -If you’re in the middle of a debugging session and realize you’d like to be able to step into NUnit code, -set breakpoints and watch variables, [follow these steps](https://github.com/nunit/docs/wiki/Debugger-Source-Stepping). - ## License ## NUnit is Open Source software and NUnit 3 is released under the [MIT license](https://raw.githubusercontent.com/nunit/nunit/master/LICENSE.txt). Earlier releases used the [NUnit license](https://nunit.org/nuget/license.html). Both of these licenses allow the use of NUnit in free and commercial applications and libraries without restrictions. diff --git a/appveyor.yml b/appveyor.yml index 06433bf7ca..3edb4eb066 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,9 +1,13 @@ -image: Visual Studio 2017 +image: Visual Studio 2019 branches: except: - /travis-.*/ +install: + - ps: Invoke-WebRequest 'https://dot.net/v1/dotnet-install.ps1' -OutFile 'dotnet-install.ps1' + - ps: .\dotnet-install.ps1 -Version 5.0.101 + build_script: - ps: .\build.ps1 -Target "Appveyor" -Configuration "Release" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index fa8a31135f..5e7387a787 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -1,59 +1,25 @@ +pr: [ '*' ] + trigger: branches: - include: [ '*' ] + include: [ 'master', 'release' ] exclude: [ 'refs/tags/*' ] jobs: - job: Windows pool: - vmImage: vs2017-win2016 + vmImage: windows-latest steps: - - powershell: .\build.ps1 --target=Test - displayName: Build and test - - # Workaround for https://github.com/nunit/nunit/issues/3012#issuecomment-441517922 - - task: PublishTestResults@2 - displayName: Publish net45 test results - inputs: - testResultsFormat: NUnit - testResultsFiles: test-results\net45\*.xml - mergeTestResults: true - testRunTitle: net45/Windows - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish net40 test results - inputs: - testResultsFormat: NUnit - testResultsFiles: test-results\net40\*.xml - mergeTestResults: true - testRunTitle: net40/Windows - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish net35 test results + - task: UseDotNet@2 + displayName: 'Install .NET Core SDK' inputs: - testResultsFormat: NUnit - testResultsFiles: test-results\net35\*.xml - mergeTestResults: true - testRunTitle: net35/Windows - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish netcoreapp1.1 test results - inputs: - testResultsFormat: NUnit - testResultsFiles: test-results\netcoreapp1.1\*.xml - mergeTestResults: true - testRunTitle: netcoreapp1.1/Windows - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish netcoreapp2.0 test results - inputs: - testResultsFormat: NUnit - testResultsFiles: test-results\netcoreapp2.0\*.xml - mergeTestResults: true - testRunTitle: netcoreapp2.0/Windows - condition: succeededOrFailed() + version: 5.0.101 + performMultiLevelLookup: true + + - powershell: .\build.ps1 --target=Test --test-run-name=Windows + displayName: Build, test, and publish results - powershell: .\build.ps1 --target=Package --artifact-dir='$(Build.ArtifactStagingDirectory)' displayName: Package @@ -64,102 +30,80 @@ jobs: PathtoPublish: $(Build.ArtifactStagingDirectory) ArtifactName: Package + - task: PublishBuildArtifacts@1 + displayName: Save test results artifacts + condition: always() + inputs: + PathtoPublish: test-results + ArtifactName: Test results (Windows) + - job: Linux pool: - vmImage: ubuntu-16.04 + vmImage: ubuntu-latest steps: - - bash: ./build.sh --target=Test --configuration=Release - displayName: Build and test - - # Workaround for https://github.com/nunit/nunit/issues/3012#issuecomment-441517922 - - task: PublishTestResults@2 - displayName: Publish net45 test results - inputs: - testResultsFormat: NUnit - testResultsFiles: test-results/net45/*.xml - mergeTestResults: true - testRunTitle: net45/Linux - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish net40 test results + - task: UseDotNet@2 + displayName: 'Install .NET Core SDK' inputs: - testResultsFormat: NUnit - testResultsFiles: test-results/net40/*.xml - mergeTestResults: true - testRunTitle: net40/Linux - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish net35 test results + version: 5.0.101 + + - task: UseDotNet@2 + displayName: 'Install .NET Core runtime 2.1' inputs: - testResultsFormat: NUnit - testResultsFiles: test-results/net35/*.xml - mergeTestResults: true - testRunTitle: net35/Linux - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish netcoreapp1.1 test results + packageType: runtime + version: 2.1.x + + - task: UseDotNet@2 + displayName: 'Install .NET Core runtime 3.1' inputs: - testResultsFormat: NUnit - testResultsFiles: test-results/netcoreapp1.1/*.xml - mergeTestResults: true - testRunTitle: netcoreapp1.1/Linux - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish netcoreapp2.0 test results + packageType: runtime + version: 3.1.x + + - bash: sudo apt-get install fsharp + displayName: Install F# for Mono + + - bash: | + dotnet tool install --global Cake.Tool + dotnet cake --target=Test --test-run-name=Linux --configuration=Release + displayName: Build, test, and publish results + + - task: PublishBuildArtifacts@1 + displayName: Save test results artifacts + condition: always() inputs: - testResultsFormat: NUnit - testResultsFiles: test-results/netcoreapp2.0/*.xml - mergeTestResults: true - testRunTitle: netcoreapp2.0/Linux - condition: succeededOrFailed() + PathtoPublish: test-results + ArtifactName: Test results (Linux) - job: macOS pool: - vmImage: macOS-10.13 + vmImage: macOS-latest steps: - - bash: ./build.sh --target=Test --configuration=Release - displayName: Build and test - - # Workaround for https://github.com/nunit/nunit/issues/3012#issuecomment-441517922 - - task: PublishTestResults@2 - displayName: Publish net45 test results + - task: UseDotNet@2 + displayName: 'Install .NET Core SDK' inputs: - testResultsFormat: NUnit - testResultsFiles: test-results/net45/*.xml - mergeTestResults: true - testRunTitle: net45/macOS - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish net40 test results - inputs: - testResultsFormat: NUnit - testResultsFiles: test-results/net40/*.xml - mergeTestResults: true - testRunTitle: net40/macOS - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish net35 test results + version: 5.0.101 + + - task: UseDotNet@2 + displayName: 'Install .NET Core runtime 2.1' inputs: - testResultsFormat: NUnit - testResultsFiles: test-results/net35/*.xml - mergeTestResults: true - testRunTitle: net35/macOS - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish netcoreapp1.1 test results + packageType: runtime + version: 2.1.x + + - task: UseDotNet@2 + displayName: 'Install .NET Core runtime 3.1' inputs: - testResultsFormat: NUnit - testResultsFiles: test-results/netcoreapp1.1/*.xml - mergeTestResults: true - testRunTitle: netcoreapp1.1/macOS - condition: succeededOrFailed() - - task: PublishTestResults@2 - displayName: Publish netcoreapp2.0 test results + packageType: runtime + version: 3.1.x + + - bash: | + dotnet tool install --global Cake.Tool + dotnet cake --target=Test --test-run-name=macOS --configuration=Release + displayName: Build, test, and publish results + + - task: PublishBuildArtifacts@1 + displayName: Save test results artifacts + condition: always() inputs: - testResultsFormat: NUnit - testResultsFiles: test-results/netcoreapp2.0/*.xml - mergeTestResults: true - testRunTitle: netcoreapp2.0/macOS - condition: succeededOrFailed() + PathtoPublish: test-results + ArtifactName: Test results (macOS) diff --git a/build-mono-docker.ps1 b/build-mono-docker.ps1 deleted file mode 100644 index 20dc6aa7f6..0000000000 --- a/build-mono-docker.ps1 +++ /dev/null @@ -1,7 +0,0 @@ -trap [Management.Automation.CommandNotFoundException] { - Write-Error 'Docker cannot be found. Make sure it is installed and added to the path.' - Start-Process -FilePath 'https://docs.docker.com/docker-for-windows/install/' - continue; -} - -docker run --rm -it -v ${PSScriptRoot}:/nunit -w=/nunit mono:latest bash build.sh $args diff --git a/build.cake b/build.cake index 016f8ef52c..d282f4da4c 100644 --- a/build.cake +++ b/build.cake @@ -1,5 +1,4 @@ -#tool nuget:?package=NUnit.ConsoleRunner&version=3.9.0 -#tool GitLink +#tool NUnit.ConsoleRunner&version=3.10.0 ////////////////////////////////////////////////////////////////////// // ARGUMENTS @@ -33,14 +32,13 @@ var AllFrameworks = new string[] "net45", "net40", "net35", - "netstandard1.4", "netstandard2.0" }; var NetCoreTests = new String[] { - "netcoreapp1.1", - "netcoreapp2.0" + "netcoreapp2.1", + "netcoreapp3.1" }; ////////////////////////////////////////////////////////////////////// @@ -145,25 +143,24 @@ Task("Build") .IsDependentOn("NuGetRestore") .Does(() => { - MSBuild(SOLUTION_FILE, CreateSettings()); - - Information("Publishing netcoreapp1.1 tests so that dependencies are present..."); - - MSBuild("src/NUnitFramework/tests/nunit.framework.tests.csproj", CreateSettings() - .WithTarget("Publish") - .WithProperty("TargetFramework", "netcoreapp1.1") - .WithProperty("NoBuild", "true") // https://github.com/dotnet/cli/issues/5331#issuecomment-338392972 - .WithProperty("PublishDir", BIN_DIR + "netcoreapp1.1/") - .WithRawArgument("/nologo")); + if(IsRunningOnWindows()) + MSBuild(SOLUTION_FILE, CreateMsBuildSettings()); + else + DotNetCoreBuild(SOLUTION_FILE, CreateDotNetCoreBuildSettings()); }); -MSBuildSettings CreateSettings() +DotNetCoreBuildSettings CreateDotNetCoreBuildSettings() => + new DotNetCoreBuildSettings + { + Configuration = configuration, + NoRestore = true, + Verbosity = DotNetCoreVerbosity.Minimal + }; + +MSBuildSettings CreateMsBuildSettings() { var settings = new MSBuildSettings { Verbosity = Verbosity.Minimal, Configuration = configuration }; - // Only needed when packaging - settings.WithProperty("DebugType", "pdbonly"); - if (IsRunningOnWindows()) { // Find MSBuild for Visual Studio 2019 and newer @@ -207,10 +204,12 @@ Task("Test45") var dir = BIN_DIR + runtime + "/"; RunNUnitTests(dir, FRAMEWORK_TESTS, runtime, ref ErrorDetail); RunTest(dir + EXECUTABLE_NUNITLITE_TESTS_EXE, dir, runtime, ref ErrorDetail); + PublishTestResults(runtime); }); Task("Test40") .Description("Tests the .NET 4.0 version of the framework") + .WithCriteria(IsRunningOnWindows()) .IsDependentOn("Build") .OnError(exception => { ErrorDetail.Add(exception.Message); }) .Does(() => @@ -219,6 +218,7 @@ Task("Test40") var dir = BIN_DIR + runtime + "/"; RunNUnitTests(dir, FRAMEWORK_TESTS, runtime, ref ErrorDetail); RunTest(dir + EXECUTABLE_NUNITLITE_TESTS_EXE, dir, runtime, ref ErrorDetail); + PublishTestResults(runtime); }); Task("Test35") @@ -231,31 +231,29 @@ Task("Test35") var dir = BIN_DIR + runtime + "/"; RunNUnitTests(dir, FRAMEWORK_TESTS, runtime, ref ErrorDetail); RunTest(dir + EXECUTABLE_NUNITLITE_TESTS_EXE, dir, runtime, ref ErrorDetail); + PublishTestResults(runtime); }); -Task("TestNetStandard14") - .Description("Tests the .NET Standard 1.4 version of the framework") - .IsDependentOn("Build") - .OnError(exception => { ErrorDetail.Add(exception.Message); }) - .Does(() => - { - var runtime = "netcoreapp1.1"; - var dir = BIN_DIR + runtime + "/"; - RunDotnetCoreTests(dir + NUNITLITE_RUNNER_DLL, dir, FRAMEWORK_TESTS, runtime, GetResultXmlPath(FRAMEWORK_TESTS, runtime), ref ErrorDetail); - RunDotnetCoreTests(dir + EXECUTABLE_NUNITLITE_TESTS_DLL, dir, runtime, ref ErrorDetail); - }); +var testNetStandard20 = Task("TestNetStandard20") + .Description("Tests the .NET Standard 2.0 version of the framework"); -Task("TestNetStandard20") - .Description("Tests the .NET Standard 2.0 version of the framework") - .IsDependentOn("Build") - .OnError(exception => { ErrorDetail.Add(exception.Message); }) - .Does(() => - { - var runtime = "netcoreapp2.0"; - var dir = BIN_DIR + runtime + "/"; - RunDotnetCoreTests(dir + NUNITLITE_RUNNER_DLL, dir, FRAMEWORK_TESTS, runtime, GetResultXmlPath(FRAMEWORK_TESTS, runtime), ref ErrorDetail); - RunDotnetCoreTests(dir + EXECUTABLE_NUNITLITE_TESTS_DLL, dir, runtime, ref ErrorDetail); - }); +foreach (var runtime in new[] { "netcoreapp2.1", "netcoreapp3.1", "net5.0", "net5.0-windows" }) +{ + var task = Task("TestNetStandard20 on " + runtime) + .Description("Tests the .NET Standard 2.0 version of the framework on " + runtime) + .WithCriteria(IsRunningOnWindows() || runtime != "net5.0-windows") + .IsDependentOn("Build") + .OnError(exception => { ErrorDetail.Add(exception.Message); }) + .Does(() => + { + var dir = BIN_DIR + runtime + "/"; + RunDotnetCoreTests(dir + NUNITLITE_RUNNER_DLL, dir, FRAMEWORK_TESTS, runtime, GetResultXmlPath(FRAMEWORK_TESTS, runtime), ref ErrorDetail); + RunDotnetCoreTests(dir + EXECUTABLE_NUNITLITE_TESTS_DLL, dir, runtime, ref ErrorDetail); + PublishTestResults(runtime); + }); + + testNetStandard20.IsDependentOn(task); +} ////////////////////////////////////////////////////////////////////// // PACKAGE @@ -276,18 +274,15 @@ var FrameworkFiles = new FilePath[] "mock-assembly.dll", "mock-assembly.exe", "nunit.framework.dll", - "nunit.framework.pdb", "nunit.framework.xml", "nunit.framework.tests.dll", "nunit.testdata.dll", "nunitlite.dll", - "nunitlite.pdb", "nunitlite.tests.exe", "nunitlite.tests.dll", "slow-nunit-tests.dll", "nunitlite-runner.exe", "nunitlite-runner.dll", - "nunitlite-runner.pdb", "Microsoft.Threading.Tasks.dll", "Microsoft.Threading.Tasks.Extensions.Desktop.dll", "Microsoft.Threading.Tasks.Extensions.dll", @@ -335,17 +330,9 @@ Task("CreateImage") } }); -Task("GitLink") - .IsDependentOn("CreateImage") - .Description("Source-indexes PDBs in the images directory to the current commit") - .Does(() => - { - GitLink3(GetFiles($"{CurrentImageDir}**/*.pdb")); - }); - Task("PackageFramework") .Description("Creates NuGet packages of the framework") - .IsDependentOn("GitLink") + .IsDependentOn("CreateImage") .Does(() => { CreateDirectory(PACKAGE_DIR); @@ -363,7 +350,7 @@ Task("PackageFramework") Task("PackageZip") .Description("Creates a ZIP file of the framework") - .IsDependentOn("GitLink") + .IsDependentOn("CreateImage") .Does(() => { CreateDirectory(PACKAGE_DIR); @@ -380,6 +367,70 @@ Task("PackageZip") Zip(CurrentImageDir, File(ZIP_PACKAGE), zipFiles); }); +Task("CreateToolManifest") + .Does(() => + { + var result = StartProcess("dotnet.exe", new ProcessSettings { Arguments = "new tool-manifest --force" }); + }); + +Task("InstallSigningTool") + .Description("Installs the signing tool") + .IsDependentOn("CreateToolManifest") + .Does(() => + { + var result = StartProcess("dotnet.exe", new ProcessSettings { Arguments = "tool install SignClient" }); + }); + +Task("SignPackages") + .Description("Signs the NuGet packages") + .IsDependentOn("InstallSigningTool") + .IsDependentOn("PackageFramework") + .Does(() => + { + // Get the secret. + var secret = EnvironmentVariable("SIGNING_SECRET"); + if(string.IsNullOrWhiteSpace(secret)) { + throw new InvalidOperationException("Could not resolve signing secret."); + } + + // Get the user. + var user = EnvironmentVariable("SIGNING_USER"); + if(string.IsNullOrWhiteSpace(user)) { + throw new InvalidOperationException("Could not resolve signing user."); + } + + var signClientPath = Context.Tools.Resolve("SignClient.exe") ?? Context.Tools.Resolve("SignClient") ?? throw new Exception("Failed to locate sign tool"); + + var settings = File("./signclient.json"); + + // Get the files to sign. + var files = GetFiles(string.Concat(PACKAGE_DIR, "*.nupkg")); + + foreach(var file in files) + { + Information("Signing {0}...", file.FullPath); + + // Build the argument list. + var arguments = new ProcessArgumentBuilder() + .Append("sign") + .AppendSwitchQuoted("-c", MakeAbsolute(settings.Path).FullPath) + .AppendSwitchQuoted("-i", MakeAbsolute(file).FullPath) + .AppendSwitchQuotedSecret("-s", secret) + .AppendSwitchQuotedSecret("-r", user) + .AppendSwitchQuoted("-n", "NUnit.org") + .AppendSwitchQuoted("-d", "NUnit is a unit-testing framework for all .NET languages.") + .AppendSwitchQuoted("-u", "https://nunit.org/"); + + // Sign the binary. + var result = StartProcess(signClientPath.FullPath, new ProcessSettings { Arguments = arguments }); + if(result != 0) + { + // We should not recover from this. + throw new InvalidOperationException("Signing failed!"); + } + } + }); + ////////////////////////////////////////////////////////////////////// // UPLOAD ARTIFACTS ////////////////////////////////////////////////////////////////////// @@ -485,6 +536,9 @@ void RunDotnetCoreTests(FilePath exePath, DirectoryPath workingDir, string frame void RunDotnetCoreTests(FilePath exePath, DirectoryPath workingDir, string arguments, string framework, FilePath resultFile, ref List errorDetail) { + if (!FileExists(exePath)) + return; + int rc = StartProcess( "dotnet", new ProcessSettings @@ -503,6 +557,27 @@ void RunDotnetCoreTests(FilePath exePath, DirectoryPath workingDir, string argum errorDetail.Add(string.Format("{0} returned rc = {1}", exePath, rc)); } +void PublishTestResults(string framework) +{ + if (EnvironmentVariable("TF_BUILD", false)) + { + var fullTestRunTitle = framework; + var ciRunName = Argument("test-run-name"); + if (!string.IsNullOrEmpty(ciRunName)) + fullTestRunTitle += '/' + ciRunName; + + AzurePipelines.Commands.PublishTestResults(new AzurePipelinesPublishTestResultsData + { + TestResultsFiles = GetFiles($@"test-results\{framework}\*.xml").ToList(), + TestRunTitle = fullTestRunTitle, + TestRunner = AzurePipelinesTestRunnerType.NUnit, + MergeTestResults = true, + PublishRunAttachments = true, + Configuration = configuration + }); + } +} + public static T WithRawArgument(this T settings, string rawArgument) where T : Cake.Core.Tooling.ToolSettings { if (settings == null) throw new ArgumentNullException(nameof(settings)); @@ -534,7 +609,6 @@ Task("Test") .IsDependentOn("Test45") .IsDependentOn("Test40") .IsDependentOn("Test35") - .IsDependentOn("TestNetStandard14") .IsDependentOn("TestNetStandard20"); Task("Package") diff --git a/global.json b/global.json new file mode 100644 index 0000000000..58ccd2df2c --- /dev/null +++ b/global.json @@ -0,0 +1,7 @@ +{ + "sdk": { + "version": "5.0.101", + "allowPrerelease": false, + "rollForward": "major" + } +} diff --git a/nuget/framework/nunit.nuspec b/nuget/framework/nunit.nuspec index 6111c27749..d106416fc8 100644 --- a/nuget/framework/nunit.nuspec +++ b/nuget/framework/nunit.nuspec @@ -10,6 +10,7 @@ https://nunit.org https://cdn.rawgit.com/nunit/resources/master/images/icon/nunit_256.png + icon.png false NUnit is a unit-testing framework for all .NET languages with a strong TDD focus. NUnit features a fluent assert syntax, parameterized, generic and theory tests and is user-extensible. @@ -18,21 +19,15 @@ This package includes the NUnit 3 framework assembly, which is referenced by you Supported platforms: - .NET Framework 3.5+ -- .NET Standard 1.4+ -- .NET Core +- .NET Standard 2.0+ This package includes the NUnit 3 framework assembly, which is referenced by your tests. You will need to install version 3 of the nunit3-console program or a third-party runner that supports NUnit 3 in order to execute tests. Runners intended for use with NUnit 2.x will not run NUnit 3 tests correctly. en-US nunit test testing tdd framework fluent assert theory plugin addin - Copyright (c) 2019 Charlie Poole, Rob Prouse + Copyright (c) 2021 Charlie Poole, Rob Prouse - - - - - @@ -42,20 +37,14 @@ Supported platforms: + - - - - - - - diff --git a/nuget/icon.png b/nuget/icon.png new file mode 100644 index 0000000000..ad14c1603e Binary files /dev/null and b/nuget/icon.png differ diff --git a/nuget/nunitlite/Program.cs b/nuget/nunitlite/Program.cs index 0a1a2f6968..cf94174fb8 100644 --- a/nuget/nunitlite/Program.cs +++ b/nuget/nunitlite/Program.cs @@ -1,5 +1,5 @@ // *********************************************************************** -// Copyright (c) 2015 Charlie Poole, Rob Prouse +// Copyright (c) 2021 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the diff --git a/nuget/nunitlite/Program.vb b/nuget/nunitlite/Program.vb index 3d204153ea..45dd7aa33e 100644 --- a/nuget/nunitlite/Program.vb +++ b/nuget/nunitlite/Program.vb @@ -1,5 +1,5 @@ ' *********************************************************************** -' Copyright (c) 2015 Charlie Poole, Rob Prouse +' Copyright (c) 2021 Charlie Poole, Rob Prouse ' ' Permission is hereby granted, free of charge, to any person obtaining ' a copy of this software and associated documentation files (the diff --git a/nuget/nunitlite/nunitlite.nuspec b/nuget/nunitlite/nunitlite.nuspec index f1106fa239..8b090290a3 100644 --- a/nuget/nunitlite/nunitlite.nuspec +++ b/nuget/nunitlite/nunitlite.nuspec @@ -10,14 +10,15 @@ https://nunit.org https://cdn.rawgit.com/nunit/resources/master/images/icon/nunit_256.png + icon.png false NUnitlite is a lightweight runner for tests that use the NUnit testing framework. NUnitLite provides a simple way to run NUnit tests, without the overhead of a full NUnit installation. It is suitable for projects that want to have a quick way to run tests using a console runner and don't need all the features of the NUnit engine and console runner. Supported platforms: -- .NET Framework 2.0+ -- .NET Standard 1.4+ -- .NET Core +- .NET Framework 3.5+ +- .NET Standard 2.0+ +- .NET Core 2.1+ How to use this package: @@ -26,7 +27,7 @@ How to use this package: 3. Add your tests to the test project and simply start the project to execute them. en-US test unit testing tdd framework fluent assert device phone embedded - Copyright (c) 2019 Charlie Poole, Rob Prouse + Copyright (c) 2021 Charlie Poole, Rob Prouse @@ -37,11 +38,6 @@ How to use this package: - - - - - @@ -52,26 +48,16 @@ How to use this package: + - - - - - - - - - - - - - + + diff --git a/nunit.sln b/nunit.sln index 2a7f28a200..316525fd0f 100644 --- a/nunit.sln +++ b/nunit.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.16 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.30709.132 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "nunit.framework", "src\NUnitFramework\framework\nunit.framework.csproj", "{B7753E96-F76B-4E9B-9071-47B16DB90FD6}" EndProject @@ -10,13 +10,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig .gitattributes = .gitattributes .gitignore = .gitignore - .travis.yml = .travis.yml appveyor.yml = appveyor.yml + azure-pipelines.yml = azure-pipelines.yml build.cake = build.cake BUILDING.md = BUILDING.md CHANGES.md = CHANGES.md CONTRIBUTING.md = CONTRIBUTING.md src\NUnitFramework\Directory.Build.props = src\NUnitFramework\Directory.Build.props + global.json = global.json LICENSE.txt = LICENSE.txt NOTICES.txt = NOTICES.txt NuGet.config = NuGet.config @@ -53,6 +54,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nunitlite", "nunitlite", "{ nuget\nunitlite\Program.vb = nuget\nunitlite\Program.vb EndProjectSection EndProject +Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "nunit.testdata.fsharp", "src\NUnitFramework\testdata.fsharp\nunit.testdata.fsharp.fsproj", "{9DF6B262-70E2-44E3-A436-3B65C7FD88DE}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -91,6 +94,10 @@ Global {6EA77093-1AD4-4066-8CC2-C35E08FE8638}.Debug|Any CPU.Build.0 = Debug|Any CPU {6EA77093-1AD4-4066-8CC2-C35E08FE8638}.Release|Any CPU.ActiveCfg = Release|Any CPU {6EA77093-1AD4-4066-8CC2-C35E08FE8638}.Release|Any CPU.Build.0 = Release|Any CPU + {9DF6B262-70E2-44E3-A436-3B65C7FD88DE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9DF6B262-70E2-44E3-A436-3B65C7FD88DE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9DF6B262-70E2-44E3-A436-3B65C7FD88DE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9DF6B262-70E2-44E3-A436-3B65C7FD88DE}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/signclient.json b/signclient.json new file mode 100644 index 0000000000..3276a45de5 --- /dev/null +++ b/signclient.json @@ -0,0 +1,13 @@ +{ + "SignClient": { + "AzureAd": { + "AADInstance": "https://login.microsoftonline.com/", + "ClientId": "c248d68a-ba6f-4aa9-8a68-71fe872063f8", + "TenantId": "16076fdc-fcc1-4a15-b1ca-32c9a255900e" + }, + "Service": { + "Url": "https://codesign.dotnetfoundation.org/", + "ResourceId": "https://SignService/3c30251f-36f3-490b-a955-520addb85001" + } + } +} \ No newline at end of file diff --git a/src/CommonAssemblyInfo.cs b/src/CommonAssemblyInfo.cs index 02a7009ba9..ca9e98a981 100644 --- a/src/CommonAssemblyInfo.cs +++ b/src/CommonAssemblyInfo.cs @@ -28,7 +28,7 @@ // [assembly: AssemblyCompany("NUnit Software")] [assembly: AssemblyProduct("NUnit 3")] -[assembly: AssemblyCopyright("Copyright (c) 2019 Charlie Poole, Rob Prouse")] +[assembly: AssemblyCopyright("Copyright (c) 2021 Charlie Poole, Rob Prouse")] [assembly: AssemblyTrademark("NUnit is a trademark of NUnit Software")] #if DEBUG @@ -38,14 +38,14 @@ [assembly: AssemblyConfiguration(".NET Framework 4.0 Debug")] #elif NET35 [assembly: AssemblyConfiguration(".NET Framework 3.5 Debug")] -#elif NETSTANDARD1_4 -[assembly: AssemblyConfiguration(".NET Standard 1.4 Debug")] #elif NETSTANDARD2_0 [assembly: AssemblyConfiguration(".NET Standard 2.0 Debug")] -#elif NETCOREAPP1_1 -[assembly: AssemblyConfiguration(".NET Core 1.1 Debug")] -#elif NETCOREAPP2_0 -[assembly: AssemblyConfiguration(".NET Core 2.0 Debug")] +#elif NETCOREAPP2_1 +[assembly: AssemblyConfiguration(".NET Core 2.1 Debug")] +#elif NETCOREAPP3_1 +[assembly: AssemblyConfiguration(".NET Core 3.1 Debug")] +#elif NET5_0 +[assembly: AssemblyConfiguration(".NET 5.0 Debug")] #else #error Missing AssemblyConfiguration attribute for this target. #endif @@ -56,14 +56,14 @@ [assembly: AssemblyConfiguration(".NET Framework 4.0")] #elif NET35 [assembly: AssemblyConfiguration(".NET Framework 3.5")] -#elif NETSTANDARD1_4 -[assembly: AssemblyConfiguration(".NET Standard 1.4")] #elif NETSTANDARD2_0 [assembly: AssemblyConfiguration(".NET Standard 2.0")] -#elif NETCOREAPP1_1 -[assembly: AssemblyConfiguration(".NET Core 1.1")] -#elif NETCOREAPP2_0 -[assembly: AssemblyConfiguration(".NET Core 2.0")] +#elif NETCOREAPP2_1 +[assembly: AssemblyConfiguration(".NET Core 2.1")] +#elif NETCOREAPP3_1 +[assembly: AssemblyConfiguration(".NET Core 3.1")] +#elif NET5_0 +[assembly: AssemblyConfiguration(".NET 5.0")] #else #error Missing AssemblyConfiguration attribute for this target. #endif diff --git a/src/NUnitFramework/Directory.Build.props b/src/NUnitFramework/Directory.Build.props index b7420cd325..a9e9c16629 100644 --- a/src/NUnitFramework/Directory.Build.props +++ b/src/NUnitFramework/Directory.Build.props @@ -1,10 +1,11 @@ - 6 + 8 strict true ..\..\nunit.snk + true false ..\..\..\bin\$(Configuration)\ @@ -16,26 +17,25 @@ - - - $(DefineConstants);PARALLEL;SERIALIZATION - $(DefineConstants);TASK_PARALLEL_LIBRARY_API - $(DefineConstants);THREAD_ABORT + $(DefineConstants);THREAD_ABORT + - $(DefineConstants);PLATFORM_DETECTION;APARTMENT_STATE + + + false + embedded + true + true - - + diff --git a/src/NUnitFramework/SchemaTestUtils.cs b/src/NUnitFramework/SchemaTestUtils.cs index f8d86649f4..3dfc319aa2 100644 --- a/src/NUnitFramework/SchemaTestUtils.cs +++ b/src/NUnitFramework/SchemaTestUtils.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !NETCOREAPP1_1 // Schema validation doesn’t exist #if !NET35 // Framework bug causes NRE: https://social.msdn.microsoft.com/Forums/en-US/53be44de-30b2-4d18-968d-d3414d0783b1 // We don’t really need these tests to run on more than one platform. @@ -96,4 +95,3 @@ public override Uri ResolveUri(Uri baseUri, string relativeUri) } } #endif -#endif diff --git a/src/NUnitFramework/TestBuilder.cs b/src/NUnitFramework/TestBuilder.cs index 68581b998b..3d26482757 100644 --- a/src/NUnitFramework/TestBuilder.cs +++ b/src/NUnitFramework/TestBuilder.cs @@ -22,25 +22,22 @@ // *********************************************************************** using System; -using System.Collections.Generic; using System.Reflection; using System.Threading; -using NUnit.Framework; -#if !NETCOREAPP1_1 using NUnit.Compatibility; -#endif +using NUnit.Framework; using NUnit.Framework.Interfaces; -using NUnit.Framework.Internal.Builders; using NUnit.Framework.Internal; -using NUnit.Framework.Internal.Commands; +using NUnit.Framework.Internal.Builders; using NUnit.Framework.Internal.Execution; +using NUnit.Framework.Internal.Abstractions; namespace NUnit.TestUtilities { /// /// Utility Class used to build and run NUnit tests used as test data /// - public static class TestBuilder + internal static class TestBuilder { #region Build Tests @@ -109,18 +106,20 @@ public static WorkItem CreateWorkItem(Test test) return CreateWorkItem(test, context); } - public static WorkItem CreateWorkItem(Test test, object testObject) + public static WorkItem CreateWorkItem(Test test, object testObject, IDebugger debugger = null) { - var context = new TestExecutionContext(); - context.TestObject = testObject; - context.Dispatcher = new SuperSimpleDispatcher(); + var context = new TestExecutionContext + { + TestObject = testObject, + Dispatcher = new SuperSimpleDispatcher() + }; - return CreateWorkItem(test, context); + return CreateWorkItem(test, context, debugger); } - public static WorkItem CreateWorkItem(Test test, TestExecutionContext context) + public static WorkItem CreateWorkItem(Test test, TestExecutionContext context, IDebugger debugger = null) { - var work = WorkItemBuilder.CreateWorkItem(test, TestFilter.Empty, true); + var work = WorkItemBuilder.CreateWorkItem(test, TestFilter.Empty, debugger ?? new DebuggerProxy(), true); work.InitializeContext(context); return work; @@ -181,9 +180,9 @@ public static ITestResult RunTest(Test test) return RunTest(test, null); } - public static ITestResult RunTest(Test test, object testObject) + public static ITestResult RunTest(Test test, object testObject, IDebugger debugger = null) { - return ExecuteWorkItem(CreateWorkItem(test, testObject)); + return ExecuteWorkItem(CreateWorkItem(test, testObject, debugger ?? new DebuggerProxy())); } public static ITestResult ExecuteWorkItem(WorkItem work) diff --git a/src/NUnitFramework/framework/Api/DefaultTestAssemblyBuilder.cs b/src/NUnitFramework/framework/Api/DefaultTestAssemblyBuilder.cs index 31018e7359..dd1e9c1651 100644 --- a/src/NUnitFramework/framework/Api/DefaultTestAssemblyBuilder.cs +++ b/src/NUnitFramework/framework/Api/DefaultTestAssemblyBuilder.cs @@ -76,11 +76,7 @@ public DefaultTestAssemblyBuilder() /// public ITest Build(Assembly assembly, IDictionary options) { -#if NETSTANDARD1_4 - log.Debug("Loading {0}", assembly.FullName); -#else log.Debug("Loading {0} in AppDomain {1}", assembly.FullName, AppDomain.CurrentDomain.FriendlyName); -#endif string assemblyPath = AssemblyHelper.GetAssemblyPath(assembly); string suiteName = assemblyPath.Equals("") @@ -100,11 +96,7 @@ public ITest Build(Assembly assembly, IDictionary options) /// public ITest Build(string assemblyNameOrPath, IDictionary options) { -#if NETSTANDARD1_4 - log.Debug("Loading {0}", assemblyNameOrPath); -#else log.Debug("Loading {0} in AppDomain {1}", assemblyNameOrPath, AppDomain.CurrentDomain.FriendlyName); -#endif TestSuite testAssembly = null; @@ -266,10 +258,13 @@ private TestSuite BuildTestAssembly(Assembly assembly, string assemblyNameOrPath testAssembly.ApplyAttributesToTest(assembly); -#if !NETSTANDARD1_4 - testAssembly.Properties.Set(PropertyNames.ProcessId, System.Diagnostics.Process.GetCurrentProcess().Id); + try + { + testAssembly.Properties.Set(PropertyNames.ProcessId, System.Diagnostics.Process.GetCurrentProcess().Id); + } + catch (PlatformNotSupportedException) + { } testAssembly.Properties.Set(PropertyNames.AppDomain, AppDomain.CurrentDomain.FriendlyName); -#endif // TODO: Make this an option? Add Option to sort assemblies as well? testAssembly.Sort(); diff --git a/src/NUnitFramework/framework/Api/FrameworkController.cs b/src/NUnitFramework/framework/Api/FrameworkController.cs index 69af26282d..4a58fbfb7e 100644 --- a/src/NUnitFramework/framework/Api/FrameworkController.cs +++ b/src/NUnitFramework/framework/Api/FrameworkController.cs @@ -146,11 +146,7 @@ private void Initialize(string assemblyNameOrPath, IDictionary settings) var workDirectory = Settings.ContainsKey(FrameworkPackageSettings.WorkDirectory) ? (string)Settings[FrameworkPackageSettings.WorkDirectory] : Directory.GetCurrentDirectory(); -#if NETSTANDARD1_4 - var id = DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss"); -#else var id = Process.GetCurrentProcess().Id; -#endif var logName = string.Format(LOG_FILE_FORMAT, id, Path.GetFileName(assemblyNameOrPath)); InternalTrace.Initialize(Path.Combine(workDirectory, logName), traceLevel); } @@ -213,7 +209,8 @@ public string LoadTests() /// The XML result of exploring the tests public string ExploreTests(string filter) { - return Runner.ExploreTests(TestFilter.FromXml(filter)).ToXml(true).OuterXml; + TNode result = Runner.ExploreTests(TestFilter.FromXml(filter)).ToXml(true); + return InsertChildElements(result).OuterXml; } /// @@ -224,13 +221,7 @@ public string ExploreTests(string filter) public string RunTests(string filter) { TNode result = Runner.Run(new TestProgressReporter(null), TestFilter.FromXml(filter)).ToXml(true); - - // Insert elements as first child in reverse order - if (Settings != null) // Some platforms don't have settings - InsertSettingsElement(result, Settings); - InsertEnvironmentElement(result); - - return result.OuterXml; + return InsertChildElements(result).OuterXml; } class ActionCallback : ICallbackEventHandler @@ -264,15 +255,8 @@ public void RaiseCallbackEvent(string report) public string RunTests(Action callback, string filter) { var handler = new ActionCallback(callback); - TNode result = Runner.Run(new TestProgressReporter(handler), TestFilter.FromXml(filter)).ToXml(true); - - // Insert elements as first child in reverse order - if (Settings != null) // Some platforms don't have settings - InsertSettingsElement(result, Settings); - InsertEnvironmentElement(result); - - return result.OuterXml; + return InsertChildElements(result).OuterXml; } /// @@ -347,6 +331,21 @@ private void CountTests(ICallbackEventHandler handler, string filter) handler.RaiseCallbackEvent(CountTests(filter).ToString()); } + /// + /// Inserts the environment and settings elements + /// + /// Target node + /// The updated target node + private TNode InsertChildElements(TNode targetNode) + { + // Insert elements as first child in reverse order + if (Settings != null) // Some platforms don't have settings + InsertSettingsElement(targetNode, Settings); + InsertEnvironmentElement(targetNode); + + return targetNode; + } + /// /// Inserts environment element /// @@ -358,27 +357,17 @@ public static TNode InsertEnvironmentElement(TNode targetNode) targetNode.ChildNodes.Insert(0, env); env.AddAttribute("framework-version", typeof(FrameworkController).GetTypeInfo().Assembly.GetName().Version.ToString()); -#if NETSTANDARD1_4 - env.AddAttribute("clr-version", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription); -#else env.AddAttribute("clr-version", Environment.Version.ToString()); -#endif -#if NETSTANDARD1_4 || NETSTANDARD2_0 +#if NETSTANDARD2_0 env.AddAttribute("os-version", System.Runtime.InteropServices.RuntimeInformation.OSDescription); #else env.AddAttribute("os-version", OSPlatform.CurrentPlatform.ToString()); #endif -#if !NETSTANDARD1_4 env.AddAttribute("platform", Environment.OSVersion.Platform.ToString()); -#endif env.AddAttribute("cwd", Directory.GetCurrentDirectory()); -#if !NETSTANDARD1_4 env.AddAttribute("machine-name", Environment.MachineName); -#endif -#if !NETSTANDARD1_4 env.AddAttribute("user", Environment.UserName); env.AddAttribute("user-domain", Environment.UserDomainName); -#endif env.AddAttribute("culture", CultureInfo.CurrentCulture.ToString()); env.AddAttribute("uiculture", CultureInfo.CurrentUICulture.ToString()); env.AddAttribute("os-architecture", GetProcessorArchitecture()); @@ -405,11 +394,9 @@ public static TNode InsertSettingsElement(TNode targetNode, IDictionary public const string DefaultTimeout = "DefaultTimeout"; + /// + /// A string representing the default thread culture to be used for + /// running tests. String should be a valid BCP-47 culture name. If + /// culture is unset, tests run on the machine's default culture. + /// + public const string DefaultCulture = "DefaultCulture"; + + /// + /// A string representing the default thread UI culture to be used for + /// running tests. String should be a valid BCP-47 culture name. If + /// culture is unset, tests run on the machine's default culture. + /// + public const string DefaultUICulture = "DefaultUICulture"; + /// /// A TextWriter to which the internal trace will be sent. /// diff --git a/src/NUnitFramework/framework/Api/NUnitTestAssemblyRunner.cs b/src/NUnitFramework/framework/Api/NUnitTestAssemblyRunner.cs index ff2b8b2f06..c9cf4698af 100644 --- a/src/NUnitFramework/framework/Api/NUnitTestAssemblyRunner.cs +++ b/src/NUnitFramework/framework/Api/NUnitTestAssemblyRunner.cs @@ -30,7 +30,9 @@ using System.Collections.Generic; using System.IO; using System.Diagnostics; +using System.Globalization; using System.Security; +using NUnit.Framework.Internal.Abstractions; #if NET35 || NET40 || NET45 using System.Windows.Forms; @@ -52,10 +54,8 @@ public class NUnitTestAssemblyRunner : ITestAssemblyRunner private TextWriter _savedOut; private TextWriter _savedErr; -#if PARALLEL // Event Pump private EventPump _pump; -#endif #region Constructors @@ -72,7 +72,6 @@ public NUnitTestAssemblyRunner(ITestAssemblyBuilder builder) #region Properties -#if PARALLEL /// /// Gets the default level of parallel execution (worker threads) /// @@ -80,7 +79,6 @@ public static int DefaultLevelOfParallelism { get { return Math.Max(Environment.ProcessorCount, 2); } } -#endif /// /// The tree of tests that was loaded by the builder @@ -235,7 +233,7 @@ public void RunAsync(ITestListener listener, ITestFilter filter) CreateTestExecutionContext(listener); - TopLevelWorkItem = WorkItemBuilder.CreateWorkItem(LoadedTest, filter, true); + TopLevelWorkItem = WorkItemBuilder.CreateWorkItem(LoadedTest, filter, new DebuggerProxy(), true); TopLevelWorkItem.InitializeContext(Context); TopLevelWorkItem.Completed += OnRunCompleted; @@ -284,7 +282,6 @@ private void StartRun(ITestListener listener) Console.SetOut(new TextCapture(Console.Out)); Console.SetError(new EventListenerTextWriter("Error", Console.Error)); -#if PARALLEL // Queue and pump events, unless settings have SynchronousEvents == false if (!Settings.ContainsKey(FrameworkPackageSettings.SynchronousEvents) || !(bool)Settings[FrameworkPackageSettings.SynchronousEvents]) { @@ -294,7 +291,6 @@ private void StartRun(ITestListener listener) _pump = new EventPump(listener, queue.Events); _pump.Start(); } -#endif if (!System.Diagnostics.Debugger.IsAttached && Settings.ContainsKey(FrameworkPackageSettings.DebugTests) && @@ -337,6 +333,10 @@ private void CreateTestExecutionContext(ITestListener listener) // Apply package settings to the context if (Settings.ContainsKey(FrameworkPackageSettings.DefaultTimeout)) Context.TestCaseTimeout = (int)Settings[FrameworkPackageSettings.DefaultTimeout]; + if (Settings.ContainsKey(FrameworkPackageSettings.DefaultCulture)) + Context.CurrentCulture = new CultureInfo((string)Settings[FrameworkPackageSettings.DefaultCulture], false); + if (Settings.ContainsKey(FrameworkPackageSettings.DefaultUICulture)) + Context.CurrentUICulture = new CultureInfo((string)Settings[FrameworkPackageSettings.DefaultUICulture], false); if (Settings.ContainsKey(FrameworkPackageSettings.StopOnError)) Context.StopOnError = (bool)Settings[FrameworkPackageSettings.StopOnError]; @@ -344,9 +344,7 @@ private void CreateTestExecutionContext(ITestListener listener) // Set the listener - overriding runners may replace this Context.Listener = listener; -#if !PARALLEL - Context.Dispatcher = new MainThreadWorkItemDispatcher(); -#else + int levelOfParallelism = GetLevelOfParallelism(); if (Settings.ContainsKey(FrameworkPackageSettings.RunOnMainThread) && @@ -356,7 +354,6 @@ private void CreateTestExecutionContext(ITestListener listener) Context.Dispatcher = new ParallelWorkItemDispatcher(levelOfParallelism); else Context.Dispatcher = new SimpleWorkItemDispatcher(); -#endif } /// @@ -364,10 +361,8 @@ private void CreateTestExecutionContext(ITestListener listener) /// private void OnRunCompleted(object sender, EventArgs e) { -#if PARALLEL if (_pump != null) _pump.Dispose(); -#endif Console.SetOut(_savedOut); Console.SetError(_savedErr); @@ -388,7 +383,6 @@ private int CountTestCases(ITest test, ITestFilter filter) return count; } -#if PARALLEL private int GetLevelOfParallelism() { return Settings.ContainsKey(FrameworkPackageSettings.NumberOfTestWorkers) @@ -397,7 +391,6 @@ private int GetLevelOfParallelism() ? (int)LoadedTest.Properties.Get(PropertyNames.LevelOfParallelism) : NUnitTestAssemblyRunner.DefaultLevelOfParallelism); } -#endif #if NET35 || NET40 || NET45 // This method invokes members on the 'System.Diagnostics.Process' class and must satisfy the link demand of diff --git a/src/NUnitFramework/framework/Assert.Conditions.cs b/src/NUnitFramework/framework/Assert.Conditions.cs index dd1559990f..391b1e0425 100644 --- a/src/NUnitFramework/framework/Assert.Conditions.cs +++ b/src/NUnitFramework/framework/Assert.Conditions.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections; using NUnit.Framework.Constraints; using System; @@ -32,32 +34,29 @@ public abstract partial class Assert #region True /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void True(bool? condition, string message, params object[] args) + public static void True(bool? condition, string? message, params object?[]? args) { Assert.That(condition, Is.True ,message, args); } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void True(bool condition, string message, params object[] args) + public static void True(bool condition, string? message, params object?[]? args) { Assert.That(condition, Is.True, message, args); } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition public static void True(bool? condition) @@ -66,8 +65,7 @@ public static void True(bool? condition) } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition public static void True(bool condition) @@ -76,32 +74,29 @@ public static void True(bool condition) } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsTrue(bool? condition, string message, params object[] args) + public static void IsTrue(bool? condition, string? message, params object?[]? args) { Assert.That(condition, Is.True ,message, args); } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsTrue(bool condition, string message, params object[] args) + public static void IsTrue(bool condition, string? message, params object?[]? args) { Assert.That(condition, Is.True ,message, args); } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition public static void IsTrue(bool? condition) @@ -110,8 +105,7 @@ public static void IsTrue(bool? condition) } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition public static void IsTrue(bool condition) @@ -124,32 +118,31 @@ public static void IsTrue(bool condition) #region False /// - /// Asserts that a condition is false. If the condition is true the method throws - /// an . + /// Asserts that a condition is false. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The evaluated condition /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void False(bool? condition, string message, params object[] args) + public static void False(bool? condition, string? message, params object?[]? args) { Assert.That(condition, Is.False ,message, args); } /// - /// Asserts that a condition is false. If the condition is true the method throws - /// an . + /// Asserts that a condition is false. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The evaluated condition /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void False(bool condition, string message, params object[] args) + public static void False(bool condition, string? message, params object?[]? args) { Assert.That(condition, Is.False ,message, args); } /// - /// Asserts that a condition is false. If the condition is true the method throws - /// an . + /// Asserts that a condition is false. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition public static void False(bool? condition) @@ -158,8 +151,8 @@ public static void False(bool? condition) } /// - /// Asserts that a condition is false. If the condition is true the method throws - /// an . + /// Asserts that a condition is false. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The evaluated condition public static void False(bool condition) @@ -168,32 +161,32 @@ public static void False(bool condition) } /// - /// Asserts that a condition is false. If the condition is true the method throws - /// an . + /// Asserts that a condition is false. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The evaluated condition /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsFalse(bool? condition, string message, params object[] args) + public static void IsFalse(bool? condition, string? message, params object?[]? args) { Assert.That(condition, Is.False ,message, args); } /// - /// Asserts that a condition is false. If the condition is true the method throws - /// an . + /// Asserts that a condition is false. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The evaluated condition /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsFalse(bool condition, string message, params object[] args) + public static void IsFalse(bool condition, string? message, params object?[]? args) { Assert.That(condition, Is.False ,message, args); } /// - /// Asserts that a condition is false. If the condition is true the method throws - /// an . + /// Asserts that a condition is false. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The evaluated condition public static void IsFalse(bool? condition) @@ -202,8 +195,8 @@ public static void IsFalse(bool? condition) } /// - /// Asserts that a condition is false. If the condition is true the method throws - /// an . + /// Asserts that a condition is false. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The evaluated condition public static void IsFalse(bool condition) @@ -216,49 +209,45 @@ public static void IsFalse(bool condition) #region NotNull /// - /// Verifies that the object that is passed in is not equal to null - /// If the object is null then an - /// is thrown. + /// Verifies that the object that is passed in is not equal to . Returns without throwing an + /// exception when inside a multiple assert block. /// /// The object that is to be tested /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void NotNull(object anObject, string message, params object[] args) + public static void NotNull(object? anObject, string? message, params object?[]? args) { Assert.That(anObject, Is.Not.Null ,message, args); } /// - /// Verifies that the object that is passed in is not equal to null - /// If the object is null then an - /// is thrown. + /// Verifies that the object that is passed in is not equal to . Returns without throwing an + /// exception when inside a multiple assert block. /// /// The object that is to be tested - public static void NotNull(object anObject) + public static void NotNull(object? anObject) { Assert.That(anObject, Is.Not.Null ,null, null); } /// - /// Verifies that the object that is passed in is not equal to null - /// If the object is null then an - /// is thrown. + /// Verifies that the object that is passed in is not equal to . Returns without throwing an + /// exception when inside a multiple assert block. /// /// The object that is to be tested /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsNotNull(object anObject, string message, params object[] args) + public static void IsNotNull(object? anObject, string? message, params object?[]? args) { Assert.That(anObject, Is.Not.Null ,message, args); } /// - /// Verifies that the object that is passed in is not equal to null - /// If the object is null then an - /// is thrown. + /// Verifies that the object that is passed in is not equal to . Returns without throwing an + /// exception when inside a multiple assert block. /// /// The object that is to be tested - public static void IsNotNull(object anObject) + public static void IsNotNull(object? anObject) { Assert.That(anObject, Is.Not.Null ,null, null); } @@ -268,49 +257,45 @@ public static void IsNotNull(object anObject) #region Null /// - /// Verifies that the object that is passed in is equal to null - /// If the object is not null then an - /// is thrown. + /// Verifies that the object that is passed in is equal to . Returns without throwing an + /// exception when inside a multiple assert block. /// /// The object that is to be tested /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Null(object anObject, string message, params object[] args) + public static void Null(object? anObject, string? message, params object?[]? args) { Assert.That(anObject, Is.Null ,message, args); } /// - /// Verifies that the object that is passed in is equal to null - /// If the object is not null then an - /// is thrown. + /// Verifies that the object that is passed in is equal to . Returns without throwing an + /// exception when inside a multiple assert block. /// /// The object that is to be tested - public static void Null(object anObject) + public static void Null(object? anObject) { Assert.That(anObject, Is.Null ,null, null); } /// - /// Verifies that the object that is passed in is equal to null - /// If the object is not null then an - /// is thrown. + /// Verifies that the object that is passed in is equal to . Returns without throwing an + /// exception when inside a multiple assert block. /// /// The object that is to be tested /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsNull(object anObject, string message, params object[] args) + public static void IsNull(object? anObject, string? message, params object?[]? args) { Assert.That(anObject, Is.Null ,message, args); } /// - /// Verifies that the object that is passed in is equal to null - /// If the object is not null then an - /// is thrown. + /// Verifies that the object that is passed in is equal to . Returns without throwing an + /// exception when inside a multiple assert block. /// /// The object that is to be tested - public static void IsNull(object anObject) + public static void IsNull(object? anObject) { Assert.That(anObject, Is.Null ,null, null); } @@ -320,22 +305,20 @@ public static void IsNull(object anObject) #region IsNaN /// - /// Verifies that the double that is passed in is an NaN value. - /// If the object is not NaN then an - /// is thrown. + /// Verifies that the double that is passed in is an NaN. Returns without throwing an + /// exception when inside a multiple assert block. /// /// The value that is to be tested /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsNaN(double aDouble, string message, params object[] args) + public static void IsNaN(double aDouble, string? message, params object?[]? args) { Assert.That(aDouble, Is.NaN ,message, args); } /// - /// Verifies that the double that is passed in is an NaN value. - /// If the object is not NaN then an - /// is thrown. + /// Verifies that the double that is passed in is an NaN value. Returns without throwing an + /// exception when inside a multiple assert block. /// /// The value that is to be tested public static void IsNaN(double aDouble) @@ -344,22 +327,20 @@ public static void IsNaN(double aDouble) } /// - /// Verifies that the double that is passed in is an NaN value. - /// If the object is not NaN then an - /// is thrown. + /// Verifies that the double that is passed in is an NaN value. Returns without throwing an + /// exception when inside a multiple assert block. /// /// The value that is to be tested /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsNaN(double? aDouble, string message, params object[] args) + public static void IsNaN(double? aDouble, string? message, params object?[]? args) { Assert.That(aDouble, Is.NaN ,message, args); } /// - /// Verifies that the double that is passed in is an NaN value. - /// If the object is not NaN then an - /// is thrown. + /// Verifies that the double that is passed in is an NaN value. Returns without throwing an + /// exception when inside a multiple assert block. /// /// The value that is to be tested public static void IsNaN(double? aDouble) @@ -374,21 +355,21 @@ public static void IsNaN(double? aDouble) #region String /// - /// Assert that a string is empty - that is equal to string.Empty + /// Assert that a string is empty. Returns without throwing an exception when inside a multiple assert block. /// /// The string to be tested /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsEmpty(string aString, string message, params object[] args) + public static void IsEmpty(string? aString, string? message, params object?[]? args) { Assert.That(aString, new EmptyStringConstraint() ,message, args); } /// - /// Assert that a string is empty - that is equal to string.Empty + /// Assert that a string is empty. Returns without throwing an exception when inside a multiple assert block. /// /// The string to be tested - public static void IsEmpty(string aString) + public static void IsEmpty(string? aString) { Assert.That(aString, new EmptyStringConstraint() ,null, null); } @@ -398,18 +379,20 @@ public static void IsEmpty(string aString) #region Collection /// - /// Assert that an array, list or other collection is empty + /// Assert that an array, list or other collection is empty. Returns without throwing an exception when inside a + /// multiple assert block. /// /// An array, list or other collection implementing ICollection /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsEmpty(IEnumerable collection, string message, params object[] args) + public static void IsEmpty(IEnumerable collection, string? message, params object?[]? args) { Assert.That(collection, new EmptyCollectionConstraint() ,message, args); } /// - /// Assert that an array, list or other collection is empty + /// Assert that an array, list or other collection is empty. Returns without throwing an exception when inside a + /// multiple assert block. /// /// An array, list or other collection implementing ICollection public static void IsEmpty(IEnumerable collection) @@ -426,21 +409,23 @@ public static void IsEmpty(IEnumerable collection) #region String /// - /// Assert that a string is not empty - that is not equal to string.Empty + /// Assert that a string is not empty. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The string to be tested /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsNotEmpty(string aString, string message, params object[] args) + public static void IsNotEmpty(string? aString, string? message, params object?[]? args) { Assert.That(aString, Is.Not.Empty ,message, args); } /// - /// Assert that a string is not empty - that is not equal to string.Empty + /// Assert that a string is not empty. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The string to be tested - public static void IsNotEmpty(string aString) + public static void IsNotEmpty(string? aString) { Assert.That(aString, Is.Not.Empty ,null, null); } @@ -450,18 +435,20 @@ public static void IsNotEmpty(string aString) #region Collection /// - /// Assert that an array, list or other collection is not empty + /// Assert that an array, list or other collection is not empty. Returns without throwing an exception when + /// inside a multiple assert block. /// /// An array, list or other collection implementing ICollection /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsNotEmpty(IEnumerable collection, string message, params object[] args) + public static void IsNotEmpty(IEnumerable collection, string? message, params object?[]? args) { Assert.That(collection, Is.Not.Empty ,message, args); } /// - /// Assert that an array, list or other collection is not empty + /// Assert that an array, list or other collection is not empty. Returns without throwing an exception when + /// inside a multiple assert block. /// /// An array, list or other collection implementing ICollection public static void IsNotEmpty(IEnumerable collection) @@ -478,7 +465,7 @@ public static void IsNotEmpty(IEnumerable collection) #region Ints /// - /// Asserts that an int is zero. + /// Asserts that an int is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Zero(int actual) @@ -487,12 +474,12 @@ public static void Zero(int actual) } /// - /// Asserts that an int is zero. + /// Asserts that an int is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Zero(int actual, string message, params object[] args) + public static void Zero(int actual, string? message, params object?[]? args) { Assert.That(actual, Is.Zero, message, args); } @@ -502,7 +489,8 @@ public static void Zero(int actual, string message, params object[] args) #region UnsignedInts /// - /// Asserts that an unsigned int is zero. + /// Asserts that an unsigned int is zero. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined [CLSCompliant(false)] @@ -512,13 +500,14 @@ public static void Zero(uint actual) } /// - /// Asserts that an unsigned int is zero. + /// Asserts that an unsigned int is zero. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message [CLSCompliant(false)] - public static void Zero(uint actual, string message, params object[] args) + public static void Zero(uint actual, string? message, params object?[]? args) { Assert.That(actual, Is.Zero, message, args); } @@ -528,7 +517,7 @@ public static void Zero(uint actual, string message, params object[] args) #region Longs /// - /// Asserts that a Long is zero. + /// Asserts that a Long is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Zero(long actual) @@ -537,12 +526,12 @@ public static void Zero(long actual) } /// - /// Asserts that a Long is zero. + /// Asserts that a Long is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Zero(long actual, string message, params object[] args) + public static void Zero(long actual, string? message, params object?[]? args) { Assert.That(actual, Is.Zero, message, args); } @@ -552,7 +541,8 @@ public static void Zero(long actual, string message, params object[] args) #region UnsignedLongs /// - /// Asserts that an unsigned Long is zero. + /// Asserts that an unsigned Long is zero. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined [CLSCompliant(false)] @@ -562,13 +552,14 @@ public static void Zero(ulong actual) } /// - /// Asserts that an unsigned Long is zero. + /// Asserts that an unsigned Long is zero. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message [CLSCompliant(false)] - public static void Zero(ulong actual, string message, params object[] args) + public static void Zero(ulong actual, string? message, params object?[]? args) { Assert.That(actual, Is.Zero, message, args); } @@ -578,7 +569,7 @@ public static void Zero(ulong actual, string message, params object[] args) #region Decimals /// - /// Asserts that a decimal is zero. + /// Asserts that a decimal is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Zero(decimal actual) @@ -587,12 +578,12 @@ public static void Zero(decimal actual) } /// - /// Asserts that a decimal is zero. + /// Asserts that a decimal is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Zero(decimal actual, string message, params object[] args) + public static void Zero(decimal actual, string? message, params object?[]? args) { Assert.That(actual, Is.Zero, message, args); } @@ -602,7 +593,7 @@ public static void Zero(decimal actual, string message, params object[] args) #region Doubles /// - /// Asserts that a double is zero. + /// Asserts that a double is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Zero(double actual) @@ -611,12 +602,12 @@ public static void Zero(double actual) } /// - /// Asserts that a double is zero. + /// Asserts that a double is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Zero(double actual, string message, params object[] args) + public static void Zero(double actual, string? message, params object?[]? args) { Assert.That(actual, Is.Zero, message, args); } @@ -626,7 +617,7 @@ public static void Zero(double actual, string message, params object[] args) #region Floats /// - /// Asserts that a float is zero. + /// Asserts that a float is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Zero(float actual) @@ -635,12 +626,12 @@ public static void Zero(float actual) } /// - /// Asserts that a float is zero. + /// Asserts that a float is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Zero(float actual, string message, params object[] args) + public static void Zero(float actual, string? message, params object?[]? args) { Assert.That(actual, Is.Zero, message, args); } @@ -654,7 +645,7 @@ public static void Zero(float actual, string message, params object[] args) #region Ints /// - /// Asserts that an int is not zero. + /// Asserts that an int is not zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void NotZero(int actual) @@ -663,12 +654,12 @@ public static void NotZero(int actual) } /// - /// Asserts that an int is not zero. + /// Asserts that an int is not zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void NotZero(int actual, string message, params object[] args) + public static void NotZero(int actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.Zero, message, args); } @@ -678,7 +669,8 @@ public static void NotZero(int actual, string message, params object[] args) #region UnsignedInts /// - /// Asserts that an unsigned int is not zero. + /// Asserts that an unsigned int is not zero. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined [CLSCompliant(false)] @@ -688,13 +680,14 @@ public static void NotZero(uint actual) } /// - /// Asserts that an unsigned int is not zero. + /// Asserts that an unsigned int is not zero. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message [CLSCompliant(false)] - public static void NotZero(uint actual, string message, params object[] args) + public static void NotZero(uint actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.Zero, message, args); } @@ -704,7 +697,7 @@ public static void NotZero(uint actual, string message, params object[] args) #region Longs /// - /// Asserts that a Long is not zero. + /// Asserts that a Long is not zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void NotZero(long actual) @@ -713,12 +706,12 @@ public static void NotZero(long actual) } /// - /// Asserts that a Long is not zero. + /// Asserts that a Long is not zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void NotZero(long actual, string message, params object[] args) + public static void NotZero(long actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.Zero, message, args); } @@ -728,7 +721,8 @@ public static void NotZero(long actual, string message, params object[] args) #region UnsignedLongs /// - /// Asserts that an unsigned Long is not zero. + /// Asserts that an unsigned Long is not zero. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined [CLSCompliant(false)] @@ -738,13 +732,14 @@ public static void NotZero(ulong actual) } /// - /// Asserts that an unsigned Long is not zero. + /// Asserts that an unsigned Long is not zero. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message [CLSCompliant(false)] - public static void NotZero(ulong actual, string message, params object[] args) + public static void NotZero(ulong actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.Zero, message, args); } @@ -754,7 +749,7 @@ public static void NotZero(ulong actual, string message, params object[] args) #region Decimals /// - /// Asserts that a decimal is zero. + /// Asserts that a decimal is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void NotZero(decimal actual) @@ -763,12 +758,12 @@ public static void NotZero(decimal actual) } /// - /// Asserts that a decimal is zero. + /// Asserts that a decimal is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void NotZero(decimal actual, string message, params object[] args) + public static void NotZero(decimal actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.Zero, message, args); } @@ -778,7 +773,7 @@ public static void NotZero(decimal actual, string message, params object[] args) #region Doubles /// - /// Asserts that a double is zero. + /// Asserts that a double is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void NotZero(double actual) @@ -787,12 +782,12 @@ public static void NotZero(double actual) } /// - /// Asserts that a double is zero. + /// Asserts that a double is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void NotZero(double actual, string message, params object[] args) + public static void NotZero(double actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.Zero, message, args); } @@ -802,7 +797,7 @@ public static void NotZero(double actual, string message, params object[] args) #region Floats /// - /// Asserts that a float is zero. + /// Asserts that a float is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void NotZero(float actual) @@ -811,12 +806,12 @@ public static void NotZero(float actual) } /// - /// Asserts that a float is zero. + /// Asserts that a float is zero. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void NotZero(float actual, string message, params object[] args) + public static void NotZero(float actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.Zero, message, args); } @@ -830,7 +825,7 @@ public static void NotZero(float actual, string message, params object[] args) #region Ints /// - /// Asserts that an int is positive. + /// Asserts that an int is positive. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Positive(int actual) @@ -839,12 +834,12 @@ public static void Positive(int actual) } /// - /// Asserts that an int is positive. + /// Asserts that an int is positive. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Positive(int actual, string message, params object[] args) + public static void Positive(int actual, string? message, params object?[]? args) { Assert.That(actual, Is.Positive, message, args); } @@ -854,7 +849,8 @@ public static void Positive(int actual, string message, params object[] args) #region UnsignedInts /// - /// Asserts that an unsigned int is positive. + /// Asserts that an unsigned int is positive. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined [CLSCompliant(false)] @@ -864,13 +860,14 @@ public static void Positive(uint actual) } /// - /// Asserts that an unsigned int is positive. + /// Asserts that an unsigned int is positive. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message [CLSCompliant(false)] - public static void Positive(uint actual, string message, params object[] args) + public static void Positive(uint actual, string? message, params object?[]? args) { Assert.That(actual, Is.Positive, message, args); } @@ -880,7 +877,7 @@ public static void Positive(uint actual, string message, params object[] args) #region Longs /// - /// Asserts that a Long is positive. + /// Asserts that a Long is positive. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Positive(long actual) @@ -889,12 +886,12 @@ public static void Positive(long actual) } /// - /// Asserts that a Long is positive. + /// Asserts that a Long is positive. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Positive(long actual, string message, params object[] args) + public static void Positive(long actual, string? message, params object?[]? args) { Assert.That(actual, Is.Positive, message, args); } @@ -904,7 +901,8 @@ public static void Positive(long actual, string message, params object[] args) #region UnsignedLongs /// - /// Asserts that an unsigned Long is positive. + /// Asserts that an unsigned Long is positive. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined [CLSCompliant(false)] @@ -914,13 +912,14 @@ public static void Positive(ulong actual) } /// - /// Asserts that an unsigned Long is positive. + /// Asserts that an unsigned Long is positive. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message [CLSCompliant(false)] - public static void Positive(ulong actual, string message, params object[] args) + public static void Positive(ulong actual, string? message, params object?[]? args) { Assert.That(actual, Is.Positive, message, args); } @@ -930,7 +929,8 @@ public static void Positive(ulong actual, string message, params object[] args) #region Decimals /// - /// Asserts that a decimal is positive. + /// Asserts that a decimal is positive. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined public static void Positive(decimal actual) @@ -939,12 +939,13 @@ public static void Positive(decimal actual) } /// - /// Asserts that a decimal is positive. + /// Asserts that a decimal is positive. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Positive(decimal actual, string message, params object[] args) + public static void Positive(decimal actual, string? message, params object?[]? args) { Assert.That(actual, Is.Positive, message, args); } @@ -954,7 +955,8 @@ public static void Positive(decimal actual, string message, params object[] args #region Doubles /// - /// Asserts that a double is positive. + /// Asserts that a double is positive. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined public static void Positive(double actual) @@ -963,12 +965,12 @@ public static void Positive(double actual) } /// - /// Asserts that a double is positive. + /// Asserts that a double is positive. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Positive(double actual, string message, params object[] args) + public static void Positive(double actual, string? message, params object?[]? args) { Assert.That(actual, Is.Positive, message, args); } @@ -978,7 +980,7 @@ public static void Positive(double actual, string message, params object[] args) #region Floats /// - /// Asserts that a float is positive. + /// Asserts that a float is positive. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Positive(float actual) @@ -987,12 +989,12 @@ public static void Positive(float actual) } /// - /// Asserts that a float is positive. + /// Asserts that a float is positive. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Positive(float actual, string message, params object[] args) + public static void Positive(float actual, string? message, params object?[]? args) { Assert.That(actual, Is.Positive, message, args); } @@ -1006,7 +1008,7 @@ public static void Positive(float actual, string message, params object[] args) #region Ints /// - /// Asserts that an int is negative. + /// Asserts that an int is negative. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Negative(int actual) @@ -1015,12 +1017,12 @@ public static void Negative(int actual) } /// - /// Asserts that an int is negative. + /// Asserts that an int is negative. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Negative(int actual, string message, params object[] args) + public static void Negative(int actual, string? message, params object?[]? args) { Assert.That(actual, Is.Negative, message, args); } @@ -1030,7 +1032,8 @@ public static void Negative(int actual, string message, params object[] args) #region UnsignedInts /// - /// Asserts that an unsigned int is negative. + /// Asserts that an unsigned int is negative. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined [CLSCompliant(false)] @@ -1040,13 +1043,14 @@ public static void Negative(uint actual) } /// - /// Asserts that an unsigned int is negative. + /// Asserts that an unsigned int is negative. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message [CLSCompliant(false)] - public static void Negative(uint actual, string message, params object[] args) + public static void Negative(uint actual, string? message, params object?[]? args) { Assert.That(actual, Is.Negative, message, args); } @@ -1056,7 +1060,7 @@ public static void Negative(uint actual, string message, params object[] args) #region Longs /// - /// Asserts that a Long is negative. + /// Asserts that a Long is negative. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Negative(long actual) @@ -1065,12 +1069,12 @@ public static void Negative(long actual) } /// - /// Asserts that a Long is negative. + /// Asserts that a Long is negative. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Negative(long actual, string message, params object[] args) + public static void Negative(long actual, string? message, params object?[]? args) { Assert.That(actual, Is.Negative, message, args); } @@ -1080,7 +1084,8 @@ public static void Negative(long actual, string message, params object[] args) #region UnsignedLongs /// - /// Asserts that an unsigned Long is negative. + /// Asserts that an unsigned Long is negative. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined [CLSCompliant(false)] @@ -1090,13 +1095,14 @@ public static void Negative(ulong actual) } /// - /// Asserts that an unsigned Long is negative. + /// Asserts that an unsigned Long is negative. Returns without throwing an exception when inside a multiple + /// assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message [CLSCompliant(false)] - public static void Negative(ulong actual, string message, params object[] args) + public static void Negative(ulong actual, string? message, params object?[]? args) { Assert.That(actual, Is.Negative, message, args); } @@ -1106,7 +1112,8 @@ public static void Negative(ulong actual, string message, params object[] args) #region Decimals /// - /// Asserts that a decimal is negative. + /// Asserts that a decimal is negative. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined public static void Negative(decimal actual) @@ -1115,12 +1122,13 @@ public static void Negative(decimal actual) } /// - /// Asserts that a decimal is negative. + /// Asserts that a decimal is negative. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Negative(decimal actual, string message, params object[] args) + public static void Negative(decimal actual, string? message, params object?[]? args) { Assert.That(actual, Is.Negative, message, args); } @@ -1130,7 +1138,8 @@ public static void Negative(decimal actual, string message, params object[] args #region Doubles /// - /// Asserts that a double is negative. + /// Asserts that a double is negative. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined public static void Negative(double actual) @@ -1139,12 +1148,13 @@ public static void Negative(double actual) } /// - /// Asserts that a double is negative. + /// Asserts that a double is negative. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Negative(double actual, string message, params object[] args) + public static void Negative(double actual, string? message, params object?[]? args) { Assert.That(actual, Is.Negative, message, args); } @@ -1154,7 +1164,7 @@ public static void Negative(double actual, string message, params object[] args) #region Floats /// - /// Asserts that a float is negative. + /// Asserts that a float is negative. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined public static void Negative(float actual) @@ -1163,12 +1173,12 @@ public static void Negative(float actual) } /// - /// Asserts that a float is negative. + /// Asserts that a float is negative. Returns without throwing an exception when inside a multiple assert block. /// /// The number to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Negative(float actual, string message, params object[] args) + public static void Negative(float actual, string? message, params object?[]? args) { Assert.That(actual, Is.Negative, message, args); } diff --git a/src/NUnitFramework/framework/Assert.Equality.cs b/src/NUnitFramework/framework/Assert.Equality.cs index f43636252e..800caf296f 100644 --- a/src/NUnitFramework/framework/Assert.Equality.cs +++ b/src/NUnitFramework/framework/Assert.Equality.cs @@ -21,8 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; -using NUnit.Framework.Internal; +#nullable enable namespace NUnit.Framework { @@ -33,67 +32,59 @@ public abstract partial class Assert #region Doubles /// - /// Verifies that two doubles are equal considering a delta. If the - /// expected value is infinity then the delta value is ignored. If - /// they are not equal then an is - /// thrown. + /// Verifies that two doubles are equal considering a delta. If the expected value is infinity then the delta + /// value is ignored. Returns without throwing an exception when inside a multiple assert block. /// /// The expected value /// The actual value - /// The maximum acceptable difference between the - /// the expected and the actual + /// The maximum acceptable difference between the the expected and the actual /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void AreEqual(double expected, double actual, double delta, string message, params object[] args) + public static void AreEqual(double expected, double actual, double delta, string? message, params object?[]? args) { AssertDoublesAreEqual(expected, actual, delta, message, args); } /// - /// Verifies that two doubles are equal considering a delta. If the - /// expected value is infinity then the delta value is ignored. If - /// they are not equal then an is - /// thrown. + /// Verifies that two doubles are equal considering a delta. If the expected value is infinity then the delta + /// value is ignored. Returns without throwing an exception when inside a multiple assert block. /// /// The expected value /// The actual value - /// The maximum acceptable difference between the - /// the expected and the actual + /// The maximum acceptable difference between the the expected and the actual public static void AreEqual(double expected, double actual, double delta) { AssertDoublesAreEqual(expected, actual, delta, null, null); } /// - /// Verifies that two doubles are equal considering a delta. If the - /// expected value is infinity then the delta value is ignored. If - /// they are not equal then an is - /// thrown. + /// Verifies that two doubles are equal considering a delta. If the expected value is infinity then the delta + /// value is ignored. Returns without throwing an exception when inside a multiple assert block. /// /// The expected value /// The actual value - /// The maximum acceptable difference between the - /// the expected and the actual + /// The maximum acceptable difference between the the expected and the actual /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void AreEqual(double expected, double? actual, double delta, string message, params object[] args) + public static void AreEqual(double expected, double? actual, double delta, string? message, params object?[]? args) { - AssertDoublesAreEqual(expected, (double)actual, delta, message, args); + // TODO: https://github.com/nunit/nunit/issues/3449 + // ↓↓↓↓↓↓↓ + AssertDoublesAreEqual(expected, actual!.Value, delta, message, args); } /// - /// Verifies that two doubles are equal considering a delta. If the - /// expected value is infinity then the delta value is ignored. If - /// they are not equal then an is - /// thrown. + /// Verifies that two doubles are equal considering a delta. If the expected value is infinity then the delta + /// value is ignored. Returns without throwing an exception when inside a multiple assert block. /// /// The expected value /// The actual value - /// The maximum acceptable difference between the - /// the expected and the actual + /// The maximum acceptable difference between the the expected and the actual public static void AreEqual(double expected, double? actual, double delta) { - AssertDoublesAreEqual(expected, (double)actual, delta, null, null); + // TODO: https://github.com/nunit/nunit/issues/3449 + // ↓↓↓↓↓↓↓ + AssertDoublesAreEqual(expected, actual!.Value, delta, null, null); } #endregion @@ -101,29 +92,27 @@ public static void AreEqual(double expected, double? actual, double delta) #region Objects /// - /// Verifies that two objects are equal. Two objects are considered - /// equal if both are null, or if both have the same value. NUnit - /// has special semantics for some object types. - /// If they are not equal an is thrown. + /// Verifies that two objects are equal. Two objects are considered equal if both are null, or if both have the + /// same value. NUnit has special semantics for some object types. Returns without throwing an exception when + /// inside a multiple assert block. /// /// The value that is expected /// The actual value /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void AreEqual(object expected, object actual, string message, params object[] args) + public static void AreEqual(object? expected, object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.EqualTo(expected), message, args); } /// - /// Verifies that two objects are equal. Two objects are considered - /// equal if both are null, or if both have the same value. NUnit - /// has special semantics for some object types. - /// If they are not equal an is thrown. + /// Verifies that two objects are equal. Two objects are considered equal if both are null, or if both have the + /// same value. NUnit has special semantics for some object types. Returns without throwing an exception when + /// inside a multiple assert block. /// /// The value that is expected /// The actual value - public static void AreEqual(object expected, object actual) + public static void AreEqual(object? expected, object? actual) { Assert.That(actual, Is.EqualTo(expected), null, null); } @@ -137,29 +126,27 @@ public static void AreEqual(object expected, object actual) #region Objects /// - /// Verifies that two objects are not equal. Two objects are considered - /// equal if both are null, or if both have the same value. NUnit - /// has special semantics for some object types. - /// If they are equal an is thrown. + /// Verifies that two objects are not equal. Two objects are considered equal if both are null, or if both have + /// the same value. NUnit has special semantics for some object types. Returns without throwing an exception + /// when inside a multiple assert block. /// /// The value that is expected /// The actual value /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void AreNotEqual(object expected, object actual, string message, params object[] args) + public static void AreNotEqual(object? expected, object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.EqualTo(expected), message, args); } /// - /// Verifies that two objects are not equal. Two objects are considered - /// equal if both are null, or if both have the same value. NUnit - /// has special semantics for some object types. - /// If they are equal an is thrown. + /// Verifies that two objects are not equal. Two objects are considered equal if both are null, or if both have + /// the same value. NUnit has special semantics for some object types. Returns without throwing an exception + /// when inside a multiple assert block. /// /// The value that is expected /// The actual value - public static void AreNotEqual(object expected, object actual) + public static void AreNotEqual(object? expected, object? actual) { Assert.That(actual, Is.Not.EqualTo(expected), null, null); } @@ -171,25 +158,25 @@ public static void AreNotEqual(object expected, object actual) #region AreSame /// - /// Asserts that two objects refer to the same object. If they - /// are not the same an is thrown. + /// Asserts that two objects refer to the same object. Returns without throwing an exception when inside a + /// multiple assert block. /// /// The expected object /// The actual object /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void AreSame(object expected, object actual, string message, params object[] args) + public static void AreSame(object? expected, object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.SameAs(expected), message, args); } /// - /// Asserts that two objects refer to the same object. If they - /// are not the same an is thrown. + /// Asserts that two objects refer to the same object. Returns without throwing an exception when inside a + /// multiple assert block. /// /// The expected object /// The actual object - public static void AreSame(object expected, object actual) + public static void AreSame(object? expected, object? actual) { Assert.That(actual, Is.SameAs(expected), null, null); } @@ -199,25 +186,25 @@ public static void AreSame(object expected, object actual) #region AreNotSame /// - /// Asserts that two objects do not refer to the same object. If they - /// are the same an is thrown. + /// Asserts that two objects do not refer to the same object. Returns without throwing an exception when inside + /// a multiple assert block. /// /// The expected object /// The actual object /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void AreNotSame(object expected, object actual, string message, params object[] args) + public static void AreNotSame(object? expected, object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.SameAs(expected), message, args); } /// - /// Asserts that two objects do not refer to the same object. If they - /// are the same an is thrown. + /// Asserts that two objects do not refer to the same object. Returns without throwing an exception when inside + /// a multiple assert block. /// /// The expected object /// The actual object - public static void AreNotSame(object expected, object actual) + public static void AreNotSame(object? expected, object? actual) { Assert.That(actual, Is.Not.SameAs(expected), null, null); } @@ -236,7 +223,7 @@ public static void AreNotSame(object expected, object actual) /// the expected and the actual /// The message to display in case of failure /// Array of objects to be used in formatting the message - protected static void AssertDoublesAreEqual(double expected, double actual, double delta, string message, object[] args) + protected static void AssertDoublesAreEqual(double expected, double actual, double delta, string? message, object?[]? args) { if (double.IsNaN(expected) || double.IsInfinity(expected)) Assert.That(actual, Is.EqualTo(expected), message, args); diff --git a/src/NUnitFramework/framework/Assert.Exceptions.Async.cs b/src/NUnitFramework/framework/Assert.Exceptions.Async.cs index d26905e0d7..afe844f913 100644 --- a/src/NUnitFramework/framework/Assert.Exceptions.Async.cs +++ b/src/NUnitFramework/framework/Assert.Exceptions.Async.cs @@ -22,11 +22,13 @@ // *********************************************************************** #if TASK_PARALLEL_LIBRARY_API + +#nullable enable + using System; using NUnit.Framework.Constraints; using NUnit.Framework.Internal; - namespace NUnit.Framework { public partial class Assert @@ -34,15 +36,16 @@ public partial class Assert #region ThrowsAsync /// - /// Verifies that an async delegate throws a particular exception when called. + /// Verifies that an async delegate throws a particular exception when called. The returned exception may be + /// when inside a multiple assert block. /// /// A constraint to be satisfied by the exception /// A TestSnippet delegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static Exception ThrowsAsync(IResolveConstraint expression, AsyncTestDelegate code, string message, params object[] args) + public static Exception? ThrowsAsync(IResolveConstraint expression, AsyncTestDelegate code, string? message, params object?[]? args) { - Exception caughtException = null; + Exception? caughtException = null; try { AsyncToSyncAdapter.Await(code.Invoke); @@ -58,33 +61,36 @@ public static Exception ThrowsAsync(IResolveConstraint expression, AsyncTestDele } /// - /// Verifies that an async delegate throws a particular exception when called. + /// Verifies that an async delegate throws a particular exception when called. The returned exception may be + /// when inside a multiple assert block. /// /// A constraint to be satisfied by the exception /// A TestSnippet delegate - public static Exception ThrowsAsync(IResolveConstraint expression, AsyncTestDelegate code) + public static Exception? ThrowsAsync(IResolveConstraint expression, AsyncTestDelegate code) { return ThrowsAsync(expression, code, string.Empty, null); } /// - /// Verifies that an async delegate throws a particular exception when called. + /// Verifies that an async delegate throws a particular exception when called. The returned exception may be + /// when inside a multiple assert block. /// /// The exception Type expected /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static Exception ThrowsAsync(Type expectedExceptionType, AsyncTestDelegate code, string message, params object[] args) + public static Exception? ThrowsAsync(Type expectedExceptionType, AsyncTestDelegate code, string? message, params object?[]? args) { return ThrowsAsync(new ExceptionTypeConstraint(expectedExceptionType), code, message, args); } /// - /// Verifies that an async delegate throws a particular exception when called. + /// Verifies that an async delegate throws a particular exception when called. The returned exception may be + /// when inside a multiple assert block. /// /// The exception Type expected /// A TestDelegate - public static Exception ThrowsAsync(Type expectedExceptionType, AsyncTestDelegate code) + public static Exception? ThrowsAsync(Type expectedExceptionType, AsyncTestDelegate code) { return ThrowsAsync(new ExceptionTypeConstraint(expectedExceptionType), code, string.Empty, null); } @@ -94,23 +100,25 @@ public static Exception ThrowsAsync(Type expectedExceptionType, AsyncTestDelegat #region ThrowsAsync /// - /// Verifies that an async delegate throws a particular exception when called. + /// Verifies that an async delegate throws a particular exception when called. The returned exception may be + /// when inside a multiple assert block. /// /// Type of the expected exception /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static TActual ThrowsAsync(AsyncTestDelegate code, string message, params object[] args) where TActual : Exception + public static TActual? ThrowsAsync(AsyncTestDelegate code, string? message, params object?[]? args) where TActual : Exception { - return (TActual)ThrowsAsync(typeof (TActual), code, message, args); + return (TActual?)ThrowsAsync(typeof (TActual), code, message, args); } /// - /// Verifies that an async delegate throws a particular exception when called. + /// Verifies that an async delegate throws a particular exception when called. The returned exception may be + /// when inside a multiple assert block. /// /// Type of the expected exception /// A TestDelegate - public static TActual ThrowsAsync(AsyncTestDelegate code) where TActual : Exception + public static TActual? ThrowsAsync(AsyncTestDelegate code) where TActual : Exception { return ThrowsAsync(code, string.Empty, null); } @@ -120,47 +128,47 @@ public static Exception ThrowsAsync(Type expectedExceptionType, AsyncTestDelegat #region CatchAsync /// - /// Verifies that an async delegate throws an exception when called - /// and returns it. + /// Verifies that an async delegate throws an exception when called and returns it. The returned exception may + /// be when inside a multiple assert block. /// /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static Exception CatchAsync(AsyncTestDelegate code, string message, params object[] args) + public static Exception? CatchAsync(AsyncTestDelegate code, string? message, params object?[]? args) { return ThrowsAsync(new InstanceOfTypeConstraint(typeof(Exception)), code, message, args); } /// - /// Verifies that an async delegate throws an exception when called - /// and returns it. + /// Verifies that an async delegate throws an exception when called and returns it. The returned exception may + /// be when inside a multiple assert block. /// /// A TestDelegate - public static Exception CatchAsync(AsyncTestDelegate code) + public static Exception? CatchAsync(AsyncTestDelegate code) { return ThrowsAsync(new InstanceOfTypeConstraint(typeof(Exception)), code); } /// - /// Verifies that an async delegate throws an exception of a certain Type - /// or one derived from it when called and returns it. + /// Verifies that an async delegate throws an exception of a certain Type or one derived from it when called and + /// returns it. The returned exception may be when inside a multiple assert block. /// /// The expected Exception Type /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static Exception CatchAsync(Type expectedExceptionType, AsyncTestDelegate code, string message, params object[] args) + public static Exception? CatchAsync(Type expectedExceptionType, AsyncTestDelegate code, string? message, params object?[]? args) { return ThrowsAsync(new InstanceOfTypeConstraint(expectedExceptionType), code, message, args); } /// - /// Verifies that an async delegate throws an exception of a certain Type - /// or one derived from it when called and returns it. + /// Verifies that an async delegate throws an exception of a certain Type or one derived from it when called and + /// returns it. The returned exception may be when inside a multiple assert block. /// /// The expected Exception Type /// A TestDelegate - public static Exception CatchAsync(Type expectedExceptionType, AsyncTestDelegate code) + public static Exception? CatchAsync(Type expectedExceptionType, AsyncTestDelegate code) { return ThrowsAsync(new InstanceOfTypeConstraint(expectedExceptionType), code); } @@ -170,25 +178,25 @@ public static Exception CatchAsync(Type expectedExceptionType, AsyncTestDelegate #region CatchAsync /// - /// Verifies that an async delegate throws an exception of a certain Type - /// or one derived from it when called and returns it. + /// Verifies that an async delegate throws an exception of a certain Type or one derived from it when called and + /// returns it. The returned exception may be when inside a multiple assert block. /// /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static TActual CatchAsync(AsyncTestDelegate code, string message, params object[] args) where TActual : Exception + public static TActual? CatchAsync(AsyncTestDelegate code, string? message, params object?[]? args) where TActual : Exception { - return (TActual)ThrowsAsync(new InstanceOfTypeConstraint(typeof (TActual)), code, message, args); + return (TActual?)ThrowsAsync(new InstanceOfTypeConstraint(typeof (TActual)), code, message, args); } /// - /// Verifies that an async delegate throws an exception of a certain Type - /// or one derived from it when called and returns it. + /// Verifies that an async delegate throws an exception of a certain Type or one derived from it when called and + /// returns it. The returned exception may be when inside a multiple assert block. /// /// A TestDelegate - public static TActual CatchAsync(AsyncTestDelegate code) where TActual : Exception + public static TActual? CatchAsync(AsyncTestDelegate code) where TActual : Exception { - return (TActual)ThrowsAsync(new InstanceOfTypeConstraint(typeof (TActual)), code); + return (TActual?)ThrowsAsync(new InstanceOfTypeConstraint(typeof (TActual)), code); } #endregion @@ -201,7 +209,7 @@ public static Exception CatchAsync(Type expectedExceptionType, AsyncTestDelegate /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void DoesNotThrowAsync(AsyncTestDelegate code, string message, params object[] args) + public static void DoesNotThrowAsync(AsyncTestDelegate code, string? message, params object?[]? args) { Assert.That(code, new ThrowsNothingConstraint(), message, args); } @@ -217,4 +225,4 @@ public static void DoesNotThrowAsync(AsyncTestDelegate code) #endregion } } -#endif \ No newline at end of file +#endif diff --git a/src/NUnitFramework/framework/Assert.Exceptions.cs b/src/NUnitFramework/framework/Assert.Exceptions.cs index 4540fb3d27..2b30a75b1b 100644 --- a/src/NUnitFramework/framework/Assert.Exceptions.cs +++ b/src/NUnitFramework/framework/Assert.Exceptions.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Constraints; using NUnit.Framework.Internal; @@ -31,15 +33,16 @@ public abstract partial class Assert { #region Throws /// - /// Verifies that a delegate throws a particular exception when called. + /// Verifies that a delegate throws a particular exception when called. The returned exception may be when inside a multiple assert block. /// /// A constraint to be satisfied by the exception /// A TestSnippet delegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static Exception Throws(IResolveConstraint expression, TestDelegate code, string message, params object[] args) + public static Exception? Throws(IResolveConstraint expression, TestDelegate code, string? message, params object?[]? args) { - Exception caughtException = null; + Exception? caughtException = null; // Since TestDelegate returns void, it’s always async void if it’s async at all. Guard.ArgumentNotAsyncVoid(code, nameof(code)); @@ -62,33 +65,36 @@ public static Exception Throws(IResolveConstraint expression, TestDelegate code, } /// - /// Verifies that a delegate throws a particular exception when called. + /// Verifies that a delegate throws a particular exception when called. The returned exception may be when inside a multiple assert block. /// /// A constraint to be satisfied by the exception /// A TestSnippet delegate - public static Exception Throws(IResolveConstraint expression, TestDelegate code) + public static Exception? Throws(IResolveConstraint expression, TestDelegate code) { return Throws(expression, code, string.Empty, null); } /// - /// Verifies that a delegate throws a particular exception when called. + /// Verifies that a delegate throws a particular exception when called. The returned exception may be when inside a multiple assert block. /// /// The exception Type expected /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static Exception Throws(Type expectedExceptionType, TestDelegate code, string message, params object[] args) + public static Exception? Throws(Type expectedExceptionType, TestDelegate code, string? message, params object?[]? args) { return Throws(new ExceptionTypeConstraint(expectedExceptionType), code, message, args); } /// - /// Verifies that a delegate throws a particular exception when called. + /// Verifies that a delegate throws a particular exception when called. The returned exception may be when inside a multiple assert block. /// /// The exception Type expected /// A TestDelegate - public static Exception Throws(Type expectedExceptionType, TestDelegate code) + public static Exception? Throws(Type expectedExceptionType, TestDelegate code) { return Throws(new ExceptionTypeConstraint(expectedExceptionType), code, string.Empty, null); } @@ -98,23 +104,25 @@ public static Exception Throws(Type expectedExceptionType, TestDelegate code) #region Throws /// - /// Verifies that a delegate throws a particular exception when called. + /// Verifies that a delegate throws a particular exception when called. The returned exception may be when inside a multiple assert block. /// /// Type of the expected exception /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static TActual Throws(TestDelegate code, string message, params object[] args) where TActual : Exception + public static TActual? Throws(TestDelegate code, string? message, params object?[]? args) where TActual : Exception { - return (TActual)Throws(typeof(TActual), code, message, args); + return (TActual?)Throws(typeof(TActual), code, message, args); } /// - /// Verifies that a delegate throws a particular exception when called. + /// Verifies that a delegate throws a particular exception when called. The returned exception may be when inside a multiple assert block. /// /// Type of the expected exception /// A TestDelegate - public static TActual Throws(TestDelegate code) where TActual : Exception + public static TActual? Throws(TestDelegate code) where TActual : Exception { return Throws(code, string.Empty, null); } @@ -123,47 +131,47 @@ public static Exception Throws(Type expectedExceptionType, TestDelegate code) #region Catch /// - /// Verifies that a delegate throws an exception when called - /// and returns it. + /// Verifies that a delegate throws an exception when called and returns it. The returned exception may be when inside a multiple assert block. /// /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static Exception Catch(TestDelegate code, string message, params object[] args) + public static Exception? Catch(TestDelegate code, string? message, params object?[]? args) { return Throws(new InstanceOfTypeConstraint(typeof(Exception)), code, message, args); } /// - /// Verifies that a delegate throws an exception when called - /// and returns it. + /// Verifies that a delegate throws an exception when called and returns it. The returned exception may be when inside a multiple assert block. /// /// A TestDelegate - public static Exception Catch(TestDelegate code) + public static Exception? Catch(TestDelegate code) { return Throws(new InstanceOfTypeConstraint(typeof(Exception)), code); } /// - /// Verifies that a delegate throws an exception of a certain Type - /// or one derived from it when called and returns it. + /// Verifies that a delegate throws an exception of a certain Type or one derived from it when called and + /// returns it. The returned exception may be when inside a multiple assert block. /// /// The expected Exception Type /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static Exception Catch(Type expectedExceptionType, TestDelegate code, string message, params object[] args) + public static Exception? Catch(Type expectedExceptionType, TestDelegate code, string? message, params object?[]? args) { return Throws(new InstanceOfTypeConstraint(expectedExceptionType), code, message, args); } /// - /// Verifies that a delegate throws an exception of a certain Type - /// or one derived from it when called and returns it. + /// Verifies that a delegate throws an exception of a certain Type or one derived from it when called and + /// returns it. The returned exception may be when inside a multiple assert block. /// /// The expected Exception Type /// A TestDelegate - public static Exception Catch(Type expectedExceptionType, TestDelegate code) + public static Exception? Catch(Type expectedExceptionType, TestDelegate code) { return Throws(new InstanceOfTypeConstraint(expectedExceptionType), code); } @@ -172,25 +180,25 @@ public static Exception Catch(Type expectedExceptionType, TestDelegate code) #region Catch /// - /// Verifies that a delegate throws an exception of a certain Type - /// or one derived from it when called and returns it. + /// Verifies that a delegate throws an exception of a certain Type or one derived from it when called and + /// returns it. The returned exception may be when inside a multiple assert block. /// /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static TActual Catch(TestDelegate code, string message, params object[] args) where TActual : System.Exception + public static TActual? Catch(TestDelegate code, string? message, params object?[]? args) where TActual : System.Exception { - return (TActual)Throws(new InstanceOfTypeConstraint(typeof(TActual)), code, message, args); + return (TActual?)Throws(new InstanceOfTypeConstraint(typeof(TActual)), code, message, args); } /// - /// Verifies that a delegate throws an exception of a certain Type - /// or one derived from it when called and returns it. + /// Verifies that a delegate throws an exception of a certain Type or one derived from it when called and + /// returns it. The returned exception may be when inside a multiple assert block. /// /// A TestDelegate - public static TActual Catch(TestDelegate code) where TActual : System.Exception + public static TActual? Catch(TestDelegate code) where TActual : System.Exception { - return (TActual)Throws(new InstanceOfTypeConstraint(typeof(TActual)), code); + return (TActual?)Throws(new InstanceOfTypeConstraint(typeof(TActual)), code); } #endregion @@ -203,7 +211,7 @@ public static Exception Catch(Type expectedExceptionType, TestDelegate code) /// A TestDelegate /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void DoesNotThrow(TestDelegate code, string message, params object[] args) + public static void DoesNotThrow(TestDelegate code, string? message, params object?[]? args) { Assert.That(code, new ThrowsNothingConstraint(), message, args); } diff --git a/src/NUnitFramework/framework/Assert.That.cs b/src/NUnitFramework/framework/Assert.That.cs index 795751d4e4..a55f122d42 100644 --- a/src/NUnitFramework/framework/Assert.That.cs +++ b/src/NUnitFramework/framework/Assert.That.cs @@ -21,9 +21,10 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Constraints; -using NUnit.Framework.Internal; namespace NUnit.Framework { @@ -38,20 +39,18 @@ public abstract partial class Assert #region Boolean /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition /// The message to display if the condition is false /// Arguments to be used in formatting the message - public static void That(bool condition, string message, params object[] args) + public static void That(bool condition, string? message, params object?[]? args) { Assert.That(condition, Is.True, message, args); } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition public static void That(bool condition) @@ -60,12 +59,11 @@ public static void That(bool condition) } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// The evaluated condition /// A function to build the message included with the Exception - public static void That(bool condition, Func getExceptionMessage) + public static void That(bool condition, Func getExceptionMessage) { Assert.That(condition, Is.True, getExceptionMessage); } @@ -75,20 +73,18 @@ public static void That(bool condition, Func getExceptionMessage) #region Lambda returning Boolean /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// A lambda that returns a Boolean /// The message to display if the condition is false /// Arguments to be used in formatting the message - public static void That(Func condition, string message, params object[] args) + public static void That(Func condition, string? message, params object?[]? args) { Assert.That(condition.Invoke(), Is.True, message, args); } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// A lambda that returns a Boolean public static void That(Func condition) @@ -97,12 +93,11 @@ public static void That(Func condition) } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. Returns without throwing an exception when inside a multiple assert block. /// /// A lambda that returns a Boolean /// A function to build the message included with the Exception - public static void That(Func condition, Func getExceptionMessage) + public static void That(Func condition, Func getExceptionMessage) { Assert.That(condition.Invoke(), Is.True, getExceptionMessage); } @@ -112,8 +107,7 @@ public static void That(Func condition, Func getExceptionMessage) #region ActualValueDelegate /// - /// Apply a constraint to an actual value, succeeding if the constraint - /// is satisfied and throwing an assertion exception on failure. + /// Apply a constraint to a delegate. Returns without throwing an exception when inside a multiple assert block. /// /// The Type being compared. /// An ActualValueDelegate returning the value to be tested @@ -124,15 +118,14 @@ public static void That(ActualValueDelegate del, IResolveConst } /// - /// Apply a constraint to an actual value, succeeding if the constraint - /// is satisfied and throwing an assertion exception on failure. + /// Apply a constraint to a delegate. Returns without throwing an exception when inside a multiple assert block. /// /// The Type being compared. /// An ActualValueDelegate returning the value to be tested /// A Constraint expression to be applied /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void That(ActualValueDelegate del, IResolveConstraint expr, string message, params object[] args) + public static void That(ActualValueDelegate del, IResolveConstraint expr, string? message, params object?[]? args) { var constraint = expr.Resolve(); @@ -143,8 +136,7 @@ public static void That(ActualValueDelegate del, IResolveConst } /// - /// Apply a constraint to an actual value, succeeding if the constraint - /// is satisfied and throwing an assertion exception on failure. + /// Apply a constraint to a delegate. Returns without throwing an exception when inside a multiple assert block. /// /// The Type being compared. /// An ActualValueDelegate returning the value to be tested @@ -153,7 +145,7 @@ public static void That(ActualValueDelegate del, IResolveConst public static void That( ActualValueDelegate del, IResolveConstraint expr, - Func getExceptionMessage) + Func getExceptionMessage) { var constraint = expr.Resolve(); @@ -168,8 +160,7 @@ public static void That(ActualValueDelegate del, IResolveConst #region TestDelegate /// - /// Asserts that the code represented by a delegate throws an exception - /// that satisfies the constraint provided. + /// Apply a constraint to a delegate. Returns without throwing an exception when inside a multiple assert block. /// /// A TestDelegate to be executed /// A Constraint expression to be applied @@ -179,26 +170,24 @@ public static void That(TestDelegate code, IResolveConstraint constraint) } /// - /// Asserts that the code represented by a delegate throws an exception - /// that satisfies the constraint provided. + /// Apply a constraint to a delegate. Returns without throwing an exception when inside a multiple assert block. /// /// A TestDelegate to be executed /// A Constraint expression to be applied /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void That(TestDelegate code, IResolveConstraint constraint, string message, params object[] args) + public static void That(TestDelegate code, IResolveConstraint constraint, string? message, params object?[]? args) { Assert.That((object)code, constraint, message, args); } /// - /// Asserts that the code represented by a delegate throws an exception - /// that satisfies the constraint provided. + /// Apply a constraint to a delegate. Returns without throwing an exception when inside a multiple assert block. /// /// A TestDelegate to be executed /// A Constraint expression to be applied /// A function to build the message included with the Exception - public static void That(TestDelegate code, IResolveConstraint constraint, Func getExceptionMessage) + public static void That(TestDelegate code, IResolveConstraint constraint, Func getExceptionMessage) { Assert.That((object)code, constraint, getExceptionMessage); } @@ -210,8 +199,8 @@ public static void That(TestDelegate code, IResolveConstraint constraint, Func /// - /// Apply a constraint to an actual value, succeeding if the constraint - /// is satisfied and throwing an assertion exception on failure. + /// Apply a constraint to an actual value. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The Type being compared. /// The actual value to test @@ -222,15 +211,15 @@ public static void That(TActual actual, IResolveConstraint expression) } /// - /// Apply a constraint to an actual value, succeeding if the constraint - /// is satisfied and throwing an assertion exception on failure. + /// Apply a constraint to an actual value. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The Type being compared. /// The actual value to test /// A Constraint expression to be applied /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void That(TActual actual, IResolveConstraint expression, string message, params object[] args) + public static void That(TActual actual, IResolveConstraint expression, string? message, params object?[]? args) { var constraint = expression.Resolve(); @@ -241,8 +230,8 @@ public static void That(TActual actual, IResolveConstraint expression, } /// - /// Apply a constraint to an actual value, succeeding if the constraint - /// is satisfied and throwing an assertion exception on failure. + /// Apply a constraint to an actual value. Returns without throwing an exception when inside a multiple assert + /// block. /// /// The Type being compared. /// The actual value to test @@ -251,7 +240,7 @@ public static void That(TActual actual, IResolveConstraint expression, public static void That( TActual actual, IResolveConstraint expression, - Func getExceptionMessage) + Func getExceptionMessage) { var constraint = expression.Resolve(); @@ -266,33 +255,31 @@ public static void That(TActual actual, IResolveConstraint expression, #region Assert.ByVal /// - /// Apply a constraint to an actual value, succeeding if the constraint - /// is satisfied and throwing an assertion exception on failure. - /// Used as a synonym for That in rare cases where a private setter - /// causes a Visual Basic compilation error. + /// Apply a constraint to an actual value. Returns without throwing an exception when inside a multiple assert + /// block. Used as a synonym for That in rare cases where a private setter causes a Visual Basic compilation + /// error. /// /// The actual value to test /// A Constraint expression to be applied - public static void ByVal(object actual, IResolveConstraint expression) + public static void ByVal(object? actual, IResolveConstraint expression) { Assert.That(actual, expression, null, null); } /// - /// Apply a constraint to an actual value, succeeding if the constraint - /// is satisfied and throwing an assertion exception on failure. - /// Used as a synonym for That in rare cases where a private setter - /// causes a Visual Basic compilation error. + /// Apply a constraint to an actual value. Returns without throwing an exception when inside a multiple assert + /// block. Used as a synonym for That in rare cases where a private setter causes a Visual Basic compilation + /// error. /// /// - /// This method is provided for use by VB developers needing to test - /// the value of properties with private setters. + /// This method is provided for use by VB developers needing to test the value of properties with private + /// setters. /// /// The actual value to test /// A Constraint expression to be applied /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void ByVal(object actual, IResolveConstraint expression, string message, params object[] args) + public static void ByVal(object? actual, IResolveConstraint expression, string? message, params object?[]? args) { Assert.That(actual, expression, message, args); } diff --git a/src/NUnitFramework/framework/Assert.Types.cs b/src/NUnitFramework/framework/Assert.Types.cs index d319a078b4..b77689a29f 100644 --- a/src/NUnitFramework/framework/Assert.Types.cs +++ b/src/NUnitFramework/framework/Assert.Types.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; namespace NUnit.Framework @@ -30,23 +32,25 @@ public abstract partial class Assert #region IsAssignableFrom /// - /// Asserts that an object may be assigned a value of a given Type. + /// Asserts that an object may be assigned a value of a given Type. Returns without throwing an exception when + /// inside a multiple assert block. /// /// The expected Type. /// The object under examination /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsAssignableFrom(Type expected, object actual, string message, params object[] args) + public static void IsAssignableFrom(Type expected, object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.AssignableFrom(expected) ,message, args); } /// - /// Asserts that an object may be assigned a value of a given Type. + /// Asserts that an object may be assigned a value of a given Type. Returns without throwing an exception when + /// inside a multiple assert block. /// /// The expected Type. /// The object under examination - public static void IsAssignableFrom(Type expected, object actual) + public static void IsAssignableFrom(Type expected, object? actual) { Assert.That(actual, Is.AssignableFrom(expected) ,null, null); } @@ -56,23 +60,25 @@ public static void IsAssignableFrom(Type expected, object actual) #region IsAssignableFrom /// - /// Asserts that an object may be assigned a value of a given Type. + /// Asserts that an object may be assigned a value of a given Type. Returns without throwing an exception when + /// inside a multiple assert block. /// /// The expected Type. /// The object under examination /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsAssignableFrom(object actual, string message, params object[] args) + public static void IsAssignableFrom(object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.AssignableFrom(typeof(TExpected)) ,message, args); } /// - /// Asserts that an object may be assigned a value of a given Type. + /// Asserts that an object may be assigned a value of a given Type. Returns without throwing an exception when + /// inside a multiple assert block. /// /// The expected Type. /// The object under examination - public static void IsAssignableFrom(object actual) + public static void IsAssignableFrom(object? actual) { Assert.That(actual, Is.AssignableFrom(typeof(TExpected)) ,null, null); } @@ -82,23 +88,25 @@ public static void IsAssignableFrom(object actual) #region IsNotAssignableFrom /// - /// Asserts that an object may not be assigned a value of a given Type. + /// Asserts that an object may not be assigned a value of a given Type. Returns without throwing an exception + /// when inside a multiple assert block. /// /// The expected Type. /// The object under examination /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsNotAssignableFrom(Type expected, object actual, string message, params object[] args) + public static void IsNotAssignableFrom(Type expected, object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.AssignableFrom(expected) ,message, args); } /// - /// Asserts that an object may not be assigned a value of a given Type. + /// Asserts that an object may not be assigned a value of a given Type. Returns without throwing an exception + /// when inside a multiple assert block. /// /// The expected Type. /// The object under examination - public static void IsNotAssignableFrom(Type expected, object actual) + public static void IsNotAssignableFrom(Type expected, object? actual) { Assert.That(actual, Is.Not.AssignableFrom(expected) ,null, null); } @@ -108,23 +116,25 @@ public static void IsNotAssignableFrom(Type expected, object actual) #region IsNotAssignableFrom /// - /// Asserts that an object may not be assigned a value of a given Type. + /// Asserts that an object may not be assigned a value of a given Type. Returns without throwing an exception + /// when inside a multiple assert block. /// /// The expected Type. /// The object under examination /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsNotAssignableFrom(object actual, string message, params object[] args) + public static void IsNotAssignableFrom(object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.AssignableFrom(typeof(TExpected)) ,message, args); } /// - /// Asserts that an object may not be assigned a value of a given Type. + /// Asserts that an object may not be assigned a value of a given Type. Returns without throwing an exception + /// when inside a multiple assert block. /// /// The expected Type. /// The object under examination - public static void IsNotAssignableFrom(object actual) + public static void IsNotAssignableFrom(object? actual) { Assert.That(actual, Is.Not.AssignableFrom(typeof(TExpected)) ,null, null); } @@ -134,23 +144,25 @@ public static void IsNotAssignableFrom(object actual) #region IsInstanceOf /// - /// Asserts that an object is an instance of a given type. + /// Asserts that an object is an instance of a given type. Returns without throwing an exception when inside a + /// multiple assert block. /// /// The expected Type /// The object being examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsInstanceOf(Type expected, object actual, string message, params object[] args) + public static void IsInstanceOf(Type expected, object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.InstanceOf(expected) ,message, args); } /// - /// Asserts that an object is an instance of a given type. + /// Asserts that an object is an instance of a given type. Returns without throwing an exception when inside a + /// multiple assert block. /// /// The expected Type /// The object being examined - public static void IsInstanceOf(Type expected, object actual) + public static void IsInstanceOf(Type expected, object? actual) { Assert.That(actual, Is.InstanceOf(expected) ,null, null); } @@ -160,23 +172,25 @@ public static void IsInstanceOf(Type expected, object actual) #region IsInstanceOf /// - /// Asserts that an object is an instance of a given type. + /// Asserts that an object is an instance of a given type. Returns without throwing an exception when inside a + /// multiple assert block. /// /// The expected Type /// The object being examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsInstanceOf(object actual, string message, params object[] args) + public static void IsInstanceOf(object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.InstanceOf(typeof(TExpected)) ,message, args); } /// - /// Asserts that an object is an instance of a given type. + /// Asserts that an object is an instance of a given type. Returns without throwing an exception when inside a + /// multiple assert block. /// /// The expected Type /// The object being examined - public static void IsInstanceOf(object actual) + public static void IsInstanceOf(object? actual) { Assert.That(actual, Is.InstanceOf(typeof(TExpected)) ,null, null); } @@ -186,23 +200,25 @@ public static void IsInstanceOf(object actual) #region IsNotInstanceOf /// - /// Asserts that an object is not an instance of a given type. + /// Asserts that an object is not an instance of a given type. Returns without throwing an exception when inside + /// a multiple assert block. /// /// The expected Type /// The object being examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsNotInstanceOf(Type expected, object actual, string message, params object[] args) + public static void IsNotInstanceOf(Type expected, object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.InstanceOf(expected) ,message, args); } /// - /// Asserts that an object is not an instance of a given type. + /// Asserts that an object is not an instance of a given type. Returns without throwing an exception when inside + /// a multiple assert block. /// /// The expected Type /// The object being examined - public static void IsNotInstanceOf(Type expected, object actual) + public static void IsNotInstanceOf(Type expected, object? actual) { Assert.That(actual, Is.Not.InstanceOf(expected) ,null, null); } @@ -212,23 +228,25 @@ public static void IsNotInstanceOf(Type expected, object actual) #region IsNotInstanceOf /// - /// Asserts that an object is not an instance of a given type. + /// Asserts that an object is not an instance of a given type. Returns without throwing an exception when inside + /// a multiple assert block. /// /// The expected Type /// The object being examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void IsNotInstanceOf(object actual, string message, params object[] args) + public static void IsNotInstanceOf(object? actual, string? message, params object?[]? args) { Assert.That(actual, Is.Not.InstanceOf(typeof(TExpected)) ,message, args); } /// - /// Asserts that an object is not an instance of a given type. + /// Asserts that an object is not an instance of a given type. Returns without throwing an exception when inside + /// a multiple assert block. /// /// The expected Type /// The object being examined - public static void IsNotInstanceOf(object actual) + public static void IsNotInstanceOf(object? actual) { Assert.That(actual, Is.Not.InstanceOf(typeof(TExpected)) ,null, null); } diff --git a/src/NUnitFramework/framework/Assert.cs b/src/NUnitFramework/framework/Assert.cs index dbc325cc26..cf095362fa 100644 --- a/src/NUnitFramework/framework/Assert.cs +++ b/src/NUnitFramework/framework/Assert.cs @@ -21,11 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; -using System.Collections.Generic; using System.ComponentModel; -using System.Linq; using NUnit.Framework.Constraints; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -92,7 +92,7 @@ public static new void ReferenceEquals(object a, object b) /// /// The message to initialize the with. /// Arguments to be used in formatting the message - static public void Pass(string message, params object[] args) + static public void Pass(string? message, params object?[]? args) { if (message == null) message = string.Empty; else if (args != null && args.Length > 0) @@ -111,7 +111,7 @@ static public void Pass(string message, params object[] args) /// of success returned to NUnit. /// /// The message to initialize the with. - static public void Pass(string message) + static public void Pass(string? message) { Assert.Pass(message, null); } @@ -131,12 +131,12 @@ static public void Pass() #region Fail /// - /// Throws an with the message and arguments - /// that are passed in. This is used by the other Assert functions. + /// Marks the test as failed with the message and arguments that are passed in. Returns without throwing an + /// exception when inside a multiple assert block. /// /// The message to initialize the with. /// Arguments to be used in formatting the message - static public void Fail(string message, params object[] args) + static public void Fail(string? message, params object?[]? args) { if (message == null) message = string.Empty; else if (args != null && args.Length > 0) @@ -146,18 +146,17 @@ static public void Fail(string message, params object[] args) } /// - /// Throws an with the message that is - /// passed in. This is used by the other Assert functions. + /// Marks the test as failed with the message that is passed in. Returns without throwing an exception when + /// inside a multiple assert block. /// /// The message to initialize the with. - static public void Fail(string message) + static public void Fail(string? message) { Assert.Fail(message, null); } /// - /// Throws an . - /// This is used by the other Assert functions. + /// Marks the test as failed. Returns without throwing an exception when inside a multiple assert block. /// static public void Fail() { @@ -173,7 +172,7 @@ static public void Fail() /// /// The message to display. /// Arguments to be used in formatting the message - static public void Warn(string message, params object[] args) + static public void Warn(string? message, params object?[]? args) { if (message == null) message = string.Empty; else if (args != null && args.Length > 0) @@ -186,7 +185,7 @@ static public void Warn(string message, params object[] args) /// Issues a warning using the message provided. /// /// The message to display. - static public void Warn(string message) + static public void Warn(string? message) { IssueWarning(message); } @@ -201,7 +200,7 @@ static public void Warn(string message) /// /// The message to initialize the with. /// Arguments to be used in formatting the message - static public void Ignore(string message, params object[] args) + static public void Ignore(string? message, params object?[]? args) { if (message == null) message = string.Empty; else if (args != null && args.Length > 0) @@ -219,7 +218,7 @@ static public void Ignore(string message, params object[] args) /// passed in. This causes the test to be reported as ignored. /// /// The message to initialize the with. - static public void Ignore(string message) + static public void Ignore(string? message) { Assert.Ignore(message, null); } @@ -243,7 +242,7 @@ static public void Ignore() /// /// The message to initialize the with. /// Arguments to be used in formatting the message - static public void Inconclusive(string message, params object[] args) + static public void Inconclusive(string? message, params object?[]? args) { if (message == null) message = string.Empty; else if (args != null && args.Length > 0) @@ -261,7 +260,7 @@ static public void Inconclusive(string message, params object[] args) /// passed in. This causes the test to be reported as inconclusive. /// /// The message to initialize the with. - static public void Inconclusive(string message) + static public void Inconclusive(string? message) { Assert.Inconclusive(message, null); } @@ -280,23 +279,25 @@ static public void Inconclusive() #region Contains /// - /// Asserts that an object is contained in a collection. + /// Asserts that an object is contained in a collection. Returns without throwing an exception when inside a + /// multiple assert block. /// /// The expected object /// The collection to be examined /// The message to display in case of failure /// Array of objects to be used in formatting the message - public static void Contains(object expected, ICollection actual, string message, params object[] args) + public static void Contains(object? expected, ICollection? actual, string? message, params object?[]? args) { Assert.That(actual, new SomeItemsConstraint(new EqualConstraint(expected)) ,message, args); } /// - /// Asserts that an object is contained in a collection. + /// Asserts that an object is contained in a collection. Returns without throwing an exception when inside a + /// multiple assert block. /// /// The expected object /// The collection to be examined - public static void Contains(object expected, ICollection actual) + public static void Contains(object? expected, ICollection? actual) { Assert.That(actual, new SomeItemsConstraint(new EqualConstraint(expected)) ,null, null); } @@ -369,12 +370,12 @@ public static void Multiple(AsyncTestDelegate testDelegate) #region Helper Methods - private static void ReportFailure(ConstraintResult result, string message) + private static void ReportFailure(ConstraintResult result, string? message) { ReportFailure(result, message, null); } - private static void ReportFailure(ConstraintResult result, string message, params object[] args) + private static void ReportFailure(ConstraintResult result, string? message, params object?[]? args) { MessageWriter writer = new TextMessageWriter(message, args); result.WriteMessageTo(writer); @@ -382,7 +383,7 @@ private static void ReportFailure(ConstraintResult result, string message, param ReportFailure(writer.ToString()); } - private static void ReportFailure(string message) + private static void ReportFailure(string? message) { // Record the failure in an element var result = TestExecutionContext.CurrentContext.CurrentResult; @@ -394,7 +395,7 @@ private static void ReportFailure(string message) throw new AssertionException(result.Message); } - private static void IssueWarning(string message) + private static void IssueWarning(string? message) { var result = TestExecutionContext.CurrentContext.CurrentResult; result.RecordAssertion(AssertionStatus.Warning, message, GetStackTrace()); @@ -404,7 +405,29 @@ private static void IssueWarning(string message) private static readonly StackFilter SystemEnvironmentFilter = new StackFilter(@" System\.Environment\."); private static string GetStackTrace() => - StackFilter.DefaultFilter.Filter(SystemEnvironmentFilter.Filter(Environment.StackTrace)); + StackFilter.DefaultFilter.Filter(SystemEnvironmentFilter.Filter(GetEnvironmentStackTraceWithoutThrowing())); + + /// + /// If throws, returns "SomeException was thrown by the + /// Environment.StackTrace property." See also . + /// +#if !NET35 + // https://github.com/dotnet/coreclr/issues/19698 is also currently present in .NET Framework 4.7 and 4.8. A + // race condition between threads reading the same PDB file to obtain file and line info for a stack trace + // results in AccessViolationException when the stack trace is accessed even indirectly e.g. Exception.ToString. + [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] +#endif + private static string GetEnvironmentStackTraceWithoutThrowing() + { + try + { + return Environment.StackTrace; + } + catch (Exception ex) + { + return ex.GetType().Name + " was thrown by the Environment.StackTrace property."; + } + } private static void IncrementAssertCount() { diff --git a/src/NUnitFramework/framework/AssertionHelper.cs b/src/NUnitFramework/framework/AssertionHelper.cs index 6b292e7e85..d4890a169c 100644 --- a/src/NUnitFramework/framework/AssertionHelper.cs +++ b/src/NUnitFramework/framework/AssertionHelper.cs @@ -140,7 +140,8 @@ static public void Expect(TActual actual, IResolveConstraint expression /// Returns a ListMapper based on a collection. /// /// The original collection - /// + [Obsolete("The ListMapper class has been deprecated and will be removed in a future release. " + + "Please use the extension method System.Linq.Enumerable.Select instead.")] public ListMapper Map( ICollection original ) { return new ListMapper( original ); @@ -422,8 +423,6 @@ public UniqueItemsConstraint Unique #endregion -#if SERIALIZATION - /// /// Returns a constraint that tests whether an object graph is serializable in binary format. /// @@ -440,8 +439,6 @@ public XmlSerializableConstraint XmlSerializable get { return new XmlSerializableConstraint(); } } -#endif - #region EqualTo /// diff --git a/src/NUnitFramework/framework/Assume.cs b/src/NUnitFramework/framework/Assume.cs index 0966eecb07..f6224bf081 100644 --- a/src/NUnitFramework/framework/Assume.cs +++ b/src/NUnitFramework/framework/Assume.cs @@ -21,8 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using NUnit.Framework.Constraints; using NUnit.Framework.Internal; @@ -91,7 +94,7 @@ public static void That(ActualValueDelegate del, IResolveConst /// A Constraint expression to be applied /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void That(ActualValueDelegate del, IResolveConstraint expr, string message, params object[] args) + public static void That(ActualValueDelegate del, IResolveConstraint expr, string? message, params object?[]? args) { CheckMultipleAssertLevel(); @@ -102,7 +105,7 @@ public static void That(ActualValueDelegate del, IResolveConst ReportFailure(result, message, args); } - private static void ReportFailure(ConstraintResult result, string message, object[] args) + private static void ReportFailure(ConstraintResult result, string? message, object?[]? args) { MessageWriter writer = new TextMessageWriter(message, args); result.WriteMessageTo(writer); @@ -120,7 +123,7 @@ private static void ReportFailure(ConstraintResult result, string message, objec public static void That( ActualValueDelegate del, IResolveConstraint expr, - Func getExceptionMessage) + Func getExceptionMessage) { CheckMultipleAssertLevel(); @@ -138,34 +141,34 @@ private static void ReportFailure(ConstraintResult result, string message, objec #region Boolean /// - /// Asserts that a condition is true. If the condition is false the method throws + /// Asserts that a condition is true. If the condition is false, the method throws /// an . /// /// The evaluated condition /// The message to display if the condition is false /// Arguments to be used in formatting the message - public static void That(bool condition, string message, params object[] args) + public static void That([DoesNotReturnIf(false)] bool condition, string? message, params object?[]? args) { Assume.That(condition, Is.True, message, args); } /// - /// Asserts that a condition is true. If the condition is false the + /// Asserts that a condition is true. If the condition is false, the /// method throws an . /// /// The evaluated condition - public static void That(bool condition) + public static void That([DoesNotReturnIf(false)] bool condition) { Assume.That(condition, Is.True, null, null); } /// - /// Asserts that a condition is true. If the condition is false the method throws + /// Asserts that a condition is true. If the condition is false, the method throws /// an . /// /// The evaluated condition /// A function to build the message included with the Exception - public static void That(bool condition, Func getExceptionMessage) + public static void That([DoesNotReturnIf(false)] bool condition, Func getExceptionMessage) { Assume.That(condition, Is.True, getExceptionMessage); } @@ -175,19 +178,19 @@ public static void That(bool condition, Func getExceptionMessage) #region Lambda returning Boolean /// - /// Asserts that a condition is true. If the condition is false the method throws + /// Asserts that a condition is true. If the condition is false, the method throws /// an . /// /// A lambda that returns a Boolean /// The message to display if the condition is false /// Arguments to be used in formatting the message - public static void That(Func condition, string message, params object[] args) + public static void That(Func condition, string? message, params object?[]? args) { Assume.That(condition.Invoke(), Is.True, message, args); } /// - /// Asserts that a condition is true. If the condition is false the method throws + /// Asserts that a condition is true. If the condition is false, the method throws /// an . /// /// A lambda that returns a Boolean @@ -197,12 +200,12 @@ public static void That(Func condition) } /// - /// Asserts that a condition is true. If the condition is false the method throws + /// Asserts that a condition is true. If the condition is false, the method throws /// an . /// /// A lambda that returns a Boolean /// A function to build the message included with the Exception - public static void That(Func condition, Func getExceptionMessage) + public static void That(Func condition, Func getExceptionMessage) { Assume.That(condition.Invoke(), Is.True, getExceptionMessage); } @@ -249,7 +252,7 @@ public static void That(TActual actual, IResolveConstraint expression) /// A Constraint expression to be applied /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void That(TActual actual, IResolveConstraint expression, string message, params object[] args) + public static void That(TActual actual, IResolveConstraint expression, string? message, params object?[]? args) { CheckMultipleAssertLevel(); @@ -275,7 +278,7 @@ public static void That(TActual actual, IResolveConstraint expression, public static void That( TActual actual, IResolveConstraint expression, - Func getExceptionMessage) + Func getExceptionMessage) { CheckMultipleAssertLevel(); diff --git a/src/NUnitFramework/framework/Attributes/ApartmentAttribute.cs b/src/NUnitFramework/framework/Attributes/ApartmentAttribute.cs index 6afe5e7c4f..d7147923e8 100644 --- a/src/NUnitFramework/framework/Attributes/ApartmentAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/ApartmentAttribute.cs @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if APARTMENT_STATE +#nullable enable + using System; using System.Threading; using NUnit.Framework.Internal; @@ -46,4 +47,3 @@ public ApartmentAttribute(ApartmentState apartmentState) } } } -#endif diff --git a/src/NUnitFramework/framework/Attributes/AuthorAttribute.cs b/src/NUnitFramework/framework/Attributes/AuthorAttribute.cs index 04ac276f9c..a4a111aab9 100644 --- a/src/NUnitFramework/framework/Attributes/AuthorAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/AuthorAttribute.cs @@ -1,4 +1,4 @@ -// ********************************************************************************** +// ********************************************************************************** // The MIT License (MIT) // // Copyright (c) 2014 Charlie Poole, Rob Prouse @@ -22,14 +22,11 @@ // // ********************************************************************************** - -#region Using Directives +#nullable enable using System; using NUnit.Framework.Internal; -#endregion - namespace NUnit.Framework { /// diff --git a/src/NUnitFramework/framework/Attributes/CategoryAttribute.cs b/src/NUnitFramework/framework/Attributes/CategoryAttribute.cs index 05ebeebf9c..7435fbeb99 100644 --- a/src/NUnitFramework/framework/Attributes/CategoryAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/CategoryAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; diff --git a/src/NUnitFramework/framework/Attributes/CombinatorialAttribute.cs b/src/NUnitFramework/framework/Attributes/CombinatorialAttribute.cs index ea377a84bc..ec76b58c5f 100644 --- a/src/NUnitFramework/framework/Attributes/CombinatorialAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/CombinatorialAttribute.cs @@ -21,9 +21,9 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -using System.Collections; -using NUnit.Framework.Interfaces; using NUnit.Framework.Internal.Builders; namespace NUnit.Framework diff --git a/src/NUnitFramework/framework/Attributes/CombiningStrategyAttribute.cs b/src/NUnitFramework/framework/Attributes/CombiningStrategyAttribute.cs index a6b1f69774..95b428c90c 100644 --- a/src/NUnitFramework/framework/Attributes/CombiningStrategyAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/CombiningStrategyAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Collections.Generic; @@ -74,7 +76,7 @@ protected CombiningStrategyAttribute(object strategy, object provider) /// /// The MethodInfo for which tests are to be constructed. /// The suite to which the tests will be added. - public IEnumerable BuildFrom(IMethodInfo method, Test suite) + public IEnumerable BuildFrom(IMethodInfo method, Test? suite) { List tests = new List(); diff --git a/src/NUnitFramework/framework/Attributes/CultureAttribute.cs b/src/NUnitFramework/framework/Attributes/CultureAttribute.cs index e969231799..5bfe91e755 100644 --- a/src/NUnitFramework/framework/Attributes/CultureAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/CultureAttribute.cs @@ -21,7 +21,10 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -47,7 +50,7 @@ public class CultureAttribute : IncludeExcludeAttribute, IApplyToTest /// Constructor taking one or more cultures /// /// Comma-delimited list of cultures - public CultureAttribute(string cultures) : base(cultures) { } + public CultureAttribute(string? cultures) : base(cultures) { } #region IApplyToTest members @@ -57,10 +60,13 @@ public class CultureAttribute : IncludeExcludeAttribute, IApplyToTest /// The test to modify public void ApplyToTest(Test test) { - if (test.RunState != RunState.NotRunnable && !IsCultureSupported()) + if (test.RunState != RunState.NotRunnable && !IsCultureSupported(out var reason)) { test.RunState = RunState.Skipped; - test.Properties.Set(PropertyNames.SkipReason, Reason); + + // Discards the existing user-specified reason, if any. + Reason = reason; + test.Properties.Set(PropertyNames.SkipReason, reason); } } @@ -71,20 +77,21 @@ public void ApplyToTest(Test test) /// based on the properties of this attribute. /// /// True, if the current culture is supported - private bool IsCultureSupported() + private bool IsCultureSupported([NotNullWhen(false)] out string? reason) { if (Include != null && !cultureDetector.IsCultureSupported(Include)) { - Reason = string.Format("Only supported under culture {0}", Include); + reason = string.Format("Only supported under culture {0}", Include); return false; } if (Exclude != null && cultureDetector.IsCultureSupported(Exclude)) { - Reason = string.Format("Not supported under culture {0}", Exclude); + reason = string.Format("Not supported under culture {0}", Exclude); return false; } + reason = null; return true; } diff --git a/src/NUnitFramework/framework/Attributes/DatapointAttribute.cs b/src/NUnitFramework/framework/Attributes/DatapointAttribute.cs index e161c3bc6d..066e083818 100644 --- a/src/NUnitFramework/framework/Attributes/DatapointAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/DatapointAttribute.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2009 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; namespace NUnit.Framework diff --git a/src/NUnitFramework/framework/Attributes/DatapointSourceAttribute.cs b/src/NUnitFramework/framework/Attributes/DatapointSourceAttribute.cs index b470b3e14e..4ec0829bfe 100644 --- a/src/NUnitFramework/framework/Attributes/DatapointSourceAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/DatapointSourceAttribute.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2009 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; diff --git a/src/NUnitFramework/framework/Attributes/DatapointsAttribute.cs b/src/NUnitFramework/framework/Attributes/DatapointsAttribute.cs index ccbc6bc739..489e5d4b96 100644 --- a/src/NUnitFramework/framework/Attributes/DatapointsAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/DatapointsAttribute.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2009 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; diff --git a/src/NUnitFramework/framework/Attributes/DefaultFloatingPointToleranceAttribute.cs b/src/NUnitFramework/framework/Attributes/DefaultFloatingPointToleranceAttribute.cs index ca083dc95a..7a3dae5bfa 100644 --- a/src/NUnitFramework/framework/Attributes/DefaultFloatingPointToleranceAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/DefaultFloatingPointToleranceAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Constraints; using NUnit.Framework.Interfaces; diff --git a/src/NUnitFramework/framework/Attributes/DescriptionAttribute.cs b/src/NUnitFramework/framework/Attributes/DescriptionAttribute.cs index 4749583f96..4e938b7c86 100644 --- a/src/NUnitFramework/framework/Attributes/DescriptionAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/DescriptionAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Internal; diff --git a/src/NUnitFramework/framework/Attributes/ExplicitAttribute.cs b/src/NUnitFramework/framework/Attributes/ExplicitAttribute.cs index 60e3583e22..f2444b77d1 100644 --- a/src/NUnitFramework/framework/Attributes/ExplicitAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/ExplicitAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -35,7 +37,7 @@ namespace NUnit.Framework [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method|AttributeTargets.Assembly, AllowMultiple=false, Inherited=false)] public class ExplicitAttribute : NUnitAttribute, IApplyToTest { - private readonly string _reason; + private readonly string? _reason; /// /// Default constructor @@ -48,7 +50,7 @@ public ExplicitAttribute() /// Constructor with a reason /// /// The reason test is marked explicit - public ExplicitAttribute(string reason) + public ExplicitAttribute(string? reason) { _reason = reason; } diff --git a/src/NUnitFramework/framework/Attributes/FixtureLifeCycleAttribute.cs b/src/NUnitFramework/framework/Attributes/FixtureLifeCycleAttribute.cs new file mode 100644 index 0000000000..544afc0514 --- /dev/null +++ b/src/NUnitFramework/framework/Attributes/FixtureLifeCycleAttribute.cs @@ -0,0 +1,64 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; + +namespace NUnit.Framework +{ + /// + /// Specify the life cycle of a Fixture + /// + [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Class, AllowMultiple = false, Inherited = true)] + public sealed class FixtureLifeCycleAttribute : NUnitAttribute, IApplyToTest + { + /// + /// Construct a FixtureLifeCycleAttribute with a specified . + /// + public FixtureLifeCycleAttribute(LifeCycle lifeCycle) + { + LifeCycle = lifeCycle; + } + + /// + /// Defines the life cycle for this test fixture or assembly. + /// + public LifeCycle LifeCycle { get; } + + /// + /// Overridden to set a TestFixture's . + /// + public void ApplyToTest(Test test) + { + var testFixture = test as TestFixture; + if (testFixture != null) + { + testFixture.LifeCycle = LifeCycle; + } + } + } +} diff --git a/src/NUnitFramework/framework/Attributes/IgnoreAttribute.cs b/src/NUnitFramework/framework/Attributes/IgnoreAttribute.cs index 3b55c7a504..431a56c09f 100644 --- a/src/NUnitFramework/framework/Attributes/IgnoreAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/IgnoreAttribute.cs @@ -21,10 +21,13 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Globalization; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; +using NUnit.Framework.Internal.Extensions; namespace NUnit.Framework { @@ -36,7 +39,7 @@ public class IgnoreAttribute : NUnitAttribute, IApplyToTest { private readonly string _reason; private DateTime? _untilDate; - private string _until; + private string? _until; /// /// Constructs the attribute giving a reason for ignoring the test @@ -58,7 +61,7 @@ public IgnoreAttribute(string reason) /// property set which will appear in the test results. /// /// The string does not contain a valid string representation of a date and time. - public string Until + public string? Until { get { return _until; } set @@ -83,8 +86,7 @@ public void ApplyToTest(Test test) if (_untilDate.Value > DateTime.Now) { test.RunState = RunState.Ignored; - string reason = string.Format("Ignoring until {0}. {1}", _untilDate.Value.ToString("u"), _reason); - test.Properties.Set(PropertyNames.SkipReason, reason); + test.Properties.AddIgnoreUntilReason(_untilDate.Value, _reason); } test.Properties.Set(PropertyNames.IgnoreUntilDate, _untilDate.Value.ToString("u") ); diff --git a/src/NUnitFramework/framework/Attributes/IncludeExcludeAttribute.cs b/src/NUnitFramework/framework/Attributes/IncludeExcludeAttribute.cs index 19682cc276..7c170c835d 100644 --- a/src/NUnitFramework/framework/Attributes/IncludeExcludeAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/IncludeExcludeAttribute.cs @@ -21,7 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; +#nullable enable namespace NUnit.Framework { @@ -31,9 +31,9 @@ namespace NUnit.Framework /// public abstract class IncludeExcludeAttribute : NUnitAttribute { - private string include; - private string exclude; - private string reason; + private string? include; + private string? exclude; + private string? reason; /// /// Constructor with no included items specified, for use @@ -45,7 +45,7 @@ public abstract class IncludeExcludeAttribute : NUnitAttribute /// Constructor taking one or more included items /// /// Comma-delimited list of included items - public IncludeExcludeAttribute( string include ) + public IncludeExcludeAttribute(string? include) { this.include = include; } @@ -55,7 +55,7 @@ public IncludeExcludeAttribute( string include ) /// a test to run. Multiple items may be given, /// separated by a comma. /// - public string Include + public string? Include { get { return this.include; } set { include = value; } @@ -65,7 +65,7 @@ public string Include /// Name of the item to be excluded. Multiple items /// may be given, separated by a comma. /// - public string Exclude + public string? Exclude { get { return this.exclude; } set { this.exclude = value; } @@ -74,7 +74,7 @@ public string Exclude /// /// The reason for including or excluding the test /// - public string Reason + public string? Reason { get { return reason; } set { reason = value; } diff --git a/src/NUnitFramework/framework/Attributes/LevelOfParallelismAttribute.cs b/src/NUnitFramework/framework/Attributes/LevelOfParallelismAttribute.cs index d19c4225ee..b35374d162 100644 --- a/src/NUnitFramework/framework/Attributes/LevelOfParallelismAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/LevelOfParallelismAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; namespace NUnit.Framework diff --git a/src/NUnitFramework/framework/Attributes/LifeCycle.cs b/src/NUnitFramework/framework/Attributes/LifeCycle.cs new file mode 100644 index 0000000000..0049582603 --- /dev/null +++ b/src/NUnitFramework/framework/Attributes/LifeCycle.cs @@ -0,0 +1,41 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +namespace NUnit.Framework +{ + /// + /// Specifies the life cycle for a test fixture. + /// + public enum LifeCycle + { + /// + /// A single instance is created and shared for all test cases. + /// + SingleInstance, + + /// + /// A new instance is created for each test case. + /// + InstancePerTestCase + } +} diff --git a/src/NUnitFramework/framework/Attributes/MaxTimeAttribute.cs b/src/NUnitFramework/framework/Attributes/MaxTimeAttribute.cs index 51b11f7517..a1d5090d02 100644 --- a/src/NUnitFramework/framework/Attributes/MaxTimeAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/MaxTimeAttribute.cs @@ -21,10 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -using System.Collections.Generic; -using NUnit.Framework.Internal.Commands; using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal.Commands; namespace NUnit.Framework { diff --git a/src/NUnitFramework/framework/Attributes/NUnitAttribute.cs b/src/NUnitFramework/framework/Attributes/NUnitAttribute.cs index 371f1a1cfc..304cdcc937 100644 --- a/src/NUnitFramework/framework/Attributes/NUnitAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/NUnitAttribute.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2010 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; namespace NUnit.Framework diff --git a/src/NUnitFramework/framework/Attributes/NonParallelizableAttribute.cs b/src/NUnitFramework/framework/Attributes/NonParallelizableAttribute.cs index 249863a154..b4d305de03 100644 --- a/src/NUnitFramework/framework/Attributes/NonParallelizableAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/NonParallelizableAttribute.cs @@ -21,10 +21,9 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -using NUnit.Framework.Interfaces; -using NUnit.Framework.Internal; -using NUnit.Framework.Internal.Execution; namespace NUnit.Framework { diff --git a/src/NUnitFramework/framework/Attributes/NonTestAssemblyAttribute.cs b/src/NUnitFramework/framework/Attributes/NonTestAssemblyAttribute.cs index 340a5d09ac..5c12673bdb 100644 --- a/src/NUnitFramework/framework/Attributes/NonTestAssemblyAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/NonTestAssemblyAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.ComponentModel; diff --git a/src/NUnitFramework/framework/Attributes/OneTimeSetUpAttribute.cs b/src/NUnitFramework/framework/Attributes/OneTimeSetUpAttribute.cs index 5d31d76278..a25fc061f5 100644 --- a/src/NUnitFramework/framework/Attributes/OneTimeSetUpAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/OneTimeSetUpAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + namespace NUnit.Framework { using System; diff --git a/src/NUnitFramework/framework/Attributes/OneTimeTearDownAttribute.cs b/src/NUnitFramework/framework/Attributes/OneTimeTearDownAttribute.cs index b7fa0a0aa2..84ec6077a2 100644 --- a/src/NUnitFramework/framework/Attributes/OneTimeTearDownAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/OneTimeTearDownAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + namespace NUnit.Framework { using System; diff --git a/src/NUnitFramework/framework/Attributes/OrderAttribute.cs b/src/NUnitFramework/framework/Attributes/OrderAttribute.cs index 4ca01648c0..c729cd0564 100644 --- a/src/NUnitFramework/framework/Attributes/OrderAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/OrderAttribute.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2016 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -57,4 +59,4 @@ public void ApplyToTest(Test test) test.Properties.Set(PropertyNames.Order, Order); } } - } \ No newline at end of file + } diff --git a/src/NUnitFramework/framework/Attributes/PairwiseAttribute.cs b/src/NUnitFramework/framework/Attributes/PairwiseAttribute.cs index de86515555..4ef6f862ef 100644 --- a/src/NUnitFramework/framework/Attributes/PairwiseAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/PairwiseAttribute.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2008 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,9 +21,9 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -using System.Collections; -using NUnit.Framework.Interfaces; using NUnit.Framework.Internal.Builders; namespace NUnit.Framework diff --git a/src/NUnitFramework/framework/Attributes/ParallelizableAttribute.cs b/src/NUnitFramework/framework/Attributes/ParallelizableAttribute.cs index 673b257b16..b8c03a7208 100644 --- a/src/NUnitFramework/framework/Attributes/ParallelizableAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/ParallelizableAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; diff --git a/src/NUnitFramework/framework/Attributes/PlatformAttribute.cs b/src/NUnitFramework/framework/Attributes/PlatformAttribute.cs index 891c8667d7..265f6a1b6c 100644 --- a/src/NUnitFramework/framework/Attributes/PlatformAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/PlatformAttribute.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PLATFORM_DETECTION +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -46,7 +47,7 @@ public class PlatformAttribute : IncludeExcludeAttribute, IApplyToTest /// Constructor taking one or more platforms /// /// Comma-delimited list of platforms - public PlatformAttribute(string platforms) : base(platforms) { } + public PlatformAttribute(string? platforms) : base(platforms) { } #region IApplyToTest members @@ -82,4 +83,3 @@ public void ApplyToTest(Test test) #endregion } } -#endif diff --git a/src/NUnitFramework/framework/Attributes/PropertyAttribute.cs b/src/NUnitFramework/framework/Attributes/PropertyAttribute.cs index a9318b9941..8bcf57f4a5 100644 --- a/src/NUnitFramework/framework/Attributes/PropertyAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/PropertyAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; diff --git a/src/NUnitFramework/framework/Attributes/RandomAttribute.cs b/src/NUnitFramework/framework/Attributes/RandomAttribute.cs index 8f493dcca1..c34a3dfe31 100644 --- a/src/NUnitFramework/framework/Attributes/RandomAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/RandomAttribute.cs @@ -21,9 +21,12 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; using NUnit.Compatibility; @@ -38,7 +41,7 @@ namespace NUnit.Framework [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)] public class RandomAttribute : NUnitAttribute, IParameterDataSource { - private RandomDataSource _source; + private RandomDataSource? _source; private readonly int _count; /// @@ -59,7 +62,7 @@ public RandomAttribute(int count) } /// - /// Construct a set of ints within a specified range + /// Generates values within a specified range. /// public RandomAttribute(int min, int max, int count) { @@ -67,7 +70,7 @@ public RandomAttribute(int min, int max, int count) } /// - /// Construct a set of unsigned ints within a specified range + /// Generates values within a specified range. /// [CLSCompliant(false)] public RandomAttribute(uint min, uint max, int count) @@ -76,7 +79,7 @@ public RandomAttribute(uint min, uint max, int count) } /// - /// Construct a set of longs within a specified range + /// Generates values within a specified range. /// public RandomAttribute(long min, long max, int count) { @@ -84,7 +87,7 @@ public RandomAttribute(long min, long max, int count) } /// - /// Construct a set of unsigned longs within a specified range + /// Generates values within a specified range. /// [CLSCompliant(false)] public RandomAttribute(ulong min, ulong max, int count) @@ -93,7 +96,7 @@ public RandomAttribute(ulong min, ulong max, int count) } /// - /// Construct a set of shorts within a specified range + /// Generates values within a specified range. /// public RandomAttribute(short min, short max, int count) { @@ -101,7 +104,7 @@ public RandomAttribute(short min, short max, int count) } /// - /// Construct a set of unsigned shorts within a specified range + /// Generates values within a specified range. /// [CLSCompliant(false)] public RandomAttribute(ushort min, ushort max, int count) @@ -110,7 +113,7 @@ public RandomAttribute(ushort min, ushort max, int count) } /// - /// Construct a set of doubles within a specified range + /// Generates values within a specified range. /// public RandomAttribute(double min, double max, int count) { @@ -118,7 +121,7 @@ public RandomAttribute(double min, double max, int count) } /// - /// Construct a set of floats within a specified range + /// Generates values within a specified range. /// public RandomAttribute(float min, float max, int count) { @@ -126,7 +129,7 @@ public RandomAttribute(float min, float max, int count) } /// - /// Construct a set of bytes within a specified range + /// Generates values within a specified range. /// public RandomAttribute(byte min, byte max, int count) { @@ -134,7 +137,7 @@ public RandomAttribute(byte min, byte max, int count) } /// - /// Construct a set of sbytes within a specified range + /// Generates values within a specified range. /// [CLSCompliant(false)] public RandomAttribute(sbyte min, sbyte max, int count) @@ -219,6 +222,8 @@ private bool WeConvert(Type sourceType, Type targetType) abstract class RandomDataSource : IParameterDataSource { + protected RandomDataSource(Type dataType) => DataType = dataType; + public Type DataType { get; protected set; } public bool Distinct { get; set; } @@ -227,40 +232,40 @@ abstract class RandomDataSource : IParameterDataSource abstract class RandomDataSource : RandomDataSource { + [AllowNull, MaybeNull] private readonly T _min; + + [AllowNull, MaybeNull] private readonly T _max; + private readonly int _count; private readonly bool _inRange; private readonly List previousValues = new List(); - protected Randomizer Randomizer; - - protected RandomDataSource(int count) + protected RandomDataSource(int count) : base(typeof(T)) { + _min = default; + _max = default; _count = count; _inRange = false; - - DataType = typeof(T); } - protected RandomDataSource(T min, T max, int count) + protected RandomDataSource(T min, T max, int count) : base(typeof(T)) { _min = min; _max = max; _count = count; _inRange = true; - - DataType = typeof(T); } public override IEnumerable GetData(IParameterInfo parameter) { //Guard.ArgumentValid(parameter.ParameterType == typeof(T), "Parameter type must be " + typeof(T).Name, "parameter"); - Randomizer = Randomizer.GetRandomizer(parameter.ParameterInfo); + var randomizer = Randomizer.GetRandomizer(parameter.ParameterInfo); - Guard.OperationValid(!(Distinct && _inRange && !CanBeDistinct(_min, _max, _count)), $"The range of values is [{_min}, {_max}[ and the random value count is {_count} so the values cannot be distinct."); + Guard.OperationValid(!(Distinct && _inRange && !CanBeDistinct(_min!, _max!, _count)), $"The range of values is [{_min}, {_max}[ and the random value count is {_count} so the values cannot be distinct."); for (int i = 0; i < _count; i++) @@ -272,8 +277,8 @@ public override IEnumerable GetData(IParameterInfo parameter) do { next = _inRange - ? GetNext(_min, _max) - : GetNext(); + ? GetNext(randomizer, _min!, _max!) + : GetNext(randomizer); } while (previousValues.Contains(next)); previousValues.Add(next); @@ -282,13 +287,13 @@ public override IEnumerable GetData(IParameterInfo parameter) } else yield return _inRange - ? GetNext(_min, _max) - : GetNext(); + ? GetNext(randomizer, _min!, _max!) + : GetNext(randomizer); } } - protected abstract T GetNext(); - protected abstract T GetNext(T min, T max); + protected abstract T GetNext(Randomizer randomizer); + protected abstract T GetNext(Randomizer randomizer, T min, T max); protected abstract bool CanBeDistinct(T min, T max, int count); } @@ -300,7 +305,7 @@ class RandomDataConverter : RandomDataSource { readonly IParameterDataSource _source; - public RandomDataConverter(IParameterDataSource source) + public RandomDataConverter(RandomDataSource source) : base(source.DataType) { _source = source; } @@ -345,14 +350,14 @@ class IntDataSource : RandomDataSource public IntDataSource(int min, int max, int count) : base(min, max, count) { } - protected override int GetNext() + protected override int GetNext(Randomizer randomizer) { - return Randomizer.Next(); + return randomizer.Next(); } - protected override int GetNext(int min, int max) + protected override int GetNext(Randomizer randomizer, int min, int max) { - return Randomizer.Next(min, max); + return randomizer.Next(min, max); } protected override bool CanBeDistinct(int min, int max, int count) @@ -371,14 +376,14 @@ class UIntDataSource : RandomDataSource public UIntDataSource(uint min, uint max, int count) : base(min, max, count) { } - protected override uint GetNext() + protected override uint GetNext(Randomizer randomizer) { - return Randomizer.NextUInt(); + return randomizer.NextUInt(); } - protected override uint GetNext(uint min, uint max) + protected override uint GetNext(Randomizer randomizer, uint min, uint max) { - return Randomizer.NextUInt(min, max); + return randomizer.NextUInt(min, max); } protected override bool CanBeDistinct(uint min, uint max, int count) @@ -397,14 +402,14 @@ class LongDataSource : RandomDataSource public LongDataSource(long min, long max, int count) : base(min, max, count) { } - protected override long GetNext() + protected override long GetNext(Randomizer randomizer) { - return Randomizer.NextLong(); + return randomizer.NextLong(); } - protected override long GetNext(long min, long max) + protected override long GetNext(Randomizer randomizer, long min, long max) { - return Randomizer.NextLong(min, max); + return randomizer.NextLong(min, max); } protected override bool CanBeDistinct(long min, long max, int count) @@ -423,14 +428,14 @@ class ULongDataSource : RandomDataSource public ULongDataSource(ulong min, ulong max, int count) : base(min, max, count) { } - protected override ulong GetNext() + protected override ulong GetNext(Randomizer randomizer) { - return Randomizer.NextULong(); + return randomizer.NextULong(); } - protected override ulong GetNext(ulong min, ulong max) + protected override ulong GetNext(Randomizer randomizer, ulong min, ulong max) { - return Randomizer.NextULong(min, max); + return randomizer.NextULong(min, max); } protected override bool CanBeDistinct(ulong min, ulong max, int count) @@ -449,14 +454,14 @@ class ShortDataSource : RandomDataSource public ShortDataSource(short min, short max, int count) : base(min, max, count) { } - protected override short GetNext() + protected override short GetNext(Randomizer randomizer) { - return Randomizer.NextShort(); + return randomizer.NextShort(); } - protected override short GetNext(short min, short max) + protected override short GetNext(Randomizer randomizer, short min, short max) { - return Randomizer.NextShort(min, max); + return randomizer.NextShort(min, max); } protected override bool CanBeDistinct(short min, short max, int count) @@ -475,14 +480,14 @@ class UShortDataSource : RandomDataSource public UShortDataSource(ushort min, ushort max, int count) : base(min, max, count) { } - protected override ushort GetNext() + protected override ushort GetNext(Randomizer randomizer) { - return Randomizer.NextUShort(); + return randomizer.NextUShort(); } - protected override ushort GetNext(ushort min, ushort max) + protected override ushort GetNext(Randomizer randomizer, ushort min, ushort max) { - return Randomizer.NextUShort(min, max); + return randomizer.NextUShort(min, max); } protected override bool CanBeDistinct(ushort min, ushort max, int count) @@ -501,14 +506,14 @@ class DoubleDataSource : RandomDataSource public DoubleDataSource(double min, double max, int count) : base(min, max, count) { } - protected override double GetNext() + protected override double GetNext(Randomizer randomizer) { - return Randomizer.NextDouble(); + return randomizer.NextDouble(); } - protected override double GetNext(double min, double max) + protected override double GetNext(Randomizer randomizer, double min, double max) { - return Randomizer.NextDouble(min, max); + return randomizer.NextDouble(min, max); } protected override bool CanBeDistinct(double min, double max, int count) @@ -527,14 +532,14 @@ class FloatDataSource : RandomDataSource public FloatDataSource(float min, float max, int count) : base(min, max, count) { } - protected override float GetNext() + protected override float GetNext(Randomizer randomizer) { - return Randomizer.NextFloat(); + return randomizer.NextFloat(); } - protected override float GetNext(float min, float max) + protected override float GetNext(Randomizer randomizer, float min, float max) { - return Randomizer.NextFloat(min, max); + return randomizer.NextFloat(min, max); } protected override bool CanBeDistinct(float min, float max, int count) @@ -553,14 +558,14 @@ class ByteDataSource : RandomDataSource public ByteDataSource(byte min, byte max, int count) : base(min, max, count) { } - protected override byte GetNext() + protected override byte GetNext(Randomizer randomizer) { - return Randomizer.NextByte(); + return randomizer.NextByte(); } - protected override byte GetNext(byte min, byte max) + protected override byte GetNext(Randomizer randomizer, byte min, byte max) { - return Randomizer.NextByte(min, max); + return randomizer.NextByte(min, max); } protected override bool CanBeDistinct(byte min, byte max, int count) @@ -579,14 +584,14 @@ class SByteDataSource : RandomDataSource public SByteDataSource(sbyte min, sbyte max, int count) : base(min, max, count) { } - protected override sbyte GetNext() + protected override sbyte GetNext(Randomizer randomizer) { - return Randomizer.NextSByte(); + return randomizer.NextSByte(); } - protected override sbyte GetNext(sbyte min, sbyte max) + protected override sbyte GetNext(Randomizer randomizer, sbyte min, sbyte max) { - return Randomizer.NextSByte(min, max); + return randomizer.NextSByte(min, max); } protected override bool CanBeDistinct(sbyte min, sbyte max, int count) @@ -605,10 +610,9 @@ class EnumDataSource : RandomDataSource private readonly List previousValues = new List(); - public EnumDataSource(int count) + public EnumDataSource(int count) : base(typeof(Enum)) { _count = count; - DataType = typeof(Enum); } public override IEnumerable GetData(IParameterInfo parameter) @@ -647,23 +651,18 @@ public override IEnumerable GetData(IParameterInfo parameter) #region DecimalDataSource - // Currently, Randomizer doesn't implement methods for decimal - // so we use random Ulongs and convert them. This doesn't cover - // the full range of decimal, so it's temporary. class DecimalDataSource : RandomDataSource { public DecimalDataSource(int count) : base(count) { } - public DecimalDataSource(decimal min, decimal max, int count) : base(min, max, count) { } - - protected override decimal GetNext() + protected override decimal GetNext(Randomizer randomizer) { - return Randomizer.NextDecimal(); + return randomizer.NextDecimal(); } - protected override decimal GetNext(decimal min, decimal max) + protected override decimal GetNext(Randomizer randomizer, decimal min, decimal max) { - return Randomizer.NextDecimal(min, max); + return randomizer.NextDecimal(min, max); } protected override bool CanBeDistinct(decimal min, decimal max, int count) diff --git a/src/NUnitFramework/framework/Attributes/RangeAttribute.cs b/src/NUnitFramework/framework/Attributes/RangeAttribute.cs index 895011e471..51dec9f348 100644 --- a/src/NUnitFramework/framework/Attributes/RangeAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/RangeAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Reflection; @@ -43,12 +45,12 @@ public class RangeAttribute : NUnitAttribute, IParameterDataSource #region Ints /// - /// Construct a range of ints using default step of 1 + /// Constructs a range of values using the default step of 1. /// public RangeAttribute(int from, int to) : this(from, to, from > to ? -1 : 1) { } /// - /// Construct a range of ints specifying the step size + /// Constructs a range of values with the specified step size. /// public RangeAttribute(int from, int to, int step) { @@ -65,13 +67,13 @@ public RangeAttribute(int from, int to, int step) #region Unsigned Ints /// - /// Construct a range of unsigned ints using default step of 1 + /// Constructs a range of values using the default step of 1. /// [CLSCompliant(false)] public RangeAttribute(uint from, uint to) : this(from, to, 1u) { } /// - /// Construct a range of unsigned ints specifying the step size + /// Constructs a range of values with the specified step size. /// [CLSCompliant(false)] public RangeAttribute(uint from, uint to, uint step) @@ -89,12 +91,12 @@ public RangeAttribute(uint from, uint to, uint step) #region Longs /// - /// Construct a range of longs using a default step of 1 + /// Constructs a range of values using a default step of 1. /// public RangeAttribute(long from, long to) : this(from, to, from > to ? -1L : 1L) { } /// - /// Construct a range of longs + /// Constructs a range of values with the specified step size. /// public RangeAttribute(long from, long to, long step) { @@ -111,13 +113,13 @@ public RangeAttribute(long from, long to, long step) #region Unsigned Longs /// - /// Construct a range of unsigned longs using default step of 1 + /// Constructs a range of values using the default step of 1. /// [CLSCompliant(false)] public RangeAttribute(ulong from, ulong to) : this(from, to, 1ul) { } /// - /// Construct a range of unsigned longs specifying the step size + /// Constructs a range of values with the specified step size. /// [CLSCompliant(false)] public RangeAttribute(ulong from, ulong to, ulong step) @@ -135,7 +137,7 @@ public RangeAttribute(ulong from, ulong to, ulong step) #region Doubles /// - /// Construct a range of doubles + /// Constructs a range of values with the specified step size. /// public RangeAttribute(double from, double to, double step) { @@ -152,7 +154,7 @@ public RangeAttribute(double from, double to, double step) #region Floats /// - /// Construct a range of floats + /// Constructs a range of values with the specified step size. /// public RangeAttribute(float from, float to, float step) { @@ -177,8 +179,7 @@ public IEnumerable GetData(IParameterInfo parameter) var valueGenerator = ValueGenerator.Create(parameter.ParameterType); - ValueGenerator.Step step; - if (!valueGenerator.TryCreateStep(_step, out step)) + if (!valueGenerator.TryCreateStep(_step, out var step)) { // ValueGenerator.CreateStep has the responsibility to enable Byte values to be incremented // by the Int32 value -1. Or perhaps in the future, DateTime values to be incremented by a TimeSpan. @@ -188,8 +189,7 @@ public IEnumerable GetData(IParameterInfo parameter) // that are only of a different type due to IL limitations or NUnit smoothing over overload differences. // See the XML docs for the ParamAttributeTypeConversions class. - object stepValueToRequire; - if (!ParamAttributeTypeConversions.TryConvert(_step, parameter.ParameterType, out stepValueToRequire)) + if (!ParamAttributeTypeConversions.TryConvert(_step, parameter.ParameterType, out var stepValueToRequire)) { // This will cause CreateStep to throw the same exception as it would throw if TryConvert // succeeded but the value generator still didn’t recognize the step value. diff --git a/src/NUnitFramework/framework/Attributes/RepeatAttribute.cs b/src/NUnitFramework/framework/Attributes/RepeatAttribute.cs index dadee4c7e2..8230cccad9 100644 --- a/src/NUnitFramework/framework/Attributes/RepeatAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/RepeatAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + // TODO: Rework this // RepeatAttribute should either // 1) Apply at load time to create the exact number of tests, or @@ -102,6 +104,8 @@ public override TestResult Execute(TestExecutionContext context) // TODO: We may want to change this so that all iterations are run if (context.CurrentResult.ResultState != ResultState.Success) break; + + context.CurrentRepeatCount++; } return context.CurrentResult; diff --git a/src/NUnitFramework/framework/Attributes/RequiresThreadAttribute.cs b/src/NUnitFramework/framework/Attributes/RequiresThreadAttribute.cs index 4d7b12c27d..30f9b8bc64 100644 --- a/src/NUnitFramework/framework/Attributes/RequiresThreadAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/RequiresThreadAttribute.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL +#nullable enable + using System; using System.Threading; using NUnit.Framework.Interfaces; @@ -41,7 +42,6 @@ public class RequiresThreadAttribute : PropertyAttribute, IApplyToTest public RequiresThreadAttribute() : base(true) { } -#if APARTMENT_STATE /// /// Construct a RequiresThreadAttribute, specifying the apartment /// @@ -52,7 +52,6 @@ public RequiresThreadAttribute(ApartmentState apartment) this.Properties.Add(PropertyNames.ApartmentState, apartment); } -#endif void IApplyToTest.ApplyToTest(Test test) { @@ -61,4 +60,3 @@ void IApplyToTest.ApplyToTest(Test test) } } } -#endif diff --git a/src/NUnitFramework/framework/Attributes/RetryAttribute.cs b/src/NUnitFramework/framework/Attributes/RetryAttribute.cs index a1670c3e52..85320e8335 100644 --- a/src/NUnitFramework/framework/Attributes/RetryAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/RetryAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; diff --git a/src/NUnitFramework/framework/Attributes/SequentialAttribute.cs b/src/NUnitFramework/framework/Attributes/SequentialAttribute.cs index 061c8b6c25..c0a98af6b3 100644 --- a/src/NUnitFramework/framework/Attributes/SequentialAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/SequentialAttribute.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2008 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,9 +21,9 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -using System.Collections; -using NUnit.Framework.Interfaces; using NUnit.Framework.Internal.Builders; namespace NUnit.Framework diff --git a/src/NUnitFramework/framework/Attributes/SetCultureAttribute.cs b/src/NUnitFramework/framework/Attributes/SetCultureAttribute.cs index d22ed43730..6766219f3e 100644 --- a/src/NUnitFramework/framework/Attributes/SetCultureAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/SetCultureAttribute.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -28,9 +30,9 @@ namespace NUnit.Framework { /// - /// Sets the current Culture on an assembly, test fixture or test method for + /// Sets the current Culture on an assembly, test fixture or test method for /// the duration of a test. The culture remains set until the test or fixture - /// completes and is then reset to its original value. + /// completes and is then reset to its original value. /// /// [AttributeUsage(AttributeTargets.Class|AttributeTargets.Method|AttributeTargets.Assembly, AllowMultiple=false, Inherited=true)] @@ -42,7 +44,7 @@ public class SetCultureAttribute : PropertyAttribute, IApplyToContext /// Construct given the name of a culture /// /// - public SetCultureAttribute( string culture ) : base( PropertyNames.SetCulture, culture ) + public SetCultureAttribute( string culture ) : base( PropertyNames.SetCulture, culture ) { _culture = culture; } @@ -51,11 +53,7 @@ public SetCultureAttribute( string culture ) : base( PropertyNames.SetCulture, c void IApplyToContext.ApplyToContext(TestExecutionContext context) { -#if NETSTANDARD1_4 - context.CurrentCulture = new System.Globalization.CultureInfo(_culture); -#else context.CurrentCulture = new System.Globalization.CultureInfo(_culture, false); -#endif } #endregion diff --git a/src/NUnitFramework/framework/Attributes/SetUICultureAttribute.cs b/src/NUnitFramework/framework/Attributes/SetUICultureAttribute.cs index b76b9da971..68a5b9bdaa 100644 --- a/src/NUnitFramework/framework/Attributes/SetUICultureAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/SetUICultureAttribute.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -28,9 +30,9 @@ namespace NUnit.Framework { /// - /// Sets the current UI Culture on an assembly, test fixture or test method + /// Sets the current UI Culture on an assembly, test fixture or test method /// for the duration of a test. The UI culture remains set until the test or - /// fixture completes and is then reset to its original value. + /// fixture completes and is then reset to its original value. /// /// [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method | AttributeTargets.Assembly, AllowMultiple = false, Inherited=true)] @@ -42,7 +44,7 @@ public class SetUICultureAttribute : PropertyAttribute, IApplyToContext /// Construct given the name of a culture /// /// - public SetUICultureAttribute(string culture) : base("SetUICulture", culture) + public SetUICultureAttribute(string culture) : base("SetUICulture", culture) { _culture = culture; } @@ -51,11 +53,7 @@ public SetUICultureAttribute(string culture) : base("SetUICulture", culture) void IApplyToContext.ApplyToContext(TestExecutionContext context) { -#if NETSTANDARD1_4 - context.CurrentUICulture = new System.Globalization.CultureInfo(_culture); -#else context.CurrentUICulture = new System.Globalization.CultureInfo(_culture, false); -#endif } #endregion diff --git a/src/NUnitFramework/framework/Attributes/SetUpAttribute.cs b/src/NUnitFramework/framework/Attributes/SetUpAttribute.cs index 72c42f8285..1d32bdfcbb 100644 --- a/src/NUnitFramework/framework/Attributes/SetUpAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/SetUpAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + namespace NUnit.Framework { using System; diff --git a/src/NUnitFramework/framework/Attributes/SetUpFixtureAttribute.cs b/src/NUnitFramework/framework/Attributes/SetUpFixtureAttribute.cs index 7ebb1841ed..ef3ec11209 100644 --- a/src/NUnitFramework/framework/Attributes/SetUpFixtureAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/SetUpFixtureAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; using System.Reflection; @@ -28,6 +30,7 @@ namespace NUnit.Framework { + using System.Diagnostics.CodeAnalysis; using Interfaces; using Internal; @@ -51,7 +54,7 @@ public IEnumerable BuildFrom(ITypeInfo typeInfo) if (fixture.RunState != RunState.NotRunnable) { - string reason = null; + string? reason = null; if (!IsValidFixtureType(typeInfo, ref reason)) fixture.MakeInvalid(reason); } @@ -65,7 +68,7 @@ public IEnumerable BuildFrom(ITypeInfo typeInfo) #region Helper Methods - private static bool IsValidFixtureType(ITypeInfo typeInfo, ref string reason) + private static bool IsValidFixtureType(ITypeInfo typeInfo, [NotNullWhen(false)] ref string? reason) { if (!typeInfo.IsStaticClass) { diff --git a/src/NUnitFramework/framework/Attributes/SingleThreadedAttribute.cs b/src/NUnitFramework/framework/Attributes/SingleThreadedAttribute.cs index 4ca9f8607f..ed29caa601 100644 --- a/src/NUnitFramework/framework/Attributes/SingleThreadedAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/SingleThreadedAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; diff --git a/src/NUnitFramework/framework/Attributes/TearDownAttribute.cs b/src/NUnitFramework/framework/Attributes/TearDownAttribute.cs index 84f1245d57..f5c0c36fbe 100644 --- a/src/NUnitFramework/framework/Attributes/TearDownAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TearDownAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + namespace NUnit.Framework { using System; diff --git a/src/NUnitFramework/framework/Attributes/TestActionAttribute.cs b/src/NUnitFramework/framework/Attributes/TestActionAttribute.cs index 99c9384fdf..1c33698e66 100644 --- a/src/NUnitFramework/framework/Attributes/TestActionAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TestActionAttribute.cs @@ -21,9 +21,9 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -using System.Collections.Generic; -using System.Text; namespace NUnit.Framework { diff --git a/src/NUnitFramework/framework/Attributes/TestAssemblyDirectoryResolveAttribute.cs b/src/NUnitFramework/framework/Attributes/TestAssemblyDirectoryResolveAttribute.cs index 63a7774554..39871eb49c 100644 --- a/src/NUnitFramework/framework/Attributes/TestAssemblyDirectoryResolveAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TestAssemblyDirectoryResolveAttribute.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2016 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,9 +21,9 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -using System.Collections.Generic; -using System.Text; namespace NUnit.Framework { diff --git a/src/NUnitFramework/framework/Attributes/TestAttribute.cs b/src/NUnitFramework/framework/Attributes/TestAttribute.cs index 2eb01a424e..1cab92ace1 100644 --- a/src/NUnitFramework/framework/Attributes/TestAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TestAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -49,7 +51,7 @@ namespace NUnit.Framework [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=true)] public class TestAttribute : NUnitAttribute, ISimpleTestBuilder, IApplyToTest, IImplyFixture { - private object _expectedResult; + private object? _expectedResult; private bool _hasExpectedResult = false; // needed in case result is set to null private readonly NUnitTestCaseBuilder _builder = new NUnitTestCaseBuilder(); @@ -57,24 +59,24 @@ public class TestAttribute : NUnitAttribute, ISimpleTestBuilder, IApplyToTest, I /// /// Descriptive text for this test /// - public string Description { get; set; } + public string? Description { get; set; } /// /// The author of this test /// - public string Author { get; set; } + public string? Author { get; set; } /// /// The type that this test is testing /// - public Type TestOf { get; set; } + public Type? TestOf { get; set; } /// /// Gets or sets the expected result. Not valid if the test /// method has parameters. /// /// The result. - public object ExpectedResult + public object? ExpectedResult { get { return _expectedResult; } set @@ -92,6 +94,8 @@ public object ExpectedResult /// The test to modify public void ApplyToTest(Test test) { + Guard.ArgumentValid(test.Method is object, "This attribute must only be applied to tests that have an associated method.", nameof(test)); + if (!test.Properties.ContainsKey(PropertyNames.Description) && Description != null) test.Properties.Set(PropertyNames.Description, Description); @@ -115,9 +119,9 @@ public void ApplyToTest(Test test) /// /// The method for which a test is to be constructed. /// The suite to which the test will be added. - public TestMethod BuildFrom(IMethodInfo method, Test suite) + public TestMethod BuildFrom(IMethodInfo method, Test? suite) { - TestCaseParameters parms = null; + TestCaseParameters? parms = null; if (_hasExpectedResult) { diff --git a/src/NUnitFramework/framework/Attributes/TestCaseAttribute.cs b/src/NUnitFramework/framework/Attributes/TestCaseAttribute.cs index 08649a799c..ad4ea6e398 100644 --- a/src/NUnitFramework/framework/Attributes/TestCaseAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TestCaseAttribute.cs @@ -21,8 +21,12 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Reflection; using NUnit.Compatibility; using NUnit.Framework.Interfaces; @@ -44,12 +48,12 @@ public class TestCaseAttribute : NUnitAttribute, ITestBuilder, ITestCaseData, II /// This constructor is not CLS-Compliant /// /// - public TestCaseAttribute(params object[] arguments) + public TestCaseAttribute(params object?[]? arguments) { RunState = RunState.Runnable; if (arguments == null) - Arguments = new object[] { null }; + Arguments = new object?[] { null }; else Arguments = arguments; @@ -60,10 +64,10 @@ public TestCaseAttribute(params object[] arguments) /// Construct a TestCaseAttribute with a single argument /// /// - public TestCaseAttribute(object arg) + public TestCaseAttribute(object? arg) { RunState = RunState.Runnable; - Arguments = new object[] { arg }; + Arguments = new object?[] { arg }; Properties = new PropertyBag(); } @@ -72,10 +76,10 @@ public TestCaseAttribute(object arg) /// /// /// - public TestCaseAttribute(object arg1, object arg2) + public TestCaseAttribute(object? arg1, object? arg2) { RunState = RunState.Runnable; - Arguments = new object[] { arg1, arg2 }; + Arguments = new object?[] { arg1, arg2 }; Properties = new PropertyBag(); } @@ -85,10 +89,10 @@ public TestCaseAttribute(object arg1, object arg2) /// /// /// - public TestCaseAttribute(object arg1, object arg2, object arg3) + public TestCaseAttribute(object? arg1, object? arg2, object? arg3) { RunState = RunState.Runnable; - Arguments = new object[] { arg1, arg2, arg3 }; + Arguments = new object?[] { arg1, arg2, arg3 }; Properties = new PropertyBag(); } @@ -100,7 +104,7 @@ public TestCaseAttribute(object arg1, object arg2, object arg3) /// Gets or sets the name of the test. /// /// The name of the test. - public string TestName { get; set; } + public string? TestName { get; set; } /// /// Gets or sets the RunState of this test case. @@ -110,7 +114,7 @@ public TestCaseAttribute(object arg1, object arg2, object arg3) /// /// Gets the list of arguments to a test case /// - public object[] Arguments { get; } + public object?[] Arguments { get; } /// /// Gets the properties of the test case @@ -125,7 +129,7 @@ public TestCaseAttribute(object arg1, object arg2, object arg3) /// Gets or sets the expected result. /// /// The result. - public object ExpectedResult + public object? ExpectedResult { get { return _expectedResult; } set @@ -134,7 +138,7 @@ public object ExpectedResult HasExpectedResult = true; } } - private object _expectedResult; + private object? _expectedResult; /// /// Returns true if the expected result has been set @@ -143,55 +147,79 @@ public object ExpectedResult #endregion + #region Instance Fields + + private RunState _originalRunState; + private DateTimeOffset? _untilDate; + + #endregion + #region Other Properties /// /// Gets or sets the description. /// /// The description. - public string Description + [DisallowNull] + public string? Description { get { return Properties.Get(PropertyNames.Description) as string; } - set { Properties.Set(PropertyNames.Description, value); } + set + { + Guard.ArgumentNotNull(value, nameof(value)); + Properties.Set(PropertyNames.Description, value); + } } /// /// The author of this test /// - public string Author + [DisallowNull] + public string? Author { get { return Properties.Get(PropertyNames.Author) as string; } - set { Properties.Set(PropertyNames.Author, value); } + set + { + Guard.ArgumentNotNull(value, nameof(value)); + Properties.Set(PropertyNames.Author, value); + } } /// /// The type that this test is testing /// - public Type TestOf + [DisallowNull] + public Type? TestOf { get { return _testOf; } set { + Guard.ArgumentNotNull(value, nameof(value)); _testOf = value; Properties.Set(PropertyNames.TestOf, value.FullName); } } - private Type _testOf; + private Type? _testOf; /// /// Gets or sets the reason for ignoring the test /// - public string Ignore + [DisallowNull] + public string? Ignore { get { return IgnoreReason; } - set { IgnoreReason = value; } + set + { + Guard.ArgumentNotNull(value, nameof(value)); + IgnoreReason = value; + } } /// /// Gets or sets a value indicating whether this is explicit. /// /// - /// true if explicit; otherwise, false. + /// if explicit; otherwise, . /// public bool Explicit { @@ -203,10 +231,15 @@ public bool Explicit /// Gets or sets the reason for not running the test. /// /// The reason. - public string Reason + [DisallowNull] + public string? Reason { get { return Properties.Get(PropertyNames.SkipReason) as string; } - set { Properties.Set(PropertyNames.SkipReason, value); } + set + { + Guard.ArgumentNotNull(value, nameof(value)); + Properties.Set(PropertyNames.SkipReason, value); + } } /// @@ -214,42 +247,64 @@ public string Reason /// non-empty value, the test is marked as ignored. /// /// The ignore reason. - public string IgnoreReason + [DisallowNull] + public string? IgnoreReason { get { return Reason; } set { + Guard.ArgumentNotNull(value, nameof(value)); + _originalRunState = RunState; RunState = RunState.Ignored; Reason = value; } } -#if PLATFORM_DETECTION /// /// Comma-delimited list of platforms to run the test for /// - public string IncludePlatform { get; set; } + public string? IncludePlatform { get; set; } /// /// Comma-delimited list of platforms to not run the test for /// - public string ExcludePlatform { get; set; } -#endif + public string? ExcludePlatform { get; set; } /// /// Gets and sets the category for this test case. /// May be a comma-separated list of categories. /// - public string Category + [DisallowNull] + public string? Category { get { return Properties.Get(PropertyNames.Category) as string; } set { + Guard.ArgumentNotNull(value, nameof(value)); + foreach (string cat in value.Split(new char[] { ',' }) ) Properties.Add(PropertyNames.Category, cat); } } + /// + /// Gets and sets the ignore until date for this test case. + /// + public string? Until + { + get { return Properties.Get(PropertyNames.IgnoreUntilDate) as string; } + set + { + if (!string.IsNullOrEmpty(IgnoreReason)) + { + _untilDate = DateTimeOffset.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal); + Properties.Set(PropertyNames.IgnoreUntilDate, _untilDate.Value.ToString("u")); + } + else + this.RunState = RunState.NotRunnable; + } + } + #endregion #region Helper Methods @@ -267,9 +322,8 @@ private TestCaseParameters GetParametersForTestCase(IMethodInfo method) parms = new TestCaseParameters(this); // Special handling for ExpectedResult (see if it needs to be converted into method return type) - object expectedResultInTargetType; if (parms.HasExpectedResult - && ParamAttributeTypeConversions.TryConvert(parms.ExpectedResult, method.ReturnType.Type, out expectedResultInTargetType)) + && ParamAttributeTypeConversions.TryConvert(parms.ExpectedResult, method.ReturnType.Type, out var expectedResultInTargetType)) { parms.ExpectedResult = expectedResultInTargetType; } @@ -294,7 +348,7 @@ private TestCaseParameters GetParametersForTestCase(IMethodInfo method) } else { - object[] newArglist = new object[argsNeeded]; + object?[] newArglist = new object?[argsNeeded]; for (int i = 0; i < argsNeeded && i < argsProvided; i++) newArglist[i] = parms.Arguments[i]; @@ -313,7 +367,7 @@ private TestCaseParameters GetParametersForTestCase(IMethodInfo method) //Special handling for optional parameters if (parms.Arguments.Length < argsNeeded) { - object[] newArgList = new object[parameters.Length]; + object?[] newArgList = new object?[parameters.Length]; Array.Copy(parms.Arguments, newArgList, parms.Arguments.Length); //Fill with Type.Missing for remaining required parameters where optional @@ -339,7 +393,7 @@ private TestCaseParameters GetParametersForTestCase(IMethodInfo method) if (argsNeeded == 1 && method.GetParameters()[0].ParameterType == typeof(object[])) { if (argsProvided > 1 || - argsProvided == 1 && parms.Arguments[0].GetType() != typeof(object[])) + argsProvided == 1 && parms.Arguments[0]?.GetType() != typeof(object[])) { parms.Arguments = new object[] { parms.Arguments }; } @@ -363,14 +417,13 @@ private TestCaseParameters GetParametersForTestCase(IMethodInfo method) /// /// The arguments to be converted /// The ParameterInfo array for the method - private static void PerformSpecialConversions(object[] arglist, IParameterInfo[] parameters) + private static void PerformSpecialConversions(object?[] arglist, IParameterInfo[] parameters) { for (int i = 0; i < arglist.Length; i++) { - object arg = arglist[i]; + object? arg = arglist[i]; Type targetType = parameters[i].ParameterType; - object argAsTargetType; - if (ParamAttributeTypeConversions.TryConvert(arg, targetType, out argAsTargetType)) + if (ParamAttributeTypeConversions.TryConvert(arg, targetType, out var argAsTargetType)) { arglist[i] = argAsTargetType; } @@ -385,11 +438,24 @@ private static void PerformSpecialConversions(object[] arglist, IParameterInfo[] /// /// The MethodInfo for which tests are to be constructed. /// The suite to which the tests will be added. - public IEnumerable BuildFrom(IMethodInfo method, Test suite) + public IEnumerable BuildFrom(IMethodInfo method, Test? suite) { TestMethod test = new NUnitTestCaseBuilder().BuildTestMethod(method, suite, GetParametersForTestCase(method)); + + if (_untilDate.HasValue) + { + if (_untilDate > DateTimeOffset.UtcNow) + { + test.RunState = RunState.Ignored; + string reason = string.Format("Ignoring until {0}. {1}", _untilDate.Value.ToString("u"), IgnoreReason); + test.Properties.Set(PropertyNames.SkipReason, reason); + } + else + { + test.RunState = _originalRunState; + } + } -#if PLATFORM_DETECTION if (IncludePlatform != null || ExcludePlatform != null) { if (test.RunState == RunState.NotRunnable || test.RunState == RunState.Ignored) @@ -406,7 +472,6 @@ public IEnumerable BuildFrom(IMethodInfo method, Test suite) test.Properties.Add(PropertyNames.SkipReason, platformHelper.Reason); } } -#endif yield return test; } diff --git a/src/NUnitFramework/framework/Attributes/TestCaseSourceAttribute.cs b/src/NUnitFramework/framework/Attributes/TestCaseSourceAttribute.cs index 43ccb4ee70..376d4a0c31 100644 --- a/src/NUnitFramework/framework/Attributes/TestCaseSourceAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TestCaseSourceAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Collections.Generic; @@ -59,7 +61,7 @@ public TestCaseSourceAttribute(string sourceName) /// The name of a static method, property or field that will provide data. /// A set of parameters passed to the method, works only if the Source Name is a method. /// If the source name is a field or property has no effect. - public TestCaseSourceAttribute(Type sourceType, string sourceName, object[] methodParams) + public TestCaseSourceAttribute(Type sourceType, string sourceName, object?[]? methodParams) { this.MethodParams = methodParams; this.SourceType = sourceType; @@ -82,7 +84,7 @@ public TestCaseSourceAttribute(Type sourceType, string sourceName) /// The name of a static method, property or field that will provide data. /// A set of parameters passed to the method, works only if the Source Name is a method. /// If the source name is a field or property has no effect. - public TestCaseSourceAttribute(string sourceName, object[] methodParams) + public TestCaseSourceAttribute(string sourceName, object?[]? methodParams) { this.MethodParams = methodParams; this.SourceName = sourceName; @@ -103,22 +105,22 @@ public TestCaseSourceAttribute(Type sourceType) /// A set of parameters passed to the method, works only if the Source Name is a method. /// If the source name is a field or property has no effect. /// - public object[] MethodParams { get; } + public object?[]? MethodParams { get; } /// - /// The name of a the method, property or fiend to be used as a source + /// The name of a the method, property or field to be used as a source /// - public string SourceName { get; } + public string? SourceName { get; } /// /// A Type to be used as a source /// - public Type SourceType { get; } + public Type? SourceType { get; } /// /// Gets or sets the category associated with every fixture created from /// this attribute. May be a single category or a comma-separated list. /// - public string Category { get; set; } + public string? Category { get; set; } #endregion @@ -129,7 +131,7 @@ public TestCaseSourceAttribute(Type sourceType) /// /// The IMethod for which tests are to be constructed. /// The suite to which the tests will be added. - public IEnumerable BuildFrom(IMethodInfo method, Test suite) + public IEnumerable BuildFrom(IMethodInfo method, Test? suite) { int count = 0; @@ -162,21 +164,11 @@ private IEnumerable GetTestCasesFor(IMethodInfo method) try { - IEnumerable source; - - var previousState = SandboxedThreadState.Capture(); - try - { - source = GetTestCaseSource(method); - } - finally - { - previousState.Restore(); - } + IEnumerable? source = ContextUtils.DoIsolated(() => GetTestCaseSource(method)); if (source != null) { - foreach (object item in source) + foreach (object? item in source) { // First handle two easy cases: // 1. Source is null. This is really an error but if we @@ -185,13 +177,13 @@ private IEnumerable GetTestCasesFor(IMethodInfo method) // single null argument will cause an error to be // reported at the test level, in most cases. // 2. User provided an ITestCaseData and we just use it. - ITestCaseData parms = item == null - ? new TestCaseParameters(new object[] { null }) + ITestCaseData? parms = item == null + ? new TestCaseParameters(new object?[] { null }) : item as ITestCaseData; if (parms == null) { - object[] args = null; + object?[]? args = null; // 3. An array was passed, it may be an object[] // or possibly some other kind of array, which @@ -207,7 +199,7 @@ private IEnumerable GetTestCasesFor(IMethodInfo method) var argsNeeded = parameters.Length; if (argsNeeded > 0 && argsNeeded == array.Length && parameters[0].ParameterType != array.GetType()) { - args = new object[array.Length]; + args = new object?[array.Length]; for (var i = 0; i < array.Length; i++) args[i] = array.GetValue(i); } @@ -215,7 +207,7 @@ private IEnumerable GetTestCasesFor(IMethodInfo method) if (args == null) { - args = new object[] { item }; + args = new object?[] { item }; } parms = new TestCaseParameters(args); @@ -243,7 +235,7 @@ private IEnumerable GetTestCasesFor(IMethodInfo method) return data; } - private IEnumerable GetTestCaseSource(IMethodInfo method) + private IEnumerable? GetTestCaseSource(IMethodInfo method) { Type sourceType = SourceType ?? method.TypeInfo.Type; diff --git a/src/NUnitFramework/framework/Attributes/TestFixtureAttribute.cs b/src/NUnitFramework/framework/Attributes/TestFixtureAttribute.cs index 22c83d7675..d804015825 100644 --- a/src/NUnitFramework/framework/Attributes/TestFixtureAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TestFixtureAttribute.cs @@ -21,8 +21,13 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using NUnit.Compatibility; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; using NUnit.Framework.Internal.Builders; @@ -42,17 +47,17 @@ public class TestFixtureAttribute : NUnitAttribute, IFixtureBuilder2, ITestFixtu /// /// Default constructor /// - public TestFixtureAttribute() : this( new object[0] ) { } + public TestFixtureAttribute() : this(Internal.TestParameters.NoArguments) { } /// /// Construct with a object[] representing a set of arguments. /// The arguments may later be separated into type arguments and constructor arguments. /// /// - public TestFixtureAttribute(params object[] arguments) + public TestFixtureAttribute(params object?[]? arguments) { RunState = RunState.Runnable; - Arguments = arguments ?? new object[] { null }; + Arguments = arguments ?? new object?[] { null }; TypeArgs = new Type[0]; Properties = new PropertyBag(); } @@ -65,7 +70,7 @@ public TestFixtureAttribute(params object[] arguments) /// Gets or sets the name of the test. /// /// The name of the test. - public string TestName { get; set; } + public string? TestName { get; set; } /// /// Gets or sets the RunState of this test fixture. @@ -75,7 +80,7 @@ public TestFixtureAttribute(params object[] arguments) /// /// The arguments originally provided to the attribute /// - public object[] Arguments { get; } + public object?[] Arguments { get; } /// /// Properties pertaining to this fixture @@ -100,53 +105,75 @@ public TestFixtureAttribute(params object[] arguments) /// /// Descriptive text for this fixture /// - public string Description + [DisallowNull] + public string? Description { get { return Properties.Get(PropertyNames.Description) as string; } - set { Properties.Set(PropertyNames.Description, value); } + set + { + Guard.ArgumentNotNull(value, nameof(value)); + Properties.Set(PropertyNames.Description, value); + } } /// /// The author of this fixture /// - public string Author + [DisallowNull] + public string? Author { get { return Properties.Get(PropertyNames.Author) as string; } - set { Properties.Set(PropertyNames.Author, value); } + set + { + Guard.ArgumentNotNull(value, nameof(value)); + Properties.Set(PropertyNames.Author, value); + } } /// /// The type that this fixture is testing /// - public Type TestOf + [DisallowNull] + public Type? TestOf { get { return _testOf; } set { + Guard.ArgumentNotNull(value, nameof(value)); _testOf = value; Properties.Set(PropertyNames.TestOf, value.FullName); } } - private Type _testOf; + private Type? _testOf; /// /// Gets or sets the ignore reason. May set RunState as a side effect. /// /// The ignore reason. - public string Ignore + [DisallowNull] + public string? Ignore { get { return IgnoreReason; } - set { IgnoreReason = value; } + set + { + Guard.ArgumentNotNull(value, nameof(value)); + IgnoreReason = value; + } } /// /// Gets or sets the reason for not running the fixture. /// /// The reason. - public string Reason + [DisallowNull] + public string? Reason { get { return this.Properties.Get(PropertyNames.SkipReason) as string; } - set { this.Properties.Set(PropertyNames.SkipReason, value); } + set + { + Guard.ArgumentNotNull(value, nameof(value)); + this.Properties.Set(PropertyNames.SkipReason, value); + } } /// @@ -154,11 +181,13 @@ public string Reason /// non-empty value, the test is marked as ignored. /// /// The ignore reason. - public string IgnoreReason + [DisallowNull] + public string? IgnoreReason { get { return Reason; } set { + Guard.ArgumentNotNull(value, nameof(value)); RunState = RunState.Ignored; Reason = value; } @@ -168,7 +197,7 @@ public string IgnoreReason /// Gets or sets a value indicating whether this is explicit. /// /// - /// true if explicit; otherwise, false. + /// if explicit; otherwise, . /// public bool Explicit { @@ -180,7 +209,8 @@ public bool Explicit /// Gets and sets the category for this fixture. /// May be a comma-separated list of categories. /// - public string Category + [DisallowNull] + public string? Category { get { @@ -206,6 +236,8 @@ public string Category } set { + Guard.ArgumentNotNull(value, nameof(value)); + foreach (string cat in value.Split(new char[] { ',' })) Properties.Add(PropertyNames.Category, cat); } @@ -220,7 +252,7 @@ public string Category /// public IEnumerable BuildFrom(ITypeInfo typeInfo) { - yield return _builder.BuildFrom(typeInfo, PreFilter.Empty, this); + return this.BuildFrom(typeInfo, PreFilter.Empty); } #endregion @@ -234,7 +266,10 @@ public IEnumerable BuildFrom(ITypeInfo typeInfo) /// Filter used to select methods as tests. public IEnumerable BuildFrom(ITypeInfo typeInfo, IPreFilter filter) { - yield return _builder.BuildFrom(typeInfo, filter, this); + var fixture = _builder.BuildFrom(typeInfo, filter, this); + fixture.ApplyAttributesToTest(typeInfo.Type.GetTypeInfo()); + + yield return fixture; } #endregion diff --git a/src/NUnitFramework/framework/Attributes/TestFixtureSourceAttribute.cs b/src/NUnitFramework/framework/Attributes/TestFixtureSourceAttribute.cs index 813fcd9da3..f04526cfc1 100644 --- a/src/NUnitFramework/framework/Attributes/TestFixtureSourceAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TestFixtureSourceAttribute.cs @@ -21,10 +21,13 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Collections.Generic; using System.Reflection; +using NUnit.Compatibility; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; using NUnit.Framework.Internal.Builders; @@ -80,20 +83,20 @@ public TestFixtureSourceAttribute(Type sourceType) #region Properties /// - /// The name of a the method, property or fiend to be used as a source + /// The name of a the method, property or field to be used as a source /// - public string SourceName { get; } + public string? SourceName { get; } /// /// A Type to be used as a source /// - public Type SourceType { get; } + public Type? SourceType { get; } /// /// Gets or sets the category associated with every fixture created from /// this attribute. May be a single category or a comma-separated list. /// - public string Category { get; set; } + public string? Category { get; set; } #endregion @@ -121,8 +124,16 @@ public IEnumerable BuildFrom(ITypeInfo typeInfo, IPreFilter filter) { Type sourceType = SourceType ?? typeInfo.Type; + var fixtureSuite = new ParameterizedFixtureSuite(typeInfo); + fixtureSuite.ApplyAttributesToTest(typeInfo.Type.GetTypeInfo()); + foreach (ITestFixtureData parms in GetParametersFor(sourceType)) - yield return _builder.BuildFrom(typeInfo, filter, parms); + { + TestSuite fixture = _builder.BuildFrom(typeInfo, filter, parms); + fixtureSuite.Add(fixture); + } + + yield return fixtureSuite; } #endregion @@ -141,20 +152,20 @@ public IEnumerable GetParametersFor(Type sourceType) try { - IEnumerable source = GetTestFixtureSource(sourceType); + IEnumerable? source = GetTestFixtureSource(sourceType); if (source != null) { - foreach (object item in source) + foreach (object? item in source) { var parms = item as ITestFixtureData; if (parms == null) { - object[] args = item as object[]; + object?[]? args = item as object?[]; if (args == null) { - args = new object[] { item }; + args = new object?[] { item }; } parms = new TestFixtureParameters(args); @@ -177,7 +188,7 @@ public IEnumerable GetParametersFor(Type sourceType) return data; } - private IEnumerable GetTestFixtureSource(Type sourceType) + private IEnumerable? GetTestFixtureSource(Type sourceType) { // Handle Type implementing IEnumerable separately if (SourceName == null) diff --git a/src/NUnitFramework/framework/Attributes/TestOfAttribute.cs b/src/NUnitFramework/framework/Attributes/TestOfAttribute.cs index d4469c2321..521ecf0e3a 100644 --- a/src/NUnitFramework/framework/Attributes/TestOfAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TestOfAttribute.cs @@ -1,4 +1,4 @@ -// ********************************************************************************** +// ********************************************************************************** // The MIT License (MIT) // // Copyright (c) 2014 Charlie Poole, Rob Prouse @@ -22,14 +22,11 @@ // // ********************************************************************************** - -#region Using Directives +#nullable enable using System; using NUnit.Framework.Internal; -#endregion - namespace NUnit.Framework { /// diff --git a/src/NUnitFramework/framework/Attributes/TheoryAttribute.cs b/src/NUnitFramework/framework/Attributes/TheoryAttribute.cs index 6450ae135d..6267f820a2 100644 --- a/src/NUnitFramework/framework/Attributes/TheoryAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TheoryAttribute.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2014 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -20,6 +20,9 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** + +#nullable enable + using System; namespace NUnit.Framework @@ -60,4 +63,4 @@ public class TheoryAttribute : CombiningStrategyAttribute, ITestBuilder, IImplyF { } } -} \ No newline at end of file +} diff --git a/src/NUnitFramework/framework/Attributes/TimeoutAttribute.cs b/src/NUnitFramework/framework/Attributes/TimeoutAttribute.cs index 1f4ddbf9a7..40ac789c80 100644 --- a/src/NUnitFramework/framework/Attributes/TimeoutAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/TimeoutAttribute.cs @@ -21,10 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -using NUnit.Framework.Internal; -using NUnit.Framework.Internal.Commands; using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; namespace NUnit.Framework { diff --git a/src/NUnitFramework/framework/Attributes/ValueSourceAttribute.cs b/src/NUnitFramework/framework/Attributes/ValueSourceAttribute.cs index fb79756a87..a37b576074 100644 --- a/src/NUnitFramework/framework/Attributes/ValueSourceAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/ValueSourceAttribute.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Reflection; @@ -65,14 +67,14 @@ public ValueSourceAttribute(Type sourceType, string sourceName) #region Properties /// - /// The name of a the method, property or fiend to be used as a source + /// The name of a the method, property or field to be used as a source /// - public string SourceName { get; } + public string? SourceName { get; } /// /// A Type to be used as a source /// - public Type SourceType { get; } + public Type? SourceType { get; } #endregion @@ -95,24 +97,20 @@ private IEnumerable GetDataSource(IParameterInfo parameter) { Type sourceType = SourceType ?? parameter.Method.TypeInfo.Type; - // TODO: Test this if (SourceName == null) - return Reflect.Construct(sourceType) as IEnumerable; + { + return Reflect.Construct(sourceType) as IEnumerable + ?? throw new InvalidDataSourceException($"The value source type '{sourceType}' does not implement IEnumerable."); + } MemberInfo[] members = sourceType.GetMember(SourceName, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.FlattenHierarchy); - var dataSource = GetDataSourceValue(members); - - if (dataSource == null) - { - ThrowInvalidDataSourceException(); - } - - return dataSource; + return ContextUtils.DoIsolated(() => GetDataSourceValue(members)) + ?? throw CreateSourceNameException(); } - private static IEnumerable GetDataSourceValue(MemberInfo[] members) + private static IEnumerable? GetDataSourceValue(MemberInfo[] members) { if (members.Length != 1) return null; @@ -124,7 +122,7 @@ private static IEnumerable GetDataSourceValue(MemberInfo[] members) if (field.IsStatic) return (IEnumerable)field.GetValue(null); - ThrowInvalidDataSourceException(); + throw CreateSourceNameException(); } var property = member as PropertyInfo; @@ -133,7 +131,7 @@ private static IEnumerable GetDataSourceValue(MemberInfo[] members) if (property.GetGetMethod(true).IsStatic) return (IEnumerable)property.GetValue(null, null); - ThrowInvalidDataSourceException(); + throw CreateSourceNameException(); } var m = member as MethodInfo; @@ -142,15 +140,15 @@ private static IEnumerable GetDataSourceValue(MemberInfo[] members) if (m.IsStatic) return (IEnumerable)m.Invoke(null, null); - ThrowInvalidDataSourceException(); + throw CreateSourceNameException(); } return null; } - private static void ThrowInvalidDataSourceException() + private static InvalidDataSourceException CreateSourceNameException() { - throw new InvalidDataSourceException("The sourceName specified on a ValueSourceAttribute must refer to a non-null static field, property or method."); + return new InvalidDataSourceException("The sourceName specified on a ValueSourceAttribute must refer to a non-null static field, property or method."); } #endregion diff --git a/src/NUnitFramework/framework/Attributes/ValuesAttribute.cs b/src/NUnitFramework/framework/Attributes/ValuesAttribute.cs index a9d1096cfd..43b4077c28 100644 --- a/src/NUnitFramework/framework/Attributes/ValuesAttribute.cs +++ b/src/NUnitFramework/framework/Attributes/ValuesAttribute.cs @@ -21,13 +21,14 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Reflection; - +using NUnit.Compatibility; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; -using NUnit.Compatibility; namespace NUnit.Framework { @@ -47,7 +48,7 @@ public class ValuesAttribute : NUnitAttribute, IParameterDataSource #pragma warning disable IDE1006 // ReSharper disable once InconsistentNaming // Disregarding naming convention for back-compat - protected object[] data; + protected object?[] data; #pragma warning restore IDE1006 /// @@ -56,16 +57,16 @@ public class ValuesAttribute : NUnitAttribute, IParameterDataSource /// public ValuesAttribute() { - data = new object[]{}; + data = Internal.TestParameters.NoArguments; } /// /// Construct with one argument /// /// - public ValuesAttribute(object arg1) + public ValuesAttribute(object? arg1) { - data = new object[] { arg1 }; + data = new object?[] { arg1 }; } /// @@ -73,9 +74,9 @@ public ValuesAttribute(object arg1) /// /// /// - public ValuesAttribute(object arg1, object arg2) + public ValuesAttribute(object? arg1, object? arg2) { - data = new object[] { arg1, arg2 }; + data = new object?[] { arg1, arg2 }; } /// @@ -84,18 +85,18 @@ public ValuesAttribute(object arg1, object arg2) /// /// /// - public ValuesAttribute(object arg1, object arg2, object arg3) + public ValuesAttribute(object? arg1, object? arg2, object? arg3) { - data = new object[] { arg1, arg2, arg3 }; + data = new object?[] { arg1, arg2, arg3 }; } /// /// Construct with an array of arguments /// /// - public ValuesAttribute(params object[] args) + public ValuesAttribute(params object?[]? args) { - data = args ?? new object[] { null }; + data = args ?? new object?[] { null }; } /// @@ -128,14 +129,14 @@ private static IEnumerable GenerateData(Type targetType) } if (targetType == typeof(bool?)) { - return new object[] { null, true, false }; + return new object?[] { null, true, false }; } if (targetType == typeof(bool)) { return new object[] { true, false }; } - return new object[] { }; + return Internal.TestParameters.NoArguments; } /// diff --git a/src/NUnitFramework/framework/Compatibility/AttributeHelper.cs b/src/NUnitFramework/framework/Compatibility/AttributeHelper.cs index 603fc1b50d..751e72d411 100644 --- a/src/NUnitFramework/framework/Compatibility/AttributeHelper.cs +++ b/src/NUnitFramework/framework/Compatibility/AttributeHelper.cs @@ -37,33 +37,12 @@ public static class AttributeHelper /// /// The actual. /// Type of the attribute. - /// if set to true [inherit]. + /// if set to [inherit]. /// A list of the given attribute on the given object. public static Attribute[] GetCustomAttributes(object actual, Type attributeType, bool inherit) { -#if NETSTANDARD1_4 - var member = actual as MemberInfo; - if (member != null) return (Attribute[])member.GetCustomAttributes(attributeType, inherit); - - var type = actual as Type; - if (type != null) return (Attribute[])type.GetTypeInfo().GetCustomAttributes(attributeType, inherit); - - var parameter = actual as ParameterInfo; - if (parameter != null) return (Attribute[])parameter.GetCustomAttributes(attributeType, inherit); - - var assembly = actual as Assembly; - if (assembly != null) return (Attribute[])assembly.GetCustomAttributes(attributeType); - - var interfaceType = actual?.GetType().GetInterfaces().SingleOrDefault(i => i.FullName == "System.Reflection.ICustomAttributeProvider"); - if (interfaceType != null) - { - var method = interfaceType.GetMethod("GetCustomAttributes", new[] { typeof(Type), typeof(bool) }); - return (Attribute[])method.Invoke(actual, new object[] { attributeType, inherit }); - } -#else var attrProvider = actual as ICustomAttributeProvider; if (attrProvider != null) return (Attribute[])attrProvider.GetCustomAttributes(attributeType, inherit); -#endif throw new ArgumentException($"Actual value {actual} does not implement ICustomAttributeProvider.", nameof(actual)); } diff --git a/src/NUnitFramework/framework/Compatibility/LongLivedMarshalByRefObject.cs b/src/NUnitFramework/framework/Compatibility/LongLivedMarshalByRefObject.cs index 55dcabe32b..4beb9f1766 100644 --- a/src/NUnitFramework/framework/Compatibility/LongLivedMarshalByRefObject.cs +++ b/src/NUnitFramework/framework/Compatibility/LongLivedMarshalByRefObject.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -29,11 +29,6 @@ namespace NUnit.Compatibility /// /// A MarshalByRefObject that lives forever /// -#if NETSTANDARD1_4 - public class LongLivedMarshalByRefObject - { - } -#else public class LongLivedMarshalByRefObject : MarshalByRefObject { /// @@ -45,5 +40,4 @@ public override object InitializeLifetimeService() return null; } } -#endif } diff --git a/src/NUnitFramework/framework/Compatibility/System.Collections.Concurrent/ConcurrentQueue.cs b/src/NUnitFramework/framework/Compatibility/System.Collections.Concurrent/ConcurrentQueue.cs index ffdc0e051d..bcd292dbae 100644 --- a/src/NUnitFramework/framework/Compatibility/System.Collections.Concurrent/ConcurrentQueue.cs +++ b/src/NUnitFramework/framework/Compatibility/System.Collections.Concurrent/ConcurrentQueue.cs @@ -126,7 +126,7 @@ private void OnSerializing(StreamingContext context) } /// - /// Construct the queue from a previously seiralized one + /// Construct the queue from a previously serialized one. /// [OnDeserialized] private void OnDeserialized(StreamingContext context) @@ -241,7 +241,7 @@ bool IProducerConsumerCollection.TryAdd(T item) /// When this method returns, if the operation was successful, contains the /// object removed. If no object was available to be removed, the value is unspecified. /// - /// true if an element was removed and returned succesfully; otherwise, false. + /// true if an element was removed and returned successfully; otherwise, false. /// For , this operation will attempt to remove the object /// from the beginning of the . /// @@ -321,9 +321,7 @@ private List ToList() try { //store head and tail positions in buffer, - Segment head, tail; - int headLow, tailHigh; - GetHeadTailPositions(out head, out tail, out headLow, out tailHigh); + GetHeadTailPositions(out var head, out var tail, out var headLow, out var tailHigh); if (head == tail) { @@ -399,9 +397,7 @@ public int Count get { //store head and tail positions in buffer, - Segment head, tail; - int headLow, tailHigh; - GetHeadTailPositions(out head, out tail, out headLow, out tailHigh); + GetHeadTailPositions(out var head, out var tail, out var headLow, out var tailHigh); if (head == tail) { @@ -481,21 +477,19 @@ public IEnumerator GetEnumerator() // A design flaw here: if a Thread.Abort() happens, we cannot decrement m_numSnapshotTakers. But we cannot // wrap the following with a try/finally block, otherwise the decrement will happen before the yield return // statements in the GetEnumerator (head, tail, headLow, tailHigh) method. - Segment head, tail; - int headLow, tailHigh; - GetHeadTailPositions(out head, out tail, out headLow, out tailHigh); + GetHeadTailPositions(out var head, out var tail, out var headLow, out var tailHigh); //If we put yield-return here, the iterator will be lazily evaluated. As a result a snapshot of // the queue is not taken when GetEnumerator is initialized but when MoveNext() is first called. // This is inconsistent with existing generic collections. In order to prevent it, we capture the // value of m_head in a buffer and call out to a helper method. //The old way of doing this was to return the ToList().GetEnumerator(), but ToList() was an - // unnecessary perfomance hit. + // unnecessary performance hit. return GetEnumerator(head, tail, headLow, tailHigh); } /// - /// Helper method of GetEnumerator to seperate out yield return statement, and prevent lazy evaluation. + /// Helper method of GetEnumerator to separate out yield return statement, and prevent lazy evaluation. /// private IEnumerator GetEnumerator(Segment head, Segment tail, int headLow, int tailHigh) { @@ -598,9 +592,9 @@ public void Enqueue(T item) /// When this method returns, if the operation was successful, contains the /// object removed. If no object was available to be removed, the value is unspecified. /// - /// true if an element was removed and returned from the beggining of the true if an element was removed and returned from the beginning of the - /// succesfully; otherwise, false. + /// successfully; otherwise, false. public bool TryDequeue(out T result) { while (!IsEmpty) @@ -720,9 +714,9 @@ internal bool IsEmpty /// /// Add an element to the tail of the current segment - /// exclusively called by ConcurrentQueue.InitializedFromCollection - /// InitializeFromCollection is responsible to guaratee that there is no index overflow, - /// and there is no contention + /// exclusively called by ConcurrentQueue.InitializedFromCollection. + /// InitializeFromCollection is responsible to guarantee that there is no index overflow + /// and no contention. /// /// internal void UnsafeAdd(T value) @@ -737,7 +731,7 @@ internal void UnsafeAdd(T value) /// Create a new segment and append to the current one /// Does not update the m_tail pointer /// exclusively called by ConcurrentQueue.InitializedFromCollection - /// InitializeFromCollection is responsible to guaratee that there is no index overflow, + /// InitializeFromCollection is responsible to guarantee that there is no index overflow, /// and there is no contention /// /// the reference to the new Segment @@ -790,7 +784,7 @@ internal bool TryAppend(T value) int newhigh = SEGMENT_SIZE; //initial value set to be over the boundary //We need do Interlocked.Increment and value/state update in a finally block to ensure that they run - //without interuption. This is to prevent anything from happening between them, and another dequeue + //without interruption. This is to prevent anything from happening between them, and another dequeue //thread maybe spinning forever to wait for m_state[] to be true; try { } @@ -853,7 +847,7 @@ internal bool TryRemove(out T result) if (lowLocal + 1 >= SEGMENT_SIZE) { // Invariant: we only dispose the current m_head, not any other segment - // In usual situation, disposing a segment is simply seting m_head to m_head.m_next + // In usual situation, disposing a segment is simply setting m_head to m_head.m_next // But there is one special case, where m_head and m_tail points to the same and ONLY //segment of the queue: Another thread A is doing Enqueue and finds that it needs to grow, //while the *current* thread is doing *this* Dequeue operation, and finds that it needs to @@ -934,7 +928,7 @@ internal int Low /// /// return the logical position of the tail of the current segment - /// Value range [-1, SEGMENT_SIZE-1]. When it's -1, it means this is a new segment and has no elemnet yet + /// Value range [-1, SEGMENT_SIZE-1]. When it's -1, it means this is a new segment and has no element yet /// internal int High { @@ -950,15 +944,11 @@ internal int High }//end of class Segment /// - /// A wrapper struct for volatile bool, please note the copy of the struct it self will not be volatile - /// for example this statement will not include in volatilness operation volatileBool1 = volatileBool2 the jit will copy the struct and will ignore the volatile + /// A wrapper struct for volatile bool. Please note that a copy of the struct itself will not be volatile, e.g. + /// volatileBool1 = volatileBool2. /// struct VolatileBool { - public VolatileBool(bool value) - { - m_value = value; - } public volatile bool m_value; } } diff --git a/src/NUnitFramework/framework/Compatibility/System.Diagnostics.CodeAnalysis/NullableAttributes.cs b/src/NUnitFramework/framework/Compatibility/System.Diagnostics.CodeAnalysis/NullableAttributes.cs new file mode 100644 index 0000000000..6f154fa413 --- /dev/null +++ b/src/NUnitFramework/framework/Compatibility/System.Diagnostics.CodeAnalysis/NullableAttributes.cs @@ -0,0 +1,110 @@ +// Source: https://github.com/dotnet/runtime/blob/4c71c6cf112d5cdfe5d39485505f1b2991c6e0d5/src/libraries/System.Private.CoreLib/src/System/Diagnostics/CodeAnalysis/NullableAttributes.cs + +// The MIT License (MIT) +// +// Copyright (c) .NET Foundation and Contributors +// +// All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +namespace System.Diagnostics.CodeAnalysis +{ + /// Specifies that null is allowed as an input even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class AllowNullAttribute : Attribute + { } + + /// Specifies that null is disallowed as an input even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)] + internal sealed class DisallowNullAttribute : Attribute + { } + + /// Specifies that an output may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class MaybeNullAttribute : Attribute + { } + + /// Specifies that an output will not be null even if the corresponding type allows it. Specifies that an input argument was not null when the call returns. + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)] + internal sealed class NotNullAttribute : Attribute + { } + + /// Specifies that when a method returns , the parameter may be null even if the corresponding type disallows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class MaybeNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter may be null. + /// + public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that when a method returns , the parameter will not be null even if the corresponding type allows it. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class NotNullWhenAttribute : Attribute + { + /// Initializes the attribute with the specified return value condition. + /// + /// The return value condition. If the method returns this value, the associated parameter will not be null. + /// + public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue; + + /// Gets the return value condition. + public bool ReturnValue { get; } + } + + /// Specifies that the output will be non-null if the named parameter is non-null. + [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)] + internal sealed class NotNullIfNotNullAttribute : Attribute + { + /// Initializes the attribute with the associated parameter name. + /// + /// The associated parameter name. The output will be non-null if the argument to the parameter specified is non-null. + /// + public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName; + + /// Gets the associated parameter name. + public string ParameterName { get; } + } + + /// Applied to a method that will never return under any circumstance. + [AttributeUsage(AttributeTargets.Method, Inherited = false)] + internal sealed class DoesNotReturnAttribute : Attribute + { } + + /// Specifies that the method will not return if the associated Boolean parameter is passed the specified value. + [AttributeUsage(AttributeTargets.Parameter, Inherited = false)] + internal sealed class DoesNotReturnIfAttribute : Attribute + { + /// Initializes the attribute with the specified parameter value. + /// + /// The condition parameter value. Code after the method will be considered unreachable by diagnostics if the argument to + /// the associated parameter matches this value. + /// + public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue; + + /// Gets the condition parameter value. + public bool ParameterValue { get; } + } +} diff --git a/src/NUnitFramework/framework/Compatibility/System.Threading/ManualResetEventSlim.cs b/src/NUnitFramework/framework/Compatibility/System.Threading/ManualResetEventSlim.cs index 3918294d20..7f25cacf55 100644 --- a/src/NUnitFramework/framework/Compatibility/System.Threading/ManualResetEventSlim.cs +++ b/src/NUnitFramework/framework/Compatibility/System.Threading/ManualResetEventSlim.cs @@ -40,6 +40,8 @@ internal sealed class ManualResetEventSlim : IDisposable public void Wait() => _mres.WaitOne(); public bool Wait(int millisecondsTimeout) => _mres.WaitOne(millisecondsTimeout); + + public bool IsSet => _mres.WaitOne(0); } } #endif diff --git a/src/NUnitFramework/framework/Compatibility/System.Threading/SpinWait.cs b/src/NUnitFramework/framework/Compatibility/System.Threading/SpinWait.cs index fe931dfd86..f6640a6b9e 100644 --- a/src/NUnitFramework/framework/Compatibility/System.Threading/SpinWait.cs +++ b/src/NUnitFramework/framework/Compatibility/System.Threading/SpinWait.cs @@ -127,7 +127,7 @@ public void SpinOnce() // We prefer to call Thread.Yield first, triggering a SwitchToThread. This // unfortunately doesn't consider all runnable threads on all OS SKUs. In // some cases, it may only consult the runnable threads whose ideal processor - // is the one currently executing code. Thus we oc----ionally issue a call to + // is the one currently executing code. Thus we occasionally issue a call to // Sleep(0), which considers all runnable threads at equal priority. Even this // is insufficient since we may be spin waiting for lower priority threads to // execute; we therefore must call Sleep(1) once in a while too, which considers @@ -142,14 +142,19 @@ public void SpinOnce() { Thread.Sleep(1); } - else // if ((yieldsSoFar % SLEEP_0_EVERY_HOW_MANY_TIMES) == (SLEEP_0_EVERY_HOW_MANY_TIMES - 1)) + else if ((yieldsSoFar % SLEEP_0_EVERY_HOW_MANY_TIMES) == (SLEEP_0_EVERY_HOW_MANY_TIMES - 1)) { Thread.Sleep(0); } - //else - //{ - // Thread.Yield(); - //} + else + { + // Polyfill note: + // Thread.Yield() is not available in the 2.0 CLR, and the native implementation + // is much more nuanced than p/invoking SwitchToThread. + // Replace with Thread.Sleep(1) since Thread.Sleep(0) kills performance by often not actually yielding. + // http://joeduffyblog.com/2006/08/22/priorityinduced-starvation-why-sleep1-is-better-than-sleep0-and-the-windows-balance-set-manager/ + Thread.Sleep(1); + } } else { @@ -283,7 +288,7 @@ public static bool SpinUntil(Func condition, int millisecondsTimeout) /// internal static class PlatformHelper { - private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30000; // How often to refresh the count, in milliseconds. + private const int PROCESSOR_COUNT_REFRESH_INTERVAL_MS = 30_000; // How often to refresh the count, in milliseconds. private static volatile int s_processorCount; // The last count seen. private static volatile int s_lastProcessorCountRefreshTicks; // The last time we refreshed. @@ -317,13 +322,13 @@ internal static bool IsSingleProcessor } /// - /// A helper class to capture a start time using Environment.TickCout as a time in milliseconds, also updates a given timeout bu subtracting the current time from + /// A helper class to capture a start time using Environment.TickCount as a time in milliseconds, also updates a given timeout bu subtracting the current time from /// the start time /// internal static class TimeoutHelper { /// - /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from postive to negative every ~ 25 days + /// Returns the Environment.TickCount as a start time in milliseconds as a uint, TickCount tools over from positive to negative every ~ 25 days /// then ~25 days to back to positive again, uint is sued to ignore the sign and double the range to 50 days /// /// @@ -336,7 +341,7 @@ public static uint GetTime() /// Helper function to measure and update the elapsed time /// /// The first time (in milliseconds) observed when the wait started - /// The orginal wait timeoutout in milliseconds + /// The original wait timeout in milliseconds /// The new wait time in milliseconds, -1 if the time expired public static int UpdateTimeOut(uint startTime, int originalWaitMillisecondsTimeout) { diff --git a/src/NUnitFramework/framework/Compatibility/System/SerializableAttribute.cs b/src/NUnitFramework/framework/Compatibility/System/SerializableAttribute.cs deleted file mode 100644 index a9c2e1aa36..0000000000 --- a/src/NUnitFramework/framework/Compatibility/System/SerializableAttribute.cs +++ /dev/null @@ -1,9 +0,0 @@ -#if !SERIALIZATION -namespace System -{ - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Enum | AttributeTargets.Delegate, Inherited = false)] - internal sealed class SerializableAttribute : Attribute - { - } -} -#endif diff --git a/src/NUnitFramework/framework/Constraints/BinarySerializableConstraint.cs b/src/NUnitFramework/framework/Constraints/BinarySerializableConstraint.cs index 5db5c43f99..bcad9a8570 100644 --- a/src/NUnitFramework/framework/Constraints/BinarySerializableConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/BinarySerializableConstraint.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if SERIALIZATION using System; using System.IO; using System.Runtime.Serialization; @@ -30,7 +29,7 @@ namespace NUnit.Framework.Constraints { /// - /// BinarySerializableConstraint tests whether + /// BinarySerializableConstraint tests whether /// an object is serializable in binary format. /// public class BinarySerializableConstraint : Constraint @@ -84,4 +83,3 @@ protected override string GetStringRepresentation() } } } -#endif diff --git a/src/NUnitFramework/framework/Constraints/CollectionConstraint.cs b/src/NUnitFramework/framework/Constraints/CollectionConstraint.cs index 14b760ce37..30024ed3d2 100644 --- a/src/NUnitFramework/framework/Constraints/CollectionConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/CollectionConstraint.cs @@ -49,7 +49,7 @@ public abstract class CollectionConstraint : Constraint /// /// The enumerable. /// - /// true if the specified enumerable is empty; otherwise, false. + /// if the specified enumerable is empty; otherwise, . /// protected static bool IsEmpty(IEnumerable enumerable) { diff --git a/src/NUnitFramework/framework/Constraints/CollectionEquivalentConstraintResult.cs b/src/NUnitFramework/framework/Constraints/CollectionEquivalentConstraintResult.cs index 6c42243b5c..5dcd9e4dac 100644 --- a/src/NUnitFramework/framework/Constraints/CollectionEquivalentConstraintResult.cs +++ b/src/NUnitFramework/framework/Constraints/CollectionEquivalentConstraintResult.cs @@ -30,10 +30,6 @@ public class CollectionEquivalentConstraintResult : ConstraintResult /// Result of a of the collections to compare for equivalence. private readonly CollectionTally.CollectionTallyResult _tallyResult; - /// Maximum amount of elements to write to the if there are - /// extra/missing elements from the collection. - private const int MaxDifferingElemsToWrite = 10; - /// Construct a using a . /// Source . /// Result of the collection comparison. @@ -54,13 +50,13 @@ public class CollectionEquivalentConstraintResult : ConstraintResult /// Write any additional lines (following Expected: and But was:) for a failing constraint. /// The to write the failure message to. public override void WriteAdditionalLinesTo(MessageWriter writer) - { + { if (_tallyResult.MissingItems.Count > 0) { int missingItemsCount = _tallyResult.MissingItems.Count; string missingStr = $"Missing ({missingItemsCount}): "; - missingStr += MsgUtils.FormatCollection(_tallyResult.MissingItems, 0, MaxDifferingElemsToWrite); + missingStr += MsgUtils.FormatCollection(_tallyResult.MissingItems); writer.WriteMessageLine(missingStr); } @@ -70,7 +66,7 @@ public override void WriteAdditionalLinesTo(MessageWriter writer) int extraItemsCount = _tallyResult.ExtraItems.Count; string extraStr = $"Extra ({extraItemsCount}): "; - extraStr += MsgUtils.FormatCollection(_tallyResult.ExtraItems, 0, MaxDifferingElemsToWrite); + extraStr += MsgUtils.FormatCollection(_tallyResult.ExtraItems); writer.WriteMessageLine(extraStr); } diff --git a/src/NUnitFramework/framework/Constraints/CollectionOrderedConstraint.cs b/src/NUnitFramework/framework/Constraints/CollectionOrderedConstraint.cs index 377185b92a..bf1157db31 100644 --- a/src/NUnitFramework/framework/Constraints/CollectionOrderedConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/CollectionOrderedConstraint.cs @@ -26,7 +26,6 @@ using System.Collections.Generic; using System.Reflection; using System.Text; -using NUnit.Compatibility; using NUnit.Framework.Internal; namespace NUnit.Framework.Constraints @@ -336,8 +335,6 @@ public OrderingStep(string propertyName) private sealed class CollectionOrderedConstraintResult : ConstraintResult { - private const int MaxDisplayedItems = 10; - private readonly int _breakingIndex; private readonly object _breakingValue; @@ -367,8 +364,9 @@ public CollectionOrderedConstraintResult(IConstraint constraint, IEnumerable act public override void WriteActualValueTo(MessageWriter writer) { - int startIndex = Math.Max(0, _breakingIndex - MaxDisplayedItems + 2); - var actualValueMessage = MsgUtils.FormatCollection((IEnumerable)ActualValue, startIndex, MaxDisplayedItems); + // Choose startIndex in such way that '_breakingIndex' is always visible in message. + int startIndex = Math.Max(0, _breakingIndex - MsgUtils.DefaultMaxItems + 2); + var actualValueMessage = MsgUtils.FormatCollection((IEnumerable)ActualValue, startIndex); writer.Write(actualValueMessage); } diff --git a/src/NUnitFramework/framework/Constraints/CollectionSubsetConstraint.cs b/src/NUnitFramework/framework/Constraints/CollectionSubsetConstraint.cs index b6a864afd7..57f0ce8d57 100644 --- a/src/NUnitFramework/framework/Constraints/CollectionSubsetConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/CollectionSubsetConstraint.cs @@ -20,8 +20,13 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** + +#nullable enable + using System; using System.Collections; +using System.Collections.Generic; +using NUnit.Framework.Internal; namespace NUnit.Framework.Constraints { @@ -32,6 +37,7 @@ namespace NUnit.Framework.Constraints public class CollectionSubsetConstraint : CollectionItemsEqualConstraint { private readonly IEnumerable _expected; + private List? _extraItems; /// /// Construct a CollectionSubsetConstraint @@ -70,9 +76,20 @@ protected override bool Matches(IEnumerable actual) CollectionTally tally = Tally(_expected); tally.TryRemove(actual); - return tally.Result.ExtraItems.Count == 0; + _extraItems = tally.Result.ExtraItems; + + return _extraItems.Count == 0; } + /// + /// Test whether the constraint is satisfied by a given value. + /// + public override ConstraintResult ApplyTo(TActual actual) + { + IEnumerable enumerable = ConstraintUtils.RequireActual(actual, nameof(actual)); + bool matches = Matches(enumerable); + return new CollectionSubsetConstraintResult(this, actual, matches, _extraItems); + } /// /// Flag the constraint to use the supplied predicate function @@ -84,5 +101,31 @@ protected override bool Matches(IEnumerable actual) base.Using(EqualityAdapter.For(comparison)); return this; } + + #region Private CollectionSubsetConstraintResult Class + + private sealed class CollectionSubsetConstraintResult : ConstraintResult + { + private readonly List? _extraItems; + + public CollectionSubsetConstraintResult(IConstraint constraint, object actualValue, bool isSuccess, List? extraItems) + : base(constraint, actualValue, isSuccess) + { + _extraItems = extraItems; + } + + public override void WriteAdditionalLinesTo(MessageWriter writer) + { + if (_extraItems?.Count > 0) + { + string extraItemsMessage = "Extra items: "; + extraItemsMessage += MsgUtils.FormatCollection(_extraItems); + + writer.WriteMessageLine(extraItemsMessage); + } + } + } + + #endregion } -} \ No newline at end of file +} diff --git a/src/NUnitFramework/framework/Constraints/CollectionSupersetConstraint.cs b/src/NUnitFramework/framework/Constraints/CollectionSupersetConstraint.cs index 9de23fbd0e..b182c222f0 100644 --- a/src/NUnitFramework/framework/Constraints/CollectionSupersetConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/CollectionSupersetConstraint.cs @@ -21,8 +21,12 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; +using System.Collections.Generic; +using NUnit.Framework.Internal; namespace NUnit.Framework.Constraints { @@ -33,6 +37,7 @@ namespace NUnit.Framework.Constraints public class CollectionSupersetConstraint : CollectionItemsEqualConstraint { private readonly IEnumerable _expected; + private List? _missingItems; /// /// Construct a CollectionSupersetConstraint @@ -69,10 +74,24 @@ public override string Description /// protected override bool Matches(IEnumerable actual) { + // Create tally from 'actual' collection, and remove '_expected'. + // ExtraItems from tally would be missing items for '_expected' collection. CollectionTally tally = Tally(actual); tally.TryRemove(_expected); - return tally.Result.ExtraItems.Count == 0; + _missingItems = tally.Result.ExtraItems; + + return _missingItems.Count == 0; + } + + /// + /// Test whether the constraint is satisfied by a given value. + /// + public override ConstraintResult ApplyTo(TActual actual) + { + IEnumerable enumerable = ConstraintUtils.RequireActual(actual, nameof(actual)); + bool matches = Matches(enumerable); + return new CollectionSupersetConstraintResult(this, actual, matches, _missingItems); } /// @@ -87,5 +106,31 @@ protected override bool Matches(IEnumerable actual) base.Using(EqualityAdapter.For(invertedComparison)); return this; } + + #region Private CollectionSupersetConstraintResult Class + + private sealed class CollectionSupersetConstraintResult : ConstraintResult + { + private readonly List? _missingItems; + + public CollectionSupersetConstraintResult(IConstraint constraint, object actualValue, bool isSuccess, List? missingItems) + : base(constraint, actualValue, isSuccess) + { + _missingItems = missingItems; + } + + public override void WriteAdditionalLinesTo(MessageWriter writer) + { + if (_missingItems?.Count > 0) + { + string missingItemsMessage = "Missing items: "; + missingItemsMessage += MsgUtils.FormatCollection(_missingItems); + + writer.WriteMessageLine(missingItemsMessage); + } + } + } + + #endregion } -} \ No newline at end of file +} diff --git a/src/NUnitFramework/framework/Constraints/Comparers/ArraysComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/ArraysComparer.cs index 6847720c98..1ce41ef954 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/ArraysComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/ArraysComparer.cs @@ -39,7 +39,7 @@ internal ArraysComparer(NUnitEqualityComparer equalityComparer, EnumerablesCompa _enumerablesComparer = enumerablesComparer; } - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { if (!x.GetType().IsArray || !y.GetType().IsArray || _equalityComparer.CompareAsCollection) return null; @@ -56,7 +56,7 @@ internal ArraysComparer(NUnitEqualityComparer equalityComparer, EnumerablesCompa if (xArray.GetLength(r) != yArray.GetLength(r)) return false; - return _enumerablesComparer.Equal(xArray, yArray, ref tolerance); + return _enumerablesComparer.Equal(xArray, yArray, ref tolerance, state); } } } diff --git a/src/NUnitFramework/framework/Constraints/Comparers/CharsComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/CharsComparer.cs index 277b46753a..54feb83333 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/CharsComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/CharsComparer.cs @@ -37,7 +37,7 @@ internal CharsComparer(NUnitEqualityComparer equalityComparer) _equalityComparer = equalityComparer; } - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { if (!(x is char) || !(y is char)) return null; diff --git a/src/NUnitFramework/framework/Constraints/Comparers/ComparisonState.cs b/src/NUnitFramework/framework/Constraints/Comparers/ComparisonState.cs new file mode 100644 index 0000000000..38230e6b29 --- /dev/null +++ b/src/NUnitFramework/framework/Constraints/Comparers/ComparisonState.cs @@ -0,0 +1,80 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using NUnit.Framework.Internal; + +namespace NUnit.Framework.Constraints.Comparers +{ + internal readonly ref struct ComparisonState + { + /// + /// Flag indicating whether or not this is the top level comparison. + /// + public readonly bool TopLevelComparison { get; } + + /// + /// A list of tracked comparisons + /// + private readonly ImmutableStack _comparisons; + + public ComparisonState(bool topLevelComparison) + : this(topLevelComparison, ImmutableStack.Empty) + { + } + + private ComparisonState(bool topLevelComparison, ImmutableStack comparisons) + { + TopLevelComparison = topLevelComparison; + _comparisons = comparisons; + } + + public ComparisonState PushComparison(object x, object y) + { + return new ComparisonState( + false, + _comparisons.Push(new Comparison(x, y)) + ); + } + + public bool DidCompare(object x, object y) + { + foreach (var comparison in _comparisons) + if (ReferenceEquals(comparison.X, x) && ReferenceEquals(comparison.Y, y)) + return true; + + return false; + } + + private readonly struct Comparison + { + public readonly object X { get; } + public readonly object Y { get; } + + public Comparison(object x, object y) + { + X = x; + Y = y; + } + } + } +} diff --git a/src/NUnitFramework/framework/Constraints/Comparers/DateTimeOffsetsComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/DateTimeOffsetsComparer.cs index ff46b970e0..3f21788b39 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/DateTimeOffsetsComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/DateTimeOffsetsComparer.cs @@ -37,7 +37,7 @@ internal DateTimeOffsetsComparer(NUnitEqualityComparer equalityComparer) _equalityComparer = equalityComparer; } - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { if (!(x is DateTimeOffset) || !(y is DateTimeOffset)) return null; diff --git a/src/NUnitFramework/framework/Constraints/Comparers/DictionariesComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/DictionariesComparer.cs index 7bedc8e53c..632fe58b55 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/DictionariesComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/DictionariesComparer.cs @@ -37,7 +37,7 @@ internal DictionariesComparer(NUnitEqualityComparer equalityComparer) _equalityComparer = equalityComparer; } - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { if (!(x is IDictionary) || !(y is IDictionary)) return null; @@ -54,7 +54,7 @@ internal DictionariesComparer(NUnitEqualityComparer equalityComparer) return false; foreach (object key in xDictionary.Keys) - if (!_equalityComparer.AreEqual(xDictionary[key], yDictionary[key], ref tolerance, topLevelComparison: false)) + if (!_equalityComparer.AreEqual(xDictionary[key], yDictionary[key], ref tolerance, state.PushComparison(x, y))) return false; return true; diff --git a/src/NUnitFramework/framework/Constraints/Comparers/DictionaryEntriesComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/DictionaryEntriesComparer.cs index f123cd5060..f7ac540b2d 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/DictionaryEntriesComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/DictionaryEntriesComparer.cs @@ -37,7 +37,7 @@ internal DictionaryEntriesComparer(NUnitEqualityComparer equalityComparer) _equalityComparer = equalityComparer; } - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { // Issue #70 - EquivalentTo isn't compatible with IgnoreCase for dictionaries if (!(x is DictionaryEntry) || !(y is DictionaryEntry)) @@ -47,8 +47,8 @@ internal DictionaryEntriesComparer(NUnitEqualityComparer equalityComparer) DictionaryEntry yDictionaryEntry = (DictionaryEntry)y; var keyTolerance = Tolerance.Exact; - return _equalityComparer.AreEqual(xDictionaryEntry.Key, yDictionaryEntry.Key, ref keyTolerance, false) - && _equalityComparer.AreEqual(xDictionaryEntry.Value, yDictionaryEntry.Value, ref tolerance, false); + return _equalityComparer.AreEqual(xDictionaryEntry.Key, yDictionaryEntry.Key, ref keyTolerance, state.PushComparison(x, y)) + && _equalityComparer.AreEqual(xDictionaryEntry.Value, yDictionaryEntry.Value, ref tolerance, state.PushComparison(x, y)); } } } diff --git a/src/NUnitFramework/framework/Constraints/Comparers/DirectoriesComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/DirectoriesComparer.cs index 59dbb3a846..6acf6e1a06 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/DirectoriesComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/DirectoriesComparer.cs @@ -30,7 +30,7 @@ namespace NUnit.Framework.Constraints.Comparers /// internal sealed class DirectoriesComparer : IChainComparer { - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { if (!(x is DirectoryInfo) || !(y is DirectoryInfo)) return null; diff --git a/src/NUnitFramework/framework/Constraints/Comparers/EnumerablesComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/EnumerablesComparer.cs index 06e698b15a..4a69bfef1a 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/EnumerablesComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/EnumerablesComparer.cs @@ -38,55 +38,42 @@ internal EnumerablesComparer(NUnitEqualityComparer equalityComparer) _equalityComparer = equalityComparer; } - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { - if (!(x is IEnumerable) || !(y is IEnumerable)) + if (!(x is IEnumerable xIEnumerable) || !(y is IEnumerable yIEnumerable)) return null; - IEnumerable xIEnumerable = (IEnumerable)x; - IEnumerable yIEnumerable = (IEnumerable)y; - - IEnumerator expectedEnum = null; - IEnumerator actualEnum = null; - - try + var expectedEnum = xIEnumerable.GetEnumerator(); + using (expectedEnum as IDisposable) { - expectedEnum = xIEnumerable.GetEnumerator(); - actualEnum = yIEnumerable.GetEnumerator(); - - int count; - for (count = 0; ; count++) + var actualEnum = yIEnumerable.GetEnumerator(); + using (actualEnum as IDisposable) { - bool expectedHasData = expectedEnum.MoveNext(); - bool actualHasData = actualEnum.MoveNext(); - - if (!expectedHasData && !actualHasData) - return true; - - if (expectedHasData != actualHasData || - !_equalityComparer.AreEqual(expectedEnum.Current, actualEnum.Current, ref tolerance, topLevelComparison: false)) + for (int count = 0; ; count++) { - NUnitEqualityComparer.FailurePoint fp = new NUnitEqualityComparer.FailurePoint(); - fp.Position = count; - fp.ExpectedHasData = expectedHasData; - if (expectedHasData) - fp.ExpectedValue = expectedEnum.Current; - fp.ActualHasData = actualHasData; - if (actualHasData) - fp.ActualValue = actualEnum.Current; - _equalityComparer.FailurePoints.Insert(0, fp); - return false; + bool expectedHasData = expectedEnum.MoveNext(); + bool actualHasData = actualEnum.MoveNext(); + + if (!expectedHasData && !actualHasData) + return true; + + if (expectedHasData != actualHasData || + !_equalityComparer.AreEqual(expectedEnum.Current, actualEnum.Current, ref tolerance, state.PushComparison(x, y))) + { + NUnitEqualityComparer.FailurePoint fp = new NUnitEqualityComparer.FailurePoint(); + fp.Position = count; + fp.ExpectedHasData = expectedHasData; + if (expectedHasData) + fp.ExpectedValue = expectedEnum.Current; + fp.ActualHasData = actualHasData; + if (actualHasData) + fp.ActualValue = actualEnum.Current; + _equalityComparer.FailurePoints.Insert(0, fp); + return false; + } } } } - finally - { - var expectedDisposable = expectedEnum as IDisposable; - if (expectedDisposable != null) expectedDisposable.Dispose(); - - var actualDisposable = actualEnum as IDisposable; - if (actualDisposable != null) actualDisposable.Dispose(); - } } } } diff --git a/src/NUnitFramework/framework/Constraints/Comparers/EquatablesComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/EquatablesComparer.cs index fe211a81b8..39dbe68761 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/EquatablesComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/EquatablesComparer.cs @@ -22,9 +22,7 @@ // *********************************************************************** using System; -using System.Collections.Generic; using System.Reflection; -using NUnit.Compatibility; namespace NUnit.Framework.Constraints.Comparers { @@ -40,9 +38,9 @@ internal EquatablesComparer(NUnitEqualityComparer equalityComparer) _equalityComparer = equalityComparer; } - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { - if (_equalityComparer.CompareAsCollection && topLevelComparison) + if (_equalityComparer.CompareAsCollection && state.TopLevelComparison) return null; Type xType = x.GetType(); @@ -61,35 +59,53 @@ internal EquatablesComparer(NUnitEqualityComparer equalityComparer) private static MethodInfo FirstImplementsIEquatableOfSecond(Type first, Type second) { - var pair = new KeyValuePair(); + var mostDerived = default(EquatableMethodImpl); - foreach (var xEquatableArgument in GetEquatableGenericArguments(first)) - if (xEquatableArgument.Key.IsAssignableFrom(second)) - if (pair.Key == null || pair.Key.IsAssignableFrom(xEquatableArgument.Key)) - pair = xEquatableArgument; + foreach (var implementation in GetEquatableImplementations(first)) + { + if (implementation.Argument.IsAssignableFrom(second)) + { + if (mostDerived.Argument == null || mostDerived.Argument.IsAssignableFrom(implementation.Argument)) + mostDerived = implementation; + } + } - return pair.Value; + return mostDerived.Method; } - private static IList> GetEquatableGenericArguments(Type type) + private static EquatableMethodImpl[] GetEquatableImplementations(Type type) { - var genericArgs = new List>(); + static bool IsIEquatableOfT(Type t, object filter) => t.IsGenericType && t.GetGenericTypeDefinition().Equals(typeof(IEquatable<>)); + + var interfaces = type.FindInterfaces((t,f) => IsIEquatableOfT(t,f), string.Empty); + var implementations = new EquatableMethodImpl[interfaces.Length]; - foreach (Type @interface in type.GetInterfaces()) + for(var i = 0; i < interfaces.Length; i++) { - if (@interface.GetTypeInfo().IsGenericType && @interface.GetGenericTypeDefinition().Equals(typeof(IEquatable<>))) - { - genericArgs.Add(new KeyValuePair( - @interface.GetGenericArguments()[0], @interface.GetMethod("Equals"))); - } + var iMap = type.GetInterfaceMap(interfaces[i]); + var method = iMap.TargetMethods[0]; + + implementations[i] = new EquatableMethodImpl(method, method.GetParameters()[0].ParameterType); } - return genericArgs; + return implementations; } private static bool InvokeFirstIEquatableEqualsSecond(object first, object second, MethodInfo equals) { return equals != null ? (bool)equals.Invoke(first, new object[] { second }) : false; } + + private readonly struct EquatableMethodImpl + { + public readonly MethodInfo Method { get; } + public readonly Type Argument { get; } + + public EquatableMethodImpl(MethodInfo method, Type arg) + { + Method = method; + Argument = arg; + } + } } } diff --git a/src/NUnitFramework/framework/Constraints/Comparers/IChainComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/IChainComparer.cs index 7c9bb7217d..2f4857c7f9 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/IChainComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/IChainComparer.cs @@ -36,11 +36,11 @@ internal interface IChainComparer /// The first object to compare. /// The second object to compare. /// The tolerance to use when comparing the objects. - /// Flag indicating whether or not this is the top level comparison. + /// The evaluation state of the comparison. /// - /// null if the objects cannot be compared using the method. + /// if the objects cannot be compared using the method. /// Otherwise the result of the comparison is returned. /// - bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true); + bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state); } } diff --git a/src/NUnitFramework/framework/Constraints/Comparers/KeyValuePairsComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/KeyValuePairsComparer.cs index 2827a39248..71a0de8def 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/KeyValuePairsComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/KeyValuePairsComparer.cs @@ -40,7 +40,7 @@ internal KeyValuePairsComparer(NUnitEqualityComparer equalityComparer) _equalityComparer = equalityComparer; } - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { // IDictionary<,> will eventually try to compare its key value pairs when using CollectionTally Type xType = x.GetType(); @@ -59,8 +59,8 @@ internal KeyValuePairsComparer(NUnitEqualityComparer equalityComparer) object xValue = xType.GetProperty("Value").GetValue(x, null); object yValue = yType.GetProperty("Value").GetValue(y, null); - return _equalityComparer.AreEqual(xKey, yKey, ref keyTolerance, false) - && _equalityComparer.AreEqual(xValue, yValue, ref tolerance, false); + return _equalityComparer.AreEqual(xKey, yKey, ref keyTolerance, state.PushComparison(x, y)) + && _equalityComparer.AreEqual(xValue, yValue, ref tolerance, state.PushComparison(x, y)); } } } diff --git a/src/NUnitFramework/framework/Constraints/Comparers/NumericsComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/NumericsComparer.cs index 28324df7e5..ba40bfbac7 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/NumericsComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/NumericsComparer.cs @@ -28,7 +28,7 @@ namespace NUnit.Framework.Constraints.Comparers /// internal sealed class NumericsComparer : IChainComparer { - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { if (!Numerics.IsNumericType(x) || !Numerics.IsNumericType(y)) return null; diff --git a/src/NUnitFramework/framework/Constraints/Comparers/StreamsComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/StreamsComparer.cs index e53dc26de7..3a44f1136c 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/StreamsComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/StreamsComparer.cs @@ -40,7 +40,7 @@ internal StreamsComparer(NUnitEqualityComparer equalityComparer) _equalityComparer = equalityComparer; } - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { if (!(x is Stream) || !(y is Stream)) return null; diff --git a/src/NUnitFramework/framework/Constraints/Comparers/StringsComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/StringsComparer.cs index 345fc29df0..63f366f025 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/StringsComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/StringsComparer.cs @@ -37,7 +37,7 @@ internal StringsComparer(NUnitEqualityComparer equalityComparer) _equalityComparer = equalityComparer; } - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { if (!(x is string) || !(y is string)) return null; diff --git a/src/NUnitFramework/framework/Constraints/Comparers/StructuralComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/StructuralComparer.cs new file mode 100644 index 0000000000..290f6a0d07 --- /dev/null +++ b/src/NUnitFramework/framework/Constraints/Comparers/StructuralComparer.cs @@ -0,0 +1,93 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +#if !NET35 +using System.Collections; + +namespace NUnit.Framework.Constraints.Comparers +{ + /// + /// Comparator for two types related by . + /// + class StructuralComparer : IChainComparer + { + private readonly NUnitEqualityComparer _equalityComparer; + + internal StructuralComparer(NUnitEqualityComparer equalityComparer) + { + _equalityComparer = equalityComparer; + } + + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) + { + if (_equalityComparer.CompareAsCollection && state.TopLevelComparison) + return null; + + if (x is IStructuralEquatable xEquatable && y is IStructuralEquatable yEquatable) + { + // We can't pass tolerance as a ref so we pass by value and reassign later + var equalityComparison = new NUnitEqualityComparison(_equalityComparer, tolerance); + + // Check both directions in case they are different implementations, and only one is aware of the other. + // Like how ImmutableArray is structurally equatable to int[] + // but int[] is NOT structurally equatable to ImmutableArray + var xResult = xEquatable.Equals(y, equalityComparison); + var yResult = yEquatable.Equals(x, equalityComparison); + + // Keep all the refs up to date + tolerance = equalityComparison.Tolerance; + + return xResult || yResult; + } + + return null; + } + + private sealed class NUnitEqualityComparison : IEqualityComparer + { + private readonly NUnitEqualityComparer _comparer; + + private Tolerance _tolerance; + public Tolerance Tolerance { get { return _tolerance; } } + + public NUnitEqualityComparison(NUnitEqualityComparer comparer, Tolerance tolerance) + { + _comparer = comparer; + _tolerance = tolerance; + } + + public new bool Equals(object x, object y) + { + return _comparer.AreEqual(x, y, ref _tolerance); + } + + public int GetHashCode(object obj) + { + // TODO: Better hashcode generation, likely based on the corresponding comparer to ensure types which can be + // compared with each other end up in the same bucket + return 0; + } + } + } +} +#endif diff --git a/src/NUnitFramework/framework/Constraints/Comparers/TimeSpanToleranceComparer.cs b/src/NUnitFramework/framework/Constraints/Comparers/TimeSpanToleranceComparer.cs index 8a879492e1..9b51a1b431 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/TimeSpanToleranceComparer.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/TimeSpanToleranceComparer.cs @@ -30,7 +30,7 @@ namespace NUnit.Framework.Constraints.Comparers /// internal sealed class TimeSpanToleranceComparer : IChainComparer { - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { if (tolerance == null || !(tolerance.Amount is TimeSpan)) return null; diff --git a/src/NUnitFramework/framework/Constraints/Comparers/TupleComparerBase.cs b/src/NUnitFramework/framework/Constraints/Comparers/TupleComparerBase.cs index 26c1fe1014..9ba010ef7a 100644 --- a/src/NUnitFramework/framework/Constraints/Comparers/TupleComparerBase.cs +++ b/src/NUnitFramework/framework/Constraints/Comparers/TupleComparerBase.cs @@ -43,7 +43,7 @@ internal TupleComparerBase(NUnitEqualityComparer equalityComparer) protected abstract object GetValue(Type type, string propertyName, object obj); - public bool? Equal(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool? Equal(object x, object y, ref Tolerance tolerance, ComparisonState state) { Type xType = x.GetType(); Type yType = y.GetType(); @@ -62,7 +62,7 @@ internal TupleComparerBase(NUnitEqualityComparer equalityComparer) object xItem = GetValue(xType, propertyName, x); object yItem = GetValue(yType, propertyName, y); - bool comparison = _equalityComparer.AreEqual(xItem, yItem, ref tolerance, false); + bool comparison = _equalityComparer.AreEqual(xItem, yItem, ref tolerance, state.PushComparison(x, y)); if (!comparison) return false; } diff --git a/src/NUnitFramework/framework/Constraints/ComparisonAdapter.cs b/src/NUnitFramework/framework/Constraints/ComparisonAdapter.cs index 4c70066de7..9afa8d2ee7 100644 --- a/src/NUnitFramework/framework/Constraints/ComparisonAdapter.cs +++ b/src/NUnitFramework/framework/Constraints/ComparisonAdapter.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -130,12 +130,10 @@ public ComparerAdapter(IComparer comparer) /// public override int Compare(object expected, object actual) { - T expectedCast; - if (!TypeHelper.TryCast(expected, out expectedCast)) + if (!TypeHelper.TryCast(expected, out T expectedCast)) throw new ArgumentException($"Cannot compare {expected?.ToString() ?? "null"}"); - T actualCast; - if (!TypeHelper.TryCast(actual, out actualCast)) + if (!TypeHelper.TryCast(actual, out T actualCast)) throw new ArgumentException($"Cannot compare to {actual?.ToString() ?? "null"}"); return comparer.Compare(expectedCast, actualCast); @@ -159,15 +157,13 @@ public ComparisonAdapterForComparison(Comparison comparer) /// public override int Compare(object expected, object actual) { - T expectedCast; - if (!TypeHelper.TryCast(expected, out expectedCast)) + if (!TypeHelper.TryCast(expected, out T expectedCast)) throw new ArgumentException($"Cannot compare {expected?.ToString() ?? "null"}"); - T actualCast; - if (!TypeHelper.TryCast(actual, out actualCast)) + if (!TypeHelper.TryCast(actual, out T actualCast)) throw new ArgumentException($"Cannot compare to {actual?.ToString() ?? "null"}"); - return comparison.Invoke((T)expected, (T)actual); + return comparison.Invoke(expectedCast, actualCast); } } } diff --git a/src/NUnitFramework/framework/Constraints/ConstraintBuilder.cs b/src/NUnitFramework/framework/Constraints/ConstraintBuilder.cs index 91ae5a354b..7d4e15034d 100644 --- a/src/NUnitFramework/framework/Constraints/ConstraintBuilder.cs +++ b/src/NUnitFramework/framework/Constraints/ConstraintBuilder.cs @@ -56,7 +56,7 @@ public OperatorStack(ConstraintBuilder builder) /// /// Gets a value indicating whether this is empty. /// - /// true if empty; otherwise, false. + /// if empty; otherwise, . public bool Empty { get { return stack.Count == 0; } @@ -113,7 +113,7 @@ public ConstraintStack(ConstraintBuilder builder) /// /// Gets a value indicating whether this is empty. /// - /// true if empty; otherwise, false. + /// if empty; otherwise, . public bool Empty { get { return stack.Count == 0; } @@ -270,7 +270,7 @@ public IConstraint Resolve() /// Gets a value indicating whether this instance is resolvable. /// /// - /// true if this instance is resolvable; otherwise, false. + /// if this instance is resolvable; otherwise, . /// private bool IsResolvable { diff --git a/src/NUnitFramework/framework/Constraints/ConstraintExpression.cs b/src/NUnitFramework/framework/Constraints/ConstraintExpression.cs index ca7baaf411..ddb5f1fdd7 100644 --- a/src/NUnitFramework/framework/Constraints/ConstraintExpression.cs +++ b/src/NUnitFramework/framework/Constraints/ConstraintExpression.cs @@ -23,6 +23,7 @@ using System; using System.Collections; +using System.Text.RegularExpressions; namespace NUnit.Framework.Constraints { @@ -459,8 +460,6 @@ public UniqueItemsConstraint Unique #endregion -#if SERIALIZATION - /// /// Returns a constraint that tests whether an object graph is serializable in binary format. /// @@ -477,8 +476,6 @@ public XmlSerializableConstraint XmlSerializable get { return (XmlSerializableConstraint)this.Append(new XmlSerializableConstraint()); } } -#endif - #region EqualTo /// @@ -903,6 +900,15 @@ public RegexConstraint Match(string pattern) { return (RegexConstraint)this.Append(new RegexConstraint(pattern)); } + + /// + /// Returns a constraint that succeeds if the actual + /// value matches the regular expression supplied as an argument. + /// + public RegexConstraint Match(Regex regex) + { + return (RegexConstraint)this.Append(new RegexConstraint(regex)); + } /// /// Returns a constraint that succeeds if the actual @@ -912,6 +918,16 @@ public RegexConstraint Matches(string pattern) { return (RegexConstraint)this.Append(new RegexConstraint(pattern)); } + + /// + /// Returns a constraint that succeeds if the actual + /// value matches the regular expression supplied as an argument. + /// + public RegexConstraint Matches(Regex regex) + { + return (RegexConstraint)this.Append(new RegexConstraint(regex)); + } + /// /// Returns a constraint that succeeds if the actual @@ -1008,5 +1024,19 @@ public AnyOfConstraint AnyOf(params object[] expected) } #endregion + + #region ItemAt + + /// + /// Returns a new IndexerConstraintExpression, which will + /// apply any following constraint to that indexer value. + /// + /// Index accessor values. + public ConstraintExpression ItemAt(params object[] indexArgs) + { + return this.Append(new IndexerOperator(indexArgs)); + } + + #endregion } } diff --git a/src/NUnitFramework/framework/Constraints/DelayedConstraint.cs b/src/NUnitFramework/framework/Constraints/DelayedConstraint.cs index 2932a90817..b30aeb3bf6 100644 --- a/src/NUnitFramework/framework/Constraints/DelayedConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/DelayedConstraint.cs @@ -258,13 +258,13 @@ public override ConstraintResult ApplyTo(TActual actual) ConstraintResult result = BaseConstraint.ApplyTo(actual); if (result.IsSuccess) - return new ConstraintResult(this, actual, true); + return new DelegatingConstraintResult(this, result); } } if ((now = Stopwatch.GetTimestamp()) < delayEnd) ThreadUtility.BlockingDelay((int)TimestampDiff(delayEnd, now).TotalMilliseconds); - return new ConstraintResult(this, actual, BaseConstraint.ApplyTo(actual).IsSuccess); + return new DelegatingConstraintResult(this, BaseConstraint.ApplyTo(actual)); } /// @@ -293,7 +293,7 @@ public override ConstraintResult ApplyTo(ActualValueDelegate d { ConstraintResult result = BaseConstraint.ApplyTo(actual); if (result.IsSuccess) - return new ConstraintResult(this, actual, true); + return new DelegatingConstraintResult(this, result); } catch (Exception) { @@ -305,7 +305,7 @@ public override ConstraintResult ApplyTo(ActualValueDelegate d ThreadUtility.BlockingDelay((int)TimestampDiff(delayEnd, now).TotalMilliseconds); actual = InvokeDelegate(del); - return new ConstraintResult(this, actual, BaseConstraint.ApplyTo(actual).IsSuccess); + return new DelegatingConstraintResult(this, BaseConstraint.ApplyTo(actual)); } /// @@ -333,7 +333,7 @@ public override ConstraintResult ApplyTo(ref TActual actual) { ConstraintResult result = BaseConstraint.ApplyTo(actual); if (result.IsSuccess) - return new ConstraintResult(this, actual, true); + return new DelegatingConstraintResult(this, result); } catch (Exception) { @@ -344,7 +344,7 @@ public override ConstraintResult ApplyTo(ref TActual actual) if ((now = Stopwatch.GetTimestamp()) < delayEnd) ThreadUtility.BlockingDelay((int)TimestampDiff(delayEnd, now).TotalMilliseconds); - return new ConstraintResult(this, actual, BaseConstraint.ApplyTo(actual).IsSuccess); + return new DelegatingConstraintResult(this, BaseConstraint.ApplyTo(actual)); } private static object InvokeDelegate(ActualValueDelegate del) @@ -384,5 +384,20 @@ private static TimeSpan TimestampDiff(long timestamp1, long timestamp2) { return TimeSpan.FromSeconds((double)(timestamp1 - timestamp2) / Stopwatch.Frequency); } + + private class DelegatingConstraintResult : ConstraintResult + { + private readonly ConstraintResult _innerResult; + + public DelegatingConstraintResult(IConstraint constraint, ConstraintResult innerResult) + : base(constraint, innerResult.ActualValue, innerResult.Status) + { + _innerResult = innerResult; + } + + public override void WriteActualValueTo(MessageWriter writer) => _innerResult.WriteActualValueTo(writer); + + public override void WriteAdditionalLinesTo(MessageWriter writer) => _innerResult.WriteAdditionalLinesTo(writer); + } } } diff --git a/src/NUnitFramework/framework/Constraints/DictionaryContainsKeyConstraint.cs b/src/NUnitFramework/framework/Constraints/DictionaryContainsKeyConstraint.cs index 43a9382aa5..92b14cce37 100644 --- a/src/NUnitFramework/framework/Constraints/DictionaryContainsKeyConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/DictionaryContainsKeyConstraint.cs @@ -76,6 +76,24 @@ public override string Description /// protected object Expected { get; } + /// + /// Returns a new DictionaryContainsKeyValuePairConstraint checking for the + /// presence of a particular key-value-pair in the dictionary. + /// + public DictionaryContainsKeyValuePairConstraint WithValue(object expectedValue) + { + var builder = this.Builder; + if (builder == null) + { + builder = new ConstraintBuilder(); + builder.Append(this); + } + + var constraint = new DictionaryContainsKeyValuePairConstraint(Expected, expectedValue); + builder.Append(constraint); + return constraint; + } + /// /// Flag the constraint to ignore case and return self. /// @@ -94,7 +112,7 @@ private bool Matches(object actual) if (_isDeprecatedMode) { var dictionary = ConstraintUtils.RequireActual(actual, nameof(actual)); - foreach (object obj in dictionary.Keys) + foreach (var obj in dictionary.Keys) if (ItemsEqual(obj, Expected)) return true; @@ -231,7 +249,7 @@ private static MethodInfo FindContainsKeyMethod(Type type) var methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Public); var method = methods.FirstOrDefault(m => m.ReturnType == typeof(bool) - && m.Name == "ContainsKey" + && (m.Name == "ContainsKey" || (m.Name == nameof(IDictionary.Contains) && m.DeclaringType == typeof(IDictionary))) && !m.IsGenericMethod && m.GetParameters().Length == 1); @@ -252,14 +270,7 @@ private static MethodInfo FindContainsKeyMethod(Type type) if (method != null) { -#if NETSTANDARD1_4 - method = methods.Single(m => m.Name == method.Name && - m.GetParameters().Length == 1 && - method.GetParameters().Length == 1 && - m.GetParameters()[0].Name == method.GetParameters()[0].Name); -#else method = methods.Single(m => m.MetadataToken == method.MetadataToken); -#endif } } } diff --git a/src/NUnitFramework/framework/Constraints/DictionaryContainsKeyValuePairConstraint.cs b/src/NUnitFramework/framework/Constraints/DictionaryContainsKeyValuePairConstraint.cs new file mode 100644 index 0000000000..cda4e9a53c --- /dev/null +++ b/src/NUnitFramework/framework/Constraints/DictionaryContainsKeyValuePairConstraint.cs @@ -0,0 +1,89 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System.Collections; +using NUnit.Framework.Internal; + +namespace NUnit.Framework.Constraints +{ + /// + /// DictionaryContainsKeyValuePairConstraint is used to test whether a dictionary + /// contains an expected object as a key-value-pair. + /// + public sealed class DictionaryContainsKeyValuePairConstraint : CollectionItemsEqualConstraint + { + private readonly DictionaryEntry _expected; + + /// + /// Construct a DictionaryContainsKeyValuePairConstraint + /// + public DictionaryContainsKeyValuePairConstraint(object key, object value) + { + _expected = new DictionaryEntry(key, value); + } + + /// + /// The display name of this Constraint for use by ToString(). + /// The default value is the name of the constraint with + /// trailing "Constraint" removed. Derived classes may set + /// this to another name in their constructors. + /// + public override string DisplayName { get { return "ContainsKeyValuePair"; } } + + /// + /// The Description of what this constraint tests, for + /// use in messages and in the ConstraintResult. + /// + public override string Description + { + get { return "dictionary containing entry " + MsgUtils.FormatValue(_expected); } + } + + private bool Matches(object actual) + { + var dictionary = ConstraintUtils.RequireActual(actual, nameof(actual)); + foreach (var entry in dictionary) + if (ItemsEqual(entry, _expected)) + return true; + + return false; + } + + /// + /// Test whether the constraint is satisfied by a given value + /// + /// The value to be tested + public override ConstraintResult ApplyTo(TActual actual) + { + return new ConstraintResult(this, actual, Matches(actual)); + } + + /// + /// Test whether the expected key is contained in the dictionary + /// + protected override bool Matches(IEnumerable collection) + { + return Matches(collection); + } + } +} diff --git a/src/NUnitFramework/framework/Constraints/EmptyConstraint.cs b/src/NUnitFramework/framework/Constraints/EmptyConstraint.cs index 464dbaf4ed..98cdaf5b54 100644 --- a/src/NUnitFramework/framework/Constraints/EmptyConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/EmptyConstraint.cs @@ -50,13 +50,15 @@ public override string Description /// True for success, false for failure public override ConstraintResult ApplyTo(TActual actual) { - // NOTE: actual is string will fail for a null typed as string + // NOTE: actual is string will fail for a null typed as string Type actualType = typeof(TActual); if (actualType == typeof(string)) realConstraint = new EmptyStringConstraint(); + else if (actual is Guid || actualType == typeof(Guid?)) + realConstraint = new EmptyGuidConstraint(); else if (actual == null) - throw new System.ArgumentException($"The actual value must be a string, non-null IEnumerable or DirectoryInfo. The value passed was of type {actualType}.", nameof(actual)); + throw new System.ArgumentException($"The actual value must be a string, Guid, non-null IEnumerable or DirectoryInfo. The value passed was of type {actualType}.", nameof(actual)); else if (actual is System.IO.DirectoryInfo) realConstraint = new EmptyDirectoryConstraint(); else diff --git a/src/NUnitFramework/framework/Constraints/EmptyDirectoryConstraint.cs b/src/NUnitFramework/framework/Constraints/EmptyDirectoryConstraint.cs index d5e829ea9f..2119bb92c3 100644 --- a/src/NUnitFramework/framework/Constraints/EmptyDirectoryConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/EmptyDirectoryConstraint.cs @@ -21,8 +21,9 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; using System.IO; +using System.Linq; +using NUnit.Framework.Internal; namespace NUnit.Framework.Constraints { @@ -31,9 +32,6 @@ namespace NUnit.Framework.Constraints /// public class EmptyDirectoryConstraint : Constraint { - private int files = 0; - private int subdirs = 0; - /// /// The Description of what this constraint tests, for /// use in messages and in the ConstraintResult. @@ -50,12 +48,14 @@ public override string Description /// True for success, false for failure public override ConstraintResult ApplyTo(TActual actual) { - DirectoryInfo dirInfo = actual as DirectoryInfo; - if (dirInfo == null) - throw new ArgumentException("The actual value must be a DirectoryInfo", nameof(actual)); - files = dirInfo.GetFiles().Length; - subdirs = dirInfo.GetDirectories().Length; - bool hasSucceeded = files == 0 && subdirs == 0; + var dirInfo = ConstraintUtils.RequireActual(actual, nameof(actual)); + bool hasSucceeded; + +#if !NET35 + hasSucceeded = !dirInfo.EnumerateFileSystemInfos().Any(); +#else + hasSucceeded = !dirInfo.GetFileSystemInfos().Any(); +#endif return new ConstraintResult(this, actual, hasSucceeded); } diff --git a/src/NUnitFramework/framework/Constraints/EmptyGuidConstraint.cs b/src/NUnitFramework/framework/Constraints/EmptyGuidConstraint.cs new file mode 100644 index 0000000000..bf21020844 --- /dev/null +++ b/src/NUnitFramework/framework/Constraints/EmptyGuidConstraint.cs @@ -0,0 +1,52 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; + +namespace NUnit.Framework.Constraints +{ + /// + /// EmptyGuidConstraint tests whether a Guid is empty. + /// + public class EmptyGuidConstraint : Constraint + { + /// + /// The Description of what this constraint tests, for + /// use in messages and in the ConstraintResult. + /// + public override string Description + { + get { return ""; } + } + + /// + /// Test whether the constraint is satisfied by a given value + /// + /// The value to be tested + /// True for success, false for failure + public override ConstraintResult ApplyTo(TActual actual) + { + return new ConstraintResult(this, actual, actual as Guid? == Guid.Empty); + } + } +} \ No newline at end of file diff --git a/src/NUnitFramework/framework/Constraints/EqualConstraint.cs b/src/NUnitFramework/framework/Constraints/EqualConstraint.cs index 3e3acf2283..c8d90d792d 100644 --- a/src/NUnitFramework/framework/Constraints/EqualConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/EqualConstraint.cs @@ -86,7 +86,7 @@ public Tolerance Tolerance /// Gets a value indicating whether to compare case insensitive. /// /// - /// true if comparing case insensitive; otherwise, false. + /// if comparing case insensitive; otherwise, . /// public bool CaseInsensitive { @@ -97,7 +97,7 @@ public bool CaseInsensitive /// Gets a value indicating whether or not to clip strings. /// /// - /// true if set to clip strings otherwise, false. + /// if set to clip strings otherwise, . /// public bool ClipStrings { get; private set; } diff --git a/src/NUnitFramework/framework/Constraints/EqualConstraintResult.cs b/src/NUnitFramework/framework/Constraints/EqualConstraintResult.cs index 2b89096b93..5c877327bd 100644 --- a/src/NUnitFramework/framework/Constraints/EqualConstraintResult.cs +++ b/src/NUnitFramework/framework/Constraints/EqualConstraintResult.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -30,7 +30,7 @@ namespace NUnit.Framework.Constraints { /// /// The EqualConstraintResult class is tailored for formatting - /// and displaying the result of an EqualConstraint. + /// and displaying the result of an EqualConstraint. /// public class EqualConstraintResult : ConstraintResult { @@ -73,7 +73,7 @@ public EqualConstraintResult(EqualConstraint constraint, object actual, bool has } /// - /// Write a failure message. Overridden to provide custom + /// Write a failure message. Overridden to provide custom /// failure messages for EqualConstraint. /// /// The MessageWriter to write to @@ -164,7 +164,7 @@ private void DisplayCollectionDifferences(MessageWriter writer, ICollection expe /// /// Displays a single line showing the types and sizes of the expected - /// and actual collections or arrays. If both are identical, the value is + /// and actual collections or arrays. If both are identical, the value is /// only shown once. /// /// The MessageWriter on which to display @@ -225,23 +225,6 @@ private void DisplayFailurePoint(MessageWriter writer, IEnumerable expected, IEn } } - private static object GetValueFromCollection(ICollection collection, int index) - { - Array array = collection as Array; - - if (array != null && array.Rank > 1) - return array.GetValue(MsgUtils.GetArrayIndicesFromCollectionIndex(array, index)); - - if (collection is IList) - return ((IList)collection)[index]; - - foreach (object obj in collection) - if (--index < 0) - return obj; - - return null; - } - #endregion #region DisplayEnumerableDifferences diff --git a/src/NUnitFramework/framework/Constraints/EqualityAdapter.cs b/src/NUnitFramework/framework/Constraints/EqualityAdapter.cs index 164e4fa90d..def70a0f43 100644 --- a/src/NUnitFramework/framework/Constraints/EqualityAdapter.cs +++ b/src/NUnitFramework/framework/Constraints/EqualityAdapter.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2009 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -49,13 +49,8 @@ public abstract class EqualityAdapter /// public virtual bool CanCompare(object x, object y) { - if (x is string && y is string) - return true; - - if (x is IEnumerable || y is IEnumerable) - return false; - - return true; + return (x is string || !(x is IEnumerable)) && + (y is string || !(y is IEnumerable)); } #region Nested IComparer Adapter @@ -135,7 +130,7 @@ internal sealed class PredicateEqualityAdapter : EqualityAda /// public override bool CanCompare(object x, object y) { - return true; + return TypeHelper.CanCast(x) && TypeHelper.CanCast(y); } /// @@ -200,8 +195,7 @@ public EqualityComparerAdapter(IEqualityComparer comparer) public override bool AreEqual(object x, object y) { - T xValue, yValue; - CastOrThrow(x, y, out xValue, out yValue); + CastOrThrow(x, y, out var xValue, out var yValue); return comparer.Equals(xValue, yValue); } } @@ -232,8 +226,7 @@ public ComparerAdapter(IComparer comparer) public override bool AreEqual(object x, object y) { - T xValue, yValue; - CastOrThrow(x, y, out xValue, out yValue); + CastOrThrow(x, y, out var xValue, out var yValue); return comparer.Compare(xValue, yValue) == 0; } } @@ -261,8 +254,7 @@ public ComparisonAdapter(Comparison comparer) public override bool AreEqual(object x, object y) { - T xValue, yValue; - CastOrThrow(x, y, out xValue, out yValue); + CastOrThrow(x, y, out var xValue, out var yValue); return comparer.Invoke(xValue, yValue) == 0; } } diff --git a/src/NUnitFramework/framework/Constraints/ExactCountConstraint.cs b/src/NUnitFramework/framework/Constraints/ExactCountConstraint.cs index b2c236c258..52aeeecb74 100644 --- a/src/NUnitFramework/framework/Constraints/ExactCountConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/ExactCountConstraint.cs @@ -87,7 +87,7 @@ public override ConstraintResult ApplyTo(TActual actual) // We intentionally add one item too many because we use it to trigger // the ellipsis when we call "MsgUtils.FormatCollection" later on. - if (itemList.Count <= ExactCountConstraintResult.MaxDisplayCount) + if (itemList.Count <= MsgUtils.DefaultMaxItems ) itemList.Add(item); } diff --git a/src/NUnitFramework/framework/Constraints/ExactCountConstraintResult.cs b/src/NUnitFramework/framework/Constraints/ExactCountConstraintResult.cs index 3eb8833c65..4ab1071e16 100644 --- a/src/NUnitFramework/framework/Constraints/ExactCountConstraintResult.cs +++ b/src/NUnitFramework/framework/Constraints/ExactCountConstraintResult.cs @@ -30,11 +30,6 @@ namespace NUnit.Framework.Constraints /// internal sealed class ExactCountConstraintResult : ConstraintResult { - /// - /// The maximum count of list elements that are shown on the constraint result - /// - internal const int MaxDisplayCount = 10; - /// /// The count of matched items of the /// @@ -42,7 +37,6 @@ internal sealed class ExactCountConstraintResult : ConstraintResult /// /// A list with maximum count (+1) of items of the - /// (The maximum count is set in ) /// private readonly ICollection _itemList; @@ -74,7 +68,7 @@ public override void WriteActualValueTo(MessageWriter writer) } writer.Write(_matchCount != 1 ? "{0} items " : "{0} item ", _matchCount); - writer.Write(MsgUtils.FormatCollection(_itemList, 0, MaxDisplayCount)); + writer.Write(MsgUtils.FormatCollection(_itemList)); } } } diff --git a/src/NUnitFramework/framework/Constraints/ExceptionTypeConstraint.cs b/src/NUnitFramework/framework/Constraints/ExceptionTypeConstraint.cs index 2bab4396b5..e972e31ea0 100644 --- a/src/NUnitFramework/framework/Constraints/ExceptionTypeConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/ExceptionTypeConstraint.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -22,6 +22,7 @@ // *********************************************************************** using System; +using NUnit.Framework.Internal; namespace NUnit.Framework.Constraints { @@ -44,10 +45,7 @@ public class ExceptionTypeConstraint : ExactTypeConstraint /// A ConstraintResult public override ConstraintResult ApplyTo(TActual actual) { - Exception castedToException = actual as Exception; - - if (actual != null && castedToException == null) - throw new ArgumentException("Actual value must be an Exception", nameof(actual)); + ConstraintUtils.RequireActual(actual, nameof(actual), allowNull: true); actualType = actual == null ? null : actual.GetType(); @@ -60,8 +58,8 @@ class ExceptionTypeConstraintResult : ConstraintResult private readonly object caughtException; public ExceptionTypeConstraintResult(ExceptionTypeConstraint constraint, object caughtException, Type type, bool matches) - : base(constraint, type, matches) - { + : base(constraint, type, matches) + { this.caughtException = caughtException; } diff --git a/src/NUnitFramework/framework/Constraints/FileOrDirectoryExistsConstraint.cs b/src/NUnitFramework/framework/Constraints/FileOrDirectoryExistsConstraint.cs index 42dc6cbefd..ddf81e5e45 100644 --- a/src/NUnitFramework/framework/Constraints/FileOrDirectoryExistsConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/FileOrDirectoryExistsConstraint.cs @@ -68,7 +68,7 @@ public FileOrDirectoryExistsConstraint IgnoreFiles /// Initializes a new instance of the class that /// will only check files if ignoreDirectories is true. /// - /// if set to true [ignore directories]. + /// if set to [ignore directories]. public FileOrDirectoryExistsConstraint(bool ignoreDirectories) { _ignoreDirectories = ignoreDirectories; @@ -103,12 +103,13 @@ public override string Description /// A ConstraintResult public override ConstraintResult ApplyTo(TActual actual) { - if(actual == null) + if (actual == null) throw new ArgumentNullException(nameof(actual), "The actual value must be a non-null string" + ErrorSubstring); - if(actual is string) + var stringValue = actual as string; + if (stringValue != null) { - return CheckString(actual); + return CheckString(stringValue); } var fileInfo = actual as FileInfo; @@ -125,18 +126,17 @@ public override ConstraintResult ApplyTo(TActual actual) throw new ArgumentException("The actual value must be a string" + ErrorSubstring, nameof(actual)); } - private ConstraintResult CheckString(TActual actual) + private ConstraintResult CheckString(string actual) { - var str = actual as string; - if (String.IsNullOrEmpty(str)) + if (string.IsNullOrEmpty(actual)) throw new ArgumentException("The actual value cannot be an empty string", nameof(actual)); - var fileInfo = new FileInfo(str); + var fileInfo = new FileInfo(actual); if (_ignoreDirectories && !_ignoreFiles) { return new ConstraintResult(this, actual, fileInfo.Exists); } - var directoryInfo = new DirectoryInfo(str); + var directoryInfo = new DirectoryInfo(actual); if (_ignoreFiles && !_ignoreDirectories) { return new ConstraintResult(this, actual, directoryInfo.Exists); diff --git a/src/NUnitFramework/framework/Constraints/FloatingPointNumerics.cs b/src/NUnitFramework/framework/Constraints/FloatingPointNumerics.cs index d653fd5cce..821b86c6fc 100644 --- a/src/NUnitFramework/framework/Constraints/FloatingPointNumerics.cs +++ b/src/NUnitFramework/framework/Constraints/FloatingPointNumerics.cs @@ -132,6 +132,13 @@ public static bool AreAlmostEqualUlps(float left, float right, int maxUlps) if (leftSignMask != rightSignMask) // Overflow possible, check each against zero { + // This check is specifically used to trap the case of 0 == -0 + // In IEEE floating point maths, -0 is converted to Float.MinValue, which cannot be used with + // Math.Abs(...) below due to overflow issues. This should only match the 0 == -0 condition. + if (left == right) + { + return true; + } if (Math.Abs(leftUnion.Int) > maxUlps || Math.Abs(rightUnion.Int) > maxUlps) return false; } @@ -184,6 +191,13 @@ public static bool AreAlmostEqualUlps(double left, double right, long maxUlps) if (leftSignMask != rightSignMask) // Overflow possible, check each against zero { + // This check is specifically used to trap the case of 0 == -0 + // In IEEE floating point maths, -0 is converted to Double.MinValue, which cannot be used with + // Math.Abs(...) below due to overflow issues. This should only match the 0 == -0 condition. + if (left == right) + { + return true; + } if (Math.Abs(leftUnion.Long) > maxUlps || Math.Abs(rightUnion.Long) > maxUlps) return false; } diff --git a/src/NUnitFramework/framework/Constraints/IndexerConstraint.cs b/src/NUnitFramework/framework/Constraints/IndexerConstraint.cs new file mode 100644 index 0000000000..7bc969c427 --- /dev/null +++ b/src/NUnitFramework/framework/Constraints/IndexerConstraint.cs @@ -0,0 +1,85 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework.Internal; + +namespace NUnit.Framework.Constraints +{ + /// + /// IndexerConstraint extracts a named property and uses + /// its value as the actual value for a chained constraint. + /// + public class IndexerConstraint : PrefixConstraint + { + private readonly Type[] _argumentTypes; + private readonly object[] _arguments; + + /// + /// Initializes a new instance of the class. + /// + /// The argument list for the indexer. + /// The constraint to apply to the indexer. + public IndexerConstraint(IEnumerable indexerArguments, IConstraint baseConstraint) + : base(baseConstraint) + { + _arguments = indexerArguments.ToArray(); + _argumentTypes = _arguments.Select(a => a.GetType()).ToArray(); + + DescriptionPrefix = $"Default indexer accepting arguments {MsgUtils.FormatCollection(_arguments)}"; + } + + /// + /// Test whether the constraint is satisfied by a given value + /// + /// The value to be tested + public override ConstraintResult ApplyTo(TActual actual) + { + Guard.ArgumentNotNull(actual, nameof(actual)); + + object indexedValue; + var actualType = actual as Type ?? actual.GetType(); + + if (actualType.IsArray) + { + var array = actual as Array; + indexedValue = array?.GetValue(_arguments.Cast().ToArray()); + } + else + { + var getMethod = Reflect.GetDefaultIndexer(actualType, _argumentTypes) ?? throw new ArgumentException($"Default indexer accepting arguments {MsgUtils.FormatCollection(_arguments)} was not found on {actualType}."); + + indexedValue = Reflect.InvokeMethod(getMethod, actual, _arguments); + } + + return BaseConstraint.ApplyTo(indexedValue); + } + + /// + /// Returns the string representation of the constraint. + /// + protected override string GetStringRepresentation() => $""; + } +} diff --git a/src/NUnitFramework/framework/Constraints/MsgUtils.cs b/src/NUnitFramework/framework/Constraints/MsgUtils.cs index 674b9f1394..500e478188 100644 --- a/src/NUnitFramework/framework/Constraints/MsgUtils.cs +++ b/src/NUnitFramework/framework/Constraints/MsgUtils.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,9 +21,12 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Reflection; using System.Text; @@ -52,6 +55,11 @@ namespace NUnit.Framework.Constraints /// internal static class MsgUtils { + /// + /// Default amount of items used by method. + /// + internal const int DefaultMaxItems = 10; + /// /// Static string used when strings are clipped /// @@ -70,6 +78,7 @@ internal static class MsgUtils private static readonly string Fmt_DateTimeOffset = "yyyy-MM-dd HH:mm:ss.FFFFFFFzzz"; private static readonly string Fmt_ValueType = "{0}"; private static readonly string Fmt_Default = "<{0}>"; + private static readonly string Fmt_ExceptionThrown = ""; /// /// Current head of chain of value formatters. Public for testing. @@ -79,7 +88,7 @@ internal static class MsgUtils static MsgUtils() { // Initialize formatter to default for values of indeterminate type. - DefaultValueFormatter = val => string.Format(Fmt_Default, val); + DefaultValueFormatter = FormatValueWithoutThrowing; AddFormatter(next => val => val is ValueType ? string.Format(Fmt_ValueType, val) : next(val)); @@ -95,7 +104,7 @@ static MsgUtils() AddFormatter(next => val => val is char ? string.Format(Fmt_Char, val) : next(val)); - AddFormatter(next => val => val is IEnumerable ? FormatCollection((IEnumerable)val, 0, 10) : next(val)); + AddFormatter(next => val => val is IEnumerable ? FormatCollection((IEnumerable)val) : next(val)); AddFormatter(next => val => val is string ? FormatString((string)val) : next(val)); @@ -108,6 +117,24 @@ static MsgUtils() AddFormatter(next => val => TryFormatTuple(val, TypeHelper.IsValueTuple, GetValueFromValueTuple) ?? next(val)); } +#if !NET35 + [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] +#endif + private static string FormatValueWithoutThrowing(object? val) + { + string? asString; + try + { + asString = val?.ToString(); + } + catch (Exception ex) + { + return string.Format(Fmt_ExceptionThrown, $"{ex.GetType().Name} was thrown by {val!.GetType().Name}.ToString()"); + } + + return string.Format(Fmt_Default, asString); + } + /// /// Add a formatter to the chain of responsibility. /// @@ -122,7 +149,7 @@ public static void AddFormatter(ValueFormatterFactory formatterFactory) /// /// The value /// The formatted text - public static string FormatValue(object val) + public static string FormatValue(object? val) { if (val == null) return Fmt_Null; @@ -142,7 +169,7 @@ public static string FormatValue(object val) /// The collection containing elements to write. /// The starting point of the elements to write /// The maximum number of elements to write - public static string FormatCollection(IEnumerable collection, long start, int max) + public static string FormatCollection(IEnumerable collection, long start = 0, int max = DefaultMaxItems) { int count = 0; int index = 0; @@ -153,7 +180,7 @@ public static string FormatCollection(IEnumerable collection, long start, int ma if (start > 0) sb.Append("..."); - foreach (object obj in collection) + foreach (object? obj in collection) { if (index++ >= start) { @@ -216,7 +243,7 @@ private static string FormatArray(Array array) return sb.ToString(); } - private static string TryFormatKeyValuePair(object value) + private static string? TryFormatKeyValuePair(object? value) { if (value == null) return null; @@ -229,28 +256,28 @@ private static string TryFormatKeyValuePair(object value) if (baseValueType != typeof(KeyValuePair<,>)) return null; - object k = valueType.GetProperty("Key").GetValue(value, null); - object v = valueType.GetProperty("Value").GetValue(value, null); + object? k = valueType.GetProperty("Key").GetValue(value, null); + object? v = valueType.GetProperty("Value").GetValue(value, null); return FormatKeyValuePair(k, v); } - private static string FormatKeyValuePair(object key, object value) + private static string FormatKeyValuePair(object? key, object? value) { return string.Format("[{0}, {1}]", FormatValue(key), FormatValue(value)); } - private static object GetValueFromTuple(Type type, string propertyName, object obj) + private static object? GetValueFromTuple(Type type, string propertyName, object obj) { return type.GetProperty(propertyName).GetValue(obj, null); } - private static object GetValueFromValueTuple(Type type, string propertyName, object obj) + private static object? GetValueFromValueTuple(Type type, string propertyName, object obj) { return type.GetField(propertyName).GetValue(obj); } - private static string TryFormatTuple(object value, Func isTuple, Func getValue) + private static string? TryFormatTuple(object? value, Func isTuple, Func getValue) { if (value == null) return null; @@ -262,7 +289,7 @@ private static string TryFormatTuple(object value, Func isTuple, Fun return FormatTuple(value, true, getValue); } - private static string FormatTuple(object value, bool printParentheses, Func getValue) + private static string FormatTuple(object value, bool printParentheses, Func getValue) { Type valueType = value.GetType(); int numberOfGenericArgs = valueType.GetGenericArguments().Length; @@ -277,8 +304,8 @@ private static string FormatTuple(object value, bool printParentheses, Func public static string GetTypeRepresentation(object obj) { - Array array = obj as Array; + Array? array = obj as Array; if (array == null) return string.Format("<{0}>", obj.GetType()); @@ -376,12 +403,13 @@ public static string GetTypeRepresentation(object obj) } /// - /// Converts any control characters in a string + /// Converts any control characters in a string /// to their escaped representation. /// /// The string to be converted /// The converted string - public static string EscapeControlChars(string s) + [return: NotNullIfNotNull("s")] + public static string? EscapeControlChars(string? s) { if (s != null) { @@ -444,12 +472,13 @@ public static string EscapeControlChars(string s) } /// - /// Converts any null characters in a string + /// Converts any null characters in a string /// to their escaped representation. /// /// The string to be converted /// The converted string - public static string EscapeNullCharacters(string s) + [return: NotNullIfNotNull("s")] + public static string? EscapeNullCharacters(string? s) { if (s != null) { @@ -501,14 +530,14 @@ public static string GetArrayIndicesAsString(int[] indices) /// Array of indices public static int[] GetArrayIndicesFromCollectionIndex(IEnumerable collection, long index) { - Array array = collection as Array; + Array? array = collection as Array; int rank = array == null ? 1 : array.Rank; int[] result = new int[rank]; for (int r = rank; --r > 0;) { - int l = array.GetLength(r); + int l = array!.GetLength(r); result[r] = (int)index % l; index /= l; } @@ -551,7 +580,7 @@ public static string ClipString(string s, int maxStringLength, int clipStart) } /// - /// Clip the expected and actual strings in a coordinated fashion, + /// Clip the expected and actual strings in a coordinated fashion, /// so that they may be displayed together. /// /// @@ -578,7 +607,7 @@ public static void ClipExpectedAndActual(ref string expected, ref string actual, } /// - /// Shows the position two strings start to differ. Comparison + /// Shows the position two strings start to differ. Comparison /// starts at the start index. /// /// The expected string diff --git a/src/NUnitFramework/framework/Constraints/NUnitComparer.cs b/src/NUnitFramework/framework/Constraints/NUnitComparer.cs index 9893e8d464..be21bb4fcd 100644 --- a/src/NUnitFramework/framework/Constraints/NUnitComparer.cs +++ b/src/NUnitFramework/framework/Constraints/NUnitComparer.cs @@ -23,6 +23,7 @@ using System; using System.Collections; +using System.Linq; using System.Reflection; using NUnit.Compatibility; @@ -58,23 +59,29 @@ public int Compare(object x, object y) if (Numerics.IsNumericType(x) && Numerics.IsNumericType(y)) return Numerics.Compare(x, y); - if (x is IComparable) - return ((IComparable)x).CompareTo(y); - - if (y is IComparable) - return -((IComparable)y).CompareTo(x); - Type xType = x.GetType(); Type yType = y.GetType(); + // If we use BindingFlags.ExactBinding it will prevent us finding CompareTo(object) + // It however also prevents finding CompareTo(TBase) when called with TDerived + // Nor will it find CompareTo(int) when called with a short. + // We fallback to explicitly exclude CompareTo(object) + bool IsIComparable(MethodInfo method) => method.GetParameters()[0].ParameterType == typeof(object); + MethodInfo method = xType.GetMethod("CompareTo", new Type[] { yType }); - if (method != null) + if (method != null && !IsIComparable(method)) return (int)method.Invoke(x, new object[] { y }); method = yType.GetMethod("CompareTo", new Type[] { xType }); - if (method != null) + if (method != null && !IsIComparable(method)) return -(int)method.Invoke(y, new object[] { x }); + if (x is IComparable) + return ((IComparable)x).CompareTo(y); + + if (y is IComparable) + return -((IComparable)y).CompareTo(x); + throw new ArgumentException("Neither value implements IComparable or IComparable"); } } diff --git a/src/NUnitFramework/framework/Constraints/NUnitEqualityComparer.cs b/src/NUnitFramework/framework/Constraints/NUnitEqualityComparer.cs index 411b296936..e9d998da34 100644 --- a/src/NUnitFramework/framework/Constraints/NUnitEqualityComparer.cs +++ b/src/NUnitFramework/framework/Constraints/NUnitEqualityComparer.cs @@ -83,6 +83,9 @@ public NUnitEqualityComparer() new TimeSpanToleranceComparer(), new TupleComparer(this), new ValueTupleComparer(this), +#if !NET35 + new StructuralComparer(this), +#endif new EquatablesComparer(this), enumerablesComparer }; @@ -154,7 +157,20 @@ public IList FailurePoints /// /// Compares two objects for equality within a tolerance. /// - public bool AreEqual(object x, object y, ref Tolerance tolerance, bool topLevelComparison = true) + public bool AreEqual(object x, object y, ref Tolerance tolerance) + { + return AreEqual(x, y, ref tolerance, true); + } + + /// + /// Compares two objects for equality within a tolerance. + /// + public bool AreEqual(object x, object y, ref Tolerance tolerance, bool topLevelComparison) + { + return AreEqual(x, y, ref tolerance, new ComparisonState(topLevelComparison)); + } + + internal bool AreEqual(object x, object y, ref Tolerance tolerance, ComparisonState state) { this.failurePoints = new List(); @@ -167,13 +183,16 @@ public bool AreEqual(object x, object y, ref Tolerance tolerance, bool topLevelC if (object.ReferenceEquals(x, y)) return true; + if (state.DidCompare(x, y)) + return false; + EqualityAdapter externalComparer = GetExternalComparer(x, y); if (externalComparer != null) return externalComparer.AreEqual(x, y); foreach (IChainComparer comparer in _comparers) { - bool? result = comparer.Equal(x, y, ref tolerance, topLevelComparison); + bool? result = comparer.Equal(x, y, ref tolerance, state); if (result.HasValue) return result.Value; } diff --git a/src/NUnitFramework/framework/Constraints/Numerics.cs b/src/NUnitFramework/framework/Constraints/Numerics.cs index aad04b8278..566e998461 100644 --- a/src/NUnitFramework/framework/Constraints/Numerics.cs +++ b/src/NUnitFramework/framework/Constraints/Numerics.cs @@ -43,6 +43,11 @@ public static bool IsNumericType(Object obj) return IsFloatingPointNumeric(obj) || IsFixedPointNumeric(obj); } + internal static bool IsNumericType(Type type) + { + return IsFloatingPointNumeric(type) || IsFixedPointNumeric(type); + } + /// /// Checks the type of the object, returning true if /// the object is a floating point numeric type. @@ -58,6 +63,18 @@ public static bool IsFloatingPointNumeric(Object obj) } return false; } + + internal static bool IsFloatingPointNumeric(Type type) + { + if (null != type) + { + if (type == typeof(double)) return true; + if (type == typeof(float)) return true; + } + return false; + } + + /// /// Checks the type of the object, returning true if /// the object is a fixed point numeric type. @@ -81,6 +98,23 @@ public static bool IsFixedPointNumeric(Object obj) } return false; } + + internal static bool IsFixedPointNumeric(Type type) + { + if (null != type) + { + if (type == typeof(byte)) return true; + if (type == typeof(sbyte)) return true; + if (type == typeof(int)) return true; + if (type == typeof(uint)) return true; + if (type == typeof(long)) return true; + if (type == typeof(ulong)) return true; + if (type == typeof(short)) return true; + if (type == typeof(ushort)) return true; + if (type == typeof(char)) return true; + } + return false; + } #endregion #region Numeric Equality @@ -384,5 +418,68 @@ public static int Compare(object expected, object actual) return Convert.ToInt32(expected).CompareTo(Convert.ToInt32(actual)); } #endregion + + #region Numeric Difference + + /// + /// Calculates the difference between 2 values in absolute/percent mode. + /// + /// The expected value + /// The actual value + /// Tolerance mode to specify difference representation + /// The difference between the values + internal static object Difference(object expected, object actual, ToleranceMode toleranceMode) + { + switch (toleranceMode) + { + case ToleranceMode.Linear: + return Difference(expected, actual, true); + case ToleranceMode.Percent: + return Difference(expected, actual, false); + default: + throw new InvalidOperationException("Cannot calculate a difference for specified tolerance mode"); + } + } + + private static object Difference(object expected, object actual, bool isAbsolute) + { + if (!IsNumericType(expected) || !IsNumericType(actual)) + throw new ArgumentException("Both arguments must be numeric"); + + if (IsFloatingPointNumeric(expected) || IsFloatingPointNumeric(actual)) + { + var difference = Convert.ToDouble(expected) - Convert.ToDouble(actual); + return isAbsolute ? difference : difference / Convert.ToDouble(expected) * 100; + } + + if (expected is decimal || actual is decimal) + { + var difference = Convert.ToDecimal(expected) - Convert.ToDecimal(actual); + return isAbsolute ? difference : difference / Convert.ToDecimal(expected) * 100; + } + + if (expected is ulong || actual is ulong) + { + var difference = Convert.ToUInt64(expected) - Convert.ToUInt64(actual); + return isAbsolute ? difference : difference / (double)Convert.ToUInt64(expected) * 100; + } + + if (expected is long || actual is long) + { + var difference = Convert.ToInt64(expected) - Convert.ToInt64(actual); + return isAbsolute ? difference : difference / (double)Convert.ToInt64(expected) * 100; + } + + if (expected is uint || actual is uint) + { + var difference = Convert.ToUInt32(expected) - Convert.ToUInt32(actual); + return isAbsolute ? difference : difference / (double)Convert.ToUInt32(expected) * 100; + } + + var intDifference = Convert.ToInt32(expected) - Convert.ToInt32(actual); + return isAbsolute ? intDifference : intDifference / (double)Convert.ToInt32(expected) * 100; + } + + #endregion } -} \ No newline at end of file +} diff --git a/src/NUnitFramework/framework/Constraints/Operators/IndexerOperator.cs b/src/NUnitFramework/framework/Constraints/Operators/IndexerOperator.cs new file mode 100644 index 0000000000..a16d33b9a6 --- /dev/null +++ b/src/NUnitFramework/framework/Constraints/Operators/IndexerOperator.cs @@ -0,0 +1,56 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +namespace NUnit.Framework.Constraints +{ + /// + /// Operator used to test for the presence of a Indexer + /// on an object and optionally apply further tests to the + /// value of that indexer. + /// + public class IndexerOperator : PrefixOperator + { + private readonly object[] _indexArguments; + + /// + /// Constructs a IndexerOperator for a particular set of indexer + /// parameters + /// + public IndexerOperator(params object[] indexArgs) + { + _indexArguments = indexArgs; + + // Indexer stacks on anything and allows only + // prefix operators to stack on it. + this.left_precedence = this.right_precedence = 1; + } + + /// + /// Returns a IndexerConstraint applied to its argument. + /// + public override IConstraint ApplyPrefix(IConstraint constraint) + { + return new IndexerConstraint(_indexArguments, constraint); + } + } +} diff --git a/src/NUnitFramework/framework/Constraints/PropertyConstraint.cs b/src/NUnitFramework/framework/Constraints/PropertyConstraint.cs index 5635848290..f1ffebb30c 100644 --- a/src/NUnitFramework/framework/Constraints/PropertyConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/PropertyConstraint.cs @@ -56,21 +56,33 @@ public override ConstraintResult ApplyTo(TActual actual) { // TODO: Use an error result for null Guard.ArgumentNotNull(actual, nameof(actual)); + const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - Type actualType = actual as Type; - if (actualType == null) - actualType = actual.GetType(); + PropertyInfo property = Reflect.GetUltimateShadowingProperty(typeof(TActual), name, bindingFlags); - PropertyInfo property = Reflect.GetUltimateShadowingProperty(actualType, name, - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); + if (property == null && typeof(TActual).IsInterface) + { + foreach (var @interface in typeof(TActual).GetInterfaces()) + { + property = Reflect.GetUltimateShadowingProperty(@interface, name, bindingFlags); + if (property != null) break; + } + } - // TODO: Use an error result here if (property == null) - throw new ArgumentException($"Property {name} was not found on {actualType}.", "name"); + { + Type actualType = actual as Type ?? actual.GetType(); + + property = Reflect.GetUltimateShadowingProperty(actualType, name, bindingFlags); + + // TODO: Use an error result here + if (property == null) + throw new ArgumentException($"Property {name} was not found on {actualType}.", nameof(name)); + } propValue = property.GetValue(actual, null); var baseResult = BaseConstraint.ApplyTo(propValue); - return new PropertyConstraintResult(this, baseResult); + return new PropertyConstraintResult(this, baseResult); } /// diff --git a/src/NUnitFramework/framework/Constraints/PropertyExistsConstraint.cs b/src/NUnitFramework/framework/Constraints/PropertyExistsConstraint.cs index 3defa576e3..0121894f6e 100644 --- a/src/NUnitFramework/framework/Constraints/PropertyExistsConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/PropertyExistsConstraint.cs @@ -68,13 +68,25 @@ public override string Description public override ConstraintResult ApplyTo(TActual actual) { Guard.ArgumentNotNull(actual, nameof(actual)); + const BindingFlags bindingFlags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - actualType = actual as Type; - if (actualType == null) - actualType = actual.GetType(); + PropertyInfo property = Reflect.GetUltimateShadowingProperty(typeof(TActual), name, bindingFlags); + + if (property == null && typeof(TActual).IsInterface) + { + foreach (var @interface in typeof(TActual).GetInterfaces()) + { + property = Reflect.GetUltimateShadowingProperty(@interface, name, bindingFlags); + if (property != null) break; + } + } + + if (property == null) + { + actualType = actual as Type ?? actual.GetType(); + property = Reflect.GetUltimateShadowingProperty(actualType, name, bindingFlags); + } - PropertyInfo property = Reflect.GetUltimateShadowingProperty(actualType, name, - BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); return new ConstraintResult(this, actualType, property != null); } diff --git a/src/NUnitFramework/framework/Constraints/RegexConstraint.cs b/src/NUnitFramework/framework/Constraints/RegexConstraint.cs index ac6792dccd..2875521528 100644 --- a/src/NUnitFramework/framework/Constraints/RegexConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/RegexConstraint.cs @@ -29,28 +29,67 @@ namespace NUnit.Framework.Constraints /// RegexConstraint can test whether a string matches /// the pattern provided. /// - public class RegexConstraint : StringConstraint + public class RegexConstraint : Constraint { + private Regex _regex; + + /// + /// The Description of what this constraint tests, for + /// use in messages and in the ConstraintResult. + /// + public override string Description + { + get + { + var description = $"String matching \"{_regex}\""; + var caseInsensitive = (_regex.Options & RegexOptions.IgnoreCase) == RegexOptions.IgnoreCase; + if (caseInsensitive) + { + description += ", ignoring case"; + } + return description; + } + } + /// /// Initializes a new instance of the class. /// /// The pattern. - public RegexConstraint(string pattern) : base(pattern) + public RegexConstraint(string pattern) : base(pattern) + { + _regex = new Regex(pattern); + } + + /// + /// Initializes a new instance of the class. + /// + /// The Regex pattern object. + public RegexConstraint(Regex regex) : base(regex.ToString()) + { + _regex = regex; + } + + /// + /// Modify the constraint to ignore case in matching. + /// + public RegexConstraint IgnoreCase { - descriptionText = "String matching"; + get + { + _regex = new Regex(_regex.ToString(), _regex.Options | RegexOptions.IgnoreCase); + return this; + } } /// - /// Test whether the constraint is satisfied by a given value + /// Applies the regex constraint to an actual value, returning a ConstraintResult. /// - /// The value to be tested - /// True for success, false for failure - protected override bool Matches(string actual) + /// The string to be tested. + /// True for success, false for failure. + public override ConstraintResult ApplyTo(TActual actual) { - return actual != null && Regex.IsMatch( - actual, - this.expected, - this.caseInsensitive ? RegexOptions.IgnoreCase : RegexOptions.None); + return new ConstraintResult(this, actual, + actual is string actualString && _regex.Match(actualString).Success); } } -} \ No newline at end of file +} diff --git a/src/NUnitFramework/framework/Constraints/StringConstraint.cs b/src/NUnitFramework/framework/Constraints/StringConstraint.cs index 36d45c1812..10bb6ab240 100644 --- a/src/NUnitFramework/framework/Constraints/StringConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/StringConstraint.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; +using NUnit.Framework.Internal; namespace NUnit.Framework.Constraints { @@ -65,8 +65,8 @@ public abstract class StringConstraint : Constraint /// public override string Description { - get - { + get + { string desc = string.Format("{0} {1}", descriptionText, MsgUtils.FormatValue(expected)); if (caseInsensitive) desc += ", ignoring case"; @@ -104,13 +104,11 @@ public virtual StringConstraint IgnoreCase /// True for success, false for failure public override ConstraintResult ApplyTo(TActual actual) { - string actualAsString = actual as string; - if (actual != null && actualAsString == null) - throw new ArgumentException("Actual value must be a string", nameof(actual)); + var stringValue = ConstraintUtils.RequireActual(actual, nameof(actual), allowNull: true); - return new ConstraintResult(this, actual, Matches(actualAsString)); + return new ConstraintResult(this, actual, Matches(stringValue)); } - + /// /// Test whether the constraint is satisfied by a given string /// diff --git a/src/NUnitFramework/framework/Constraints/UniqueItemsConstraint.cs b/src/NUnitFramework/framework/Constraints/UniqueItemsConstraint.cs index 5723a58aa6..46dfee16df 100644 --- a/src/NUnitFramework/framework/Constraints/UniqueItemsConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/UniqueItemsConstraint.cs @@ -21,11 +21,13 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using System.Reflection; -using NUnit.Compatibility; using NUnit.Framework.Internal; namespace NUnit.Framework.Constraints @@ -52,97 +54,188 @@ public override string Description /// protected override bool Matches(IEnumerable actual) { - return TryFastAlgorithm(actual) ?? OriginalAlgorithm(actual); + var nonUniqueItems = GetNonUniqueItems(actual); + return nonUniqueItems.Count == 0; } - private bool OriginalAlgorithm(IEnumerable actual) + /// + public override ConstraintResult ApplyTo(TActual actual) { - var list = new List(); + IEnumerable enumerable = ConstraintUtils.RequireActual(actual, nameof(actual)); + + var nonUniqueItems = GetNonUniqueItems(enumerable); - foreach (object o1 in actual) + return new UniqueItemsConstraintResult(this, actual, nonUniqueItems); + } + + private ICollection OriginalAlgorithm(IEnumerable actual) + { + var nonUniques = new List(); + var processedItems = new List(); + + foreach (var o1 in actual) { - foreach (object o2 in list) + var isUnique = true; + var unknownNonUnique = false; + + foreach (var o2 in processedItems) + { if (ItemsEqual(o1, o2)) - return false; - list.Add(o1); + { + isUnique = false; + unknownNonUnique = !nonUniques.Any(o2 => ItemsEqual(o1, o2)); + break; + } + } + + if (isUnique) + processedItems.Add(o1); + else if (unknownNonUnique) + { + nonUniques.Add(o1); + if (nonUniques.Count == MsgUtils.DefaultMaxItems) + break; + } + } + + return nonUniques; + } + + private ICollection? TryInferFastPath(IEnumerable actual) + { + var allTypes = new List(); + var allItems = new List(); + foreach (var item in actual) + { + allItems.Add(item); + if (item != null) + allTypes.Add(item.GetType()); } - return true; + // Partly optimization, partly makes any subsequent all()/any() calls reliable + if (allTypes.Count == 0) + return new object[0]; + + var distinctTypes = allTypes.Distinct().ToList(); + if (distinctTypes.Count == 1) + { + var itemsType = distinctTypes.FirstOrDefault(); + if (IsTypeSafeForFastPath(itemsType)) + { + var itemsOfT = ItemsCastMethod.MakeGenericMethod(itemsType).Invoke(null, new[] { actual }); + + if (IgnoringCase) + { + if (itemsType == typeof(string)) + return (ICollection)StringsUniqueIgnoringCase((IEnumerable)itemsOfT); + else if (itemsType == typeof(char)) + return (ICollection)CharsUniqueIgnoringCase((IEnumerable)itemsOfT); + } + + return (ICollection)ItemsUniqueMethod.MakeGenericMethod(itemsType).Invoke(null, new object[] { itemsOfT }); + } + } + else + { + if (distinctTypes.All(o => IsTypeSafeForFastPath(o) && !IsSpecialComparisonType(o))) + { + return (ICollection)ItemsUnique(allItems); + } + } + + return null; } - private bool? TryFastAlgorithm(IEnumerable actual) + private static bool IsSpecialComparisonType(Type type) + { + if (type.IsGenericType) + return type.FullName.StartsWith("System.Collections.Generic.KeyValuePair`2"); + else if (Numerics.IsNumericType(type)) + return true; + else + return + type == typeof(string) + || type == typeof(char) + || type == typeof(DateTimeOffset) + || type == typeof(DictionaryEntry); + } + + private ICollection GetNonUniqueItems(IEnumerable actual) { // If the user specified any external comparer with Using, exit if (UsingExternalComparer) - return null; + return OriginalAlgorithm(actual); // If IEnumerable is not implemented exit, // Otherwise return value is the Type of T - Type memberType = GetGenericTypeArgument(actual); - if (memberType == null || !IsSealed(memberType) || IsHandledSpeciallyByNUnit(memberType)) - return null; + Type? memberType = GetGenericTypeArgument(actual); + if (memberType == null) + return TryInferFastPath(actual) ?? OriginalAlgorithm(actual); + else if (!IsTypeSafeForFastPath(memberType)) + return OriginalAlgorithm(actual); + // Special handling for ignore case with strings and chars if (IgnoringCase) { if (memberType == typeof(string)) - return StringsUniqueIgnoringCase((IEnumerable)actual); + return (ICollection)StringsUniqueIgnoringCase((IEnumerable)actual); else if (memberType == typeof(char)) - return CharsUniqueIgnoringCase((IEnumerable)actual); + return (ICollection)CharsUniqueIgnoringCase((IEnumerable)actual); } - return (bool)ItemsUniqueMethod.MakeGenericMethod(memberType).Invoke(null, new object[] { actual }); + return (ICollection)ItemsUniqueMethod.MakeGenericMethod(memberType).Invoke(null, new object[] { actual }); } - private bool IsSealed(Type type) + private static bool IsTypeSafeForFastPath(Type? type) { - return type.GetTypeInfo().IsSealed; + return type != null && type.IsSealed && !IsHandledSpeciallyByNUnit(type); } private static readonly MethodInfo ItemsUniqueMethod = typeof(UniqueItemsConstraint).GetMethod(nameof(ItemsUnique), BindingFlags.Static | BindingFlags.NonPublic); - private static bool ItemsUnique(IEnumerable actual) - { - var hash = new HashSet(); + private static readonly MethodInfo ItemsCastMethod = + typeof(Enumerable).GetMethod(nameof(Enumerable.Cast), BindingFlags.Static | BindingFlags.Public); - foreach (T item in actual) - { - if (!hash.Add(item)) - return false; - } + private static ICollection ItemsUnique(IEnumerable actual) + => NonUniqueItemsInternal(actual, EqualityComparer.Default); - return true; - } + private ICollection StringsUniqueIgnoringCase(IEnumerable actual) + => NonUniqueItemsInternal(actual, new NUnitStringEqualityComparer(IgnoringCase)); - private static bool StringsUniqueIgnoringCase(IEnumerable actual) + private ICollection CharsUniqueIgnoringCase(IEnumerable actual) { - var hash = new HashSet(); - - foreach (string item in actual) - { - string s = item.ToLower(); - - if (!hash.Add(s)) - return false; - } - - return true; + var result = NonUniqueItemsInternal( + actual.Select(x => x.ToString()), + new NUnitStringEqualityComparer(IgnoringCase) + ); + return result.Select(x => x[0]).ToList(); } - private static bool CharsUniqueIgnoringCase(IEnumerable actual) + private static ICollection NonUniqueItemsInternal(IEnumerable actual, IEqualityComparer comparer) { - var hash = new HashSet(); + var processedItems = new HashSet(comparer); + var knownNonUniques = new HashSet(comparer); + var nonUniques = new List(); - foreach (char item in actual) + foreach (T item in actual) { - char ch = char.ToLower(item); + // Check if 'item' is a duplicate of a previously-processed item + if (!processedItems.Add(item)) + { + // Check if 'item' has previously been flagged as a duplicate + if (knownNonUniques.Add(item)) + { + nonUniques.Add(item); - if (!hash.Add(ch)) - return false; + if (nonUniques.Count == MsgUtils.DefaultMaxItems) + break; + } + } } - return true; + return nonUniques; } // Return true if NUnitEqualityHandler has special logic for Type @@ -158,7 +251,7 @@ private static bool IsHandledSpeciallyByNUnit(Type type) || type.FullName == "System.ValueTuple"; } - private Type GetGenericTypeArgument(IEnumerable actual) + private static Type? GetGenericTypeArgument(IEnumerable actual) { foreach (var type in actual.GetType().GetInterfaces()) { @@ -174,5 +267,54 @@ private Type GetGenericTypeArgument(IEnumerable actual) return null; } + + private sealed class NUnitStringEqualityComparer : IEqualityComparer + { + private readonly bool _ignoreCase; + + public NUnitStringEqualityComparer(bool ignoreCase) + { + _ignoreCase = ignoreCase; + } + + public bool Equals(string x, string y) + { + string s1 = _ignoreCase ? x.ToLower() : x; + string s2 = _ignoreCase ? y.ToLower() : y; + + return s1.Equals(s2); + } + + public int GetHashCode(string obj) + { + if (obj is null) + return 0; + else if (_ignoreCase) + return obj.ToLower().GetHashCode(); + else + return obj.GetHashCode(); + } + } + + internal sealed class UniqueItemsConstraintResult : ConstraintResult + { + internal ICollection NonUniqueItems { get; } + + public UniqueItemsConstraintResult(IConstraint constraint, object actualValue, ICollection nonUniqueItems) + : base(constraint, actualValue, nonUniqueItems.Count == 0) + { + NonUniqueItems = nonUniqueItems; + } + + public override void WriteAdditionalLinesTo(MessageWriter writer) + { + if (this.Status == ConstraintStatus.Failure) + { + writer.Write(" Not unique items: "); + var output = MsgUtils.FormatCollection(NonUniqueItems, 0, MsgUtils.DefaultMaxItems); + writer.WriteLine(output); + } + } + } } } diff --git a/src/NUnitFramework/framework/Constraints/XmlSerializableConstraint.cs b/src/NUnitFramework/framework/Constraints/XmlSerializableConstraint.cs index e092b25bff..623cfeb06a 100644 --- a/src/NUnitFramework/framework/Constraints/XmlSerializableConstraint.cs +++ b/src/NUnitFramework/framework/Constraints/XmlSerializableConstraint.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if SERIALIZATION using System; using System.IO; using System.Xml.Serialization; @@ -29,7 +28,7 @@ namespace NUnit.Framework.Constraints { /// - /// XmlSerializableConstraint tests whether + /// XmlSerializableConstraint tests whether /// an object is serializable in XML format. /// public class XmlSerializableConstraint : Constraint @@ -89,4 +88,3 @@ protected override string GetStringRepresentation() } } } -#endif diff --git a/src/NUnitFramework/framework/Does.cs b/src/NUnitFramework/framework/Does.cs index 400e654a50..a7c1e4d850 100644 --- a/src/NUnitFramework/framework/Does.cs +++ b/src/NUnitFramework/framework/Does.cs @@ -21,6 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +using System.Text.RegularExpressions; using NUnit.Framework.Constraints; namespace NUnit.Framework @@ -140,6 +141,15 @@ public static RegexConstraint Match(string pattern) return new RegexConstraint(pattern); } + /// + /// Returns a constraint that succeeds if the actual + /// value matches the regular expression supplied as an argument. + /// + public static RegexConstraint Match(Regex regex) + { + return new RegexConstraint(regex); + } + #endregion } } diff --git a/src/NUnitFramework/framework/ExceptionExtensions.cs b/src/NUnitFramework/framework/ExceptionExtensions.cs new file mode 100644 index 0000000000..37cf5aedcf --- /dev/null +++ b/src/NUnitFramework/framework/ExceptionExtensions.cs @@ -0,0 +1,102 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Collections; + +namespace NUnit.Framework +{ + internal static class ExceptionExtensions + { + /// + /// If throws, returns "SomeException was thrown by the Exception.StackTrace + /// property." See also . + /// +#if !NET35 + // https://github.com/dotnet/coreclr/issues/19698 is also currently present in .NET Framework 4.7 and 4.8. A + // race condition between threads reading the same PDB file to obtain file and line info for a stack trace + // results in AccessViolationException when the stack trace is accessed even indirectly e.g. Exception.ToString. + [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] +#endif + public static string GetStackTraceWithoutThrowing(this Exception exception) + { + if (exception is null) throw new ArgumentNullException(nameof(exception)); + + try + { + return exception.StackTrace; + } + catch (Exception ex) + { + return ex.GetType().Name + " was thrown by the Exception.StackTrace property."; + } + } + + /// + /// If throws, returns "SomeException was thrown by the Exception.Message + /// property." + /// +#if !NET35 + // https://github.com/dotnet/coreclr/issues/19698 is also currently present in .NET Framework 4.7 and 4.8. A + // race condition between threads reading the same PDB file to obtain file and line info for a stack trace + // results in AccessViolationException when the stack trace is accessed even indirectly e.g. Exception.ToString. + [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] +#endif + public static string GetMessageWithoutThrowing(this Exception exception) + { + if (exception is null) throw new ArgumentNullException(nameof(exception)); + + try + { + return exception.Message; + } + catch (Exception ex) + { + return ex.GetType().Name + " was thrown by the Exception.Message property."; + } + } + + /// + /// If throws, returns "SomeException was thrown by the Exception.Data property." + /// +#if !NET35 + // https://github.com/dotnet/coreclr/issues/19698 is also currently present in .NET Framework 4.7 and 4.8. A + // race condition between threads reading the same PDB file to obtain file and line info for a stack trace + // results in AccessViolationException when the stack trace is accessed even indirectly e.g. Exception.ToString. + [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] +#endif + public static Result GetDataWithoutThrowing(this Exception exception) + { + if (exception is null) throw new ArgumentNullException(nameof(exception)); + + try + { + return Result.Success(exception.Data); + } + catch (Exception ex) + { + return Result.Error(ex.GetType().Name + " was thrown by the Exception.Data property."); + } + } + } +} diff --git a/src/NUnitFramework/framework/Exceptions/AssertionException.cs b/src/NUnitFramework/framework/Exceptions/AssertionException.cs index 2ef4e63f59..d83b1f7d21 100644 --- a/src/NUnitFramework/framework/Exceptions/AssertionException.cs +++ b/src/NUnitFramework/framework/Exceptions/AssertionException.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,9 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -namespace NUnit.Framework +namespace NUnit.Framework { using Interfaces; @@ -33,27 +35,25 @@ namespace NUnit.Framework [Serializable] public class AssertionException : ResultStateException { - /// The error message that explains + /// The error message that explains /// the reason for the exception - public AssertionException (string message) : base(message) + public AssertionException(string? message) : base(message) {} - /// The error message that explains + /// The error message that explains /// the reason for the exception - /// The exception that caused the + /// The exception that caused the /// current exception - public AssertionException(string message, Exception inner) : - base(message, inner) + public AssertionException(string? message, Exception? inner) : + base(message, inner) {} -#if SERIALIZATION /// /// Serialization Constructor /// - protected AssertionException(System.Runtime.Serialization.SerializationInfo info, + protected AssertionException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info,context) {} -#endif /// /// Gets the ResultState provided by this exception diff --git a/src/NUnitFramework/framework/Exceptions/IgnoreException.cs b/src/NUnitFramework/framework/Exceptions/IgnoreException.cs index aa5a0038b9..b6021227f4 100644 --- a/src/NUnitFramework/framework/Exceptions/IgnoreException.cs +++ b/src/NUnitFramework/framework/Exceptions/IgnoreException.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,9 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -namespace NUnit.Framework +namespace NUnit.Framework { using Interfaces; @@ -34,25 +36,23 @@ namespace NUnit.Framework public class IgnoreException : ResultStateException { /// - public IgnoreException (string message) : base(message) + public IgnoreException(string? message) : base(message) {} - /// The error message that explains + /// The error message that explains /// the reason for the exception - /// The exception that caused the + /// The exception that caused the /// current exception - public IgnoreException(string message, Exception inner) : - base(message, inner) + public IgnoreException(string? message, Exception? inner) : + base(message, inner) {} -#if SERIALIZATION /// /// Serialization Constructor /// - protected IgnoreException(System.Runtime.Serialization.SerializationInfo info, + protected IgnoreException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info,context) {} -#endif /// /// Gets the ResultState provided by this exception diff --git a/src/NUnitFramework/framework/Exceptions/InconclusiveException.cs b/src/NUnitFramework/framework/Exceptions/InconclusiveException.cs index 15daf82b61..81c5958952 100644 --- a/src/NUnitFramework/framework/Exceptions/InconclusiveException.cs +++ b/src/NUnitFramework/framework/Exceptions/InconclusiveException.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; namespace NUnit.Framework @@ -33,22 +35,21 @@ namespace NUnit.Framework [Serializable] public class InconclusiveException : ResultStateException { - /// The error message that explains + /// The error message that explains /// the reason for the exception - public InconclusiveException(string message) + public InconclusiveException(string? message) : base(message) { } - /// The error message that explains + /// The error message that explains /// the reason for the exception - /// The exception that caused the + /// The exception that caused the /// current exception - public InconclusiveException(string message, Exception inner) + public InconclusiveException(string? message, Exception? inner) : base(message, inner) { } -#if SERIALIZATION /// /// Serialization Constructor /// @@ -56,7 +57,6 @@ public InconclusiveException(string message, Exception inner) System.Runtime.Serialization.StreamingContext context) : base(info, context) { } -#endif /// /// Gets the ResultState provided by this exception diff --git a/src/NUnitFramework/framework/Exceptions/MultipleAssertException.cs b/src/NUnitFramework/framework/Exceptions/MultipleAssertException.cs index 7802d9dca4..bee30f240c 100644 --- a/src/NUnitFramework/framework/Exceptions/MultipleAssertException.cs +++ b/src/NUnitFramework/framework/Exceptions/MultipleAssertException.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,9 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -namespace NUnit.Framework +namespace NUnit.Framework { using Interfaces; @@ -33,18 +35,13 @@ namespace NUnit.Framework [Serializable] public class MultipleAssertException : ResultStateException { - /// - /// Default Constructor - /// - public MultipleAssertException(string message) : base(message) { } - /// /// Construct based on the TestResult so far. This is the constructor /// used normally, when exiting the multiple assert block with failures. /// Not used internally but provided to facilitate debugging. /// /// - /// The current result, up to this point. The result is not used + /// The current result, up to this point. The result is not used /// internally by NUnit but is provided to facilitate debugging. /// public MultipleAssertException(ITestResult testResult) @@ -54,22 +51,15 @@ public MultipleAssertException(ITestResult testResult) TestResult = testResult; } - /// The error message that explains - /// the reason for the exception - /// The exception that caused the - /// current exception - public MultipleAssertException(string message, Exception inner) : - base(message, inner) - {} - -#if SERIALIZATION +#pragma warning disable CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. /// /// Serialization Constructor /// - protected MultipleAssertException(System.Runtime.Serialization.SerializationInfo info, + protected MultipleAssertException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info,context) - {} -#endif + { + } +#pragma warning restore CS8618 // Non-nullable field is uninitialized. Consider declaring as nullable. /// /// Gets the provided by this exception. diff --git a/src/NUnitFramework/framework/Exceptions/ResultStateException.cs b/src/NUnitFramework/framework/Exceptions/ResultStateException.cs index 25c7bc99a0..9798a250a2 100644 --- a/src/NUnitFramework/framework/Exceptions/ResultStateException.cs +++ b/src/NUnitFramework/framework/Exceptions/ResultStateException.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,9 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -namespace NUnit.Framework +namespace NUnit.Framework { using Interfaces; @@ -33,27 +35,25 @@ namespace NUnit.Framework [Serializable] public abstract class ResultStateException : Exception { - /// The error message that explains + /// The error message that explains /// the reason for the exception - public ResultStateException (string message) : base(message) + public ResultStateException(string? message) : base(message) {} - /// The error message that explains + /// The error message that explains /// the reason for the exception - /// The exception that caused the + /// The exception that caused the /// current exception - public ResultStateException(string message, Exception inner) : - base(message, inner) + public ResultStateException(string? message, Exception? inner) : + base(message, inner) {} -#if SERIALIZATION /// /// Serialization Constructor /// - protected ResultStateException(System.Runtime.Serialization.SerializationInfo info, + protected ResultStateException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) : base(info,context) {} -#endif /// /// Gets the ResultState provided by this exception diff --git a/src/NUnitFramework/framework/Exceptions/SuccessException.cs b/src/NUnitFramework/framework/Exceptions/SuccessException.cs index 8fd8a94c9e..874854980c 100644 --- a/src/NUnitFramework/framework/Exceptions/SuccessException.cs +++ b/src/NUnitFramework/framework/Exceptions/SuccessException.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -20,6 +20,9 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** + +#nullable enable + using System; namespace NUnit.Framework @@ -33,20 +36,19 @@ namespace NUnit.Framework public class SuccessException : ResultStateException { /// - public SuccessException(string message) + public SuccessException(string? message) : base(message) { } - /// The error message that explains + /// The error message that explains /// the reason for the exception - /// The exception that caused the + /// The exception that caused the /// current exception - public SuccessException(string message, Exception inner) + public SuccessException(string? message, Exception? inner) : base(message, inner) { } -#if SERIALIZATION /// /// Serialization Constructor /// @@ -54,7 +56,6 @@ public SuccessException(string message, Exception inner) System.Runtime.Serialization.StreamingContext context) : base(info, context) { } -#endif /// /// Gets the ResultState provided by this exception diff --git a/src/NUnitFramework/framework/Extensions.cs b/src/NUnitFramework/framework/Extensions.cs index 23089c2b15..1576caaaf7 100644 --- a/src/NUnitFramework/framework/Extensions.cs +++ b/src/NUnitFramework/framework/Extensions.cs @@ -23,7 +23,6 @@ using System; using System.Collections; -using System.Linq; using System.Reflection; using NUnit.Compatibility; @@ -39,63 +38,6 @@ public static bool IsStatic(this Type type) return type.GetTypeInfo().IsAbstract && type.GetTypeInfo().IsSealed; } -#if NETSTANDARD1_4 - public static bool HasAttribute(this MemberInfo attributeProvider, bool inherit) where T : class - { - // Filter manually for any targets that may run on .NET Core 1.1 or lower - // because only types derived from System.Attribute are allowed. - return attributeProvider.GetCustomAttributes(inherit).OfType().Any(); - } - - public static bool HasAttribute(this ParameterInfo attributeProvider, bool inherit) where T : class - { - // Filter manually for any targets that may run on .NET Core 1.1 or lower - // because only types derived from System.Attribute are allowed. - return attributeProvider.GetCustomAttributes(inherit).OfType().Any(); - } - - public static bool HasAttribute(this Type attributeProvider, bool inherit) where T : class - { - // Filter manually for any targets that may run on .NET Core 1.1 or lower - // because only types derived from System.Attribute are allowed. - return attributeProvider.GetTypeInfo().GetCustomAttributes(inherit).OfType().Any(); - } - - public static bool HasAttribute(this Assembly attributeProvider) where T : class - { - // Filter manually for any targets that may run on .NET Core 1.1 or lower - // because only types derived from System.Attribute are allowed. - return attributeProvider.GetCustomAttributes().OfType().Any(); - } - - public static T[] GetAttributes(this MemberInfo attributeProvider, bool inherit) where T : class - { - // Filter manually for any targets that may run on .NET Core 1.1 or lower - // because only types derived from System.Attribute are allowed. - return attributeProvider.GetCustomAttributes(inherit).OfType().ToArray(); - } - - public static T[] GetAttributes(this ParameterInfo attributeProvider, bool inherit) where T : class - { - // Filter manually for any targets that may run on .NET Core 1.1 or lower - // because only types derived from System.Attribute are allowed. - return attributeProvider.GetCustomAttributes(inherit).OfType().ToArray(); - } - - public static T[] GetAttributes(this Type attributeProvider, bool inherit) where T : class - { - // Filter manually for any targets that may run on .NET Core 1.1 or lower - // because only types derived from System.Attribute are allowed. - return attributeProvider.GetTypeInfo().GetCustomAttributes(inherit).OfType().ToArray(); - } - - public static T[] GetAttributes(this Assembly attributeProvider) where T : class - { - // Filter manually for any targets that may run on .NET Core 1.1 or lower - // because only types derived from System.Attribute are allowed. - return attributeProvider.GetCustomAttributes().OfType().ToArray(); - } -#else public static bool HasAttribute(this ICustomAttributeProvider attributeProvider, bool inherit) { return attributeProvider.IsDefined(typeof(T), inherit); @@ -120,7 +62,6 @@ public static bool HasAttribute(this Type type, bool inherit) { return ((ICustomAttributeProvider)type.GetTypeInfo()).GetAttributes(inherit); } -#endif public static IEnumerable Skip(this IEnumerable enumerable, long skip) { diff --git a/src/NUnitFramework/framework/Guard.cs b/src/NUnitFramework/framework/Guard.cs index 51f649f9bb..753fa6b0e8 100644 --- a/src/NUnitFramework/framework/Guard.cs +++ b/src/NUnitFramework/framework/Guard.cs @@ -21,7 +21,10 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using NUnit.Compatibility; using NUnit.Framework.Internal; @@ -39,7 +42,7 @@ internal static class Guard /// /// The value to be tested /// The name of the argument - public static void ArgumentNotNull(object value, string name) + public static void ArgumentNotNull([NotNull] object? value, string name) { if (value == null) throw new ArgumentNullException(name, "Argument " + name + " must not be null"); @@ -50,7 +53,7 @@ public static void ArgumentNotNull(object value, string name) /// /// The value to be tested /// The name of the argument - public static void ArgumentNotNullOrEmpty(string value, string name) + public static void ArgumentNotNullOrEmpty([NotNull] string? value, string name) { ArgumentNotNull(value, name); @@ -64,7 +67,7 @@ public static void ArgumentNotNullOrEmpty(string value, string name) /// The condition that must be met /// The exception message to be used /// The name of the argument - public static void ArgumentInRange(bool condition, string message, string paramName) + public static void ArgumentInRange([DoesNotReturnIf(false)] bool condition, string message, string paramName) { if (!condition) throw new ArgumentOutOfRangeException(paramName, message); @@ -76,7 +79,7 @@ public static void ArgumentInRange(bool condition, string message, string paramN /// The condition that must be met /// The exception message to be used /// The name of the argument - public static void ArgumentValid(bool condition, string message, string paramName) + public static void ArgumentValid([DoesNotReturnIf(false)] bool condition, string message, string paramName) { if (!condition) throw new ArgumentException(message, paramName); @@ -87,7 +90,7 @@ public static void ArgumentValid(bool condition, string message, string paramNam /// /// The condition that must be met /// The exception message to be used - public static void OperationValid(bool condition, string message) + public static void OperationValid([DoesNotReturnIf(false)] bool condition, string message) { if (!condition) throw new InvalidOperationException(message); diff --git a/src/NUnitFramework/framework/Has.cs b/src/NUnitFramework/framework/Has.cs index 848566abd8..63d812e03f 100644 --- a/src/NUnitFramework/framework/Has.cs +++ b/src/NUnitFramework/framework/Has.cs @@ -220,5 +220,18 @@ public static SomeItemsConstraint Member(object expected) #endregion + #region ItemAt + + /// + /// Returns a new IndexerConstraintExpression, which will + /// apply any following constraint to that indexer value. + /// + /// Index accessor values. + public static ConstraintExpression ItemAt(params object[] indexArgs) + { + return new ConstraintExpression().ItemAt(indexArgs); + } + + #endregion } } diff --git a/src/NUnitFramework/framework/Interfaces/AssertionResult.cs b/src/NUnitFramework/framework/Interfaces/AssertionResult.cs index fc81cc7f0a..29cd5a1b08 100644 --- a/src/NUnitFramework/framework/Interfaces/AssertionResult.cs +++ b/src/NUnitFramework/framework/Interfaces/AssertionResult.cs @@ -1,3 +1,28 @@ +// *********************************************************************** +// Copyright (c) 2016 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +#nullable enable + using System; using System.Collections.Generic; using System.Text; @@ -7,12 +32,12 @@ namespace NUnit.Framework.Interfaces /// /// The AssertionResult class represents the result of a single assertion. /// - public class AssertionResult + public class AssertionResult : IEquatable { /// /// Construct an AssertionResult /// - public AssertionResult(AssertionStatus status, string message, string stackTrace) + public AssertionResult(AssertionStatus status, string? message, string? stackTrace) { Status = status; Message = message; @@ -23,36 +48,44 @@ public AssertionResult(AssertionStatus status, string message, string stackTrace public AssertionStatus Status { get; } /// The message produced by the assertion, or null - public string Message { get; } + public string? Message { get; } /// The stack trace associated with the assertion, or null - public string StackTrace { get; } + public string? StackTrace { get; } - /// - /// ToString Override - /// - public override string ToString() + /// Determines whether the specified object is equal to the current object. + /// The object to compare with the current object. + public override bool Equals(object? obj) { - return string.Format("Assert {0}: {1}", Status, Message) + Environment.NewLine + StackTrace; + return Equals(obj as AssertionResult); } - /// - /// Override GetHashCode - /// + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + public bool Equals(AssertionResult? other) + { + return other != null && + Status == other.Status && + Message == other.Message && + StackTrace == other.StackTrace; + } + + /// Serves as the default hash function. public override int GetHashCode() { - return ToString().GetHashCode(); + var hashCode = -783279553; + hashCode = hashCode * -1521134295 + Status.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Message); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(StackTrace); + return hashCode; } /// - /// Override Equals + /// ToString Override /// - /// - public override bool Equals(object obj) + public override string ToString() { - var other = obj as AssertionResult; - - return other != null && Status == other.Status && Message == other.Message && StackTrace == other.StackTrace; + return string.Format("Assert {0}: {1}", Status, Message) + Environment.NewLine + StackTrace; } } } diff --git a/src/NUnitFramework/framework/Interfaces/IApplyToContext.cs b/src/NUnitFramework/framework/Interfaces/IApplyToContext.cs index 976d2ad853..df00174fb6 100644 --- a/src/NUnitFramework/framework/Interfaces/IApplyToContext.cs +++ b/src/NUnitFramework/framework/Interfaces/IApplyToContext.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2012 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using NUnit.Framework.Internal; namespace NUnit.Framework.Interfaces diff --git a/src/NUnitFramework/framework/Interfaces/IApplyToTest.cs b/src/NUnitFramework/framework/Interfaces/IApplyToTest.cs index e8e39e0be8..93f9ee09df 100644 --- a/src/NUnitFramework/framework/Interfaces/IApplyToTest.cs +++ b/src/NUnitFramework/framework/Interfaces/IApplyToTest.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2010 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; +#nullable enable + using NUnit.Framework.Internal; namespace NUnit.Framework.Interfaces diff --git a/src/NUnitFramework/framework/Interfaces/ICombiningStrategy.cs b/src/NUnitFramework/framework/Interfaces/ICombiningStrategy.cs index 36aac2a171..9e6c0fc6cd 100644 --- a/src/NUnitFramework/framework/Interfaces/ICombiningStrategy.cs +++ b/src/NUnitFramework/framework/Interfaces/ICombiningStrategy.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2008 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections; using System.Collections.Generic; diff --git a/src/NUnitFramework/framework/Interfaces/ICommandWrapper.cs b/src/NUnitFramework/framework/Interfaces/ICommandWrapper.cs index 6d12c32cfa..b61552d04b 100644 --- a/src/NUnitFramework/framework/Interfaces/ICommandWrapper.cs +++ b/src/NUnitFramework/framework/Interfaces/ICommandWrapper.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2011-2015 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,8 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; -using System.Collections.Generic; +#nullable enable + using NUnit.Framework.Internal.Commands; namespace NUnit.Framework.Interfaces diff --git a/src/NUnitFramework/framework/Interfaces/IFixtureBuilder.cs b/src/NUnitFramework/framework/Interfaces/IFixtureBuilder.cs index d020fe9690..d4c0745a78 100644 --- a/src/NUnitFramework/framework/Interfaces/IFixtureBuilder.cs +++ b/src/NUnitFramework/framework/Interfaces/IFixtureBuilder.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections.Generic; namespace NUnit.Framework.Interfaces diff --git a/src/NUnitFramework/framework/Interfaces/IMethodInfo.cs b/src/NUnitFramework/framework/Interfaces/IMethodInfo.cs index db56acb8c9..4b0caf16e0 100644 --- a/src/NUnitFramework/framework/Interfaces/IMethodInfo.cs +++ b/src/NUnitFramework/framework/Interfaces/IMethodInfo.cs @@ -20,6 +20,9 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** + +#nullable enable + using System; using System.Reflection; @@ -106,7 +109,7 @@ public interface IMethodInfo : IReflectionInfo /// The object on which to invoke the method /// The argument list for the method /// The return value from the invoked method - object Invoke(object fixture, params object[] args); + object? Invoke(object? fixture, params object?[]? args); #endregion } diff --git a/src/NUnitFramework/framework/Interfaces/IParameterDataProvider.cs b/src/NUnitFramework/framework/Interfaces/IParameterDataProvider.cs index 6627d4477f..563d12c573 100644 --- a/src/NUnitFramework/framework/Interfaces/IParameterDataProvider.cs +++ b/src/NUnitFramework/framework/Interfaces/IParameterDataProvider.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections; namespace NUnit.Framework.Interfaces diff --git a/src/NUnitFramework/framework/Interfaces/IParameterDataSource.cs b/src/NUnitFramework/framework/Interfaces/IParameterDataSource.cs index 21b5fa0b00..2b1f7cbed8 100644 --- a/src/NUnitFramework/framework/Interfaces/IParameterDataSource.cs +++ b/src/NUnitFramework/framework/Interfaces/IParameterDataSource.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections; namespace NUnit.Framework.Interfaces diff --git a/src/NUnitFramework/framework/Interfaces/IParameterInfo.cs b/src/NUnitFramework/framework/Interfaces/IParameterInfo.cs index ed5bff9784..86e13a6987 100644 --- a/src/NUnitFramework/framework/Interfaces/IParameterInfo.cs +++ b/src/NUnitFramework/framework/Interfaces/IParameterInfo.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Reflection; diff --git a/src/NUnitFramework/framework/Interfaces/IPreFilter.cs b/src/NUnitFramework/framework/Interfaces/IPreFilter.cs index e9c3fc4f78..57176ba03a 100644 --- a/src/NUnitFramework/framework/Interfaces/IPreFilter.cs +++ b/src/NUnitFramework/framework/Interfaces/IPreFilter.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Reflection; diff --git a/src/NUnitFramework/framework/Interfaces/IPropertyBag.cs b/src/NUnitFramework/framework/Interfaces/IPropertyBag.cs index 9c91028938..4c45eca4d0 100644 --- a/src/NUnitFramework/framework/Interfaces/IPropertyBag.cs +++ b/src/NUnitFramework/framework/Interfaces/IPropertyBag.cs @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; +#nullable enable + using System.Collections; using System.Collections.Generic; @@ -87,7 +88,7 @@ public interface IPropertyBag : IXmlNodeBuilder /// one if multiple values are present and returning /// null if the value is not found. /// - object Get(string key); + object? Get(string key); /// /// Gets a flag indicating whether the specified key has diff --git a/src/NUnitFramework/framework/Interfaces/IReflectionInfo.cs b/src/NUnitFramework/framework/Interfaces/IReflectionInfo.cs index 6db123ba89..cbc8d30eba 100644 --- a/src/NUnitFramework/framework/Interfaces/IReflectionInfo.cs +++ b/src/NUnitFramework/framework/Interfaces/IReflectionInfo.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + namespace NUnit.Framework.Interfaces { /// diff --git a/src/NUnitFramework/framework/Interfaces/ISimpleTestBuilder.cs b/src/NUnitFramework/framework/Interfaces/ISimpleTestBuilder.cs index bbd72a2263..d7741c1aef 100644 --- a/src/NUnitFramework/framework/Interfaces/ISimpleTestBuilder.cs +++ b/src/NUnitFramework/framework/Interfaces/ISimpleTestBuilder.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using NUnit.Framework.Internal; // TODO: We shouldn't reference this in the interface namespace NUnit.Framework.Interfaces @@ -38,6 +40,6 @@ public interface ISimpleTestBuilder /// /// The method to be used as a test /// The TestSuite to which the method will be added - TestMethod BuildFrom(IMethodInfo method, Test suite); + TestMethod BuildFrom(IMethodInfo method, Test? suite); } } diff --git a/src/NUnitFramework/framework/Interfaces/ISuiteBuilder.cs b/src/NUnitFramework/framework/Interfaces/ISuiteBuilder.cs index 8ecbd0d645..1e56346d31 100644 --- a/src/NUnitFramework/framework/Interfaces/ISuiteBuilder.cs +++ b/src/NUnitFramework/framework/Interfaces/ISuiteBuilder.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using NUnit.Framework.Internal; namespace NUnit.Framework.Interfaces diff --git a/src/NUnitFramework/framework/Interfaces/ITest.cs b/src/NUnitFramework/framework/Interfaces/ITest.cs index 17c40b622a..3166708a3a 100644 --- a/src/NUnitFramework/framework/Interfaces/ITest.cs +++ b/src/NUnitFramework/framework/Interfaces/ITest.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + namespace NUnit.Framework.Interfaces { /// @@ -55,25 +57,25 @@ public interface ITest : IXmlNodeBuilder /// Gets the name of the class containing this test. Returns /// null if the test is not associated with a class. /// - string ClassName { get; } + string? ClassName { get; } /// /// Gets the name of the method implementing this test. /// Returns null if the test is not implemented as a method. /// - string MethodName { get; } + string? MethodName { get; } /// /// Gets the Type of the test fixture, if applicable, or /// null if no fixture type is associated with this test. /// - ITypeInfo TypeInfo { get; } + ITypeInfo? TypeInfo { get; } /// /// Gets the method which declares the test, or /// if no method is associated with this test. /// - IMethodInfo Method { get; } + IMethodInfo? Method { get; } /// /// Gets the RunState of the test, indicating whether it can be run. @@ -94,7 +96,7 @@ public interface ITest : IXmlNodeBuilder /// Gets the parent test, if any. /// /// The parent test or null if none exists. - ITest Parent { get; } + ITest? Parent { get; } /// /// Returns true if this is a test suite @@ -116,12 +118,12 @@ public interface ITest : IXmlNodeBuilder /// /// Gets a fixture object for running this test. /// - object Fixture { get; } + object? Fixture { get; } /// /// The arguments to use in creating the test or empty array if none are required. /// - object[] Arguments { get; } + object?[] Arguments { get; } } } diff --git a/src/NUnitFramework/framework/Interfaces/ITestBuilder.cs b/src/NUnitFramework/framework/Interfaces/ITestBuilder.cs index 9f4074406c..64b58e8ac8 100644 --- a/src/NUnitFramework/framework/Interfaces/ITestBuilder.cs +++ b/src/NUnitFramework/framework/Interfaces/ITestBuilder.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections.Generic; using NUnit.Framework.Internal; // TODO: We shouldn't reference this in the interface @@ -39,6 +41,6 @@ public interface ITestBuilder /// /// The method to be used as a test /// The TestSuite to which the method will be added - IEnumerable BuildFrom(IMethodInfo method, Test suite); + IEnumerable BuildFrom(IMethodInfo method, Test? suite); } } diff --git a/src/NUnitFramework/framework/Interfaces/ITestCaseBuilder.cs b/src/NUnitFramework/framework/Interfaces/ITestCaseBuilder.cs index fc3add60ef..1d27375c9d 100644 --- a/src/NUnitFramework/framework/Interfaces/ITestCaseBuilder.cs +++ b/src/NUnitFramework/framework/Interfaces/ITestCaseBuilder.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using NUnit.Framework.Internal; namespace NUnit.Framework.Interfaces @@ -43,7 +45,7 @@ public interface ITestCaseBuilder /// /// The test method to examine /// The suite being populated - bool CanBuildFrom(IMethodInfo method, Test suite); + bool CanBuildFrom(IMethodInfo method, Test? suite); /// /// Builds a single test from the specified method and context, @@ -51,6 +53,6 @@ public interface ITestCaseBuilder /// /// The method to be used as a test case /// The test suite being populated, or null - Test BuildFrom(IMethodInfo method, Test suite); + Test? BuildFrom(IMethodInfo method, Test? suite); } } diff --git a/src/NUnitFramework/framework/Interfaces/ITestCaseData.cs b/src/NUnitFramework/framework/Interfaces/ITestCaseData.cs index db47d959bf..338efce41e 100644 --- a/src/NUnitFramework/framework/Interfaces/ITestCaseData.cs +++ b/src/NUnitFramework/framework/Interfaces/ITestCaseData.cs @@ -21,8 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; -using System.Collections; +#nullable enable namespace NUnit.Framework.Interfaces { @@ -36,7 +35,7 @@ public interface ITestCaseData : ITestData /// /// Gets the expected result of the test case /// - object ExpectedResult { get; } + object? ExpectedResult { get; } /// /// Returns true if an expected result has been set diff --git a/src/NUnitFramework/framework/Interfaces/ITestData.cs b/src/NUnitFramework/framework/Interfaces/ITestData.cs index 7b707ebb71..1d0718e50f 100644 --- a/src/NUnitFramework/framework/Interfaces/ITestData.cs +++ b/src/NUnitFramework/framework/Interfaces/ITestData.cs @@ -21,8 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; -using System.Collections; +#nullable enable namespace NUnit.Framework.Interfaces { @@ -35,7 +34,7 @@ public interface ITestData /// /// Gets the name to be used for the test /// - string TestName { get; } + string? TestName { get; } /// /// Gets the RunState for this test case. @@ -45,7 +44,7 @@ public interface ITestData /// /// Gets the argument list to be provided to the test /// - object[] Arguments { get; } + object?[] Arguments { get; } /// /// Gets the property dictionary for the test case diff --git a/src/NUnitFramework/framework/Interfaces/ITestFilter.cs b/src/NUnitFramework/framework/Interfaces/ITestFilter.cs index e1aa844d2e..ccbfca737d 100644 --- a/src/NUnitFramework/framework/Interfaces/ITestFilter.cs +++ b/src/NUnitFramework/framework/Interfaces/ITestFilter.cs @@ -21,7 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; +#nullable enable namespace NUnit.Framework.Interfaces { diff --git a/src/NUnitFramework/framework/Interfaces/ITestFixtureData.cs b/src/NUnitFramework/framework/Interfaces/ITestFixtureData.cs index af4c99d05b..d3b1c7a662 100644 --- a/src/NUnitFramework/framework/Interfaces/ITestFixtureData.cs +++ b/src/NUnitFramework/framework/Interfaces/ITestFixtureData.cs @@ -21,8 +21,9 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -using System.Collections; namespace NUnit.Framework.Interfaces { @@ -36,6 +37,6 @@ public interface ITestFixtureData : ITestData /// /// Get the TypeArgs if separately set /// - Type[] TypeArgs { get; } + Type[]? TypeArgs { get; } } } diff --git a/src/NUnitFramework/framework/Interfaces/ITestListener.cs b/src/NUnitFramework/framework/Interfaces/ITestListener.cs index 33b5a753cb..2a77f17c32 100644 --- a/src/NUnitFramework/framework/Interfaces/ITestListener.cs +++ b/src/NUnitFramework/framework/Interfaces/ITestListener.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + namespace NUnit.Framework.Interfaces { /// diff --git a/src/NUnitFramework/framework/Interfaces/ITestResult.cs b/src/NUnitFramework/framework/Interfaces/ITestResult.cs index ff9d914670..a11c4c6548 100644 --- a/src/NUnitFramework/framework/Interfaces/ITestResult.cs +++ b/src/NUnitFramework/framework/Interfaces/ITestResult.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; @@ -84,7 +86,7 @@ DateTime EndTime /// Gets the message associated with a test /// failure or with not running the test /// - string Message + string? Message { get; } @@ -92,7 +94,16 @@ string Message /// /// Gets any stack trace associated with an /// error or failure. - string StackTrace + string? StackTrace + { + get; + } + + /// + /// Gets the total number of tests executed + /// when running the test and all its children. + /// + int TotalCount { get; } diff --git a/src/NUnitFramework/framework/Interfaces/ITypeInfo.cs b/src/NUnitFramework/framework/Interfaces/ITypeInfo.cs index 7d73d70ddf..941a27c9cd 100644 --- a/src/NUnitFramework/framework/Interfaces/ITypeInfo.cs +++ b/src/NUnitFramework/framework/Interfaces/ITypeInfo.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Reflection; using NUnit.Compatibility; @@ -42,7 +44,7 @@ public interface ITypeInfo : IReflectionInfo /// /// Gets the base type of this type as an ITypeInfo /// - ITypeInfo BaseType { get; } + ITypeInfo? BaseType { get; } /// /// Returns true if the Type wrapped is equal to the argument @@ -111,7 +113,7 @@ public interface ITypeInfo : IReflectionInfo /// /// Get the display name for an object of this type, constructed with specific arguments /// - string GetDisplayName(object[] args); + string GetDisplayName(object?[]? args); /// /// Returns a Type representing a generic type definition from which this Type can be constructed. @@ -137,7 +139,7 @@ public interface ITypeInfo : IReflectionInfo /// /// Gets the public constructor taking the specified argument Types /// - ConstructorInfo GetConstructor(Type[] argTypes); + ConstructorInfo? GetConstructor(Type[] argTypes); /// /// Returns a value indicating whether this Type has a public constructor taking the specified argument Types. @@ -147,7 +149,7 @@ public interface ITypeInfo : IReflectionInfo /// /// Construct an object of this Type, using the specified arguments. /// - object Construct(object[] args); + object Construct(object?[]? args); #endregion } diff --git a/src/NUnitFramework/framework/Interfaces/IXmlNodeBuilder.cs b/src/NUnitFramework/framework/Interfaces/IXmlNodeBuilder.cs index 96b2fbf284..52d2492ed0 100644 --- a/src/NUnitFramework/framework/Interfaces/IXmlNodeBuilder.cs +++ b/src/NUnitFramework/framework/Interfaces/IXmlNodeBuilder.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2015 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; namespace NUnit.Framework.Interfaces diff --git a/src/NUnitFramework/framework/Interfaces/ResultState.cs b/src/NUnitFramework/framework/Interfaces/ResultState.cs index 48fbbdeddb..62ec7a7b82 100644 --- a/src/NUnitFramework/framework/Interfaces/ResultState.cs +++ b/src/NUnitFramework/framework/Interfaces/ResultState.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,6 +21,10 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + +using System; +using System.Collections.Generic; using System.Text; namespace NUnit.Framework.Interfaces @@ -32,7 +36,7 @@ namespace NUnit.Framework.Interfaces /// skipped or was inconclusive. The Label provides a more /// detailed breakdown for use by client runners. /// - public class ResultState + public class ResultState : IEquatable { #region Constructors @@ -49,7 +53,7 @@ public ResultState(TestStatus status) : this (status, string.Empty, FailureSite. /// /// The TestStatus. /// The label. - public ResultState(TestStatus status, string label) : this (status, label, FailureSite.Test) + public ResultState(TestStatus status, string? label) : this (status, label, FailureSite.Test) { } @@ -68,7 +72,7 @@ public ResultState(TestStatus status, FailureSite site) : this(status, string.Em /// The TestStatus. /// The label. /// The stage at which the result was produced - public ResultState(TestStatus status, string label, FailureSite site) + public ResultState(TestStatus status, string? label, FailureSite site) { Status = status; Label = label == null ? string.Empty : label; @@ -83,9 +87,9 @@ public ResultState(TestStatus status, string label, FailureSite site) /// The result is inconclusive /// public readonly static ResultState Inconclusive = new ResultState(TestStatus.Inconclusive); - + /// - /// The test has been skipped. + /// The test has been skipped. /// public readonly static ResultState Skipped = new ResultState(TestStatus.Skipped); @@ -143,7 +147,7 @@ public ResultState(TestStatus status, string label, FailureSite site) /// A suite is marked ignored because one or more child tests were ignored /// public readonly static ResultState ChildIgnored = ResultState.Ignored.WithSite(FailureSite.Child); - + /// /// A suite failed in its OneTimeSetUp /// @@ -171,7 +175,7 @@ public ResultState(TestStatus status, string label, FailureSite site) /// /// Gets the label under which this test result is - /// categorized, if any. + /// categorized, or if none. /// public string Label { get; } @@ -206,32 +210,33 @@ public bool Matches(ResultState other) #endregion - #region Equals Override + #region Equality - /// - /// Determines whether the specified , is equal to this instance. - /// - /// The to compare with this instance. - /// - /// true if the specified is equal to this instance; otherwise, false. - /// - public override bool Equals(object obj) + /// Determines whether the specified object is equal to the current object. + /// The object to compare with the current object. + public override bool Equals(object? obj) { - var other = obj as ResultState; - if (object.ReferenceEquals(other, null)) return false; + return Equals(obj as ResultState); + } - return Status.Equals(other.Status) && Label.Equals(other.Label) && Site.Equals(other.Site); + /// Indicates whether the current object is equal to another object of the same type. + /// An object to compare with this object. + public bool Equals(ResultState? other) + { + return other != null && + Status == other.Status && + Label == other.Label && + Site == other.Site; } - /// - /// Returns a hash code for this instance. - /// - /// - /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. - /// + /// Serves as the default hash function. public override int GetHashCode() { - return (int)Status << 8 + (int)Site ^ Label.GetHashCode(); ; + var hashCode = -665355758; + hashCode = hashCode * -1521134295 + Status.GetHashCode(); + hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(Label); + hashCode = hashCode * -1521134295 + Site.GetHashCode(); + return hashCode; } #endregion @@ -241,7 +246,7 @@ public override int GetHashCode() /// /// Overload == operator for ResultStates /// - public static bool operator ==(ResultState left, ResultState right) + public static bool operator ==(ResultState? left, ResultState? right) { if (object.ReferenceEquals(left, null)) return object.ReferenceEquals(right, null); @@ -252,7 +257,7 @@ public override int GetHashCode() /// /// Overload != operator for ResultStates /// - public static bool operator !=(ResultState left, ResultState right) + public static bool operator !=(ResultState? left, ResultState? right) { return !(left == right); } diff --git a/src/NUnitFramework/framework/Interfaces/TNode.cs b/src/NUnitFramework/framework/Interfaces/TNode.cs index 8852c7dfea..117273ccd8 100644 --- a/src/NUnitFramework/framework/Interfaces/TNode.cs +++ b/src/NUnitFramework/framework/Interfaces/TNode.cs @@ -21,15 +21,13 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; +using System.Diagnostics.CodeAnalysis; using System.Text; -using System.Text.RegularExpressions; using System.Xml; -#if NETSTANDARD1_4 -using System.Xml.Linq; -#endif - namespace NUnit.Framework.Interfaces { /// @@ -60,7 +58,7 @@ public TNode(string name) /// /// The name of the node /// The text content of the node - public TNode(string name, string value) : this(name, value, false) { } + public TNode(string name, string? value) : this(name, value, false) { } /// /// Constructs a new instance of TNode with a value @@ -68,7 +66,7 @@ public TNode(string name) /// The name of the node /// The text content of the node /// Flag indicating whether to use CDATA when writing the text - public TNode(string name, string value, bool valueIsCDATA) + public TNode(string name, string? value, bool valueIsCDATA) : this(name) { Value = value; @@ -87,7 +85,7 @@ public TNode(string name, string value, bool valueIsCDATA) /// /// Gets the value of the node /// - public string Value { get; set; } + public string? Value { get; set; } /// /// Gets a flag indicating whether the value should be output using CDATA. @@ -107,7 +105,7 @@ public TNode(string name, string value, bool valueIsCDATA) /// /// Gets the first ChildNode /// - public TNode FirstChild + public TNode? FirstChild { get { return ChildNodes.Count == 0 ? null : ChildNodes[0]; } } @@ -143,13 +141,9 @@ public string OuterXml /// A TNode public static TNode FromXml(string xmlText) { -#if NETSTANDARD1_4 - return FromXml(XElement.Parse(xmlText)); -#else var doc = new XmlDocument(); doc.LoadXml(xmlText); return FromXml(doc.FirstChild); -#endif } #endregion @@ -212,7 +206,7 @@ public void AddAttribute(string name, string value) /// /// /// - public TNode SelectSingleNode(string xpath) + public TNode? SelectSingleNode(string xpath) { NodeList nodes = SelectNodes(xpath); @@ -261,20 +255,6 @@ public void WriteTo(XmlWriter writer) #region Helper Methods -#if NETSTANDARD1_4 - private static TNode FromXml(XElement xElement) - { - TNode tNode = new TNode(xElement.Name.ToString(), xElement.Value); - - foreach (var attr in xElement.Attributes()) - tNode.AddAttribute(attr.Name.ToString(), attr.Value); - - foreach (var child in xElement.Elements()) - tNode.ChildNodes.Add(FromXml(child)); - - return tNode; - } -#else private static TNode FromXml(XmlNode xmlNode) { TNode tNode = new TNode(xmlNode.Name, xmlNode.InnerText); @@ -288,7 +268,6 @@ private static TNode FromXml(XmlNode xmlNode) return tNode; } -#endif private static NodeList ApplySelection(NodeList nodeList, string xpath) { @@ -299,7 +278,7 @@ private static NodeList ApplySelection(NodeList nodeList, string xpath) throw new ArgumentException("XPath expressions with '//' are not supported", nameof(xpath)); string head = xpath; - string tail = null; + string? tail = null; int slash = xpath.IndexOf('/'); if (slash >= 0) @@ -321,11 +300,12 @@ private static NodeList ApplySelection(NodeList nodeList, string xpath) : resultNodes; } - private static string EscapeInvalidXmlCharacters(string str) + [return: NotNullIfNotNull("str")] + private static string? EscapeInvalidXmlCharacters(string? str) { if (str == null) return null; - StringBuilder builder = null; + StringBuilder? builder = null; for (int i = 0; i < str.Length; i++) { char c = str[i]; @@ -351,7 +331,7 @@ private static string EscapeInvalidXmlCharacters(string str) if (builder != null) builder.Append(c); } - // Also check if the char is actually a high/low surogate pair of two characters. + // Also check if the char is actually a high/low surrogate pair of two characters. // If it is, then it is a valid XML character (from above based on the surrogate blocks). else if (char.IsHighSurrogate(c) && i + 1 != str.Length && @@ -394,7 +374,7 @@ private static string CharToUnicodeSequence(char symbol) private void WriteCDataTo(XmlWriter writer) { int start = 0; - string text = Value; + string text = Value ?? throw new InvalidOperationException(); while (true) { @@ -420,8 +400,8 @@ private void WriteCDataTo(XmlWriter writer) class NodeFilter { private readonly string _nodeName; - private readonly string _propName; - private readonly string _propValue; + private readonly string? _propName; + private readonly string? _propValue; public NodeFilter(string xpath) { @@ -478,16 +458,11 @@ public class AttributeDictionary : System.Collections.Generic.Dictionary /// The key. /// Value of the attribute or null - public new string this[string key] + public new string? this[string key] { get { - string value; - - if (TryGetValue(key, out value)) - return value; - - return null; + return TryGetValue(key, out var value) ? value : null; } } } diff --git a/src/NUnitFramework/framework/Interfaces/TestAttachment.cs b/src/NUnitFramework/framework/Interfaces/TestAttachment.cs index 554922a348..0bce840bdb 100644 --- a/src/NUnitFramework/framework/Interfaces/TestAttachment.cs +++ b/src/NUnitFramework/framework/Interfaces/TestAttachment.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2017 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + namespace NUnit.Framework.Interfaces { /// @@ -35,16 +37,16 @@ public class TestAttachment public string FilePath { get; } /// - /// User specifed description of attachment. May be null. + /// User specified description of attachment. May be null. /// - public string Description { get; } + public string? Description { get; } /// /// Creates a TestAttachment class to represent a file attached to a test result. /// /// Absolute file path to attachment file - /// User specifed description of attachment. May be null. - public TestAttachment(string filePath, string description) + /// User specified description of attachment. May be null. + public TestAttachment(string filePath, string? description) { FilePath = filePath; Description = description; diff --git a/src/NUnitFramework/framework/Interfaces/TestMessage.cs b/src/NUnitFramework/framework/Interfaces/TestMessage.cs index 03bf132465..294e4a42c5 100644 --- a/src/NUnitFramework/framework/Interfaces/TestMessage.cs +++ b/src/NUnitFramework/framework/Interfaces/TestMessage.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Diagnostics; diff --git a/src/NUnitFramework/framework/Interfaces/TestOutput.cs b/src/NUnitFramework/framework/Interfaces/TestOutput.cs index ad6532d293..a5be0c4748 100644 --- a/src/NUnitFramework/framework/Interfaces/TestOutput.cs +++ b/src/NUnitFramework/framework/Interfaces/TestOutput.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; namespace NUnit.Framework.Interfaces diff --git a/src/NUnitFramework/framework/Internal/Abstractions/DebuggerProxy.cs b/src/NUnitFramework/framework/Internal/Abstractions/DebuggerProxy.cs new file mode 100644 index 0000000000..c4e51f7991 --- /dev/null +++ b/src/NUnitFramework/framework/Internal/Abstractions/DebuggerProxy.cs @@ -0,0 +1,38 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System.Diagnostics; + +namespace NUnit.Framework.Internal.Abstractions +{ + /// + /// A production implementation that delegates directly to .NET's . + /// + internal sealed class DebuggerProxy : IDebugger + { + /// + /// Returns whether a debugger is currently attached to the process + /// + public bool IsAttached => Debugger.IsAttached; + } +} diff --git a/src/NUnitFramework/framework/Internal/Abstractions/IDebugger.cs b/src/NUnitFramework/framework/Internal/Abstractions/IDebugger.cs new file mode 100644 index 0000000000..1740b31560 --- /dev/null +++ b/src/NUnitFramework/framework/Internal/Abstractions/IDebugger.cs @@ -0,0 +1,36 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +namespace NUnit.Framework.Internal.Abstractions +{ + /// + /// A layer of abstraction around to facilitate testing. + /// + internal interface IDebugger + { + /// + /// Whether a debugger is currently attached to the process. + /// + bool IsAttached { get; } + } +} diff --git a/src/NUnitFramework/framework/Internal/AssemblyHelper.cs b/src/NUnitFramework/framework/Internal/AssemblyHelper.cs index 6f9280a466..c7562f4367 100644 --- a/src/NUnitFramework/framework/Internal/AssemblyHelper.cs +++ b/src/NUnitFramework/framework/Internal/AssemblyHelper.cs @@ -33,13 +33,8 @@ namespace NUnit.Framework.Internal /// public static class AssemblyHelper { -#if NETSTANDARD1_4 - const string UriSchemeFile = "file"; - const string SchemeDelimiter = "://"; -#else static readonly string UriSchemeFile = Uri.UriSchemeFile; static readonly string SchemeDelimiter = Uri.SchemeDelimiter; -#endif #region GetAssemblyPath @@ -52,16 +47,12 @@ public static class AssemblyHelper /// The path. public static string GetAssemblyPath(Assembly assembly) { -#if NETSTANDARD1_4 - return assembly.ManifestModule.FullyQualifiedName; -#else string codeBase = assembly.CodeBase; if (IsFileUri(codeBase)) return GetAssemblyPathFromCodeBase(codeBase); return assembly.Location; -#endif } #endregion @@ -96,7 +87,7 @@ public static AssemblyName GetAssemblyName(Assembly assembly) #region Load -#if NETSTANDARD1_4 || NETSTANDARD2_0 +#if NETSTANDARD2_0 private sealed class ReflectionAssemblyLoader { private static ReflectionAssemblyLoader instance; diff --git a/src/NUnitFramework/framework/Internal/AsyncToSyncAdapter.cs b/src/NUnitFramework/framework/Internal/AsyncToSyncAdapter.cs index 678458bac9..0534ecd6d2 100644 --- a/src/NUnitFramework/framework/Internal/AsyncToSyncAdapter.cs +++ b/src/NUnitFramework/framework/Internal/AsyncToSyncAdapter.cs @@ -49,21 +49,20 @@ public static object Await(Func invoke) using (InitializeExecutionEnvironment()) { - var awaitAdapter = AwaitAdapter.FromAwaitable(invoke.Invoke()); + var awaiter = AwaitAdapter.FromAwaitable(invoke.Invoke()); - if (!awaitAdapter.IsCompleted) + if (!awaiter.IsCompleted) { var waitStrategy = MessagePumpStrategy.FromCurrentSynchronizationContext(); - waitStrategy.WaitForCompletion(awaitAdapter); + waitStrategy.WaitForCompletion(awaiter); } - return awaitAdapter.GetResult(); + return awaiter.GetResult(); } } private static IDisposable InitializeExecutionEnvironment() { -#if APARTMENT_STATE if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) { var context = SynchronizationContext.Current; @@ -81,7 +80,7 @@ private static IDisposable InitializeExecutionEnvironment() }); } } -#endif + return null; } diff --git a/src/NUnitFramework/framework/Internal/AwaitAdapter.cs b/src/NUnitFramework/framework/Internal/AwaitAdapter.cs index 0517a6c47e..0585671d60 100644 --- a/src/NUnitFramework/framework/Internal/AwaitAdapter.cs +++ b/src/NUnitFramework/framework/Internal/AwaitAdapter.cs @@ -25,6 +25,9 @@ namespace NUnit.Framework.Internal { + /// + /// Adapts various styles of asynchronous waiting to a common API. + /// internal abstract class AwaitAdapter { public abstract bool IsCompleted { get; } @@ -34,12 +37,16 @@ internal abstract class AwaitAdapter public static bool IsAwaitable(Type awaitableType) { - return CSharpPatternBasedAwaitAdapter.IsAwaitable(awaitableType); + return + CSharpPatternBasedAwaitAdapter.IsAwaitable(awaitableType) + || FSharpAsyncAwaitAdapter.IsAwaitable(awaitableType); } public static Type GetResultType(Type awaitableType) { - return CSharpPatternBasedAwaitAdapter.GetResultType(awaitableType); + return + CSharpPatternBasedAwaitAdapter.GetResultType(awaitableType) + ?? FSharpAsyncAwaitAdapter.GetResultType(awaitableType); } public static AwaitAdapter FromAwaitable(object awaitable) @@ -56,9 +63,11 @@ public static AwaitAdapter FromAwaitable(object awaitable) if (task != null) return TaskAwaitAdapter.Create(task); #endif - // Await all the (C#) things - var patternBasedAdapter = CSharpPatternBasedAwaitAdapter.TryCreate(awaitable); - if (patternBasedAdapter != null) return patternBasedAdapter; + // Await all the (C# and F#) things + var adapter = + CSharpPatternBasedAwaitAdapter.TryCreate(awaitable) + ?? FSharpAsyncAwaitAdapter.TryCreate(awaitable); + if (adapter != null) return adapter; #if NET40 // If System.Threading.Tasks.Task does not have a GetAwaiter instance method diff --git a/src/NUnitFramework/framework/Internal/Builders/CombinatorialStrategy.cs b/src/NUnitFramework/framework/Internal/Builders/CombinatorialStrategy.cs index 034e3d4fab..bdd279bb51 100644 --- a/src/NUnitFramework/framework/Internal/Builders/CombinatorialStrategy.cs +++ b/src/NUnitFramework/framework/Internal/Builders/CombinatorialStrategy.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2008 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; +#nullable enable + using System.Collections; using System.Collections.Generic; using NUnit.Framework.Interfaces; @@ -53,7 +54,7 @@ public IEnumerable GetTestCases(IEnumerable[] sources) return testCases; } - object[] testdata = new object[sources.Length]; + object?[] testdata = new object?[sources.Length]; for (int i = 0; i < sources.Length; i++) testdata[i] = enumerators[i].Current; diff --git a/src/NUnitFramework/framework/Internal/Builders/DatapointProvider.cs b/src/NUnitFramework/framework/Internal/Builders/DatapointProvider.cs index 0e8215e8cd..78a05338b8 100644 --- a/src/NUnitFramework/framework/Internal/Builders/DatapointProvider.cs +++ b/src/NUnitFramework/framework/Internal/Builders/DatapointProvider.cs @@ -20,6 +20,9 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** + +#nullable enable + using System; using System.Reflection; using System.Collections; @@ -35,6 +38,8 @@ namespace NUnit.Framework.Internal.Builders /// public class DatapointProvider : IParameterDataProvider { + private static readonly ProviderCache ProviderCache = new ProviderCache(); + #region IDataPointProvider Members /// @@ -71,7 +76,7 @@ public bool HasDataFor(IParameterInfo parameter) /// The parameter of a parameterized test public IEnumerable GetDataFor(IParameterInfo parameter) { - var datapoints = new List(); + var datapoints = new List(); Type parameterType = parameter.ParameterType; Type fixtureType = parameter.Method.TypeInfo.Type; @@ -93,11 +98,11 @@ public IEnumerable GetDataFor(IParameterInfo parameter) { if (GetElementTypeFromMemberInfo(member) == parameterType) { - object instance; + object? instance; - FieldInfo field = member as FieldInfo; - PropertyInfo property = member as PropertyInfo; - MethodInfo method = member as MethodInfo; + FieldInfo? field = member as FieldInfo; + PropertyInfo? property = member as PropertyInfo; + MethodInfo? method = member as MethodInfo; if (field != null) { instance = field.IsStatic ? null : ProviderCache.GetInstanceOf(fixtureType); @@ -151,7 +156,7 @@ public IEnumerable GetDataFor(IParameterInfo parameter) return datapoints; } - private Type GetTypeFromMemberInfo(MemberInfo member) + private Type? GetTypeFromMemberInfo(MemberInfo member) { var field = member as FieldInfo; if (field != null) @@ -168,9 +173,9 @@ private Type GetTypeFromMemberInfo(MemberInfo member) return null; } - private Type GetElementTypeFromMemberInfo(MemberInfo member) + private Type? GetElementTypeFromMemberInfo(MemberInfo member) { - Type type = GetTypeFromMemberInfo(member); + Type? type = GetTypeFromMemberInfo(member); if (type == null) return null; diff --git a/src/NUnitFramework/framework/Internal/Builders/DefaultSuiteBuilder.cs b/src/NUnitFramework/framework/Internal/Builders/DefaultSuiteBuilder.cs index 9e2160ec5a..4c351b9ef1 100644 --- a/src/NUnitFramework/framework/Internal/Builders/DefaultSuiteBuilder.cs +++ b/src/NUnitFramework/framework/Internal/Builders/DefaultSuiteBuilder.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; using NUnit.Framework.Interfaces; @@ -143,7 +145,7 @@ private TestSuite BuildMultipleFixtures(ITypeInfo typeInfo, IEnumerable /// The type being examined for attributes - private IFixtureBuilder[] GetFixtureBuilderAttributes(ITypeInfo typeInfo) + private IFixtureBuilder[] GetFixtureBuilderAttributes(ITypeInfo? typeInfo) { IFixtureBuilder[] attrs = new IFixtureBuilder[0]; diff --git a/src/NUnitFramework/framework/Internal/Builders/DefaultTestCaseBuilder.cs b/src/NUnitFramework/framework/Internal/Builders/DefaultTestCaseBuilder.cs index cda4a1051d..4952fa7b33 100644 --- a/src/NUnitFramework/framework/Internal/Builders/DefaultTestCaseBuilder.cs +++ b/src/NUnitFramework/framework/Internal/Builders/DefaultTestCaseBuilder.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections.Generic; using System.Linq; using NUnit.Framework.Interfaces; @@ -88,7 +90,7 @@ public Test BuildFrom(IMethodInfo method) /// /// An IMethodInfo for the method being used as a test method /// The test suite being built, to which the new test would be added - public bool CanBuildFrom(IMethodInfo method, Test parentSuite) + public bool CanBuildFrom(IMethodInfo method, Test? parentSuite) { return CanBuildFrom(method); } @@ -99,7 +101,7 @@ public bool CanBuildFrom(IMethodInfo method, Test parentSuite) /// /// The method for which a test is to be built /// The test fixture being populated, or null - public Test BuildFrom(IMethodInfo method, Test parentSuite) + public Test BuildFrom(IMethodInfo method, Test? parentSuite) { var tests = new List(); @@ -147,7 +149,7 @@ private Test BuildParameterizedMethodSuite(IMethodInfo method, IEnumerable /// The MethodInfo for which a test is to be built /// The test suite for which the method is being built - private Test BuildSingleTestMethod(IMethodInfo method, Test suite) + private Test BuildSingleTestMethod(IMethodInfo method, Test? suite) { var builders = method.GetCustomAttributes(false); return builders.Length > 0 diff --git a/src/NUnitFramework/framework/Internal/Builders/NUnitTestCaseBuilder.cs b/src/NUnitFramework/framework/Internal/Builders/NUnitTestCaseBuilder.cs index c5acbb8ecd..89000bff9a 100644 --- a/src/NUnitFramework/framework/Internal/Builders/NUnitTestCaseBuilder.cs +++ b/src/NUnitFramework/framework/Internal/Builders/NUnitTestCaseBuilder.cs @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; +#nullable enable + using NUnit.Framework.Interfaces; namespace NUnit.Framework.Internal.Builders @@ -50,7 +51,7 @@ public NUnitTestCaseBuilder() /// The MethodInfo from which to construct the TestMethod /// The suite or fixture to which the new test will be added /// The ParameterSet to be used, or null - public TestMethod BuildTestMethod(IMethodInfo method, Test parentSuite, TestCaseParameters parms) + public TestMethod BuildTestMethod(IMethodInfo method, Test? parentSuite, TestCaseParameters? parms) { var testMethod = new TestMethod(method, parentSuite) { @@ -61,7 +62,7 @@ public TestMethod BuildTestMethod(IMethodInfo method, Test parentSuite, TestCase CheckTestMethodSignature(testMethod, parms); - if (parms == null || parms.Arguments == null) + if (parms == null || parms.Arguments.Length == 0) testMethod.ApplyAttributesToTest(method.MethodInfo); // NOTE: After the call to CheckTestMethodSignature, the Method @@ -114,7 +115,7 @@ public TestMethod BuildTestMethod(IMethodInfo method, Test parentSuite, TestCase /// True if the method signature is valid, false if not private static bool CheckTestMethodAttributes(TestMethod testMethod) { - if (testMethod.Method.MethodInfo.GetAttributes(true).Length > 1) + if (testMethod.Method.GetCustomAttributes(true).Length > 1) return MarkAsNotRunnable(testMethod, "Multiple attributes that repeat a test may cause issues."); return true; @@ -140,7 +141,7 @@ private static bool CheckTestMethodAttributes(TestMethod testMethod) /// The return value is no longer used internally, but is retained /// for testing purposes. /// - private static bool CheckTestMethodSignature(TestMethod testMethod, TestCaseParameters parms) + private static bool CheckTestMethodSignature(TestMethod testMethod, TestCaseParameters? parms) { if (testMethod.Method.IsAbstract) return MarkAsNotRunnable(testMethod, "Method is abstract"); @@ -160,7 +161,7 @@ private static bool CheckTestMethodSignature(TestMethod testMethod, TestCasePara int maxArgsNeeded = parameters.Length; - object[] arglist = null; + object?[]? arglist = null; int argsProvided = 0; if (parms != null) @@ -184,7 +185,7 @@ private static bool CheckTestMethodSignature(TestMethod testMethod, TestCasePara if (returnType == typeof(void)) return MarkAsNotRunnable(testMethod, "Async test method must have non-void return type"); - var voidResult = AwaitAdapter.GetResultType(returnType) == typeof(void); + var voidResult = Reflect.IsVoidOrUnit(AwaitAdapter.GetResultType(returnType)); if (!voidResult && (parms == null || !parms.HasExpectedResult)) return MarkAsNotRunnable(testMethod, @@ -194,7 +195,7 @@ private static bool CheckTestMethodSignature(TestMethod testMethod, TestCasePara return MarkAsNotRunnable(testMethod, "Async test method must return an awaitable with a non-void result when a result is expected"); } - else if (returnType == typeof(void)) + else if (Reflect.IsVoidOrUnit(returnType)) { if (parms != null && parms.HasExpectedResult) return MarkAsNotRunnable(testMethod, "Method returning void cannot have an expected result"); @@ -216,8 +217,7 @@ private static bool CheckTestMethodSignature(TestMethod testMethod, TestCasePara if (testMethod.Method.IsGenericMethodDefinition && arglist != null) { - Type[] typeArguments; - if (!new GenericMethodHelper(testMethod.Method.MethodInfo).TryGetTypeArguments(arglist, out typeArguments)) + if (!new GenericMethodHelper(testMethod.Method.MethodInfo).TryGetTypeArguments(arglist, out var typeArguments)) return MarkAsNotRunnable(testMethod, "Unable to determine type arguments for method"); testMethod.Method = testMethod.Method.MakeGenericMethod(typeArguments); diff --git a/src/NUnitFramework/framework/Internal/Builders/NUnitTestFixtureBuilder.cs b/src/NUnitFramework/framework/Internal/Builders/NUnitTestFixtureBuilder.cs index 16ec9a4d99..20c91495a7 100644 --- a/src/NUnitFramework/framework/Internal/Builders/NUnitTestFixtureBuilder.cs +++ b/src/NUnitFramework/framework/Internal/Builders/NUnitTestFixtureBuilder.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Linq; using System.Reflection; @@ -61,7 +63,7 @@ public class NUnitTestFixtureBuilder /// must always be returned, since the method is generally called /// because the user has marked the target class as a fixture. /// If something prevents the fixture from being used, it should - /// be returned nonetheless, labelled as non-runnable. + /// be returned nonetheless, labeled as non-runnable. /// /// An ITypeInfo for the fixture to be used. /// Filter used to select methods as tests. @@ -94,25 +96,25 @@ public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter, ITestFixtureDa { Guard.ArgumentNotNull(testFixtureData, nameof(testFixtureData)); - object[] arguments = testFixtureData.Arguments; + object?[] arguments = testFixtureData.Arguments; if (typeInfo.ContainsGenericParameters) { - Type[] typeArgs = testFixtureData.TypeArgs; + Type[]? typeArgs = testFixtureData.TypeArgs; if (typeArgs == null || typeArgs.Length == 0) { int cnt = 0; - foreach (object o in arguments) + foreach (object? o in arguments) if (o is Type) cnt++; else break; typeArgs = new Type[cnt]; for (int i = 0; i < cnt; i++) - typeArgs[i] = (Type)arguments[i]; + typeArgs[i] = (Type)arguments[i]!; if (cnt > 0) { - object[] args = new object[arguments.Length - cnt]; + object?[] args = new object?[arguments.Length - cnt]; for (int i = 0; i < args.Length; i++) args[i] = arguments[cnt + i]; @@ -168,8 +170,6 @@ public TestSuite BuildFrom(ITypeInfo typeInfo, IPreFilter filter, ITestFixtureDa if (fixture.RunState != RunState.NotRunnable) CheckTestFixtureIsValid(fixture); - fixture.ApplyAttributesToTest(typeInfo.Type.GetTypeInfo()); - AddTestCasesToFixture(fixture, filter); return fixture; @@ -198,7 +198,7 @@ private void AddTestCasesToFixture(TestFixture fixture, IPreFilter filter) { if (filter.IsMatch(fixture.TypeInfo.Type, method.MethodInfo)) { - Test test = BuildTestCase(method, fixture); + Test? test = BuildTestCase(method, fixture); if (test != null) fixture.Add(test); @@ -223,7 +223,7 @@ private void AddTestCasesToFixture(TestFixture fixture, IPreFilter filter) /// The method for which a test is to be created /// The test suite being built. /// A newly constructed Test - private Test BuildTestCase(IMethodInfo method, TestSuite suite) + private Test? BuildTestCase(IMethodInfo method, TestSuite suite) { return _testBuilder.CanBuildFrom(method, suite) ? _testBuilder.BuildFrom(method, suite) @@ -238,7 +238,7 @@ private static void CheckTestFixtureIsValid(TestFixture fixture) } else if (!fixture.TypeInfo.IsStaticClass) { - Type[] argTypes = Reflect.GetTypeArray(fixture.Arguments); + Type?[] argTypes = Reflect.GetTypeArray(fixture.Arguments); if (!Reflect.GetConstructors(fixture.TypeInfo.Type, argTypes).Any()) { diff --git a/src/NUnitFramework/framework/Internal/Builders/NamespaceTreeBuilder.cs b/src/NUnitFramework/framework/Internal/Builders/NamespaceTreeBuilder.cs index 25dc59ba16..35cc079b19 100644 --- a/src/NUnitFramework/framework/Internal/Builders/NamespaceTreeBuilder.cs +++ b/src/NUnitFramework/framework/Internal/Builders/NamespaceTreeBuilder.cs @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System.Collections; +#nullable enable + using System.Collections.Generic; namespace NUnit.Framework.Internal.Builders @@ -121,7 +122,7 @@ private TestSuite GetNamespaceSuite( string ns ) if (_namespaceIndex.ContainsKey(ns)) return _namespaceIndex[ns]; - TestSuite suite = null; + TestSuite suite; int index = ns.LastIndexOf("."); if( index == -1 ) @@ -167,7 +168,7 @@ private void AddSetUpFixture(TestSuite newSetupFixture, TestSuite containingSuit // Make the parent of the containing suite point to this // fixture instead // TODO: Get rid of this somehow? - TestSuite parent = (TestSuite)containingSuite.Parent; + TestSuite? parent = (TestSuite?)containingSuite.Parent; if (parent == null) { newSetupFixture.Name = RootSuite.Name; diff --git a/src/NUnitFramework/framework/Internal/Builders/PairwiseStrategy.cs b/src/NUnitFramework/framework/Internal/Builders/PairwiseStrategy.cs index 2797d8ac0d..7666ddec86 100644 --- a/src/NUnitFramework/framework/Internal/Builders/PairwiseStrategy.cs +++ b/src/NUnitFramework/framework/Internal/Builders/PairwiseStrategy.cs @@ -21,10 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Collections.Generic; -using System.Text; using NUnit.Framework.Interfaces; namespace NUnit.Framework.Internal.Builders @@ -301,11 +302,11 @@ public bool IsTupleCovered( FeatureTuple tuple ) /// internal class PairwiseTestCaseGenerator { - private FleaRand _prng; + private FleaRand? _prng; - private int[] _dimensions; + private int[]? _dimensions; - private List[][] _uncoveredTuples; + private List[][]? _uncoveredTuples; /// /// Creates a set of test cases for specified dimensions. @@ -328,7 +329,7 @@ public IEnumerable GetTestCases( int[] dimensions ) while ( true ) { - FeatureTuple tuple = GetNextTuple(); + FeatureTuple? tuple = GetNextTuple(); if ( tuple == null ) { @@ -351,12 +352,12 @@ public IEnumerable GetTestCases( int[] dimensions ) private int GetNextRandomNumber() { - return (int)( _prng.Next() >> 1 ); + return (int)( _prng!.Next() >> 1 ); } private void CreateAllTuples() { - _uncoveredTuples = new List[_dimensions.Length][]; + _uncoveredTuples = new List[_dimensions!.Length][]; for ( int d = 0; d < _dimensions.Length; d++ ) { @@ -375,7 +376,7 @@ private List CreateTuples( int dimension, int feature ) result.Add( new FeatureTuple( new FeatureInfo( dimension, feature ) ) ); - for ( int d = 0; d < _dimensions.Length; d++ ) + for ( int d = 0; d < _dimensions!.Length; d++ ) { if ( d != dimension ) { @@ -389,9 +390,9 @@ private List CreateTuples( int dimension, int feature ) return result; } - private FeatureTuple GetNextTuple() + private FeatureTuple? GetNextTuple() { - for ( int d = 0; d < _uncoveredTuples.Length; d++ ) + for ( int d = 0; d < _uncoveredTuples!.Length; d++ ) { for ( int f = 0; f < _uncoveredTuples[d].Length; f++ ) { @@ -411,7 +412,7 @@ private FeatureTuple GetNextTuple() private TestCaseInfo CreateTestCase( FeatureTuple tuple ) { - TestCaseInfo bestTestCase = null; + TestCaseInfo? bestTestCase = null; int bestCoverage = -1; for ( int i = 0; i < 7; i++ ) @@ -427,12 +428,12 @@ private TestCaseInfo CreateTestCase( FeatureTuple tuple ) } } - return bestTestCase; + return bestTestCase!; } private TestCaseInfo CreateRandomTestCase( FeatureTuple tuple ) { - TestCaseInfo result = new TestCaseInfo( _dimensions.Length ); + TestCaseInfo result = new TestCaseInfo( _dimensions!.Length ); for ( int d = 0; d < _dimensions.Length; d++ ) { @@ -486,7 +487,7 @@ private int[] GetMutableDimensions( FeatureTuple tuple ) { List result = new List(); - bool[] immutableDimensions = new bool[_dimensions.Length]; + bool[] immutableDimensions = new bool[_dimensions!.Length]; for ( int i = 0; i < tuple.Length; i++ ) { @@ -517,7 +518,7 @@ private void ScrambleDimensions( int[] dimensions ) private int MaximizeCoverageForDimension( TestCaseInfo testCase, int dimension, int bestCoverage ) { - List bestFeatures = new List( _dimensions[dimension] ); + List bestFeatures = new List( _dimensions![dimension] ); for ( int f = 0; f < _dimensions[dimension]; f++ ) { @@ -546,7 +547,7 @@ private int CountTuplesCoveredByTest( TestCaseInfo testCase, int dimension, int { int result = 0; - List tuples = _uncoveredTuples[dimension][feature]; + List tuples = _uncoveredTuples![dimension][feature]; for ( int i = 0; i < tuples.Count; i++ ) { @@ -561,7 +562,7 @@ private int CountTuplesCoveredByTest( TestCaseInfo testCase, int dimension, int private void RemoveTuplesCoveredByTest( TestCaseInfo testCase ) { - for ( int d = 0; d < _uncoveredTuples.Length; d++ ) + for ( int d = 0; d < _uncoveredTuples!.Length; d++ ) { for ( int f = 0; f < _uncoveredTuples[d].Length; f++ ) { @@ -581,7 +582,7 @@ private void RemoveTuplesCoveredByTest( TestCaseInfo testCase ) #if DEBUG private void SelfTest( List testCases ) { - for ( int d1 = 0; d1 < _dimensions.Length - 1; d1++ ) + for ( int d1 = 0; d1 < _dimensions!.Length - 1; d1++ ) { for ( int d2 = d1 + 1; d2 < _dimensions.Length; d2++ ) { @@ -625,14 +626,14 @@ private bool IsTupleCovered( List testCases, FeatureTuple tuple ) public IEnumerable GetTestCases(IEnumerable[] sources) { List testCases = new List(); - List[] valueSet = CreateValueSet(sources); + List[] valueSet = CreateValueSet(sources); int[] dimensions = CreateDimensions(valueSet); IEnumerable pairwiseTestCases = new PairwiseTestCaseGenerator().GetTestCases( dimensions ); foreach (TestCaseInfo pairwiseTestCase in pairwiseTestCases) { - object[] testData = new object[pairwiseTestCase.Features.Length]; + object?[] testData = new object?[pairwiseTestCase.Features.Length]; for (int i = 0; i < pairwiseTestCase.Features.Length; i++) { @@ -646,15 +647,15 @@ public IEnumerable GetTestCases(IEnumerable[] sources) return testCases; } - private List[] CreateValueSet(IEnumerable[] sources) + private List[] CreateValueSet(IEnumerable[] sources) { - var valueSet = new List[sources.Length]; + var valueSet = new List[sources.Length]; for (int i = 0; i < valueSet.Length; i++) { - var values = new List(); + var values = new List(); - foreach (object value in sources[i]) + foreach (object? value in sources[i]) { values.Add(value); } @@ -665,7 +666,7 @@ private List[] CreateValueSet(IEnumerable[] sources) return valueSet; } - private int[] CreateDimensions(List[] valueSet) + private int[] CreateDimensions(List[] valueSet) { int[] dimensions = new int[valueSet.Length]; diff --git a/src/NUnitFramework/framework/Internal/Builders/ParameterDataProvider.cs b/src/NUnitFramework/framework/Internal/Builders/ParameterDataProvider.cs index 6bc6338641..6193b89e38 100644 --- a/src/NUnitFramework/framework/Internal/Builders/ParameterDataProvider.cs +++ b/src/NUnitFramework/framework/Internal/Builders/ParameterDataProvider.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections; using System.Collections.Generic; using NUnit.Framework.Interfaces; @@ -63,7 +65,7 @@ public bool HasDataFor(IParameterInfo parameter) public IEnumerable GetDataFor(IParameterInfo parameter) { foreach (var provider in _providers) - foreach (object data in provider.GetDataFor(parameter)) + foreach (object? data in provider.GetDataFor(parameter)) yield return data; } } diff --git a/src/NUnitFramework/framework/Internal/Builders/ParameterDataSourceProvider.cs b/src/NUnitFramework/framework/Internal/Builders/ParameterDataSourceProvider.cs index 6387d54682..1b38eef7ab 100644 --- a/src/NUnitFramework/framework/Internal/Builders/ParameterDataSourceProvider.cs +++ b/src/NUnitFramework/framework/Internal/Builders/ParameterDataSourceProvider.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections; using System.Collections.Generic; using NUnit.Framework.Interfaces; @@ -50,11 +52,11 @@ public bool HasDataFor(IParameterInfo parameter) /// The parameter of a parameterized test public IEnumerable GetDataFor(IParameterInfo parameter) { - var data = new List(); + var data = new List(); foreach (IParameterDataSource source in parameter.GetCustomAttributes(false)) { - foreach (object item in source.GetData(parameter)) + foreach (object? item in source.GetData(parameter)) data.Add(item); } diff --git a/src/NUnitFramework/framework/Internal/Builders/ProviderCache.cs b/src/NUnitFramework/framework/Internal/Builders/ProviderCache.cs index d0e844589c..8c381bba4d 100644 --- a/src/NUnitFramework/framework/Internal/Builders/ProviderCache.cs +++ b/src/NUnitFramework/framework/Internal/Builders/ProviderCache.cs @@ -1,5 +1,5 @@ -// *********************************************************************** -// Copyright (c) 2008 Charlie Poole, Rob Prouse +// *********************************************************************** +// Copyright (c) 2008–2019 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,69 +21,28 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; -using System.Collections; using System.Collections.Generic; -using System.Text; namespace NUnit.Framework.Internal.Builders { - class ProviderCache + internal sealed class ProviderCache { - private static readonly Dictionary instances = new Dictionary(); + private readonly Dictionary _instances = new Dictionary(); - public static object GetInstanceOf(Type providerType) + public object GetInstanceOf(Type providerType) { return GetInstanceOf(providerType, null); } - public static object GetInstanceOf(Type providerType, object[] providerArgs) + public object GetInstanceOf(Type providerType, object[]? providerArgs) { - CacheEntry entry = new CacheEntry(providerType, providerArgs); - - object instance = instances.ContainsKey(entry) - ?instances[entry] - : null; - - if (instance == null) - instances[entry] = instance = Reflect.Construct(providerType, providerArgs); + if (!_instances.TryGetValue(providerType, out var instance)) + _instances.Add(providerType, instance = Reflect.Construct(providerType, providerArgs)); return instance; } - - public static void Clear() - { - foreach (CacheEntry key in instances.Keys) - { - IDisposable provider = instances[key] as IDisposable; - if (provider != null) - provider.Dispose(); - } - - instances.Clear(); - } - - class CacheEntry - { - private readonly Type providerType; - - public CacheEntry(Type providerType, object[] providerArgs) - { - this.providerType = providerType; - } - - public override bool Equals(object obj) - { - CacheEntry other = obj as CacheEntry; - if (other == null) return false; - - return this.providerType == other.providerType; - } - - public override int GetHashCode() - { - return providerType.GetHashCode(); - } - } } } diff --git a/src/NUnitFramework/framework/Internal/Builders/SequentialStrategy.cs b/src/NUnitFramework/framework/Internal/Builders/SequentialStrategy.cs index b8538472a0..ef80537a05 100644 --- a/src/NUnitFramework/framework/Internal/Builders/SequentialStrategy.cs +++ b/src/NUnitFramework/framework/Internal/Builders/SequentialStrategy.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2008 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; +#nullable enable + using System.Collections; using System.Collections.Generic; using NUnit.Framework.Interfaces; @@ -30,7 +31,7 @@ namespace NUnit.Framework.Internal.Builders { /// /// SequentialStrategy creates test cases by using all of the - /// parameter data sources in parallel, substituting null + /// parameter data sources in parallel, substituting /// when any of them run out of data. /// public class SequentialStrategy : ICombiningStrategy @@ -50,7 +51,7 @@ public IEnumerable GetTestCases(IEnumerable[] sources) for (; ; ) { bool gotData = false; - object[] testdata = new object[sources.Length]; + object?[] testdata = new object?[sources.Length]; for (int i = 0; i < sources.Length; i++) if (enumerators[i].MoveNext()) diff --git a/src/NUnitFramework/framework/Internal/CSharpPatternBasedAwaitAdapter.cs b/src/NUnitFramework/framework/Internal/CSharpPatternBasedAwaitAdapter.cs index 39c2683624..796b45c8ea 100644 --- a/src/NUnitFramework/framework/Internal/CSharpPatternBasedAwaitAdapter.cs +++ b/src/NUnitFramework/framework/Internal/CSharpPatternBasedAwaitAdapter.cs @@ -55,15 +55,14 @@ public static Type GetResultType(Type awaitableType) private static AwaitShapeInfo GetShapeInfo(Type type) { #if NET35 - AwaitShapeInfo info; - lock (ShapeInfoByType) { - if (!ShapeInfoByType.TryGetValue(type, out info)) + if (!ShapeInfoByType.TryGetValue(type, out var info)) ShapeInfoByType.Add(type, info = AwaitShapeInfo.TryCreate(type)); + + return info; } - return info; #else return ShapeInfoByType.GetOrAdd(type, AwaitShapeInfo.TryCreate); #endif diff --git a/src/NUnitFramework/framework/Internal/Commands/DisposeFixtureCommand.cs b/src/NUnitFramework/framework/Internal/Commands/DisposeFixtureCommand.cs index c6d603f2b2..d6cc6b4729 100644 --- a/src/NUnitFramework/framework/Internal/Commands/DisposeFixtureCommand.cs +++ b/src/NUnitFramework/framework/Internal/Commands/DisposeFixtureCommand.cs @@ -41,7 +41,9 @@ public class DisposeFixtureCommand : AfterTestCommand public DisposeFixtureCommand(TestCommand innerCommand) : base(innerCommand) { - Guard.OperationValid(Test is IDisposableFixture, "DisposeFixtureCommand does not apply to a " + Test.GetType().Name); + Guard.OperationValid( + Test is IDisposableFixture || Test?.Parent is IDisposableFixture, + $"DisposeFixtureCommand does not apply neither to {Test.GetType().Name}, nor to {Test.Parent?.GetType().Name ?? "it's parent (null)"}"); AfterTest = (context) => { diff --git a/src/NUnitFramework/framework/Internal/Commands/FixturePerTestCaseCommand.cs b/src/NUnitFramework/framework/Internal/Commands/FixturePerTestCaseCommand.cs new file mode 100644 index 0000000000..00d50ed531 --- /dev/null +++ b/src/NUnitFramework/framework/Internal/Commands/FixturePerTestCaseCommand.cs @@ -0,0 +1,69 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using NUnit.Framework.Interfaces; + +namespace NUnit.Framework.Internal.Commands +{ + /// + /// ConstructFixtureCommand constructs the user test object if necessary. + /// + public class FixturePerTestCaseCommand : BeforeAndAfterTestCommand + { + /// + /// Handles the construction and disposement of a fixture per test case + /// + /// The inner command to which the command applies + public FixturePerTestCaseCommand(TestCommand innerCommand) + : base(innerCommand) + { + TestSuite testSuite = null; + + ITest currentTest = Test; + while (currentTest != null && testSuite == null) + { + testSuite = testSuite ?? currentTest as TestSuite; + currentTest = currentTest.Parent; + } + + Guard.ArgumentValid(testSuite != null, "FixturePerTestCaseCommand must reference a TestSuite", nameof(innerCommand)); + + ITypeInfo typeInfo = testSuite.TypeInfo; + + BeforeTest = (context) => + { + if (typeInfo != null && !typeInfo.IsStaticClass) + { + context.TestObject = typeInfo.Construct(testSuite.Arguments); + Test.Fixture = context.TestObject; + } + }; + + AfterTest = (context) => + { + }; + } + } +} + diff --git a/src/NUnitFramework/framework/Internal/Commands/SetUpTearDownItem.cs b/src/NUnitFramework/framework/Internal/Commands/SetUpTearDownItem.cs index 35d9d15001..ff12d15f1a 100644 --- a/src/NUnitFramework/framework/Internal/Commands/SetUpTearDownItem.cs +++ b/src/NUnitFramework/framework/Internal/Commands/SetUpTearDownItem.cs @@ -24,6 +24,7 @@ using System; using System.Collections.Generic; using System.Reflection; +using NUnit.Framework.Internal.Execution; namespace NUnit.Framework.Internal.Commands { @@ -33,6 +34,7 @@ namespace NUnit.Framework.Internal.Commands /// public class SetUpTearDownItem { + private readonly IMethodValidator _methodValidator; private readonly IList _setUpMethods; private readonly IList _tearDownMethods; private bool _setUpWasRun; @@ -42,10 +44,15 @@ public class SetUpTearDownItem /// /// A list of setup methods for this level /// A list teardown methods for this level - public SetUpTearDownItem(IList setUpMethods, IList tearDownMethods) + /// A method validator to validate each method before calling. + public SetUpTearDownItem( + IList setUpMethods, + IList tearDownMethods, + IMethodValidator methodValidator = null) { _setUpMethods = setUpMethods; _tearDownMethods = tearDownMethods; + _methodValidator = methodValidator; } /// @@ -100,9 +107,10 @@ public void RunTearDown(TestExecutionContext context) } } - private static void RunSetUpOrTearDownMethod(TestExecutionContext context, MethodInfo method) + private void RunSetUpOrTearDownMethod(TestExecutionContext context, MethodInfo method) { Guard.ArgumentNotAsyncVoid(method, nameof(method)); + _methodValidator?.Validate(method); if (AsyncToSyncAdapter.IsAsyncOperation(method)) AsyncToSyncAdapter.Await(() => InvokeMethod(method, context)); diff --git a/src/NUnitFramework/framework/Internal/Commands/TimeoutCommand.cs b/src/NUnitFramework/framework/Internal/Commands/TimeoutCommand.cs index 568c68a460..4bfaa19026 100644 --- a/src/NUnitFramework/framework/Internal/Commands/TimeoutCommand.cs +++ b/src/NUnitFramework/framework/Internal/Commands/TimeoutCommand.cs @@ -27,6 +27,7 @@ using System.Threading.Tasks; #endif using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal.Abstractions; namespace NUnit.Framework.Internal.Commands { @@ -38,6 +39,7 @@ namespace NUnit.Framework.Internal.Commands public class TimeoutCommand : BeforeAndAfterTestCommand { private readonly int _timeout; + private readonly IDebugger _debugger; #if THREAD_ABORT Timer _commandTimer; private bool _commandTimedOut; @@ -48,11 +50,25 @@ public class TimeoutCommand : BeforeAndAfterTestCommand /// /// The inner command /// Timeout value - public TimeoutCommand(TestCommand innerCommand, int timeout) : base(innerCommand) + [Obsolete("This member will be removed in a future major release.")] + public TimeoutCommand(TestCommand innerCommand, int timeout) : this(innerCommand, timeout, new DebuggerProxy()) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The inner command + /// Timeout value + /// An instance + internal TimeoutCommand(TestCommand innerCommand, int timeout, IDebugger debugger) : base(innerCommand) { _timeout = timeout; + _debugger = debugger; + Guard.ArgumentValid(innerCommand.Test is TestMethod, "TimeoutCommand may only apply to a TestMethod", nameof(innerCommand)); Guard.ArgumentValid(timeout > 0, "Timeout value must be greater than zero", nameof(timeout)); + Guard.ArgumentNotNull(debugger, nameof(debugger)); #if THREAD_ABORT BeforeTest = (context) => @@ -64,6 +80,11 @@ public TimeoutCommand(TestCommand innerCommand, int timeout) : base(innerCommand _commandTimer = new Timer( (o) => { + if (_debugger.IsAttached) + { + return; + } + _commandTimedOut = true; ThreadUtility.Abort(testThread, nativeThreadId); // No join here, since the thread doesn't really terminate @@ -99,7 +120,13 @@ public override TestResult Execute(TestExecutionContext context) { try { - if (!Task.Run(() => context.CurrentResult = innerCommand.Execute(context)).Wait(_timeout)) + var testExecution = RunTestOnSeparateThread(context); + if (Task.WaitAny(new Task[] { testExecution }, _timeout) != -1 + || _debugger.IsAttached) + { + context.CurrentResult = testExecution.GetAwaiter().GetResult(); + } + else { context.CurrentResult.SetResult(new ResultState( TestStatus.Failed, @@ -114,6 +141,11 @@ public override TestResult Execute(TestExecutionContext context) return context.CurrentResult; } + + private Task RunTestOnSeparateThread(TestExecutionContext context) + { + return Task.Run(() => innerCommand.Execute(context)); + } #endif } } diff --git a/src/NUnitFramework/framework/Internal/ConstraintUtils.cs b/src/NUnitFramework/framework/Internal/ConstraintUtils.cs index a90ddde2d4..65313b83ce 100644 --- a/src/NUnitFramework/framework/Internal/ConstraintUtils.cs +++ b/src/NUnitFramework/framework/Internal/ConstraintUtils.cs @@ -41,8 +41,7 @@ internal static class ConstraintUtils /// The type to require. public static T RequireActual(object actual, string paramName, bool allowNull = false) { - T result; - if (TypeHelper.TryCast(actual, out result) && (allowNull || result != null)) + if (TypeHelper.TryCast(actual, out T result) && (allowNull || result != null)) { return result; } diff --git a/src/NUnitFramework/framework/Internal/ContextUtils.cs b/src/NUnitFramework/framework/Internal/ContextUtils.cs new file mode 100644 index 0000000000..074ae0cf96 --- /dev/null +++ b/src/NUnitFramework/framework/Internal/ContextUtils.cs @@ -0,0 +1,64 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Security; +using System.Threading; + +namespace NUnit.Framework.Internal +{ + internal static class ContextUtils + { + public static void DoIsolated(Action action) + { + DoIsolated(state => ((Action)state).Invoke(), state: action); + } + + public static T DoIsolated(Func func) + { + var returnValue = default(T); + DoIsolated(_ => returnValue = func.Invoke(), state: null); + return returnValue; + } + + [SecuritySafeCritical] + public static void DoIsolated(ContextCallback callback, object state) + { + var previousState = SandboxedThreadState.Capture(); + try + { + var executionContext = ExecutionContext.Capture() + ?? throw new InvalidOperationException("Execution context flow must not be suppressed."); + + using ((object)executionContext as IDisposable) + { + ExecutionContext.Run(executionContext, callback, state); + } + } + finally + { + previousState.Restore(); + } + } + } +} diff --git a/src/NUnitFramework/framework/Internal/DecimalParts.cs b/src/NUnitFramework/framework/Internal/DecimalParts.cs new file mode 100644 index 0000000000..d7e5d0420e --- /dev/null +++ b/src/NUnitFramework/framework/Internal/DecimalParts.cs @@ -0,0 +1,60 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +namespace NUnit.Framework.Internal +{ + internal struct DecimalParts + { + public uint Low { get; } + public uint Mid { get; } + public uint High { get; } + public bool IsNegative { get; } + public byte Scale { get; } + + public static DecimalParts FromValue(decimal value) + { + var parts = decimal.GetBits(value); + + unchecked + { + var flags = (uint)parts[3]; + + return new DecimalParts( + low: (uint)parts[0], + mid: (uint)parts[1], + high: (uint)parts[2], + isNegative: (flags & 0x80000000) != 0, + scale: (byte)(flags >> 16)); + } + } + + private DecimalParts(uint low, uint mid, uint high, bool isNegative, byte scale) + { + Low = low; + Mid = mid; + High = high; + IsNegative = isNegative; + Scale = scale; + } + } +} diff --git a/src/NUnitFramework/framework/Internal/DefaultBlockingAwaitAdapter.cs b/src/NUnitFramework/framework/Internal/DefaultBlockingAwaitAdapter.cs index f95522221a..bd270e88ab 100644 --- a/src/NUnitFramework/framework/Internal/DefaultBlockingAwaitAdapter.cs +++ b/src/NUnitFramework/framework/Internal/DefaultBlockingAwaitAdapter.cs @@ -26,7 +26,7 @@ namespace NUnit.Framework.Internal { /// - /// Useful when wrapping awaitables whose GetResult method does not block until complete. + /// Useful when wrapping awaiters whose GetResult method does not block until complete. /// Contains a default mechanism to implement /// via and . /// diff --git a/src/NUnitFramework/framework/Internal/ExceptionHelper.cs b/src/NUnitFramework/framework/Internal/ExceptionHelper.cs index a88c95e02b..59fd2202ef 100644 --- a/src/NUnitFramework/framework/Internal/ExceptionHelper.cs +++ b/src/NUnitFramework/framework/Internal/ExceptionHelper.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Globalization; @@ -73,7 +75,7 @@ public static void Rethrow(Exception exception) /// /// Builds up a message, using the Message field of the specified exception - /// as well as any InnerExceptions. Optionally excludes exception names, + /// as well as any InnerExceptions. Optionally excludes exception names, /// creating a more readable message. /// /// The exception. @@ -110,7 +112,7 @@ public static string BuildMessage(Exception exception, bool excludeExceptionName /// A combined stack trace. public static string BuildStackTrace(Exception exception) { - StringBuilder sb = new StringBuilder(GetSafeStackTrace(exception)); + StringBuilder sb = new StringBuilder(exception.GetStackTraceWithoutThrowing()); foreach (Exception inner in FlattenExceptionHierarchy(exception)) { @@ -118,33 +120,17 @@ public static string BuildStackTrace(Exception exception) sb.Append("--"); sb.Append(inner.GetType().Name); sb.Append(Environment.NewLine); - sb.Append(GetSafeStackTrace(inner)); + sb.Append(inner.GetStackTraceWithoutThrowing()); } return sb.ToString(); } - /// - /// Gets the stack trace of the exception. If no stack trace - /// is provided, returns "No stack trace available". - /// - /// The exception. - /// A string representation of the stack trace. - private static string GetSafeStackTrace(Exception exception) - { - try - { - return exception.StackTrace; - } - catch (Exception) - { - return "No stack trace available"; - } - } - private static string GetExceptionMessage(Exception ex) { - if (string.IsNullOrEmpty(ex.Message)) + var message = ex.GetMessageWithoutThrowing(); + + if (string.IsNullOrEmpty(message)) { // Special handling for Mono 5.0, which returns an empty message var fnfEx = ex as System.IO.FileNotFoundException; @@ -153,22 +139,27 @@ private static string GetExceptionMessage(Exception ex) : "No message provided"; } - return ex.Message; + return message; } private static void AppendExceptionDataContents(Exception ex, StringBuilder sb) { - if (ex.Data.Count == 0) + var data = ex.GetDataWithoutThrowing(); + + if (data.IsError(out var message)) { - return; + sb.AppendLine(); + sb.Append(message); } - - sb.AppendLine(); - sb.AppendLine("Data:"); - foreach (DictionaryEntry kvp in ex.Data) + else if (data.Value.Count != 0) { - sb.AppendFormat(" {0}: {1}", kvp.Key, kvp.Value?.ToString() ?? ""); sb.AppendLine(); + sb.Append("Data:"); + foreach (DictionaryEntry kvp in data.Value) + { + sb.AppendLine(); + sb.AppendFormat(" {0}: {1}", kvp.Key, kvp.Value?.ToString() ?? ""); + } } } @@ -176,18 +167,16 @@ private static List FlattenExceptionHierarchy(Exception exception) { var result = new List(); - if (exception is ReflectionTypeLoadException) + if (exception is ReflectionTypeLoadException reflectionException) { - var reflectionException = exception as ReflectionTypeLoadException; result.AddRange(reflectionException.LoaderExceptions); foreach (var innerException in reflectionException.LoaderExceptions) result.AddRange(FlattenExceptionHierarchy(innerException)); } #if TASK_PARALLEL_LIBRARY_API - if (exception is AggregateException) + if (exception is AggregateException aggregateException) { - var aggregateException = (exception as AggregateException); result.AddRange(aggregateException.InnerExceptions); foreach (var innerException in aggregateException.InnerExceptions) @@ -207,14 +196,14 @@ private static List FlattenExceptionHierarchy(Exception exception) /// /// Executes a parameterless synchronous or async delegate and returns the exception it throws, if any. /// - internal static Exception RecordException(Delegate parameterlessDelegate, string parameterName) + internal static Exception? RecordException(Delegate parameterlessDelegate, string parameterName) { Guard.ArgumentNotNull(parameterlessDelegate, parameterName); Guard.ArgumentValid( - parameterlessDelegate.GetMethodInfo().GetParameters().Length == 0, + parameterlessDelegate.GetType().GetMethod("Invoke").GetParameters().Length == 0, $"The actual value must be a parameterless delegate but was {parameterlessDelegate.GetType().Name}.", - nameof(parameterName)); + parameterName); Guard.ArgumentNotAsyncVoid(parameterlessDelegate, parameterName); diff --git a/src/NUnitFramework/framework/Internal/Execution/CompositeWorkItem.cs b/src/NUnitFramework/framework/Internal/Execution/CompositeWorkItem.cs index 067b06af32..72a718158c 100644 --- a/src/NUnitFramework/framework/Internal/Execution/CompositeWorkItem.cs +++ b/src/NUnitFramework/framework/Internal/Execution/CompositeWorkItem.cs @@ -28,6 +28,7 @@ using NUnit.Compatibility; using NUnit.Framework.Internal.Commands; using NUnit.Framework.Interfaces; +using System.Diagnostics; namespace NUnit.Framework.Internal.Execution { @@ -50,7 +51,6 @@ public class CompositeWorkItem : WorkItem /// public List Children { get; } = new List(); -#if PARALLEL /// /// Indicates whether this work item should use a separate dispatcher. /// @@ -58,7 +58,6 @@ public override bool IsolateChildTests { get { return ExecutionStrategy == ParallelExecutionStrategy.NonParallel && Context.Dispatcher.LevelOfParallelism > 0; } } -#endif private CountdownEvent _childTestCountdown; @@ -163,8 +162,13 @@ private bool CheckForCancellation() private void InitializeSetUpAndTearDownCommands() { + var methodValidator = CheckInstancePerTestCase() + ? new StaticMethodValidator( + $"Only static OneTimeSetUp and OneTimeTearDown are allowed for {nameof(LifeCycle.InstancePerTestCase)} mode.") + : null; + List setUpTearDownItems = - BuildSetUpTearDownList(_suite.OneTimeSetUpMethods, _suite.OneTimeTearDownMethods); + BuildSetUpTearDownList(_suite.OneTimeSetUpMethods, _suite.OneTimeTearDownMethods, methodValidator); var actionItems = new List(); foreach (ITestAction action in Test.Actions) @@ -199,6 +203,19 @@ private void InitializeSetUpAndTearDownCommands() _teardownCommand = MakeOneTimeTearDownCommand(setUpTearDownItems, actionItems); } + private bool CheckInstancePerTestCase() + { + ITest test = Test; + while (test != null) + { + if (test is TestFixture fixture && fixture.LifeCycle == LifeCycle.InstancePerTestCase) + return true; + + test = test.Parent; + } + return false; + } + private TestCommand MakeOneTimeSetUpCommand(List setUpTearDown, List actions) { TestCommand command = new EmptyTestCommand(Test); @@ -244,7 +261,7 @@ private TestCommand MakeOneTimeTearDownCommand(List setUpTear command = new OneTimeTearDownCommand(command, item); // Dispose of fixture if necessary - if (Test is IDisposableFixture && typeof(IDisposable).IsAssignableFrom(Test.TypeInfo.Type)) + if (Test is IDisposableFixture && typeof(IDisposable).IsAssignableFrom(Test.TypeInfo.Type) && !CheckInstancePerTestCase()) command = new DisposeFixtureCommand(command); return command; @@ -287,10 +304,8 @@ private void RunChildren() child.Completed += new EventHandler(OnChildItemCompleted); child.InitializeContext(new TestExecutionContext(Context)); -#if PARALLEL // In case we run directly, on same thread child.TestWorker = TestWorker; -#endif Context.Dispatcher.Dispatch(child); childCount--; @@ -382,8 +397,10 @@ private void OnChildItemCompleted(object sender, EventArgs e) /// private void OnAllChildItemsCompleted() { - var teardown = new OneTimeTearDownWorkItem(this); - Context.Dispatcher.Dispatch(teardown); + if (Context.ExecutionStatus == TestExecutionStatus.AbortRequested) + WorkItemComplete(); + else + Context.Dispatcher.Dispatch(new OneTimeTearDownWorkItem(this)); } private readonly object cancelLock = new object(); @@ -440,7 +457,6 @@ public override string Name get { return string.Format("{0} OneTimeTearDown", base.Name); } } -#if PARALLEL /// /// The ExecutionStrategy for use in running this work item /// @@ -448,7 +464,6 @@ public override ParallelExecutionStrategy ExecutionStrategy { get { return _originalWorkItem.ExecutionStrategy; } } -#endif /// /// @@ -457,9 +472,6 @@ public override void Execute() { lock (_teardownLock) { - //if (Test.Parent != null && Test.Parent.Name.EndsWith("nunit.framework.tests.dll")) - // System.Diagnostics.Debugger.Launch(); - if (Test.TestType == "Theory" && Result.ResultState == ResultState.Success && Result.PassCount == 0) Result.SetResult(ResultState.Failure, "No test cases were provided"); @@ -481,6 +493,18 @@ public override void Execute() /// PerformWork is not used in CompositeWorkItem /// protected override void PerformWork() { } + + /// + /// WorkItemCancelled is called directly by the parallel dispatcher + /// when a test suite is left hanging after a forced StopRun. We + /// simulate WorkItemComplete() but without the ripple effect to + /// higher level suites, since we are controlling it all directly. + /// + internal void WorkItemCancelled() + { + Result.SetResult(ResultState.Cancelled, TestResult.USER_CANCELLED_MESSAGE); + _originalWorkItem.WorkItemComplete(); + } } #endregion diff --git a/src/NUnitFramework/framework/Internal/Execution/EventListenerTextWriter.cs b/src/NUnitFramework/framework/Internal/Execution/EventListenerTextWriter.cs index ec4e4afb3f..e0e8e055eb 100644 --- a/src/NUnitFramework/framework/Internal/Execution/EventListenerTextWriter.cs +++ b/src/NUnitFramework/framework/Internal/Execution/EventListenerTextWriter.cs @@ -34,34 +34,42 @@ namespace NUnit.Framework.Internal.Execution /// listener is active in the context, or if there is no context, /// the output is forwarded to the supplied default writer. /// - public class EventListenerTextWriter : TextWriter - { + public class EventListenerTextWriter : TextWriter + { private readonly TextWriter _defaultWriter; - private readonly string _streamName; + private readonly string _streamName; /// /// Construct an EventListenerTextWriter /// /// The name of the stream to use for events /// The default writer to use if no listener is available - public EventListenerTextWriter( string streamName, TextWriter defaultWriter ) - { - _streamName = streamName; + public EventListenerTextWriter( string streamName, TextWriter defaultWriter ) + { + _streamName = streamName; _defaultWriter = defaultWriter; - } + } /// /// Get the Encoding for this TextWriter /// public override Encoding Encoding { get; } = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false); + private string FormatForListener(object value) + { + return + value is null ? string.Empty : + value is IFormattable formattable ? formattable.ToString(null, FormatProvider) : + value.ToString(); + } + private bool TrySendToListener(string text) { var context = TestExecutionContext.CurrentContext; if (context == null || context.Listener == null) return false; - context.Listener.TestOutput(new TestOutput(text, _streamName, + context.Listener.TestOutput(new TestOutput(text, _streamName, context.CurrentTest?.Id, context.CurrentTest?.FullName)); return true; @@ -111,24 +119,8 @@ public override void Write(string format, object arg0) /// public override void Write(object value) { - if (value != null) - { - IFormattable f = value as IFormattable; - if (f != null) - { - if (!TrySendToListener(f.ToString(null, FormatProvider))) - _defaultWriter.Write(value); - } - else - { - if (!TrySendToListener(value.ToString())) - _defaultWriter.Write(value); - } - } - else - { + if (value == null || !TrySendToListener(FormatForListener(value))) _defaultWriter.Write(value); - } } /// @@ -264,25 +256,8 @@ public override void WriteLine(string value) /// public override void WriteLine(object value) { - if (value == null) - { - if (!TrySendLineToListener(string.Empty)) - _defaultWriter.WriteLine(value); - } - else - { - IFormattable f = value as IFormattable; - if (f != null) - { - if (!TrySendLineToListener(f.ToString(null, FormatProvider))) - _defaultWriter.WriteLine(value); - } - else - { - if (!TrySendLineToListener(value.ToString())) - _defaultWriter.WriteLine(value); - } - } + if (!TrySendLineToListener(FormatForListener(value))) + _defaultWriter.WriteLine(value); } /// diff --git a/src/NUnitFramework/framework/Internal/Execution/EventPump.cs b/src/NUnitFramework/framework/Internal/Execution/EventPump.cs index c9bc3a13cf..a2411bfd91 100644 --- a/src/NUnitFramework/framework/Internal/Execution/EventPump.cs +++ b/src/NUnitFramework/framework/Internal/Execution/EventPump.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System; using System.Threading; using NUnit.Framework.Interfaces; @@ -170,6 +169,8 @@ public void Stop() /// private void PumpThreadProc() { + log.Debug("Starting EventPump"); + //ITestListener hostListeners = CoreExtensions.Host.Listeners; try { @@ -178,7 +179,7 @@ private void PumpThreadProc() Event e = _events.Dequeue( PumpState == EventPumpState.Pumping ); if ( e == null ) break; - try + try { e.Send(_eventListener); //e.Send(hostListeners); @@ -188,6 +189,8 @@ private void PumpThreadProc() log.Error("Exception in event handler {0}", ExceptionHelper.BuildStackTrace(ex)); } } + + log.Debug("EventPump Terminating"); } catch (Exception ex) { @@ -204,4 +207,3 @@ private void PumpThreadProc() #endregion } } -#endif diff --git a/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs b/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs index f508b1558c..a473b0bd5a 100644 --- a/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs +++ b/src/NUnitFramework/framework/Internal/Execution/EventQueue.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System.Collections.Concurrent; using System.Threading; using NUnit.Framework.Interfaces; @@ -220,15 +219,15 @@ public void Enqueue(Event e) } while (true); // Setting this to anything other than 0 causes NUnit to be sensitive - // to the windows timer resolution - see issue #2217 + // to the windows timer resolution - see issue #2217 Thread.Sleep(0); // give EventPump thread a chance to process the event } /// - /// Removes the first element from the queue and returns it (or null). + /// Removes the first element from the queue and returns it (or ). /// /// - /// If true and the queue is empty, the calling thread is blocked until + /// If and the queue is empty, the calling thread is blocked until /// either an element is enqueued, or is called. /// /// @@ -238,9 +237,9 @@ public void Enqueue(Event e) /// the first element. /// /// - /// otherwise, if ==false + /// otherwise, if == /// or has been called - /// null. + /// . /// /// /// @@ -311,5 +310,3 @@ public void Stop() } } } - -#endif diff --git a/src/NUnitFramework/framework/Internal/Execution/IMethodValidator.cs b/src/NUnitFramework/framework/Internal/Execution/IMethodValidator.cs new file mode 100644 index 0000000000..c88da00466 --- /dev/null +++ b/src/NUnitFramework/framework/Internal/Execution/IMethodValidator.cs @@ -0,0 +1,39 @@ +// *********************************************************************** +// Copyright (c) 2012-2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System.Reflection; + +namespace NUnit.Framework.Internal.Execution +{ + /// + /// Validates method to execute. + /// + public interface IMethodValidator + { + /// + /// Determines whether a method is allowed to execute and throws an exception otherwise. + /// + /// The method to validate. + void Validate(MethodInfo method); + } +} diff --git a/src/NUnitFramework/framework/Internal/Execution/MainThreadWorkItemDispatcher.cs b/src/NUnitFramework/framework/Internal/Execution/MainThreadWorkItemDispatcher.cs index 76a4a219b5..62fc57fe41 100644 --- a/src/NUnitFramework/framework/Internal/Execution/MainThreadWorkItemDispatcher.cs +++ b/src/NUnitFramework/framework/Internal/Execution/MainThreadWorkItemDispatcher.cs @@ -53,8 +53,8 @@ public void Start(WorkItem topLevelWorkItem) /// /// Dispatch a single work item for execution by /// executing it directly. - /// The item to dispatch /// + /// The item to dispatch public void Dispatch(WorkItem work) { if (work != null) diff --git a/src/NUnitFramework/framework/Internal/Execution/ParallelExecutionStrategy.cs b/src/NUnitFramework/framework/Internal/Execution/ParallelExecutionStrategy.cs index d310ae22bb..520c68a681 100644 --- a/src/NUnitFramework/framework/Internal/Execution/ParallelExecutionStrategy.cs +++ b/src/NUnitFramework/framework/Internal/Execution/ParallelExecutionStrategy.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2017 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL namespace NUnit.Framework.Internal.Execution { /// @@ -46,4 +45,3 @@ public enum ParallelExecutionStrategy NonParallel, } } -#endif diff --git a/src/NUnitFramework/framework/Internal/Execution/ParallelWorkItemDispatcher.cs b/src/NUnitFramework/framework/Internal/Execution/ParallelWorkItemDispatcher.cs index 8cd2ea982d..d7018e616d 100644 --- a/src/NUnitFramework/framework/Internal/Execution/ParallelWorkItemDispatcher.cs +++ b/src/NUnitFramework/framework/Internal/Execution/ParallelWorkItemDispatcher.cs @@ -21,12 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL - using System; using System.Collections.Generic; using System.Linq; using System.Threading; +using NUnit.Framework.Interfaces; namespace NUnit.Framework.Internal.Execution { @@ -38,9 +37,13 @@ public class ParallelWorkItemDispatcher : IWorkItemDispatcher { private static readonly Logger log = InternalTrace.GetLogger("Dispatcher"); + private const int WAIT_FOR_FORCED_TERMINATION = 5000; + private WorkItem _topLevelWorkItem; private readonly Stack _savedWorkItems = new Stack(); + private readonly List _activeWorkItems = new List(); + #region Events /// @@ -77,13 +80,9 @@ private void InitializeShifts() // Assign queues to shifts ParallelShift.AddQueue(ParallelQueue); -#if APARTMENT_STATE ParallelShift.AddQueue(ParallelSTAQueue); -#endif NonParallelShift.AddQueue(NonParallelQueue); -#if APARTMENT_STATE NonParallelSTAShift.AddQueue(NonParallelSTAQueue); -#endif // Create workers and assign to shifts and queues // TODO: Avoid creating all the workers till needed @@ -93,19 +92,15 @@ private void InitializeShifts() ParallelShift.Assign(new TestWorker(ParallelQueue, name)); } -#if APARTMENT_STATE ParallelShift.Assign(new TestWorker(ParallelSTAQueue, "ParallelSTAWorker")); -#endif var worker = new TestWorker(NonParallelQueue, "NonParallelWorker"); worker.Busy += OnStartNonParallelWorkItem; NonParallelShift.Assign(worker); -#if APARTMENT_STATE worker = new TestWorker(NonParallelSTAQueue, "NonParallelSTAWorker"); worker.Busy += OnStartNonParallelWorkItem; NonParallelSTAShift.Assign(worker); -#endif } private void OnStartNonParallelWorkItem(TestWorker worker, WorkItem work) @@ -134,9 +129,7 @@ public IEnumerable Shifts { yield return ParallelShift; yield return NonParallelShift; -#if APARTMENT_STATE yield return NonParallelSTAShift; -#endif } } @@ -148,13 +141,9 @@ public IEnumerable Queues get { yield return ParallelQueue; -#if APARTMENT_STATE yield return ParallelSTAQueue; -#endif yield return NonParallelQueue; -#if APARTMENT_STATE yield return NonParallelSTAQueue; -#endif } } @@ -162,7 +151,6 @@ public IEnumerable Queues // See comment in Workshift.cs for a more detailed explanation. private WorkShift ParallelShift { get; } = new WorkShift("Parallel"); private WorkShift NonParallelShift { get; } = new WorkShift("NonParallel"); -#if APARTMENT_STATE private WorkShift NonParallelSTAShift { get; } = new WorkShift("NonParallelSTA"); // WorkItemQueues @@ -170,11 +158,7 @@ public IEnumerable Queues private WorkItemQueue ParallelSTAQueue { get; } = new WorkItemQueue("ParallelSTAQueue", true, ApartmentState.STA); private WorkItemQueue NonParallelQueue { get; } = new WorkItemQueue("NonParallelQueue", false, ApartmentState.MTA); private WorkItemQueue NonParallelSTAQueue { get; } = new WorkItemQueue("NonParallelSTAQueue", false, ApartmentState.STA); -#else - // WorkItemQueues - private WorkItemQueue ParallelQueue { get; } = new WorkItemQueue("ParallelQueue", true); - private WorkItemQueue NonParallelQueue { get; } = new WorkItemQueue("NonParallelQueue", false); -#endif + #endregion #region IWorkItemDispatcher Members @@ -221,6 +205,15 @@ private void Dispatch(WorkItem work, ParallelExecutionStrategy strategy) { log.Debug("Using {0} strategy for {1}", strategy, work.Name); + // Currently, we only track CompositeWorkItems - this could be expanded + var composite = work as CompositeWorkItem; + if (composite != null) + lock (_activeWorkItems) + { + _activeWorkItems.Add(composite); + composite.Completed += OnWorkItemCompletion; + } + switch (strategy) { default: @@ -228,19 +221,15 @@ private void Dispatch(WorkItem work, ParallelExecutionStrategy strategy) work.Execute(); break; case ParallelExecutionStrategy.Parallel: -#if APARTMENT_STATE if (work.TargetApartment == ApartmentState.STA) ParallelSTAQueue.Enqueue(work); else -#endif ParallelQueue.Enqueue(work); break; case ParallelExecutionStrategy.NonParallel: -#if APARTMENT_STATE if (work.TargetApartment == ApartmentState.STA) NonParallelSTAQueue.Enqueue(work); else -#endif NonParallelQueue.Enqueue(work); break; } @@ -254,6 +243,25 @@ public void CancelRun(bool force) { foreach (var shift in Shifts) shift.Cancel(force); + + if (force) + { + SpinWait.SpinUntil(() => _topLevelWorkItem.State == WorkItemState.Complete, WAIT_FOR_FORCED_TERMINATION); + + // Notify termination of any remaining in-process suites + lock (_activeWorkItems) + { + int index = _activeWorkItems.Count; + + while (index > 0) + { + var work = _activeWorkItems[--index]; + + if (work.State == WorkItemState.Running) + new CompositeWorkItem.OneTimeTearDownWorkItem(work).WorkItemCancelled(); + } + } + } } private readonly object _queueLock = new object(); @@ -301,9 +309,20 @@ private void TryRestoreQueues() } } -#endregion + #endregion -#region Helper Methods + #region Helper Methods + + private void OnWorkItemCompletion(object sender, EventArgs args) + { + var work = (CompositeWorkItem)sender; + + lock (_activeWorkItems) + { + _activeWorkItems.Remove(work); + work.Completed -= OnWorkItemCompletion; + } + } private void OnEndOfShift(WorkShift endingShift) { @@ -365,4 +384,3 @@ public static bool HasFlag(this ParallelScope scope, ParallelScope value) #endregion } -#endif diff --git a/src/NUnitFramework/framework/Internal/Execution/QueuingEventListener.cs b/src/NUnitFramework/framework/Internal/Execution/QueuingEventListener.cs index 67c6f5331b..2b30e63c6d 100644 --- a/src/NUnitFramework/framework/Internal/Execution/QueuingEventListener.cs +++ b/src/NUnitFramework/framework/Internal/Execution/QueuingEventListener.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL +#nullable enable + using System; using NUnit.Framework.Interfaces; @@ -86,4 +87,3 @@ public void SendMessage(TestMessage message) #endregion } } -#endif diff --git a/src/NUnitFramework/framework/Internal/Execution/SimpleWorkItem.cs b/src/NUnitFramework/framework/Internal/Execution/SimpleWorkItem.cs index 53d0f7161c..8fa4bfb915 100644 --- a/src/NUnitFramework/framework/Internal/Execution/SimpleWorkItem.cs +++ b/src/NUnitFramework/framework/Internal/Execution/SimpleWorkItem.cs @@ -24,6 +24,7 @@ using System; using System.Threading; using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal.Abstractions; using NUnit.Framework.Internal.Commands; namespace NUnit.Framework.Internal.Execution @@ -35,6 +36,8 @@ namespace NUnit.Framework.Internal.Execution /// public class SimpleWorkItem : WorkItem { + private readonly IDebugger _debugger; + readonly TestMethod _testMethod; /// @@ -42,9 +45,21 @@ public class SimpleWorkItem : WorkItem /// /// The test to be executed /// The filter used to select this test - public SimpleWorkItem(TestMethod test, ITestFilter filter) + [Obsolete("This member will be removed in a future major release.")] + public SimpleWorkItem(TestMethod test, ITestFilter filter) : this(test, filter, new DebuggerProxy()) + { + } + + /// + /// Construct a simple work item for a test. + /// + /// The test to be executed + /// The filter used to select this test + /// An instance + internal SimpleWorkItem(TestMethod test, ITestFilter filter, IDebugger debugger) : base(test, filter) { + _debugger = debugger; _testMethod = test; } @@ -55,7 +70,11 @@ protected override void PerformWork() { try { - Result = MakeTestCommand().Execute(Context); + var testCommand = MakeTestCommand(); + + // Isolate the Execute call because the WorkItemComplete below will run one-time teardowns. Execution + // context values should not flow from a particular test case into the shared one-time teardown. + Result = ContextUtils.DoIsolated(() => testCommand.Execute(Context)); } catch (Exception ex) { @@ -118,6 +137,11 @@ private TestCommand MakeTestCommand() foreach (var item in setUpTearDownList) command = new SetUpTearDownCommand(command, item); + // Dispose of fixture if necessary + var isInstancePerTestCase = parentFixture?.LifeCycle == LifeCycle.InstancePerTestCase; + if (isInstancePerTestCase && parentFixture is IDisposableFixture && typeof(IDisposable).IsAssignableFrom(parentFixture.TypeInfo.Type)) + command = new DisposeFixtureCommand(command); + // In the current implementation, upstream actions only apply to tests. If that should change in the future, // then actions would have to be tested for here. For now we simply assert it in Debug. We allow // ActionTargets.Default, because it is passed down by ParameterizedMethodSuite. @@ -140,6 +164,11 @@ private TestCommand MakeTestCommand() foreach (var attr in method.GetCustomAttributes(true)) command = new ApplyChangesToContextCommand(command, attr); + // Add a construct command and optionally a dispose command in case of instance per test case. + if (isInstancePerTestCase) + { + command = new FixturePerTestCaseCommand(command); + } // If a timeout is specified, create a TimeoutCommand // Timeout set at a higher level int timeout = Context.TestCaseTimeout; @@ -149,10 +178,10 @@ private TestCommand MakeTestCommand() timeout = (int)Test.Properties.Get(PropertyNames.Timeout); if (timeout > 0) - command = new TimeoutCommand(command, timeout); + command = new TimeoutCommand(command, timeout, _debugger); // Add wrappers for repeatable tests after timeout so the timeout is reset on each repeat - foreach (var repeatableAttribute in method.MethodInfo.GetAttributes(true)) + foreach (var repeatableAttribute in method.GetCustomAttributes(true)) command = repeatableAttribute.Wrap(command); return command; diff --git a/src/NUnitFramework/framework/Internal/Execution/SimpleWorkItemDispatcher.cs b/src/NUnitFramework/framework/Internal/Execution/SimpleWorkItemDispatcher.cs index 4347da01ad..88e540e29d 100644 --- a/src/NUnitFramework/framework/Internal/Execution/SimpleWorkItemDispatcher.cs +++ b/src/NUnitFramework/framework/Internal/Execution/SimpleWorkItemDispatcher.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System; using System.Collections.Generic; using System.Diagnostics; @@ -59,7 +58,6 @@ public void Start(WorkItem topLevelWorkItem) _topLevelWorkItem = topLevelWorkItem; _runnerThread = new Thread(RunnerThreadProc); -#if APARTMENT_STATE if (topLevelWorkItem.TargetApartment != ApartmentState.Unknown) { try @@ -71,7 +69,6 @@ public void Start(WorkItem topLevelWorkItem) topLevelWorkItem.MarkNotRunnable("Apartment state cannot be set on this platform."); } } -#endif _runnerThread.Start(); } @@ -79,8 +76,8 @@ public void Start(WorkItem topLevelWorkItem) /// /// Dispatch a single work item for execution by /// executing it directly. - /// The item to dispatch /// + /// The item to dispatch public void Dispatch(WorkItem work) { if (work != null) @@ -118,4 +115,3 @@ public void CancelRun(bool force) #endregion } } -#endif diff --git a/src/NUnitFramework/framework/Internal/Execution/StaticMethodValidator.cs b/src/NUnitFramework/framework/Internal/Execution/StaticMethodValidator.cs new file mode 100644 index 0000000000..9859b3ed77 --- /dev/null +++ b/src/NUnitFramework/framework/Internal/Execution/StaticMethodValidator.cs @@ -0,0 +1,57 @@ +// *********************************************************************** +// Copyright (c) 2012-2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Reflection; + +namespace NUnit.Framework.Internal.Execution +{ + /// + /// Checks whether the method to execute is static. + /// + public class StaticMethodValidator : IMethodValidator + { + private readonly string _failMessage; + + /// + /// Construct a StaticMethodValidator. + /// + /// The error message to output in case the validation fails. + public StaticMethodValidator(string failMessage) + { + Guard.ArgumentNotNullOrEmpty(failMessage, nameof(failMessage)); + + _failMessage = failMessage; + } + + /// + /// Determines whether a method to execute is static and throws an InvalidOperationException otherwise. + /// + /// The method to validate. + public void Validate(MethodInfo method) + { + if (!method.IsStatic) + throw new InvalidOperationException(_failMessage); + } + } +} diff --git a/src/NUnitFramework/framework/Internal/Execution/TestWorker.cs b/src/NUnitFramework/framework/Internal/Execution/TestWorker.cs index a7c23b8fd6..21091209b9 100644 --- a/src/NUnitFramework/framework/Internal/Execution/TestWorker.cs +++ b/src/NUnitFramework/framework/Internal/Execution/TestWorker.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System; using System.Threading; using NUnit.Framework.Interfaces; @@ -157,7 +156,7 @@ public void Start() { _workerThread = new Thread(new ThreadStart(TestWorkerThreadProc)); _workerThread.Name = Name; -#if APARTMENT_STATE + try { _workerThread.SetApartmentState(WorkQueue.TargetApartment); @@ -165,7 +164,7 @@ public void Start() catch (PlatformNotSupportedException) { } -#endif + log.Info("{0} starting on thread [{1}]", Name, _workerThread.ManagedThreadId); _workerThread.Start(); } @@ -191,5 +190,3 @@ public void Cancel(bool force) } } } - -#endif diff --git a/src/NUnitFramework/framework/Internal/Execution/TextMessageWriter.cs b/src/NUnitFramework/framework/Internal/Execution/TextMessageWriter.cs index ed910dc4e7..8b83decff9 100644 --- a/src/NUnitFramework/framework/Internal/Execution/TextMessageWriter.cs +++ b/src/NUnitFramework/framework/Internal/Execution/TextMessageWriter.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -51,6 +51,10 @@ public class TextMessageWriter : MessageWriter /// public static readonly string Pfx_Actual = " But was: "; /// + /// Prefix used for the actual difference between actual and expected values if compared with a tolerance + /// + public static readonly string Pfx_Difference = " Off by: "; + /// /// Length of a message prefix /// public static readonly int PrefixLength = Pfx_Expected.Length; @@ -96,7 +100,7 @@ public override int MaxLineLength #region Public Methods - High Level /// /// Method to write single line message with optional args, usually - /// written to precede the general failure message, at a given + /// written to precede the general failure message, at a given /// indentation level. /// /// The indentation level of the message @@ -117,8 +121,8 @@ public override void WriteMessageLine(int level, string message, params object[] /// /// Display Expected and Actual lines for a constraint. This - /// is called by MessageWriter's default implementation of - /// WriteMessageTo and provides the generic two-line display. + /// is called by MessageWriter's default implementation of + /// WriteMessageTo and provides the generic two-line display. /// /// The result of the constraint that failed public override void DisplayDifferences(ConstraintResult result) @@ -171,7 +175,11 @@ public override void DisplayDifferences(object expected, object actual, Toleranc ResolveTypeNameDifference(expected, actual, out _expectedType, out _actualType); } WriteExpectedLine(expected, tolerance); - WriteActualLine(actual); + WriteActualLine(actual); + if (tolerance != null) + { + WriteDifferenceLine(expected, actual, tolerance); + } } /// @@ -257,15 +265,6 @@ private void WriteExpectedLine(ConstraintResult result) WriteLine(result.Description); } - /// - /// Write the generic 'Expected' line for a given value - /// - /// The expected value - private void WriteExpectedLine(object expected) - { - WriteExpectedLine(expected, null); - } - /// /// Write the generic 'Expected' line for a given value /// and tolerance. @@ -276,7 +275,7 @@ private void WriteExpectedLine(object expected, Tolerance tolerance) { Write(Pfx_Expected); Write(MsgUtils.FormatValue(expected)); - if (_sameValDiffTypes) { + if (_sameValDiffTypes) { Write(_expectedType); } if (tolerance != null && !tolerance.IsUnsetOrDefault) @@ -322,6 +321,23 @@ private void WriteActualLine(object actual) WriteLine(); } + private void WriteDifferenceLine(object expected, object actual, Tolerance tolerance) + { + // It only makes sense to display absolute/percent difference + if (tolerance.Mode != ToleranceMode.Linear && tolerance.Mode != ToleranceMode.Percent) + return; + + var differenceString = MsgUtils.FormatValue(Numerics.Difference(expected, actual, tolerance.Mode)); + if (differenceString != double.NaN.ToString()) + { + Write(Pfx_Difference); + Write(differenceString); + if (tolerance.Mode != ToleranceMode.Linear) + Write(" {0}", tolerance.Mode); + WriteLine(); + } + } + private void WriteCaretLine(int mismatch) { // We subtract 2 for the initial 2 blanks and add back 1 for the initial quote diff --git a/src/NUnitFramework/framework/Internal/Execution/WorkItem.cs b/src/NUnitFramework/framework/Internal/Execution/WorkItem.cs index 364a557062..070f955b24 100644 --- a/src/NUnitFramework/framework/Internal/Execution/WorkItem.cs +++ b/src/NUnitFramework/framework/Internal/Execution/WorkItem.cs @@ -67,9 +67,7 @@ public WorkItem(Test test, ITestFilter filter) ? (ParallelScope)Test.Properties.Get(PropertyNames.ParallelScope) : ParallelScope.Default; -#if APARTMENT_STATE TargetApartment = GetTargetApartment(Test); -#endif State = WorkItemState.Ready; } @@ -89,12 +87,8 @@ public WorkItem(WorkItem wrappedItem) Result = wrappedItem.Result; Context = wrappedItem.Context; ParallelScope = wrappedItem.ParallelScope; -#if PARALLEL TestWorker = wrappedItem.TestWorker; -#endif -#if APARTMENT_STATE TargetApartment = wrappedItem.TargetApartment; -#endif // State is independent of the wrapped item State = WorkItemState.Ready; @@ -130,7 +124,7 @@ public void InitializeContext(TestExecutionContext context) /// /// Gets the current state of the WorkItem /// - public WorkItemState State { get; private set; } + public WorkItemState State { get; protected set; } /// /// The test being executed by the work item @@ -155,7 +149,6 @@ public virtual string Name /// public TestExecutionContext Context { get; private set; } -#if PARALLEL /// /// The worker executing this item. /// @@ -181,7 +174,6 @@ public virtual ParallelExecutionStrategy ExecutionStrategy /// Indicates whether this work item should use a separate dispatcher. /// public virtual bool IsolateChildTests { get; } = false; -#endif /// /// The test result @@ -194,10 +186,8 @@ public virtual ParallelExecutionStrategy ExecutionStrategy /// public ParallelScope ParallelScope { get; } -#if APARTMENT_STATE internal ApartmentState TargetApartment { get; set; } private ApartmentState CurrentApartment { get; set; } -#endif #endregion @@ -209,7 +199,6 @@ public virtual ParallelExecutionStrategy ExecutionStrategy /// public virtual void Execute() { -#if PARALLEL // A supplementary thread is required in two conditions... // // 1. If the test used the RequiresThreadAttribute. This @@ -222,15 +211,11 @@ public virtual void Execute() // (--workers=0 option) it occurs routinely whenever a // different apartment is requested. -#if APARTMENT_STATE CurrentApartment = Thread.CurrentThread.GetApartmentState(); var targetApartment = TargetApartment == ApartmentState.Unknown ? CurrentApartment : TargetApartment; var needsNewThreadToSetApartmentState = targetApartment != CurrentApartment; if (Test.RequiresThread || needsNewThreadToSetApartmentState) -#else - if (Test.RequiresThread) -#endif { // Handle error conditions in a single threaded fixture if (Context.IsSingleThreaded) @@ -248,17 +233,10 @@ public virtual void Execute() log.Debug("Running on separate thread because {0} is specified.", Test.RequiresThread ? "RequiresThread" : "different Apartment"); -#if APARTMENT_STATE RunOnSeparateThread(targetApartment); -#else - RunOnSeparateThread(); -#endif } else RunOnCurrentThread(); -#else - RunOnCurrentThread(); -#endif } private readonly ManualResetEventSlim _completionEvent = new ManualResetEventSlim(); @@ -303,6 +281,7 @@ public virtual void Cancel(bool force) lock (threadLock) { + // Exit if not running on a separate thread if (thread == null) return; @@ -387,8 +366,12 @@ protected void WorkItemComplete() /// /// Unsorted array of setup MethodInfos. /// Unsorted array of teardown MethodInfos. + /// Method validator used before each method execution. /// A list of SetUpTearDownItems - protected List BuildSetUpTearDownList(MethodInfo[] setUpMethods, MethodInfo[] tearDownMethods) + protected List BuildSetUpTearDownList( + MethodInfo[] setUpMethods, + MethodInfo[] tearDownMethods, + IMethodValidator methodValidator = null) { Guard.ArgumentNotNull(setUpMethods, nameof(setUpMethods)); Guard.ArgumentNotNull(tearDownMethods, nameof(tearDownMethods)); @@ -401,7 +384,7 @@ protected List BuildSetUpTearDownList(MethodInfo[] setUpMetho while (fixtureType != null && fixtureType != typeof(object)) { - var node = BuildNode(fixtureType, setUpMethods, tearDownMethods); + var node = BuildNode(fixtureType, setUpMethods, tearDownMethods, methodValidator); if (node.HasMethods) list.Add(node); @@ -423,7 +406,11 @@ protected List BuildSetUpTearDownList(MethodInfo[] setUpMetho // teardown methods, found using a single reflection call, // and then descend through the inheritance hierarchy, // adding each method to the appropriate level as we go. - private static SetUpTearDownItem BuildNode(Type fixtureType, IList setUpMethods, IList tearDownMethods) + private static SetUpTearDownItem BuildNode( + Type fixtureType, + IList setUpMethods, + IList tearDownMethods, + IMethodValidator methodValidator) { // Create lists of methods for this level only. // Note that FindAll can't be used because it's not @@ -431,7 +418,7 @@ private static SetUpTearDownItem BuildNode(Type fixtureType, IList s var mySetUpMethods = SelectMethodsByDeclaringType(fixtureType, setUpMethods); var myTearDownMethods = SelectMethodsByDeclaringType(fixtureType, tearDownMethods); - return new SetUpTearDownItem(mySetUpMethods, myTearDownMethods); + return new SetUpTearDownItem(mySetUpMethods, myTearDownMethods, methodValidator); } private static List SelectMethodsByDeclaringType(Type type, IList methods) @@ -461,14 +448,9 @@ protected void ChangeResult(ResultState resultState, string message) #region Private Methods -#if PARALLEL private Thread thread; -#if APARTMENT_STATE private void RunOnSeparateThread(ApartmentState apartment) -#else - private void RunOnSeparateThread() -#endif { thread = new Thread(() => { @@ -480,7 +462,7 @@ private void RunOnSeparateThread() #endif RunOnCurrentThread(); }); -#if APARTMENT_STATE + try { thread.SetApartmentState(apartment); @@ -493,40 +475,28 @@ private void RunOnSeparateThread() WorkItemComplete(); return; } -#endif + thread.Start(); thread.Join(); } -#endif [SecuritySafeCritical] private void RunOnCurrentThread() { - var previousState = SandboxedThreadState.Capture(); - try - { - Context.CurrentTest = this.Test; - Context.CurrentResult = this.Result; - Context.Listener.TestStarted(this.Test); - Context.StartTime = DateTime.UtcNow; - Context.StartTicks = Stopwatch.GetTimestamp(); -#if PARALLEL - Context.TestWorker = this.TestWorker; -#endif + Context.CurrentTest = this.Test; + Context.CurrentResult = this.Result; + Context.Listener.TestStarted(this.Test); + Context.StartTime = DateTime.UtcNow; + Context.StartTicks = Stopwatch.GetTimestamp(); + Context.TestWorker = this.TestWorker; - Context.EstablishExecutionEnvironment(); + Context.EstablishExecutionEnvironment(); - State = WorkItemState.Running; + State = WorkItemState.Running; - PerformWork(); - } - finally - { - previousState.Restore(); - } + PerformWork(); } -#if PARALLEL private ParallelExecutionStrategy GetExecutionStrategy() { // If there is no fixture and so nothing to do but dispatch @@ -560,9 +530,7 @@ private ParallelExecutionStrategy GetExecutionStrategy() ? ParallelExecutionStrategy.Direct : ParallelExecutionStrategy.NonParallel; } -#endif -#if APARTMENT_STATE /// /// Recursively walks up the test hierarchy to see if the /// has been set on any of the parent tests. @@ -578,7 +546,6 @@ static ApartmentState GetTargetApartment(ITest test) return apartment; } -#endif #endregion } diff --git a/src/NUnitFramework/framework/Internal/Execution/WorkItemBuilder.cs b/src/NUnitFramework/framework/Internal/Execution/WorkItemBuilder.cs index 26b642c754..1129716635 100644 --- a/src/NUnitFramework/framework/Internal/Execution/WorkItemBuilder.cs +++ b/src/NUnitFramework/framework/Internal/Execution/WorkItemBuilder.cs @@ -21,9 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +using System; using System.Collections.Generic; using System.Threading; using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal.Abstractions; namespace NUnit.Framework.Internal.Execution { @@ -41,11 +43,25 @@ public static class WorkItemBuilder /// The filter to be used in selecting any child Tests. /// True if child work items should be created and added. /// + [Obsolete("This member will be removed in a future major release.")] static public WorkItem CreateWorkItem(ITest test, ITestFilter filter, bool recursive = false) + { + return CreateWorkItem(test, filter, new DebuggerProxy(), recursive); + } + + /// + /// Creates a work item. + /// + /// The test for which this WorkItem is being created. + /// The filter to be used in selecting any child Tests. + /// An instance. + /// True if child work items should be created and added. + /// + internal static WorkItem CreateWorkItem(ITest test, ITestFilter filter, IDebugger debugger, bool recursive = false) { TestSuite suite = test as TestSuite; if (suite == null) - return new SimpleWorkItem((TestMethod)test, filter); + return new SimpleWorkItem((TestMethod)test, filter, debugger); var work = new CompositeWorkItem(suite, filter); @@ -57,12 +73,10 @@ static public WorkItem CreateWorkItem(ITest test, ITestFilter filter, bool recur { if (filter.Pass(childTest)) { - var childItem = CreateWorkItem(childTest, filter, recursive); + var childItem = CreateWorkItem(childTest, filter, debugger, recursive); -#if APARTMENT_STATE if (childItem.TargetApartment == ApartmentState.Unknown && work.TargetApartment != ApartmentState.Unknown) childItem.TargetApartment = work.TargetApartment; -#endif if (childTest.Properties.ContainsKey(PropertyNames.Order)) { diff --git a/src/NUnitFramework/framework/Internal/Execution/WorkItemQueue.cs b/src/NUnitFramework/framework/Internal/Execution/WorkItemQueue.cs index 98d1cd0f28..fa97e9f662 100644 --- a/src/NUnitFramework/framework/Internal/Execution/WorkItemQueue.cs +++ b/src/NUnitFramework/framework/Internal/Execution/WorkItemQueue.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System.Collections.Concurrent; using System.Collections.Generic; using System.Threading; @@ -101,7 +100,6 @@ public SavedState(WorkItemQueue queue) private int _addId = int.MinValue; private int _removeId = int.MinValue; -#if APARTMENT_STATE /// /// Initializes a new instance of the class. /// @@ -109,20 +107,10 @@ public SavedState(WorkItemQueue queue) /// Flag indicating whether this is a parallel queue /// ApartmentState to use for items on this queue public WorkItemQueue(string name, bool isParallel, ApartmentState apartment) -#else - /// - /// Initializes a new instance of the class. - /// - /// The name of the queue. - /// Flag indicating whether this is a parallel queue - public WorkItemQueue(string name, bool isParallel) -#endif { Name = name; IsParallelQueue = isParallel; -#if APARTMENT_STATE TargetApartment = apartment; -#endif State = WorkItemQueueState.Paused; ItemsProcessed = 0; @@ -152,12 +140,10 @@ private void InitializeQueues() /// public bool IsParallelQueue { get; } -#if APARTMENT_STATE /// /// Gets the target ApartmentState for work items on this queue /// public ApartmentState TargetApartment { get; } -#endif private int _itemsProcessed; /// @@ -367,8 +353,7 @@ internal void Restore() // If there are any queued items, copy to the next lower level for (int i = 0; i < PRIORITY_LEVELS; i++) { - WorkItem work; - while (_innerQueues[i].TryDequeue(out work)) + while (_innerQueues[i].TryDequeue(out var work)) state.InnerQueues[i].Enqueue(work); } @@ -418,4 +403,3 @@ internal string DumpContents() #endregion } } -#endif diff --git a/src/NUnitFramework/framework/Internal/Execution/WorkShift.cs b/src/NUnitFramework/framework/Internal/Execution/WorkShift.cs index 06a854cec9..5a23ec3a40 100644 --- a/src/NUnitFramework/framework/Internal/Execution/WorkShift.cs +++ b/src/NUnitFramework/framework/Internal/Execution/WorkShift.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System; using System.Collections.Generic; using System.Threading; @@ -71,7 +70,7 @@ public WorkShift(string name) /// Event that fires when the shift has ended /// public event ShiftChangeEventHandler EndOfShift; - + /// /// The Name of this shift /// @@ -161,7 +160,7 @@ public void Start() _firstStart = false; } - + private void StartWorkers() { foreach (var worker in Workers) @@ -233,5 +232,3 @@ public void Cancel(bool force) #endregion } } - -#endif diff --git a/src/NUnitFramework/framework/Internal/Extensions/IPropertyBagDataExtensions.cs b/src/NUnitFramework/framework/Internal/Extensions/IPropertyBagDataExtensions.cs new file mode 100644 index 0000000000..8255f6a761 --- /dev/null +++ b/src/NUnitFramework/framework/Internal/Extensions/IPropertyBagDataExtensions.cs @@ -0,0 +1,48 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +#nullable enable + +using System; +using NUnit.Framework.Interfaces; + +namespace NUnit.Framework.Internal.Extensions +{ + /// + /// Extensions to . + /// + internal static class IPropertyBagDataExtensions + { + /// + /// Adds the skip reason to tests that are ignored until a specific date. + /// + /// The test properties to add the skip reason to + /// The date that the test is being ignored until + /// The reason the test is being ignored until that date + internal static void AddIgnoreUntilReason(this IPropertyBag properties, DateTimeOffset untilDate, string reason) + { + string skipReason = string.Format("Ignoring until {0}. {1}", untilDate.ToString("u"), reason); + properties.Set(PropertyNames.SkipReason, skipReason); + } + } +} diff --git a/src/NUnitFramework/framework/Internal/FSharpAsyncAwaitAdapter.cs b/src/NUnitFramework/framework/Internal/FSharpAsyncAwaitAdapter.cs new file mode 100644 index 0000000000..a83ce4938a --- /dev/null +++ b/src/NUnitFramework/framework/Internal/FSharpAsyncAwaitAdapter.cs @@ -0,0 +1,107 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Linq; +using System.Reflection; +using NUnit.Compatibility; + +namespace NUnit.Framework.Internal +{ + internal static class FSharpAsyncAwaitAdapter + { + private static MethodInfo _startImmediateAsTaskMethod; + + public static bool IsAwaitable(Type awaitableType) + { + return GetAsyncInfo(awaitableType) != null; + } + + public static Type GetResultType(Type awaitableType) + { + return GetAsyncInfo(awaitableType)?.ResultType; + } + + private static AsyncInfo GetAsyncInfo(Type asyncType) + { + if (asyncType == null) return null; + + if (!asyncType.GetTypeInfo().IsGenericType) return null; + var genericDefinition = asyncType.GetGenericTypeDefinition(); + if (genericDefinition.FullName != "Microsoft.FSharp.Control.FSharpAsync`1") return null; + + return new AsyncInfo(genericDefinition, asyncType.GetGenericArguments()[0]); + } + + private sealed class AsyncInfo + { + public AsyncInfo(Type fSharpAsyncTypeDefinition, Type resultType) + { + FSharpAsyncTypeDefinition = fSharpAsyncTypeDefinition; + ResultType = resultType; + } + + public Type FSharpAsyncTypeDefinition { get; } + public Type ResultType { get; } + } + + public static AwaitAdapter TryCreate(object awaitable) + { + if (awaitable == null) return null; + + var info = GetAsyncInfo(awaitable.GetType()); + if (info == null) return null; + + if (_startImmediateAsTaskMethod == null) + { + var asyncHelperMethodsType = info.FSharpAsyncTypeDefinition.GetTypeInfo().Assembly.GetType("Microsoft.FSharp.Control.FSharpAsync"); + if (asyncHelperMethodsType == null) + throw new InvalidOperationException("Cannot find non-generic FSharpAsync type in the same assembly as the generic one."); + + _startImmediateAsTaskMethod = asyncHelperMethodsType + .GetMethods(BindingFlags.Public | BindingFlags.Static) + .Single(method => + { + if (method.Name != "StartImmediateAsTask") return false; + var typeArguments = method.GetGenericArguments(); + if (typeArguments.Length != 1) return false; + + var parameters = method.GetParameters(); + if (parameters.Length != 2) return false; + + if (parameters[0].ParameterType != info.FSharpAsyncTypeDefinition.MakeGenericType(typeArguments[0])) return false; + + Type someType; + return parameters[1].ParameterType.IsFSharpOption(out someType) + && someType.FullName == "System.Threading.CancellationToken"; + }); + } + + var task = _startImmediateAsTaskMethod + .MakeGenericMethod(info.ResultType) + .Invoke(null, new[] { awaitable, null }); + + return AwaitAdapter.FromAwaitable(task); + } + } +} diff --git a/src/NUnitFramework/framework/Internal/Filters/AndFilter.cs b/src/NUnitFramework/framework/Internal/Filters/AndFilter.cs index 2ae43e8ecc..409fab5031 100644 --- a/src/NUnitFramework/framework/Internal/Filters/AndFilter.cs +++ b/src/NUnitFramework/framework/Internal/Filters/AndFilter.cs @@ -23,6 +23,7 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework.Interfaces; namespace NUnit.Framework.Internal.Filters @@ -42,20 +43,20 @@ internal class AndFilter : CompositeFilter /// Constructs an AndFilter from an array of filters /// /// - public AndFilter(params ITestFilter[] filters) : base(filters) { } + public AndFilter(params TestFilter[] filters) : base(filters) { } /// /// Checks whether the AndFilter is matched by a test /// /// The test to be matched + /// If set to we are carrying a negation through /// True if all the component filters pass, otherwise false - public override bool Pass( ITest test ) + public override bool Pass( ITest test, bool negated ) { - foreach( ITestFilter filter in Filters ) - if ( !filter.Pass( test ) ) - return false; + if (negated) + return Filters.Any(f => f.Pass(test, negated)); - return true; + return Filters.All(f => f.Pass(test, negated)); } /// diff --git a/src/NUnitFramework/framework/Internal/Filters/CompositeFilter.cs b/src/NUnitFramework/framework/Internal/Filters/CompositeFilter.cs index be803b3329..30ce05f1c6 100644 --- a/src/NUnitFramework/framework/Internal/Filters/CompositeFilter.cs +++ b/src/NUnitFramework/framework/Internal/Filters/CompositeFilter.cs @@ -37,23 +37,23 @@ internal abstract class CompositeFilter : TestFilter /// public CompositeFilter() { - Filters = new List(); + Filters = new List(); } /// /// Constructs a CompositeFilter from an array of filters /// /// - public CompositeFilter( params ITestFilter[] filters ) + public CompositeFilter( params TestFilter[] filters ) { - Filters = new List(filters); + Filters = new List(filters); } /// /// Adds a filter to the list of filters /// /// The filter to be added - public void Add(ITestFilter filter) + public void Add(TestFilter filter) { Filters.Add(filter); } @@ -61,13 +61,14 @@ public void Add(ITestFilter filter) /// /// Return a list of the composing filters. /// - public IList Filters { get; } + public IList Filters { get; } /// /// Checks whether the CompositeFilter is matched by a test. /// /// The test to be matched - public abstract override bool Pass(ITest test); + /// If set to we are carrying a negation through + public abstract override bool Pass(ITest test, bool negated); /// /// Checks whether the CompositeFilter is matched by a test. diff --git a/src/NUnitFramework/framework/Internal/Filters/NotFilter.cs b/src/NUnitFramework/framework/Internal/Filters/NotFilter.cs index 1e51cdaef6..79f0baf2a0 100644 --- a/src/NUnitFramework/framework/Internal/Filters/NotFilter.cs +++ b/src/NUnitFramework/framework/Internal/Filters/NotFilter.cs @@ -47,20 +47,13 @@ public NotFilter( TestFilter baseFilter) /// /// Determine if a particular test passes the filter criteria. - /// - /// Overriden in NotFilter so that - /// 1. Two nested NotFilters are simply ignored - /// 2. Otherwise, we only look at the test itself and parents, ignoring - /// any child test matches. /// /// The test to which the filter is applied + /// If set to we are carrying a negation through /// True if the test passes the filter, otherwise false - public override bool Pass(ITest test) + public override bool Pass(ITest test, bool negated) { - var secondNotFilter = BaseFilter as NotFilter; - return secondNotFilter != null - ? secondNotFilter.BaseFilter.Pass(test) - : !BaseFilter.Match (test) && !BaseFilter.MatchParent (test); + return BaseFilter.Pass(test, !negated); } /// diff --git a/src/NUnitFramework/framework/Internal/Filters/OrFilter.cs b/src/NUnitFramework/framework/Internal/Filters/OrFilter.cs index a39768f8a2..ce8efb60f8 100644 --- a/src/NUnitFramework/framework/Internal/Filters/OrFilter.cs +++ b/src/NUnitFramework/framework/Internal/Filters/OrFilter.cs @@ -23,6 +23,7 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework.Interfaces; namespace NUnit.Framework.Internal.Filters @@ -39,23 +40,23 @@ internal class OrFilter : CompositeFilter public OrFilter() { } /// - /// Constructs an AndFilter from an array of filters + /// Constructs an OrFilter from an array of filters /// /// - public OrFilter( params ITestFilter[] filters ) : base(filters) { } + public OrFilter( params TestFilter[] filters ) : base(filters) { } /// /// Checks whether the OrFilter is matched by a test /// /// The test to be matched + /// If set to we are carrying a negation through /// True if any of the component filters pass, otherwise false - public override bool Pass( ITest test ) + public override bool Pass( ITest test, bool negated ) { - foreach( ITestFilter filter in Filters ) - if ( filter.Pass( test ) ) - return true; + if (negated) + return Filters.All(f => f.Pass(test, negated)); - return false; + return Filters.Any(f => f.Pass(test, negated)); } /// diff --git a/src/NUnitFramework/framework/Internal/Filters/ValueMatchFilter.cs b/src/NUnitFramework/framework/Internal/Filters/ValueMatchFilter.cs index b01bb0ffb3..a3267755f0 100644 --- a/src/NUnitFramework/framework/Internal/Filters/ValueMatchFilter.cs +++ b/src/NUnitFramework/framework/Internal/Filters/ValueMatchFilter.cs @@ -56,7 +56,7 @@ public ValueMatchFilter(string expectedValue) /// /// Match the input provided by the derived class /// - /// The value to be matchedT + /// The value to be matched /// True for a match, false otherwise. protected bool Match(string input) { diff --git a/src/NUnitFramework/framework/Internal/GenericMethodHelper.cs b/src/NUnitFramework/framework/Internal/GenericMethodHelper.cs index d7f54a25f8..9438419ff5 100644 --- a/src/NUnitFramework/framework/Internal/GenericMethodHelper.cs +++ b/src/NUnitFramework/framework/Internal/GenericMethodHelper.cs @@ -109,7 +109,9 @@ private void TryApplyArgType(Type parmType, Type argType) } else if (parmType.GetTypeInfo().ContainsGenericParameters) { - var genericArgTypes = parmType.GetGenericArguments(); + var genericArgTypes = parmType.IsArray + ? new[] { parmType.GetElementType() } + : parmType.GetGenericArguments(); if (argType.HasElementType) { diff --git a/src/NUnitFramework/framework/Internal/IgnoredTestCaseData.cs b/src/NUnitFramework/framework/Internal/IgnoredTestCaseData.cs new file mode 100644 index 0000000000..7f34baf417 --- /dev/null +++ b/src/NUnitFramework/framework/Internal/IgnoredTestCaseData.cs @@ -0,0 +1,94 @@ +// *********************************************************************** +// Copyright (c) 2008–2018 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.ComponentModel; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using NUnit.Framework.Internal.Extensions; + +namespace NUnit.Framework +{ + /// + /// The IgnoredTestCaseData class represents a ignored TestCaseData. It adds + /// the ability to set a date until which the test will be ignored. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + public sealed class IgnoredTestCaseData : TestCaseData + { + #region Instance Fields + + /// + /// The previous RunState + /// + private RunState _prevRunState; + + #endregion + + #region Constructors + + internal IgnoredTestCaseData(TestCaseData data, RunState prevRunState) + { + this.Arguments = data.Arguments; + this.ArgDisplayNames = data.ArgDisplayNames; + this.ExpectedResult = data.ExpectedResult; + this.HasExpectedResult = data.HasExpectedResult; + this.OriginalArguments = data.OriginalArguments; + this.Properties = data.Properties; + this.RunState = data.RunState; + this.TestName = data.TestName; + this._prevRunState = prevRunState; + } + + #endregion + + #region Fluent Instance Modifiers + + /// + /// Set the date that the test is being ignored until + /// + /// The date that the test is being ignored until + /// A modified TestCaseData. + public TestCaseData Until(DateTimeOffset datetime) + { + if (_prevRunState != RunState.NotRunnable) + { + if (datetime > DateTimeOffset.UtcNow) + { + RunState = RunState.Ignored; + string reason = (string)Properties.Get(PropertyNames.SkipReason); + Properties.AddIgnoreUntilReason(datetime, reason); + } + else + { + RunState = _prevRunState; + } + Properties.Set(PropertyNames.IgnoreUntilDate, datetime.ToString("u") ); + } + return this; + } + + #endregion + + } +} diff --git a/src/NUnitFramework/framework/Internal/ImmutableStack.cs b/src/NUnitFramework/framework/Internal/ImmutableStack.cs new file mode 100644 index 0000000000..4a77245651 --- /dev/null +++ b/src/NUnitFramework/framework/Internal/ImmutableStack.cs @@ -0,0 +1,76 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System.Collections; +using System.Collections.Generic; + +namespace NUnit.Framework.Internal +{ + /// + /// A minimalistic implementation of an immutable stack. Add members as needed. + /// + internal struct ImmutableStack : IEnumerable + { + /// + /// Represents an empty stack. may be used instead. + /// + public static ImmutableStack Empty => default(ImmutableStack); + + private readonly Node _head; + + private ImmutableStack(Node head) + { + _head = head; + } + + /// + /// Returns a new immutable stack which begins with the specified value and continues with the values in the + /// current stack. + /// + /// The beginning value of the new stack. + public ImmutableStack Push(T value) + { + return new ImmutableStack(new Node(value, _head)); + } + + public IEnumerator GetEnumerator() + { + for (var current = _head; current != null; current = current.Next) + yield return current.Value; + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + private sealed class Node + { + public Node(T value, Node next) + { + Value = value; + Next = next; + } + + public T Value { get; } + public Node Next { get; } + } + } +} diff --git a/src/NUnitFramework/framework/Internal/InvalidDataSourceException.cs b/src/NUnitFramework/framework/Internal/InvalidDataSourceException.cs index a60cbfefba..957ef52d55 100644 --- a/src/NUnitFramework/framework/Internal/InvalidDataSourceException.cs +++ b/src/NUnitFramework/framework/Internal/InvalidDataSourceException.cs @@ -24,9 +24,7 @@ namespace NUnit.Framework.Internal { using System; -#if SERIALIZATION using System.Runtime.Serialization; -#endif /// /// InvalidTestFixtureException is thrown when an appropriate test @@ -55,12 +53,10 @@ public InvalidDataSourceException(string message) : base(message) public InvalidDataSourceException(string message, Exception inner) : base(message, inner) { } -#if SERIALIZATION /// /// Serialization Constructor /// protected InvalidDataSourceException(SerializationInfo info, StreamingContext context) : base(info,context){} -#endif } } diff --git a/src/NUnitFramework/framework/Internal/InvalidPlatformException.cs b/src/NUnitFramework/framework/Internal/InvalidPlatformException.cs index 8066af2562..14fae0c895 100644 --- a/src/NUnitFramework/framework/Internal/InvalidPlatformException.cs +++ b/src/NUnitFramework/framework/Internal/InvalidPlatformException.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PLATFORM_DETECTION using System; using System.Runtime.Serialization; @@ -60,4 +59,3 @@ protected InvalidPlatformException(SerializationInfo info, StreamingContext cont { } } } -#endif diff --git a/src/NUnitFramework/framework/Internal/InvalidTestFixtureException.cs b/src/NUnitFramework/framework/Internal/InvalidTestFixtureException.cs index c47d74c592..03505884bf 100644 --- a/src/NUnitFramework/framework/Internal/InvalidTestFixtureException.cs +++ b/src/NUnitFramework/framework/Internal/InvalidTestFixtureException.cs @@ -24,9 +24,7 @@ namespace NUnit.Framework.Internal { using System; -#if SERIALIZATION using System.Runtime.Serialization; -#endif /// /// InvalidTestFixtureException is thrown when an appropriate test @@ -55,12 +53,10 @@ public InvalidTestFixtureException(string message) : base(message) public InvalidTestFixtureException(string message, Exception inner) : base(message, inner) { } -#if SERIALIZATION /// /// Serialization Constructor /// protected InvalidTestFixtureException(SerializationInfo info, StreamingContext context) : base(info,context){} -#endif } } diff --git a/src/NUnitFramework/framework/Internal/Logging/Logger.cs b/src/NUnitFramework/framework/Internal/Logging/Logger.cs index 416dd889fd..a0b3dae367 100644 --- a/src/NUnitFramework/framework/Internal/Logging/Logger.cs +++ b/src/NUnitFramework/framework/Internal/Logging/Logger.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -165,11 +165,7 @@ private void WriteLog(InternalTraceLevel level, string message) writer.WriteLine(TRACE_FMT, DateTime.Now.ToString(TIME_FMT), level == InternalTraceLevel.Verbose ? "Debug" : level.ToString(), -#if NETSTANDARD1_4 - System.Environment.CurrentManagedThreadId, -#else System.Threading.Thread.CurrentThread.ManagedThreadId, -#endif name, message); } diff --git a/src/NUnitFramework/framework/Internal/MessagePumpStrategy.cs b/src/NUnitFramework/framework/Internal/MessagePumpStrategy.cs index 1ac0bfcc44..d719eb13b7 100644 --- a/src/NUnitFramework/framework/Internal/MessagePumpStrategy.cs +++ b/src/NUnitFramework/framework/Internal/MessagePumpStrategy.cs @@ -22,8 +22,11 @@ // *********************************************************************** using System; +using System.Reflection; +using System.Runtime.CompilerServices; using System.Security; using System.Threading; +using NUnit.Compatibility; #if NET40 || NET45 using System.Windows.Forms; @@ -34,7 +37,7 @@ namespace NUnit.Framework.Internal { internal abstract class MessagePumpStrategy { - public abstract void WaitForCompletion(AwaitAdapter awaitable); + public abstract void WaitForCompletion(AwaitAdapter awaiter); public static MessagePumpStrategy FromCurrentSynchronizationContext() { @@ -43,15 +46,9 @@ public static MessagePumpStrategy FromCurrentSynchronizationContext() if (context is SingleThreadedTestSynchronizationContext) return SingleThreadedTestMessagePumpStrategy.Instance; -#if NET40 || NET45 - if (context is WindowsFormsSynchronizationContext) - return WindowsFormsMessagePumpStrategy.Instance; - - if (context is DispatcherSynchronizationContext) - return WpfMessagePumpStrategy.Instance; -#endif - - return NoMessagePumpStrategy.Instance; + return WindowsFormsMessagePumpStrategy.GetIfApplicable() + ?? WpfMessagePumpStrategy.GetIfApplicable() + ?? NoMessagePumpStrategy.Instance; } private sealed class NoMessagePumpStrategy : MessagePumpStrategy @@ -59,39 +56,73 @@ private sealed class NoMessagePumpStrategy : MessagePumpStrategy public static readonly NoMessagePumpStrategy Instance = new NoMessagePumpStrategy(); private NoMessagePumpStrategy() { } - public override void WaitForCompletion(AwaitAdapter awaitable) + public override void WaitForCompletion(AwaitAdapter awaiter) { - awaitable.BlockUntilCompleted(); + awaiter.BlockUntilCompleted(); } } -#if NET40 || NET45 private sealed class WindowsFormsMessagePumpStrategy : MessagePumpStrategy { - public static readonly WindowsFormsMessagePumpStrategy Instance = new WindowsFormsMessagePumpStrategy(); - private WindowsFormsMessagePumpStrategy() { } + private static WindowsFormsMessagePumpStrategy _instance; + + private readonly Action _applicationRun; + private readonly Action _applicationExit; + + private WindowsFormsMessagePumpStrategy(Action applicationRun, Action applicationExit) + { + _applicationRun = applicationRun; + _applicationExit = applicationExit; + } + + public static MessagePumpStrategy GetIfApplicable() + { + if (!IsApplicable(SynchronizationContext.Current)) return null; + + if (_instance is null) + { + var applicationType = SynchronizationContext.Current.GetType().Assembly.GetType("System.Windows.Forms.Application", throwOnError: true); + + var applicationRun = (Action)applicationType + .GetMethod("Run", BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly, null, Type.EmptyTypes, null) + .CreateDelegate(typeof(Action)); + + var applicationExit = (Action)applicationType + .GetMethod("Exit", BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly, null, Type.EmptyTypes, null) + .CreateDelegate(typeof(Action)); + + _instance = new WindowsFormsMessagePumpStrategy(applicationRun, applicationExit); + } + + return _instance; + } + + private static bool IsApplicable(SynchronizationContext context) + { + return context?.GetType().FullName == "System.Windows.Forms.WindowsFormsSynchronizationContext"; + } [SecuritySafeCritical] - public override void WaitForCompletion(AwaitAdapter awaitable) + public override void WaitForCompletion(AwaitAdapter awaiter) { var context = SynchronizationContext.Current; - if (!(context is WindowsFormsSynchronizationContext)) + if (!IsApplicable(context)) throw new InvalidOperationException("This strategy must only be used from a WindowsFormsSynchronizationContext."); - if (awaitable.IsCompleted) return; + if (awaiter.IsCompleted) return; // Wait for a post rather than scheduling the continuation now. If there has been a race condition // and it completed after the IsCompleted check, it will wait until the application runs *before* // shutting it down. Otherwise Application.Exit is a no-op and we would then proceed to do // Application.Run and never return. context.Post( - state => ContinueOnSameSynchronizationContext((AwaitAdapter)state, Application.Exit), - state: awaitable); + state => ContinueOnSameSynchronizationContext((AwaitAdapter)state, _applicationExit), + state: awaiter); try { - Application.Run(); + _applicationRun.Invoke(); } finally { @@ -102,64 +133,98 @@ public override void WaitForCompletion(AwaitAdapter awaitable) private sealed class WpfMessagePumpStrategy : MessagePumpStrategy { - public static readonly WpfMessagePumpStrategy Instance = new WpfMessagePumpStrategy(); - private WpfMessagePumpStrategy() { } + private static WpfMessagePumpStrategy _instance; + + private readonly Action _dispatcherRun; + private readonly Action _dispatcherExitAllFrames; + + private WpfMessagePumpStrategy(Action dispatcherRun, Action dispatcherExitAllFrames) + { + _dispatcherRun = dispatcherRun; + _dispatcherExitAllFrames = dispatcherExitAllFrames; + } + + public static MessagePumpStrategy GetIfApplicable() + { + if (!IsApplicable(SynchronizationContext.Current)) return null; + + if (_instance is null) + { + var dispatcherType = SynchronizationContext.Current.GetType().Assembly.GetType("System.Windows.Threading.Dispatcher", throwOnError: true); - public override void WaitForCompletion(AwaitAdapter awaitable) + var dispatcherRun = (Action)dispatcherType + .GetMethod("Run", BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly, null, Type.EmptyTypes, null) + .CreateDelegate(typeof(Action)); + + var dispatcherExitAllFrames = (Action)dispatcherType + .GetMethod("ExitAllFrames", BindingFlags.Public | BindingFlags.Static | BindingFlags.DeclaredOnly, null, Type.EmptyTypes, null) + .CreateDelegate(typeof(Action)); + + _instance = new WpfMessagePumpStrategy(dispatcherRun, dispatcherExitAllFrames); + } + + return _instance; + } + + private static bool IsApplicable(SynchronizationContext context) + { + return context?.GetType().FullName == "System.Windows.Threading.DispatcherSynchronizationContext"; + } + + public override void WaitForCompletion(AwaitAdapter awaiter) { var context = SynchronizationContext.Current; - if (!(context is DispatcherSynchronizationContext)) + if (!IsApplicable(context)) throw new InvalidOperationException("This strategy must only be used from a DispatcherSynchronizationContext."); - if (awaitable.IsCompleted) return; + if (awaiter.IsCompleted) return; // Wait for a post rather than scheduling the continuation now. If there has been a race condition // and it completed after the IsCompleted check, it will wait until the application runs *before* // shutting it down. Otherwise Dispatcher.ExitAllFrames is a no-op and we would then proceed to do // Dispatcher.Run and never return. context.Post( - state => ContinueOnSameSynchronizationContext((AwaitAdapter)state, Dispatcher.ExitAllFrames), - state: awaitable); + state => ContinueOnSameSynchronizationContext((AwaitAdapter)state, _dispatcherExitAllFrames), + state: awaiter); - Dispatcher.Run(); + _dispatcherRun.Invoke(); } } -#endif private sealed class SingleThreadedTestMessagePumpStrategy : MessagePumpStrategy { public static readonly SingleThreadedTestMessagePumpStrategy Instance = new SingleThreadedTestMessagePumpStrategy(); private SingleThreadedTestMessagePumpStrategy() { } - public override void WaitForCompletion(AwaitAdapter awaitable) + public override void WaitForCompletion(AwaitAdapter awaiter) { var context = SynchronizationContext.Current as SingleThreadedTestSynchronizationContext; if (context == null) throw new InvalidOperationException("This strategy must only be used from a SingleThreadedTestSynchronizationContext."); - if (awaitable.IsCompleted) return; + if (awaiter.IsCompleted) return; // Wait for a post rather than scheduling the continuation now. If there has been a race condition // and it completed after the IsCompleted check, it will wait until the message loop runs *before* // shutting it down. Otherwise context.ShutDown will throw. context.Post( state => ContinueOnSameSynchronizationContext((AwaitAdapter)state, context.ShutDown), - state: awaitable); + state: awaiter); context.Run(); } } - private static void ContinueOnSameSynchronizationContext(AwaitAdapter adapter, Action continuation) + private static void ContinueOnSameSynchronizationContext(AwaitAdapter awaiter, Action continuation) { - if (adapter == null) throw new ArgumentNullException(nameof(adapter)); + if (awaiter == null) throw new ArgumentNullException(nameof(awaiter)); if (continuation == null) throw new ArgumentNullException(nameof(continuation)); var context = SynchronizationContext.Current; - adapter.OnCompleted(() => + awaiter.OnCompleted(() => { if (SynchronizationContext.Current == context) continuation.Invoke(); diff --git a/src/NUnitFramework/framework/Internal/MethodWrapper.cs b/src/NUnitFramework/framework/Internal/MethodWrapper.cs index 4c15112f13..f6f798df49 100644 --- a/src/NUnitFramework/framework/Internal/MethodWrapper.cs +++ b/src/NUnitFramework/framework/Internal/MethodWrapper.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Reflection; using NUnit.Compatibility; @@ -175,7 +177,7 @@ public IMethodInfo MakeGenericMethod(params Type[] typeArguments) /// The object on which to invoke the method /// The argument list for the method /// The return value from the invoked method - public object Invoke(object fixture, params object[] args) + public object? Invoke(object? fixture, params object?[]? args) { return Reflect.InvokeMethod(MethodInfo, fixture, args); } diff --git a/src/NUnitFramework/framework/Internal/NUnitException.cs b/src/NUnitFramework/framework/Internal/NUnitException.cs index 7ee1402694..db42b982cd 100644 --- a/src/NUnitFramework/framework/Internal/NUnitException.cs +++ b/src/NUnitFramework/framework/Internal/NUnitException.cs @@ -24,9 +24,7 @@ namespace NUnit.Framework.Internal { using System; -#if SERIALIZATION using System.Runtime.Serialization; -#endif /// /// Thrown when an assertion failed. Here to preserve the inner @@ -60,12 +58,10 @@ public NUnitException(string message) : base (message) base(message, inner) { } -#if SERIALIZATION /// /// Serialization Constructor /// protected NUnitException(SerializationInfo info, StreamingContext context) : base(info,context){} -#endif } } diff --git a/src/NUnitFramework/framework/Internal/OSPlatform.cs b/src/NUnitFramework/framework/Internal/OSPlatform.cs index 260aefede0..96071caee0 100644 --- a/src/NUnitFramework/framework/Internal/OSPlatform.cs +++ b/src/NUnitFramework/framework/Internal/OSPlatform.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PLATFORM_DETECTION using Microsoft.Win32; using System; using System.Runtime.InteropServices; @@ -33,16 +32,12 @@ namespace NUnit.Framework.Internal /// /// OSPlatform represents a particular operating system platform /// - // This class invokes security critical P/Invoke and 'System.Runtime.InteropServices.Marshal' methods. - // Callers of this method have no influence on how these methods are used so we define a 'SecuritySafeCriticalAttribute' + // This class invokes security critical P/Invoke and 'System.Runtime.InteropServices.Marshal' methods. + // Callers of this method have no influence on how these methods are used so we define a 'SecuritySafeCriticalAttribute' // rather than a 'SecurityCriticalAttribute' to enable use by security transparent callers. [SecuritySafeCritical] public class OSPlatform { - readonly PlatformID _platform; - readonly Version _version; - readonly ProductType _product; - #region Static Members private static readonly Lazy currentPlatform = new Lazy (() => { @@ -52,12 +47,17 @@ public class OSPlatform if (os.Platform == PlatformID.Win32NT && os.Version.Major >= 5) { + if (os.Version.Major == 6 && os.Version.Minor >= 2) + os = new OperatingSystem(os.Platform, GetWindows81PlusVersion(os.Version)); +#if NETSTANDARD2_0 + ProductType productType = GetProductType(); + currentPlatform = new OSPlatform(os.Platform, os.Version, productType); +#else OSVERSIONINFOEX osvi = new OSVERSIONINFOEX(); osvi.dwOSVersionInfoSize = (uint)Marshal.SizeOf(osvi); GetVersionEx(ref osvi); - if (os.Version.Major == 6 && os.Version.Minor >= 2) - os = new OperatingSystem(os.Platform, GetWindows81PlusVersion(os.Version)); currentPlatform = new OSPlatform(os.Platform, os.Version, (ProductType)osvi.ProductType); +#endif } else if (CheckIfIsMacOSX(os.Platform)) { @@ -102,9 +102,12 @@ public static OSPlatform CurrentPlatform return currentPlatform.Value; } } + #endregion + + #region Members used for Win32NT platform only /// - /// Gets the actual OS Version, not the incorrect value that might be + /// Gets the actual OS Version, not the incorrect value that might be /// returned for Win 8.1 and Win 10 /// /// @@ -124,8 +127,7 @@ private static Version GetWindows81PlusVersion(Version version) if (key != null) { var buildStr = key.GetValue("CurrentBuildNumber") as string; - int build = 0; - int.TryParse(buildStr, out build); + int.TryParse(buildStr, out var build); // These two keys are in Windows 10 only and are DWORDS var major = key.GetValue("CurrentMajorVersionNumber") as int?; @@ -152,9 +154,7 @@ private static Version GetWindows81PlusVersion(Version version) } return version; } - #endregion - #region Members used for Win32NT platform only /// /// Product Type Enumeration used for Windows /// @@ -181,6 +181,35 @@ public enum ProductType Server, } +#if NETSTANDARD2_0 + private static ProductType GetProductType() + { + try + { + using (var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion")) + { + if (key != null) + { + var installationType = key.GetValue("InstallationType") as string; + switch(installationType) + { + case "Client": + return ProductType.WorkStation; + case "Server": + case "Server Core": + return ProductType.Server; + default: + return ProductType.Unknown; + } + } + } + } + catch (Exception) + { + } + return ProductType.Unknown; + } +#else [StructLayout(LayoutKind.Sequential)] struct OSVERSIONINFOEX { @@ -202,15 +231,16 @@ struct OSVERSIONINFOEX [DllImport("Kernel32.dll")] private static extern bool GetVersionEx(ref OSVERSIONINFOEX osvi); - #endregion +#endif +#endregion /// /// Construct from a platform ID and version /// public OSPlatform(PlatformID platform, Version version) { - _platform = platform; - _version = version; + Platform = platform; + Version = version; } /// @@ -219,16 +249,13 @@ public OSPlatform(PlatformID platform, Version version) public OSPlatform(PlatformID platform, Version version, ProductType product) : this( platform, version ) { - _product = product; + Product = product; } /// /// Get the platform ID of this instance /// - public PlatformID Platform - { - get { return _platform; } - } + public PlatformID Platform { get; } /// /// Implemented to use in place of Environment.OSVersion.ToString() @@ -237,7 +264,7 @@ public PlatformID Platform public override string ToString() { var sb = new StringBuilder(); - + switch (Platform) { case PlatformID.Win32NT: @@ -264,18 +291,12 @@ public override string ToString() /// /// Get the Version of this instance /// - public Version Version - { - get { return _version; } - } + public Version Version { get; } /// /// Get the Product Type of this instance /// - public ProductType Product - { - get { return _product; } - } + public ProductType Product { get; } /// /// Return true if this is a windows platform @@ -284,10 +305,10 @@ public bool IsWindows { get { - return _platform == PlatformID.Win32NT - || _platform == PlatformID.Win32Windows - || _platform == PlatformID.Win32S - || _platform == PlatformID.WinCE; + return Platform == PlatformID.Win32NT + || Platform == PlatformID.Win32Windows + || Platform == PlatformID.Win32S + || Platform == PlatformID.WinCE; } } @@ -298,8 +319,8 @@ public bool IsUnix { get { - return _platform == UnixPlatformID_Microsoft - || _platform == UnixPlatformID_Mono; + return Platform == UnixPlatformID_Microsoft + || Platform == UnixPlatformID_Mono; } } @@ -308,7 +329,7 @@ public bool IsUnix /// public bool IsWin32S { - get { return _platform == PlatformID.Win32S; } + get { return Platform == PlatformID.Win32S; } } /// @@ -316,7 +337,7 @@ public bool IsWin32S /// public bool IsWin32Windows { - get { return _platform == PlatformID.Win32Windows; } + get { return Platform == PlatformID.Win32Windows; } } /// @@ -324,7 +345,7 @@ public bool IsWin32Windows /// public bool IsWin32NT { - get { return _platform == PlatformID.Win32NT; } + get { return Platform == PlatformID.Win32NT; } } /// @@ -332,23 +353,36 @@ public bool IsWin32NT /// public bool IsWinCE { - get { return _platform == PlatformID.WinCE; } + get { return Platform == PlatformID.WinCE; } } - + /// /// Return true if the platform is Xbox /// public bool IsXbox { - get { return _platform == XBoxPlatformID; } + get { return Platform == XBoxPlatformID; } } /// /// Return true if the platform is MacOSX /// - public bool IsMacOSX + public bool IsMacOSX + { + get { return Platform == MacOSXPlatformID; } + } + + static int UnameSafe(IntPtr buf) { - get { return _platform == MacOSXPlatformID; } + try + { + return uname(buf); + } + catch (DllNotFoundException) + { + // https://github.com/nunit/nunit/issues/3608 + return -1; + } } [DllImport("libc")] @@ -359,14 +393,14 @@ public bool IsMacOSX static bool CheckIfIsMacOSX(PlatformID platform) { if (platform == PlatformID.MacOSX) - return true; + return true; if (platform != PlatformID.Unix) return false; IntPtr buf = Marshal.AllocHGlobal(8192); bool isMacOSX = false; - if (uname(buf) == 0) + if (UnameSafe(buf) == 0) { string os = Marshal.PtrToStringAnsi(buf); isMacOSX = os.Equals("Darwin"); @@ -380,7 +414,7 @@ static bool CheckIfIsMacOSX(PlatformID platform) /// public bool IsWin95 { - get { return _platform == PlatformID.Win32Windows && _version.Major == 4 && _version.Minor == 0; } + get { return Platform == PlatformID.Win32Windows && Version.Major == 4 && Version.Minor == 0; } } /// @@ -388,7 +422,7 @@ public bool IsWin95 /// public bool IsWin98 { - get { return _platform == PlatformID.Win32Windows && _version.Major == 4 && _version.Minor == 10; } + get { return Platform == PlatformID.Win32Windows && Version.Major == 4 && Version.Minor == 10; } } /// @@ -396,7 +430,7 @@ public bool IsWin98 /// public bool IsWinME { - get { return _platform == PlatformID.Win32Windows && _version.Major == 4 && _version.Minor == 90; } + get { return Platform == PlatformID.Win32Windows && Version.Major == 4 && Version.Minor == 90; } } /// @@ -404,7 +438,7 @@ public bool IsWinME /// public bool IsNT3 { - get { return _platform == PlatformID.Win32NT && _version.Major == 3; } + get { return Platform == PlatformID.Win32NT && Version.Major == 3; } } /// @@ -412,7 +446,7 @@ public bool IsNT3 /// public bool IsNT4 { - get { return _platform == PlatformID.Win32NT && _version.Major == 4; } + get { return Platform == PlatformID.Win32NT && Version.Major == 4; } } /// @@ -420,7 +454,7 @@ public bool IsNT4 /// public bool IsNT5 { - get { return _platform == PlatformID.Win32NT && _version.Major == 5; } + get { return Platform == PlatformID.Win32NT && Version.Major == 5; } } /// @@ -428,7 +462,7 @@ public bool IsNT5 /// public bool IsWin2K { - get { return IsNT5 && _version.Minor == 0; } + get { return IsNT5 && Version.Minor == 0; } } /// @@ -436,7 +470,7 @@ public bool IsWin2K /// public bool IsWinXP { - get { return IsNT5 && (_version.Minor == 1 || _version.Minor == 2 && Product == ProductType.WorkStation); } + get { return IsNT5 && (Version.Minor == 1 || Version.Minor == 2 && Product == ProductType.WorkStation); } } /// @@ -444,7 +478,7 @@ public bool IsWinXP /// public bool IsWin2003Server { - get { return IsNT5 && _version.Minor == 2 && Product == ProductType.Server; } + get { return IsNT5 && Version.Minor == 2 && Product == ProductType.Server; } } /// @@ -452,7 +486,7 @@ public bool IsWin2003Server /// public bool IsNT6 { - get { return _platform == PlatformID.Win32NT && _version.Major == 6; } + get { return Platform == PlatformID.Win32NT && Version.Major == 6; } } /// @@ -460,7 +494,7 @@ public bool IsNT6 /// public bool IsNT60 { - get { return IsNT6 && _version.Minor == 0; } + get { return IsNT6 && Version.Minor == 0; } } /// @@ -468,7 +502,7 @@ public bool IsNT60 /// public bool IsNT61 { - get { return IsNT6 && _version.Minor == 1; } + get { return IsNT6 && Version.Minor == 1; } } /// @@ -476,7 +510,7 @@ public bool IsNT61 /// public bool IsNT62 { - get { return IsNT6 && _version.Minor == 2; } + get { return IsNT6 && Version.Minor == 2; } } /// @@ -484,7 +518,7 @@ public bool IsNT62 /// public bool IsNT63 { - get { return IsNT6 && _version.Minor == 3; } + get { return IsNT6 && Version.Minor == 3; } } /// @@ -572,7 +606,7 @@ public bool IsWindows81 /// public bool IsWindows10 { - get { return _platform == PlatformID.Win32NT && _version.Major == 10 && Product == ProductType.WorkStation; } + get { return Platform == PlatformID.Win32NT && Version.Major == 10 && Product == ProductType.WorkStation; } } /// @@ -581,8 +615,7 @@ public bool IsWindows10 /// public bool IsWindowsServer10 { - get { return _platform == PlatformID.Win32NT && _version.Major == 10 && Product == ProductType.Server; } + get { return Platform == PlatformID.Win32NT && Version.Major == 10 && Product == ProductType.Server; } } } } -#endif diff --git a/src/NUnitFramework/framework/Internal/ParamAttributeTypeConversions.cs b/src/NUnitFramework/framework/Internal/ParamAttributeTypeConversions.cs index 6fd986d868..b780f583cc 100644 --- a/src/NUnitFramework/framework/Internal/ParamAttributeTypeConversions.cs +++ b/src/NUnitFramework/framework/Internal/ParamAttributeTypeConversions.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.ComponentModel; @@ -55,19 +57,18 @@ internal static class ParamAttributeTypeConversions /// /// Converts an array of objects to the , if it is supported. /// - public static IEnumerable ConvertData(object[] data, Type targetType) + public static IEnumerable ConvertData(object?[] data, Type targetType) { Guard.ArgumentNotNull(data, nameof(data)); Guard.ArgumentNotNull(targetType, nameof(targetType)); return GetData(data, targetType); } - private static IEnumerable GetData(object[] data, Type targetType) + private static IEnumerable GetData(object?[] data, Type targetType) { for (int i = 0; i < data.Length; i++) { - object convertedValue; - if (TryConvert(data[i], targetType, out convertedValue)) + if (TryConvert(data[i], targetType, out var convertedValue)) data[i] = convertedValue; } @@ -77,10 +78,9 @@ private static IEnumerable GetData(object[] data, Type targetType) /// /// Converts a single value to the , if it is supported. /// - public static object Convert(object value, Type targetType) + public static object? Convert(object? value, Type targetType) { - object convertedValue; - if (TryConvert(value, targetType, out convertedValue)) + if (TryConvert(value, targetType, out var convertedValue)) return convertedValue; throw new InvalidOperationException( @@ -97,10 +97,10 @@ public static object Convert(object value, Type targetType) /// The target in which the should be converted /// If conversion was successfully applied, the converted into /// - /// true if was converted and should be used; - /// false is no conversion was applied and should be ignored + /// if was converted and should be used; + /// is no conversion was applied and should be ignored /// - public static bool TryConvert(object value, Type targetType, out object convertedValue) + public static bool TryConvert(object? value, Type targetType, out object? convertedValue) { if (targetType.IsInstanceOfType(value)) { diff --git a/src/NUnitFramework/framework/Internal/ParameterWrapper.cs b/src/NUnitFramework/framework/Internal/ParameterWrapper.cs index 322c083679..ed3937b508 100644 --- a/src/NUnitFramework/framework/Internal/ParameterWrapper.cs +++ b/src/NUnitFramework/framework/Internal/ParameterWrapper.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Reflection; using NUnit.Compatibility; diff --git a/src/NUnitFramework/framework/Internal/PlatformHelper.cs b/src/NUnitFramework/framework/Internal/PlatformHelper.cs index 17e9c0a902..fd6299b897 100644 --- a/src/NUnitFramework/framework/Internal/PlatformHelper.cs +++ b/src/NUnitFramework/framework/Internal/PlatformHelper.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PLATFORM_DETECTION using System; using System.Linq; @@ -292,7 +291,7 @@ private bool IsRuntimeSupported(string platformName) switch (platformName.ToUpper()) { case "NET": - return IsRuntimeSupported(RuntimeType.Net, versionSpecification); + return IsRuntimeSupported(RuntimeType.NetFramework, versionSpecification); case "NETCORE": return IsNetCoreRuntimeSupported(RuntimeType.NetCore, versionSpecification); @@ -336,4 +335,3 @@ private bool IsNetCoreRuntimeSupported(RuntimeType runtime, string versionSpecif } } } -#endif diff --git a/src/NUnitFramework/framework/Internal/PreFilter.cs b/src/NUnitFramework/framework/Internal/PreFilter.cs index 8b062249ba..0fb1dffe16 100644 --- a/src/NUnitFramework/framework/Internal/PreFilter.cs +++ b/src/NUnitFramework/framework/Internal/PreFilter.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -23,6 +23,7 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reflection; using NUnit.Framework.Interfaces; @@ -30,7 +31,7 @@ namespace NUnit.Framework.Internal { /// /// Implements a simplified filter for use in deciding which - /// Types and Methods should be used to generate tests. It is consructed with a + /// Types and Methods should be used to generate tests. It is constructed with a /// list of strings, each of which may end up being interpreted in various ways. /// internal class PreFilter : IPreFilter @@ -75,8 +76,6 @@ public void Add(string filterText) return; } - var newFilter = new FilterElement(filterText); - // Check to see if it makes any of the existing // filter elements redundant. for (int index = _filters.Count - 1; index >= 0; index--) @@ -168,6 +167,12 @@ public FilterElement(string text) } public bool Match(Type type) + { + return MatchElementType(type) || + MatchSetUpFixture(type); + } + + private bool MatchElementType(Type type) { switch(ElementType) { @@ -220,6 +225,20 @@ private bool MatchMethodElement(Type type) { return type.FullName == ClassName; } + + private bool MatchSetUpFixture(Type type) + { + return IsSubNamespace(type.Namespace) && + type.GetCustomAttributes(typeof(SetUpFixtureAttribute), true).Any(); + } + + private bool IsSubNamespace(string typeNamespace) + { + if (string.IsNullOrEmpty(typeNamespace)) + return true; + + return (ClassName + '.').StartsWith(typeNamespace + '.'); + } } #endregion diff --git a/src/NUnitFramework/framework/Internal/PropertyBag.cs b/src/NUnitFramework/framework/Internal/PropertyBag.cs index 7148c854d9..4779e1b214 100644 --- a/src/NUnitFramework/framework/Internal/PropertyBag.cs +++ b/src/NUnitFramework/framework/Internal/PropertyBag.cs @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; +#nullable enable + using System.Collections; using System.Collections.Generic; using NUnit.Framework.Interfaces; @@ -51,8 +52,7 @@ public void Add(string key, object value) { Guard.ArgumentNotNull(value, "value"); - IList list; - if (!inner.TryGetValue(key, out list)) + if (!inner.TryGetValue(key, out var list)) { list = new List(); inner.Add(key, list); @@ -84,10 +84,9 @@ public void Set(string key, object value) /// /// /// - public object Get(string key) + public object? Get(string key) { - IList list; - return inner.TryGetValue(key, out list) && list.Count > 0 + return inner.TryGetValue(key, out var list) && list.Count > 0 ? list[0] : null; } @@ -121,8 +120,7 @@ public ICollection Keys { get { - IList list; - if (!inner.TryGetValue(key, out list)) + if (!inner.TryGetValue(key, out var list)) { list = new List(); inner.Add(key, list); diff --git a/src/NUnitFramework/framework/Internal/Randomizer.cs b/src/NUnitFramework/framework/Internal/Randomizer.cs index 75a3d1e5ab..60a9164589 100644 --- a/src/NUnitFramework/framework/Internal/Randomizer.cs +++ b/src/NUnitFramework/framework/Internal/Randomizer.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections; using System.Collections.Generic; @@ -34,18 +36,18 @@ namespace NUnit.Framework.Internal /// way, to allow re-running of tests if necessary. It extends /// the .NET Random class, providing random values for a much /// wider range of types. - /// - /// The class is used internally by the framework to generate - /// test case data and is also exposed for use by users through + /// + /// The class is used internally by the framework to generate + /// test case data and is also exposed for use by users through /// the TestContext.Random property. /// /// - /// For consistency with the underlying Random Type, methods + /// For consistency with the underlying Random Type, methods /// returning a single value use the prefix "Next..." Those /// without an argument return a non-negative value up to /// the full positive range of the Type. Overloads are provided /// for specifying a maximum or a range. Methods that return - /// arrays or strings use the prefix "Get..." to avoid + /// arrays or strings use the prefix "Get..." to avoid /// confusion with the single-value methods. /// public class Randomizer : Random @@ -61,7 +63,7 @@ static Randomizer() // Static Random instance used exclusively for the generation // of seed values for new Randomizers. - private static Random _seedGenerator; + private static Random _seedGenerator = null!; // Initialized by the InitialSeed setter called by the static constructor. /// /// Initial seed used to create randomizers for this run @@ -184,10 +186,10 @@ public uint NextUInt(uint min, uint max) uint raw; do { - raw = RawUInt(); + raw = RawUInt32(); } while (raw > limit); - + return unchecked(raw % range + min); } @@ -287,7 +289,7 @@ public long NextLong(long min, long max) ulong raw; do { - raw = RawULong(); + raw = RawUInt64(); } while (raw > limit); @@ -334,10 +336,10 @@ public ulong NextULong(ulong min, ulong max) ulong raw; do { - raw = RawULong(); + raw = RawUInt64(); } while (raw > limit); - + return unchecked(raw % range + min); } @@ -500,9 +502,9 @@ public T NextEnum() } #endregion - + #region String - + /// /// Default characters for random functions. /// @@ -510,7 +512,7 @@ public T NextEnum() public const string DefaultStringChars = "abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNOPQRSTUVWXYZ0123456789_"; private const int DefaultStringLength = 25; - + /// /// Generate a random string based on the characters from the input string. /// @@ -519,15 +521,12 @@ public T NextEnum() /// A random string of arbitrary length public string GetString(int outputLength, string allowedChars) { + var data = new char[outputLength]; - var sb = new StringBuilder(outputLength); + for (int i = 0; i < data.Length; i++) + data[i] = allowedChars[Next(0, allowedChars.Length)]; - for (int i = 0; i < outputLength ; i++) - { - sb.Append(allowedChars[Next(0,allowedChars.Length)]); - } - - return sb.ToString(); + return new string(data); } /// @@ -563,10 +562,7 @@ public string GetString() /// public decimal NextDecimal() { - int low = Next(0, int.MaxValue); - int mid = Next(0, int.MaxValue); - int high = Next(0, int.MaxValue); - return new Decimal(low, mid, high, false, 0); + return new decimal(RawInt32(), RawInt32(), RawInt32(), false, 0); } /// @@ -574,7 +570,70 @@ public decimal NextDecimal() /// public decimal NextDecimal(decimal max) { - return NextDecimal() % max; + if (max <= 1) + { + Guard.ArgumentInRange(max > 0, "Maximum must be greater than zero.", nameof(max)); + return 0; + } + + unchecked + { + // Flooring sets the exponent (scale) to zero. This is necessary even if the number is already an + // integer so that the low, mid and high parts of the mantissa get shifted by the appropriate power of + // ten until they only contain the integral part of the number. + // May as well set it back to the parameter so that the `result < max` lower down is doing less work. + max = decimal.Floor(max); + + var parts = DecimalParts.FromValue(max); + + // If scale is not zero, that means that low, mid, and high are shifted to make room for digits from the + // fractional part of the number. We relied on Decimal.Floor to prevent this. + Guard.OperationValid(parts.Scale == 0, "Decimal.Floor returned a value whose scale was not 0."); + + while (true) + { + // Fill all 96 bits with uniformly-distributed randomness except for the bits that must be zero in + // order to stay under the exclusive maximum. + int low, mid, high; + + if (parts.High != 0) + { + var inclusiveMaximum = parts.High - 1; + + low = RawInt32(); + mid = RawInt32(); + high = RawInt32() & (int)MaskToRemoveBitsGuaranteedToExceedMaximum(inclusiveMaximum); + } + else if (parts.Mid != 0) + { + var inclusiveMaximum = parts.Mid - 1; + + low = RawInt32(); + mid = RawInt32() & (int)MaskToRemoveBitsGuaranteedToExceedMaximum(inclusiveMaximum); + high = 0; + } + else + { + var inclusiveMaximum = parts.Low - 1; + + low = RawInt32() & (int)MaskToRemoveBitsGuaranteedToExceedMaximum(inclusiveMaximum); + mid = 0; + high = 0; + } + + var result = new decimal(low, mid, high, false, 0); + + // By masking out the random bits which would put the result at or over the exclusive maximum, the + // chance of having to loop and try again is strictly less than 50% in the worst-case scenario. The + // best-case scenario is 0% chance, and an average is 25% chance per iteration. + + // For the worst-case scenario of 50% chance per iteration, the total chance of having to iterate + // twice is < 25%. Three times, 12.5%. And so on. + // For the average scenario of 25% chance per iteration, the total chance of having to iterate twice + // is < 6.25%. Three times, 1.5625%. And so on. + if (result < max) return result; + } + } } /// @@ -589,7 +648,7 @@ public decimal NextDecimal(decimal min, decimal max) { Guard.ArgumentInRange(max >= min, "Maximum value must be greater than or equal to minimum.", nameof(max)); - // Check that the range is not greater than MaxValue without + // Check that the range is not greater than MaxValue without // first calculating it, since this would cause overflow Guard.ArgumentValid(max < 0M == min < 0M || min + decimal.MaxValue >= max, "Range too great for decimal data, use double range", nameof(max)); @@ -597,21 +656,24 @@ public decimal NextDecimal(decimal min, decimal max) if (min == max) return min; - decimal range = max - min; + return NextDecimal(max - min) + min; + } - // Avoid introduction of modulo bias - decimal limit = decimal.MaxValue - decimal.MaxValue % range; - decimal raw; - do - { - raw = NextDecimal(); - } - while (raw > limit); + private static uint MaskToRemoveBitsGuaranteedToExceedMaximum(uint maximum) + { + // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 but + // without the value-- and value++ - return unchecked(raw % range + min); + var value = maximum; + value |= value >> 1; + value |= value >> 2; + value |= value >> 4; + value |= value >> 8; + value |= value >> 16; + return value; } - #endregion + #endregion #region Guid @@ -634,44 +696,25 @@ public Guid NextGuid() #region Helper Methods - private uint RawUInt() + private int RawInt32() { - var buffer = new byte[sizeof(uint)]; - NextBytes(buffer); - return BitConverter.ToUInt32(buffer, 0); + return unchecked((int)RawUInt32()); } - private uint RawUShort() + private uint RawUInt32() { var buffer = new byte[sizeof(uint)]; NextBytes(buffer); return BitConverter.ToUInt32(buffer, 0); } - private ulong RawULong() + private ulong RawUInt64() { var buffer = new byte[sizeof(ulong)]; NextBytes(buffer); return BitConverter.ToUInt64(buffer, 0); } - private long RawLong() - { - var buffer = new byte[sizeof(long)]; - NextBytes(buffer); - return BitConverter.ToInt64(buffer, 0); - } - - private decimal RawDecimal() - { - int low = Next(0, int.MaxValue); - int mid = Next(0, int.MaxValue); - int hi = Next(0, int.MaxValue); - bool isNegative = NextBool(); - byte scale = NextByte(29); - return new Decimal(low, mid, hi, isNegative, scale); - } - #endregion } } diff --git a/src/NUnitFramework/framework/Internal/Reflect.cs b/src/NUnitFramework/framework/Internal/Reflect.cs index f6a6e4ad1f..91abc533f6 100644 --- a/src/NUnitFramework/framework/Internal/Reflect.cs +++ b/src/NUnitFramework/framework/Internal/Reflect.cs @@ -21,11 +21,14 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Reflection; -#if !(NET35 || NETSTANDARD1_4) +#if !NET35 using System.Runtime.ExceptionServices; #endif using NUnit.Compatibility; @@ -58,43 +61,32 @@ public static class Reflect #region Get Methods of a type /// - /// Examine a fixture type and return an array of methods having a - /// particular attribute. The array is order with base methods first. + /// Returns all methods declared by the specified fixture type that have the specified attribute, optionally + /// including base classes. Methods from a base class are always returned before methods from a class that + /// inherits from it. /// - /// The type to examine - /// The attribute Type to look for - /// Specifies whether to search the fixture type inheritance chain - /// The array of methods found + /// The type to examine. + /// Only methods to which this attribute is applied will be returned. + /// Specifies whether to search the fixture type inheritance chain. public static MethodInfo[] GetMethodsWithAttribute(Type fixtureType, Type attributeType, bool inherit) { - List list = new List(); - - var flags = AllMembers | (inherit ? BindingFlags.FlattenHierarchy : BindingFlags.DeclaredOnly); - foreach (MethodInfo method in fixtureType.GetMethods(flags)) + if (!inherit) { - if (method.IsDefined(attributeType, inherit)) - list.Add(method); + return fixtureType + .GetMethods(AllMembers | BindingFlags.DeclaredOnly) + .Where(method => method.IsDefined(attributeType, inherit: false)) + .ToArray(); } - list.Sort(new BaseTypesFirstComparer()); + var methodsByDeclaringType = fixtureType + .GetMethods(AllMembers | BindingFlags.FlattenHierarchy) // FlattenHierarchy is complex to replicate by looping over base types with DeclaredOnly. + .Where(method => method.IsDefined(attributeType, inherit: true)) + .ToLookup(method => method.DeclaringType); - return list.ToArray(); - } - - private class BaseTypesFirstComparer : IComparer - { - public int Compare(MethodInfo m1, MethodInfo m2) - { - if (m1 == null || m2 == null) return 0; - - Type m1Type = m1.DeclaringType; - Type m2Type = m2.DeclaringType; - - if (m1Type == m2Type) return 0; - if (m1Type.IsAssignableFrom(m2Type)) return -1; - - return 1; - } + return fixtureType.TypeAndBaseTypes() + .Reverse() + .SelectMany(declaringType => methodsByDeclaringType[declaringType]) + .ToArray(); } /// @@ -108,20 +100,8 @@ public static bool HasMethodWithAttribute(Type fixtureType, Type attributeType) { foreach (MethodInfo method in fixtureType.GetMethods(AllMembers | BindingFlags.FlattenHierarchy)) { -#if NETSTANDARD1_4 - // For .NET Standard 1.x, MethodInfo.IsDefined resolves to an extension method, - // CustomAttributeExtensions.IsDefined, which delegates to Attribute.IsDefined. - // On .NET Core and .NET Framework, Attribute.IsDefined throws ArgumentException - // for types which aren’t assignable to System.Attribute (such as interface types). - foreach (var attributeData in method.CustomAttributes) - { - if (attributeType.IsAssignableFrom(attributeData.AttributeType)) - return true; - } -#else if (method.IsDefined(attributeType, false)) return true; -#endif } return false; } @@ -150,11 +130,11 @@ public static object Construct(Type type) /// The Type to be constructed /// Arguments to the constructor /// An instance of the Type - public static object Construct(Type type, object[] arguments) + public static object Construct(Type type, object?[]? arguments) { if (arguments == null) return Construct(type); - Type[] argTypes = GetTypeArray(arguments); + Type?[] argTypes = GetTypeArray(arguments); ConstructorInfo ctor = GetConstructors(type, argTypes).FirstOrDefault(); if (ctor == null) throw new InvalidTestFixtureException(type.FullName + " does not have a suitable constructor"); @@ -167,11 +147,11 @@ public static object Construct(Type type, object[] arguments) /// Differs from by returning /// for null elements rather than throwing . /// - internal static Type[] GetTypeArray(object[] objects) + internal static Type?[] GetTypeArray(object?[] objects) { - Type[] types = new Type[objects.Length]; + Type?[] types = new Type?[objects.Length]; int index = 0; - foreach (object o in objects) + foreach (object? o in objects) { types[index++] = o?.GetType(); } @@ -181,7 +161,7 @@ internal static Type[] GetTypeArray(object[] objects) /// /// Gets the constructors to which the specified argument types can be coerced. /// - internal static IEnumerable GetConstructors(Type type, Type[] matchingTypes) + internal static IEnumerable GetConstructors(Type type, Type?[] matchingTypes) { return type .GetConstructors() @@ -191,7 +171,7 @@ internal static IEnumerable GetConstructors(Type type, Type[] m /// /// Determines if the given types can be coerced to match the given parameters. /// - internal static bool ParametersMatch(this ParameterInfo[] pinfos, Type[] ptypes) + internal static bool ParametersMatch(this ParameterInfo[] pinfos, Type?[] ptypes) { if (pinfos.Length != ptypes.Length) return false; @@ -220,14 +200,17 @@ internal static bool ParametersMatch(this ParameterInfo[] pinfos, Type[] ptypes) /// /// Determines whether the current type can be implicitly converted to the specified type. /// - internal static bool CanImplicitlyConvertTo(this Type from, Type to) + internal static bool CanImplicitlyConvertTo(this Type? from, Type to) { if (to.IsAssignableFrom(from)) return true; // Look for the marker that indicates from was null - if (from == null && (to.GetTypeInfo().IsClass || to.FullName.StartsWith("System.Nullable"))) - return true; + if (from == null) + { + // Look for the marker that indicates from was null + return to.GetTypeInfo().IsClass || to.FullName.StartsWith("System.Nullable"); + } if (convertibleValueTypes.ContainsKey(to) && convertibleValueTypes[to].Contains(from)) return true; @@ -246,7 +229,7 @@ internal static bool CanImplicitlyConvertTo(this Type from, Type to) /// /// A MethodInfo for the method to be invoked /// The object on which to invoke the method - public static object InvokeMethod(MethodInfo method, object fixture) + public static object? InvokeMethod(MethodInfo method, object? fixture) { return InvokeMethod(method, fixture, null); } @@ -258,10 +241,10 @@ public static object InvokeMethod(MethodInfo method, object fixture) /// The object on which to invoke the method /// The argument list for the method /// The return value from the invoked method -#if !(NET35 || NETSTANDARD1_4) +#if !NET35 [HandleProcessCorruptedStateExceptions] //put here to handle C++ exceptions. #endif - public static object InvokeMethod(MethodInfo method, object fixture, params object[] args) + public static object? InvokeMethod(MethodInfo method, object? fixture, params object?[]? args) { if (method != null) { @@ -295,27 +278,9 @@ public static object InvokeMethod(MethodInfo method, object fixture, params obje #endregion -#if NETSTANDARD1_4 /// /// - /// Selects the ultimate shadowing property just like would, - /// rather than throwing - /// for properties that shadow properties of a different property type - /// which is what does. - /// - /// - /// If you request both public and nonpublic properties, every public property is preferred - /// over every nonpublic property. It would violate the principle of least surprise for a - /// derived class’s implementation detail to be chosen over the public API for a type. - /// - /// - /// See . - /// See . - /// See . -#else - /// - /// - /// Selects the ultimate shadowing property just like would, + /// Selects the ultimate shadowing property just like dynamic would, /// rather than throwing /// for properties that shadow properties of a different property type /// which is what does. @@ -329,8 +294,7 @@ public static object InvokeMethod(MethodInfo method, object fixture, params obje /// See . /// See . /// See . -#endif - public static PropertyInfo GetUltimateShadowingProperty(Type type, string name, BindingFlags bindingFlags) + public static PropertyInfo? GetUltimateShadowingProperty(Type type, string name, BindingFlags bindingFlags) { Guard.ArgumentNotNull(type, nameof(type)); Guard.ArgumentNotNull(name, nameof(name)); @@ -388,21 +352,13 @@ internal static IEnumerable TypeAndBaseTypes(this Type type) } } -#if NETSTANDARD1_4 - internal static Type GetInterface(this Type type, string name) - { - return type.GetTypeInfo().ImplementedInterfaces - .SingleOrDefault(implementedInterface => implementedInterface.FullName == name); - } -#endif - /// /// Same as GetMethod(, | /// , , , /// ) except that it also chooses only non-generic methods. /// Useful for avoiding the you can have with GetMethod. /// - internal static MethodInfo GetNonGenericPublicInstanceMethod(this Type type, string name, Type[] parameterTypes) + internal static MethodInfo? GetNonGenericPublicInstanceMethod(this Type type, string name, Type[] parameterTypes) { foreach (var currentType in type.TypeAndBaseTypes()) { @@ -428,7 +384,7 @@ internal static MethodInfo GetNonGenericPublicInstanceMethod(this Type type, str return null; } - internal static PropertyInfo GetPublicInstanceProperty(this Type type, string name, Type[] indexParameterTypes) + internal static PropertyInfo? GetPublicInstanceProperty(this Type type, string name, Type[] indexParameterTypes) { for (var currentType = type; currentType != null; currentType = currentType.GetTypeInfo().BaseType) { @@ -453,7 +409,7 @@ internal static PropertyInfo GetPublicInstanceProperty(this Type type, string na return null; } - internal static object InvokeWithTransparentExceptions(this MethodBase methodBase, object instance) + internal static object? InvokeWithTransparentExceptions(this MethodBase methodBase, object? instance) { // If we ever target .NET Core 2.1, we can keep from mucking with the exception stack trace // using BindingFlags.DoNotWrapExceptions rather than try…catch. @@ -471,7 +427,7 @@ internal static object InvokeWithTransparentExceptions(this MethodBase methodBas } } - internal static object DynamicInvokeWithTransparentExceptions(this Delegate @delegate) + internal static object? DynamicInvokeWithTransparentExceptions(this Delegate @delegate) { try { @@ -485,5 +441,51 @@ internal static object DynamicInvokeWithTransparentExceptions(this Delegate @del throw new InvalidOperationException("ExceptionHelper.Rethrow failed to throw an exception."); } } + + internal static bool IsFSharpOption(this Type type, [NotNullWhen(true)] out Type? someType) + { + Guard.ArgumentNotNull(type, nameof(type)); + + if (type.GetTypeInfo().IsGenericType + && type.GetGenericTypeDefinition().FullName == "Microsoft.FSharp.Core.FSharpOption`1") + { + someType = type.GetGenericArguments()[0]; + return true; + } + + someType = null; + return false; + } + + internal static bool IsVoidOrUnit(Type type) + { + Guard.ArgumentNotNull(type, nameof(type)); + + return type == typeof(void) || type.FullName == "Microsoft.FSharp.Core.Unit"; + } + + /// + /// Returns the get accessor for the indexer. + /// + /// Type to reflect on for the indexer. + /// List of indexer types that matches the indexer type order. + /// The Get accessor + public static MethodInfo? GetDefaultIndexer(Type type, Type[] indexerTypes) + { + const BindingFlags bindingFlags = BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance; + + var indexerName = GetIndexerName(type); + var indexer = type.GetProperty(indexerName, bindingFlags, null, null, indexerTypes, null); + + return indexer?.GetGetMethod(true); + } + + private static string GetIndexerName(Type type) + { + const string defaultFrameworkIndexerName = "Item"; + var defaultMemberAttribute = type.GetAttributes(true).FirstOrDefault(); + + return defaultMemberAttribute?.MemberName ?? defaultFrameworkIndexerName; + } } } diff --git a/src/NUnitFramework/framework/Internal/Results/TestCaseResult.cs b/src/NUnitFramework/framework/Internal/Results/TestCaseResult.cs index 5e3ac72b63..6b9f43eef6 100644 --- a/src/NUnitFramework/framework/Internal/Results/TestCaseResult.cs +++ b/src/NUnitFramework/framework/Internal/Results/TestCaseResult.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections.Generic; using NUnit.Framework.Interfaces; @@ -48,6 +50,15 @@ public override int FailCount get { return ResultState.Status == TestStatus.Failed ? 1 : 0; } } + /// + /// Gets the number of test cases that executed + /// when running the test and all its children. + /// + public override int TotalCount + { + get { return 1; } + } + /// /// Gets the number of test cases that had warnings /// when running the test and all its children. diff --git a/src/NUnitFramework/framework/Internal/Results/TestResult.cs b/src/NUnitFramework/framework/Internal/Results/TestResult.cs index 2ea0bcfc0a..8b0e06f449 100644 --- a/src/NUnitFramework/framework/Internal/Results/TestResult.cs +++ b/src/NUnitFramework/framework/Internal/Results/TestResult.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -57,6 +59,11 @@ public abstract class TestResult : LongLivedMarshalByRefObject, ITestResult /// internal static readonly string CHILD_IGNORE_MESSAGE = "One or more child tests were ignored"; + /// + /// Error message for when user has cancelled the test run + /// + internal const string USER_CANCELLED_MESSAGE = "Test run cancelled by user"; + /// /// The minimum duration for tests /// @@ -73,18 +80,16 @@ public abstract class TestResult : LongLivedMarshalByRefObject, ITestResult protected int InternalAssertCount; private ResultState _resultState; - private string _message; - private string _stackTrace; + private string? _message; + private string? _stackTrace; private readonly List _assertionResults = new List(); private readonly List _testAttachments = new List(); -#if PARALLEL /// /// ReaderWriterLock /// protected ReaderWriterLockSlim RwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion); -#endif #endregion @@ -97,13 +102,9 @@ public abstract class TestResult : LongLivedMarshalByRefObject, ITestResult public TestResult(ITest test) { Test = test; - ResultState = ResultState.Inconclusive; + _resultState = ResultState.Inconclusive; -#if !PARALLEL - OutWriter = new StringWriter(_output); -#else OutWriter = TextWriter.Synchronized(new StringWriter(_output)); -#endif } #endregion @@ -123,18 +124,14 @@ public ResultState ResultState { get { -#if PARALLEL RwLock.EnterReadLock(); -#endif try { return _resultState; } finally { -#if PARALLEL RwLock.ExitReadLock(); -#endif } } private set { _resultState = value; } @@ -193,22 +190,18 @@ internal void AddTestAttachment(TestAttachment attachment) /// Gets the message associated with a test /// failure or with not running the test /// - public string Message + public string? Message { get { -#if PARALLEL RwLock.EnterReadLock(); -#endif try { return _message; } finally { -#if PARALLEL RwLock.ExitReadLock(); -#endif } } @@ -222,22 +215,18 @@ private set /// Gets any stack trace associated with an /// error or failure. /// - public virtual string StackTrace + public virtual string? StackTrace { get { -#if PARALLEL RwLock.EnterReadLock(); -#endif try { return _stackTrace; } finally { -#if PARALLEL RwLock.ExitReadLock(); -#endif } } @@ -255,18 +244,14 @@ public int AssertCount { get { -#if PARALLEL RwLock.EnterReadLock(); -#endif try { return InternalAssertCount; } finally { -#if PARALLEL - RwLock.ExitReadLock (); -#endif + RwLock.ExitReadLock(); } } @@ -276,6 +261,12 @@ internal set } } + /// + /// Gets the number of test cases executed + /// when running the test and all its children. + /// + public abstract int TotalCount { get; } + /// /// Gets the number of test cases that failed /// when running the test and all its children. @@ -328,14 +319,10 @@ public string Output { get { -#if PARALLEL lock (OutWriter) { return _output.ToString(); } -#else - return _output.ToString(); -#endif } } @@ -379,13 +366,13 @@ public virtual TNode AddToXml(TNode parentNode, bool recursive) if (ResultState.Site != FailureSite.Test) thisNode.AddAttribute("site", ResultState.Site.ToString()); - thisNode.AddAttribute("start-time", StartTime.ToString("u")); - thisNode.AddAttribute("end-time", EndTime.ToString("u")); + thisNode.AddAttribute("start-time", StartTime.ToString("o")); + thisNode.AddAttribute("end-time", EndTime.ToString("o")); thisNode.AddAttribute("duration", Duration.ToString("0.000000", NumberFormatInfo.InvariantInfo)); if (Test is TestSuite) { - thisNode.AddAttribute("total", (PassCount + FailCount + SkipCount + InconclusiveCount).ToString()); + thisNode.AddAttribute("total", TotalCount.ToString()); thisNode.AddAttribute("passed", PassCount.ToString()); thisNode.AddAttribute("failed", FailCount.ToString()); thisNode.AddAttribute("warnings", WarningCount.ToString()); @@ -405,7 +392,10 @@ public virtual TNode AddToXml(TNode parentNode, bool recursive) case TestStatus.Inconclusive: case TestStatus.Warning: if (Message != null && Message.Trim().Length > 0) - AddReasonElement(thisNode); + { + TNode reasonNode = thisNode.AddElement("reason"); + reasonNode.AddElementWithCDATA("message", Message); + } break; } @@ -419,7 +409,7 @@ public virtual TNode AddToXml(TNode parentNode, bool recursive) AddAttachmentsElement(thisNode); if (recursive && HasChildren) - foreach (TestResult child in Children) + foreach (var child in Children) child.AddToXml(thisNode, recursive); return thisNode; @@ -463,7 +453,7 @@ public void SetResult(ResultState resultState) /// /// The ResultState to use in the result /// A message associated with the result state - public void SetResult(ResultState resultState, string message) + public void SetResult(ResultState resultState, string? message) { SetResult(resultState, message, null); } @@ -474,11 +464,9 @@ public void SetResult(ResultState resultState, string message) /// The ResultState to use in the result /// A message associated with the result state /// Stack trace giving the location of the command - public void SetResult(ResultState resultState, string message, string stackTrace) + public void SetResult(ResultState resultState, string? message, string? stackTrace) { -#if PARALLEL RwLock.EnterWriteLock(); -#endif try { ResultState = resultState; @@ -487,9 +475,7 @@ public void SetResult(ResultState resultState, string message, string stackTrace } finally { -#if PARALLEL RwLock.ExitWriteLock(); -#endif } } @@ -506,7 +492,7 @@ public void RecordException(Exception ex) if (AssertionResults.Count > 0 && result.ResultState == ResultState.Error) { // Add pending failures to the legacy result message - Message += CreateLegacyFailureMessage(); + Message += Environment.NewLine + Environment.NewLine + CreateLegacyFailureMessage(); // Add to the list of assertion errors, so that newer runners will see it AssertionResults.Add(new AssertionResult(AssertionStatus.Error, result.Message, result.StackTrace)); @@ -571,27 +557,24 @@ private struct ExceptionResult { public ResultState ResultState { get; } public string Message { get; } - public string StackTrace { get; } - public Exception Exception { get; } + public string StackTrace { get; } public ExceptionResult(Exception ex, FailureSite site) { - ex = ValidateAndUnwrap(ex); - - Exception = ex; + ex = ValidateAndUnwrap(ex); if (ex is ResultStateException) { ResultState = ((ResultStateException)ex).ResultState.WithSite(site); - Message = ex.Message; - StackTrace = StackFilter.DefaultFilter.Filter(ex.StackTrace); + Message = ex.GetMessageWithoutThrowing(); + StackTrace = StackFilter.DefaultFilter.Filter(ex.GetStackTraceWithoutThrowing()); } #if THREAD_ABORT else if (ex is ThreadAbortException) { ResultState = ResultState.Cancelled.WithSite(site); Message = "Test cancelled by user"; - StackTrace = ex.StackTrace; + StackTrace = ex.GetStackTraceWithoutThrowing(); } #endif else @@ -639,7 +622,7 @@ public void RecordAssertion(AssertionResult assertion) /// /// Record an assertion result /// - public void RecordAssertion(AssertionStatus status, string message, string stackTrace) + public void RecordAssertion(AssertionStatus status, string? message, string? stackTrace) { RecordAssertion(new AssertionResult(status, message, stackTrace)); } @@ -647,7 +630,7 @@ public void RecordAssertion(AssertionStatus status, string message, string stack /// /// Record an assertion result /// - public void RecordAssertion(AssertionStatus status, string message) + public void RecordAssertion(AssertionStatus status, string? message) { RecordAssertion(status, message, null); } @@ -677,17 +660,6 @@ private string CreateLegacyFailureMessage() #region Helper Methods - /// - /// Adds a reason element to a node and returns it. - /// - /// The target node. - /// The new reason element. - private TNode AddReasonElement(TNode targetNode) - { - TNode reasonNode = targetNode.AddElement("reason"); - return reasonNode.AddElementWithCDATA("message", Message); - } - /// /// Adds a failure element to a node and returns it. /// diff --git a/src/NUnitFramework/framework/Internal/Results/TestSuiteResult.cs b/src/NUnitFramework/framework/Internal/Results/TestSuiteResult.cs index 4b238457c7..441c28d200 100644 --- a/src/NUnitFramework/framework/Internal/Results/TestSuiteResult.cs +++ b/src/NUnitFramework/framework/Internal/Results/TestSuiteResult.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,11 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; -#if PARALLEL using System.Collections.Concurrent; -#endif using NUnit.Framework.Interfaces; using System.Threading; @@ -41,11 +41,8 @@ public class TestSuiteResult : TestResult private int _warningCount = 0; private int _skipCount = 0; private int _inconclusiveCount = 0; -#if PARALLEL + private int _totalCount = 0; private readonly ConcurrentQueue _children = new ConcurrentQueue(); -#else - private readonly List _children = new List(); -#endif /// /// Construct a TestSuiteResult base on a TestSuite @@ -55,7 +52,29 @@ public TestSuiteResult(TestSuite suite) : base(suite) { } -#region Overrides + #region Overrides + + + /// + /// Gets the number of test cases that executed + /// when running the test and all its children. + /// + public override int TotalCount + { + get + { + RwLock.EnterReadLock(); + try + { + return _totalCount; + } + finally + { + RwLock.ExitReadLock(); + } + } + } + /// /// Gets the number of test cases that failed @@ -65,18 +84,14 @@ public override int FailCount { get { -#if PARALLEL RwLock.EnterReadLock(); -#endif try { return _failCount; } finally { -#if PARALLEL RwLock.ExitReadLock(); -#endif } } } @@ -89,18 +104,14 @@ public override int PassCount { get { -#if PARALLEL RwLock.EnterReadLock(); -#endif try { return _passCount; } finally { -#if PARALLEL RwLock.ExitReadLock(); -#endif } } } @@ -113,18 +124,14 @@ public override int WarningCount { get { -#if PARALLEL RwLock.EnterReadLock(); -#endif try { return _warningCount; } finally { -#if PARALLEL RwLock.ExitReadLock(); -#endif } } } @@ -137,20 +144,16 @@ public override int SkipCount { get { -#if PARALLEL RwLock.EnterReadLock(); -#endif try { return _skipCount; } finally { -#if PARALLEL RwLock.ExitReadLock(); -#endif } - } + } } /// @@ -161,18 +164,14 @@ public override int InconclusiveCount { get { -#if PARALLEL RwLock.EnterReadLock(); -#endif try { return _inconclusiveCount; } finally { -#if PARALLEL RwLock.ExitReadLock(); -#endif } } } @@ -184,11 +183,7 @@ public override bool HasChildren { get { -#if PARALLEL return !_children.IsEmpty; -#else - return _children.Count != 0; -#endif } } @@ -211,7 +206,6 @@ public override IEnumerable Children /// The result to be added public virtual void AddResult(ITestResult result) { -#if PARALLEL _children.Enqueue(result); RwLock.EnterWriteLock(); try @@ -222,10 +216,6 @@ public virtual void AddResult(ITestResult result) { RwLock.ExitWriteLock(); } -#else - _children.Add(result); - MergeChildResult(result); -#endif } private void MergeChildResult(ITestResult childResult) @@ -240,6 +230,7 @@ private void MergeChildResult(ITestResult childResult) _warningCount += childResult.WarningCount; _skipCount += childResult.SkipCount; _inconclusiveCount += childResult.InconclusiveCount; + _totalCount += childResult.PassCount + childResult.FailCount + childResult.SkipCount + childResult.InconclusiveCount + childResult.WarningCount; } private void UpdateResultState(ResultState childResultState) @@ -257,7 +248,9 @@ private void UpdateResultState(ResultState childResultState) break; case TestStatus.Failed: - if (ResultState.Status != TestStatus.Failed) + if (childResultState.Label == "Cancelled") + SetResult(ResultState.Cancelled, USER_CANCELLED_MESSAGE); + else if (ResultState.Status != TestStatus.Failed) SetResult(ResultState.ChildFailure, CHILD_ERRORS_MESSAGE); break; diff --git a/src/NUnitFramework/framework/Internal/RuntimeFramework.cs b/src/NUnitFramework/framework/Internal/RuntimeFramework.cs index a4f2adec6b..cee5ef1203 100644 --- a/src/NUnitFramework/framework/Internal/RuntimeFramework.cs +++ b/src/NUnitFramework/framework/Internal/RuntimeFramework.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,10 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PLATFORM_DETECTION using System; -using System.Collections.Generic; -using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; @@ -36,26 +33,6 @@ namespace NUnit.Framework.Internal { - /// - /// Enumeration identifying a common language - /// runtime implementation. - /// - public enum RuntimeType - { - /// Any supported runtime framework - Any, - /// Microsoft .NET Framework - Net, - /// Microsoft Shared Source CLI - SSCLI, - /// Mono - Mono, - /// MonoTouch - MonoTouch, - /// Microsoft .NET Core - NetCore - } - /// /// RuntimeFramework represents a particular version /// of a common language runtime implementation. @@ -101,7 +78,7 @@ public sealed class RuntimeFramework ? RuntimeType.Mono : isNetCore ? RuntimeType.NetCore - : RuntimeType.Net; + : RuntimeType.NetFramework; int major = Environment.Version.Major; int minor = Environment.Version.Minor; @@ -192,7 +169,7 @@ private void InitFromFrameworkVersion(Version version) ClrVersion = new Version(4, 0, 30319); break; - case RuntimeType.Net: + case RuntimeType.NetFramework: case RuntimeType.Mono: case RuntimeType.Any: switch (version.Major) @@ -241,7 +218,7 @@ private void InitFromClrVersion(Version version) ClrVersion = version; if (Runtime == RuntimeType.Mono && version.Major == 1) FrameworkVersion = new Version(1, 0); - if (Runtime == RuntimeType.Net && version.Major == 4 && version.Minor == 5) + if (Runtime == RuntimeType.NetFramework && version.Major == 4 && version.Minor == 5) ClrVersion = new Version(4, 0, 30319); if (Runtime == RuntimeType.NetCore) ClrVersion = new Version(4, 0, 30319); @@ -314,9 +291,14 @@ public static RuntimeFramework Parse(string s) runtime = (RuntimeType)Enum.Parse(typeof(RuntimeType), parts[0], true); string vstring = parts[1]; if (vstring != "") + { version = new Version(vstring); + + if (runtime == RuntimeType.NetFramework && version.Major >= 5) + runtime = RuntimeType.NetCore; + } } - else if (char.ToLower(s[0]) == 'v') + else if (s.StartsWith("v", StringComparison.OrdinalIgnoreCase)) { version = new Version(s.Substring(1)); } @@ -340,7 +322,7 @@ public override string ToString() { if (AllowAnyVersion) { - return Runtime.ToString().ToLower(); + return GetShortName(Runtime, FrameworkVersion).ToLowerInvariant(); } else { @@ -348,7 +330,7 @@ public override string ToString() if (Runtime == RuntimeType.Any) return "v" + vstring; else - return Runtime.ToString().ToLower() + "-" + vstring; + return GetShortName(Runtime, FrameworkVersion).ToLowerInvariant() + "-" + vstring; } } @@ -386,8 +368,10 @@ public bool Supports(RuntimeFramework target) private static bool IsNetCore() { + if (Environment.Version.Major >= 5) return true; + #if NETSTANDARD2_0 - // Mono versions will throw a TypeLoadException when attempting to run the internal method, so we wrap it in a try/catch + // Mono versions will throw a TypeLoadException when attempting to run the internal method, so we wrap it in a try/catch // block to stop any inlining in release builds and check whether the type exists Type runtimeInfoType = Type.GetType("System.Runtime.InteropServices.RuntimeInformation,System.Runtime.InteropServices.RuntimeInformation", false); if (runtimeInfoType != null) @@ -403,33 +387,41 @@ private static bool IsNetCore() return false; } +#if NETSTANDARD2_0 [MethodImpl(MethodImplOptions.NoInlining)] private static bool IsNetCore_Internal() { -#if NETSTANDARD2_0 // Mono versions will throw a TypeLoadException when attempting to run any method that uses RuntimeInformation // so we wrap it in a try/catch block in IsNetCore to catch it in case it ever gets this far if (System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription.StartsWith(".NET Core", StringComparison.OrdinalIgnoreCase)) { return true; } -#endif + return false; } +#endif private static bool IsRuntimeTypeName(string name) { - return Enum.GetNames( typeof(RuntimeType)).Any( item => item.ToLower() == name.ToLower() ); + return Enum.GetNames(typeof(RuntimeType)).Contains(name, StringComparer.OrdinalIgnoreCase); + } + + private static string GetShortName(RuntimeType runtime, Version version) + { + return runtime == RuntimeType.NetFramework || (runtime == RuntimeType.NetCore && version.Major >= 5) + ? "Net" + : runtime.ToString(); } private static string GetDefaultDisplayName(RuntimeType runtime, Version version) { if (version == DefaultVersion) - return runtime.ToString(); + return GetShortName(runtime, version); else if (runtime == RuntimeType.Any) return "v" + version; else - return runtime + " " + version; + return GetShortName(runtime, version) + " " + version; } private static bool VersionsMatch(Version v1, Version v2) @@ -443,4 +435,3 @@ private static bool VersionsMatch(Version v1, Version v2) #endregion } } -#endif diff --git a/src/NUnitFramework/framework/Internal/RuntimeType.cs b/src/NUnitFramework/framework/Internal/RuntimeType.cs new file mode 100644 index 0000000000..fb280841e3 --- /dev/null +++ b/src/NUnitFramework/framework/Internal/RuntimeType.cs @@ -0,0 +1,50 @@ +// *********************************************************************** +// Copyright (c) 2007-2016 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; + +namespace NUnit.Framework.Internal +{ + /// + /// Enumeration identifying a common language + /// runtime implementation. + /// + public enum RuntimeType + { + /// Any supported runtime framework + Any, + /// Microsoft .NET Framework + NetFramework, + /// Microsoft .NET Framework + [Obsolete("Use NetFramework instead. 'Net' is ambiguous now that the .NET Core runtime has been renamed .NET.", error: true)] + Net = NetFramework, + /// Microsoft Shared Source CLI + SSCLI, + /// Mono + Mono, + /// MonoTouch + MonoTouch, + /// Microsoft .NET Core, including .NET 5+ + NetCore + } +} diff --git a/src/NUnitFramework/framework/Internal/SandboxedThreadState.cs b/src/NUnitFramework/framework/Internal/SandboxedThreadState.cs index 83b4129882..b4435dab1f 100644 --- a/src/NUnitFramework/framework/Internal/SandboxedThreadState.cs +++ b/src/NUnitFramework/framework/Internal/SandboxedThreadState.cs @@ -34,24 +34,23 @@ internal sealed class SandboxedThreadState { public CultureInfo Culture { get; } public CultureInfo UICulture { get; } -#if !NETSTANDARD1_4 + + /// + /// Thread principal. + /// This will be null on platforms that don't support . + /// public System.Security.Principal.IPrincipal Principal { get; } -#endif private readonly SynchronizationContext _synchronizationContext; private SandboxedThreadState( CultureInfo culture, CultureInfo uiCulture, -#if !NETSTANDARD1_4 System.Security.Principal.IPrincipal principal, -#endif SynchronizationContext synchronizationContext) { Culture = culture; UICulture = uiCulture; -#if !NETSTANDARD1_4 Principal = principal; -#endif _synchronizationContext = synchronizationContext; } @@ -63,9 +62,7 @@ public static SandboxedThreadState Capture() return new SandboxedThreadState( CultureInfo.CurrentCulture, CultureInfo.CurrentUICulture, -#if !NETSTANDARD1_4 - Thread.CurrentPrincipal, -#endif + ThreadUtility.GetCurrentThreadPrincipal(), SynchronizationContext.Current); } @@ -75,14 +72,9 @@ public static SandboxedThreadState Capture() [SecurityCritical] public void Restore() { -#if NETSTANDARD1_4 - CultureInfo.CurrentCulture = Culture; - CultureInfo.CurrentUICulture = UICulture; -#else Thread.CurrentThread.CurrentCulture = Culture; Thread.CurrentThread.CurrentUICulture = UICulture; - Thread.CurrentPrincipal = Principal; -#endif + ThreadUtility.SetCurrentThreadPrincipal(Principal); SynchronizationContext.SetSynchronizationContext(_synchronizationContext); } @@ -95,9 +87,7 @@ public SandboxedThreadState WithCulture(CultureInfo culture) return new SandboxedThreadState( culture, UICulture, -#if !NETSTANDARD1_4 Principal, -#endif _synchronizationContext); } @@ -109,13 +99,10 @@ public SandboxedThreadState WithUICulture(CultureInfo uiCulture) return new SandboxedThreadState( Culture, uiCulture, -#if !NETSTANDARD1_4 Principal, -#endif _synchronizationContext); } -#if !NETSTANDARD1_4 /// /// Returns a copy with the specified principal. /// @@ -127,6 +114,5 @@ public SandboxedThreadState WithPrincipal(System.Security.Principal.IPrincipal p principal, _synchronizationContext); } -#endif } } diff --git a/src/NUnitFramework/framework/Internal/SingleThreadedSynchronizationContext.cs b/src/NUnitFramework/framework/Internal/SingleThreadedSynchronizationContext.cs index 9eab82da00..c6b817dff9 100644 --- a/src/NUnitFramework/framework/Internal/SingleThreadedSynchronizationContext.cs +++ b/src/NUnitFramework/framework/Internal/SingleThreadedSynchronizationContext.cs @@ -142,8 +142,7 @@ public void Run() _status = Status.Running; } - ScheduledWork scheduledWork; - while (TryTake(out scheduledWork)) + while (TryTake(out var scheduledWork)) scheduledWork.Execute(); } diff --git a/src/NUnitFramework/framework/Internal/StringUtil.cs b/src/NUnitFramework/framework/Internal/StringUtil.cs index f807a0732d..94f106c81e 100644 --- a/src/NUnitFramework/framework/Internal/StringUtil.cs +++ b/src/NUnitFramework/framework/Internal/StringUtil.cs @@ -36,7 +36,7 @@ public class StringUtil /// /// The first string. /// The second string.. - /// if set to true, the case of the letters in the strings is ignored. + /// if set to , the case of the letters in the strings is ignored. /// Zero if the strings are equivalent, a negative number if strA is sorted first, a positive number if /// strB is sorted first public static int Compare(string strA, string strB, bool ignoreCase) @@ -50,7 +50,7 @@ public static int Compare(string strA, string strB, bool ignoreCase) /// /// The first string. /// The second string.. - /// if set to true, the case of the letters in the strings is ignored. + /// if set to , the case of the letters in the strings is ignored. /// True if the strings are equivalent, false if not. public static bool StringsEqual(string strA, string strB, bool ignoreCase) { diff --git a/src/NUnitFramework/framework/Internal/TestCaseParameters.cs b/src/NUnitFramework/framework/Internal/TestCaseParameters.cs index f123645997..3957dc729a 100644 --- a/src/NUnitFramework/framework/Internal/TestCaseParameters.cs +++ b/src/NUnitFramework/framework/Internal/TestCaseParameters.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; @@ -38,7 +40,7 @@ public class TestCaseParameters : TestParameters, ITestCaseData, IApplyToTest /// /// The expected result to be returned /// - private object _expectedResult; + private object? _expectedResult; #endregion @@ -59,7 +61,7 @@ public class TestCaseParameters : TestParameters, ITestCaseData, IApplyToTest /// Construct a parameter set with a list of arguments /// /// - public TestCaseParameters(object[] args) : base(args) { } + public TestCaseParameters(object?[] args) : base(args) { } /// /// Construct a ParameterSet from an object implementing ITestCaseData @@ -79,7 +81,7 @@ public TestCaseParameters(ITestCaseData data) : base(data) /// The expected result of the test, which /// must match the method return type. /// - public object ExpectedResult + public object? ExpectedResult { get { return _expectedResult; } set diff --git a/src/NUnitFramework/framework/Internal/TestCaseTimeoutException.cs b/src/NUnitFramework/framework/Internal/TestCaseTimeoutException.cs index a127e9e9a3..2763e56bb6 100644 --- a/src/NUnitFramework/framework/Internal/TestCaseTimeoutException.cs +++ b/src/NUnitFramework/framework/Internal/TestCaseTimeoutException.cs @@ -24,9 +24,7 @@ namespace NUnit.Framework.Internal { using System; -#if SERIALIZATION using System.Runtime.Serialization; -#endif /// /// TestCaseTimeoutException is thrown when a test running directly @@ -55,12 +53,10 @@ public TestCaseTimeoutException(string message) : base(message) public TestCaseTimeoutException(string message, Exception inner) : base(message, inner) { } -#if SERIALIZATION /// /// Serialization Constructor /// protected TestCaseTimeoutException(SerializationInfo info, StreamingContext context) : base(info,context){} -#endif } } diff --git a/src/NUnitFramework/framework/Internal/TestExecutionContext.cs b/src/NUnitFramework/framework/Internal/TestExecutionContext.cs index f37dfc4a66..29a428ee2c 100644 --- a/src/NUnitFramework/framework/Internal/TestExecutionContext.cs +++ b/src/NUnitFramework/framework/Internal/TestExecutionContext.cs @@ -27,16 +27,13 @@ using System.IO; using System.Reflection; using System.Security; +using System.Security.Principal; using System.Threading; using NUnit.Compatibility; using NUnit.Framework.Constraints; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal.Execution; -#if !NETSTANDARD1_4 -using System.Security.Principal; -#endif - #if NET35 || NET40 || NET45 using System.Runtime.Remoting.Messaging; #endif @@ -297,13 +294,11 @@ internal ITestListener Listener /// public Tolerance DefaultFloatingPointTolerance { get; set; } -#if PARALLEL /// /// The worker that spawned the context. /// For builds without the parallel feature, it is null. /// public TestWorker TestWorker {get; internal set;} -#endif /// /// Gets the RandomGenerator specific to this Test @@ -355,11 +350,7 @@ public CultureInfo CurrentCulture set { _sandboxedThreadState = _sandboxedThreadState.WithCulture(value); -#if NETSTANDARD1_4 - CultureInfo.CurrentCulture = value; -#else Thread.CurrentThread.CurrentCulture = value; -#endif } } @@ -372,15 +363,10 @@ public CultureInfo CurrentUICulture set { _sandboxedThreadState = _sandboxedThreadState.WithUICulture(value); -#if NETSTANDARD1_4 - CultureInfo.CurrentUICulture = value; -#else Thread.CurrentThread.CurrentUICulture = value; -#endif } } -#if !NETSTANDARD1_4 /// /// Gets or sets the current for the Thread. /// @@ -390,10 +376,9 @@ public IPrincipal CurrentPrincipal set { _sandboxedThreadState = _sandboxedThreadState.WithPrincipal(value); - Thread.CurrentPrincipal = value; + ThreadUtility.SetCurrentThreadPrincipal(value); } } -#endif /// /// The current head of the ValueFormatter chain, copied from MsgUtils.ValueFormatter @@ -475,9 +460,7 @@ private TestExecutionContext CreateIsolatedContext() if (context.CurrentTest != null) context.CurrentResult = context.CurrentTest.MakeTestResult(); -#if PARALLEL context.TestWorker = TestWorker; -#endif return context; } @@ -496,7 +479,6 @@ public void SendMessage(string destination, string message) #region InitializeLifetimeService -#if !NETSTANDARD1_4 /// /// Obtain lifetime service object /// @@ -506,7 +488,6 @@ public override object InitializeLifetimeService() { return null; } -#endif #endregion @@ -568,7 +549,7 @@ public class AdhocContext : TestExecutionContext public AdhocContext() { var type = GetType(); - var method = type.GetMethod("AdhocTestMethod", BindingFlags.NonPublic | BindingFlags.Instance); + var method = type.GetMethod(nameof(AdhocTestMethod), BindingFlags.NonPublic | BindingFlags.Instance); CurrentTest = new TestMethod(new MethodWrapper(type, method)); CurrentResult = CurrentTest.MakeTestResult(); diff --git a/src/NUnitFramework/framework/Internal/TestFilter.cs b/src/NUnitFramework/framework/Internal/TestFilter.cs index 4cbd7db72c..623da69943 100644 --- a/src/NUnitFramework/framework/Internal/TestFilter.cs +++ b/src/NUnitFramework/framework/Internal/TestFilter.cs @@ -65,6 +65,24 @@ public bool IsEmpty /// True if the test passes the filter, otherwise false public virtual bool Pass(ITest test) { + return Pass(test, false); + } + + /// + /// Determine if a particular test passes the filter criteria. The default + /// implementation checks the test itself, its parents and any descendants. + /// + /// Derived classes may override this method or any of the Match methods + /// to change the behavior of the filter. + /// + /// The test to which the filter is applied + /// If set to we are carrying a negation through + /// True if the test passes the filter, otherwise false + public virtual bool Pass(ITest test, bool negated) + { + if (negated) + return !Match(test) && !MatchParent(test); + return Match(test) || MatchParent(test) || MatchDescendant(test); } @@ -211,7 +229,7 @@ public override bool Match( ITest test ) return true; } - public override bool Pass( ITest test ) + public override bool Pass( ITest test, bool negated ) { return true; } diff --git a/src/NUnitFramework/framework/Internal/TestFixtureParameters.cs b/src/NUnitFramework/framework/Internal/TestFixtureParameters.cs index 9fb31ce853..438b0a435a 100644 --- a/src/NUnitFramework/framework/Internal/TestFixtureParameters.cs +++ b/src/NUnitFramework/framework/Internal/TestFixtureParameters.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; @@ -50,7 +52,7 @@ public class TestFixtureParameters : TestParameters, ITestFixtureData /// Construct a parameter set with a list of arguments /// /// - public TestFixtureParameters(params object[] args) : base(args) { } + public TestFixtureParameters(params object?[] args) : base(args) { } /// /// Construct a ParameterSet from an object implementing ITestCaseData @@ -68,7 +70,7 @@ public TestFixtureParameters(ITestFixtureData data) : base(data) /// /// Type arguments used to create a generic fixture instance /// - public Type[] TypeArgs { get; } + public Type[]? TypeArgs { get; } #endregion } diff --git a/src/NUnitFramework/framework/Internal/TestListener.cs b/src/NUnitFramework/framework/Internal/TestListener.cs index 551e1374dd..0b95e75bf3 100644 --- a/src/NUnitFramework/framework/Internal/TestListener.cs +++ b/src/NUnitFramework/framework/Internal/TestListener.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; diff --git a/src/NUnitFramework/framework/Internal/TestNameGenerator.cs b/src/NUnitFramework/framework/Internal/TestNameGenerator.cs index 31c98fdcf7..81b39849ed 100644 --- a/src/NUnitFramework/framework/Internal/TestNameGenerator.cs +++ b/src/NUnitFramework/framework/Internal/TestNameGenerator.cs @@ -90,7 +90,7 @@ public string GetDisplayName(TestMethod testMethod, object[] args) var result = new StringBuilder(); foreach (var fragment in _fragments) - result.Append(fragment.GetText(testMethod, args)); + fragment.AppendTextTo(result, testMethod, args); return result.ToString(); } @@ -143,6 +143,9 @@ private static List BuildFragmentList(string pattern) case "{a}": fragments.Add(new ArgListFragment(0)); break; + case "{p}": + fragments.Add(new ParamArgListFragment(0)); + break; case "{0}": case "{1}": case "{2}": @@ -158,13 +161,14 @@ private static List BuildFragmentList(string pattern) break; default: char c = token[1]; - if (token.Length >= 5 && token[2] == ':' && (c == 'a' || char.IsDigit(c))) + if (token.Length >= 5 && token[2] == ':' && (c == 'a' || c == 'p' || char.IsDigit(c))) { - int length; - if (int.TryParse(token.Substring(3, token.Length - 4), out length) && length > 0) + if (int.TryParse(token.Substring(3, token.Length - 4), out var length) && length > 0) { if (c == 'a') fragments.Add(new ArgListFragment(length)); + else if (c == 'p') + fragments.Add(new ParamArgListFragment(length)); else // It's a digit fragments.Add(new ArgumentFragment(c - '0', length)); break; @@ -195,12 +199,12 @@ private abstract class NameFragment { private const string THREE_DOTS = "..."; - public virtual string GetText(TestMethod testMethod, object[] args) + public virtual void AppendTextTo(StringBuilder sb, TestMethod testMethod, object[] args) { - return GetText(testMethod.Method.MethodInfo, args); + AppendTextTo(sb, testMethod.Method.MethodInfo, args); } - public abstract string GetText(MethodInfo method, object[] args); + public abstract void AppendTextTo(StringBuilder sb, MethodInfo method, object[] args); protected static void AppendGenericTypeNames(StringBuilder sb, MethodInfo method) { @@ -260,19 +264,17 @@ protected static string GetDisplayString(object arg, int stringMax) display = builder.ToString(); } } - else if (arg is double) + else if (arg is double dbl) { - double d = (double)arg; - - if (double.IsNaN(d)) + if (double.IsNaN(dbl)) display = "double.NaN"; - else if (double.IsPositiveInfinity(d)) + else if (double.IsPositiveInfinity(dbl)) display = "double.PositiveInfinity"; - else if (double.IsNegativeInfinity(d)) + else if (double.IsNegativeInfinity(dbl)) display = "double.NegativeInfinity"; - else if (d == double.MaxValue) + else if (dbl == double.MaxValue) display = "double.MaxValue"; - else if (d == double.MinValue) + else if (dbl == double.MinValue) display = "double.MinValue"; else { @@ -281,10 +283,8 @@ protected static string GetDisplayString(object arg, int stringMax) display += "d"; } } - else if (arg is float) + else if (arg is float f) { - float f = (float)arg; - if (float.IsNaN(f)) display = "float.NaN"; else if (float.IsPositiveInfinity(f)) @@ -302,28 +302,26 @@ protected static string GetDisplayString(object arg, int stringMax) display += "f"; } } - else if (arg is decimal) + else if (arg is decimal dec) { - decimal d = (decimal)arg; - if (d == decimal.MinValue) + if (dec == decimal.MinValue) display = "decimal.MinValue"; - else if (d == decimal.MaxValue) + else if (dec == decimal.MaxValue) display = "decimal.MaxValue"; else display += "m"; } - else if (arg is long) + else if (arg is long l) { - if (arg.Equals(long.MinValue)) + if (l.Equals(long.MinValue)) display = "long.MinValue"; - else if (arg.Equals(long.MaxValue)) + else if (l.Equals(long.MaxValue)) display = "long.MaxValue"; else display += "L"; } - else if (arg is ulong) + else if (arg is ulong ul) { - ulong ul = (ulong)arg; if (ul == ulong.MinValue) display = "ulong.MinValue"; else if (ul == ulong.MaxValue) @@ -331,9 +329,8 @@ protected static string GetDisplayString(object arg, int stringMax) else display += "UL"; } - else if (arg is string) + else if (arg is string str) { - var str = (string)arg; bool tooLong = stringMax > 0 && str.Length > stringMax; int limit = tooLong ? stringMax - THREE_DOTS.Length : 0; @@ -351,50 +348,50 @@ protected static string GetDisplayString(object arg, int stringMax) sb.Append("\""); display = sb.ToString(); } - else if (arg is char) + else if (arg is char c) { - display = "\'" + EscapeSingleChar((char)arg) + "\'"; + display = "\'" + EscapeSingleChar(c) + "\'"; } - else if (arg is int) + else if (arg is int i) { - if (arg.Equals(int.MaxValue)) + if (i.Equals(int.MaxValue)) display = "int.MaxValue"; - else if (arg.Equals(int.MinValue)) + else if (i.Equals(int.MinValue)) display = "int.MinValue"; } - else if (arg is uint) + else if (arg is uint ui) { - if (arg.Equals(uint.MaxValue)) + if (ui.Equals(uint.MaxValue)) display = "uint.MaxValue"; - else if (arg.Equals(uint.MinValue)) + else if (ui.Equals(uint.MinValue)) display = "uint.MinValue"; } - else if (arg is short) + else if (arg is short s) { - if (arg.Equals(short.MaxValue)) + if (s.Equals(short.MaxValue)) display = "short.MaxValue"; - else if (arg.Equals(short.MinValue)) + else if (s.Equals(short.MinValue)) display = "short.MinValue"; } - else if (arg is ushort) + else if (arg is ushort us) { - if (arg.Equals(ushort.MaxValue)) + if (us.Equals(ushort.MaxValue)) display = "ushort.MaxValue"; - else if (arg.Equals(ushort.MinValue)) + else if (us.Equals(ushort.MinValue)) display = "ushort.MinValue"; } - else if (arg is byte) + else if (arg is byte b) { - if (arg.Equals(byte.MaxValue)) + if (b.Equals(byte.MaxValue)) display = "byte.MaxValue"; - else if (arg.Equals(byte.MinValue)) + else if (b.Equals(byte.MinValue)) display = "byte.MinValue"; } - else if (arg is sbyte) + else if (arg is sbyte sb) { - if (arg.Equals(sbyte.MaxValue)) + if (sb.Equals(sbyte.MaxValue)) display = "sbyte.MaxValue"; - else if (arg.Equals(sbyte.MinValue)) + else if (sb.Equals(sbyte.MinValue)) display = "sbyte.MinValue"; } @@ -451,20 +448,20 @@ private static string EscapeControlChar(char c) } } - private class TestIDFragment : NameFragment + private sealed class TestIDFragment : NameFragment { - public override string GetText(MethodInfo method, object[] args) + public override void AppendTextTo(StringBuilder sb, MethodInfo method, object[] args) { - return "{i}"; // No id available using MethodInfo + sb.Append("{i}"); // No id available using MethodInfo } - public override string GetText(TestMethod testMethod, object[] args) + public override void AppendTextTo(StringBuilder sb, TestMethod testMethod, object[] args) { - return testMethod.Id; + sb.Append(testMethod.Id); } } - private class FixedTextFragment : NameFragment + private sealed class FixedTextFragment : NameFragment { private readonly string _text; @@ -473,69 +470,61 @@ public FixedTextFragment(string text) _text = text; } - public override string GetText(MethodInfo method, object[] args) + public override void AppendTextTo(StringBuilder sb, MethodInfo method, object[] args) { - return _text; + sb.Append(_text); } } - private class MethodNameFragment : NameFragment + private sealed class MethodNameFragment : NameFragment { - public override string GetText(MethodInfo method, object[] args) + public override void AppendTextTo(StringBuilder sb, MethodInfo method, object[] args) { - var sb = new StringBuilder(); - sb.Append(method.Name); if (method.IsGenericMethod) AppendGenericTypeNames(sb, method); - - return sb.ToString(); } } - private class NamespaceFragment : NameFragment + private sealed class NamespaceFragment : NameFragment { - public override string GetText(MethodInfo method, object[] args) + public override void AppendTextTo(StringBuilder sb, MethodInfo method, object[] args) { - return method.DeclaringType.Namespace; + sb.Append(method.DeclaringType.Namespace); } } - private class MethodFullNameFragment : NameFragment + private sealed class MethodFullNameFragment : NameFragment { - public override string GetText(MethodInfo method, object[] args) + public override void AppendTextTo(StringBuilder sb, MethodInfo method, object[] args) { - var sb = new StringBuilder(); - sb.Append(method.DeclaringType.FullName); sb.Append('.'); sb.Append(method.Name); if (method.IsGenericMethod) AppendGenericTypeNames(sb, method); - - return sb.ToString(); } } - private class ClassNameFragment : NameFragment + private sealed class ClassNameFragment : NameFragment { - public override string GetText(MethodInfo method, object[] args) + public override void AppendTextTo(StringBuilder sb, MethodInfo method, object[] args) { - return method.DeclaringType.Name; + sb.Append(method.DeclaringType.Name); } } - private class ClassFullNameFragment : NameFragment + private sealed class ClassFullNameFragment : NameFragment { - public override string GetText(MethodInfo method, object[] args) + public override void AppendTextTo(StringBuilder sb, MethodInfo method, object[] args) { - return method.DeclaringType.FullName; + sb.Append(method.DeclaringType.FullName); } } - private class ArgListFragment : NameFragment + private sealed class ArgListFragment : NameFragment { private readonly int _maxStringLength; @@ -544,10 +533,8 @@ public ArgListFragment(int maxStringLength) _maxStringLength = maxStringLength; } - public override string GetText(MethodInfo method, object[] arglist) + public override void AppendTextTo(StringBuilder sb, MethodInfo method, object[] arglist) { - var sb = new StringBuilder(); - if (arglist != null) { sb.Append('('); @@ -560,12 +547,10 @@ public override string GetText(MethodInfo method, object[] arglist) sb.Append(')'); } - - return sb.ToString(); } } - private class ArgumentFragment : NameFragment + private sealed class ArgumentFragment : NameFragment { private readonly int _index; private readonly int _maxStringLength; @@ -576,11 +561,45 @@ public ArgumentFragment(int index, int maxStringLength) _maxStringLength = maxStringLength; } - public override string GetText(MethodInfo method, object[] args) + public override void AppendTextTo(StringBuilder sb, MethodInfo method, object[] args) + { + if (_index < args.Length) + sb.Append(GetDisplayString(args[_index], _maxStringLength)); + } + } + + private sealed class ParamArgListFragment : NameFragment + { + private readonly int _maxStringLength; + + public ParamArgListFragment(int maxStringLength) { - return _index < args.Length - ? GetDisplayString(args[_index], _maxStringLength) - : string.Empty; + _maxStringLength = maxStringLength; + } + + public override void AppendTextTo(StringBuilder sb, MethodInfo method, object[] args) + { + if (args != null) + { + sb.Append('('); + + var parameters = method.GetParameters(); + + for (int i = 0; i < args.Length; i++) + { + if (i > 0) sb.Append(", "); + + if (i < parameters.Length) + { + sb.Append(parameters[i].Name); + sb.Append(": "); + } + + sb.Append(GetDisplayString(args[i], _maxStringLength)); + } + + sb.Append(')'); + } } } diff --git a/src/NUnitFramework/framework/Internal/TestParameters.cs b/src/NUnitFramework/framework/Internal/TestParameters.cs index a11aaf8636..5d98a2d153 100644 --- a/src/NUnitFramework/framework/Internal/TestParameters.cs +++ b/src/NUnitFramework/framework/Internal/TestParameters.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using NUnit.Framework.Interfaces; @@ -32,12 +34,19 @@ namespace NUnit.Framework.Internal /// public abstract class TestParameters : ITestData, IApplyToTest { + internal static readonly object[] NoArguments = +#if NET35 || NET40 || NET45 // New in net46 + new object[0]; +#else + Array.Empty(); +#endif + /// /// Default Constructor creates an empty parameter set /// public TestParameters() + : this(RunState.Runnable, NoArguments) { - RunState = RunState.Runnable; Properties = new PropertyBag(); } @@ -45,10 +54,9 @@ public TestParameters() /// Construct a parameter set with a list of arguments /// /// - public TestParameters(object[] args) + public TestParameters(object?[] args) + : this(RunState.Runnable, args) { - RunState = RunState.Runnable; - InitializeArguments(args); Properties = new PropertyBag(); } @@ -57,10 +65,9 @@ public TestParameters(object[] args) /// the provider exception that made it invalid. /// public TestParameters(Exception exception) + : this(RunState.NotRunnable, NoArguments) { - RunState = RunState.NotRunnable; Properties = new PropertyBag(); - Properties.Set(PropertyNames.SkipReason, ExceptionHelper.BuildMessage(exception)); Properties.Set(PropertyNames.ProviderStackTrace, ExceptionHelper.BuildStackTrace(exception)); } @@ -70,26 +77,26 @@ public TestParameters(Exception exception) /// /// public TestParameters(ITestData data) + : this(data.RunState, data.Arguments) { - RunState = data.RunState; - Properties = new PropertyBag(); - TestName = data.TestName; - InitializeArguments(data.Arguments); - foreach (string key in data.Properties.Keys) this.Properties[key] = data.Properties[key]; } - private void InitializeArguments(object[] args) + private TestParameters(RunState runState, object?[] args) { + RunState = runState; + OriginalArguments = args; // We need to copy args, since we may change them var numArgs = args.Length; - Arguments = new object[numArgs]; + Arguments = new object?[numArgs]; Array.Copy(args, Arguments, numArgs); + + Properties = new PropertyBag(); } /// @@ -101,16 +108,16 @@ private void InitializeArguments(object[] args) /// The arguments to be used in running the test, /// which must match the method signature. /// - public object[] Arguments { get; internal set; } + public object?[] Arguments { get; internal set; } - private string _testName; + private string? _testName; /// /// A name to be used for this test case in lieu /// of the standard generated name containing /// the argument list. /// - public string TestName + public string? TestName { get { @@ -126,7 +133,7 @@ public string TestName /// /// Gets the property dictionary for this test /// - public IPropertyBag Properties { get; } + public IPropertyBag Properties { get; protected internal set; } /// /// Applies ParameterSet values to the test itself. @@ -146,14 +153,14 @@ public void ApplyToTest(Test test) /// The original arguments provided by the user, /// used for display purposes. /// - public object[] OriginalArguments { get; private set; } + public object?[] OriginalArguments { get; protected internal set; } - private string[] _argDisplayNames; + private string[]? _argDisplayNames; /// /// The list of display names to use as the parameters in the test name. /// - internal string[] ArgDisplayNames + internal string[]? ArgDisplayNames { get { diff --git a/src/NUnitFramework/framework/Internal/TestProgressReporter.cs b/src/NUnitFramework/framework/Internal/TestProgressReporter.cs index d8d6a265aa..896cc232f3 100644 --- a/src/NUnitFramework/framework/Internal/TestProgressReporter.cs +++ b/src/NUnitFramework/framework/Internal/TestProgressReporter.cs @@ -21,8 +21,12 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; +using System.Reflection; using System.Web.UI; +using NUnit.Compatibility; using NUnit.Framework.Interfaces; namespace NUnit.Framework.Internal @@ -55,21 +59,20 @@ public TestProgressReporter(ICallbackEventHandler handler) /// The test that is starting public void TestStarted(ITest test) { - string startElement = test is TestSuite - ? "start-suite" - : "start-test"; - var parent = GetParent(test); try { - string report = string.Format( - "<{0} id=\"{1}\" parentId=\"{2}\" name=\"{3}\" fullname=\"{4}\" type=\"{5}\"/>", - startElement, - test.Id, - parent != null ? parent.Id : string.Empty, - FormatAttributeValue(test.Name), - FormatAttributeValue(test.FullName), - test.TestType); + string report; + if (test is TestSuite) + { + // Only add framework-version for the Assembly start-suite + string version = test.TestType == "Assembly" ? $"framework-version=\"{typeof(TestProgressReporter).GetTypeInfo().Assembly.GetName().Version}\" " : ""; + report = $""; + } + else + { + report = $""; + } handler.RaiseCallbackEvent(report); } @@ -140,7 +143,7 @@ public void SendMessage(TestMessage message) /// /// /// parent test item - private static ITest GetParent(ITest test) + private static ITest? GetParent(ITest test) { if (test == null || test.Parent == null) { diff --git a/src/NUnitFramework/framework/Internal/Tests/ParameterizedFixtureSuite.cs b/src/NUnitFramework/framework/Internal/Tests/ParameterizedFixtureSuite.cs index 27193968c1..d6111672ac 100644 --- a/src/NUnitFramework/framework/Internal/Tests/ParameterizedFixtureSuite.cs +++ b/src/NUnitFramework/framework/Internal/Tests/ParameterizedFixtureSuite.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using NUnit.Framework.Interfaces; namespace NUnit.Framework.Internal diff --git a/src/NUnitFramework/framework/Internal/Tests/ParameterizedMethodSuite.cs b/src/NUnitFramework/framework/Internal/Tests/ParameterizedMethodSuite.cs index e377c99d37..59ad332c90 100644 --- a/src/NUnitFramework/framework/Internal/Tests/ParameterizedMethodSuite.cs +++ b/src/NUnitFramework/framework/Internal/Tests/ParameterizedMethodSuite.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using NUnit.Framework.Interfaces; namespace NUnit.Framework.Internal @@ -39,7 +41,7 @@ public class ParameterizedMethodSuite : TestSuite public ParameterizedMethodSuite(IMethodInfo method) : base(method.TypeInfo.FullName, method.Name) { - Method = method; + base.Method = method; _isTheory = method.IsDefined(true); this.MaintainTestOrder = true; } @@ -54,6 +56,11 @@ public ParameterizedMethodSuite(ParameterizedMethodSuite suite, ITestFilter filt { } + /// + /// Gets a MethodInfo for the method implementing this test. + /// + public new IMethodInfo Method => base.Method!; + /// /// Gets a string representing the type of test /// diff --git a/src/NUnitFramework/framework/Internal/Tests/SetUpFixture.cs b/src/NUnitFramework/framework/Internal/Tests/SetUpFixture.cs index 8b34a102ac..cc57d840b5 100644 --- a/src/NUnitFramework/framework/Internal/Tests/SetUpFixture.cs +++ b/src/NUnitFramework/framework/Internal/Tests/SetUpFixture.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using NUnit.Framework.Interfaces; namespace NUnit.Framework.Internal @@ -66,6 +68,11 @@ public SetUpFixture(SetUpFixture setUpFixture, ITestFilter filter) #region Test Suite Overrides + /// + /// Gets the TypeInfo of the fixture used in running this test. + /// + public new ITypeInfo TypeInfo => base.TypeInfo!; + /// /// Creates a filtered copy of the test suite. /// diff --git a/src/NUnitFramework/framework/Internal/Tests/Test.cs b/src/NUnitFramework/framework/Internal/Tests/Test.cs index d259df3625..a8d13b8dbe 100644 --- a/src/NUnitFramework/framework/Internal/Tests/Test.cs +++ b/src/NUnitFramework/framework/Internal/Tests/Test.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; using System.Reflection; @@ -32,7 +34,7 @@ namespace NUnit.Framework.Internal /// /// The Test abstract class represents a test within the framework. /// - public abstract class Test : ITest, IComparable + public abstract class Test : ITest, IComparable, IComparable { #region Fields @@ -45,12 +47,12 @@ public abstract class Test : ITest, IComparable /// /// Used to cache the declaring type for this MethodInfo /// - private ITypeInfo _declaringTypeInfo; + private ITypeInfo? _declaringTypeInfo; /// /// Method property backing field /// - private IMethodInfo _method; + private IMethodInfo? _method; #endregion @@ -60,11 +62,9 @@ public abstract class Test : ITest, IComparable /// Constructs a test given its name /// /// The name of the test - protected Test( string name ) + protected Test(string name) + : this(pathName: null, name, typeInfo: null, method: null) { - Guard.ArgumentNotNullOrEmpty(name, nameof(name)); - - Initialize(name); } /// @@ -73,43 +73,39 @@ protected Test( string name ) /// /// The parent tests full name /// The name of the test - protected Test( string pathName, string name ) + protected Test(string? pathName, string name) + : this(pathName, name, typeInfo: null, method: null) { - Initialize(name); - - if (!string.IsNullOrEmpty(pathName)) - FullName = pathName + "." + name; } /// /// Constructs a test for a specific type. /// protected Test(ITypeInfo typeInfo) + : this(pathName: typeInfo.Namespace, name: typeInfo.GetDisplayName(), typeInfo: typeInfo, method: null) { - Initialize(typeInfo.GetDisplayName()); - - string nspace = typeInfo.Namespace; - if (nspace != null && nspace != "") - FullName = nspace + "." + Name; - TypeInfo = typeInfo; } /// /// Constructs a test for a specific method. /// protected Test(IMethodInfo method) + : this(pathName: method.TypeInfo.FullName, name: method.Name, typeInfo: method.TypeInfo, method: method) { - Initialize(method.Name); - - Method = method; - TypeInfo = method.TypeInfo; - FullName = method.TypeInfo.FullName + "." + Name; } - private void Initialize(string name) + private Test(string? pathName, string name, ITypeInfo? typeInfo, IMethodInfo? method) { - FullName = Name = name; + Guard.ArgumentNotNullOrEmpty(name, nameof(name)); + Id = GetNextId(); + Name = name; + FullName = !string.IsNullOrEmpty(pathName) + ? pathName + '.' + name + : name; + + TypeInfo = typeInfo; + Method = method; Properties = new PropertyBag(); RunState = RunState.Runnable; SetUpMethods = new MethodInfo[0]; @@ -146,11 +142,11 @@ private static string GetNextId() /// Gets the name of the class where this test was declared. /// Returns null if the test is not associated with a class. /// - public string ClassName + public string? ClassName { get { - ITypeInfo typeInfo = TypeInfo; + ITypeInfo? typeInfo = TypeInfo; if (Method != null) { @@ -173,7 +169,7 @@ public string ClassName /// Gets the name of the method implementing this test. /// Returns null if the test is not implemented as a method. /// - public virtual string MethodName + public virtual string? MethodName { get { return null; } } @@ -181,19 +177,19 @@ public virtual string MethodName /// /// The arguments to use in creating the test or empty array if none required. /// - public abstract object[] Arguments { get; } + public abstract object?[] Arguments { get; } /// /// Gets the TypeInfo of the fixture used in running this test /// or null if no fixture type is associated with it. /// - public ITypeInfo TypeInfo { get; private set; } + public ITypeInfo? TypeInfo { get; } /// /// Gets a MethodInfo for the method implementing this test. /// Returns null if the test is not implemented as a method. /// - public IMethodInfo Method + public IMethodInfo? Method { get { return _method; } set @@ -236,7 +232,7 @@ public virtual int TestCaseCount /// /// Gets the properties for this test /// - public IPropertyBag Properties { get; private set; } + public IPropertyBag Properties { get; } /// /// Returns true if this is a TestSuite @@ -256,7 +252,7 @@ public bool IsSuite /// Gets the parent as a Test object. /// Used by the core to set the parent. /// - public ITest Parent { get; set; } + public ITest? Parent { get; set; } /// /// Gets this test's child tests @@ -267,7 +263,7 @@ public bool IsSuite /// /// Gets or sets a fixture object for running this test. /// - public virtual object Fixture { get; set; } + public virtual object? Fixture { get; set; } #endregion @@ -277,7 +273,7 @@ public bool IsSuite /// Static prefix used for ids in this AppDomain. /// Set by FrameworkController. /// - public static string IdPrefix { get; set; } + public static string? IdPrefix { get; set; } /// /// Gets or Sets the Int value representing the seed for the RandomGenerator @@ -301,7 +297,7 @@ public bool IsSuite internal bool RequiresThread { get; set; } - private ITestAction[] _actions; + private ITestAction[]? _actions; internal ITestAction[] Actions { @@ -330,43 +326,6 @@ internal ITestAction[] Actions /// A TestResult suitable for this type of test. public abstract TestResult MakeTestResult(); -#if NETSTANDARD1_4 - /// - /// Modify a newly constructed test by applying any of NUnit's common - /// attributes, based on a supplied , which is - /// usually the reflection element from which the test was constructed, - /// but may not be in some instances. The attributes retrieved are - /// saved for use in subsequent operations. - /// - public void ApplyAttributesToTest(MemberInfo provider) - { - ApplyAttributesToTest(provider.GetAttributes(inherit: true)); - } - - /// - /// Modify a newly constructed test by applying any of NUnit's common - /// attributes, based on a supplied , which is - /// usually the reflection element from which the test was constructed, - /// but may not be in some instances. The attributes retrieved are - /// saved for use in subsequent operations. - /// - public void ApplyAttributesToTest(TypeInfo provider) - { - ApplyAttributesToTest(provider.GetAttributes(inherit: true)); - } - - /// - /// Modify a newly constructed test by applying any of NUnit's common - /// attributes, based on a supplied , which is - /// usually the reflection element from which the test was constructed, - /// but may not be in some instances. The attributes retrieved are - /// saved for use in subsequent operations. - /// - public void ApplyAttributesToTest(Assembly provider) - { - ApplyAttributesToTest(provider.GetAttributes()); - } -#else /// /// Modify a newly constructed test by applying any of NUnit's common /// attributes, based on a supplied , which is @@ -378,7 +337,6 @@ public void ApplyAttributesToTest(ICustomAttributeProvider provider) { ApplyAttributesToTest(provider.GetAttributes(inherit: true)); } -#endif private void ApplyAttributesToTest(IEnumerable attributes) { @@ -484,19 +442,18 @@ public TNode ToXml(bool recursive) #region IComparable Members - /// - /// Compares this test to another test for sorting purposes - /// - /// The other test - /// Value of -1, 0 or +1 depending on whether the current test is less than, equal to or greater than the other test - public int CompareTo(object obj) + /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. + /// An object to compare with this instance. + public int CompareTo(object? obj) { - Test other = obj as Test; - - if (other == null) - return -1; + return CompareTo(obj as Test); + } - return this.FullName.CompareTo(other.FullName); + /// Compares the current instance with another object of the same type and returns an integer that indicates whether the current instance precedes, follows, or occurs in the same position in the sort order as the other object. + /// An object to compare with this instance. + public int CompareTo(Test? other) + { + return other == null ? -1 : this.FullName.CompareTo(other.FullName); } #endregion diff --git a/src/NUnitFramework/framework/Internal/Tests/TestAssembly.cs b/src/NUnitFramework/framework/Internal/Tests/TestAssembly.cs index a6c5b4eada..d67148a0f8 100644 --- a/src/NUnitFramework/framework/Internal/Tests/TestAssembly.cs +++ b/src/NUnitFramework/framework/Internal/Tests/TestAssembly.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.IO; using System.Reflection; using NUnit.Framework.Interfaces; @@ -74,7 +76,7 @@ public TestAssembly(TestAssembly assembly, ITestFilter filter) /// /// Gets the Assembly represented by this instance. /// - public Assembly Assembly { get; } + public Assembly? Assembly { get; } /// /// Gets the name used for the top-level element in the diff --git a/src/NUnitFramework/framework/Internal/Tests/TestFixture.cs b/src/NUnitFramework/framework/Internal/Tests/TestFixture.cs index 23b6f8e0d2..bd68996cd1 100644 --- a/src/NUnitFramework/framework/Internal/Tests/TestFixture.cs +++ b/src/NUnitFramework/framework/Internal/Tests/TestFixture.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using NUnit.Framework.Interfaces; namespace NUnit.Framework.Internal @@ -31,6 +33,11 @@ namespace NUnit.Framework.Internal /// public class TestFixture : TestSuite, IDisposableFixture { + /// + /// The life cycle specified for the current test fixture. + /// + public LifeCycle LifeCycle { get; set; } + #region Constructor /// @@ -38,7 +45,7 @@ public class TestFixture : TestSuite, IDisposableFixture /// /// Type of the fixture. /// Arguments used to instantiate the test fixture, or null if none used - public TestFixture(ITypeInfo fixtureType, object[] arguments = null) : base(fixtureType, arguments) + public TestFixture(ITypeInfo fixtureType, object?[]? arguments = null) : base(fixtureType, arguments) { SetUpMethods = Reflect.GetMethodsWithAttribute(TypeInfo.Type, typeof(SetUpAttribute), true); TearDownMethods = Reflect.GetMethodsWithAttribute(TypeInfo.Type, typeof(TearDownAttribute), true); @@ -65,6 +72,11 @@ private TestFixture(TestFixture fixture, ITestFilter filter) #region Test Suite Overrides + /// + /// Gets the TypeInfo of the fixture used in running this test. + /// + public new ITypeInfo TypeInfo => base.TypeInfo!; + /// /// Creates a filtered copy of the test suite. /// diff --git a/src/NUnitFramework/framework/Internal/Tests/TestMethod.cs b/src/NUnitFramework/framework/Internal/Tests/TestMethod.cs index f27ca916c5..46fe394d02 100644 --- a/src/NUnitFramework/framework/Internal/Tests/TestMethod.cs +++ b/src/NUnitFramework/framework/Internal/Tests/TestMethod.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System.Collections.Generic; using NUnit.Framework.Interfaces; @@ -32,12 +34,11 @@ namespace NUnit.Framework.Internal public class TestMethod : Test { #region Fields - private static readonly object[] NoArguments = new object[0]; /// /// The ParameterSet used to create this test method /// - internal TestCaseParameters parms; + internal TestCaseParameters? parms; #endregion @@ -54,7 +55,7 @@ public class TestMethod : Test /// /// The method to be used as a test. /// The suite or fixture to which the new test will be added - public TestMethod(IMethodInfo method, Test parentSuite) : base(method) + public TestMethod(IMethodInfo method, Test? parentSuite) : base(method) { // Needed to give proper fullname to test in a parameterized fixture. // Without this, the arguments to the fixture are not included. @@ -71,7 +72,7 @@ internal bool HasExpectedResult get { return parms != null && parms.HasExpectedResult; } } - internal object ExpectedResult + internal object? ExpectedResult { get { return parms != null ? parms.ExpectedResult : null; } } @@ -79,12 +80,17 @@ internal object ExpectedResult #region Test Overrides + /// + /// Gets a MethodInfo for the method implementing this test. + /// + public new IMethodInfo Method { get => base.Method!; set => base.Method = value; } + /// /// The arguments to use in executing the test method, or empty array if none are provided. /// - public override object[] Arguments + public override object?[] Arguments { - get { return parms != null ? parms.Arguments : NoArguments; } + get { return parms != null ? parms.Arguments : TestParameters.NoArguments; } } /// diff --git a/src/NUnitFramework/framework/Internal/Tests/TestSuite.cs b/src/NUnitFramework/framework/Internal/Tests/TestSuite.cs index 2ddb90837b..d477de1486 100644 --- a/src/NUnitFramework/framework/Internal/Tests/TestSuite.cs +++ b/src/NUnitFramework/framework/Internal/Tests/TestSuite.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; using System.Reflection; @@ -50,7 +52,7 @@ public class TestSuite : Test /// The name of the suite. public TestSuite(string name) : base(name) { - Arguments = new object[0]; + Arguments = TestParameters.NoArguments; OneTimeSetUpMethods = new MethodInfo[0]; OneTimeTearDownMethods = new MethodInfo[0]; } @@ -63,7 +65,7 @@ public TestSuite(string name) : base(name) public TestSuite(string parentSuiteName, string name) : base(parentSuiteName, name) { - Arguments = new object[0]; + Arguments = TestParameters.NoArguments; OneTimeSetUpMethods = new MethodInfo[0]; OneTimeTearDownMethods = new MethodInfo[0]; } @@ -73,10 +75,10 @@ public TestSuite(string parentSuiteName, string name) /// /// Type of the fixture. /// Arguments used to instantiate the test fixture, or null if none used. - public TestSuite(ITypeInfo fixtureType, object[] arguments = null) + public TestSuite(ITypeInfo fixtureType, object?[]? arguments = null) : base(fixtureType) { - Arguments = arguments ?? new object[0]; + Arguments = arguments ?? TestParameters.NoArguments; OneTimeSetUpMethods = new MethodInfo[0]; OneTimeTearDownMethods = new MethodInfo[0]; } @@ -88,7 +90,7 @@ public TestSuite(ITypeInfo fixtureType, object[] arguments = null) public TestSuite(Type fixtureType) : base(new TypeWrapper(fixtureType)) { - Arguments = new object[0]; + Arguments = TestParameters.NoArguments; OneTimeSetUpMethods = new MethodInfo[0]; OneTimeTearDownMethods = new MethodInfo[0]; } @@ -99,14 +101,18 @@ public TestSuite(Type fixtureType) /// The to copy. /// Determines which descendants are copied. public TestSuite(TestSuite suite, ITestFilter filter) - : base(suite.Name) + : this(suite.Name) { this.FullName = suite.FullName; this.Method = suite.Method; this.RunState = suite.RunState; this.Fixture = suite.Fixture; - foreach(var child in suite.tests) + foreach (string key in suite.Properties.Keys) + foreach (object val in suite.Properties[key]) + this.Properties.Add(key, val); + + foreach (var child in suite.tests) { if(filter.Pass(child)) { @@ -139,7 +145,7 @@ public void Sort() foreach (Test test in Tests) { - TestSuite suite = test as TestSuite; + TestSuite? suite = test as TestSuite; if (suite != null) suite.Sort(); } @@ -218,7 +224,7 @@ public override int TestCaseCount /// /// The arguments to use in creating the fixture, or empty array if none are provided. /// - public override object[] Arguments { get; } + public override object?[] Arguments { get; } /// /// Set to true to suppress sorting this suite's contents @@ -300,7 +306,6 @@ public override TNode AddToXml(TNode parentNode, bool recursive) /// Check that setup and teardown methods marked by certain attributes /// meet NUnit's requirements and mark the tests not runnable otherwise. /// - /// A list of methodinfos to check protected void CheckSetUpTearDownMethods(MethodInfo[] methods) { foreach (MethodInfo method in methods) @@ -311,7 +316,7 @@ protected void CheckSetUpTearDownMethods(MethodInfo[] methods) } else if (!(method.IsPublic || method.IsFamily)) { - MakeInvalid("SetUp and TearDown methods must be public or internal: " + method.Name); + MakeInvalid("SetUp and TearDown methods must be public or protected: " + method.Name); } else if (method.GetParameters().Length != 0) { @@ -321,12 +326,12 @@ protected void CheckSetUpTearDownMethods(MethodInfo[] methods) { if (method.ReturnType == typeof(void)) MakeInvalid("SetUp and TearDown methods must not be async void: " + method.Name); - else if (AwaitAdapter.GetResultType(method.ReturnType) != typeof(void)) + else if (!Reflect.IsVoidOrUnit(AwaitAdapter.GetResultType(method.ReturnType))) MakeInvalid("SetUp and TearDown methods must return void or an awaitable type with a void result: " + method.Name); } else { - if (method.ReturnType != typeof(void)) + if (!Reflect.IsVoidOrUnit(method.ReturnType)) MakeInvalid("SetUp and TearDown methods must return void or an awaitable type with a void result: " + method.Name); } } diff --git a/src/NUnitFramework/framework/Internal/ThreadUtility.cs b/src/NUnitFramework/framework/Internal/ThreadUtility.cs index 20dadd1cd5..539d7c4e63 100644 --- a/src/NUnitFramework/framework/Internal/ThreadUtility.cs +++ b/src/NUnitFramework/framework/Internal/ThreadUtility.cs @@ -37,11 +37,7 @@ public static class ThreadUtility { internal static void BlockingDelay(int milliseconds) { -#if !PARALLEL - System.Threading.Tasks.Task.Delay(milliseconds).GetAwaiter().GetResult(); -#else Thread.Sleep(milliseconds); -#endif } #if THREAD_ABORT @@ -136,18 +132,18 @@ public static void Kill(Thread thread, object stateInfo, int nativeId = 0) #pragma warning restore 0618,0612 // Thread.Resume has been deprecated } - if ( (thread.ThreadState & ThreadState.WaitSleepJoin) != 0 ) + if ((thread.ThreadState & ThreadState.WaitSleepJoin) != 0) thread.Interrupt(); } /// - /// Schedule a threadpool thread to check on the aborting thread in case it's in a message pump native blocking wait + /// Schedule a thread pool thread to check on the aborting thread in case it's in a message pump native blocking wait /// private static void DislodgeThreadInNativeMessageWait(Thread thread, int nativeId) { if (nativeId == 0) throw new ArgumentOutOfRangeException(nameof(nativeId), "Native thread ID must not be zero."); - // Schedule a threadpool thread to check on the aborting thread in case it's in a message pump native blocking wait + // Schedule a thread pool thread to check on the aborting thread in case it's in a message pump native blocking wait Delay(ThreadAbortedCheckDelay, CheckOnAbortingThread, new CheckOnAbortingThreadState(thread, nativeId)); } @@ -234,5 +230,30 @@ private enum WM : uint CLOSE = 0x0010 } #endif + + /// Gets or if the current platform does not support it. + public static System.Security.Principal.IPrincipal GetCurrentThreadPrincipal() + { + try + { + return Thread.CurrentPrincipal; + } + catch (PlatformNotSupportedException) //E.g. Mono.WASM + { + return null; + } + } + + /// Sets if current platform supports it. + /// Value to set. If the current platform does not support then the only allowed value is . + public static void SetCurrentThreadPrincipal(System.Security.Principal.IPrincipal principal) + { + try + { + Thread.CurrentPrincipal = principal; + } + catch (PlatformNotSupportedException) when (principal == null) //E.g. Mono.WASM + { } + } } } diff --git a/src/NUnitFramework/framework/Internal/TypeHelper.cs b/src/NUnitFramework/framework/Internal/TypeHelper.cs index 082727c4c4..3db535ce76 100644 --- a/src/NUnitFramework/framework/Internal/TypeHelper.cs +++ b/src/NUnitFramework/framework/Internal/TypeHelper.cs @@ -21,8 +21,11 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text; using NUnit.Compatibility; @@ -107,7 +110,7 @@ public static string GetDisplayName(Type type) /// The Type for which a display name is needed. /// The arglist provided. /// The display name for the Type - public static string GetDisplayName(Type type, object[] arglist) + public static string GetDisplayName(Type type, object?[]? arglist) { string baseName = GetDisplayName(type); if (arglist == null || arglist.Length == 0) @@ -120,8 +123,8 @@ public static string GetDisplayName(Type type, object[] arglist) { if (i > 0) sb.Append(","); - object arg = arglist[i]; - string display = arg == null ? "null" : arg.ToString(); + object? arg = arglist[i]; + string? display = arg == null ? "null" : arg.ToString(); if (arg is double || arg is float) { @@ -150,7 +153,7 @@ public static string GetDisplayName(Type type, object[] arglist) /// Returns the best fit for a common type to be used in /// matching actual arguments to a methods Type parameters. /// - public static bool TryGetBestCommonType(Type type1, Type type2, out Type bestCommonType) + public static bool TryGetBestCommonType(Type? type1, Type? type2, [NotNullIfNotNull("type1"), NotNullIfNotNull("type2")] out Type? bestCommonType) { if (type1 == type2) { bestCommonType = type1; return true; } if (type1 == null) { bestCommonType = type2; return true; } @@ -195,7 +198,7 @@ public static bool TryGetBestCommonType(Type type1, Type type2, out Type bestCom if (type1.IsAssignableFrom(type2)) { bestCommonType = type1; return true; } if (type2.IsAssignableFrom(type1)) { bestCommonType = type2; return true; } - bestCommonType = null; + bestCommonType = typeof(object); return false; } @@ -204,7 +207,7 @@ public static bool TryGetBestCommonType(Type type1, Type type2, out Type bestCom /// /// The type to be examined. /// - /// true if the specified type is numeric; otherwise, false. + /// if the specified type is numeric; otherwise, . /// public static bool IsNumeric(Type type) { @@ -227,13 +230,13 @@ public static bool IsNumeric(Type type) /// /// An array of args to be converted /// A ParameterInfo[] whose types will be used as targets - public static void ConvertArgumentList(object[] arglist, IParameterInfo[] parameters) + public static void ConvertArgumentList(object?[] arglist, IParameterInfo[] parameters) { System.Diagnostics.Debug.Assert(arglist.Length <= parameters.Length); for (int i = 0; i < arglist.Length; i++) { - object arg = arglist[i]; + object? arg = arglist[i]; if (arg is IConvertible) { @@ -267,9 +270,9 @@ public static void ConvertArgumentList(object[] arglist, IParameterInfo[] parame /// The arglist. /// The type args to be used. /// - /// true if this the provided args give sufficient information to determine the type args to be used; otherwise, false. + /// if this the provided args give sufficient information to determine the type args to be used; otherwise, . /// - public static bool CanDeduceTypeArgsFromArgs(Type type, object[] arglist, ref Type[] typeArgsOut) + public static bool CanDeduceTypeArgsFromArgs(Type type, object?[] arglist, [NotNullWhen(true)] ref Type[]? typeArgsOut) { Type[] typeParameters = type.GetGenericArguments(); @@ -279,7 +282,7 @@ public static bool CanDeduceTypeArgsFromArgs(Type type, object[] arglist, ref Ty if (parameters.Length != arglist.Length) continue; - Type[] typeArgs = new Type[typeParameters.Length]; + Type?[]? typeArgs = new Type?[typeParameters.Length]; for (int i = 0; i < typeArgs.Length; i++) { for (int j = 0; j < arglist.Length; j++) @@ -288,7 +291,7 @@ public static bool CanDeduceTypeArgsFromArgs(Type type, object[] arglist, ref Ty { if (!TypeHelper.TryGetBestCommonType( typeArgs[i], - arglist[j].GetType(), + arglist[j]?.GetType(), out typeArgs[i])) { typeArgs[i] = null; @@ -306,7 +309,7 @@ public static bool CanDeduceTypeArgsFromArgs(Type type, object[] arglist, ref Ty if (typeArgs != null) { - typeArgsOut = typeArgs; + typeArgsOut = typeArgs!; return true; } } @@ -362,7 +365,7 @@ private static bool IsTupleInternal(Type type, string tupleName) { string typeName = type.FullName; - if (typeName.EndsWith("[]")) + if (typeName.EndsWith("[]", StringComparison.Ordinal)) return false; string typeNameWithoutGenerics = GetTypeNameWithoutGenerics(typeName); @@ -383,7 +386,9 @@ private static string GetTypeNameWithoutGenerics(string fullTypeName) /// The object to cast. internal static bool CanCast(object obj) { - return obj is T || (obj == null && default(T) == null); + // Workaround for https://github.com/dotnet/roslyn/issues/34757, fixed in VS 16.5 + // ↓ + return obj is T || (obj == null && default(T)! == null); } /// @@ -393,7 +398,7 @@ internal static bool CanCast(object obj) /// /// The object to cast. /// The value of the object, if the cast succeeded. - internal static bool TryCast(object obj, out T value) + internal static bool TryCast(object obj, [MaybeNull] out T value) { if (obj is T) { @@ -401,8 +406,12 @@ internal static bool TryCast(object obj, out T value) return true; } - value = default(T); - return obj == null && default(T) == null; + // Workaround for https://github.com/dotnet/roslyn/issues/36039, fixed in VS 16.5 + // ↓ + value = default(T)!; + // Workaround for https://github.com/dotnet/roslyn/issues/34757, fixed in VS 16.5 + // ↓ + return obj == null && default(T)! == null; } } } diff --git a/src/NUnitFramework/framework/Internal/TypeNameDifferenceResolver.cs b/src/NUnitFramework/framework/Internal/TypeNameDifferenceResolver.cs index ad56266f34..9a5a5d7d11 100644 --- a/src/NUnitFramework/framework/Internal/TypeNameDifferenceResolver.cs +++ b/src/NUnitFramework/framework/Internal/TypeNameDifferenceResolver.cs @@ -56,11 +56,9 @@ public void ResolveTypeNameDifference(Type expected, Type actual, out string exp { if (IsTypeGeneric(expected) && IsTypeGeneric(actual)) { - string shortenedGenericTypeExpected, shortenedGenericTypeActual; - GetShortenedGenericTypes(expected, actual, out shortenedGenericTypeExpected, out shortenedGenericTypeActual); + GetShortenedGenericTypes(expected, actual, out var shortenedGenericTypeExpected, out var shortenedGenericTypeActual); - List shortenedParamsExpected, shortenedParamsActual; - GetShortenedGenericParams(expected, actual, out shortenedParamsExpected, out shortenedParamsActual); + GetShortenedGenericParams(expected, actual, out var shortenedParamsExpected, out var shortenedParamsActual); expectedTypeShortened = ReconstructGenericTypeName(shortenedGenericTypeExpected, shortenedParamsExpected); actualTypeShortened = ReconstructGenericTypeName(shortenedGenericTypeActual, shortenedParamsActual); @@ -93,8 +91,7 @@ private void GetShortenedGenericParams(Type expectedFullType, Type actualFullTyp shortenedParamsActual = new List(); while ((templateParamsExpected.Count > 0) && (templateParamsActual.Count > 0)) { - string shortenedExpected, shortenedActual; - ResolveTypeNameDifference(templateParamsExpected[0], templateParamsActual[0], out shortenedExpected, out shortenedActual); + ResolveTypeNameDifference(templateParamsExpected[0], templateParamsActual[0], out var shortenedExpected, out var shortenedActual); shortenedParamsExpected.Add(shortenedExpected); shortenedParamsActual.Add(shortenedActual); diff --git a/src/NUnitFramework/framework/Internal/TypeWrapper.cs b/src/NUnitFramework/framework/Internal/TypeWrapper.cs index ef143edc7b..3a8c41fe0b 100644 --- a/src/NUnitFramework/framework/Internal/TypeWrapper.cs +++ b/src/NUnitFramework/framework/Internal/TypeWrapper.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Reflection; using NUnit.Compatibility; @@ -52,7 +54,7 @@ public TypeWrapper(Type type) /// /// Gets the base type of this type as an ITypeInfo /// - public ITypeInfo BaseType + public ITypeInfo? BaseType { get { @@ -160,7 +162,7 @@ public string GetDisplayName() /// /// Get the display name for an object of this type, constructed with the specified args. /// - public string GetDisplayName(object[] args) + public string GetDisplayName(object?[]? args) { return TypeHelper.GetDisplayName(Type, args); } @@ -228,7 +230,7 @@ public IMethodInfo[] GetMethods(BindingFlags flags) /// /// Gets the public constructor taking the specified argument Types /// - public ConstructorInfo GetConstructor(Type[] argTypes) + public ConstructorInfo? GetConstructor(Type[] argTypes) { return Type.GetConstructor(argTypes); } @@ -244,7 +246,7 @@ public bool HasConstructor(Type[] argTypes) /// /// Construct an object of this Type, using the specified arguments. /// - public object Construct(object[] args) + public object Construct(object?[]? args) { return Reflect.Construct(Type, args); } diff --git a/src/NUnitFramework/framework/Internal/ValueGenerator.cs b/src/NUnitFramework/framework/Internal/ValueGenerator.cs index 7415729f1c..e1ba14699d 100644 --- a/src/NUnitFramework/framework/Internal/ValueGenerator.cs +++ b/src/NUnitFramework/framework/Internal/ValueGenerator.cs @@ -162,8 +162,7 @@ public new abstract class Step : ValueGenerator.Step /// public sealed override ValueGenerator.Step CreateStep(object value) { - ValueGenerator.Step step; - if (TryCreateStep(value, out step)) return step; + if (TryCreateStep(value, out var step)) return step; throw CreateNotSupportedException($"creating a step of type {value.GetType()}"); } diff --git a/src/NUnitFramework/framework/Is.cs b/src/NUnitFramework/framework/Is.cs index 9b6250b055..fbb972e1fa 100644 --- a/src/NUnitFramework/framework/Is.cs +++ b/src/NUnitFramework/framework/Is.cs @@ -170,8 +170,6 @@ public static UniqueItemsConstraint Unique #endregion -#if SERIALIZATION - /// /// Returns a constraint that tests whether an object graph is serializable in binary format. /// @@ -188,8 +186,6 @@ public static XmlSerializableConstraint XmlSerializable get { return new XmlSerializableConstraint(); } } -#endif - #region EqualTo /// diff --git a/src/NUnitFramework/framework/List.cs b/src/NUnitFramework/framework/List.cs index d362f5250b..fba6ebbf77 100644 --- a/src/NUnitFramework/framework/List.cs +++ b/src/NUnitFramework/framework/List.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,25 +21,28 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +using System; using System.Collections; namespace NUnit.Framework { - /// - /// The List class is a helper class with properties and methods - /// that supply a number of constraints used with lists and collections. - /// - public class List - { - /// - /// List.Map returns a ListMapper, which can be used to map - /// the original collection to another collection. - /// - /// - /// - public static ListMapper Map( ICollection actual ) - { - return new ListMapper( actual ); - } - } + /// + /// The List class is a helper class with properties and methods + /// that supply a number of constraints used with lists and collections. + /// + [Obsolete("The List class has been deprecated and will be removed in a future release. " + + "Please use the extension method System.Linq.Enumerable.Select instead.")] + public class List + { + /// + /// List.Map returns a ListMapper, which can be used to map + /// the original collection to another collection. + /// + /// + /// + public static ListMapper Map( ICollection actual ) + { + return new ListMapper( actual ); + } + } } diff --git a/src/NUnitFramework/framework/ListMapper.cs b/src/NUnitFramework/framework/ListMapper.cs index 9aba8988b1..23584e0489 100644 --- a/src/NUnitFramework/framework/ListMapper.cs +++ b/src/NUnitFramework/framework/ListMapper.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -33,6 +33,8 @@ namespace NUnit.Framework /// ListMapper is used to transform a collection used as an actual argument /// producing another collection to be used in the assertion. /// + [Obsolete("The ListMapper class has been deprecated and will be removed in a future release. " + + "Please use the extension method System.Linq.Enumerable.Select instead.")] public class ListMapper { readonly ICollection original; @@ -56,7 +58,7 @@ public ICollection Property( string name ) var propList = new List(); foreach( object item in original ) { - PropertyInfo property = item.GetType().GetProperty( name, + PropertyInfo property = item.GetType().GetProperty( name, BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance ); if ( property == null ) throw new ArgumentException( string.Format( diff --git a/src/NUnitFramework/framework/Properties/AssemblyInfo.cs b/src/NUnitFramework/framework/Properties/AssemblyInfo.cs index 5e272dd133..d4b6c7d77a 100644 --- a/src/NUnitFramework/framework/Properties/AssemblyInfo.cs +++ b/src/NUnitFramework/framework/Properties/AssemblyInfo.cs @@ -56,8 +56,6 @@ [assembly: AssemblyTitle("NUnit Framework (.NET Framework 4.0)")] #elif NET35 [assembly: AssemblyTitle("NUnit Framework (.NET Framework 3.5)")] -#elif NETSTANDARD1_4 -[assembly: AssemblyTitle("NUnit Framework (.NET Standard 1.4)")] #elif NETSTANDARD2_0 [assembly: AssemblyTitle("NUnit Framework (.NET Standard 2.0)")] #else diff --git a/src/NUnitFramework/framework/Result.cs b/src/NUnitFramework/framework/Result.cs new file mode 100644 index 0000000000..ba97e8f39e --- /dev/null +++ b/src/NUnitFramework/framework/Result.cs @@ -0,0 +1,84 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; + +namespace NUnit.Framework +{ + internal static class Result + { + public static Result Success(T value) => Result.Success(value); + + public static Result Error(string message) => Result.Error(message); + } + + internal struct Result + { + private readonly T _value; + private readonly string _errorMessage; + + private Result(T value, string errorMessage) + { + _value = value; + _errorMessage = errorMessage; + } + + public static Result Success(T value) + { + return new Result(value, errorMessage: null); + } + + public static Result Error(string message) + { + if (string.IsNullOrEmpty(message)) + throw new ArgumentException("Error message must be specified.", nameof(message)); + + return new Result(default(T), message); + } + + public bool IsSuccess(out T value) + { + value = _value; + return _errorMessage is null; + } + + public bool IsError(out string message) + { + message = _errorMessage; + return _errorMessage != null; + } + + public T Value + { + get + { + if (_errorMessage != null) throw new InvalidOperationException("The result is not success."); + return _value; + } + } + + public static implicit operator Result(T value) => Success(value); + + public static implicit operator Result(string value) => Error(value); + } +} diff --git a/src/NUnitFramework/framework/Schemas/TestDefinitions.xsd b/src/NUnitFramework/framework/Schemas/TestDefinitions.xsd index 59ed8c8158..f6f81d1826 100644 --- a/src/NUnitFramework/framework/Schemas/TestDefinitions.xsd +++ b/src/NUnitFramework/framework/Schemas/TestDefinitions.xsd @@ -50,11 +50,50 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/NUnitFramework/framework/Schemas/TestResult.xsd b/src/NUnitFramework/framework/Schemas/TestResult.xsd index 591deacc5b..9647dd20fa 100644 --- a/src/NUnitFramework/framework/Schemas/TestResult.xsd +++ b/src/NUnitFramework/framework/Schemas/TestResult.xsd @@ -36,7 +36,6 @@ - @@ -133,51 +132,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -214,6 +168,7 @@ + diff --git a/src/NUnitFramework/framework/TestCaseData.cs b/src/NUnitFramework/framework/TestCaseData.cs index 6a03c0cb24..65d6fa0c10 100644 --- a/src/NUnitFramework/framework/TestCaseData.cs +++ b/src/NUnitFramework/framework/TestCaseData.cs @@ -21,7 +21,9 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable using System; +using System.Globalization; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -42,8 +44,8 @@ public class TestCaseData : TestCaseParameters /// Initializes a new instance of the class. /// /// The arguments. - public TestCaseData(params object[] args) - : base(args == null ? new object[] { null } : args) + public TestCaseData(params object?[]? args) + : base(args == null ? new object?[] { null } : args) { } @@ -51,8 +53,8 @@ public TestCaseData(params object[] args) /// Initializes a new instance of the class. /// /// The argument. - public TestCaseData(object arg) - : base(new object[] { arg }) + public TestCaseData(object? arg) + : base(new object?[] { arg }) { } @@ -61,8 +63,8 @@ public TestCaseData(object arg) /// /// The first argument. /// The second argument. - public TestCaseData(object arg1, object arg2) - : base(new object[] { arg1, arg2 }) + public TestCaseData(object? arg1, object? arg2) + : base(new object?[] { arg1, arg2 }) { } @@ -72,8 +74,8 @@ public TestCaseData(object arg1, object arg2) /// The first argument. /// The second argument. /// The third argument. - public TestCaseData(object arg1, object arg2, object arg3) - : base( new object[] { arg1, arg2, arg3 }) + public TestCaseData(object? arg1, object? arg2, object? arg3) + : base(new object?[] { arg1, arg2, arg3 }) { } @@ -86,7 +88,7 @@ public TestCaseData(object arg1, object arg2, object arg3) /// /// The expected result /// A modified TestCaseData - public TestCaseData Returns(object result) + public TestCaseData Returns(object? result) { this.ExpectedResult = result; return this; @@ -96,7 +98,7 @@ public TestCaseData Returns(object result) /// Sets the name of the test case /// /// The modified TestCaseData instance - public TestCaseData SetName(string name) + public TestCaseData SetName(string? name) { this.TestName = name; return this; @@ -105,7 +107,7 @@ public TestCaseData SetName(string name) /// /// Sets the list of display names to use as the parameters in the test name. /// - public TestCaseData SetArgDisplayNames(params string[] displayNames) + public TestCaseData SetArgDisplayNames(params string[]? displayNames) { ArgDisplayNames = displayNames; return this; @@ -193,11 +195,13 @@ public TestCaseData Explicit(string reason) /// /// The reason. /// - public TestCaseData Ignore(string reason) + public IgnoredTestCaseData Ignore(string reason) { + RunState prevRunState = this.RunState; this.RunState = RunState.Ignored; this.Properties.Set(PropertyNames.SkipReason, reason); - return this; + var ignoredData = new IgnoredTestCaseData(this, prevRunState); + return ignoredData; } #endregion diff --git a/src/NUnitFramework/framework/TestContext.cs b/src/NUnitFramework/framework/TestContext.cs index 8ee34fd67d..2886cb0fc5 100644 --- a/src/NUnitFramework/framework/TestContext.cs +++ b/src/NUnitFramework/framework/TestContext.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.Collections.Generic; using System.IO; @@ -40,8 +42,8 @@ namespace NUnit.Framework public class TestContext { private readonly TestExecutionContext _testExecutionContext; - private TestAdapter _test; - private ResultAdapter _result; + private TestAdapter? _test; + private ResultAdapter? _result; #region Constructor @@ -97,7 +99,7 @@ public static TextWriter Out /// of the public instance property WorkDirectory. This is /// a bit odd but necessary to avoid breaking user tests. /// - internal static string DefaultWorkDirectory; + internal static string? DefaultWorkDirectory; /// /// Get a representation of the current test. @@ -115,15 +117,13 @@ public ResultAdapter Result get { return _result ?? (_result = new ResultAdapter(_testExecutionContext.CurrentResult)); } } -#if PARALLEL /// /// Gets the unique name of the Worker that is executing this test. /// - public string WorkerId + public string? WorkerId { get { return _testExecutionContext.TestWorker?.Name; } } -#endif /// /// Gets the directory containing the current test assembly. @@ -132,20 +132,14 @@ public string TestDirectory { get { - Assembly assembly = _testExecutionContext?.CurrentTest?.TypeInfo?.Assembly; + Assembly? assembly = _testExecutionContext?.CurrentTest?.TypeInfo?.Assembly; if (assembly != null) return AssemblyHelper.GetDirectoryName(assembly); -#if NETSTANDARD1_4 - // Test is null, we may be loading tests rather than executing. - // Assume that the NUnit framework is in the same directory as the tests - return AssemblyHelper.GetDirectoryName(typeof(TestContext).GetTypeInfo().Assembly); -#else // Test is null, we may be loading tests rather than executing. // Assume that calling assembly is the test assembly. return AssemblyHelper.GetDirectoryName(Assembly.GetCallingAssembly()); -#endif } } @@ -153,13 +147,8 @@ public string TestDirectory /// Gets the directory to be used for outputting files created /// by this test run. /// - public string WorkDirectory - { - get - { - return DefaultWorkDirectory; - } - } + public string WorkDirectory => DefaultWorkDirectory + ?? throw new InvalidOperationException("TestContext.WorkDirectory must not be accessed before DefaultTestAssemblyBuilder.Build runs."); /// /// Gets the random generator. @@ -182,9 +171,8 @@ public int AssertCount } /// - /// Get the number of times the current Test has been repeated. This is currently only - /// set when using the . - /// TODO: add this to the RepeatAttribute as well + /// Get the number of times the current Test has been repeated + /// when using the or . /// public int CurrentRepeatCount { @@ -202,7 +190,7 @@ public int CurrentRepeatCount public static void Write(char value) { Out.Write(value); } /// Write a char array to the current result - public static void Write(char[] value) { Out.Write(value); } + public static void Write(char[]? value) { Out.Write(value); } /// Write the string representation of a double to the current result public static void Write(double value) { Out.Write(value); } @@ -217,13 +205,13 @@ public int CurrentRepeatCount public static void Write(decimal value) { Out.Write(value); } /// Write the string representation of an object to the current result - public static void Write(object value) { Out.Write(value); } + public static void Write(object? value) { Out.Write(value); } /// Write the string representation of a Single value to the current result public static void Write(Single value) { Out.Write(value); } /// Write a string to the current result - public static void Write(string value) { Out.Write(value); } + public static void Write(string? value) { Out.Write(value); } /// Write the string representation of a UInt32 value to the current result [CLSCompliant(false)] @@ -234,16 +222,16 @@ public int CurrentRepeatCount public static void Write(UInt64 value) { Out.Write(value); } /// Write a formatted string to the current result - public static void Write(string format, object arg1) { Out.Write(format, arg1); } + public static void Write(string format, object? arg1) { Out.Write(format, arg1); } /// Write a formatted string to the current result - public static void Write(string format, object arg1, object arg2) { Out.Write(format, arg1, arg2); } + public static void Write(string format, object? arg1, object? arg2) { Out.Write(format, arg1, arg2); } /// Write a formatted string to the current result - public static void Write(string format, object arg1, object arg2, object arg3) { Out.Write(format, arg1, arg2, arg3); } + public static void Write(string format, object? arg1, object? arg2, object? arg3) { Out.Write(format, arg1, arg2, arg3); } /// Write a formatted string to the current result - public static void Write(string format, params object[] args) { Out.Write(format, args); } + public static void Write(string format, params object?[] args) { Out.Write(format, args); } /// Write a line terminator to the current result public static void WriteLine() { Out.WriteLine(); } @@ -255,7 +243,7 @@ public int CurrentRepeatCount public static void WriteLine(char value) { Out.WriteLine(value); } /// Write a char array to the current result followed by a line terminator - public static void WriteLine(char[] value) { Out.WriteLine(value); } + public static void WriteLine(char[]? value) { Out.WriteLine(value); } /// Write the string representation of a double to the current result followed by a line terminator public static void WriteLine(double value) { Out.WriteLine(value); } @@ -270,13 +258,13 @@ public int CurrentRepeatCount public static void WriteLine(decimal value) { Out.WriteLine(value); } /// Write the string representation of an object to the current result followed by a line terminator - public static void WriteLine(object value) { Out.WriteLine(value); } + public static void WriteLine(object? value) { Out.WriteLine(value); } /// Write the string representation of a Single value to the current result followed by a line terminator public static void WriteLine(Single value) { Out.WriteLine(value); } /// Write a string to the current result followed by a line terminator - public static void WriteLine(string value) { Out.WriteLine(value); } + public static void WriteLine(string? value) { Out.WriteLine(value); } /// Write the string representation of a UInt32 value to the current result followed by a line terminator [CLSCompliant(false)] @@ -287,16 +275,16 @@ public int CurrentRepeatCount public static void WriteLine(UInt64 value) { Out.WriteLine(value); } /// Write a formatted string to the current result followed by a line terminator - public static void WriteLine(string format, object arg1) { Out.WriteLine(format, arg1); } + public static void WriteLine(string format, object? arg1) { Out.WriteLine(format, arg1); } /// Write a formatted string to the current result followed by a line terminator - public static void WriteLine(string format, object arg1, object arg2) { Out.WriteLine(format, arg1, arg2); } + public static void WriteLine(string format, object? arg1, object? arg2) { Out.WriteLine(format, arg1, arg2); } /// Write a formatted string to the current result followed by a line terminator - public static void WriteLine(string format, object arg1, object arg2, object arg3) { Out.WriteLine(format, arg1, arg2, arg3); } + public static void WriteLine(string format, object? arg1, object? arg2, object? arg3) { Out.WriteLine(format, arg1, arg2, arg3); } /// Write a formatted string to the current result followed by a line terminator - public static void WriteLine(string format, params object[] args) { Out.WriteLine(format, args); } + public static void WriteLine(string format, params object?[] args) { Out.WriteLine(format, args); } /// /// This method adds the a new ValueFormatterFactory to the @@ -314,7 +302,7 @@ public static void AddFormatter(ValueFormatterFactory formatterFactory) /// /// Relative or absolute file path to attachment /// Optional description of attachment - public static void AddTestAttachment(string filePath, string description = null) + public static void AddTestAttachment(string filePath, string? description = null) { Guard.ArgumentNotNull(filePath, nameof(filePath)); Guard.ArgumentValid(filePath.IndexOfAny(Path.GetInvalidPathChars()) == -1, @@ -391,14 +379,9 @@ public string Name /// /// The name of the method representing the test. /// - public string MethodName + public string? MethodName { - get - { - return _test is TestMethod - ? _test.Method.Name - : null; - } + get { return (_test as TestMethod)?.Method.Name; } } /// @@ -412,7 +395,7 @@ public string FullName /// /// The ClassName of the test /// - public string ClassName + public string? ClassName { get { return _test.ClassName; } } @@ -428,7 +411,7 @@ public PropertyBagAdapter Properties /// /// The arguments to use in creating the test or empty array if none are required. /// - public object[] Arguments + public object?[] Arguments { get { return _test.Arguments; } } @@ -485,7 +468,7 @@ public IEnumerable Assertions /// Gets the message associated with a test /// failure or with not running the test /// - public string Message + public string? Message { get { return _result.Message; } } @@ -494,7 +477,7 @@ public string Message /// Gets any stack trace associated with an /// error or failure. /// - public virtual string StackTrace + public virtual string? StackTrace { get { return _result.StackTrace; } } @@ -572,7 +555,7 @@ public PropertyBagAdapter(IPropertyBag source) /// Get the first property with the given , if it can be found, otherwise /// returns null. /// - public object Get(string key) + public object? Get(string key) { return _source.Get(key); } diff --git a/src/NUnitFramework/framework/TestFixtureData.cs b/src/NUnitFramework/framework/TestFixtureData.cs index 7cadefe2f7..36391adad0 100644 --- a/src/NUnitFramework/framework/TestFixtureData.cs +++ b/src/NUnitFramework/framework/TestFixtureData.cs @@ -21,7 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -using System; +#nullable enable + using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -42,8 +43,8 @@ public class TestFixtureData : TestFixtureParameters /// Initializes a new instance of the class. /// /// The arguments. - public TestFixtureData(params object[] args) - : base(args == null ? new object[] { null } : args) + public TestFixtureData(params object?[]? args) + : base(args == null ? new object?[] { null } : args) { } @@ -51,8 +52,8 @@ public TestFixtureData(params object[] args) /// Initializes a new instance of the class. /// /// The argument. - public TestFixtureData(object arg) - : base(new object[] { arg }) + public TestFixtureData(object? arg) + : base(new object?[] { arg }) { } @@ -61,8 +62,8 @@ public TestFixtureData(object arg) /// /// The first argument. /// The second argument. - public TestFixtureData(object arg1, object arg2) - : base(new object[] { arg1, arg2 }) + public TestFixtureData(object? arg1, object? arg2) + : base(new object?[] { arg1, arg2 }) { } @@ -72,8 +73,8 @@ public TestFixtureData(object arg1, object arg2) /// The first argument. /// The second argument. /// The third argument. - public TestFixtureData(object arg1, object arg2, object arg3) - : base( new object[] { arg1, arg2, arg3 }) + public TestFixtureData(object? arg1, object? arg2, object? arg3) + : base(new object?[] { arg1, arg2, arg3 }) { } @@ -85,7 +86,7 @@ public TestFixtureData(object arg1, object arg2, object arg3) /// Sets the name of the test fixture /// /// The modified TestFixtureData instance - internal TestFixtureData SetName(string name) + internal TestFixtureData SetName(string? name) { TestName = name; return this; @@ -94,7 +95,7 @@ internal TestFixtureData SetName(string name) /// /// Sets the list of display names to use as the parameters in the test name. /// - public TestFixtureData SetArgDisplayNames(params string[] displayNames) + public TestFixtureData SetArgDisplayNames(params string[]? displayNames) { ArgDisplayNames = displayNames; return this; diff --git a/src/NUnitFramework/framework/TestParameters.cs b/src/NUnitFramework/framework/TestParameters.cs index c5e2528dc5..d29de2c6e7 100644 --- a/src/NUnitFramework/framework/TestParameters.cs +++ b/src/NUnitFramework/framework/TestParameters.cs @@ -1,5 +1,31 @@ -using System; +// *********************************************************************** +// Copyright (c) 2016 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +#nullable enable + +using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; namespace NUnit.Framework @@ -44,7 +70,7 @@ public bool Exists(string name) /// /// Name of the parameter /// Value of the parameter or null if not present - public string this[string name] + public string? this[string name] { get { return Get(name); } } @@ -54,7 +80,7 @@ public bool Exists(string name) /// /// Name of the parameter /// Value of the parameter or null if not present - public string Get(string name) + public string? Get(string name) { return Exists(name) ? _parameters[name] : null; } @@ -65,7 +91,8 @@ public string Get(string name) /// Name of the parameter /// Default value of the parameter /// Value of the parameter or default value if not present - public string Get(string name, string defaultValue) + [return: NotNullIfNotNull("defaultValue")] + public string? Get(string name, string? defaultValue) { return Get(name) ?? defaultValue; } @@ -77,9 +104,10 @@ public string Get(string name, string defaultValue) /// Name of the parameter /// Default value of the parameter /// Value of the parameter or default value if not present - public T Get(string name, T defaultValue) + [return: NotNullIfNotNull("defaultValue")] + public T Get(string name, [MaybeNull] T defaultValue) { - string val = Get(name); + string? val = Get(name); return val != null ? (T)Convert.ChangeType(val, typeof(T), MODIFIED_INVARIANT_CULTURE) : defaultValue; } @@ -106,4 +134,4 @@ private static IFormatProvider CreateModifiedInvariantCulture() return culture; } } -} \ No newline at end of file +} diff --git a/src/NUnitFramework/framework/Warn.cs b/src/NUnitFramework/framework/Warn.cs index 799ece3fe0..e835206b91 100644 --- a/src/NUnitFramework/framework/Warn.cs +++ b/src/NUnitFramework/framework/Warn.cs @@ -21,6 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +#nullable enable + using System; using System.ComponentModel; using NUnit.Framework.Constraints; @@ -91,7 +93,7 @@ public static void Unless(ActualValueDelegate del, IResolveCon /// A Constraint expression to be applied /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void Unless(ActualValueDelegate del, IResolveConstraint expr, string message, params object[] args) + public static void Unless(ActualValueDelegate del, IResolveConstraint expr, string? message, params object?[]? args) { var constraint = expr.Resolve(); @@ -102,7 +104,7 @@ public static void Unless(ActualValueDelegate del, IResolveCon IssueWarning(result, message, args); } - private static void IssueWarning(ConstraintResult result, string message, object[] args) + private static void IssueWarning(ConstraintResult result, string? message, object?[]? args) { MessageWriter writer = new TextMessageWriter(message, args); result.WriteMessageTo(writer); @@ -120,7 +122,7 @@ private static void IssueWarning(ConstraintResult result, string message, object public static void Unless( ActualValueDelegate del, IResolveConstraint expr, - Func getExceptionMessage) + Func getExceptionMessage) { var constraint = expr.Resolve(); @@ -136,18 +138,18 @@ private static void IssueWarning(ConstraintResult result, string message, object #region Boolean /// - /// Asserts that a condition is true. If the condition is false a warning is issued. + /// Asserts that a condition is true. If the condition is false, a warning is issued. /// /// The evaluated condition /// The message to display if the condition is false /// Arguments to be used in formatting the message - public static void Unless(bool condition, string message, params object[] args) + public static void Unless(bool condition, string? message, params object?[]? args) { Warn.Unless(condition, Is.True, message, args); } /// - /// Asserts that a condition is true. If the condition is false a warning is issued. + /// Asserts that a condition is true. If the condition is false, a warning is issued. /// /// The evaluated condition public static void Unless(bool condition) @@ -156,11 +158,11 @@ public static void Unless(bool condition) } /// - /// Asserts that a condition is true. If the condition is false a warning is issued. + /// Asserts that a condition is true. If the condition is false, a warning is issued. /// /// The evaluated condition /// A function to build the message included with the Exception - public static void Unless(bool condition, Func getExceptionMessage) + public static void Unless(bool condition, Func getExceptionMessage) { Warn.Unless(condition, Is.True, getExceptionMessage); } @@ -170,20 +172,18 @@ public static void Unless(bool condition, Func getExceptionMessage) #region Lambda returning Boolean /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. If the condition is false, a warning is issued. /// /// A lambda that returns a Boolean /// The message to display if the condition is false /// Arguments to be used in formatting the message - public static void Unless(Func condition, string message, params object[] args) + public static void Unless(Func condition, string? message, params object?[]? args) { Warn.Unless(condition.Invoke(), Is.True, message, args); } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. If the condition is false, a warning is issued. /// /// A lambda that returns a Boolean public static void Unless(Func condition) @@ -192,12 +192,11 @@ public static void Unless(Func condition) } /// - /// Asserts that a condition is true. If the condition is false the method throws - /// an . + /// Asserts that a condition is true. If the condition is false, a warning is issued. /// /// A lambda that returns a Boolean /// A function to build the message included with the Exception - public static void Unless(Func condition, Func getExceptionMessage) + public static void Unless(Func condition, Func getExceptionMessage) { Warn.Unless(condition.Invoke(), Is.True, getExceptionMessage); } @@ -242,7 +241,7 @@ public static void Unless(TActual actual, IResolveConstraint expression /// A Constraint expression to be applied /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void Unless(TActual actual, IResolveConstraint expression, string message, params object[] args) + public static void Unless(TActual actual, IResolveConstraint expression, string? message, params object?[]? args) { var constraint = expression.Resolve(); @@ -264,7 +263,7 @@ public static void Unless(TActual actual, IResolveConstraint expression public static void Unless( TActual actual, IResolveConstraint expression, - Func getExceptionMessage) + Func getExceptionMessage) { var constraint = expression.Resolve(); @@ -304,7 +303,7 @@ public static void If(ActualValueDelegate del, IResolveConstra /// A Constraint expression to be applied /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void If(ActualValueDelegate del, IResolveConstraint expr, string message, params object[] args) + public static void If(ActualValueDelegate del, IResolveConstraint expr, string? message, params object?[]? args) { var constraint = new NotConstraint(expr.Resolve()); @@ -315,7 +314,7 @@ public static void If(ActualValueDelegate del, IResolveConstra IssueWarning(result, message, args); } - //private static void IssueWarning(ConstraintResult result, string message, object[] args) + //private static void IssueWarning(ConstraintResult result, string? message, object?[]? args) //{ // MessageWriter writer = new TextMessageWriter(message, args); // result.WriteMessageTo(writer); @@ -333,7 +332,7 @@ public static void If(ActualValueDelegate del, IResolveConstra public static void If( ActualValueDelegate del, IResolveConstraint expr, - Func getExceptionMessage) + Func getExceptionMessage) { var constraint = new NotConstraint(expr.Resolve()); @@ -349,18 +348,18 @@ public static void If(ActualValueDelegate del, IResolveConstra #region Boolean /// - /// Asserts that a condition is true. If the condition is false a warning is issued. + /// Asserts that a condition is true. If the condition is false, a warning is issued. /// /// The evaluated condition /// The message to display if the condition is false /// Arguments to be used in formatting the message - public static void If(bool condition, string message, params object[] args) + public static void If(bool condition, string? message, params object?[]? args) { Warn.If(condition, Is.True, message, args); } /// - /// Asserts that a condition is true. If the condition is false a warning is issued. + /// Asserts that a condition is true. If the condition is false, a warning is issued. /// /// The evaluated condition public static void If(bool condition) @@ -369,11 +368,11 @@ public static void If(bool condition) } /// - /// Asserts that a condition is true. If the condition is false a warning is issued. + /// Asserts that a condition is true. If the condition is false, a warning is issued. /// /// The evaluated condition /// A function to build the message included with the Exception - public static void If(bool condition, Func getExceptionMessage) + public static void If(bool condition, Func getExceptionMessage) { Warn.If(condition, Is.True, getExceptionMessage); } @@ -388,7 +387,7 @@ public static void If(bool condition, Func getExceptionMessage) /// A lambda that returns a Boolean /// The message to display if the condition is true /// Arguments to be used in formatting the message - public static void If(Func condition, string message, params object[] args) + public static void If(Func condition, string? message, params object?[]? args) { Warn.If(condition.Invoke(), Is.True, message, args); } @@ -407,7 +406,7 @@ public static void If(Func condition) /// /// A lambda that returns a Boolean /// A function to build the message included with the Exception - public static void If(Func condition, Func getExceptionMessage) + public static void If(Func condition, Func getExceptionMessage) { Warn.If(condition.Invoke(), Is.True, getExceptionMessage); } @@ -437,7 +436,7 @@ public static void If(TActual actual, IResolveConstraint expression) /// A Constraint expression to be applied /// The message that will be displayed on failure /// Arguments to be used in formatting the message - public static void If(TActual actual, IResolveConstraint expression, string message, params object[] args) + public static void If(TActual actual, IResolveConstraint expression, string? message, params object?[]? args) { var constraint = new NotConstraint(expression.Resolve()); @@ -459,7 +458,7 @@ public static void If(TActual actual, IResolveConstraint expression, st public static void If( TActual actual, IResolveConstraint expression, - Func getExceptionMessage) + Func getExceptionMessage) { var constraint = new NotConstraint(expression.Resolve()); diff --git a/src/NUnitFramework/framework/nunit.framework.csproj b/src/NUnitFramework/framework/nunit.framework.csproj index 0059668395..4926ad1257 100644 --- a/src/NUnitFramework/framework/nunit.framework.csproj +++ b/src/NUnitFramework/framework/nunit.framework.csproj @@ -1,26 +1,35 @@ - + - net35;net40;net45;netstandard1.4;netstandard2.0 + net35;net40;net45;netstandard2.0 NUnit.Framework true - - + + + - - - + + + + + + - + - + + + + + + diff --git a/src/NUnitFramework/mock-assembly/MockAssembly.cs b/src/NUnitFramework/mock-assembly/MockAssembly.cs index 23d6033adc..a5a105b726 100644 --- a/src/NUnitFramework/mock-assembly/MockAssembly.cs +++ b/src/NUnitFramework/mock-assembly/MockAssembly.cs @@ -100,11 +100,7 @@ public class MockAssembly public const int Inconclusive = MockTestFixture.Inconclusive; -#if NETCOREAPP1_1 - public static readonly Assembly ThisAssembly = typeof(MockAssembly).GetTypeInfo().Assembly; -#else public static readonly Assembly ThisAssembly = typeof(MockAssembly).Assembly; -#endif public static readonly string AssemblyPath = AssemblyHelper.GetAssemblyPath(ThisAssembly); public static void Main(string[] args) diff --git a/src/NUnitFramework/mock-assembly/mock-assembly.csproj b/src/NUnitFramework/mock-assembly/mock-assembly.csproj index 3ce7bbbff9..7fe2364419 100644 --- a/src/NUnitFramework/mock-assembly/mock-assembly.csproj +++ b/src/NUnitFramework/mock-assembly/mock-assembly.csproj @@ -1,11 +1,8 @@  - net35;net40;net45;netcoreapp1.1;netcoreapp2.0 + net35;netcoreapp2.1 NUnit.Tests - - - false @@ -17,4 +14,9 @@ + + + + + diff --git a/src/NUnitFramework/nunitlite-runner/nunitlite-runner.csproj b/src/NUnitFramework/nunitlite-runner/nunitlite-runner.csproj index 9a70a7dfb4..60511d6cf1 100644 --- a/src/NUnitFramework/nunitlite-runner/nunitlite-runner.csproj +++ b/src/NUnitFramework/nunitlite-runner/nunitlite-runner.csproj @@ -1,14 +1,41 @@ - + + + true + $(DefineConstants);UseWindowsFormsAndWPF + true + true + + + + + + net35;net40;net45;netcoreapp2.1;netcoreapp3.1;net5.0-windows + + + + net35;net45;netcoreapp2.1;netcoreapp3.1;net5.0 + - net35;net40;net45;netcoreapp1.1;netcoreapp2.0 Exe NUnitLite + + + + + + + + + + + + diff --git a/src/NUnitFramework/nunitlite.tests/CommandLineTests.cs b/src/NUnitFramework/nunitlite.tests/CommandLineTests.cs index f3cff7983f..0e54b7fb10 100644 --- a/src/NUnitFramework/nunitlite.tests/CommandLineTests.cs +++ b/src/NUnitFramework/nunitlite.tests/CommandLineTests.cs @@ -265,13 +265,10 @@ public void CanRecognizeLowerCaseOptionValues(string propertyName, string option Assert.AreEqual(canonicalValue, (string)property.GetValue(options, null), "Didn't recognize " + optionPlusValue); } } -#if !NETCOREAPP1_1 + [TestCase("DefaultTimeout", "timeout")] -#endif [TestCase("RandomSeed", "seed")] -#if PARALLEL [TestCase("NumberOfTestWorkers", "workers")] -#endif public void CanRecognizeIntOptions(string propertyName, string pattern) { string[] prototypes = pattern.Split('|'); @@ -331,9 +328,7 @@ public void CanRecognizeTestSelectionOptions(string propertyName, string args, p [TestCase("--trace")] [TestCase("--test")] [TestCase("--prefilter")] -#if !NETCOREAPP1_1 [TestCase("--timeout")] -#endif public void MissingValuesAreReported(string option) { var options = new NUnitLiteOptions(option + "="); @@ -407,7 +402,7 @@ public void TimeoutIsMinusOneIfNoOptionIsProvided() Assert.True(options.Validate()); Assert.AreEqual(-1, options.DefaultTimeout); } -#if !NETCOREAPP1_1 + [Test] public void TimeoutThrowsExceptionIfOptionHasNoValue() { @@ -429,7 +424,6 @@ public void TimeoutCausesErrorIfValueIsNotInteger() Assert.False(options.Validate()); Assert.AreEqual(-1, options.DefaultTimeout); } -#endif #endregion @@ -518,7 +512,7 @@ public void NoResultSuppressesAllResultSpecifications() Assert.AreEqual(0, options.ResultOutputSpecifications.Count); } - [Test] + [Test, SetCulture("en-US")] public void InvalidResultSpecRecordsError() { var options = new NUnitLiteOptions("test.dll", "-result:userspecifed.xml;format=nunit2;format=nunit3"); diff --git a/src/NUnitFramework/nunitlite.tests/MakeRunSettingsTests.cs b/src/NUnitFramework/nunitlite.tests/MakeRunSettingsTests.cs index de70733dbc..81a56e2278 100644 --- a/src/NUnitFramework/nunitlite.tests/MakeRunSettingsTests.cs +++ b/src/NUnitFramework/nunitlite.tests/MakeRunSettingsTests.cs @@ -31,15 +31,14 @@ public class MakeRunSettingsTests { private static TestCaseData[] SettingsData = { -#if !NETCOREAPP1_1 new TestCaseData("--timeout=50", "DefaultTimeout", 50), -#endif new TestCaseData("--work=results", "WorkDirectory", Path.GetFullPath("results")), new TestCaseData("--seed=1234", "RandomSeed", 1234), new TestCaseData("--workers=8", "NumberOfTestWorkers", 8), new TestCaseData("--prefilter=A.B.C", "LOAD", new string[] { "A.B.C" }), new TestCaseData("--test=A.B.C", "LOAD", new string[] { "A.B.C" }), new TestCaseData("--test=A.B.C(arg)", "LOAD", new string[] { "A.B.C" }), + new TestCaseData("--test=A.B.C (arg)", "LOAD", new string[] { "A.B.C" }), new TestCaseData("--test=A.B.C(arg)", "LOAD", new string[] { "A.B" }) }; diff --git a/src/NUnitFramework/nunitlite.tests/NUnit2XmlOutputWriterTests.cs b/src/NUnitFramework/nunitlite.tests/NUnit2XmlOutputWriterTests.cs index 4565d3af9c..aaa90ff30f 100644 --- a/src/NUnitFramework/nunitlite.tests/NUnit2XmlOutputWriterTests.cs +++ b/src/NUnitFramework/nunitlite.tests/NUnit2XmlOutputWriterTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !NETCOREAPP1_1 using System; using System.IO; using System.Text; @@ -127,16 +126,14 @@ public void TestResults_CounterIsCorrect(string name, int count) public void TestResults_HasValidDateAttribute() { string dateString = RequiredAttribute(topNode, "date"); - DateTime date; - Assert.That(DateTime.TryParse(dateString, out date), "Invalid date attribute: {0}", dateString); + Assert.That(DateTime.TryParse(dateString, out _), "Invalid date attribute: {0}", dateString); } [Test] public void TestResults_HasValidTimeAttribute() { string timeString = RequiredAttribute(topNode, "time"); - DateTime time; - Assert.That(DateTime.TryParse(timeString, out time), "Invalid time attribute: {0}", timeString); + Assert.That(DateTime.TryParse(timeString, out _), "Invalid time attribute: {0}", timeString); } [Test] @@ -204,18 +201,12 @@ public void TestSuite_ExpectedAttribute(string name, string value) [Test] public void TestSuite_HasValidTimeAttribute() { - double time; var timeString = RequiredAttribute(suiteNode, "time"); // NOTE: We use the TryParse overload with 4 args because it's supported in .NET 1.1 - var success = double.TryParse(timeString,System.Globalization.NumberStyles.Float,System.Globalization.NumberFormatInfo.InvariantInfo, out time); + var success = double.TryParse(timeString, System.Globalization.NumberStyles.Float, System.Globalization.NumberFormatInfo.InvariantInfo, out _); Assert.That(success, "{0} is an invalid value for time", timeString); } - [Test] - public void TestSuite_ResultIsFailure() - { - } - #region Helper Methods private string RequiredAttribute(XmlNode node, string name) @@ -229,4 +220,3 @@ private string RequiredAttribute(XmlNode node, string name) #endregion } } -#endif diff --git a/src/NUnitFramework/nunitlite.tests/NUnit3XmlOutputWriterTests.cs b/src/NUnitFramework/nunitlite.tests/NUnit3XmlOutputWriterTests.cs new file mode 100644 index 0000000000..042ee4516d --- /dev/null +++ b/src/NUnitFramework/nunitlite.tests/NUnit3XmlOutputWriterTests.cs @@ -0,0 +1,224 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +#if !NETCOREAPP1_1 +using System; +using System.Globalization; +using System.IO; +using System.Text; +using System.Xml; +using NUnit.Framework; +using NUnit.Framework.Interfaces; +using NUnit.Tests.Assemblies; + +namespace NUnitLite.Tests +{ + public class NUnit3XmlOutputWriterTests + { + private XmlDocument doc; + private XmlNode topNode; + private XmlNode envNode; + private XmlNode suiteNode; + + [OneTimeSetUp] + public void RunMockAssemblyTests() + { + ITestResult result = NUnit.TestUtilities.TestBuilder.RunTestFixture(typeof(MockTestFixture)); + Assert.NotNull(result); + + StringBuilder sb = new StringBuilder(); + StringWriter writer = new StringWriter(sb); + new NUnit3XmlOutputWriter().WriteResultFile(result, writer, null, null); + writer.Close(); + +#if DEBUG + StreamWriter sw = new StreamWriter("MockAssemblyResult.xml"); + sw.WriteLine(sb.ToString()); + sw.Close(); +#endif + + doc = new XmlDocument(); + doc.LoadXml(sb.ToString()); + + topNode = doc.SelectSingleNode("/test-run"); + if (topNode != null) + { + suiteNode = topNode.SelectSingleNode("test-suite"); + envNode = suiteNode.SelectSingleNode("environment"); + } + } + + [Test] + public void Document_HasTwoChildren() + { + Assert.That(doc.ChildNodes.Count, Is.EqualTo(2)); + } + + [Test] + public void Document_FirstChildIsXmlDeclaration() + { + Assume.That(doc.FirstChild != null); + Assert.That(doc.FirstChild.NodeType, Is.EqualTo(XmlNodeType.XmlDeclaration)); + Assert.That(doc.FirstChild.Name, Is.EqualTo("xml")); + } + + [Test] + public void Document_SecondChildIsComment() + { + Assume.That(doc.ChildNodes.Count >= 2); + Assert.That(doc.ChildNodes[1].Name, Is.EqualTo("test-run")); + } + + [Test] + public void Document_HasTestResults() + { + Assert.That(topNode, Is.Not.Null); + Assert.That(topNode.Name, Is.EqualTo("test-run")); + } + + [Test] + public void TestResults_AssemblyPathIsCorrect() + { + Assert.That(RequiredAttribute(topNode, "fullname"), Is.EqualTo("NUnit.Tests.Assemblies.MockTestFixture")); + Assert.That(RequiredAttribute(topNode, "name"), Is.EqualTo("MockTestFixture")); + } + + [TestCase("testcasecount", MockTestFixture.Tests)] + [TestCase("passed", MockTestFixture.Passed)] + [TestCase("failed", MockTestFixture.Failed)] + [TestCase("inconclusive", MockTestFixture.Inconclusive)] + [TestCase("skipped", MockTestFixture.Skipped)] + public void TestResults_CounterIsCorrect(string name, int count) + { + Assert.That(RequiredAttribute(topNode, name), Is.EqualTo(count.ToString())); + } + + [Test] + public void TestResults_HasValidStartTimeAttribute() + { + string startTimeString = RequiredAttribute(topNode, "start-time"); + Assert.That(DateTime.TryParseExact(startTimeString, "o", CultureInfo.InvariantCulture, DateTimeStyles.None, out _), "Invalid start time attribute: {0}. Expecting DateTime in 'o' format.", startTimeString); + } + + [Test] + public void TestResults_HasValidEndTimeAttribute() + { + string endTimeString = RequiredAttribute(topNode, "end-time"); + Assert.That(DateTime.TryParseExact(endTimeString, "o", CultureInfo.InvariantCulture, DateTimeStyles.None, out _), "Invalid end time attribute: {0}. Expecting DateTime in 'o' format.", endTimeString); + } + + [Test] + public void Environment_HasEnvironmentElement() + { + Assert.That(envNode, Is.Not.Null, "Missing environment element"); + } + + [TestCase("framework-version")] + [TestCase("clr-version")] + [TestCase("os-version")] + [TestCase("platform")] + [TestCase("cwd")] + [TestCase("machine-name")] + [TestCase("user")] + [TestCase("user-domain")] + [TestCase("os-architecture")] + public void Environment_HasRequiredAttribute(string name) + { + RequiredAttribute(envNode, name); + } + + [Test] + public void CultureInfo_HasCultureInfoElement() + { + Assert.That(envNode.Attributes["culture"], Is.Not.Null, "Missing culture-info attribute"); + } + + [TestCase("culture")] + [TestCase("uiculture")] + public void CultureInfo_HasRequiredAttribute(string name) + { + string cultureName = RequiredAttribute(envNode, name); + System.Globalization.CultureInfo culture = null; + + try + { + culture = System.Globalization.CultureInfo.CreateSpecificCulture(cultureName); + } + catch (ArgumentException) + { + // Do nothing - culture will be null + } + + Assert.That(culture, Is.Not.Null, "Invalid value for {0}: {1}", name, cultureName); + } + + [Test] + public void TestSuite_HasTestSuiteElement() + { + Assert.That(suiteNode, Is.Not.Null, "Missing test-suite element"); + } + + [TestCase("type", "TestFixture")] + [TestCase("name", "MockTestFixture")] + [TestCase("fullname", "NUnit.Tests.Assemblies.MockTestFixture")] + [TestCase("classname", "NUnit.Tests.Assemblies.MockTestFixture")] + [TestCase("runstate", "Runnable")] + [TestCase("result", "Failed")] + [TestCase("asserts", "0")] + public void TestSuite_ExpectedAttribute(string name, string value) + { + Assert.That(RequiredAttribute(suiteNode, name), Is.EqualTo(value)); + } + + [Test] + public void TestSuite_HasValidStartTimeAttribute() + { + var startTimeString = RequiredAttribute(suiteNode, "start-time"); + + var success = DateTime.TryParse(startTimeString, out _); + Assert.That(success, "{0} is an invalid value for start time", startTimeString); + } + + [Test] + public void TestSuite_HasValidEndTimeAttribute() + { + var endTimeString = RequiredAttribute(suiteNode, "end-time"); + + var success = DateTime.TryParse(endTimeString, out _); + Assert.That(success, "{0} is an invalid value for end time", endTimeString); + } + + #region Helper Methods + + private string RequiredAttribute(XmlNode node, string name) + { + XmlAttribute attr = node.Attributes[name]; + Assert.That(attr, Is.Not.Null, "Missing attribute {0} on element {1}", name, node.Name); + + return attr.Value; + } + + #endregion + } +} +#endif diff --git a/src/NUnitFramework/nunitlite.tests/Program.cs b/src/NUnitFramework/nunitlite.tests/Program.cs index 77063b4331..7ccde3c80d 100644 --- a/src/NUnitFramework/nunitlite.tests/Program.cs +++ b/src/NUnitFramework/nunitlite.tests/Program.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Reflection; using System.Text; @@ -10,11 +10,7 @@ class Program { public static int Main(string[] args) { -#if NETCOREAPP1_1 - return new AutoRun(Assembly.GetEntryAssembly()).Execute(args, new ColorConsoleWriter(), Console.In); -#else return new AutoRun().Execute(args); -#endif } } } diff --git a/src/NUnitFramework/nunitlite.tests/SchemaTests.cs b/src/NUnitFramework/nunitlite.tests/SchemaTests.cs index 8c9be5b4c6..913064b19e 100644 --- a/src/NUnitFramework/nunitlite.tests/SchemaTests.cs +++ b/src/NUnitFramework/nunitlite.tests/SchemaTests.cs @@ -22,7 +22,6 @@ // *********************************************************************** -#if !NETCOREAPP1_1 // Schema validation doesn’t exist #if !NET35 // Framework bug causes NRE: https://social.msdn.microsoft.com/Forums/en-US/53be44de-30b2-4d18-968d-d3414d0783b1 // We don’t really need these tests to run on more than one platform. @@ -70,4 +69,3 @@ public static void TestResultSchemaMatches() } } #endif -#endif diff --git a/src/NUnitFramework/nunitlite.tests/TestSelectionParserTests.cs b/src/NUnitFramework/nunitlite.tests/TestSelectionParserTests.cs index 5b4863cbe7..dbd305cd7a 100644 --- a/src/NUnitFramework/nunitlite.tests/TestSelectionParserTests.cs +++ b/src/NUnitFramework/nunitlite.tests/TestSelectionParserTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -77,12 +77,8 @@ public void TestParser(string input, string output) { Assert.That(_parser.Parse(input), Is.EqualTo(output)); -#if NETCOREAPP1_1 - Assert.DoesNotThrow(() => System.Xml.Linq.XDocument.Parse(output)); -#else XmlDocument doc = new XmlDocument(); Assert.DoesNotThrow(() => doc.LoadXml(output)); -#endif } [TestCase(null, typeof(ArgumentNullException))] diff --git a/src/NUnitFramework/nunitlite.tests/nunitlite.tests.csproj b/src/NUnitFramework/nunitlite.tests/nunitlite.tests.csproj index e79407c21e..084f088b4e 100644 --- a/src/NUnitFramework/nunitlite.tests/nunitlite.tests.csproj +++ b/src/NUnitFramework/nunitlite.tests/nunitlite.tests.csproj @@ -1,10 +1,16 @@ - + - net35;net40;net45;netcoreapp1.1;netcoreapp2.0 Exe NUnitLite.Tests - Full + + + + net35;net40;net45;netcoreapp2.1;netcoreapp3.1;net5.0 + + + + net35;net45;netcoreapp2.1;netcoreapp3.1;net5.0 @@ -12,6 +18,11 @@ + + + + + diff --git a/src/NUnitFramework/nunitlite/AutoRun.cs b/src/NUnitFramework/nunitlite/AutoRun.cs index cd7cd1d1d2..905d459765 100644 --- a/src/NUnitFramework/nunitlite/AutoRun.cs +++ b/src/NUnitFramework/nunitlite/AutoRun.cs @@ -65,12 +65,10 @@ public AutoRun(Assembly testAssembly) _testAssembly = testAssembly; } -#if !NETSTANDARD1_4 /// /// Default Constructor, only used where GetCallingAssembly is available /// public AutoRun() : this(Assembly.GetCallingAssembly()) { } -#endif /// /// Execute the tests in the assembly, passing in diff --git a/src/NUnitFramework/nunitlite/CommandLineOptions.cs b/src/NUnitFramework/nunitlite/CommandLineOptions.cs index d6996413a6..f0f64107d9 100644 --- a/src/NUnitFramework/nunitlite/CommandLineOptions.cs +++ b/src/NUnitFramework/nunitlite/CommandLineOptions.cs @@ -291,8 +291,7 @@ protected string RequiredValue(string val, string option, params string[] validV protected int RequiredInt(string val, string option) { - int result; - if (int.TryParse(val, out result)) return result; + if (int.TryParse(val, out var result)) return result; ErrorMessages.Add(string.IsNullOrEmpty(val) ? "Missing required value for option '" + option + "'." @@ -382,10 +381,9 @@ protected virtual void ConfigureOptions(bool allowInputFile) } } }); -#if PARALLEL this.Add("timeout=", "Set timeout for each test case in {MILLISECONDS}.", v => DefaultTimeout = RequiredInt(v, "--timeout")); -#endif + this.Add("seed=", "Set the random {SEED} used to generate test cases.", v => RandomSeed = RequiredInt(v, "--seed")); diff --git a/src/NUnitFramework/nunitlite/DebugWriter.cs b/src/NUnitFramework/nunitlite/DebugWriter.cs index 9fdbe207b8..7c53a4325a 100644 --- a/src/NUnitFramework/nunitlite/DebugWriter.cs +++ b/src/NUnitFramework/nunitlite/DebugWriter.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -28,16 +28,17 @@ namespace NUnitLite { /// - /// DebugWriter is a TextWriter that sends its + /// DebugWriter is a TextWriter that sends its /// output to Debug. We don't use Trace because /// writing to it is not supported in CF. /// + [Obsolete("No longer used")] public class DebugWriter : TextWriter { private static TextWriter writer; /// - /// Singleon instance of a DebugWriter. + /// Singleton instance of a DebugWriter. /// /// The DebugWriter singleton. public static TextWriter Out diff --git a/src/NUnitFramework/nunitlite/Options.cs b/src/NUnitFramework/nunitlite/Options.cs index 5e5bcb34af..a7367c1c40 100644 --- a/src/NUnitFramework/nunitlite/Options.cs +++ b/src/NUnitFramework/nunitlite/Options.cs @@ -133,9 +133,10 @@ using System.Collections.Generic; using System.Collections.ObjectModel; using System.ComponentModel; -using System.Globalization; using System.IO; using System.Reflection; +using System.Runtime.Serialization; +using System.Security.Permissions; using System.Text; using System.Text.RegularExpressions; using NUnit.Compatibility; @@ -143,10 +144,6 @@ // Missing XML Docs #pragma warning disable 1591 -#if !NETSTANDARD1_4 -using System.Security.Permissions; -using System.Runtime.Serialization; -#endif namespace NUnit.Options { @@ -352,17 +349,11 @@ protected static T Parse (string value, OptionContext c) tt.GetGenericTypeDefinition () == typeof (Nullable<>); Type targetType = nullable ? tt.GetGenericArguments () [0] : typeof (T); -#if !NETSTANDARD1_4 TypeConverter conv = TypeDescriptor.GetConverter (targetType); -#endif T t = default (T); try { if (value != null) -#if NETSTANDARD1_4 - t = (T)Convert.ChangeType(value, tt, CultureInfo.InvariantCulture); -#else t = (T) conv.ConvertFromString (value); -#endif } catch (Exception e) { throw new OptionException ( @@ -489,26 +480,22 @@ public OptionException (string message, string optionName, Exception innerExcept this.option = optionName; } -#if SERIALIZATION protected OptionException (SerializationInfo info, StreamingContext context) : base (info, context) { this.option = info.GetString ("OptionName"); } -#endif public string OptionName { get {return this.option;} } -#if SERIALIZATION [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)] public override void GetObjectData (SerializationInfo info, StreamingContext context) { base.GetObjectData (info, context); info.AddValue ("OptionName", option); } -#endif } public delegate void OptionAction (TKey key, TValue value); diff --git a/src/NUnitFramework/nunitlite/OutputWriters/NUnit2XmlOutputWriter.cs b/src/NUnitFramework/nunitlite/OutputWriters/NUnit2XmlOutputWriter.cs index 1304ea9e9b..661e9def3c 100644 --- a/src/NUnitFramework/nunitlite/OutputWriters/NUnit2XmlOutputWriter.cs +++ b/src/NUnitFramework/nunitlite/OutputWriters/NUnit2XmlOutputWriter.cs @@ -118,36 +118,25 @@ private void WriteEnvironment() var assemblyName = AssemblyHelper.GetAssemblyName(typeof(NUnit2XmlOutputWriter).GetTypeInfo().Assembly); xmlWriter.WriteAttributeString("nunit-version", assemblyName.Version.ToString()); -#if NETSTANDARD1_4 - xmlWriter.WriteAttributeString("clr-version", - System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription); -#else xmlWriter.WriteAttributeString("clr-version", Environment.Version.ToString()); -#endif -#if NETSTANDARD1_4 || NETSTANDARD2_0 +#if NETSTANDARD2_0 xmlWriter.WriteAttributeString("os-version", System.Runtime.InteropServices.RuntimeInformation.OSDescription); #else xmlWriter.WriteAttributeString("os-version", OSPlatform.CurrentPlatform.ToString()); #endif -#if !NETSTANDARD1_4 xmlWriter.WriteAttributeString("platform", Environment.OSVersion.Platform.ToString()); -#endif xmlWriter.WriteAttributeString("cwd", Directory.GetCurrentDirectory()); -#if !NETSTANDARD1_4 xmlWriter.WriteAttributeString("machine-name", Environment.MachineName); -#endif -#if !NETSTANDARD1_4 xmlWriter.WriteAttributeString("user", Environment.UserName); xmlWriter.WriteAttributeString("user-domain", Environment.UserDomainName); -#endif xmlWriter.WriteEndElement(); } diff --git a/src/NUnitFramework/nunitlite/OutputWriters/NUnit3XmlOutputWriter.cs b/src/NUnitFramework/nunitlite/OutputWriters/NUnit3XmlOutputWriter.cs index 78875338c2..33c617488c 100644 --- a/src/NUnitFramework/nunitlite/OutputWriters/NUnit3XmlOutputWriter.cs +++ b/src/NUnitFramework/nunitlite/OutputWriters/NUnit3XmlOutputWriter.cs @@ -86,9 +86,7 @@ private void WriteXmlResultOutput(ITestResult result, XmlWriter xmlWriter, IDict TNode testRun = MakeTestRunElement(result); -#if !NETSTANDARD1_4 testRun.ChildNodes.Add(MakeCommandLineElement()); -#endif testRun.ChildNodes.Add(MakeTestFilterElement(filter)); testRun.ChildNodes.Add(resultNode); @@ -108,17 +106,17 @@ private TNode MakeTestRunElement(ITestResult result) if (result.ResultState.Label != string.Empty) testRun.AddAttribute("label", result.ResultState.Label); - testRun.AddAttribute("start-time", result.StartTime.ToString("u")); - testRun.AddAttribute("end-time", result.EndTime.ToString("u")); + testRun.AddAttribute("start-time", result.StartTime.ToString("o")); + testRun.AddAttribute("end-time", result.EndTime.ToString("o")); testRun.AddAttribute("duration", result.Duration.ToString("0.000000", NumberFormatInfo.InvariantInfo)); - testRun.AddAttribute("total", (result.PassCount + result.FailCount + result.SkipCount + result.InconclusiveCount).ToString()); + testRun.AddAttribute("total", result.TotalCount.ToString()); testRun.AddAttribute("passed", result.PassCount.ToString()); testRun.AddAttribute("failed", result.FailCount.ToString()); testRun.AddAttribute("inconclusive", result.InconclusiveCount.ToString()); testRun.AddAttribute("skipped", result.SkipCount.ToString()); + testRun.AddAttribute("warnings", result.WarningCount.ToString()); testRun.AddAttribute("asserts", result.AssertCount.ToString()); - testRun.AddAttribute("random-seed", Randomizer.InitialSeed.ToString()); // NOTE: The console runner adds attributes for engine-version and clr-version @@ -128,17 +126,15 @@ private TNode MakeTestRunElement(ITestResult result) return testRun; } -#if !NETSTANDARD1_4 private static TNode MakeCommandLineElement() { return new TNode("command-line", Environment.CommandLine, true); } -#endif private static TNode MakeTestFilterElement(TestFilter filter) { TNode result = new TNode("filter"); - if (!filter.IsEmpty) + if (filter != null && !filter.IsEmpty) filter.AddToXml(result, true); return result; } diff --git a/src/NUnitFramework/nunitlite/Properties/AssemblyInfo.cs b/src/NUnitFramework/nunitlite/Properties/AssemblyInfo.cs index 2bb2aa2b52..87089ce636 100644 --- a/src/NUnitFramework/nunitlite/Properties/AssemblyInfo.cs +++ b/src/NUnitFramework/nunitlite/Properties/AssemblyInfo.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -38,8 +38,6 @@ [assembly: AssemblyTitle("NUnitLite Runner (.NET Framework 4.0)")] #elif NET35 [assembly: AssemblyTitle("NUnitLite Runner (.NET Framework 3.5)")] -#elif NETSTANDARD1_4 -[assembly: AssemblyTitle("NUnitLite Runner (.NET Standard 1.4)")] #elif NETSTANDARD2_0 [assembly: AssemblyTitle("NUnitLite Runner (.NET Standard 2.0)")] #else diff --git a/src/NUnitFramework/nunitlite/TestSelectionParserException.cs b/src/NUnitFramework/nunitlite/TestSelectionParserException.cs index 4f7aec49e2..cd7a823ffa 100644 --- a/src/NUnitFramework/nunitlite/TestSelectionParserException.cs +++ b/src/NUnitFramework/nunitlite/TestSelectionParserException.cs @@ -22,9 +22,7 @@ // *********************************************************************** using System; -#if SERIALIZATION using System.Runtime.Serialization; -#endif namespace NUnit.Common { @@ -47,11 +45,9 @@ public class TestSelectionParserException : Exception /// public TestSelectionParserException(string message, Exception innerException) : base(message, innerException) { } -#if SERIALIZATION /// /// Serialization constructor /// public TestSelectionParserException(SerializationInfo info, StreamingContext context) : base(info, context) { } -#endif } } diff --git a/src/NUnitFramework/nunitlite/TextRunner.cs b/src/NUnitFramework/nunitlite/TextRunner.cs index 697ec3ecdb..737a1fef5c 100644 --- a/src/NUnitFramework/nunitlite/TextRunner.cs +++ b/src/NUnitFramework/nunitlite/TextRunner.cs @@ -116,11 +116,7 @@ public int Execute(string[] args) if (_options.OutFile != null) { var outFile = Path.Combine(_options.WorkDirectory, _options.OutFile); -#if NETSTANDARD1_4 - var textWriter = File.CreateText(outFile); -#else var textWriter = TextWriter.Synchronized(new StreamWriter(outFile)); -#endif outWriter = new ExtendedTextWrapper(textWriter); Console.SetOut(outWriter); } @@ -135,11 +131,7 @@ public int Execute(string[] args) if (_options.ErrFile != null) { var errFile = Path.Combine(_options.WorkDirectory, _options.ErrFile); -#if NETSTANDARD1_4 - errWriter = File.CreateText(errFile); -#else errWriter = TextWriter.Synchronized(new StreamWriter(errFile)); -#endif Console.SetError(errWriter); } @@ -339,7 +331,7 @@ private int ExploreTests(ITestFilter filter) { int end = testName.IndexOfAny(new char[] { '(', '<' }); if (end > 0) - prefilters.Add(testName.Substring(0, end)); + prefilters.Add(testName.Substring(0, end).Trim()); else prefilters.Add(testName); } @@ -439,11 +431,7 @@ private string GetLogFileName() ? Path.GetFileNameWithoutExtension(_options.InputFile) : "NUnitLite"; -#if NETSTANDARD1_4 - var id = DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss"); -#else var id = Process.GetCurrentProcess().Id; -#endif return string.Format(logFileFormat, id, baseName, ext); } @@ -491,7 +479,7 @@ public void TestOutput(TestOutput output) /// A TestMessage object containing the text to send public void SendMessage(TestMessage message) { - + } #endregion diff --git a/src/NUnitFramework/nunitlite/TextUI.cs b/src/NUnitFramework/nunitlite/TextUI.cs index bca4cd9f25..53affbcff0 100644 --- a/src/NUnitFramework/nunitlite/TextUI.cs +++ b/src/NUnitFramework/nunitlite/TextUI.cs @@ -73,7 +73,7 @@ public void DisplayHeader() Assembly executingAssembly = GetType().GetTypeInfo().Assembly; AssemblyName assemblyName = AssemblyHelper.GetAssemblyName(executingAssembly); Version version = assemblyName.Version; - string copyright = "Copyright (C) 2019 Charlie Poole, Rob Prouse"; + string copyright = "Copyright (C) 2021 Charlie Poole, Rob Prouse"; string build = ""; var copyrightAttr = executingAssembly.GetCustomAttribute(); @@ -166,16 +166,12 @@ public void DisplayHelp() public void DisplayRuntimeEnvironment() { WriteSectionHeader("Runtime Environment"); -#if NETSTANDARD1_4 || NETSTANDARD2_0 +#if NETSTANDARD2_0 Writer.WriteLabelLine(" OS Version: ", System.Runtime.InteropServices.RuntimeInformation.OSDescription); #else Writer.WriteLabelLine(" OS Version: ", OSPlatform.CurrentPlatform); #endif -#if NETSTANDARD1_4 - Writer.WriteLabelLine(" CLR Version: ", System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription); -#else Writer.WriteLabelLine(" CLR Version: ", Environment.Version); -#endif Writer.WriteLine(); } @@ -229,13 +225,11 @@ public void DisplayRunSettings() if (_options.DefaultTimeout >= 0) Writer.WriteLabelLine(" Default timeout: ", _options.DefaultTimeout); -#if PARALLEL Writer.WriteLabelLine( " Number of Test Workers: ", _options.NumberOfTestWorkers >= 0 ? _options.NumberOfTestWorkers : Math.Max(Environment.ProcessorCount, 2)); -#endif Writer.WriteLabelLine(" Work Directory: ", _options.WorkDirectory ?? Directory.GetCurrentDirectory()); Writer.WriteLabelLine(" Internal Trace: ", _options.InternalTraceLevel ?? "Off"); diff --git a/src/NUnitFramework/nunitlite/nunitlite.csproj b/src/NUnitFramework/nunitlite/nunitlite.csproj index f43b7d270a..4ca9b828d9 100644 --- a/src/NUnitFramework/nunitlite/nunitlite.csproj +++ b/src/NUnitFramework/nunitlite/nunitlite.csproj @@ -1,7 +1,7 @@  - net35;net40;net45;netstandard1.4;netstandard2.0 + net35;net40;net45;netstandard2.0 NUnitLite @@ -9,8 +9,9 @@ - - + + + @@ -18,4 +19,9 @@ + + + + + diff --git a/src/NUnitFramework/slow-tests/SlowTests.cs b/src/NUnitFramework/slow-tests/SlowTests.cs index b2bcb6f2fd..0adea9eed9 100644 --- a/src/NUnitFramework/slow-tests/SlowTests.cs +++ b/src/NUnitFramework/slow-tests/SlowTests.cs @@ -27,7 +27,7 @@ namespace NUnit.Tests { public class SlowTests { - const int DELAY = 1000; + public const int SINGLE_TEST_DELAY = 1000; public class AAA { @@ -61,7 +61,7 @@ public class CCC private static void Delay() { - System.Threading.Thread.Sleep(DELAY); + System.Threading.Thread.Sleep(SINGLE_TEST_DELAY); } } } diff --git a/src/NUnitFramework/slow-tests/slow-nunit-tests.csproj b/src/NUnitFramework/slow-tests/slow-nunit-tests.csproj index 42681b37db..108af198b5 100644 --- a/src/NUnitFramework/slow-tests/slow-nunit-tests.csproj +++ b/src/NUnitFramework/slow-tests/slow-nunit-tests.csproj @@ -1,7 +1,7 @@  - net35;net40;net45;netcoreapp1.1;netcoreapp2.0 + net35;netcoreapp2.1 NUnit.Tests @@ -13,4 +13,9 @@ + + + + + diff --git a/src/NUnitFramework/testdata.fsharp/AsyncExecutionApiAdapter.Fixture-based.fs b/src/NUnitFramework/testdata.fsharp/AsyncExecutionApiAdapter.Fixture-based.fs new file mode 100644 index 0000000000..e3d2bc21b9 --- /dev/null +++ b/src/NUnitFramework/testdata.fsharp/AsyncExecutionApiAdapter.Fixture-based.fs @@ -0,0 +1,68 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +module NUnit.TestData.FSharp.AsyncExecutionApiAdapter + +open NUnit.Framework + +type TaskReturningTestMethodFixture(asyncUserCode: AsyncTestDelegate) = + [] + member this.TestMethod() = async { + do! Async.AwaitTask(asyncUserCode.Invoke()) + } + +type TaskReturningSetUpFixture(asyncUserCode: AsyncTestDelegate) = + [] + member this.SetUp() = async { + do! Async.AwaitTask(asyncUserCode.Invoke()) + } + + [] + member this.DummyTest() = () + +type TaskReturningTearDownFixture(asyncUserCode: AsyncTestDelegate) = + [] + member this.TearDown() = async { + do! Async.AwaitTask(asyncUserCode.Invoke()) + } + + [] + member this.DummyTest() = () + +type TaskReturningOneTimeSetUpFixture(asyncUserCode: AsyncTestDelegate) = + [] + member this.OneTimeSetUp() = async { + do! Async.AwaitTask(asyncUserCode.Invoke()) + } + + [] + member this.DummyTest() = () + +type TaskReturningOneTimeTearDownFixture(asyncUserCode: AsyncTestDelegate) = + [] + member this.OneTimeTearDown() = async { + do! Async.AwaitTask(asyncUserCode.Invoke()) + } + + [] + member this.DummyTest() = () diff --git a/src/NUnitFramework/testdata.fsharp/nunit.testdata.fsharp.fsproj b/src/NUnitFramework/testdata.fsharp/nunit.testdata.fsharp.fsproj new file mode 100644 index 0000000000..1612aa5a2a --- /dev/null +++ b/src/NUnitFramework/testdata.fsharp/nunit.testdata.fsharp.fsproj @@ -0,0 +1,18 @@ + + + + net45;netcoreapp2.1 + false + true + + + + + + + + + + + + diff --git a/src/NUnitFramework/testdata/ApartmentData.cs b/src/NUnitFramework/testdata/ApartmentData.cs index 2c4d6ff1be..67faeb5e55 100644 --- a/src/NUnitFramework/testdata/ApartmentData.cs +++ b/src/NUnitFramework/testdata/ApartmentData.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -26,7 +26,6 @@ namespace NUnit.TestData { -#if APARTMENT_STATE [RequiresThread(ApartmentState.Unknown)] public class ApartmentDataRequiresThreadAttribute { @@ -36,5 +35,4 @@ public class ApartmentDataRequiresThreadAttribute public class ApartmentDataApartmentAttribute { } -#endif } diff --git a/src/NUnitFramework/testdata/AsyncExecutionApiAdapter.Fixture-based.cs b/src/NUnitFramework/testdata/AsyncExecutionApiAdapter.Fixture-based.cs index b33eb280af..41a18c37f3 100644 --- a/src/NUnitFramework/testdata/AsyncExecutionApiAdapter.Fixture-based.cs +++ b/src/NUnitFramework/testdata/AsyncExecutionApiAdapter.Fixture-based.cs @@ -1,5 +1,5 @@ // *********************************************************************** -// Copyright (c) 2018 Charlie Poole, Rob Prouse +// Copyright (c) 2019 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the diff --git a/src/NUnitFramework/testdata/ExecutionContextFixture.cs b/src/NUnitFramework/testdata/ExecutionContextFixture.cs new file mode 100644 index 0000000000..389b34c1ad --- /dev/null +++ b/src/NUnitFramework/testdata/ExecutionContextFixture.cs @@ -0,0 +1,90 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using NUnit.Framework; +using System.Collections.Generic; + +#if NET35 || NET40 +using System.Runtime.Remoting.Messaging; +#else +using System.Threading; +#endif + +namespace NUnit.TestData +{ + public static class ExecutionContextFixture + { +#if !(NET35 || NET40) + private static readonly AsyncLocal WasExecutionContextUsed = new AsyncLocal(); +#endif + + public static IEnumerable Source1 + { + get + { + AssertAndUseCurrentExecutionContext(); + return new[] { 1 }; + } + } + + public static IEnumerable Source2 + { + get + { + AssertAndUseCurrentExecutionContext(); + return new[] { 2 }; + } + } + + [TestCaseSource(nameof(Source1))] + [TestCaseSource(nameof(Source2))] + public static void TestCaseSourceExecutionContextIsNotShared(int testCase) + { + AssertAndUseCurrentExecutionContext(); + } + + [Test] + public static void ValueSourceExecutionContextIsNotShared([ValueSource(nameof(Source1)), ValueSource(nameof(Source2))] int testCase) + { + AssertAndUseCurrentExecutionContext(); + } + + public static void ExecutionContextIsNotSharedBetweenTestCases([Range(1, 2)] int testCase) + { + AssertAndUseCurrentExecutionContext(); + } + + private static void AssertAndUseCurrentExecutionContext() + { +#if NET35 || NET40 + Assert.Null(CallContext.LogicalGetData("WasUsed")); + + CallContext.LogicalSetData("WasUsed", true); +#else + Assert.That(!WasExecutionContextUsed.Value); + + WasExecutionContextUsed.Value = true; +#endif + } + } +} diff --git a/src/NUnitFramework/testdata/ExecutionContextFlowFixture.cs b/src/NUnitFramework/testdata/ExecutionContextFlowFixture.cs new file mode 100644 index 0000000000..803511b2d0 --- /dev/null +++ b/src/NUnitFramework/testdata/ExecutionContextFlowFixture.cs @@ -0,0 +1,337 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using NUnit.Framework; + +#if NET35 || NET40 +using System.Runtime.Remoting.Messaging; +#else +using System.Threading; +#endif + +namespace NUnit.TestData +{ + public sealed class ExecutionContextFlowFixture : IDisposable + { + // Constructor/Dispose and one-time setup/teardown may need to start flowing differently when we implement + // instance-per-test-case. + +#if !(NET35 || NET40) + private static readonly AsyncLocal FromConstructor = new AsyncLocal(); + private static readonly AsyncLocal FromOneTimeSetUp1 = new AsyncLocal(); + private static readonly AsyncLocal FromOneTimeSetUp2 = new AsyncLocal(); + private static readonly AsyncLocal FromSetUp1 = new AsyncLocal(); + private static readonly AsyncLocal FromSetUp2 = new AsyncLocal(); + private static readonly AsyncLocal FromTestMethod = new AsyncLocal(); + private static readonly AsyncLocal FromTearDown1 = new AsyncLocal(); + private static readonly AsyncLocal FromTearDown2 = new AsyncLocal(); + private static readonly AsyncLocal FromOneTimeTearDown1 = new AsyncLocal(); + private static readonly AsyncLocal FromOneTimeTearDown2 = new AsyncLocal(); +#endif + + public ExecutionContextFlowFixture() + { +#if NET35 || NET40 + CallContext.LogicalSetData("Constructor", true); +#else + FromConstructor.Value = true; +#endif + } + + [OneTimeSetUp] + public void OneTimeSetUp1() + { +#if NET35 || NET40 + Assert.That(CallContext.LogicalGetData("Constructor"), Is.True); + + CallContext.LogicalSetData("OneTimeSetUp1", true); +#else + Assert.That(FromConstructor.Value, Is.True); + + FromOneTimeSetUp1.Value = true; +#endif + } + + [OneTimeSetUp] + public void OneTimeSetUp2() + { +#if NET35 || NET40 + Assert.That(CallContext.LogicalGetData("Constructor"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp1"), Is.True); + + CallContext.LogicalSetData("OneTimeSetUp2", true); +#else + Assert.That(FromConstructor.Value, Is.True); + Assert.That(FromOneTimeSetUp1.Value, Is.True); + + FromOneTimeSetUp2.Value = true; +#endif + } + + [SetUp] + public void SetUp1() + { +#if NET35 || NET40 + Assert.That(CallContext.LogicalGetData("Constructor"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp2"), Is.True); + + Assert.That(CallContext.LogicalGetData("SetUp1"), Is.Null); + Assert.That(CallContext.LogicalGetData("SetUp2"), Is.Null); + Assert.That(CallContext.LogicalGetData("TestMethod"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown1"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown2"), Is.Null); + + CallContext.LogicalSetData("SetUp1", true); +#else + Assert.That(FromConstructor.Value, Is.True); + Assert.That(FromOneTimeSetUp1.Value, Is.True); + Assert.That(FromOneTimeSetUp2.Value, Is.True); + + Assert.That(FromSetUp1.Value, Is.False); + Assert.That(FromSetUp2.Value, Is.False); + Assert.That(FromTestMethod.Value, Is.False); + Assert.That(FromTearDown1.Value, Is.False); + Assert.That(FromTearDown2.Value, Is.False); + + FromSetUp1.Value = true; +#endif + } + + [SetUp] + public void SetUp2() + { +#if NET35 || NET40 + Assert.That(CallContext.LogicalGetData("Constructor"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp2"), Is.True); + Assert.That(CallContext.LogicalGetData("SetUp1"), Is.True); + + Assert.That(CallContext.LogicalGetData("SetUp2"), Is.Null); + Assert.That(CallContext.LogicalGetData("TestMethod"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown1"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown2"), Is.Null); + + CallContext.LogicalSetData("SetUp2", true); +#else + Assert.That(FromConstructor.Value, Is.True); + Assert.That(FromOneTimeSetUp1.Value, Is.True); + Assert.That(FromOneTimeSetUp2.Value, Is.True); + Assert.That(FromSetUp1.Value, Is.True); + + Assert.That(FromSetUp2.Value, Is.False); + Assert.That(FromTestMethod.Value, Is.False); + Assert.That(FromTearDown1.Value, Is.False); + Assert.That(FromTearDown2.Value, Is.False); + + FromSetUp2.Value = true; +#endif + } + + [Test] + public void TestMethod([Range(1, 2)] int testCase) + { +#if NET35 || NET40 + Assert.That(CallContext.LogicalGetData("Constructor"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp2"), Is.True); + Assert.That(CallContext.LogicalGetData("SetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("SetUp2"), Is.True); + + Assert.That(CallContext.LogicalGetData("TestMethod"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown1"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown2"), Is.Null); + + CallContext.LogicalSetData("TestMethod", true); +#else + Assert.That(FromConstructor.Value, Is.True); + Assert.That(FromOneTimeSetUp1.Value, Is.True); + Assert.That(FromOneTimeSetUp2.Value, Is.True); + Assert.That(FromSetUp1.Value, Is.True); + Assert.That(FromSetUp2.Value, Is.True); + + Assert.That(FromTestMethod.Value, Is.False); + Assert.That(FromTearDown1.Value, Is.False); + Assert.That(FromTearDown2.Value, Is.False); + + FromTestMethod.Value = true; +#endif + } + + [TearDown] + public void TearDown2() + { +#if NET35 || NET40 + Assert.That(CallContext.LogicalGetData("Constructor"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp2"), Is.True); + Assert.That(CallContext.LogicalGetData("SetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("SetUp2"), Is.True); + Assert.That(CallContext.LogicalGetData("TestMethod"), Is.True); + Assert.That(CallContext.LogicalGetData("TearDown1"), Is.True); + + Assert.That(CallContext.LogicalGetData("TearDown2"), Is.Null); + + CallContext.LogicalSetData("TearDown2", true); +#else + Assert.That(FromConstructor.Value, Is.True); + Assert.That(FromOneTimeSetUp1.Value, Is.True); + Assert.That(FromOneTimeSetUp2.Value, Is.True); + Assert.That(FromSetUp1.Value, Is.True); + Assert.That(FromSetUp2.Value, Is.True); + Assert.That(FromTestMethod.Value, Is.True); + Assert.That(FromTearDown1.Value, Is.True); + + Assert.That(FromTearDown2.Value, Is.False); + + FromTearDown2.Value = true; +#endif + } + + [TearDown] + public void TearDown1() + { +#if NET35 || NET40 + Assert.That(CallContext.LogicalGetData("Constructor"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp2"), Is.True); + Assert.That(CallContext.LogicalGetData("SetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("SetUp2"), Is.True); + Assert.That(CallContext.LogicalGetData("TestMethod"), Is.True); + + Assert.That(CallContext.LogicalGetData("TearDown1"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown2"), Is.Null); + + CallContext.LogicalSetData("TearDown1", true); +#else + Assert.That(FromConstructor.Value, Is.True); + Assert.That(FromOneTimeSetUp1.Value, Is.True); + Assert.That(FromOneTimeSetUp2.Value, Is.True); + Assert.That(FromSetUp1.Value, Is.True); + Assert.That(FromSetUp2.Value, Is.True); + Assert.That(FromTestMethod.Value, Is.True); + + Assert.That(FromTearDown1.Value, Is.False); + Assert.That(FromTearDown2.Value, Is.False); + + FromTearDown1.Value = true; +#endif + } + + [OneTimeTearDown] + public void OneTimeTearDown2() + { +#if NET35 || NET40 + Assert.That(CallContext.LogicalGetData("Constructor"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp2"), Is.True); + + Assert.That(CallContext.LogicalGetData("SetUp1"), Is.Null); + Assert.That(CallContext.LogicalGetData("SetUp2"), Is.Null); + Assert.That(CallContext.LogicalGetData("TestMethod"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown1"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown2"), Is.Null); + + Assert.That(CallContext.LogicalGetData("OneTimeTearDown1"), Is.True); + + CallContext.LogicalSetData("OneTimeTearDown2", true); +#else + Assert.That(FromConstructor.Value, Is.True); + Assert.That(FromOneTimeSetUp1.Value, Is.True); + Assert.That(FromOneTimeSetUp2.Value, Is.True); + + Assert.That(FromSetUp1.Value, Is.False); + Assert.That(FromSetUp2.Value, Is.False); + Assert.That(FromTestMethod.Value, Is.False); + Assert.That(FromTearDown1.Value, Is.False); + Assert.That(FromTearDown2.Value, Is.False); + + Assert.That(FromOneTimeTearDown1.Value, Is.True); + + FromOneTimeTearDown2.Value = true; +#endif + } + + [OneTimeTearDown] + public void OneTimeTearDown1() + { +#if NET35 || NET40 + Assert.That(CallContext.LogicalGetData("Constructor"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp2"), Is.True); + + Assert.That(CallContext.LogicalGetData("SetUp1"), Is.Null); + Assert.That(CallContext.LogicalGetData("SetUp2"), Is.Null); + Assert.That(CallContext.LogicalGetData("TestMethod"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown1"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown2"), Is.Null); + + CallContext.LogicalSetData("OneTimeTearDown1", true); +#else + Assert.That(FromConstructor.Value, Is.True); + Assert.That(FromOneTimeSetUp1.Value, Is.True); + Assert.That(FromOneTimeSetUp2.Value, Is.True); + + Assert.That(FromSetUp1.Value, Is.False); + Assert.That(FromSetUp2.Value, Is.False); + Assert.That(FromTestMethod.Value, Is.False); + Assert.That(FromTearDown1.Value, Is.False); + Assert.That(FromTearDown2.Value, Is.False); + + FromOneTimeTearDown1.Value = true; +#endif + } + + public void Dispose() + { +#if NET35 || NET40 + Assert.That(CallContext.LogicalGetData("Constructor"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp1"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeSetUp2"), Is.True); + + Assert.That(CallContext.LogicalGetData("SetUp1"), Is.Null); + Assert.That(CallContext.LogicalGetData("SetUp2"), Is.Null); + Assert.That(CallContext.LogicalGetData("TestMethod"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown1"), Is.Null); + Assert.That(CallContext.LogicalGetData("TearDown2"), Is.Null); + + Assert.That(CallContext.LogicalGetData("OneTimeTearDown1"), Is.True); + Assert.That(CallContext.LogicalGetData("OneTimeTearDown2"), Is.True); +#else + Assert.That(FromConstructor.Value, Is.True); + Assert.That(FromOneTimeSetUp1.Value, Is.True); + Assert.That(FromOneTimeSetUp2.Value, Is.True); + + Assert.That(FromSetUp1.Value, Is.False); + Assert.That(FromSetUp2.Value, Is.False); + Assert.That(FromTestMethod.Value, Is.False); + Assert.That(FromTearDown1.Value, Is.False); + Assert.That(FromTearDown2.Value, Is.False); + + Assert.That(FromOneTimeTearDown1.Value, Is.True); + Assert.That(FromOneTimeTearDown2.Value, Is.True); +#endif + } + } +} diff --git a/src/NUnitFramework/testdata/LifeCycleFixture.cs b/src/NUnitFramework/testdata/LifeCycleFixture.cs new file mode 100644 index 0000000000..aba4ffc1bb --- /dev/null +++ b/src/NUnitFramework/testdata/LifeCycleFixture.cs @@ -0,0 +1,239 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + + +using System; +using NUnit.Framework; + +namespace NUnit.TestData.LifeCycleTests +{ + [TestFixture] + [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] + public class DisposableFixture : IDisposable + { + public static int DisposeCount = 0; + + [Test] + public void DummyTest1() { } + + [Test] + public void DummyTest2() { } + + public void Dispose() + { + DisposeCount++; + } + } + + [TestFixture] + [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] + public class SetupAndTearDownFixtureInstancePerTestCase + { + int _totalSetupCount = 0; + int _totalTearDownCount = 0; + + [SetUp] + public void Setup() + { + _totalSetupCount++; + } + + [TearDown] + public void TearDown() + { + _totalTearDownCount++; + } + + [Test] + public void DummyTest1() + { + Assert.That(_totalSetupCount, Is.EqualTo(1)); + Assert.That(_totalTearDownCount, Is.EqualTo(0)); + } + + [Test] + public void DummyTest2() + { + Assert.That(_totalSetupCount, Is.EqualTo(1)); + Assert.That(_totalTearDownCount, Is.EqualTo(0)); + } + + [Test] + public void DummyTest3() + { + Assert.That(_totalSetupCount, Is.EqualTo(1)); + Assert.That(_totalTearDownCount, Is.EqualTo(0)); + } + } + + [TestFixture] + [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] + public class StaticOneTimeSetupAndTearDownFixtureInstancePerTestCase + { + public static int TotalOneTimeSetupCount = 0; + public static int TotalOneTimeTearDownCount = 0; + + [OneTimeSetUp] + public static void OneTimeSetup() + { + TotalOneTimeSetupCount++; + } + + [OneTimeTearDown] + public static void OneTimeTearDown() + { + TotalOneTimeTearDownCount++; + } + + [Test] + public void DummyTest1() {} + + [Test] + public void DummyTest2() {} + } + + [TestFixture] + [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] + public class InstanceOneTimeSetupAndTearDownFixtureInstancePerTestCase + { + public int TotalOneTimeSetupCount = 0; + public int TotalOneTimeTearDownCount = 0; + + [OneTimeSetUp] + public void OneTimeSetup() + { + TotalOneTimeSetupCount++; + } + + [OneTimeTearDown] + public void OneTimeTearDown() + { + TotalOneTimeTearDownCount++; + } + + [Test] + public void DummyTest1() {} + } + + [TestFixture] + public class CountingLifeCycleTestFixture + { + public int Count { get; set; } + + [Test] + public void CountIsAlwaysOne() + { + Count++; + Assert.AreEqual(1, Count); + } + + [Test] + public void CountIsAlwaysOne_2() + { + Count++; + Assert.AreEqual(1, Count); + } + } + + [TestFixture] + [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] + public class DisposableLifeCycleFixtureInstancePerTestCase : IDisposable + { + public static int DisposeCalls { get; set; } + + [Test] + [Order(1)] + public void TestCase1() + { + Assert.That(DisposeCalls, Is.EqualTo(0)); + } + + [Test] + [Order(2)] + public void TestCase2() + { + Assert.That(DisposeCalls, Is.EqualTo(1)); + } + + [Test] + [Order(3)] + public void TestCase3() + { + Assert.That(DisposeCalls, Is.EqualTo(2)); + } + + public void Dispose() + { + DisposeCalls++; + } + } + + [TestFixture] + [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] + public class RepeatingLifeCycleFixtureInstancePerTestCase + { + public int Counter { get; set; } + public static int RepeatCounter { get; set; } + + [Test] + [Repeat(3)] + public void CounterShouldAlwaysStartAsZero() + { + Counter++; + RepeatCounter++; + Assert.That(Counter, Is.EqualTo(1)); + } + } + + [TestFixture] + [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] + [Parallelizable(ParallelScope.All)] + public class ParallelLifeCycleFixtureInstancePerTestCase + { + int _setupCount = 0; + + [SetUp] + public void SetUp() + { + _setupCount++; + } + + [Test] + public void DummyTest1() + { + Assert.That(_setupCount, Is.EqualTo(1)); + } + + [Test] + public void DummyTest2() + { + Assert.That(_setupCount, Is.EqualTo(1)); + } + + [Test] + public void DummyTest3() + { + Assert.That(_setupCount, Is.EqualTo(1)); + } + } +} diff --git a/src/NUnitFramework/testdata/ParallelExecutionData.cs b/src/NUnitFramework/testdata/ParallelExecutionData.cs index 453f0d9239..25fd98776f 100644 --- a/src/NUnitFramework/testdata/ParallelExecutionData.cs +++ b/src/NUnitFramework/testdata/ParallelExecutionData.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System.Threading; using NUnit.Framework; @@ -86,7 +85,6 @@ public void TestFixture3_Test() } } -#if APARTMENT_STATE [Apartment(ApartmentState.STA)] public class STAFixture { @@ -96,7 +94,6 @@ public void STAFixture_Test() Thread.Sleep(100); } } -#endif public class TestFixtureWithParallelParameterizedTest { @@ -110,4 +107,3 @@ public void ParameterizedTest(int i) } } } -#endif diff --git a/src/NUnitFramework/testdata/ParameterizedTestFixture.cs b/src/NUnitFramework/testdata/ParameterizedTestFixture.cs index 3be672a337..9663bf23fe 100644 --- a/src/NUnitFramework/testdata/ParameterizedTestFixture.cs +++ b/src/NUnitFramework/testdata/ParameterizedTestFixture.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2009 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -50,4 +50,24 @@ public class TestFixtureWithSingleCategory public class TestFixtureWithMultipleCategories { } + + [TestFixture(null)] + public class TestFixtureWithNullArgumentForOrdinaryValueTypeParameter + { + public TestFixtureWithNullArgumentForOrdinaryValueTypeParameter(OrdinaryValueType _) + { + } + + public struct OrdinaryValueType + { + } + } + + [TestFixture(null)] + public class TestFixtureWithNullArgumentForGenericParameter + { + public TestFixtureWithNullArgumentForGenericParameter(T _) + { + } + } } diff --git a/src/NUnitFramework/testdata/RecursivelyThrowingException.cs b/src/NUnitFramework/testdata/RecursivelyThrowingException.cs new file mode 100644 index 0000000000..3e8535c372 --- /dev/null +++ b/src/NUnitFramework/testdata/RecursivelyThrowingException.cs @@ -0,0 +1,61 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Collections; + +namespace NUnit.TestData.UnexpectedExceptionFixture +{ + /// + /// This models a .NET Framework race condition that resulted in stack traces throwing AccessViolationException + /// which would produce another AccessViolationException while attempting to print its stack trace in turn. + /// + public sealed class RecursivelyThrowingException : Exception + { + public override string Message => throw this; + + public override IDictionary Data => throw this; + + public override string StackTrace => throw this; + + public override string HelpLink + { + get => throw this; + set => throw this; + } + + public override string Source + { + get => throw this; + set => throw this; + } + + public override bool Equals(object obj) => throw this; + + public override Exception GetBaseException() => throw this; + + public override int GetHashCode() => throw this; + + public override string ToString() => throw this; + } +} diff --git a/src/NUnitFramework/testdata/RepeatedTestFixture.cs b/src/NUnitFramework/testdata/RepeatedTestFixture.cs index bb48aee261..6535717777 100644 --- a/src/NUnitFramework/testdata/RepeatedTestFixture.cs +++ b/src/NUnitFramework/testdata/RepeatedTestFixture.cs @@ -163,4 +163,24 @@ public void TestWithCategory() Assert.IsTrue(true); } } + + public class RepeatedTestVerifyAttempt : RepeatingTestsFixtureBase + { + [Test, Repeat(3)] + public void AlwaysPasses() + { + Count = TestContext.CurrentContext.CurrentRepeatCount; + } + + [Test, Repeat(3)] + public void PassesTwoTimes() + { + Assert.That(Count, Is.EqualTo(TestContext.CurrentContext.CurrentRepeatCount), "expected CurrentRepeatCount to be incremented only after first two attempts"); + if (Count > 1) + { + Assert.Fail("forced failure on 3rd repetition"); + } + Count++; + } + } } diff --git a/src/NUnitFramework/testdata/SingleThreadedFixtureData.cs b/src/NUnitFramework/testdata/SingleThreadedFixtureData.cs index d802dc8d77..a0fb1c2d13 100644 --- a/src/NUnitFramework/testdata/SingleThreadedFixtureData.cs +++ b/src/NUnitFramework/testdata/SingleThreadedFixtureData.cs @@ -1,4 +1,3 @@ -#if !NETCOREAPP1_1 using System.Threading; using NUnit.Framework; @@ -11,7 +10,6 @@ public class SingleThreadedFixture_TestWithRequiresThread public void TestWithRequiresThread() { } } -#if APARTMENT_STATE [SingleThreaded] public class SingleThreadedFixture_TestWithDifferentApartment { @@ -25,7 +23,6 @@ public class SingleThreadedFixture_TestWithRequiresThreadAndDifferentApartment [Test, RequiresThread, Apartment(ApartmentState.STA)] public void TestWithRequiresThreadAndDifferentApartment() { } } -#endif #if THREAD_ABORT [SingleThreaded] @@ -57,4 +54,3 @@ public class SingleThreadedFixture_TestWithTimeoutRequiresThreadAndDifferentApar } #endif } -#endif diff --git a/src/NUnitFramework/testdata/TestCaseAttributeFixture.cs b/src/NUnitFramework/testdata/TestCaseAttributeFixture.cs index c44128de1c..4ab65d95ca 100644 --- a/src/NUnitFramework/testdata/TestCaseAttributeFixture.cs +++ b/src/NUnitFramework/testdata/TestCaseAttributeFixture.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -48,12 +48,12 @@ public void MethodHasTestNameSpecified_WithMethodName(int x, int y, int z) [TestCase(2, 3, 4, Category = "XYZ")] public void MethodHasSingleCategory(int x, int y, int z) { } - + [TestCase(2, 3, 4, Category = "X,Y,Z")] public void MethodHasMultipleCategories(int x, int y, int z) { } - - [TestCase(2, 2000000, ExpectedResult=4)] + + [TestCase(2, 2_000_000, ExpectedResult=4)] public int MethodCausesConversionOverflow(short x, short y) { return x + y; @@ -76,6 +76,15 @@ public void MethodWithIgnoredTestCases(int num) { } + [TestCase(1)] + [TestCase(2, Ignore = "Should not run", Until = "4242-01-01")] + [TestCase(3, Ignore = "Run me after 1942", Until = "1942-01-01")] + [TestCase(4, Ignore = "Don't Run Me!", Until = "4242-01-01T01:23:45Z")] + [TestCase(5, Until = "This should err!")] + public void MethodWithIgnoredWithUntilDateTestCases(int num) + { + } + [TestCase(1)] [TestCase(2, Explicit = true)] [TestCase(3, Explicit = true, Reason = "Connection failing")] @@ -83,7 +92,6 @@ public void MethodWithExplicitTestCases(int num) { } -#if PLATFORM_DETECTION [TestCase(1, IncludePlatform = "Win")] [TestCase(2, IncludePlatform = "Linux")] [TestCase(3, IncludePlatform = "MacOSX")] @@ -112,7 +120,6 @@ public void MethodWithIncludeRuntime(int num) public void MethodWithExcludeRuntime(int num) { } -#endif [TestCase((object)new object[] { })] [TestCase((object)new object[] { 1, "text", null })] diff --git a/src/NUnitFramework/testdata/TestCaseSourceAttributeFixture.cs b/src/NUnitFramework/testdata/TestCaseSourceAttributeFixture.cs index 6587c47acd..749e82621e 100644 --- a/src/NUnitFramework/testdata/TestCaseSourceAttributeFixture.cs +++ b/src/NUnitFramework/testdata/TestCaseSourceAttributeFixture.cs @@ -50,6 +50,7 @@ public void MethodCallsIgnore(int x, int y, int z) #region Test With Ignored TestCaseData [TestCaseSource(nameof(IgnoredSource))] + [TestCaseSource(nameof(IgnoredWithDateSource))] public void MethodWithIgnoredTestCases(int num) { } @@ -60,7 +61,22 @@ private static IEnumerable IgnoredSource { return new object[] { new TestCaseData(1), - new TestCaseData(2).Ignore("Don't Run Me!") + new TestCaseData(2).Ignore("Don't Run Me!"), + + }; + } + } + + private static IEnumerable IgnoredWithDateSource + { + get + { + DateTimeOffset utcTime = DateTimeOffset.UtcNow; + TimeSpan timeZoneOffset = utcTime - utcTime.ToLocalTime(); + return new object[] { + new TestCaseData(3).Ignore("Ignore Me Until The Future").Until(new DateTimeOffset(4242, 01, 01, 0, 0, 0, timeZoneOffset)), + new TestCaseData(4).Ignore("I Was Ignored in the Past").Until(new DateTimeOffset(1492, 01, 01, 0, 0, 0, timeZoneOffset)), + new TestCaseData(5).Ignore("Ignore Me Until The Future").Until(new DateTimeOffset(4242, 01, 01, 12, 42, 33, timeZoneOffset)), }; } } diff --git a/src/NUnitFramework/testdata/TestFixtureData.cs b/src/NUnitFramework/testdata/TestFixtureData.cs index ca35f34a50..d93cc3f006 100644 --- a/src/NUnitFramework/testdata/TestFixtureData.cs +++ b/src/NUnitFramework/testdata/TestFixtureData.cs @@ -22,10 +22,8 @@ // *********************************************************************** using System; -using NUnit.Framework; -#if !NETCOREAPP1_1 using System.Security.Principal; -#endif +using NUnit.Framework; namespace NUnit.TestData.TestFixtureTests { @@ -526,7 +524,6 @@ public void Teardown(int j) } } -#if !(NETCOREAPP1_1 || NETCOREAPP2_0) [TestFixture] public class FixtureThatChangesTheCurrentPrincipal { @@ -538,7 +535,6 @@ public void ChangeCurrentPrincipal() System.Threading.Thread.CurrentPrincipal = principal; } } -#endif [TestFixture(typeof(int))] [TestFixture(typeof(string))] diff --git a/src/NUnitFramework/testdata/TimeoutFixture.cs b/src/NUnitFramework/testdata/TimeoutFixture.cs index 038c94829f..930d282e88 100644 --- a/src/NUnitFramework/testdata/TimeoutFixture.cs +++ b/src/NUnitFramework/testdata/TimeoutFixture.cs @@ -111,20 +111,6 @@ public class TimeoutTestCaseFixture const int NOT_TIMEOUTED_TIME = 10; const int TIMEOUTED_TIME = 500; - [Test] - [Timeout(TIME_OUT_TIME)] - public void TestTimeOutNotElapsed() - { - TestTimeOutTestCase(NOT_TIMEOUTED_TIME); - } - - [Test] - [Timeout(TIME_OUT_TIME)] - public void TestTimeOutElapsed() - { - TestTimeOutTestCase(TIMEOUTED_TIME); - } - [Test] [Timeout(TIME_OUT_TIME)] [TestCase(NOT_TIMEOUTED_TIME)] diff --git a/src/NUnitFramework/testdata/UnexpectedExceptionFixture.cs b/src/NUnitFramework/testdata/UnexpectedExceptionFixture.cs index cbf58792de..46f2cf429b 100644 --- a/src/NUnitFramework/testdata/UnexpectedExceptionFixture.cs +++ b/src/NUnitFramework/testdata/UnexpectedExceptionFixture.cs @@ -1,5 +1,5 @@ -// *********************************************************************** -// Copyright (c) 2007 Charlie Poole, Rob Prouse +// *********************************************************************** +// Copyright (c) 2007–2019 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -47,7 +47,7 @@ public void ThrowsWithNestedInnerException() [Test] public void ThrowsWithAggregateException() { - throw new AggregateException("Outer Aggregate Exception", + throw new AggregateException("Outer Aggregate Exception", new Exception("Inner Exception 1 of 2"), new Exception("Inner Exception 2 of 2")); } @@ -72,6 +72,18 @@ public void ThrowsCustomException() { throw new CustomException("message", new CustomType()); } + + [Test] + public void AssertThatWithRecursivelyThrowingExceptionAsActual() + { + Assert.That(new RecursivelyThrowingException(), Is.Null); + } + + [Test] + public void AssertThatWithRecursivelyThrowingExceptionAsExpected() + { + Assert.That(null, Is.EqualTo(new RecursivelyThrowingException())); + } } class CustomException : Exception diff --git a/src/NUnitFramework/testdata/WarningFixture.cs b/src/NUnitFramework/testdata/WarningFixture.cs index 86fb5d3648..64b9fb7e1a 100644 --- a/src/NUnitFramework/testdata/WarningFixture.cs +++ b/src/NUnitFramework/testdata/WarningFixture.cs @@ -661,7 +661,7 @@ public static void WarningInBeginInvoke() } }).BeginInvoke(ar => { }, null); - if (!finished.WaitOne(10000)) Assert.Fail("Timeout while waiting for BeginInvoke to execute."); + if (!finished.WaitOne(10_000)) Assert.Fail("Timeout while waiting for BeginInvoke to execute."); } } @@ -682,7 +682,7 @@ public static void WarningInThreadStart() } }).Start(); - if (!finished.WaitOne(10000)) + if (!finished.WaitOne(10_000)) Assert.Fail("Timeout while waiting for threadstart to execute."); } } @@ -704,7 +704,7 @@ public static void WarningInThreadPoolQueueUserWorkItem() } }); - if (!finished.WaitOne(10000)) + if (!finished.WaitOne(10_000)) Assert.Fail("Timeout while waiting for Threadpool.QueueUserWorkItem to execute."); } } @@ -713,7 +713,7 @@ public static void WarningInThreadPoolQueueUserWorkItem() [Test] public static void WarningInTaskRun() { - if (!Task.Run(() => Assert.Warn("(Warning message)")).Wait(10000)) + if (!Task.Run(() => Assert.Warn("(Warning message)")).Wait(10_000)) Assert.Fail("Timeout while waiting for Task.Run to execute."); } diff --git a/src/NUnitFramework/testdata/nunit.testdata.csproj b/src/NUnitFramework/testdata/nunit.testdata.csproj index 4735133493..a450df2c97 100644 --- a/src/NUnitFramework/testdata/nunit.testdata.csproj +++ b/src/NUnitFramework/testdata/nunit.testdata.csproj @@ -1,23 +1,36 @@  - net35;net40;net45;netcoreapp1.1;netcoreapp2.0 + net35;net40;net46;netcoreapp2.1 NUnit.TestData - - false - true + + net35;net40;net46;netcoreapp2.1 + + + + net35;net46;netcoreapp2.1 + + + + + + + + + + + diff --git a/src/NUnitFramework/tests/Api/FrameworkControllerTests.cs b/src/NUnitFramework/tests/Api/FrameworkControllerTests.cs index f1afc15190..026ce92a93 100644 --- a/src/NUnitFramework/tests/Api/FrameworkControllerTests.cs +++ b/src/NUnitFramework/tests/Api/FrameworkControllerTests.cs @@ -57,7 +57,7 @@ public static IEnumerable EmptyFilters { get { - yield return new TestCaseData(null); + yield return new TestCaseData((object[])null); yield return new TestCaseData(""); yield return new TestCaseData(EMPTY_FILTER); } @@ -97,12 +97,8 @@ public void InsertSettingsElement_MixedSettings_CreatesCorrectSubNodes(string va }; var inserted = FrameworkController.InsertSettingsElement(outerNode, testSettings); -#if PARALLEL // in parallel, an additional node is added with number of test workers Assert.That(inserted.ChildNodes.Count, Is.EqualTo(3)); -#else - Assert.That(inserted.ChildNodes.Count, Is.EqualTo(2)); -#endif Assert.That(inserted.ChildNodes[0].Attributes["name"], Is.EqualTo("key1")); Assert.That(inserted.ChildNodes[0].Attributes["value"], Is.EqualTo("value1")); @@ -123,12 +119,8 @@ public void InsertSettingsElement_SettingIsValue_CreatesASettingElementPerKey() var inserted = FrameworkController.InsertSettingsElement(outerNode, testSettings); -#if PARALLEL // in parallel, an additional node is added with number of test workers Assert.That(inserted.ChildNodes.Count, Is.EqualTo(3)); -#else - Assert.That(inserted.ChildNodes.Count, Is.EqualTo(2)); -#endif } [TestCaseSource(nameof(SettingsData))] @@ -286,11 +278,7 @@ public void LoadTestsAction_FileNotFound_ReturnsNonRunnableSuite() Assert.That(result.Attributes["runstate"], Is.EqualTo("NotRunnable")); Assert.That(result.Attributes["testcasecount"], Is.EqualTo("0")); // Minimal check here to allow for platform differences -#if NETCOREAPP1_1 - Assert.That(GetSkipReason(result), Contains.Substring("The system cannot find the file specified.")); -#else Assert.That(GetSkipReason(result), Contains.Substring(MISSING_NAME)); -#endif Assert.That(result.SelectNodes("test-suite").Count, Is.EqualTo(0), "Load result should not have child tests"); } @@ -380,11 +368,7 @@ public void ExploreTestsAction_FileNotFound_ReturnsNonRunnableSuite() Assert.That(result.Attributes["runstate"], Is.EqualTo("NotRunnable")); Assert.That(result.Attributes["testcasecount"], Is.EqualTo("0")); // Minimal check here to allow for platform differences -#if NETCOREAPP1_1 - Assert.That(GetSkipReason(result), Contains.Substring("The system cannot find the file specified.")); -#else Assert.That(GetSkipReason(result), Contains.Substring(MISSING_NAME)); -#endif Assert.That(result.SelectNodes("test-suite").Count, Is.EqualTo(0), "Result should not have child tests"); } @@ -491,11 +475,7 @@ public void RunTestsAction_FileNotFound_ReturnsNonRunnableSuite() Assert.That(result.Attributes["runstate"], Is.EqualTo("NotRunnable")); Assert.That(result.Attributes["testcasecount"], Is.EqualTo("0")); // Minimal check here to allow for platform differences -#if NETCOREAPP1_1 - Assert.That(GetSkipReason(result), Contains.Substring("The system cannot find the file specified.")); -#else Assert.That(GetSkipReason(result), Contains.Substring(MISSING_NAME)); -#endif Assert.That(result.SelectNodes("test-suite").Count, Is.EqualTo(0), "Load result should not have child tests"); } @@ -519,27 +499,6 @@ public void RunTestsAction_BadFile_ReturnsNonRunnableSuite() #endregion #region RunAsyncAction - [TestCaseSource(nameof(EmptyFilters))] - public void RunAsyncAction_AfterLoad_ReturnsRunnableSuite(string filter) - { - new FrameworkController.LoadTestsAction(_controller, _handler); - new FrameworkController.RunAsyncAction(_controller, filter, _handler); - //var result = TNode.FromXml(_handler.GetCallbackResult()); - - //Assert.That(result.Name.ToString(), Is.EqualTo("test-suite")); - //Assert.That(result.Attributes["type"], Is.EqualTo("Assembly")); - //Assert.That(result.Attributes["id"], Is.Not.Null.And.StartWith("ID")); - //Assert.That(result.Attributes["name"], Is.EqualTo(EXPECTED_NAME)); - //Assert.That(result.Attributes["runstate"], Is.EqualTo("Runnable")); - //Assert.That(result.Attributes["testcasecount"], Is.EqualTo(MockAssembly.Tests.ToString())); - //Assert.That(result.Attributes["result"], Is.EqualTo("Failed")); - //Assert.That(result.Attributes["passed"], Is.EqualTo(MockAssembly.Success.ToString())); - //Assert.That(result.Attributes["failed"], Is.EqualTo(MockAssembly.ErrorsAndFailures.ToString())); - //Assert.That(result.Attributes["skipped"], Is.EqualTo((MockAssembly.NotRunnable + MockAssembly.Ignored).ToString())); - //Assert.That(result.Attributes["inconclusive"], Is.EqualTo(MockAssembly.Inconclusive.ToString())); - //Assert.That(result.FindDescendants("test-suite").Count, Is.GreaterThan(0), "Run result should have child tests"); - } - [Test] public void RunAsyncAction_WithoutLoad_ReturnsError() { @@ -548,40 +507,6 @@ public void RunAsyncAction_WithoutLoad_ReturnsError() Assert.That(ex.Message, Is.EqualTo("Tests must be loaded before running them.")); } - [Test] - public void RunAsyncAction_FileNotFound_ReturnsNonRunnableSuite() - { - var controller = new FrameworkController(MISSING_FILE, "ID", _settings); - new FrameworkController.LoadTestsAction(controller, _handler); - new FrameworkController.RunAsyncAction(controller, EMPTY_FILTER, _handler); - //var result = TNode.FromXml(_handler.GetCallbackResult()); - - //Assert.That(result.Name.ToString(), Is.EqualTo("test-suite")); - //Assert.That(result.Attributes["type"], Is.EqualTo("Assembly")); - //Assert.That(result.Attributes["runstate"], Is.EqualTo("NotRunnable")); - //Assert.That(result.Attributes["testcasecount"], Is.EqualTo("0")); - // Minimal check here to allow for platform differences - //Assert.That(GetSkipReason(result), Contains.Substring(MISSING_FILE)); - //Assert.That(result.SelectNodes("test-suite").Count, Is.EqualTo(0), "Load result should not have child tests"); - } - - [Test] - public void RunAsyncAction_BadFile_ReturnsNonRunnableSuite() - { - var controller = new FrameworkController(BAD_FILE, "ID", _settings); - new FrameworkController.LoadTestsAction(controller, _handler); - new FrameworkController.RunAsyncAction(controller, EMPTY_FILTER, _handler); - //var result = TNode.FromXml(_handler.GetCallbackResult()); - - //Assert.That(result.Name.ToString(), Is.EqualTo("test-suite")); - //Assert.That(result.Attributes["type"], Is.EqualTo("Assembly")); - //Assert.That(result.Attributes["runstate"], Is.EqualTo("NotRunnable")); - //Assert.That(result.Attributes["testcasecount"], Is.EqualTo("0")); - // Minimal check here to allow for platform differences - //Assert.That(GetSkipReason(result), Contains.Substring(BAD_FILE)); - //Assert.That(result.SelectNodes("test-suite").Count, Is.EqualTo(0), "Load result should not have child tests"); - } - #endregion #region Helper Methods diff --git a/src/NUnitFramework/tests/Api/SchemaTests.cs b/src/NUnitFramework/tests/Api/SchemaTests.cs index 3b79a321b5..6e0fc3baf5 100644 --- a/src/NUnitFramework/tests/Api/SchemaTests.cs +++ b/src/NUnitFramework/tests/Api/SchemaTests.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !NETCOREAPP1_1 // Schema validation doesn’t exist #if !NET35 // Framework bug causes NRE: https://social.msdn.microsoft.com/Forums/en-US/53be44de-30b2-4d18-968d-d3414d0783b1 // We don’t really need these tests to run on more than one platform. @@ -97,6 +96,7 @@ public static void TestResultSchemaMatches() new XAttribute("failed", 0), new XAttribute("inconclusive", 0), new XAttribute("skipped", 0), + new XAttribute("warnings", 0), new XAttribute("asserts", 0), new XAttribute("random-seed", 0)); @@ -110,7 +110,7 @@ public static void MultipleTestsAreAllowedInsideTestRun() Assert.Multiple(() => { SchemaTestUtils.AssertValidXml(@" - + @@ -240,4 +240,3 @@ public static void TestFilterSchemaDisallowsRootElement_And() } } #endif -#endif diff --git a/src/NUnitFramework/tests/Api/TestAssemblyRunnerTests.cs b/src/NUnitFramework/tests/Api/TestAssemblyRunnerTests.cs index ae6f1de099..be3f1fd11d 100644 --- a/src/NUnitFramework/tests/Api/TestAssemblyRunnerTests.cs +++ b/src/NUnitFramework/tests/Api/TestAssemblyRunnerTests.cs @@ -26,6 +26,7 @@ using System.Collections.Generic; using System.IO; using System.Reflection; +using System.Text; using System.Threading; using NUnit.Compatibility; using NUnit.Framework.Interfaces; @@ -39,18 +40,18 @@ namespace NUnit.Framework.Api { // Functional tests of the TestAssemblyRunner and all subordinate classes + [NonParallelizable] public class TestAssemblyRunnerTests : ITestListener { private const string MOCK_ASSEMBLY_FILE = "mock-assembly.dll"; -#if NETCOREAPP1_1 - private const string COULD_NOT_LOAD_MSG = "The system cannot find the file specified."; -#else private const string COULD_NOT_LOAD_MSG = "Could not load"; -#endif private const string BAD_FILE = "mock-assembly.pdb"; private const string SLOW_TESTS_FILE = "slow-nunit-tests.dll"; private const string MISSING_FILE = "junk.dll"; + // Arbitrary delay for cancellation based on the time to run each case in SlowTests + private const int CANCEL_TEST_DELAY = SlowTests.SINGLE_TEST_DELAY * 2; + private static readonly string MOCK_ASSEMBLY_NAME = typeof(MockAssembly).GetTypeInfo().Assembly.FullName; private const string INVALID_FILTER_ELEMENT_MESSAGE = "Invalid filter element: {0}"; @@ -59,6 +60,7 @@ public class TestAssemblyRunnerTests : ITestListener private ITestAssemblyRunner _runner; private int _suiteStartedCount; + private int _suiteFinishedCount; private int _testStartedCount; private int _testFinishedCount; private int _testOutputCount; @@ -67,12 +69,15 @@ public class TestAssemblyRunnerTests : ITestListener private int _skipCount; private int _inconclusiveCount; + private Dictionary _activeTests; + [SetUp] public void CreateRunner() { _runner = new NUnitTestAssemblyRunner(new DefaultTestAssemblyBuilder()); _suiteStartedCount = 0; + _suiteFinishedCount = 0; _testStartedCount = 0; _testFinishedCount = 0; _testOutputCount = 0; @@ -80,6 +85,8 @@ public void CreateRunner() _failCount = 0; _skipCount = 0; _inconclusiveCount = 0; + + _activeTests = new Dictionary(); } #region Load @@ -238,9 +245,27 @@ public void ExploreTests_AfterLoad_WithFilter_ReturnSameTestCount() var explorer = _runner.ExploreTests(filter); Assert.That(explorer.TestCaseCount, Is.EqualTo(_runner.CountTestCases(filter))); } -#endregion -#region Run + [Test] + public void ExploreTests_AfterLoad_WithFilter_TestSuitesRetainProperties() + { + LoadMockAssembly(); + ITestFilter filter = new CategoryFilter("FixtureCategory"); + + var explorer = _runner.ExploreTests(filter); + + var runnerFixture = _runner.LoadedTest.Tests[0].Tests[0].Tests[0].Tests[0]; + var explorerFixture = explorer.Tests[0].Tests[0].Tests[0].Tests[0]; + + Assert.That(explorerFixture.Properties.Keys.Count, Is.EqualTo(runnerFixture.Properties.Keys.Count)); + Assert.That(explorerFixture.Properties.Get(PropertyNames.Category), + Is.EqualTo(explorerFixture.Properties.Get(PropertyNames.Category))); + Assert.That(explorerFixture.Properties.Get(PropertyNames.Description), + Is.EqualTo(explorerFixture.Properties.Get(PropertyNames.Description))); + } + #endregion + + #region Run [Test] public void Run_AfterLoad_ReturnsRunnableSuite() @@ -267,6 +292,7 @@ public void Run_AfterLoad_SendsExpectedEvents() _runner.Run(this, TestFilter.Empty); Assert.That(_suiteStartedCount, Is.EqualTo(MockAssembly.Suites)); + Assert.That(_suiteFinishedCount, Is.EqualTo(MockAssembly.Suites)); Assert.That(_testStartedCount, Is.EqualTo(MockAssembly.TestStartedEvents)); Assert.That(_testFinishedCount, Is.EqualTo(MockAssembly.TestFinishedEvents)); Assert.That(_testOutputCount, Is.EqualTo(MockAssembly.TestOutputEvents)); @@ -352,9 +378,9 @@ public void Run_BadFile_ReturnsNonRunnableSuite() Does.StartWith("Could not load")); } -#endregion + #endregion -#region RunAsync + #region RunAsync [Test] public void RunAsync_AfterLoad_ReturnsRunnableSuite() @@ -433,71 +459,74 @@ public void RunAsync_BadFile_ReturnsNonRunnableSuite() Does.StartWith("Could not load")); } -#endregion + #endregion -#region StopRun + #region StopRun +#if THREAD_ABORT // Can't stop run on platforms without ability to abort thread [Test] - public void StopRun_WhenNoTestIsRunning_Succeeds() + public void StopRun_WhenNoTestIsRunning_DoesNotThrow([Values] bool force) { - _runner.StopRun(false); + Assert.DoesNotThrow(() => _runner.StopRun(force)); } - [Test] - public void StopRun_WhenTestIsRunning_StopsTest() + private static TestCaseData[] StopRunCases = new TestCaseData[] { - var tests = LoadSlowTests(); - var count = tests.TestCaseCount; - _runner.RunAsync(TestListener.NULL, TestFilter.Empty); - _runner.StopRun(false); - _runner.WaitForCompletion(Timeout.Infinite); + new TestCaseData(0, false).SetName("{m}(Simple dispatcher, cooperative stop)"), + new TestCaseData(0, true).SetName("{m}(Simple dispatcher, forced stop)"), + new TestCaseData(2, false).SetName("{m}(Parallel dispatcher, cooperative stop)"), + new TestCaseData(2, true).SetName("{m}(Parallel dispatcher, forced stop)") + }; - Assert.True(_runner.IsTestComplete, "Test is not complete"); + [TestCaseSource(nameof(StopRunCases))] + public void StopRun_WhenTestIsRunning_StopsTest(int workers, bool force) + { + var tests = LoadSlowTests(workers); + var count = tests.TestCaseCount; + var stopType = force ? "forced stop" : "cooperative stop"; - if (_runner.Result.ResultState != ResultState.Success) // Test may have finished before we stopped it - { - Assert.That(_runner.Result.ResultState, Is.EqualTo(ResultState.Cancelled)); - Assert.That(_runner.Result.PassCount, Is.LessThan(count)); - } - } + _runner.RunAsync(this, TestFilter.Empty); -#endregion + // Ensure that at least one test started, otherwise we aren't testing anything! + SpinWait.SpinUntil(() => _testStartedCount > 0, CANCEL_TEST_DELAY); -#region Cancel Run + _runner.StopRun(force); - [Test] - public void CancelRun_WhenNoTestIsRunning_Succeeds() - { - _runner.StopRun(true); - } + var completionWasSignaled = _runner.WaitForCompletion(CANCEL_TEST_DELAY); - [Test] - public void CancelRun_WhenTestIsRunning_StopsTest() - { - var tests = LoadSlowTests(); - var count = tests.TestCaseCount; - _runner.RunAsync(TestListener.NULL, TestFilter.Empty); - _runner.StopRun(true); + // Use Assert.Multiple so we can see everything that went wrong at one time + Assert.Multiple(() => + { + Assert.True(completionWasSignaled, "Runner never signaled completion"); + Assert.True(_runner.IsTestComplete, "Test is not recorded as complete"); - // When cancelling, the completion event may not be signalled, - // so we only wait a short time before checking. - _runner.WaitForCompletion(Timeout.Infinite); + if (_activeTests.Count > 0) + { + var sb = new StringBuilder("The following tests never terminated:" + Environment.NewLine); + foreach (var name in _activeTests.Keys) + sb.AppendLine($" * {name}"); + Assert.Fail(sb.ToString()); + } - Assert.True(_runner.IsTestComplete, "Test is not complete"); + Assert.That(_suiteStartedCount, Is.GreaterThan(0), "No suites started"); + Assert.That(_testStartedCount, Is.GreaterThan(0), "No test cases started"); + Assert.That(_suiteFinishedCount, Is.EqualTo(_suiteStartedCount), $"Not all suites terminated after {stopType}"); + Assert.That(_testFinishedCount, Is.EqualTo(_testStartedCount), $"Not all test cases terminated after {stopType}"); - if (_runner.Result.ResultState != ResultState.Success) - { - Assert.That(_runner.Result.ResultState, Is.EqualTo(ResultState.Cancelled)); - Assert.That(_runner.Result.PassCount, Is.LessThan(count)); - } + Assert.That(_runner.Result.ResultState, Is.EqualTo(ResultState.Cancelled), $"Invalid ResultState after {stopType}"); + Assert.That(_runner.Result.PassCount, Is.LessThan(count), $"All tests passed in spite of {stopType}"); + }); } +#endif -#endregion + #endregion -#region ITestListener Implementation + #region ITestListener Implementation void ITestListener.TestStarted(ITest test) { + _activeTests.Add(test.Name, true); + if (test.IsSuite) _suiteStartedCount++; else @@ -506,7 +535,13 @@ void ITestListener.TestStarted(ITest test) void ITestListener.TestFinished(ITestResult result) { - if (!result.Test.IsSuite) + _activeTests.Remove(result.Test.Name); + + if (result.Test.IsSuite) + { + _suiteFinishedCount++; + } + else { _testFinishedCount++; @@ -562,9 +597,12 @@ private ITest LoadMockAssembly(IDictionary settings) settings); } - private ITest LoadSlowTests() + private ITest LoadSlowTests(int workers) { - return _runner.Load(Path.Combine(TestContext.CurrentContext.TestDirectory, SLOW_TESTS_FILE), EMPTY_SETTINGS); + var settings = new Dictionary(); + settings.Add(FrameworkPackageSettings.NumberOfTestWorkers, workers); + + return _runner.Load(Path.Combine(TestContext.CurrentContext.TestDirectory, SLOW_TESTS_FILE), settings); } private void CheckParameterOutput(ITestResult result) @@ -577,6 +615,6 @@ private void CheckParameterOutput(ITestResult result) "Parameter Y = 7" + Environment.NewLine)); } -#endregion + #endregion } } diff --git a/src/NUnitFramework/tests/Assertions/AdhocTestExecutionTests.cs b/src/NUnitFramework/tests/Assertions/AdhocTestExecutionTests.cs index 7f65733dfb..0fe0f8a869 100644 --- a/src/NUnitFramework/tests/Assertions/AdhocTestExecutionTests.cs +++ b/src/NUnitFramework/tests/Assertions/AdhocTestExecutionTests.cs @@ -3,7 +3,7 @@ using System.Reflection; using NUnit.Framework.Internal; -#if NET35 || NET40 || NET45 +#if NET35 || NET40 || NET45 || NET46 // NET46 should be removed when the framework project adds a net46 target using System.Runtime.Remoting.Messaging; #endif @@ -47,7 +47,7 @@ public void CanCallAssertWithoutTestExecutionContext(MethodInfo method) throw testException; } -#if !(NET35 || NET40 || NET45) +#if !(NET35 || NET40 || NET45 || NET46) // NET46 should be removed when the framework project adds a net46 target private TestExecutionContext ClearExecutionContext() { var savedContext = TestExecutionContext.CurrentContext; @@ -121,7 +121,7 @@ static public void TestFailingAssumption() static public void TestFailingWarning() { - // Warnings don't throw at all. They are of no use in adhoc execution. + // Warnings don't throw at all. They are of no use in ad-hoc execution. Assert.That(() => Warn.Unless(true, Is.False), Throws.Nothing); } diff --git a/src/NUnitFramework/tests/Assertions/AssertIgnoreTests.cs b/src/NUnitFramework/tests/Assertions/AssertIgnoreTests.cs index e9e27ce032..9f9cfea6f2 100644 --- a/src/NUnitFramework/tests/Assertions/AssertIgnoreTests.cs +++ b/src/NUnitFramework/tests/Assertions/AssertIgnoreTests.cs @@ -90,7 +90,7 @@ public void IgnoreWorksFromSetUp() // TODO: Decide whether to pass Ignored state to containing fixture //Assert.AreEqual(ResultState.Ignored, fixtureResult.ResultState); - foreach (TestResult testResult in fixtureResult.Children) + foreach (var testResult in fixtureResult.Children) Assert.AreEqual(ResultState.Ignored, testResult.ResultState); } diff --git a/src/NUnitFramework/tests/Assertions/AssertMultipleTests.cs b/src/NUnitFramework/tests/Assertions/AssertMultipleTests.cs index 71b8998714..4d5016f7d3 100644 --- a/src/NUnitFramework/tests/Assertions/AssertMultipleTests.cs +++ b/src/NUnitFramework/tests/Assertions/AssertMultipleTests.cs @@ -106,8 +106,12 @@ private ITestResult CheckResult(string methodName, ResultState expectedResultSta Assert.That(result.ResultState, Is.EqualTo(expectedResultState), "ResultState"); Assert.That(result.AssertCount, Is.EqualTo(expectedAsserts), "AssertCount"); Assert.That(result.AssertionResults.Count, Is.EqualTo(assertionMessageRegex.Length), "Number of AssertionResults"); - if (result.ResultState.Status == TestStatus.Failed) - Assert.That(result.StackTrace, Is.Not.Null.And.Contains(methodName), "StackTrace"); + + PlatformInconsistency.MonoMethodInfoInvokeLosesStackTrace.SkipOnAffectedPlatform(() => + { + if (result.ResultState.Status == TestStatus.Failed) + Assert.That(result.StackTrace, Is.Not.Null.And.Contains(methodName), "StackTrace"); + }); if (result.AssertionResults.Count > 0) { @@ -130,7 +134,11 @@ private ITestResult CheckResult(string methodName, ResultState expectedResultSta // NOTE: This test expects the stack trace to contain the name of the method // that actually caused the failure. To ensure it is not optimized away, we // compile the testdata assembly with optimizations disabled. - Assert.That(assertion.StackTrace, Is.Not.Null.And.Contains(methodName), errmsg); + + PlatformInconsistency.MonoMethodInfoInvokeLosesStackTrace.SkipOnAffectedPlatform(() => + { + Assert.That(assertion.StackTrace, Is.Not.Null.And.Contains(methodName), errmsg); + }); } } diff --git a/src/NUnitFramework/tests/Assertions/AssertThatTests.cs b/src/NUnitFramework/tests/Assertions/AssertThatTests.cs index 8550c64aeb..b003afc102 100644 --- a/src/NUnitFramework/tests/Assertions/AssertThatTests.cs +++ b/src/NUnitFramework/tests/Assertions/AssertThatTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -266,7 +266,7 @@ public void AssertionsAreCountedCorrectly() ITestResult result = TestBuilder.RunTestFixture(typeof(AssertCountFixture)); int totalCount = 0; - foreach (TestResult childResult in result.Children) + foreach (var childResult in result.Children) { int expectedCount = childResult.Name == "ThreeAsserts" ? 3 : 1; Assert.That(childResult.AssertCount, Is.EqualTo(expectedCount), "Bad count for {0}", childResult.Name); @@ -307,7 +307,7 @@ public void FailingAssertion_CallsExceptionStringFunc() // Act var ex = Assert.Throws(() => Assert.That(1 + 1 == 1, getExceptionMessage)); - + // Assert Assert.That(ex.Message, Does.Contain("Func was called")); Assert.That(funcWasCalled, "The getExceptionMessage function was not called when it should have been."); @@ -332,12 +332,11 @@ public void AssertThatFailure() Assert.That(async () => await AsyncReturnOne(), Is.EqualTo(2))); } -#if PLATFORM_DETECTION [Test, Platform(Exclude="Linux", Reason="Intermittent failures on Linux")] public void AssertThatErrorTask() { #if NET45 - var exception = + var exception = #endif Assert.Throws(() => Assert.That(async () => await ThrowInvalidOperationExceptionTask(), Is.EqualTo(1))); @@ -346,13 +345,12 @@ public void AssertThatErrorTask() Assert.That(exception.StackTrace, Does.Contain("ThrowInvalidOperationExceptionTask")); #endif } -#endif [Test] public void AssertThatErrorGenericTask() { #if NET45 - var exception = + var exception = #endif Assert.Throws(() => Assert.That(async () => await ThrowInvalidOperationExceptionGenericTask(), Is.EqualTo(1))); @@ -366,7 +364,7 @@ public void AssertThatErrorGenericTask() public void AssertThatErrorVoid() { #if NET45 - var exception = + var exception = #endif Assert.Throws(() => Assert.That(async () => { await ThrowInvalidOperationExceptionGenericTask(); }, Is.EqualTo(1))); diff --git a/src/NUnitFramework/tests/Assertions/AssertThrowsAsyncTests.cs b/src/NUnitFramework/tests/Assertions/AssertThrowsAsyncTests.cs index da48f58eb5..9fa87d20e4 100644 --- a/src/NUnitFramework/tests/Assertions/AssertThrowsAsyncTests.cs +++ b/src/NUnitFramework/tests/Assertions/AssertThrowsAsyncTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -50,7 +50,7 @@ public void GenericThrowsAsyncSucceedsWithDelegate() [Test] public void ThrowsConstraintSucceedsWithDelegate() - { + { // Without cast, delegate is ambiguous before C# 3.0. Assert.That((AsyncTestDelegate)delegate { throw new ArgumentException(); }, Throws.Exception.TypeOf()); @@ -86,7 +86,7 @@ public void GenericThrowsAsyncReturnsCorrectException() [Test] public void ThrowsConstraintReturnsCorrectException() - { + { // Without cast, delegate is ambiguous before C# 3.0. Assert.That( (AsyncTestDelegate)delegate { return AsyncTestDelegates.Delay(5).ContinueWith(t => { throw new ArgumentException(); }, TaskScheduler.Default); }, @@ -217,7 +217,7 @@ public void DerivedExceptionThrown() var ex = CatchException(() => Assert.ThrowsAsync(AsyncTestDelegates.ThrowsArgumentException)); Assert.That(ex.Message, Does.StartWith( " Expected: " + Environment.NewLine + - " But was: Assert.ThrowsAsync(AsyncTestDelegates.ThrowsArgumentExceptionAsync)); Assert.That(ex.Message, Does.StartWith( " Expected: " + Environment.NewLine + - " But was: { Console.WriteLine(2); TestContext.WriteLine(3); throw new Exception("test"); }, + () => { Console.WriteLine(2); TestContext.WriteLine(3); throw new Exception("test"); }, Throws.Exception); Console.WriteLine(4); @@ -97,7 +97,7 @@ public void GenericThrowsSucceedsWithLambda() [Test] public void ThrowsConstraintSucceedsWithLambda() { - Assert.That(() => { throw new ArgumentException(); }, + Assert.That(() => { throw new ArgumentException(); }, Throws.Exception.TypeOf()); } @@ -116,8 +116,8 @@ public void GenericThrowsReturnsCorrectException() [Test] public void ThrowsReturnsCorrectException() - { - var ex = Assert.Throws(typeof(ArgumentException), + { + var ex = Assert.Throws(typeof(ArgumentException), delegate { throw new ArgumentException("myMessage", "myParam"); } ) as ArgumentException; Assert.IsNotNull(ex, "No ArgumentException thrown"); @@ -130,7 +130,7 @@ public void ThrowsReturnsCorrectException() [Test] public void NoExceptionThrown() { - var ex = CatchException(() => + var ex = CatchException(() => Assert.Throws(TestDelegates.ThrowsNothing)); Assert.That(ex.Message, Is.EqualTo( @@ -143,7 +143,7 @@ public void NoExceptionThrown() [Test] public void UnrelatedExceptionThrown() { - var ex = CatchException(() => + var ex = CatchException(() => Assert.Throws(TestDelegates.ThrowsNullReferenceException)); Assert.That(ex.Message, Does.StartWith( @@ -156,7 +156,7 @@ public void UnrelatedExceptionThrown() [Test] public void BaseExceptionThrown() { - var ex = CatchException(() => + var ex = CatchException(() => Assert.Throws(TestDelegates.ThrowsSystemException)); Assert.That(ex.Message, Does.StartWith( @@ -169,12 +169,12 @@ public void BaseExceptionThrown() [Test, SetUICulture("en-US")] public void DerivedExceptionThrown() { - var ex = CatchException(() => + var ex = CatchException(() => Assert.Throws(TestDelegates.ThrowsArgumentException)); Assert.That(ex.Message, Does.StartWith( " Expected: " + Environment.NewLine + - " But was: { Assert.Fail(); }, + Assert.That(() => { Assert.Fail(); }, Throws.Exception.TypeOf()); CheckForSpuriousAssertionResults(); @@ -205,7 +205,7 @@ public void AssertDoesNotThrowSucceeds() [Test] public void AssertDoesNotThrowFails() { - var ex = CatchException(() => + var ex = CatchException(() => Assert.DoesNotThrow(TestDelegates.ThrowsArgumentException)); Assert.That(ex, Is.Not.Null.With.TypeOf()); diff --git a/src/NUnitFramework/tests/Assertions/AssertionHelperTests.cs b/src/NUnitFramework/tests/Assertions/AssertionHelperTests.cs index 0dd7481bd0..321bbfcc9e 100644 --- a/src/NUnitFramework/tests/Assertions/AssertionHelperTests.cs +++ b/src/NUnitFramework/tests/Assertions/AssertionHelperTests.cs @@ -745,7 +745,6 @@ public void NotSamePathOrUnder_RespectCase() #region BinarySerializable -#if !NETCOREAPP1_1 [Test] public void BinarySerializableConstraint() { @@ -753,13 +752,11 @@ public void BinarySerializableConstraint() Expect(constraint, TypeOf()); Expect(constraint.ToString(), EqualTo("")); } -#endif #endregion #region XmlSerializable -#if !NETCOREAPP1_1 [Test] public void XmlSerializableConstraint() { @@ -767,7 +764,6 @@ public void XmlSerializableConstraint() Expect(constraint, TypeOf()); Expect(constraint.ToString(), EqualTo("")); } -#endif #endregion diff --git a/src/NUnitFramework/tests/Assertions/CollectionAssertTest.cs b/src/NUnitFramework/tests/Assertions/CollectionAssertTest.cs index c0036eca6d..1c9b4e0141 100644 --- a/src/NUnitFramework/tests/Assertions/CollectionAssertTest.cs +++ b/src/NUnitFramework/tests/Assertions/CollectionAssertTest.cs @@ -108,7 +108,8 @@ public void UniqueFailure() { var expectedMessage = " Expected: all items unique" + Environment.NewLine + - " But was: < \"x\", \"y\", \"x\" >" + Environment.NewLine; + " But was: < \"x\", \"y\", \"x\" >" + Environment.NewLine + + " Not unique items: < \"x\" >" + Environment.NewLine; var ex = Assert.Throws(() => CollectionAssert.AllItemsAreUnique(new SimpleObjectCollection("x", "y", "x"))); Assert.That(ex.Message, Is.EqualTo(expectedMessage)); @@ -117,8 +118,27 @@ public void UniqueFailure() [Test] public void UniqueFailure_WithTwoNulls() { - Assert.Throws( + var expectedMessage = + " Expected: all items unique" + Environment.NewLine + + " But was: < \"x\", null, \"y\", null, \"z\" >" + Environment.NewLine + + " Not unique items: < null >" + Environment.NewLine; + + var ex = Assert.Throws( () => CollectionAssert.AllItemsAreUnique(new SimpleObjectCollection("x", null, "y", null, "z"))); + Assert.That(ex.Message, Is.EqualTo(expectedMessage)); + } + + [Test] + public void UniqueFailure_WithMultipleNonUniques() + { + var collection = new SimpleObjectCollection("x", "y", "x", "x", "z", "y"); + var expectedMessage = + " Expected: all items unique" + Environment.NewLine + + " But was: < \"x\", \"y\", \"x\", \"x\", \"z\", \"y\" >" + Environment.NewLine + + " Not unique items: < \"x\", \"y\" >" + Environment.NewLine; + + var ex = Assert.Throws(() => CollectionAssert.AllItemsAreUnique(collection)); + Assert.That(ex.Message, Is.EqualTo(expectedMessage)); } [Test] @@ -149,7 +169,7 @@ public void UniqueFailure_ElementTypeIsNotSealed_NUnitEqualityIsUsed() Assert.Throws(() => CollectionAssert.AllItemsAreUnique(collection)); } - static readonly IEnumerable RANGE = Enumerable.Range(0, 10000); + static readonly IEnumerable RANGE = Enumerable.Range(0, 10_000); static readonly IEnumerable[] PerformanceData = { @@ -610,7 +630,8 @@ public void IsSubsetOf_Fails() var expectedMessage = " Expected: subset of < \"y\", \"z\", \"a\" >" + Environment.NewLine + - " But was: < \"x\", \"y\", \"z\" >" + Environment.NewLine; + " But was: < \"x\", \"y\", \"z\" >" + Environment.NewLine + + " Extra items: < \"x\" >" + Environment.NewLine; var ex = Assert.Throws(() => CollectionAssert.IsSubsetOf(set1,set2)); Assert.That(ex.Message, Is.EqualTo(expectedMessage)); @@ -752,8 +773,8 @@ public void ReferenceEqualsFailsWhenUsed() [Test] public void ValueTupleAreEqual() { - var set1 = new SimpleEnumerable(ValueTuple.Create(1,2,3), ValueTuple.Create(1, 2, 3), ValueTuple.Create(1, 2, 3)); - var set2 = new SimpleEnumerable(ValueTuple.Create(1,2,3), ValueTuple.Create(1, 2, 3), ValueTuple.Create(1, 2, 3)); + var set1 = new SimpleEnumerable((1, 2, 3), (1, 2, 3), (1, 2, 3)); + var set2 = new SimpleEnumerable((1, 2, 3), (1, 2, 3), (1, 2, 3)); CollectionAssert.AreEqual(set1, set2); CollectionAssert.AreEqual(set1, set2, new TestComparer()); @@ -764,8 +785,8 @@ public void ValueTupleAreEqual() [Test] public void ValueTupleAreEqualFail() { - var set1 = new SimpleEnumerable(ValueTuple.Create(1, 2, 3), ValueTuple.Create(1, 2, 3), ValueTuple.Create(1, 2, 3)); - var set2 = new SimpleEnumerable(ValueTuple.Create(1, 2, 3), ValueTuple.Create(1, 2, 3), ValueTuple.Create(1, 2, 4)); + var set1 = new SimpleEnumerable((1, 2, 3), (1, 2, 3), (1, 2, 3)); + var set2 = new SimpleEnumerable((1, 2, 3), (1, 2, 3), (1, 2, 4)); var expectedMessage = " Expected and actual are both " + Environment.NewLine + @@ -780,14 +801,14 @@ public void ValueTupleAreEqualFail() [Test] public void ElementsWithinTuplesAreComparedUsingNUnitEqualityComparer() { - var a = new Dictionary>>() + var a = new Dictionary)>() { - { "key", ValueTuple.Create("name", new Dictionary())} + { "key", ("name", new Dictionary())} }; - var b = new Dictionary>>() + var b = new Dictionary)>() { - { "key", ValueTuple.Create("name", new Dictionary())} + { "key", ("name", new Dictionary())} }; CollectionAssert.AreEquivalent(a, b); diff --git a/src/NUnitFramework/tests/Assertions/ConditionAssertTests.cs b/src/NUnitFramework/tests/Assertions/ConditionAssertTests.cs index 467eaf39f5..0d88c0e2f2 100644 --- a/src/NUnitFramework/tests/Assertions/ConditionAssertTests.cs +++ b/src/NUnitFramework/tests/Assertions/ConditionAssertTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -96,7 +96,7 @@ public void IsFalseFailsForNullable(bool? actual, string expectedButWas) var ex = Assert.Throws(() => Assert.IsFalse(actual)); Assert.That(ex.Message, Is.EqualTo(expectedMessage)); } - + [Test] public void IsNull() { @@ -113,7 +113,7 @@ public void IsNullFails() var ex = Assert.Throws(() => Assert.IsNull(s1)); Assert.That(ex.Message, Is.EqualTo(expectedMessage)); } - + [Test] public void IsNotNull() { @@ -130,7 +130,7 @@ public void IsNotNullFails() var ex = Assert.Throws(() => Assert.IsNotNull(null)); Assert.That(ex.Message, Is.EqualTo(expectedMessage)); } - + [Test] public void IsNaN() { @@ -154,10 +154,8 @@ public void IsEmpty() Assert.IsEmpty( new int[0], "Failed on empty Array" ); Assert.IsEmpty((IEnumerable)new int[0], "Failed on empty IEnumerable"); -#if !NETCOREAPP1_1 Assert.IsEmpty( new ArrayList(), "Failed on empty ArrayList" ); Assert.IsEmpty( new Hashtable(), "Failed on empty Hashtable" ); -#endif } [Test] @@ -199,7 +197,7 @@ public void IsEmptyFailsOnNonEmptyIEnumerable() var ex = Assert.Throws(() => Assert.IsEmpty((IEnumerable)new int[] { 1, 2, 3 })); Assert.That(ex.Message, Is.EqualTo(expectedMessage)); } - + [Test] public void IsNotEmpty() { @@ -209,14 +207,12 @@ public void IsNotEmpty() Assert.IsNotEmpty( array, "Failed on Array" ); Assert.IsNotEmpty( (IEnumerable)array, "Failed on IEnumerable" ); -#if !NETCOREAPP1_1 ArrayList list = new ArrayList(array); Hashtable hash = new Hashtable(); hash.Add("array", array); Assert.IsNotEmpty(list, "Failed on ArrayList"); Assert.IsNotEmpty(hash, "Failed on Hashtable"); -#endif } [Test] @@ -249,7 +245,6 @@ public void IsNotEmptyFailsOnEmptyIEnumerable() Assert.That(ex.Message, Is.EqualTo(expectedMessage)); } -#if !NETCOREAPP1_1 [Test] public void IsNotEmptyFailsOnEmptyArrayList() { @@ -269,6 +264,5 @@ public void IsNotEmptyFailsOnEmptyHashTable() var ex = Assert.Throws(() => Assert.IsNotEmpty(new Hashtable())); Assert.That(ex.Message, Is.EqualTo(expectedMessage)); } -#endif } } diff --git a/src/NUnitFramework/tests/Assertions/WarningTests.cs b/src/NUnitFramework/tests/Assertions/WarningTests.cs index f085798b52..76f4c8c95d 100644 --- a/src/NUnitFramework/tests/Assertions/WarningTests.cs +++ b/src/NUnitFramework/tests/Assertions/WarningTests.cs @@ -265,11 +265,7 @@ private static async Task ThrowExceptionGenericTask() // See https://github.com/nunit/nunit/pull/2431#issuecomment-328404432. [TestCase(nameof(WarningFixture.WarningSynchronous), 1)] [TestCase(nameof(WarningFixture.WarningInThreadStart), 2)] -#if !PLATFORM_DETECTION - [TestCase(nameof(WarningFixture.WarningInBeginInvoke), 5)] -#else [TestCase(nameof(WarningFixture.WarningInBeginInvoke), 5, ExcludePlatform = "mono", Reason = "Warning has no effect inside BeginInvoke on Mono")] -#endif [TestCase(nameof(WarningFixture.WarningInThreadPoolQueueUserWorkItem), 2)] #if TASK_PARALLEL_LIBRARY_API [TestCase(nameof(WarningFixture.WarningInTaskRun), 4)] diff --git a/src/NUnitFramework/tests/AsyncExecutionApiAdapter.Fixture-based.FSharp.cs b/src/NUnitFramework/tests/AsyncExecutionApiAdapter.Fixture-based.FSharp.cs new file mode 100644 index 0000000000..c224f3fd7f --- /dev/null +++ b/src/NUnitFramework/tests/AsyncExecutionApiAdapter.Fixture-based.FSharp.cs @@ -0,0 +1,90 @@ +// *********************************************************************** +// Copyright (c) 2018 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +#if TASK_PARALLEL_LIBRARY_API && !NET40 +namespace NUnit.Framework +{ + partial class AsyncExecutionApiAdapter + { + private sealed class FSharpTaskReturningTestMethodAdapter : AsyncExecutionApiAdapter + { + public override void Execute(AsyncTestDelegate asyncUserCode) + { + ExecuteFixture( + typeof(TestData.FSharp.AsyncExecutionApiAdapter.TaskReturningTestMethodFixture), + asyncUserCode); + } + + public override string ToString() => "[] member this.TestMethod() = async { … }"; + } + + private sealed class FSharpTaskReturningSetUpAdapter : AsyncExecutionApiAdapter + { + public override void Execute(AsyncTestDelegate asyncUserCode) + { + ExecuteFixture( + typeof(TestData.FSharp.AsyncExecutionApiAdapter.TaskReturningSetUpFixture), + asyncUserCode); + } + + public override string ToString() => "[] member this.SetUp() = async { … }"; + } + + private sealed class FSharpTaskReturningTearDownAdapter : AsyncExecutionApiAdapter + { + public override void Execute(AsyncTestDelegate asyncUserCode) + { + ExecuteFixture( + typeof(TestData.FSharp.AsyncExecutionApiAdapter.TaskReturningTearDownFixture), + asyncUserCode); + } + + public override string ToString() => "[] member this.TearDown() = async { … }"; + } + + private sealed class FSharpTaskReturningOneTimeSetUpAdapter : AsyncExecutionApiAdapter + { + public override void Execute(AsyncTestDelegate asyncUserCode) + { + ExecuteFixture( + typeof(TestData.FSharp.AsyncExecutionApiAdapter.TaskReturningOneTimeSetUpFixture), + asyncUserCode); + } + + public override string ToString() => "[] member this.OneTimeSetUp() = async { … }"; + } + + private sealed class FSharpTaskReturningOneTimeTearDownAdapter : AsyncExecutionApiAdapter + { + public override void Execute(AsyncTestDelegate asyncUserCode) + { + ExecuteFixture( + typeof(TestData.FSharp.AsyncExecutionApiAdapter.TaskReturningOneTimeTearDownFixture), + asyncUserCode); + } + + public override string ToString() => "[] member this.OneTimeTearDown() = async { … }"; + } + } +} +#endif diff --git a/src/NUnitFramework/tests/AsyncExecutionApiAdapter.cs b/src/NUnitFramework/tests/AsyncExecutionApiAdapter.cs index 5d0a32f712..a4c5e3fb50 100644 --- a/src/NUnitFramework/tests/AsyncExecutionApiAdapter.cs +++ b/src/NUnitFramework/tests/AsyncExecutionApiAdapter.cs @@ -39,6 +39,13 @@ public abstract partial class AsyncExecutionApiAdapter new TaskReturningTearDownAdapter(), new TaskReturningOneTimeSetUpAdapter(), new TaskReturningOneTimeTearDownAdapter(), +#if !NET40 + new FSharpTaskReturningTestMethodAdapter(), + new FSharpTaskReturningSetUpAdapter(), + new FSharpTaskReturningTearDownAdapter(), + new FSharpTaskReturningOneTimeSetUpAdapter(), + new FSharpTaskReturningOneTimeTearDownAdapter(), +#endif new AssertThrowsAsyncAdapter(), new AssertDoesNotThrowAsyncAdapter(), new AssertCatchAsyncAdapter(), diff --git a/src/NUnitFramework/tests/Attributes/ApartmentAttributeTests.cs b/src/NUnitFramework/tests/Attributes/ApartmentAttributeTests.cs index 5ad59199ab..bbbf95f570 100644 --- a/src/NUnitFramework/tests/Attributes/ApartmentAttributeTests.cs +++ b/src/NUnitFramework/tests/Attributes/ApartmentAttributeTests.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if APARTMENT_STATE using System; using System.Threading; using NUnit.Framework.Interfaces; @@ -31,22 +30,18 @@ namespace NUnit.Framework.Attributes { -#if PLATFORM_DETECTION [Platform(Include = "Win, Mono")] -#endif [TestFixture] public class ApartmentAttributeTests : ThreadingTests { -#if APARTMENT_STATE [Test] public void ApartmentStateUnknownIsNotRunnable() { var testSuite = TestBuilder.MakeFixture(typeof(ApartmentDataApartmentAttribute)); Assert.That(testSuite, Has.Property(nameof(TestSuite.RunState)).EqualTo(RunState.NotRunnable)); } -#endif -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [Test, Apartment(ApartmentState.STA)] @@ -59,9 +54,9 @@ public void TestWithRequiresSTARunsInSTA() [Test] #if THREAD_ABORT - [Timeout(10000)] + [Timeout(10_000)] #endif -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [Apartment(ApartmentState.STA)] @@ -72,9 +67,9 @@ public void TestWithTimeoutAndSTARunsInSTA() [TestFixture] #if THREAD_ABORT - [Timeout(10000)] + [Timeout(10_000)] #endif -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [Apartment(ApartmentState.STA)] @@ -87,7 +82,7 @@ public void RequiresSTACanBeSetOnTestFixtureWithTimeout() } } -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [TestFixture, Apartment(ApartmentState.STA)] @@ -147,7 +142,7 @@ public void RequiresMTAAttributeIsInheritable() } } -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [TestFixture] @@ -169,7 +164,7 @@ public void TestCasesShouldInheritApartmentFromFixture(int n) } } -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [TestFixture] @@ -193,7 +188,7 @@ public void TestCasesShouldRespectTheirApartment(int n) } } -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [TestFixture] @@ -215,7 +210,7 @@ public void TestCasesShouldInheritApartmentFromFixture(int n) } } -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [TestFixture] @@ -240,4 +235,3 @@ public void TestCasesShouldRespectTheirApartment(int n) } } } -#endif diff --git a/src/NUnitFramework/tests/Attributes/ApplyToTestTests.cs b/src/NUnitFramework/tests/Attributes/ApplyToTestTests.cs index 589e227d64..6b3c5243c9 100644 --- a/src/NUnitFramework/tests/Attributes/ApplyToTestTests.cs +++ b/src/NUnitFramework/tests/Attributes/ApplyToTestTests.cs @@ -41,7 +41,7 @@ public void SetUp() } #region CategoryAttribute - + [TestCase('!')] [TestCase('+')] [TestCase(',')] @@ -362,7 +362,6 @@ public void PairwiseAttributeSetsJoinTypeOnNonRunnableTest() #region PlatformAttribute -#if PLATFORM_DETECTION [Test] public void PlatformAttributeRunsTest() { @@ -409,7 +408,6 @@ string GetMyPlatform() } return "Win"; } -#endif #endregion @@ -432,9 +430,6 @@ public void RepeatAttributeSetsRepeatCountOnNonRunnableTest() #endregion -#if PARALLEL - -#if APARTMENT_STATE #region RequiresMTAAttribute [Test] @@ -476,7 +471,6 @@ public void RequiresSTAAttributeSetsApartmentStateOnNonRunnableTest() } #endregion -#endif #region RequiresThreadAttribute @@ -495,7 +489,6 @@ public void RequiresThreadAttributeSetsRequiresThreadOnNonRunnableTest() Assert.That(test.Properties.Get(PropertyNames.RequiresThread), Is.EqualTo(true)); } -#if APARTMENT_STATE [Test] public void RequiresThreadAttributeMaySetApartmentState() { @@ -504,10 +497,8 @@ public void RequiresThreadAttributeMaySetApartmentState() Assert.That(test.Properties.Get(PropertyNames.ApartmentState), Is.EqualTo(ApartmentState.STA)); } -#endif - #endregion -#endif + #endregion #region SequentialAttribute diff --git a/src/NUnitFramework/tests/Attributes/DerivedPropertyAttributeTests.cs b/src/NUnitFramework/tests/Attributes/DerivedPropertyAttributeTests.cs index 004a6c8675..df76415e18 100644 --- a/src/NUnitFramework/tests/Attributes/DerivedPropertyAttributeTests.cs +++ b/src/NUnitFramework/tests/Attributes/DerivedPropertyAttributeTests.cs @@ -35,10 +35,8 @@ public class DerivedPropertyAttributeTests [TestCase(typeof(ParallelizableAttribute), PropertyNames.ParallelScope, ParallelScope.Fixtures)] [TestCase(typeof(SetCultureAttribute), PropertyNames.SetCulture, "fr-FR")] [TestCase(typeof(SetUICultureAttribute), PropertyNames.SetUICulture, "fr-FR")] -#if APARTMENT_STATE [TestCase(typeof(ApartmentAttribute), PropertyNames.ApartmentState, ApartmentState.MTA)] [TestCase(typeof(ApartmentAttribute), PropertyNames.ApartmentState, ApartmentState.STA)] -#endif #if THREAD_ABORT [TestCase(typeof(TimeoutAttribute), PropertyNames.Timeout, 50)] #endif @@ -50,9 +48,7 @@ public void ConstructWithOneArg(Type attrType, string propName, T propValue) } [TestCase(typeof(ParallelizableAttribute), PropertyNames.ParallelScope, ParallelScope.Self)] -#if !NETCOREAPP1_1 [TestCase(typeof(RequiresThreadAttribute), PropertyNames.RequiresThread, true)] -#endif public void ConstructWithNoArgs(Type attrType, string propName, T propValue) { var attr = Reflect.Construct(attrType) as PropertyAttribute; diff --git a/src/NUnitFramework/tests/Attributes/LifeCycleAttributeParallelTests.cs b/src/NUnitFramework/tests/Attributes/LifeCycleAttributeParallelTests.cs new file mode 100644 index 0000000000..274c5a6fc6 --- /dev/null +++ b/src/NUnitFramework/tests/Attributes/LifeCycleAttributeParallelTests.cs @@ -0,0 +1,79 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System.Runtime.Serialization; + +namespace NUnit.Framework.Attributes +{ + [FixtureLifeCycle(LifeCycle.InstancePerTestCase)] + [Parallelizable(ParallelScope.All)] + public class LifeCycleAttributeParallelTests + { + static ObjectIDGenerator generator = new ObjectIDGenerator(); + int constructorCount = 0; + int setupCount = 0; + + public LifeCycleAttributeParallelTests() + { + OutputReferenceId("Ctor"); + constructorCount++; + } + + [SetUp] + public void SetUp() + { + OutputReferenceId("SetUp"); + setupCount++; + } + + [Test] + public void EnsureParallelTestsRunInNewInstance1() + { + OutputReferenceId("EnsureParallelTestsRunInNewInstance1"); + Assert.That(constructorCount, Is.EqualTo(1)); + Assert.That(setupCount, Is.EqualTo(1)); + } + + [Test] + public void EnsureParallelTestsRunInNewInstance2() + { + OutputReferenceId("EnsureParallelTestsRunInNewInstance2"); + Assert.That(constructorCount, Is.EqualTo(1)); + Assert.That(setupCount, Is.EqualTo(1)); + } + + [Test] + public void EnsureParallelTestsRunInNewInstance3() + { + OutputReferenceId("EnsureParallelTestsRunInNewInstance3"); + Assert.That(constructorCount, Is.EqualTo(1)); + Assert.That(setupCount, Is.EqualTo(1)); + } + + private void OutputReferenceId(string location) + { + long id = generator.GetId(this, out bool _); + TestContext.WriteLine($"{location}: {id}>"); + } + } +} diff --git a/src/NUnitFramework/tests/Attributes/LifeCycleAttributeTests.cs b/src/NUnitFramework/tests/Attributes/LifeCycleAttributeTests.cs new file mode 100644 index 0000000000..d7046f36b9 --- /dev/null +++ b/src/NUnitFramework/tests/Attributes/LifeCycleAttributeTests.cs @@ -0,0 +1,126 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System.Linq; +using NUnit.Framework.Interfaces; +using NUnit.Framework.Internal; +using NUnit.TestData.LifeCycleTests; +using NUnit.TestUtilities; + +namespace NUnit.Framework.Attributes +{ + [TestFixture] + public class LifeCycleAttributeTests + { + [Test] + public void SetupTearDownIsCalledOnce() + { + var fixture = TestBuilder.MakeFixture(typeof(SetupAndTearDownFixtureInstancePerTestCase)); + + ITestResult result = TestBuilder.RunTest(fixture); + + Assert.That(result.ResultState.Status, Is.EqualTo(TestStatus.Passed), result.Message); + } + + [Test] + public void OneTimeSetupTearDownIsCalledOnce() + { + StaticOneTimeSetupAndTearDownFixtureInstancePerTestCase.TotalOneTimeSetupCount = 0; + StaticOneTimeSetupAndTearDownFixtureInstancePerTestCase.TotalOneTimeTearDownCount = 0; + + var fixture = TestBuilder.MakeFixture(typeof(StaticOneTimeSetupAndTearDownFixtureInstancePerTestCase)); + + ITestResult result = TestBuilder.RunTest(fixture); + Assert.That(result.ResultState.Status, Is.EqualTo(TestStatus.Passed)); + + Assert.AreEqual(1, StaticOneTimeSetupAndTearDownFixtureInstancePerTestCase.TotalOneTimeSetupCount); + Assert.AreEqual(1, StaticOneTimeSetupAndTearDownFixtureInstancePerTestCase.TotalOneTimeTearDownCount); + } + + [Test] + public void InstanceOneTimeSetupTearDownThrows() + { + var fixture = TestBuilder.MakeFixture(typeof(InstanceOneTimeSetupAndTearDownFixtureInstancePerTestCase)); + + ITestResult result = TestBuilder.RunTest(fixture); + Assert.That(result.ResultState.Status, Is.EqualTo(TestStatus.Failed)); + Assert.That(result.ResultState.Label, Is.EqualTo("Error")); + } + + [Test] + public void InstancePerTestCaseCreatesAnInstanceForEachTestCase() + { + var fixture = TestBuilder.MakeFixture(typeof(CountingLifeCycleTestFixture)); + var attr = new FixtureLifeCycleAttribute(LifeCycle.InstancePerTestCase); + attr.ApplyToTest(fixture); + + ITestResult result = TestBuilder.RunTest(fixture); + Assert.That(result.ResultState.Status, Is.EqualTo(TestStatus.Passed)); + } + + [Test] + public void SingleInstanceSharesAnInstanceForEachTestCase() + { + var fixture = TestBuilder.MakeFixture(typeof(CountingLifeCycleTestFixture)); + var attr = new FixtureLifeCycleAttribute(LifeCycle.SingleInstance); + attr.ApplyToTest(fixture); + + ITestResult result = TestBuilder.RunTest(fixture); + + Assert.That( + result.Children.Select(t => t.ResultState), + Is.EquivalentTo(new[] { ResultState.Success, ResultState.Failure })); + } + + [Test] + public void InstancePerTestCaseShouldDisposeForEachTestCase() + { + DisposableLifeCycleFixtureInstancePerTestCase.DisposeCalls = 0; + var fixture = TestBuilder.MakeFixture(typeof(DisposableLifeCycleFixtureInstancePerTestCase)); + ITestResult result = TestBuilder.RunTest(fixture); + Assert.That(result.ResultState.Status, Is.EqualTo(TestStatus.Passed)); + + Assert.AreEqual(3, DisposableLifeCycleFixtureInstancePerTestCase.DisposeCalls); + } + + [Test] + public void InstancePerTestCaseWithRepeatShouldWorkAsExpected() + { + RepeatingLifeCycleFixtureInstancePerTestCase.RepeatCounter = 0; + var fixture = TestBuilder.MakeFixture(typeof(RepeatingLifeCycleFixtureInstancePerTestCase)); + ITestResult result = TestBuilder.RunTest(fixture); + Assert.That(result.ResultState.Status, Is.EqualTo(TestStatus.Passed)); + + Assert.AreEqual(3, RepeatingLifeCycleFixtureInstancePerTestCase.RepeatCounter); + } + + [Test] + public void ConstructorIsCalledOnceForEachTestInParallelTests() + { + var fixture = TestBuilder.MakeFixture(typeof(ParallelLifeCycleFixtureInstancePerTestCase)); + + ITestResult result = TestBuilder.RunTest(fixture); + Assert.That(result.ResultState.Status, Is.EqualTo(TestStatus.Passed)); + } + } +} diff --git a/src/NUnitFramework/tests/Attributes/MaxTimeTests.cs b/src/NUnitFramework/tests/Attributes/MaxTimeTests.cs index bd079fdbb6..dd1302d938 100644 --- a/src/NUnitFramework/tests/Attributes/MaxTimeTests.cs +++ b/src/NUnitFramework/tests/Attributes/MaxTimeTests.cs @@ -72,7 +72,7 @@ public void FailureReportHasPriorityOverMaxTime() { ITestResult result = TestBuilder.RunTestFixture(typeof(MaxTimeFixtureWithFailure)); Assert.AreEqual(ResultState.ChildFailure, result.ResultState); - result = (TestResult)result.Children.ToArray()[0]; + result = result.Children.ToArray()[0]; Assert.AreEqual(ResultState.Failure, result.ResultState); Assert.That(result.Message, Is.EqualTo("Intentional Failure")); } diff --git a/src/NUnitFramework/tests/Attributes/OneTimeSetUpTearDownTests.cs b/src/NUnitFramework/tests/Attributes/OneTimeSetUpTearDownTests.cs index a6053422ec..f5dfd5eb5e 100644 --- a/src/NUnitFramework/tests/Attributes/OneTimeSetUpTearDownTests.cs +++ b/src/NUnitFramework/tests/Attributes/OneTimeSetUpTearDownTests.cs @@ -374,7 +374,6 @@ public void DisposeCalledOnceWhenFixtureImplementsIDisposableAndHasTestCases() } } -#if !NETCOREAPP1_1 [TestFixture] class ChangesMadeInFixtureSetUp { @@ -406,5 +405,4 @@ public void TestThatChangesPersistUsingSeparateThread() Assert.AreEqual("en-GB", Thread.CurrentThread.CurrentUICulture.Name, "#CurrentUICulture"); } } -#endif } diff --git a/src/NUnitFramework/tests/Attributes/ParameterizedTestFixtureTests.cs b/src/NUnitFramework/tests/Attributes/ParameterizedTestFixtureTests.cs index de96c80181..0d7b4cd51d 100644 --- a/src/NUnitFramework/tests/Attributes/ParameterizedTestFixtureTests.cs +++ b/src/NUnitFramework/tests/Attributes/ParameterizedTestFixtureTests.cs @@ -24,9 +24,11 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; +using NUnit.Framework.Internal.Builders; using NUnit.TestUtilities; namespace NUnit.Framework.Attributes @@ -176,13 +178,32 @@ public void CanSpecifyCategory() Test fixture = TestBuilder.MakeFixture(typeof(NUnit.TestData.TestFixtureWithSingleCategory)); Assert.AreEqual("XYZ", fixture.Properties.Get(PropertyNames.Category)); } - + [Test] public void CanSpecifyMultipleCategories() { Test fixture = TestBuilder.MakeFixture(typeof(NUnit.TestData.TestFixtureWithMultipleCategories)); Assert.AreEqual(new string[] { "X", "Y", "Z" }, fixture.Properties[PropertyNames.Category]); } + + [Test] + public void NullArgumentForOrdinaryValueTypeParameterDoesNotThrowNullReferenceException() + { + Test fixture = TestBuilder.MakeFixture(typeof(NUnit.TestData.TestFixtureWithNullArgumentForOrdinaryValueTypeParameter)); + Assert.That(fixture.RunState, Is.EqualTo(RunState.NotRunnable)); + Assert.That(fixture.Properties.Get(PropertyNames.SkipReason), Is.EqualTo("No suitable constructor was found")); + } + + [Test] + public void NullArgumentForGenericParameterDoesNotThrowNullReferenceException() + { + Test parameterizedFixture = TestBuilder.MakeFixture(typeof(NUnit.TestData.TestFixtureWithNullArgumentForGenericParameter<>)); + ITest fixture = parameterizedFixture.Tests.Single(); + + Assert.That(fixture.RunState, Is.EqualTo(RunState.NotRunnable)); + Assert.That(fixture.Properties.Get(PropertyNames.SkipReason), Is.EqualTo( + "Fixture type contains generic parameters. You must either provide Type arguments or specify constructor arguments that allow NUnit to deduce the Type arguments.")); + } } [TestFixture(typeof(int))] diff --git a/src/NUnitFramework/tests/Attributes/RepeatAttributeTests.CustomMethodWrapper.cs b/src/NUnitFramework/tests/Attributes/RepeatAttributeTests.CustomMethodWrapper.cs new file mode 100644 index 0000000000..2b286c6852 --- /dev/null +++ b/src/NUnitFramework/tests/Attributes/RepeatAttributeTests.CustomMethodWrapper.cs @@ -0,0 +1,95 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Linq; +using System.Reflection; +using NUnit.Framework.Interfaces; + +namespace NUnit.Framework.Attributes +{ + public partial class RepeatAttributeTests + { + private sealed class CustomMethodWrapper : IMethodInfo + { + private readonly IMethodInfo _baseInfo; + private readonly Attribute[] _extraAttributes; + + public CustomMethodWrapper(IMethodInfo baseInfo, Attribute[] extraAttributes) + { + _baseInfo = baseInfo; + _extraAttributes = extraAttributes; + } + + public T[] GetCustomAttributes(bool inherit) where T : class + { + return _baseInfo.GetCustomAttributes(inherit) + .Concat(_extraAttributes.OfType()) + .ToArray(); + } + + public ITypeInfo TypeInfo => _baseInfo.TypeInfo; + + public MethodInfo MethodInfo => _baseInfo.MethodInfo; + + public string Name => _baseInfo.Name; + + public bool IsAbstract => _baseInfo.IsAbstract; + + public bool IsPublic => _baseInfo.IsPublic; + + public bool ContainsGenericParameters => _baseInfo.ContainsGenericParameters; + + public bool IsGenericMethod => _baseInfo.IsGenericMethod; + + public bool IsGenericMethodDefinition => _baseInfo.IsGenericMethodDefinition; + + public ITypeInfo ReturnType => _baseInfo.ReturnType; + + public Type[] GetGenericArguments() + { + return _baseInfo.GetGenericArguments(); + } + + public IParameterInfo[] GetParameters() + { + return _baseInfo.GetParameters(); + } + + public object Invoke(object fixture, params object[] args) + { + return _baseInfo.Invoke(fixture, args); + } + + public bool IsDefined(bool inherit) where T : class + { + return _baseInfo.IsDefined(inherit); + } + + public IMethodInfo MakeGenericMethod(params Type[] typeArguments) + { + return _baseInfo.MakeGenericMethod(typeArguments); + } + } + } +} diff --git a/src/NUnitFramework/tests/Attributes/RepeatAttributeTests.CustomTypeWrapper.cs b/src/NUnitFramework/tests/Attributes/RepeatAttributeTests.CustomTypeWrapper.cs new file mode 100644 index 0000000000..c0d577afa7 --- /dev/null +++ b/src/NUnitFramework/tests/Attributes/RepeatAttributeTests.CustomTypeWrapper.cs @@ -0,0 +1,131 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Linq; +using System.Reflection; +using NUnit.Framework.Interfaces; + +namespace NUnit.Framework.Attributes +{ + public partial class RepeatAttributeTests + { + private sealed class CustomTypeWrapper : ITypeInfo + { + private readonly ITypeInfo _baseInfo; + private readonly Attribute[] _extraMethodAttributes; + + public CustomTypeWrapper(ITypeInfo baseInfo, Attribute[] extraMethodAttributes) + { + _baseInfo = baseInfo; + _extraMethodAttributes = extraMethodAttributes; + } + + public IMethodInfo[] GetMethods(BindingFlags flags) + { + return _baseInfo.GetMethods(flags) + .Select(info => new CustomMethodWrapper(info, _extraMethodAttributes)) + .ToArray(); + } + + public Type Type => _baseInfo.Type; + + public ITypeInfo BaseType => _baseInfo.BaseType; + + public string Name => _baseInfo.Name; + + public string FullName => _baseInfo.FullName; + + public Assembly Assembly => _baseInfo.Assembly; + + public string Namespace => _baseInfo.Namespace; + + public bool IsAbstract => _baseInfo.IsAbstract; + + public bool IsGenericType => _baseInfo.IsGenericType; + + public bool ContainsGenericParameters => _baseInfo.ContainsGenericParameters; + + public bool IsGenericTypeDefinition => _baseInfo.IsGenericTypeDefinition; + + public bool IsSealed => _baseInfo.IsSealed; + + public bool IsStaticClass => _baseInfo.IsStaticClass; + + public object Construct(object[] args) + { + return _baseInfo.Construct(args); + } + + public ConstructorInfo GetConstructor(Type[] argTypes) + { + return _baseInfo.GetConstructor(argTypes); + } + + public T[] GetCustomAttributes(bool inherit) where T : class + { + return _baseInfo.GetCustomAttributes(inherit); + } + + public string GetDisplayName() + { + return _baseInfo.GetDisplayName(); + } + + public string GetDisplayName(object[] args) + { + return _baseInfo.GetDisplayName(args); + } + + public Type GetGenericTypeDefinition() + { + return _baseInfo.GetGenericTypeDefinition(); + } + + public bool HasConstructor(Type[] argTypes) + { + return _baseInfo.HasConstructor(argTypes); + } + + public bool HasMethodWithAttribute(Type attrType) + { + return _baseInfo.HasMethodWithAttribute(attrType); + } + + public bool IsDefined(bool inherit) where T : class + { + return _baseInfo.IsDefined(inherit); + } + + public bool IsType(Type type) + { + return _baseInfo.IsType(type); + } + + public ITypeInfo MakeGenericType(Type[] typeArgs) + { + return _baseInfo.MakeGenericType(typeArgs); + } + } + } +} diff --git a/src/NUnitFramework/tests/Attributes/RepeatAttributeTests.cs b/src/NUnitFramework/tests/Attributes/RepeatAttributeTests.cs index 974929f7ec..4b5090d99a 100644 --- a/src/NUnitFramework/tests/Attributes/RepeatAttributeTests.cs +++ b/src/NUnitFramework/tests/Attributes/RepeatAttributeTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -29,16 +29,19 @@ // #1 is feasible but doesn't provide much benefit // #2 requires infrastructure for dynamic test cases first using System; +using System.Linq; using System.Reflection; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; +using NUnit.Framework.Internal.Builders; +using NUnit.Framework.Internal.Commands; using NUnit.TestData.RepeatingTests; using NUnit.TestUtilities; namespace NUnit.Framework.Attributes { - [TestFixture] - public class RepeatAttributeTests + [TestFixture] + public partial class RepeatAttributeTests { [TestCase(typeof(RepeatFailOnFirstTryFixture), "Failed(Child)", 1)] [TestCase(typeof(RepeatFailOnSecondTryFixture), "Failed(Child)", 2)] @@ -64,6 +67,26 @@ public void RepeatWorksAsExpected(Type fixtureType, string outcome, int nTries) Assert.AreEqual(nTries, fixture.Count); } + [Test] + public void RepeatUpdatesCurrentRepeatCountPropertyOnEachAttempt() + { + RepeatingTestsFixtureBase fixture = (RepeatingTestsFixtureBase)Reflect.Construct(typeof(RepeatedTestVerifyAttempt)); + ITestResult result = TestBuilder.RunTestCase(fixture, nameof(RepeatedTestVerifyAttempt.PassesTwoTimes)); + + Assert.AreEqual(fixture.TearDownResults.Count, fixture.Count + 1, "expected the CurrentRepeatCount property to be one less than the number of executions"); + Assert.AreEqual(result.FailCount, 1, "expected that the test failed the last repetition"); + } + + [Test] + public void RepeatUpdatesCurrentRepeatCountPropertyOnGreenTest() + { + RepeatingTestsFixtureBase fixture = (RepeatingTestsFixtureBase)Reflect.Construct(typeof(RepeatedTestVerifyAttempt)); + ITestResult result = TestBuilder.RunTestCase(fixture, nameof(RepeatedTestVerifyAttempt.AlwaysPasses)); + + Assert.AreEqual(fixture.TearDownResults.Count, fixture.Count + 1, "expected the CurrentRepeatCount property to be one less than the number of executions"); + Assert.AreEqual(result.FailCount, 0, "expected that the test passes all repetitions without a failure"); + } + [Test] public void CategoryWorksWithRepeatedTest() { @@ -74,5 +97,59 @@ public void CategoryWorksWithRepeatedTest() Assert.AreEqual(1, categories.Count); Assert.AreEqual("SAMPLE", categories[0]); } + + [Test] + public void NotRunnableWhenIMethodInfoAbstractionReturnsMultipleIRepeatTestAttributes() + { + var fixtureSuite = new DefaultSuiteBuilder().BuildFrom(new CustomTypeWrapper( + new TypeWrapper(typeof(FixtureWithMultipleRepeatAttributesOnSameMethod)), + extraMethodAttributes: new Attribute[] + { + new CustomRepeatAttribute(), + new RepeatAttribute(2) + })); + + var method = fixtureSuite.Tests.Single(); + + Assert.That(method.RunState, Is.EqualTo(RunState.NotRunnable)); + Assert.That(method.Properties.Get(PropertyNames.SkipReason), Is.EqualTo("Multiple attributes that repeat a test may cause issues.")); + } + + [Test] + public void IRepeatTestAttributeIsEffectiveWhenAddedThroughIMethodInfoAbstraction() + { + var fixtureSuite = new DefaultSuiteBuilder().BuildFrom(new CustomTypeWrapper( + new TypeWrapper(typeof(FixtureWithMultipleRepeatAttributesOnSameMethod)), + extraMethodAttributes: new Attribute[] + { + new RepeatAttribute(2) + })); + + var fixtureInstance = new FixtureWithMultipleRepeatAttributesOnSameMethod(); + fixtureSuite.Fixture = fixtureInstance; + TestBuilder.RunTest(fixtureSuite, fixtureInstance); + + Assert.That(fixtureInstance.MethodRepeatCount, Is.EqualTo(2)); + } + + private sealed class FixtureWithMultipleRepeatAttributesOnSameMethod + { + public int MethodRepeatCount { get; private set; } + + // The IRepeatTest attributes are dynamically applied via CustomTypeWrapper. + [Test] + public void MethodWithMultipleRepeatAttributes() + { + MethodRepeatCount++; + } + } + + private sealed class CustomRepeatAttribute : Attribute, IRepeatTest + { + public TestCommand Wrap(TestCommand command) + { + throw new NotImplementedException(); + } + } } } diff --git a/src/NUnitFramework/tests/Attributes/RequiresThreadAttributeTests.cs b/src/NUnitFramework/tests/Attributes/RequiresThreadAttributeTests.cs index 765a031050..083d99dc91 100644 --- a/src/NUnitFramework/tests/Attributes/RequiresThreadAttributeTests.cs +++ b/src/NUnitFramework/tests/Attributes/RequiresThreadAttributeTests.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !NETCOREAPP1_1 using System; using System.Threading; using NUnit.Framework.Interfaces; @@ -46,7 +45,6 @@ public void TestWithRequiresThreadRunsSetUpAndTestOnSameThread() Assert.That(Thread.CurrentThread, Is.EqualTo(SetupThread)); } -#if APARTMENT_STATE [Test] public void ApartmentStateUnknownIsNotRunnable() { @@ -54,7 +52,7 @@ public void ApartmentStateUnknownIsNotRunnable() Assert.That(testSuite, Has.Property(nameof(TestSuite.RunState)).EqualTo(RunState.NotRunnable)); } -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [TestFixture] @@ -74,7 +72,7 @@ public void TestWithRequiresThreadWithMTAArgRunsOnSeparateThreadInMTA() Assert.That(Thread.CurrentThread, Is.Not.EqualTo(ParentThread)); } } -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Unix")] [TestFixture] public class ApartmentStateRequiredToFailOnUnixNetCoreTests @@ -101,7 +99,6 @@ public void TestWithRequiresThreadWithMTAArgRunsOnSeparateThreadInMTA() Assert.That(result.Message, Is.EqualTo("Apartment state cannot be set on this platform.")); } } -#endif #endif [TestFixture, RequiresThread] @@ -130,4 +127,3 @@ public void RequiresThreadAttributeIsInheritable() } } } -#endif diff --git a/src/NUnitFramework/tests/Attributes/SingleThreadedFixtureTests.cs b/src/NUnitFramework/tests/Attributes/SingleThreadedFixtureTests.cs index 1dcf88ec56..f714f2fe62 100644 --- a/src/NUnitFramework/tests/Attributes/SingleThreadedFixtureTests.cs +++ b/src/NUnitFramework/tests/Attributes/SingleThreadedFixtureTests.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !NETCOREAPP1_1 using System; using System.Linq; using System.Threading; @@ -54,14 +53,13 @@ public void TestWithRequiresThreadIsInvalid() CheckTestIsInvalid("RequiresThreadAttribute may not be specified"); } -#if APARTMENT_STATE [Test] public void TestWithDifferentApartmentIsInvalid() { CheckTestIsInvalid("may not specify a different apartment"); } -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [SingleThreaded] @@ -74,7 +72,6 @@ public void TestWithSameApartmentIsValid() Assert.That(Thread.CurrentThread.GetApartmentState(), Is.EqualTo(ApartmentState.MTA)); } } -#endif private void CheckTestIsInvalid(string reason) { @@ -85,8 +82,7 @@ private void CheckTestIsInvalid(string reason) } } -#if APARTMENT_STATE -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win")] #endif [SingleThreaded, Apartment(ApartmentState.STA)] @@ -106,6 +102,4 @@ public void TestWithSameApartmentStateIsValid() Assert.That(GetApartmentState(Thread.CurrentThread), Is.EqualTo(ApartmentState.STA)); } } -#endif } -#endif diff --git a/src/NUnitFramework/tests/Attributes/TestCaseAttributeTests.cs b/src/NUnitFramework/tests/Attributes/TestCaseAttributeTests.cs index 740c4dd3eb..13bf1e3214 100644 --- a/src/NUnitFramework/tests/Attributes/TestCaseAttributeTests.cs +++ b/src/NUnitFramework/tests/Attributes/TestCaseAttributeTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -23,6 +23,7 @@ using System; using System.Collections; +using System.Globalization; using System.Linq; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; @@ -162,7 +163,7 @@ public DateTimeOffset CanConvertExpectedResultStringToDateTimeOffset(DateTimeOff { return offset; } - + [TestCase(null)] public void CanPassNullAsFirstArgument(object a) { @@ -174,7 +175,7 @@ public void CanPassNullAsFirstArgument(object a) public void CanPassObjectArrayAsFirstArgument(object[] a) { } - + [TestCase(new object[] { "a", "b" })] public void CanPassArrayAsArgument(object[] array) { @@ -196,6 +197,12 @@ public void ArgumentsOfDifferentTypeAreCoalescedInObjectArray(object[] array) Assert.AreEqual("b", array[1]); } + [TestCase(new object[] { null })] + public void NullArgumentsAreCoalescedInObjectArray(object[] array) + { + Assert.That(array, Is.EqualTo(new object[] { null })); + } + [TestCase(ExpectedResult = null)] public object ResultCanBeNull() { @@ -297,7 +304,7 @@ public void CanSpecifyCategory() IList categories = test.Properties["Category"]; Assert.AreEqual(new string[] { "XYZ" }, categories); } - + [Test] public void CanSpecifyMultipleCategories() { @@ -306,7 +313,7 @@ public void CanSpecifyMultipleCategories() IList categories = test.Properties["Category"]; Assert.AreEqual(new string[] { "X", "Y", "Z" }, categories); } - + [Test] public void CanIgnoreIndividualTestCases() { @@ -316,15 +323,47 @@ public void CanIgnoreIndividualTestCases() Test testCase = TestFinder.Find($"{methodName}(1)", suite, false); Assert.That(testCase.RunState, Is.EqualTo(RunState.Runnable)); - + testCase = TestFinder.Find($"{methodName}(2)", suite, false); Assert.That(testCase.RunState, Is.EqualTo(RunState.Ignored)); - + testCase = TestFinder.Find($"{methodName}(3)", suite, false); Assert.That(testCase.RunState, Is.EqualTo(RunState.Ignored)); Assert.That(testCase.Properties.Get(PropertyNames.SkipReason), Is.EqualTo("Don't Run Me!")); } + [Test] + public void CanIgnoreIndividualTestCasesWithUntilDate() + { + var methodName = nameof(TestCaseAttributeFixture.MethodWithIgnoredWithUntilDateTestCases); + TestSuite suite = TestBuilder.MakeParameterizedMethodSuite( + typeof(TestCaseAttributeFixture), methodName); + Test testCase = TestFinder.Find($"{methodName}(1)", suite, false); + Assert.That(testCase.RunState, Is.EqualTo(RunState.Runnable)); + + string untilDateString = DateTimeOffset.Parse("4242-01-01", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal).ToString("u"); + testCase = TestFinder.Find($"{methodName}(2)", suite, false); + Assert.That(testCase.RunState, Is.EqualTo(RunState.Ignored)); + Assert.That(testCase.Properties.Get(PropertyNames.SkipReason), Is.EqualTo(string.Format("Ignoring until {0}. Should not run", untilDateString))); + Assert.That(testCase.Properties.Get(PropertyNames.IgnoreUntilDate), Is.EqualTo(untilDateString)); + + untilDateString = DateTimeOffset.Parse("1942-01-01", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal).ToString("u"); + + testCase = TestFinder.Find($"{methodName}(3)", suite, false); + Assert.That(testCase.RunState, Is.EqualTo(RunState.Runnable)); + Assert.That(testCase.Properties.Get(PropertyNames.IgnoreUntilDate), Is.EqualTo(untilDateString)); + + untilDateString = DateTimeOffset.Parse("4242-01-01T01:23:45Z", CultureInfo.InvariantCulture, DateTimeStyles.AssumeUniversal | DateTimeStyles.AdjustToUniversal).ToString("u"); + + testCase = TestFinder.Find($"{methodName}(4)", suite, false); + Assert.That(testCase.RunState, Is.EqualTo(RunState.Ignored)); + Assert.That(testCase.Properties.Get(PropertyNames.SkipReason), Is.EqualTo(string.Format("Ignoring until {0}. Don't Run Me!", untilDateString))); + Assert.That(testCase.Properties.Get(PropertyNames.IgnoreUntilDate), Is.EqualTo(untilDateString)); + + testCase = TestFinder.Find($"{methodName}(5)", suite, false); + Assert.That(testCase.RunState, Is.EqualTo(RunState.NotRunnable)); + } + [Test] public void CanMarkIndividualTestCasesExplicit() { @@ -334,16 +373,15 @@ public void CanMarkIndividualTestCasesExplicit() Test testCase = TestFinder.Find($"{methodName}(1)", suite, false); Assert.That(testCase.RunState, Is.EqualTo(RunState.Runnable)); - + testCase = TestFinder.Find($"{methodName}(2)", suite, false); Assert.That(testCase.RunState, Is.EqualTo(RunState.Explicit)); - + testCase = TestFinder.Find($"{methodName}(3)", suite, false); Assert.That(testCase.RunState, Is.EqualTo(RunState.Explicit)); Assert.That(testCase.Properties.Get(PropertyNames.SkipReason), Is.EqualTo("Connection failing")); } -#if PLATFORM_DETECTION [Test] public void CanIncludePlatform() { @@ -424,7 +462,7 @@ public void CanIncludeRuntime() bool isNetCore; Type monoRuntimeType = Type.GetType("Mono.Runtime", false); bool isMono = monoRuntimeType != null; -#if NETCOREAPP2_0 +#if NETCOREAPP isNetCore = true; #else isNetCore = false; @@ -463,7 +501,7 @@ public void CanExcludeRuntime() bool isNetCore; Type monoRuntimeType = Type.GetType("Mono.Runtime", false); bool isMono = monoRuntimeType != null; -#if NETCOREAPP2_0 +#if NETCOREAPP isNetCore = true; #else isNetCore = false; @@ -495,7 +533,6 @@ public void CanExcludeRuntime() Assert.That(testCase3.RunState, Is.EqualTo(RunState.Runnable)); } } -#endif [Test] public void TestNameIntrospectsArrayValues() diff --git a/src/NUnitFramework/tests/Attributes/TestCaseSourceTests.cs b/src/NUnitFramework/tests/Attributes/TestCaseSourceTests.cs index 9d3be8ba9c..69e915f080 100644 --- a/src/NUnitFramework/tests/Attributes/TestCaseSourceTests.cs +++ b/src/NUnitFramework/tests/Attributes/TestCaseSourceTests.cs @@ -29,6 +29,8 @@ using NUnit.Framework.Internal; using NUnit.TestData.TestCaseSourceAttributeFixture; using NUnit.TestUtilities; +using System; +using System.Globalization; namespace NUnit.Framework.Attributes { @@ -253,13 +255,42 @@ public void CanIgnoreIndividualTestCases() { TestSuite suite = TestBuilder.MakeParameterizedMethodSuite( typeof(TestCaseSourceAttributeFixture), nameof(TestCaseSourceAttributeFixture.MethodWithIgnoredTestCases)); + Assert.Multiple(() => + { + Test testCase = TestFinder.Find("MethodWithIgnoredTestCases(1)", suite, false); + Assert.That(testCase.RunState, Is.EqualTo(RunState.Runnable)); + + testCase = TestFinder.Find("MethodWithIgnoredTestCases(2)", suite, false); + Assert.That(testCase.RunState, Is.EqualTo(RunState.Ignored)); + Assert.That(testCase.Properties.Get(PropertyNames.SkipReason), Is.EqualTo("Don't Run Me!")); + }); + } - Test testCase = TestFinder.Find("MethodWithIgnoredTestCases(1)", suite, false); + [Test] + public void CanIgnoreIndividualTestCasesWithUntilDate() + { + TestSuite suite = TestBuilder.MakeParameterizedMethodSuite( + typeof(TestCaseSourceAttributeFixture), nameof(TestCaseSourceAttributeFixture.MethodWithIgnoredTestCases)); + + DateTimeOffset untilDate = DateTimeOffset.Parse("4242-01-01 00:00:00", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + + Test testCase = TestFinder.Find("MethodWithIgnoredTestCases(3)", suite, false); + Assert.That(testCase.RunState, Is.EqualTo(RunState.Ignored)); + Assert.That(testCase.Properties.Get(PropertyNames.SkipReason), Is.EqualTo(string.Format("Ignoring until {0}. Ignore Me Until The Future", untilDate.ToString("u")))); + Assert.That(testCase.Properties.Get(PropertyNames.IgnoreUntilDate), Is.EqualTo(untilDate.ToString("u"))); + + untilDate = DateTimeOffset.Parse("1492-01-01", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); + + testCase = TestFinder.Find("MethodWithIgnoredTestCases(4)", suite, false); Assert.That(testCase.RunState, Is.EqualTo(RunState.Runnable)); + Assert.That(testCase.Properties.Get(PropertyNames.IgnoreUntilDate), Is.EqualTo(untilDate.ToString("u"))); + + untilDate = DateTimeOffset.Parse("4242-01-01 12:42:33Z", CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal | DateTimeStyles.AssumeUniversal); - testCase = TestFinder.Find("MethodWithIgnoredTestCases(2)", suite, false); + testCase = TestFinder.Find("MethodWithIgnoredTestCases(5)", suite, false); Assert.That(testCase.RunState, Is.EqualTo(RunState.Ignored)); - Assert.That(testCase.Properties.Get(PropertyNames.SkipReason), Is.EqualTo("Don't Run Me!")); + Assert.That(testCase.Properties.Get(PropertyNames.SkipReason), Is.EqualTo(string.Format("Ignoring until {0}. Ignore Me Until The Future", untilDate.ToString("u")))); + Assert.That(testCase.Properties.Get(PropertyNames.IgnoreUntilDate), Is.EqualTo(untilDate.ToString("u"))); } [Test] diff --git a/src/NUnitFramework/tests/Attributes/TestFixtureSourceTests.cs b/src/NUnitFramework/tests/Attributes/TestFixtureSourceTests.cs index 2945d86733..85bdee857c 100644 --- a/src/NUnitFramework/tests/Attributes/TestFixtureSourceTests.cs +++ b/src/NUnitFramework/tests/Attributes/TestFixtureSourceTests.cs @@ -56,8 +56,8 @@ public void CheckArgument(Type type) public void CheckNotRunnable(Type type) { var suite = TestBuilder.MakeFixture(type); - Assert.That(suite.RunState, Is.EqualTo(RunState.NotRunnable)); - Assert.That(suite.Properties.Get(PropertyNames.SkipReason), Is.EqualTo(TestFixtureSourceAttribute.MUST_BE_STATIC)); + Assert.That(suite.Tests[0].RunState, Is.EqualTo(RunState.NotRunnable)); + Assert.That(suite.Tests[0].Properties.Get(PropertyNames.SkipReason), Is.EqualTo(TestFixtureSourceAttribute.MUST_BE_STATIC)); //var result = TestBuilder.RunTestSuite(suite, null); //Assert.That(result.ResultState, Is.EqualTo(ResultState.NotRunnable)); } @@ -141,10 +141,13 @@ public void NoNamespaceSourceWithSingleValue() { TestSuite suite = TestBuilder.MakeFixture(typeof(NoNamespaceTestFixtureSourceWithSingleValue)); Assert.That(suite.RunState, Is.EqualTo(RunState.Runnable)); - Assert.That(suite, Is.TypeOf()); + Assert.That(suite, Is.TypeOf()); Assert.That(suite.TestCaseCount, Is.EqualTo(1)); Assert.That(suite.Tests.Count, Is.EqualTo(1)); - Assert.That(suite.Tests[0], Is.TypeOf()); + Assert.That(suite.Tests[0], Is.TypeOf()); + Assert.That(suite.Tests[0].TestCaseCount, Is.EqualTo(1)); + Assert.That(suite.Tests[0].Tests.Count, Is.EqualTo(1)); + Assert.That(suite.Tests[0].Tests[0], Is.TypeOf()); } [Test] @@ -153,7 +156,9 @@ public void CanRunGenericFixtureSourceWithProperTypeArgsProvided() TestSuite suite = TestBuilder.MakeFixture(typeof(GenericFixtureSourceWithProperArgsProvided<>)); Assert.That(suite.RunState, Is.EqualTo(RunState.Runnable)); Assert.That(suite is ParameterizedFixtureSuite); - Assert.That(suite.Tests.Count, Is.EqualTo(GenericFixtureSource.Source.Length)); + Assert.That(suite.Tests.Count, Is.EqualTo(1)); + Assert.That(suite.Tests[0] is ParameterizedFixtureSuite); + Assert.That(suite.Tests[0].Tests.Count, Is.EqualTo(GenericFixtureSource.Source.Length)); } [Test] @@ -162,7 +167,9 @@ public void CanRunGenericFixtureSourceWithProperTypeAndConstructorArgsProvided() TestSuite suite = TestBuilder.MakeFixture(typeof(GenericFixtureSourceWithTypeAndConstructorArgs<>)); Assert.That(suite.RunState, Is.EqualTo(RunState.Runnable)); Assert.That(suite is ParameterizedFixtureSuite); - Assert.That(suite.Tests.Count, Is.EqualTo(GenericFixtureWithTypeAndConstructorArgsSource.Source.Length)); + Assert.That(suite.Tests.Count, Is.EqualTo(1)); + Assert.That(suite.Tests[0] is ParameterizedFixtureSuite); + Assert.That(suite.Tests[0].Tests.Count, Is.EqualTo(GenericFixtureWithTypeAndConstructorArgsSource.Source.Length)); } [Test] @@ -171,7 +178,9 @@ public void CanRunGenericFixtureSourceWithConstructorArgsProvidedAndTypeArgsDedu TestSuite suite = TestBuilder.MakeFixture(typeof(GenericFixtureSourceWithConstructorArgs<>)); Assert.That(suite.RunState, Is.EqualTo(RunState.Runnable)); Assert.That(suite is ParameterizedFixtureSuite); - Assert.That(suite.Tests.Count, Is.EqualTo(GenericFixtureWithConstructorArgsSource.Source.Length)); + Assert.That(suite.Tests.Count, Is.EqualTo(1)); + Assert.That(suite.Tests[0] is ParameterizedFixtureSuite); + Assert.That(suite.Tests[0].Tests.Count, Is.EqualTo(GenericFixtureWithConstructorArgsSource.Source.Length)); } } } diff --git a/src/NUnitFramework/tests/Attributes/ThreadingTests.cs b/src/NUnitFramework/tests/Attributes/ThreadingTests.cs index d80311b4ee..dad381a004 100644 --- a/src/NUnitFramework/tests/Attributes/ThreadingTests.cs +++ b/src/NUnitFramework/tests/Attributes/ThreadingTests.cs @@ -28,7 +28,6 @@ namespace NUnit.Framework.Attributes { public class ThreadingTests { -#if !NETCOREAPP1_1 protected Thread ParentThread { get; private set; } protected Thread SetupThread { get; private set; } protected ApartmentState ParentThreadApartment { get; private set; } @@ -66,6 +65,5 @@ protected static ApartmentState GetApartmentState(Thread thread) { return thread.GetApartmentState(); } -#endif } } diff --git a/src/NUnitFramework/tests/Attributes/TimeoutTests.cs b/src/NUnitFramework/tests/Attributes/TimeoutTests.cs index 3d20b83aef..62764cfdff 100644 --- a/src/NUnitFramework/tests/Attributes/TimeoutTests.cs +++ b/src/NUnitFramework/tests/Attributes/TimeoutTests.cs @@ -22,12 +22,14 @@ // *********************************************************************** using System; +using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Threading; using NUnit.Framework; using NUnit.Framework.Interfaces; using NUnit.Framework.Internal; +using NUnit.Framework.Internal.Abstractions; using NUnit.TestData; using NUnit.TestUtilities; @@ -36,6 +38,142 @@ namespace NUnit.Framework.Attributes [NonParallelizable] public class TimeoutTests : ThreadingTests { + private static bool _testRanToCompletion; + private static Action _testAction; + private static StubDebugger _debugger; + + [TearDown] + public void ResetTestCompletionFlag() + { + _testRanToCompletion = false; + } + + private const string FailureMessage = "The test has failed"; + private const string IgnoreMessage = "The test was ignored"; + private const string InconclusiveMessage = "The test was inconclusive"; + + public class TestAction + { + public string Name { get; } + public Action Action { get; } + public Action Assertion { get; } + + public TestAction(Action action, Action assertion, string name) + { + Action = action; + Assertion = assertion; + Name = name; + } + + public override string ToString() + { + return Name; + } + } + + private static IEnumerable PossibleTestOutcomes + { + get + { + yield return new TestAction(Assert.Pass, AssertPassedResult, "Pass"); + yield return new TestAction(() => throw new Exception(), AssertExceptionResult, "Exception"); + yield return new TestAction(() => Assert.Fail(FailureMessage), AssertFailResult, "Fail"); + yield return new TestAction(() => Assert.Ignore(IgnoreMessage), AssertIgnoreResult, "Ignore"); + yield return new TestAction(() => Assert.Inconclusive(InconclusiveMessage), AssertInconclusiveResult, "Inconclusive"); + yield return new TestAction(MultipleFail, AssertFailResult, "Multiple > Fail"); +#if !NET35 && !NET40 + yield return new TestAction(AsynchronousMultipleFail, AssertFailResult, "Multiple > Async Fail"); +#endif + } + } + + private static void MultipleFail() + { + Assert.Multiple(() => Assert.Fail(FailureMessage)); + } + +#if !NET35 && !NET40 + private static void AsynchronousMultipleFail() + { + Assert.Multiple(async () => + { + await System.Threading.Tasks.Task.Yield(); + Assert.Fail(FailureMessage); + }); + } +#endif + + private static void AssertPassedResult(ITestResult result) + { + Assert.That(result.ResultState, Is.EqualTo(ResultState.Success)); + } + + private static void AssertExceptionResult(ITestResult result) + { + Assert.That(result.ResultState.Status, Is.EqualTo(TestStatus.Failed)); + Assert.That(result.ResultState.Site, Is.EqualTo(FailureSite.Test)); + Assert.That(result.ResultState.Label, Is.EqualTo(ResultState.Error.Label)); + } + + private static void AssertFailResult(ITestResult result) + { + AssertOutcome(result, TestStatus.Failed, FailureMessage); + } + + private static void AssertIgnoreResult(ITestResult result) + { + AssertOutcome(result, TestStatus.Skipped, IgnoreMessage); + } + + private static void AssertInconclusiveResult(ITestResult result) + { + AssertOutcome(result, TestStatus.Inconclusive, InconclusiveMessage); + } + + private static void AssertOutcome(ITestResult result, TestStatus status, string message) + { + Assert.That(result.ResultState.Status, Is.EqualTo(status)); + Assert.That(result.ResultState.Site, Is.EqualTo(FailureSite.Test)); + Assert.That(result.Message, Is.EqualTo(message)); + } + + private class SampleTests + { + private const int TimeExceedingTimeout = 500; + + public const int Timeout = 50; + + [Timeout(Timeout)] + public void TestThatTimesOut() + { + Thread.Sleep(TimeExceedingTimeout); + _testRanToCompletion = true; + } + + [Timeout(Timeout)] + public void TestThatTimesOutAndInvokesAction() + { + Thread.Sleep(TimeExceedingTimeout); + _testRanToCompletion = true; + _testAction.Invoke(); + } + + [Timeout(Timeout)] + public void TestThatInvokesActionImmediately() + { + _testAction.Invoke(); + _testRanToCompletion = true; + } + + [Timeout(Timeout)] + public void TestThatAttachesDebuggerAndTimesOut() + { + _debugger.IsAttached = true; + Thread.Sleep(TimeExceedingTimeout); + _testRanToCompletion = true; + } + } + [Test, Timeout(500), SetCulture("fr-BE"), SetUICulture("es-BO")] public void TestWithTimeoutRespectsCulture() { @@ -49,7 +187,64 @@ public void TestWithTimeoutCurrentContextIsNotAnAdhocContext() Assert.That(TestExecutionContext.CurrentContext, Is.Not.TypeOf()); } -#if PLATFORM_DETECTION && THREAD_ABORT + [Test] + public void TestThatCompletesWithinTimeoutPeriodHasItsOriginalResultPropagated( + [ValueSource(nameof(PossibleTestOutcomes))] TestAction test, + [Values] bool isDebuggerAttached) + { + // given + _testAction = test.Action; + + var testThatCompletesWithoutTimeout = + TestBuilder.MakeTestCase(typeof(SampleTests), nameof(SampleTests.TestThatInvokesActionImmediately)); + + var debugger = new StubDebugger { IsAttached = isDebuggerAttached }; + + // when + var result = TestBuilder.RunTest(testThatCompletesWithoutTimeout, new SampleTests(), debugger); + + // then + test.Assertion.Invoke(result); + } + + [Test] + [TestCaseSource(nameof(PossibleTestOutcomes))] + public void TestThatTimesOutIsRanToCompletionAndItsResultIsPropagatedWhenDebuggerIsAttached(TestAction test) + { + // given + _testAction = test.Action; + + var testThatTimesOut = + TestBuilder.MakeTestCase(typeof(SampleTests), nameof(SampleTests.TestThatTimesOutAndInvokesAction)); + + var attachedDebugger = new StubDebugger { IsAttached = true }; + + // when + var result = TestBuilder.RunTest(testThatTimesOut, new SampleTests(), attachedDebugger); + + // then + Assert.That(_testRanToCompletion, () => "Test did not run to completion"); + + test.Assertion.Invoke(result); + } + + [Test] + public void TestThatTimesOutIsRanToCompletionWhenDebuggerIsAttachedBeforeTimeOut() + { + // give + var testThatAttachesDebuggerAndTimesOut = + TestBuilder.MakeTestCase(typeof(SampleTests), nameof(SampleTests.TestThatAttachesDebuggerAndTimesOut)); + + _debugger = new StubDebugger { IsAttached = false }; + + // when + var result = TestBuilder.RunTest(testThatAttachesDebuggerAndTimesOut, new SampleTests(), _debugger); + + // then + Assert.That(_testRanToCompletion, () => "Test did not run to completion"); + } + +#if THREAD_ABORT [Test, Timeout(500)] public void TestWithTimeoutRunsOnSameThread() { @@ -111,24 +306,23 @@ public void TimeoutCanBeSetOnTestFixture() } [Test] - public void TestTimeoutNotElapsed() + public void TimeoutCausesOtherwisePassingTestToFailWithoutDebuggerAttached() { - TimeoutTestCaseFixture fixture = new TimeoutTestCaseFixture(); - TestSuite suite = TestBuilder.MakeFixture(fixture); - TestMethod testMethod = (TestMethod)TestFinder.Find("TestTimeOutNotElapsed", suite, false); - ITestResult result = TestBuilder.RunTest(testMethod, fixture); - Assert.That(result.ResultState, Is.EqualTo(ResultState.Success)); - } + // given + var testThatTimesOutButOtherwisePasses = + TestBuilder.MakeTestCase(typeof(SampleTests), nameof(SampleTests.TestThatTimesOut)); - [Test] - public void TestTimeoutElapsed() - { - TimeoutTestCaseFixture fixture = new TimeoutTestCaseFixture(); - TestSuite suite = TestBuilder.MakeFixture(fixture); - TestMethod testMethod = (TestMethod)TestFinder.Find("TestTimeOutElapsed", suite, false); - ITestResult result = TestBuilder.RunTest(testMethod, fixture); - Assert.That(result.ResultState, Is.EqualTo(ResultState.Failure)); - Assert.That(result.Message, Does.Contain("100ms")); + var detachedDebugger = new StubDebugger { IsAttached = false }; + + // when + var result = TestBuilder.RunTest(testThatTimesOutButOtherwisePasses, new SampleTests(), detachedDebugger); + + // then + Assert.That(_testRanToCompletion == false, () => "Test ran to completion"); + + Assert.That(result.ResultState.Status, Is.EqualTo(TestStatus.Failed)); + Assert.That(result.ResultState.Site, Is.EqualTo(FailureSite.Test)); + Assert.That(result.Message, Is.EqualTo($"Test exceeded Timeout value of {SampleTests.Timeout}ms")); } [Explicit("Tests that demonstrate Timeout failure")] @@ -181,46 +375,30 @@ public void TimeoutWithMessagePumpShouldAbort() #endif #if !THREAD_ABORT - [Timeout(50)] - public void TestTimeoutAndReportsTimeoutFailure() + [Test] + public void TimeoutCausesOtherwisePassingTestToFailWithoutDebuggerAttached() { - Thread.Sleep(Timeout.Infinite); - } + // given + var testThatTimesOutButOtherwisePasses = + TestBuilder.MakeTestCase(typeof(SampleTests), nameof(SampleTests.TestThatTimesOut)); - [Test, Timeout(100)] - public void TestTimeoutDoesNotStopCompletion() - { - Thread.Sleep(20); - Assert.True(true); - } + var detachedDebugger = new StubDebugger { IsAttached = false }; - [Timeout(100)] - public void TestTimeoutWhichThrowsTestException() - { - throw new ArgumentException($"{nameof(ArgumentException)} was thrown."); - } + // when + var result = TestBuilder.RunTest(testThatTimesOutButOtherwisePasses, new SampleTests(), detachedDebugger); - [Test] - public void TestTimesOut() - { - var testMethod = TestBuilder.MakeTestCase(GetType(), nameof(TestTimeoutAndReportsTimeoutFailure)); - var testResult = TestBuilder.RunTest(testMethod, this); + // then + Assert.That(_testRanToCompletion == false, () => "Test ran to completion"); - Assert.That(testResult?.ResultState.Status, Is.EqualTo(TestStatus.Failed)); - Assert.That(testResult?.ResultState.Site, Is.EqualTo(FailureSite.Test)); - Assert.That(testResult?.ResultState.Label, Is.EqualTo("Test exceeded Timeout value 50ms.")); + Assert.That(result.ResultState.Status, Is.EqualTo(TestStatus.Failed)); + Assert.That(result.ResultState.Site, Is.EqualTo(FailureSite.Test)); + Assert.That(result.ResultState.Label, Is.EqualTo($"Test exceeded Timeout value {SampleTests.Timeout}ms.")); } +#endif - [Test] - public void TestTimeoutWithExceptionThrown() + private class StubDebugger : IDebugger { - var testMethod = TestBuilder.MakeTestCase(GetType(), nameof(TestTimeoutWhichThrowsTestException)); - var testResult = TestBuilder.RunTest(testMethod, this); - - Assert.That(testResult?.ResultState.Status, Is.EqualTo(TestStatus.Failed)); - Assert.That(testResult?.ResultState.Site, Is.EqualTo(FailureSite.Test)); - Assert.That(testResult?.ResultState.Label, Is.EqualTo("Error")); + public bool IsAttached { get; set; } } -#endif } } diff --git a/src/NUnitFramework/tests/Attributes/ValueSourceTests.cs b/src/NUnitFramework/tests/Attributes/ValueSourceTests.cs index 7a832c2cae..ff74a9ef14 100644 --- a/src/NUnitFramework/tests/Attributes/ValueSourceTests.cs +++ b/src/NUnitFramework/tests/Attributes/ValueSourceTests.cs @@ -201,6 +201,7 @@ public static IEnumerable NullDataSourceProperty [ValueSource(nameof(NullSource))] string nullSource, [ValueSource(nameof(NullDataSourceProvider))] string nullDataSourceProvided, [ValueSource(typeof(ValueProvider), nameof(ValueProvider.ForeignNullResultProvider))] string nullDataSourceProvider, + [ValueSource(typeof(object), sourceName: null)] string typeNotImplementingIEnumerableAndNullSourceName, [ValueSource(nameof(NullDataSourceProperty))] int nullDataSourceProperty, [ValueSource("SomeNonExistingMemberSource")] int nonExistingMember) { diff --git a/src/NUnitFramework/tests/AwaitableReturnTypeTests.cs b/src/NUnitFramework/tests/AwaitableReturnTypeTests.cs index e3a3e05891..5782deeac1 100644 --- a/src/NUnitFramework/tests/AwaitableReturnTypeTests.cs +++ b/src/NUnitFramework/tests/AwaitableReturnTypeTests.cs @@ -61,32 +61,38 @@ public void GetResultIsCalledSynchronouslyWhenContinued() [Test] public void GetResultIsNotCalledUntilContinued() { - var wasCalled = false; - var continuation = (Action)null; - var result = (ITestResult)null; - - ThreadPool.QueueUserWorkItem(state => - { - result = RunCurrentTestMethod(new AsyncWorkload( - isCompleted: false, - onCompleted: action => continuation = action, - getResult: () => { wasCalled = true; return 42; }) - ); - }); - - SpinWait.SpinUntil(() => continuation != null); - - Assert.That(wasCalled, Is.False, "GetResult was called before the continuation passed to OnCompleted was invoked."); - - continuation.Invoke(); - - if (!SpinWait.SpinUntil(() => wasCalled, 1000)) + using (var continuationIsAvailable = new ManualResetEventSlim()) + using (var getResultWasCalled = new ManualResetEventSlim()) { - Assert.Fail("GetResult was not called after the continuation passed to OnCompleted was invoked."); + var continuation = (Action)null; + + ThreadPool.QueueUserWorkItem(state => + { + RunCurrentTestMethod(new AsyncWorkload( + isCompleted: false, + onCompleted: action => + { + continuation = action; + continuationIsAvailable.Set(); + }, + getResult: () => + { + getResultWasCalled.Set(); + return 42; + }) + ); + }); + + continuationIsAvailable.Wait(); + + if (getResultWasCalled.IsSet) + Assert.Fail("GetResult was called before the continuation passed to OnCompleted was invoked."); + + continuation.Invoke(); + + if (!getResultWasCalled.Wait(10_000)) + Assert.Fail("GetResult was not called after the continuation passed to OnCompleted was invoked."); } - - SpinWait.SpinUntil(() => result != null); - result.AssertPassed(); } [Test] diff --git a/src/NUnitFramework/tests/Compatibility/AttributeHelperTests.cs b/src/NUnitFramework/tests/Compatibility/AttributeHelperTests.cs index c342308fb1..d90be39d6f 100644 --- a/src/NUnitFramework/tests/Compatibility/AttributeHelperTests.cs +++ b/src/NUnitFramework/tests/Compatibility/AttributeHelperTests.cs @@ -31,7 +31,7 @@ public class AttributeHelperTests [Test] public void CanGetAttributesOnAssemblies() { - var assembly = typeof(AttributeHelperTests).GetTypeInfo().Assembly; + var assembly = typeof(TestAttribute).GetTypeInfo().Assembly; Assert.That(assembly, Is.Not.Null); var attr = AttributeHelper.GetCustomAttributes(assembly, typeof(AssemblyCompanyAttribute), true); Assert.That(attr, Is.Not.Null); diff --git a/src/NUnitFramework/tests/Constraints/AsyncDelayedConstraintTests.cs b/src/NUnitFramework/tests/Constraints/AsyncDelayedConstraintTests.cs index a0398cc87a..bfe3e1f0d3 100644 --- a/src/NUnitFramework/tests/Constraints/AsyncDelayedConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/AsyncDelayedConstraintTests.cs @@ -80,9 +80,7 @@ public void SyntaxFailure() } [Test] -#if PLATFORM_DETECTION [Platform(Exclude="Linux", Reason="Intermittent failure under Linux")] -#endif public void SyntaxError() { Assert.Throws(() => diff --git a/src/NUnitFramework/tests/Constraints/BinarySerializableTest.cs b/src/NUnitFramework/tests/Constraints/BinarySerializableTest.cs index 4423624ab2..5a4c377c86 100644 --- a/src/NUnitFramework/tests/Constraints/BinarySerializableTest.cs +++ b/src/NUnitFramework/tests/Constraints/BinarySerializableTest.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !NETCOREAPP1_1 using System; using System.Collections.Generic; @@ -39,7 +38,7 @@ public void SetUp() } static object[] SuccessData = new object[] { 1, "a", new List(), new InternalWithSerializableAttributeClass() }; - + static object[] FailureData = new object[] { new TestCaseData( new InternalClass(), "" ) }; [Test] @@ -50,4 +49,3 @@ public void NullArgumentThrowsException() } } } -#endif diff --git a/src/NUnitFramework/tests/Constraints/CollectionEqualsTests.cs b/src/NUnitFramework/tests/Constraints/CollectionEqualsTests.cs index fb4d4e4b33..415efd42a0 100644 --- a/src/NUnitFramework/tests/Constraints/CollectionEqualsTests.cs +++ b/src/NUnitFramework/tests/Constraints/CollectionEqualsTests.cs @@ -24,6 +24,9 @@ using System; using System.Collections; using System.Collections.Generic; +#if !(NET35 || NET40) +using System.Collections.Immutable; +#endif using System.Linq; using NUnit.Framework.Internal; using NUnit.TestUtilities.Collections; @@ -104,5 +107,74 @@ public void HonorsIgnoreCase(IEnumerable expected, IEnumerable actual) new object[] {new List {'A', 'B', 'C'}, new List {'a', 'b', 'c'}}, new object[] {new List {"a", "b", "c"}, new List {"A", "B", "C"}}, }; + +#if !(NET35 || NET40) + [Test] + [DefaultFloatingPointTolerance(0.5)] + public void StructuralComparerOnSameCollection_RespectsAndSetsToleranceByRef() + { + var integerTypes = ImmutableArray.Create(1); + var floatingTypes = ImmutableArray.Create(1.1); + + var equalsConstraint = Is.EqualTo(floatingTypes); + var originalTolerance = equalsConstraint.Tolerance; + + Assert.That(integerTypes, equalsConstraint); + + Assert.That(equalsConstraint.Tolerance, Is.Not.EqualTo(originalTolerance)); + Assert.That(equalsConstraint.Tolerance.Mode, Is.Not.EqualTo(originalTolerance.Mode)); + } + + [Test] + public void StructuralComparerOnSameCollection_OfDifferentUnderlyingType_UsesNUnitComparer() + { + var integerTypes = ImmutableArray.Create(1); + var floatingTypes = ImmutableArray.Create(1.1); + + Assert.That(integerTypes, Is.Not.EqualTo(floatingTypes)); + Assert.That(integerTypes, Is.EqualTo(floatingTypes).Within(0.5)); + } + + [Test] + public void StructuralComparerOnDifferentCollection_OfDifferentUnderlyingType_UsesNUnitComparer() + { + var integerTypes = ImmutableArray.Create(1); + var floatingTypes = new double[] { 1.1 }; + + Assert.That(integerTypes, Is.Not.EqualTo(floatingTypes)); + Assert.That(integerTypes, Is.EqualTo(floatingTypes).Within(0.5)); + } + + [TestCaseSource(nameof(GetImmutableCollectionsData))] + public void ImmutableCollectionsEquals(object x, object y) + { + Assert.That(x, Is.EqualTo(y)); + } + + private static IEnumerable GetImmutableCollectionsData() + { + var data = new[] { 1, 2, 3 }; + var immutableDataGenerators = new Func>[] + { + () => ImmutableArray.Create(data), + () => ImmutableList.Create(data), + () => ImmutableQueue.Create(data), + () => ImmutableStack.Create(data.Reverse().ToArray()), + () => new List(data), + () => data + }; + + for (var i = 0; i < immutableDataGenerators.Length; i++) + { + for (var j = i; j < immutableDataGenerators.Length; j++) + { + var x = immutableDataGenerators[i](); + var y = immutableDataGenerators[j](); + + yield return new object[] { x, y }; + } + } + } +#endif } } diff --git a/src/NUnitFramework/tests/Constraints/CollectionEquivalentConstraintTests.cs b/src/NUnitFramework/tests/Constraints/CollectionEquivalentConstraintTests.cs index 671547870c..1d6859e0d7 100644 --- a/src/NUnitFramework/tests/Constraints/CollectionEquivalentConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/CollectionEquivalentConstraintTests.cs @@ -141,12 +141,10 @@ public static IEnumerable TestCases yield return new TestCaseData(new Dictionary {{ "b", 2 }, { "a", 1 } }, new Dictionary {{"A", 1}, {"b", 2}}); yield return new TestCaseData(new Dictionary {{'A', 1 }}, new Dictionary {{'a', 1}}); -#if !NETCOREAPP1_1 yield return new TestCaseData(new Hashtable {{1, "a"}, {2, "b"}}, new Hashtable {{1, "A"},{2, "B"}}); yield return new TestCaseData(new Hashtable {{1, 'A'}, {2, 'B'}}, new Hashtable {{1, 'a'},{2, 'b'}}); yield return new TestCaseData(new Hashtable {{"b", 2}, {"a", 1}}, new Hashtable {{"A", 1}, {"b", 2}}); yield return new TestCaseData(new Hashtable {{'A', 1}}, new Hashtable {{'a', 1}}); -#endif } } } @@ -159,7 +157,7 @@ public void EquivalentHonorsUsing() ICollection set2 = new SimpleObjectCollection("z", "Y", "X"); Assert.That(new CollectionEquivalentConstraint(set1) - .Using((x, y) => StringUtil.Compare(x, y, true)) + .Using((x, y) => StringComparer.InvariantCultureIgnoreCase.Compare(x, y)) .ApplyTo(set2).IsSuccess); } diff --git a/src/NUnitFramework/tests/Constraints/CollectionSubsetConstraintTests.cs b/src/NUnitFramework/tests/Constraints/CollectionSubsetConstraintTests.cs index c65283ba6c..3413684552 100644 --- a/src/NUnitFramework/tests/Constraints/CollectionSubsetConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/CollectionSubsetConstraintTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,6 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +using System; using System.Collections; using System.Collections.Generic; using NUnit.Framework.Internal; @@ -29,7 +30,7 @@ namespace NUnit.Framework.Constraints { [TestFixture] - public class CollectionSubsetConstraintTests : ConstraintTestBase + public class CollectionSubsetConstraintTests : ConstraintTestBaseNoData { [SetUp] public void SetUp() @@ -40,21 +41,41 @@ public void SetUp() } static object[] SuccessData = new object[] { new int[] { 1, 3, 5 }, new int[] { 1, 2, 3, 4, 5 } }; - static object[] FailureData = new object[] { - new object[] { new int[] { 1, 3, 7 }, "< 1, 3, 7 >" }, - new object[] { new int[] { 1, 2, 2, 2, 5 }, "< 1, 2, 2, 2, 5 >" } }; + static object[] FailureData = new object[] { + new object[] { new int[] { 1, 3, 7 }, "< 1, 3, 7 >" , "< 7 >"}, + new object[] { new int[] { 1, 2, 2, 2, 5 }, "< 1, 2, 2, 2, 5 >", "< 2, 2 >" } }; + + [Test, TestCaseSource(nameof(SuccessData))] + public void SucceedsWithGoodValues(object actualValue) + { + Assert.That(actualValue, TheConstraint); + } + + [Test, TestCaseSource(nameof(FailureData))] + public void FailsWithBadValues(object badActualValue, string actualMessage, string extraMessage) + { + var constraintResult = TheConstraint.ApplyTo(badActualValue); + Assert.IsFalse(constraintResult.IsSuccess); + + TextMessageWriter writer = new TextMessageWriter(); + constraintResult.WriteMessageTo(writer); + Assert.That(writer.ToString(), Is.EqualTo( + TextMessageWriter.Pfx_Expected + ExpectedDescription + Environment.NewLine + + TextMessageWriter.Pfx_Actual + actualMessage + Environment.NewLine + + " Extra items: " + extraMessage + Environment.NewLine)); + } [Test] [TestCaseSource(typeof(IgnoreCaseDataProvider), nameof(IgnoreCaseDataProvider.TestCases))] - public void HonorsIgnoreCase( IEnumerable expected, IEnumerable actual ) + public void HonorsIgnoreCase(IEnumerable expected, IEnumerable actual) { - var constraint = new CollectionSubsetConstraint( expected ).IgnoreCase; - var constraintResult = constraint.ApplyTo( actual ); - if ( !constraintResult.IsSuccess ) + var constraint = new CollectionSubsetConstraint(expected).IgnoreCase; + var constraintResult = constraint.ApplyTo(actual); + if (!constraintResult.IsSuccess) { MessageWriter writer = new TextMessageWriter(); - constraintResult.WriteMessageTo( writer ); - Assert.Fail( writer.ToString() ); + constraintResult.WriteMessageTo(writer); + Assert.Fail(writer.ToString()); } } @@ -65,19 +86,17 @@ public static IEnumerable TestCases get { yield return new TestCaseData(new SimpleObjectCollection("w", "x", "y", "z"), new SimpleObjectCollection("z", "Y", "X")); - yield return new TestCaseData(new[] {'A', 'B', 'C', 'D', 'E'}, new object[] {'a', 'b', 'c'}); - yield return new TestCaseData(new[] {"a", "b", "c", "d", "e"}, new object[] {"A", "C", "B"}); - yield return new TestCaseData(new Dictionary {{1, "a"}, {2, "b"}}, new Dictionary {{1, "A"}}); - yield return new TestCaseData(new Dictionary {{1, 'A'}, {2, 'B'}}, new Dictionary {{1, 'a'}}); - yield return new TestCaseData(new Dictionary {{ "b", 2 }, { "a", 1 } }, new Dictionary {{"b", 2}}); - yield return new TestCaseData(new Dictionary {{'A', 1 }, {'B', 2}}, new Dictionary {{'a', 1}}); + yield return new TestCaseData(new[] { 'A', 'B', 'C', 'D', 'E' }, new object[] { 'a', 'b', 'c' }); + yield return new TestCaseData(new[] { "a", "b", "c", "d", "e" }, new object[] { "A", "C", "B" }); + yield return new TestCaseData(new Dictionary { { 1, "a" }, { 2, "b" } }, new Dictionary { { 1, "A" } }); + yield return new TestCaseData(new Dictionary { { 1, 'A' }, { 2, 'B' } }, new Dictionary { { 1, 'a' } }); + yield return new TestCaseData(new Dictionary { { "b", 2 }, { "a", 1 } }, new Dictionary { { "b", 2 } }); + yield return new TestCaseData(new Dictionary { { 'A', 1 }, { 'B', 2 } }, new Dictionary { { 'a', 1 } }); -#if !NETCOREAPP1_1 - yield return new TestCaseData(new Hashtable {{1, "a"}, {2, "b"}}, new Hashtable {{1, "A"}}); - yield return new TestCaseData(new Hashtable {{1, 'A'}, {2, 'B'}}, new Hashtable {{2, 'b'}}); - yield return new TestCaseData(new Hashtable {{"b", 2}, {"a", 1}}, new Hashtable {{"A", 1}}); - yield return new TestCaseData(new Hashtable {{'A', 1}, {'B', 2}}, new Hashtable {{'a', 1}}); -#endif + yield return new TestCaseData(new Hashtable { { 1, "a" }, { 2, "b" } }, new Hashtable { { 1, "A" } }); + yield return new TestCaseData(new Hashtable { { 1, 'A' }, { 2, 'B' } }, new Hashtable { { 2, 'b' } }); + yield return new TestCaseData(new Hashtable { { "b", 2 }, { "a", 1 } }, new Hashtable { { "A", 1 } }); + yield return new TestCaseData(new Hashtable { { 'A', 1 }, { 'B', 2 } }, new Hashtable { { 'a', 1 } }); } } } diff --git a/src/NUnitFramework/tests/Constraints/CollectionSupersetConstraintTests.cs b/src/NUnitFramework/tests/Constraints/CollectionSupersetConstraintTests.cs index 0ebc06db40..7403a9e7df 100644 --- a/src/NUnitFramework/tests/Constraints/CollectionSupersetConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/CollectionSupersetConstraintTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,6 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +using System; using System.Collections; using System.Collections.Generic; using NUnit.Framework.Internal; @@ -29,7 +30,7 @@ namespace NUnit.Framework.Constraints { [TestFixture] - public class CollectionSupersetConstraintTests : ConstraintTestBase + public class CollectionSupersetConstraintTests : ConstraintTestBaseNoData { [SetUp] public void SetUp() @@ -39,33 +40,53 @@ public void SetUp() ExpectedDescription = "superset of < 1, 2, 3, 4, 5 >"; } - static object[] SuccessData = new object[] - { + static object[] SuccessData = new object[] + { new int[] { 1, 2, 3, 4, 5, 6 } - , new int[] { 1, 2, 3, 4, 5 } + , new int[] { 1, 2, 3, 4, 5 } , new int[] { 1, 2, 2, 2, 3, 4, 5, 3 } , new int[] { 1, 2, 2, 2, 3, 4, 5, 7 } }; - static object[] FailureData = new object[] - { - new object[] { new int[] { 1, 3, 7 }, "< 1, 3, 7 >" } - , new object[] { new int[] { 1, 2, 2, 2, 5 }, "< 1, 2, 2, 2, 5 >" } - , new object[] { new int[] { 1, 2, 3, 5 }, "< 1, 2, 3, 5 >" } - , new object[] { new int[] { 1, 2, 3, 5, 7 }, "< 1, 2, 3, 5, 7 >" } + static object[] FailureData = new object[] + { + new object[] { new int[] { 1, 3, 7 }, "< 1, 3, 7 >", "< 2, 4, 5 >" } + , new object[] { new int[] { 1, 2, 2, 2, 5 }, "< 1, 2, 2, 2, 5 >", "< 3, 4 >" } + , new object[] { new int[] { 1, 2, 3, 5 }, "< 1, 2, 3, 5 >", "< 4 >" } + , new object[] { new int[] { 1, 2, 3, 5, 7 }, "< 1, 2, 3, 5, 7 >", "< 4 >" } }; + [Test, TestCaseSource(nameof(SuccessData))] + public void SucceedsWithGoodValues(object actualValue) + { + Assert.That(actualValue, TheConstraint); + } + + [Test, TestCaseSource(nameof(FailureData))] + public void FailsWithBadValues(object badActualValue, string actualMessage, string missingMessage) + { + var constraintResult = TheConstraint.ApplyTo(badActualValue); + Assert.IsFalse(constraintResult.IsSuccess); + + TextMessageWriter writer = new TextMessageWriter(); + constraintResult.WriteMessageTo(writer); + Assert.That(writer.ToString(), Is.EqualTo( + TextMessageWriter.Pfx_Expected + ExpectedDescription + Environment.NewLine + + TextMessageWriter.Pfx_Actual + actualMessage + Environment.NewLine + + " Missing items: " + missingMessage + Environment.NewLine)); + } + [Test] [TestCaseSource(typeof(IgnoreCaseDataProvider), nameof(IgnoreCaseDataProvider.TestCases))] - public void HonorsIgnoreCase( IEnumerable expected, IEnumerable actual ) + public void HonorsIgnoreCase(IEnumerable expected, IEnumerable actual) { - var constraint = new CollectionSupersetConstraint( expected ).IgnoreCase; - var constraintResult = constraint.ApplyTo( actual ); - if ( !constraintResult.IsSuccess ) + var constraint = new CollectionSupersetConstraint(expected).IgnoreCase; + var constraintResult = constraint.ApplyTo(actual); + if (!constraintResult.IsSuccess) { MessageWriter writer = new TextMessageWriter(); - constraintResult.WriteMessageTo( writer ); - Assert.Fail( writer.ToString() ); + constraintResult.WriteMessageTo(writer); + Assert.Fail(writer.ToString()); } } @@ -76,19 +97,17 @@ public static IEnumerable TestCases get { yield return new TestCaseData(new SimpleObjectCollection("z", "Y", "X"), new SimpleObjectCollection("w", "x", "y", "z")); - yield return new TestCaseData(new object[] {'a', 'b', 'c'}, new[] {'A', 'B', 'C', 'D', 'E'}); + yield return new TestCaseData(new object[] { 'a', 'b', 'c' }, new[] { 'A', 'B', 'C', 'D', 'E' }); yield return new TestCaseData(new object[] { "A", "C", "B" }, new[] { "a", "b", "c", "d", "e" }); yield return new TestCaseData(new Dictionary { { 1, "A" } }, new Dictionary { { 1, "a" }, { 2, "b" } }); yield return new TestCaseData(new Dictionary { { 1, 'a' } }, new Dictionary { { 1, 'A' }, { 2, 'B' } }); yield return new TestCaseData(new Dictionary { { "b", 2 } }, new Dictionary { { "b", 2 }, { "a", 1 } }); yield return new TestCaseData(new Dictionary { { 'a', 1 } }, new Dictionary { { 'A', 1 }, { 'B', 2 } }); -#if !NETCOREAPP1_1 yield return new TestCaseData(new Hashtable { { 1, "A" } }, new Hashtable { { 1, "a" }, { 2, "b" } }); yield return new TestCaseData(new Hashtable { { 2, 'b' } }, new Hashtable { { 1, 'A' }, { 2, 'B' } }); yield return new TestCaseData(new Hashtable { { "A", 1 } }, new Hashtable { { "b", 2 }, { "a", 1 } }); yield return new TestCaseData(new Hashtable { { 'a', 1 } }, new Hashtable { { 'A', 1 }, { 'B', 2 } }); -#endif } } } diff --git a/src/NUnitFramework/tests/Constraints/ComparisonConstraintTestBase.cs b/src/NUnitFramework/tests/Constraints/ComparisonConstraintTestBase.cs index 04b3e5c4a5..f8674a9868 100644 --- a/src/NUnitFramework/tests/Constraints/ComparisonConstraintTestBase.cs +++ b/src/NUnitFramework/tests/Constraints/ComparisonConstraintTestBase.cs @@ -96,7 +96,7 @@ public int CompareTo(object x) } } - class ClassWithIComparableOfT : IComparable + class ClassWithIComparableOfT : IComparable, IComparable { private readonly int val; @@ -109,6 +109,11 @@ public int CompareTo(ClassWithIComparableOfT other) { return val.CompareTo(other.val); } + + public int CompareTo(int other) + { + return val.CompareTo(other); + } } #endregion diff --git a/src/NUnitFramework/tests/Constraints/ContainsConstraintTests.cs b/src/NUnitFramework/tests/Constraints/ContainsConstraintTests.cs index 34592c4485..835e356857 100644 --- a/src/NUnitFramework/tests/Constraints/ContainsConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/ContainsConstraintTests.cs @@ -41,7 +41,7 @@ public void HonorsIgnoreCaseForStringCollection() Assert.That(result.IsSuccess); } - [Test] + [Test, SetCulture("en-US")] public void HonorsIgnoreCaseForString() { var actualString = "ABCdef"; diff --git a/src/NUnitFramework/tests/Constraints/DelayedConstraintTests.cs b/src/NUnitFramework/tests/Constraints/DelayedConstraintTests.cs index 139a0b1e25..4f220d6b26 100644 --- a/src/NUnitFramework/tests/Constraints/DelayedConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/DelayedConstraintTests.cs @@ -244,6 +244,21 @@ public void ThatBlockingDelegateWhichThrowsWithPolling_FailsAfterDelay() Assert.That(watch.ElapsedMilliseconds, Is.GreaterThanOrEqualTo(AFTER)); } + [Test] + public void PreservesOriginalResultAdditionalLines() + { + var exception = Assert.Throws( + () => Assert.That(new[] { 1, 2 }, Is.EquivalentTo(new[] { 2, 3 }).After(1))); + + var expectedMessage = + " Expected: equivalent to < 2, 3 > after 1 millisecond delay" + Environment.NewLine + + " But was: < 1, 2 >" + Environment.NewLine + + " Missing (1): < 3 >" + Environment.NewLine + + " Extra (1): < 1 >" + Environment.NewLine; + + Assert.That(exception.Message, Is.EqualTo(expectedMessage)); + } + private static int setValuesDelay; private static void MethodReturningVoid() { } diff --git a/src/NUnitFramework/tests/Constraints/DictionaryContainsKeyConstraintTests.cs b/src/NUnitFramework/tests/Constraints/DictionaryContainsKeyConstraintTests.cs index 79877b270f..4734458cfd 100644 --- a/src/NUnitFramework/tests/Constraints/DictionaryContainsKeyConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/DictionaryContainsKeyConstraintTests.cs @@ -54,6 +54,7 @@ public void SucceedsWhenKeyIsNotPresentUsingContainKey() var dictionary = new Dictionary { { "Hello", "World" }, { "Hola", "Mundo" } }; Assert.That(dictionary, Does.Not.ContainKey("NotKey")); } + [Test] public void FailsWhenKeyIsMissing() { @@ -75,7 +76,6 @@ public void FailsWhenNotUsedAgainstADictionary() Assert.That(act, Throws.ArgumentException.With.Message.Contains("ContainsKey")); } -#if !NETCOREAPP1_1 [Test] public void WorksWithNonGenericDictionary() { @@ -83,10 +83,9 @@ public void WorksWithNonGenericDictionary() Assert.That(dictionary, new DictionaryContainsKeyConstraint("Hello")); } -#endif #pragma warning disable CS0618 // DictionaryContainsKeyConstraint.IgnoreCase and .Using are deprecated - + [Test, SetCulture("en-US")] public void IgnoreCaseIsHonored() { var dictionary = new Dictionary { { "Hello", "World" }, { "Hola", "Mundo" } }; @@ -94,7 +93,7 @@ public void IgnoreCaseIsHonored() Assert.That(dictionary, new DictionaryContainsKeyConstraint("HELLO").IgnoreCase); } - [Test] + [Test, SetCulture("en-US")] public void UsingIsHonored() { var dictionary = new Dictionary { { "Hello", "World" }, { "Hola", "Mundo" } }; @@ -191,7 +190,8 @@ public void ShouldCallContainsKeysMethodOnPlainDictionary() { var dictionary = new TestNonGenericDictionary(99); - Assert.Catch(() => Assert.That(dictionary, Does.ContainKey(99))); + Assert.That(dictionary, Does.ContainKey(99)); + Assert.That(dictionary, !Does.ContainKey(35)); } [Test] diff --git a/src/NUnitFramework/tests/Constraints/DictionaryContainsKeyValueConstraintTests.cs b/src/NUnitFramework/tests/Constraints/DictionaryContainsKeyValueConstraintTests.cs new file mode 100644 index 0000000000..32be807960 --- /dev/null +++ b/src/NUnitFramework/tests/Constraints/DictionaryContainsKeyValueConstraintTests.cs @@ -0,0 +1,113 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System.Collections; +using System.Collections.Generic; + +using NUnit.Framework.Constraints; +using NUnit.Framework.Internal; + +namespace NUnit.Framework.Constraints +{ + [TestFixture] + public class DictionaryContainsKeyValuePairConstraintTests + { + [Test] + public void SucceedsWhenKeyValuePairIsPresent() + { + var dictionary = new Dictionary { { "Hello", "World" }, { "Hi", "Universe" }, { "Hola", "Mundo" } }; + + Assert.That(dictionary, new DictionaryContainsKeyValuePairConstraint("Hi", "Universe")); + } + + [Test] + public void FailsWhenKeyIsMissing() + { + var dictionary = new Dictionary { { "Hello", "World" }, { "Hi", "Universe" }, { "Hola", "Mundo" } }; + + TestDelegate act = () => Assert.That(dictionary, new DictionaryContainsKeyValuePairConstraint("Bye", "Universe")); + + Assert.That(act, Throws.Exception.TypeOf()); + } + + [Test] + public void FailsWhenValueIsMissing() + { + var dictionary = new Dictionary { { "Hello", "World" }, { "Hi", "Universe" }, { "Hola", "Mundo" } }; + + TestDelegate act = () => Assert.That(dictionary, new DictionaryContainsKeyValuePairConstraint("Hello", "Universe")); + + Assert.That(act, Throws.Exception.TypeOf()); + } + + [Test] + public void SucceedsWhenPairIsPresentUsingContainKeyWithValue() + { + var dictionary = new Dictionary { { "Hello", "World" }, { "Hola", "Mundo" } }; + Assert.That(dictionary, Does.ContainKey("Hola").WithValue("Mundo")); + } + + [Test] + public void SucceedsWhenPairIsNotPresentUsingContainKeyWithValue() + { + var dictionary = new Dictionary { { "Hello", "World" }, { "Hola", "Mundo" } }; + Assert.That(dictionary, Does.Not.ContainKey("Hello").WithValue("NotValue")); + } + + [Test] + public void FailsWhenNotUsedAgainstADictionary() + { + List> keyValuePairs = new List>( + new Dictionary { { "Hello", "World" }, { "Hi", "Universe" }, { "Hola", "Mundo" } }); + + TestDelegate act = () => Assert.That(keyValuePairs, new DictionaryContainsKeyValuePairConstraint("Hi", "Universe")); + + Assert.That(act, Throws.ArgumentException.With.Message.Contains("IDictionary")); + } + + [Test] + public void WorksWithNonGenericDictionary() + { + var dictionary = new Hashtable { { "Hello", "World" }, { "Hi", "Universe" }, { "Hola", "Mundo" } }; + + Assert.That(dictionary, new DictionaryContainsKeyValuePairConstraint("Hi", "Universe")); + } + + [Test, SetCulture("en-US")] + public void IgnoreCaseIsHonored() + { + var dictionary = new Dictionary { { "Hello", "World" }, { "Hi", "Universe" }, { "Hola", "Mundo" } }; + + Assert.That(dictionary, new DictionaryContainsKeyValuePairConstraint("HI", "UNIVERSE").IgnoreCase); + } + + [Test, SetCulture("en-US")] + public void UsingIsHonored() + { + var dictionary = new Dictionary { { "Hello", "World" }, { "Hi", "Universe" }, { "Hola", "Mundo" } }; + + Assert.That(dictionary, + new DictionaryContainsKeyValuePairConstraint("HI", "UNIVERSE").Using((x, y) => StringUtil.Compare(x, y, true))); + } + } +} diff --git a/src/NUnitFramework/tests/Constraints/DictionaryContainsValueConstraintTests.cs b/src/NUnitFramework/tests/Constraints/DictionaryContainsValueConstraintTests.cs index b798948185..a98e759a2f 100644 --- a/src/NUnitFramework/tests/Constraints/DictionaryContainsValueConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/DictionaryContainsValueConstraintTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -49,6 +49,7 @@ public void FailsWhenValueIsMissing() Assert.That(act, Throws.Exception.TypeOf()); } + [Test] public void SucceedsWhenValueIsPresentUsingContainKey() { @@ -62,6 +63,7 @@ public void SucceedsWhenValueIsNotPresentUsingContainKey() var dictionary = new Dictionary { { "Hello", "World" }, { "Hola", "Mundo" } }; Assert.That(dictionary, Does.Not.ContainValue("NotValue")); } + [Test] public void FailsWhenNotUsedAgainstADictionary() { @@ -69,11 +71,10 @@ public void FailsWhenNotUsedAgainstADictionary() new Dictionary { { "Hello", "World" }, { "Hi", "Universe" }, { "Hola", "Mundo" } }); TestDelegate act = () => Assert.That(keyValuePairs, new DictionaryContainsValueConstraint("Community")); - + Assert.That(act, Throws.ArgumentException.With.Message.Contains("IDictionary")); } -#if !NETCOREAPP1_1 [Test] public void WorksWithNonGenericDictionary() { @@ -81,9 +82,8 @@ public void WorksWithNonGenericDictionary() Assert.That(dictionary, new DictionaryContainsValueConstraint("Universe")); } -#endif - [Test] + [Test, SetCulture("en-US")] public void IgnoreCaseIsHonored() { var dictionary = new Dictionary { { "Hello", "World" }, { "Hi", "Universe" }, { "Hola", "Mundo" } }; @@ -91,7 +91,7 @@ public void IgnoreCaseIsHonored() Assert.That(dictionary, new DictionaryContainsValueConstraint("UNIVERSE").IgnoreCase); } - [Test] + [Test, SetCulture("en-US")] public void UsingIsHonored() { var dictionary = new Dictionary { { "Hello", "World" }, { "Hi", "Universe" }, { "Hola", "Mundo" } }; diff --git a/src/NUnitFramework/tests/Constraints/EmptyConstraintTest.cs b/src/NUnitFramework/tests/Constraints/EmptyConstraintTest.cs index 730eff6b04..338f8bb628 100644 --- a/src/NUnitFramework/tests/Constraints/EmptyConstraintTest.cs +++ b/src/NUnitFramework/tests/Constraints/EmptyConstraintTest.cs @@ -44,16 +44,16 @@ public void SetUp() { string.Empty, new object[0], -#if !NETCOREAPP1_1 new ArrayList(), -#endif - new System.Collections.Generic.List() + new System.Collections.Generic.List(), + Guid.Empty, }; static object[] FailureData = new object[] { new TestCaseData( "Hello", "\"Hello\"" ), - new TestCaseData( new object[] { 1, 2, 3 }, "< 1, 2, 3 >" ) + new TestCaseData( new object[] { 1, 2, 3 }, "< 1, 2, 3 >" ), + new TestCaseData(new Guid("12345678-1234-1234-1234-123456789012"), "12345678-1234-1234-1234-123456789012"), }; [TestCase(null)] @@ -71,6 +71,14 @@ public void NullStringGivesFailureResult() Assert.That(result.Status, Is.EqualTo(ConstraintStatus.Failure)); } + [Test] + public void NullNullableGuidGivesFailureResult() + { + Guid? actual = null; + var result = TheConstraint.ApplyTo(actual); + Assert.That(result.Status, Is.EqualTo(ConstraintStatus.Failure)); + } + [Test] public void NullArgumentExceptionMessageContainsTypeName() { @@ -116,7 +124,7 @@ public void EmptyDirectory() } [Test] - public void NotEmptyDirectory() + public void NotEmptyDirectory_ContainsFile() { using (var testDir = new TestDirectory()) { @@ -125,5 +133,54 @@ public void NotEmptyDirectory() Assert.That(testDir.Directory, Is.Not.Empty); } } + + [Test] + public void NotEmptyDirectory_ContainsDirectory() + { + using (var testDir = new TestDirectory()) + { + Directory.CreateDirectory(Path.Combine(testDir.Directory.FullName, "DUMMY_DIR")); + + Assert.That(testDir.Directory, Is.Not.Empty); + } + } + } + + [TestFixture] + public class EmptyGuidConstraintTest + { + [Test] + public void EmptyGuid() + { + Assert.That(Guid.Empty, Is.Empty); + } + + [Test] + public void EmptyNullableGuid() + { + Guid? empty = Guid.Empty; + Assert.That(empty, Is.Empty); + } + + [Test] + public void NonEmptyGuid() + { + Guid nonEmpty = new Guid("10000000-0000-0000-0000-000000000000"); + Assert.That(nonEmpty, Is.Not.Empty); + } + + [Test] + public void NonEmptyNullableGuid() + { + Guid? nonEmpty = new Guid("10000000-0000-0000-0000-000000000000"); + Assert.That(nonEmpty, Is.Not.Empty); + } + + [Test] + public void NullNullableGuid() + { + Guid? nonEmpty = null; + Assert.That(nonEmpty, Is.Not.Empty); + } } } diff --git a/src/NUnitFramework/tests/Constraints/EqualConstraintTests.cs b/src/NUnitFramework/tests/Constraints/EqualConstraintTests.cs index 5ca801e70a..d0adc1de86 100644 --- a/src/NUnitFramework/tests/Constraints/EqualConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/EqualConstraintTests.cs @@ -52,6 +52,14 @@ public void SetUp() new TestCaseData(double.PositiveInfinity, double.PositiveInfinity.ToString()) }; +#if !NET35 + [Test] + public void Complex_PassesEquality() + { + Assert.AreEqual(new System.Numerics.Complex(1, 100), new System.Numerics.Complex(1, 100)); + } +#endif + #region DateTimeEquality public class DateTimeEquality @@ -128,7 +136,7 @@ public void CanMatchDatesWithinMilliseconds() { DateTime expected = new DateTime(2007, 4, 1, 13, 0, 0); DateTime actual = new DateTime(2007, 4, 1, 13, 1, 0); - Assert.That(actual, new EqualConstraint(expected).Within(300000).Milliseconds); + Assert.That(actual, new EqualConstraint(expected).Within(300_000).Milliseconds); } [Test] @@ -308,7 +316,7 @@ public void CanMatchDatesWithinMilliseconds() { var expected = new DateTimeOffset(new DateTime(2007, 4, 1, 13, 0, 0)); var actual = new DateTimeOffset(new DateTime(2007, 4, 1, 13, 1, 0)); - Assert.That(actual, new EqualConstraint(expected).Within(300000).Milliseconds); + Assert.That(actual, new EqualConstraint(expected).Within(300_000).Milliseconds); } [Test] @@ -356,7 +364,6 @@ public void CanMatchDictionaries_DifferentOrder() new Dictionary {{0, 0}, {2, 2}, {1, 1}}); } -#if !NETCOREAPP1_1 [Test] public void CanMatchHashtables_SameOrder() { @@ -385,7 +392,6 @@ public void CanMatchHashtableWithDictionary() Assert.AreEqual(new Hashtable {{0, 0}, {1, 1}, {2, 2}}, new Dictionary {{0, 0}, {2, 2}, {1, 1}}); } -#endif } #endregion @@ -449,6 +455,8 @@ public void FailsOnDoublesOutsideOfRelativeTolerance(object value) { var ex = Assert.Throws(() => Assert.That(value, new EqualConstraint(10000.0).Within(10.0).Percent)); Assert.That(ex.Message, Does.Contain("+/- 10.0d Percent")); + var expectedPercentDiff = (10000 - (double)value) / 100; + Assert.That(ex.Message, Does.Contain($"{MsgUtils.FormatValue(expectedPercentDiff)} Percent")); } [TestCase(9500.0f)] @@ -465,6 +473,19 @@ public void FailsOnSinglesOutsideOfRelativeTolerance(object value) { var ex = Assert.Throws(() => Assert.That(value, new EqualConstraint(10000.0f).Within(10.0f).Percent)); Assert.That(ex.Message, Does.Contain("+/- 10.0f Percent")); + double expectedPercentDiff = (10000 - (float)value) / 100; + Assert.That(ex.Message, Does.Contain($"{MsgUtils.FormatValue(expectedPercentDiff)} Percent")); + } + + [TestCase(1.21)] + [TestCase(1.19)] + public void FailsOnDoublesOutsideOfAbsoluteTolerance(object value) + { + const double tolerance = 0.001; + var ex = Assert.Throws(() => Assert.That(value, new EqualConstraint(1.2).Within(tolerance))); + Assert.That(ex.Message, Does.Contain($"+/- {MsgUtils.FormatValue(tolerance)}")); + var expectedAbsoluteDiff = 1.2 - (double)value; + Assert.That(ex.Message, Does.Contain($"{MsgUtils.FormatValue(expectedAbsoluteDiff)}")); } /// Applies both the Percent and Ulps modifiers to cause an exception @@ -513,6 +534,20 @@ public void ErrorIfUlpsIsUsedOnDecimal() { Assert.Throws(() => Assert.That(100m, Is.EqualTo(100m).Within(2).Ulps)); } + + [Test] + public void CanMatchNegativeZeroToZeroForDoubles() + { + Assert.That(0d, Is.EqualTo(-0d).Within(1).Ulps); + Assert.That(-0d, Is.EqualTo(0d).Within(1).Ulps); + } + + [Test] + public void CanMatchNegativeZeroToZeroForFloats() + { + Assert.That(0f, Is.EqualTo(-0f).Within(1).Ulps); + Assert.That(-0f, Is.EqualTo(0f).Within(1).Ulps); + } } #endregion @@ -545,6 +580,38 @@ public void UsesProvidedEqualityComparer() Assert.That(comparer.Called, "Comparer was not called"); } + [Test] + public void UsesProvidedEqualityComparerForExpectedIsString() + { + var comparer = new ObjectToStringEqualityComparer(); + Assert.That(4, Is.EqualTo("4").Using(comparer)); + Assert.That(comparer.WasCalled, "Comparer was not called"); + } + + [Test] + public void UsesProvidedEqualityComparerForActualIsString() + { + var comparer = new ObjectToStringEqualityComparer(); + Assert.That("4", Is.EqualTo(4).Using(comparer)); + Assert.That(comparer.WasCalled, "Comparer was not called"); + } + + [Test] + public void UsesProvidedComparerForExpectedIsString() + { + var comparer = new ObjectToStringComparer(); + Assert.That(4, Is.EqualTo("4").Using(comparer)); + Assert.That(comparer.WasCalled, "Comparer was not called"); + } + + [Test] + public void UsesProvidedComparerForActualIsString() + { + var comparer = new ObjectToStringComparer(); + Assert.That("4", Is.EqualTo(4).Using(comparer)); + Assert.That(comparer.WasCalled, "Comparer was not called"); + } + [Test] public void UsesProvidedGenericEqualityComparer() { @@ -589,7 +656,7 @@ public void UsesProvidedLambda_IntArgs() Assert.That(2 + 2, Is.EqualTo(4).Using((x, y) => x.CompareTo(y))); } - [Test] + [Test, SetCulture("en-US")] public void UsesProvidedLambda_StringArgs() { Assert.That("hello", Is.EqualTo("HELLO").Using((x, y) => StringUtil.Compare(x, y, true))); @@ -653,6 +720,24 @@ public void HasMemberHonorsUsingWhenCollectionsAreOfDifferentTypes() ICollection strings = new List { "1", "2", "3" }; Assert.That(strings, Has.Member(2).Using((s, i) => i.ToString() == s)); } + + [Test, SetCulture("en-US")] + public void UsesProvidedPredicateForItemComparison() + { + var expected = new[] { "yeti", "łysy", "rysiu" }; + var actual = new[] { "YETI", "Łysy", "RySiU" }; + + Assert.That(actual, Is.EqualTo(expected).Using((x, y) => StringUtil.StringsEqual(x, y, true))); + } + + [Test] + public void UsesProvidedPredicateForItemComparisonDifferentTypes() + { + var expected = new[] { 1, 2, 3 }; + var actual = new[] { "1", "2", "3" }; + + Assert.That(actual, Is.EqualTo(expected).Using((s, i) => i.ToString() == s)); + } } #endregion @@ -664,12 +749,12 @@ private static IEnumerable DifferentTypeSameValueTestData get { var ptr = new System.IntPtr(0); - var ExampleTestA = new ExampleTest.ClassA(0); - var ExampleTestB = new ExampleTest.ClassB(0); + var exampleTestA = new ExampleTest.ClassA(0); + var exampleTestB = new ExampleTest.ClassB(0); var clipTestA = new ExampleTest.Outer.Middle.Inner.Outer.Middle.Inner.Outer.Middle.Outer.Middle.Inner.Outer.Middle.Inner.Outer.Middle.Inner.Outer.Middle.Inner.Clip.ReallyLongClassNameShouldBeHere(); var clipTestB = new ExampleTest.Clip.Outer.Middle.Inner.Outer.Middle.Inner.Outer.Middle.Outer.Middle.Inner.Outer.Middle.Inner.Outer.Middle.Inner.Outer.Middle.Inner.Clip.ReallyLongClassNameShouldBeHere(); yield return new object[] { 0, ptr }; - yield return new object[] { ExampleTestA, ExampleTestB }; + yield return new object[] { exampleTestA, exampleTestB }; yield return new object[] { clipTestA, clipTestB }; } } diff --git a/src/NUnitFramework/tests/Constraints/GreaterThanConstraintTests.cs b/src/NUnitFramework/tests/Constraints/GreaterThanConstraintTests.cs index aa90750f53..5626d0ff9b 100644 --- a/src/NUnitFramework/tests/Constraints/GreaterThanConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/GreaterThanConstraintTests.cs @@ -58,6 +58,14 @@ public void CanCompareIComparablesOfT() Assert.That(actual, Is.GreaterThan(expected)); } + [Test] + public void CanCompareIComparablesOfInt() + { + int expected = 0; + ClassWithIComparableOfT actual = new ClassWithIComparableOfT(42); + Assert.That(actual, Is.GreaterThan(expected)); + } + [TestCase(6.0, 5.0, 0.05)] [TestCase(5.05, 5.0, 0.05)] // upper range bound [TestCase(5.0001, 5.0, 0.05)] diff --git a/src/NUnitFramework/tests/Constraints/GreaterThanOrEqualConstraintTests.cs b/src/NUnitFramework/tests/Constraints/GreaterThanOrEqualConstraintTests.cs index cc689bc8b1..ed47175c0f 100644 --- a/src/NUnitFramework/tests/Constraints/GreaterThanOrEqualConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/GreaterThanOrEqualConstraintTests.cs @@ -58,6 +58,14 @@ public void CanCompareIComparablesOfT() Assert.That(actual, Is.GreaterThanOrEqualTo(expected)); } + [Test] + public void CanCompareIComparablesOfInt() + { + int expected = 0; + ClassWithIComparableOfT actual = new ClassWithIComparableOfT(42); + Assert.That(actual, Is.GreaterThanOrEqualTo(expected)); + } + [TestCase(6.0, 5.0, 0.05)] [TestCase(5.05, 5.0, 0.05)] // upper range bound [TestCase(5.0001, 5.0, 0.05)] diff --git a/src/NUnitFramework/tests/Constraints/IndexerConstraintTests.cs b/src/NUnitFramework/tests/Constraints/IndexerConstraintTests.cs new file mode 100644 index 0000000000..bf0e251939 --- /dev/null +++ b/src/NUnitFramework/tests/Constraints/IndexerConstraintTests.cs @@ -0,0 +1,192 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Runtime.CompilerServices; + +namespace NUnit.Framework.Constraints +{ + [TestFixture] + public class IndexerConstraintTests + { + private static readonly string NL = Environment.NewLine; + + [Test] + public void CanMatchIndexerEquality() + { + var tester = new IndexerTester(); + + Assert.That(tester, Has.ItemAt(42).EqualTo("Answer to the Ultimate Question of Life, the Universe, and Everything")); + Assert.That(tester, Has.ItemAt(41).EqualTo("Still calculating").And.ItemAt(42).EqualTo("Answer to the Ultimate Question of Life, the Universe, and Everything")); + + Assert.That(tester, Has.ItemAt(string.Empty).EqualTo("Second indexer")); + Assert.That(tester, Has.ItemAt(1, 2).EqualTo("Third indexer")); + Assert.That(tester, Has.No.ItemAt(string.Empty).EqualTo("Third indexer")); + } + + [Test] + public void CanMatchArrayEquality() + { + var tester = new[] { 1, 2, 3 }; + + Assert.That(tester, Has.ItemAt(0).EqualTo(1)); + Assert.That(tester, Has.ItemAt(2).EqualTo(3)); + } + + [Test] + public void CanMatchArrayWithMultiDimensionsEquality() + { + var tester = new[, , ,] { + { { {1}, {2}, {3} }, { {4}, {5}, {6} } }, + { { {7}, {8}, {9} }, { {10}, {11}, {12} } } + }; + + Assert.That(tester, Has.ItemAt(0,0,0,0).EqualTo(1)); + Assert.That(tester, Has.ItemAt(1,1,2,0).EqualTo(12)); + } + + [Test] + public void DoesNotMatchMissingIndexerEquality() + { + var expectedErrorMessage = $" Expected string length 14 but was 13. Strings differ at index 0.{NL} Expected: \"Second indexer\"{NL} But was: \"Third indexer\"{NL} -----------^{NL}"; + + var tester = new IndexerTester(); + + var ex = Assert.Throws(() => Assert.That(tester, Has.ItemAt(4, 2).EqualTo("Second indexer"))); + Assert.That(ex.Message, Is.EqualTo(expectedErrorMessage)); + } + + [Test] + public void DoesNotMatchWhenIndexerValueIsNotExpectedToBeEqual() + { + var expectedErrorMessage = $" Expected: not Default indexer accepting arguments < > equal to \"Second indexer\"{NL} But was: \"Second indexer\"{NL}"; + + var tester = new IndexerTester(); + + var ex = Assert.Throws(() => Assert.That(tester, Has.No.ItemAt(string.Empty).EqualTo("Second indexer"))); + Assert.That(ex.Message, Is.EqualTo(expectedErrorMessage)); + } + + [Test] + public void DoesNotMatchWhenIndexerIsNotExpectedToBeEqual() + { + var expectedErrorMessage = "Default indexer accepting arguments < 21.0d > was not found on NUnit.Framework.Constraints.IndexerConstraintTests+IndexerTester."; + + var tester = new IndexerTester(); + + var ex = Assert.Throws(() => Assert.That(tester, Has.No.ItemAt(21d).EqualTo("Should Throw"))); + Assert.That(ex.Message, Is.EqualTo(expectedErrorMessage)); + } + + [Test] + public void CanMatchGenericIndexer() + { + var tester = new GenericIndexerTester(100); + + Assert.That(tester, Has.ItemAt(42).EqualTo(100)); + } + + [Test] + public void CanMatchNamedIndexer() + { + var tester = new NamedIndexTester(); + + Assert.That(tester, Has.ItemAt(42).EqualTo("A Named Int Indexer")); + Assert.That(tester, Has.ItemAt(42, 43).EqualTo("A Named Int Int Indexer")); + } + + [Test] + public void CanMatchHidingIndexer() + { + var tester = new DerivedClassWithoutIndexer(); + + Assert.That(tester, Has.ItemAt(string.Empty).EqualTo("New value for Second indexer")); + Assert.That(tester, Has.ItemAt(1, 2).EqualTo("New value for Third indexer")); + Assert.That(tester, Has.ItemAt(41).EqualTo("Still calculating")); + } + + [Test] + public void CanMatchHidingNamedIndexer() + { + var tester = new DerivedClassWithoutNamedIndexer(); + + Assert.That(tester, Has.ItemAt(42).EqualTo("New value for Named Int Indexer")); + Assert.That(tester, Has.ItemAt(42, 43).EqualTo("A Named Int Int Indexer")); + } + + private class IndexerTester + { + public string this[int x] => x == 42 ? "Answer to the Ultimate Question of Life, the Universe, and Everything" : "Still calculating"; + + public string this[string x] => "Second indexer"; + + protected string this[int x, int y] => "Third indexer"; + } + + private class ClassHidingBaseIndexer : IndexerTester + { + public new string this[string x] => "New value for Second indexer"; + + protected new string this[int x, int y] => "New value for Third indexer"; + + private new string this[int x] => "Should not use this as private members can't be shadowed"; + } + + private class DerivedClassWithoutIndexer : ClassHidingBaseIndexer + { + } + + private class GenericIndexerTester + { + private readonly T _item; + + public int DummyParam { get; set; } + + public GenericIndexerTester(T item) + { + _item = item; + } + + public T this[int x] => _item; + } + + private class NamedIndexTester + { + [IndexerName("ANamedIndexer")] + public string this[int x] => "A Named Int Indexer"; + + [IndexerName("ANamedIndexer")] + public string this[int x, int y] => "A Named Int Int Indexer"; + } + + private class ClassHidingBaseNamedIndexer : NamedIndexTester + { + [IndexerName("ANamedIndexer")] + public new string this[int x] => "New value for Named Int Indexer"; + } + + private class DerivedClassWithoutNamedIndexer : ClassHidingBaseNamedIndexer + { + } + } +} diff --git a/src/NUnitFramework/tests/Constraints/LessThanConstraintTests.cs b/src/NUnitFramework/tests/Constraints/LessThanConstraintTests.cs index bbd2a73ea4..f43213bf8f 100644 --- a/src/NUnitFramework/tests/Constraints/LessThanConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/LessThanConstraintTests.cs @@ -58,6 +58,14 @@ public void CanCompareIComparablesOfT() Assert.That(actual, Is.LessThan(expected)); } + [Test] + public void CanCompareIComparablesOfInt() + { + int expected = 42; + ClassWithIComparableOfT actual = new ClassWithIComparableOfT(0); + Assert.That(actual, Is.LessThan(expected)); + } + [TestCase(4.0, 5.0, 0.05)] [TestCase(4.95, 5.0, 0.05)] // lower range bound [TestCase(4.9501, 5.0, 0.05)] // lower range bound + .01 diff --git a/src/NUnitFramework/tests/Constraints/LessThanOrEqualConstraintTests.cs b/src/NUnitFramework/tests/Constraints/LessThanOrEqualConstraintTests.cs index 932bd6e65c..809f5b34af 100644 --- a/src/NUnitFramework/tests/Constraints/LessThanOrEqualConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/LessThanOrEqualConstraintTests.cs @@ -58,6 +58,14 @@ public void CanCompareIComparablesOfT() Assert.That(actual, Is.LessThanOrEqualTo(expected)); } + [Test] + public void CanCompareIComparablesOfInt() + { + int expected = 42; + ClassWithIComparableOfT actual = new ClassWithIComparableOfT(0); + Assert.That(actual, Is.LessThanOrEqualTo(expected)); + } + [TestCase(4.0, 5.0, 0.05)] [TestCase(4.95, 5.0, 0.05)] // lower range bound [TestCase(4.9501, 5.0, 0.05)] // lower range bound + .01 diff --git a/src/NUnitFramework/tests/Constraints/MsgUtilTests.cs b/src/NUnitFramework/tests/Constraints/MsgUtilTests.cs index cd17f33841..cade0a57cd 100644 --- a/src/NUnitFramework/tests/Constraints/MsgUtilTests.cs +++ b/src/NUnitFramework/tests/Constraints/MsgUtilTests.cs @@ -152,7 +152,7 @@ public static void FormatValue_KeyValuePairTest(object key, object value, string Assert.That(s, Is.EqualTo(expectedResult)); } -#if NET45 +#if !NET35 [Test] public static void FormatValue_EmptyValueTupleTest() { @@ -170,21 +170,22 @@ public static void FormatValue_OneElementValueTupleTest() [Test] public static void FormatValue_TwoElementsValueTupleTest() { - string s = MsgUtils.FormatValue(ValueTuple.Create("Hello", 123)); + string s = MsgUtils.FormatValue(("Hello", 123)); Assert.That(s, Is.EqualTo("(\"Hello\", 123)")); } [Test] public static void FormatValue_ThreeElementsValueTupleTest() { - string s = MsgUtils.FormatValue(ValueTuple.Create("Hello", 123, 'a')); + string s = MsgUtils.FormatValue(("Hello", 123, 'a')); Assert.That(s, Is.EqualTo("(\"Hello\", 123, 'a')")); } [Test] public static void FormatValue_EightElementsValueTupleTest() { - var tuple = ValueTuple.Create(1, 2, 3, 4, 5, 6, 7, 8); + var tuple = (1, 2, 3, 4, 5, 6, 7, 8); + string s = MsgUtils.FormatValue(tuple); Assert.That(s, Is.EqualTo("(1, 2, 3, 4, 5, 6, 7, 8)")); } @@ -192,7 +193,8 @@ public static void FormatValue_EightElementsValueTupleTest() [Test] public static void FormatValue_EightElementsValueTupleNestedTest() { - var tuple = ValueTuple.Create(1, 2, 3, 4, 5, 6, 7, ValueTuple.Create(8, "9")); + var tuple = (1, 2, 3, 4, 5, 6, 7, (8, "9")); + string s = MsgUtils.FormatValue(tuple); Assert.That(s, Is.EqualTo("(1, 2, 3, 4, 5, 6, 7, (8, \"9\"))")); } @@ -200,16 +202,12 @@ public static void FormatValue_EightElementsValueTupleNestedTest() [Test] public static void FormatValue_FifteenElementsValueTupleTest() { - var tupleLastElements = ValueTuple.Create(8, 9, 10, 11, "12", 13, 14, "15"); - var tuple = new ValueTuple>> - (1, 2, 3, 4, 5, 6, 7, tupleLastElements); + var tuple = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, "12", 13, 14, "15"); string s = MsgUtils.FormatValue(tuple); Assert.That(s, Is.EqualTo("(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, \"12\", 13, 14, \"15\")")); } -#endif -#if !NET35 [Test] public static void FormatValue_OneElementTupleTest() { diff --git a/src/NUnitFramework/tests/Constraints/NUnitComparerTests.cs b/src/NUnitFramework/tests/Constraints/NUnitComparerTests.cs index 468360ec0e..a6aa761265 100644 --- a/src/NUnitFramework/tests/Constraints/NUnitComparerTests.cs +++ b/src/NUnitFramework/tests/Constraints/NUnitComparerTests.cs @@ -72,5 +72,98 @@ public void UnequalItems(object greater, object lesser) Assert.That(comparer.Compare(greater, lesser) > 0); Assert.That(comparer.Compare(lesser, greater) < 0); } + + [Test] + public void Comparables() + { + var greater = new ClassWithIComparable(42); + var lesser = new ClassWithIComparable(-42); + + Assert.That(comparer.Compare(greater, lesser) > 0); + Assert.That(comparer.Compare(lesser, greater) < 0); + } + + [Test] + public void ComparablesOfT() + { + var greater = new ClassWithIComparableOfT(42); + var lesser = new ClassWithIComparableOfT(-42); + + Assert.That(comparer.Compare(greater, lesser) > 0); + Assert.That(comparer.Compare(lesser, greater) < 0); + } + + [Test] + public void ComparablesOfInt1() + { + int greater = 42; + var lesser = new ClassWithIComparableOfT(-42); + + Assert.That(comparer.Compare(greater, lesser) > 0); + Assert.That(comparer.Compare(lesser, greater) < 0); + } + + [Test] + public void ComparablesOfInt2() + { + var greater = new ClassWithIComparableOfT(42); + int lesser = -42; + + Assert.That(comparer.Compare(greater, lesser) > 0); + Assert.That(comparer.Compare(lesser, greater) < 0); + } + + [Test] + public void ComparablesOfInt3() + { + short greater = 42; + var lesser = new ClassWithIComparableOfT(-42); + + Assert.That(comparer.Compare(greater, lesser) > 0); + Assert.That(comparer.Compare(lesser, greater) < 0); + } + + #region Comparison Test Classes + + private class ClassWithIComparable : IComparable + { + private readonly int val; + + public ClassWithIComparable(int val) + { + this.val = val; + } + + public int CompareTo(object x) + { + ClassWithIComparable other = x as ClassWithIComparable; + if (x is ClassWithIComparable) + return val.CompareTo(other.val); + + throw new ArgumentException(); + } + } + + private class ClassWithIComparableOfT : IComparable, IComparable + { + private readonly int val; + + public ClassWithIComparableOfT(int val) + { + this.val = val; + } + + public int CompareTo(ClassWithIComparableOfT other) + { + return val.CompareTo(other.val); + } + + public int CompareTo(int other) + { + return val.CompareTo(other); + } + } + + #endregion } } diff --git a/src/NUnitFramework/tests/Constraints/NUnitEqualityComparerTests.cs b/src/NUnitFramework/tests/Constraints/NUnitEqualityComparerTests.cs index ba2e1348b1..00a8513bbc 100644 --- a/src/NUnitFramework/tests/Constraints/NUnitEqualityComparerTests.cs +++ b/src/NUnitFramework/tests/Constraints/NUnitEqualityComparerTests.cs @@ -24,13 +24,15 @@ using System; using System.Collections; using System.Collections.Generic; +using System.Diagnostics; using System.IO; +using System.Runtime.CompilerServices; using NUnit.TestUtilities; namespace NUnit.Framework.Constraints { [TestFixture] - public class EqualityComparerTests + public class NUnitEqualityComparerTests { private Tolerance tolerance; private NUnitEqualityComparer comparer; @@ -306,6 +308,102 @@ public void IEnumeratorIsDisposed() Assert.True(comparer.AreEqual(enumeration, enumeration, ref tolerance)); Assert.That(enumeration.EnumeratorsDisposed); } + + [TestCaseSource(nameof(GetRecursiveContainsTestCases))] + public void SelfContainedItemFoundInCollection(T x, ICollection y) + { + var equalityComparer = new NUnitEqualityComparer(); + var tolerance = Tolerance.Default; + var equality = equalityComparer.AreEqual(x, y, ref tolerance); + + Assert.IsFalse(equality); + Assert.Contains(x, y); + Assert.That(y, Contains.Item(x)); + Assert.That(y, Does.Contain(x)); + } + + [TestCaseSource(nameof(GetRecursiveComparerTestCases))] + public void SelfContainedItemDoesntRecurseForever(T x, ICollection y) + { + var equalityComparer = new NUnitEqualityComparer(); + var tolerance = Tolerance.Default; + equalityComparer.ExternalComparers.Add(new DetectRecursionComparer(30)); + + Assert.DoesNotThrow(() => equalityComparer.AreEqual(x, y, ref tolerance)); + } + + [Test] + public void SelfContainedDuplicateItemsAreCompared() + { + var equalityComparer = new NUnitEqualityComparer(); + var equalInstance1 = new[] { 1 }; + var equalInstance2 = new[] { 1 }; + + var x = new[] { equalInstance1, equalInstance1 }; + var y = new[] { equalInstance2, equalInstance2 }; + + Assert.True(equalityComparer.AreEqual(x, y, ref tolerance)); + } + + public static IEnumerable GetRecursiveComparerTestCases() + { + // Separate from 'GetRecursiveContainsTestCases' until a stackoverflow issue in + // 'MsgUtils.FormatValue()' can be fixed for the below cases + foreach (var testCase in GetRecursiveContainsTestCases()) + yield return testCase; + + var dict = new Dictionary(); + var dictItem = "nunit"; + + dict[1] = dictItem; + dict[2] = dict; + + yield return new TestCaseData(dictItem, dict); + } + + public static IEnumerable GetRecursiveContainsTestCases() + { + var enumerable = new SelfContainer(); + var enumerableContainer = new SelfContainer[] { new SelfContainer(), enumerable }; + + yield return new TestCaseData(enumerable, enumerableContainer); + + object itemB = 1; + object[] itemBSet = new object[2]; + itemBSet[0] = itemB; + itemBSet[1] = itemBSet; + + yield return new TestCaseData(itemB, itemBSet); + } + } + + internal class DetectRecursionComparer : EqualityAdapter + { + private readonly int maxRecursion; + + [MethodImpl(MethodImplOptions.NoInlining)] + public DetectRecursionComparer(int maxRecursion) + { + var callerDepth = new StackTrace().FrameCount - 1; + this.maxRecursion = callerDepth + maxRecursion; + } + + [MethodImpl(MethodImplOptions.NoInlining)] + public override bool CanCompare(object x, object y) + { + var currentDepth = new StackTrace().FrameCount - 1; + return currentDepth >= maxRecursion; + } + + public override bool AreEqual(object x, object y) + { + throw new InvalidOperationException("Recurses"); + } + } + + internal class SelfContainer : IEnumerable + { + public IEnumerator GetEnumerator() { yield return this; } } internal class EnumerableWithDisposeChecks : IEnumerable diff --git a/src/NUnitFramework/tests/Constraints/NumericsTest.cs b/src/NUnitFramework/tests/Constraints/NumericsTest.cs index 66299ee6f3..0c77a487c2 100644 --- a/src/NUnitFramework/tests/Constraints/NumericsTest.cs +++ b/src/NUnitFramework/tests/Constraints/NumericsTest.cs @@ -29,11 +29,12 @@ namespace NUnit.Framework.Constraints [TestFixture] public class NumericsTests { - private Tolerance tenPercent, zeroTolerance; + private Tolerance tenPercent, zeroTolerance, absoluteTolerance; [SetUp] public void SetUp() { + absoluteTolerance = new Tolerance(0.1); tenPercent = new Tolerance(10.0).Percent; zeroTolerance = Tolerance.Exact; } @@ -83,6 +84,21 @@ public void CanMatchDecimalWithPercentage() Assert.IsTrue(Numerics.AreEqual(10000m, 10500m, ref tenPercent)); } + [Test] + public void CanCalculateAbsoluteDifference() + { + Assert.That(Numerics.Difference(10000m, 9500m, absoluteTolerance.Mode), Is.EqualTo(500m)); + Assert.That(Convert.ToDouble(Numerics.Difference(0.1, 0.05, absoluteTolerance.Mode)), Is.EqualTo(0.05).Within(0.00001)); + Assert.That(Convert.ToDouble(Numerics.Difference(0.1, 0.15, absoluteTolerance.Mode)), Is.EqualTo(-0.05).Within(0.00001)); + } + + [Test] + public void CanCalculatePercentDifference() + { + Assert.That(Numerics.Difference(10000m, 8500m, tenPercent.Mode), Is.EqualTo(15)); + Assert.That(Numerics.Difference(10000m, 11500m, tenPercent.Mode), Is.EqualTo(-15)); + } + [TestCase((int)8500)] [TestCase((int)11500)] [TestCase((uint)8500)] @@ -108,4 +124,4 @@ public void FailsOnDecimalAbovePercentage() Assert.Throws(() => Assert.IsTrue(Numerics.AreEqual(10000m, 11500m, ref tenPercent))); } } -} \ No newline at end of file +} diff --git a/src/NUnitFramework/tests/Constraints/PathConstraintTests.cs b/src/NUnitFramework/tests/Constraints/PathConstraintTests.cs index 52826cac6a..03220530d6 100644 --- a/src/NUnitFramework/tests/Constraints/PathConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/PathConstraintTests.cs @@ -28,7 +28,7 @@ namespace NUnit.Framework.Constraints /// /// Summary description for PathConstraintTests. /// ] - [TestFixture] + [TestFixture, Platform("Win")] public class SamePathTest_Windows : StringConstraintTests { [SetUp] @@ -61,7 +61,7 @@ public void RootPathEquality() } } - [TestFixture] + [TestFixture, Platform("Unix")] public class SamePathTest_Linux : StringConstraintTests { [SetUp] @@ -102,7 +102,7 @@ public void RootPathEquality() } } - [TestFixture] + [TestFixture, Platform("Win")] public class SubPathTest_Windows : ConstraintTestBase { [SetUp] @@ -140,7 +140,7 @@ public void SubPathOfRoot() } } - [TestFixture] + [TestFixture, Platform("Unix")] public class SubPathTest_Linux : ConstraintTestBase { [SetUp] @@ -179,7 +179,7 @@ public void SubPathOfRoot() } } - [TestFixture] + [TestFixture, Platform("Win")] public class SamePathOrUnderTest_Windows : StringConstraintTests { [SetUp] @@ -211,7 +211,7 @@ public void SetUp() }; } - [TestFixture] + [TestFixture, Platform("Unix")] public class SamePathOrUnderTest_Linux : StringConstraintTests { [SetUp] diff --git a/src/NUnitFramework/tests/Constraints/PropertyConstraintTypeHierarchyTests.cs b/src/NUnitFramework/tests/Constraints/PropertyConstraintTypeHierarchyTests.cs new file mode 100644 index 0000000000..980c47828a --- /dev/null +++ b/src/NUnitFramework/tests/Constraints/PropertyConstraintTypeHierarchyTests.cs @@ -0,0 +1,111 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Collections.Generic; + +namespace NUnit.Framework.Constraints +{ + [TestFixture(TestOf = typeof(PropertyConstraint))] + public class PropertyConstraintTypeHierarchyTests + { + [Test] + public void PropertyDefinedInDerivedClass_ShouldExist() + { + var sut = new PropertyConstraint(nameof(Derived.SomeProperty), new EqualConstraint(42)); + + var instance = (Base)new Derived(); + var actual = sut.ApplyTo(instance); + + Assert.That(actual, Has.Property(nameof(ConstraintResult.Status)).EqualTo(ConstraintStatus.Success)); + + + var existSut = new PropertyExistsConstraint(nameof(Derived.SomeProperty)); + var actualExist = existSut.ApplyTo(instance); + Assert.That(actualExist.IsSuccess, Is.True); + } + + class Base { } + class Derived : Base + { + public int SomeProperty { get; set; } = 42; + } + + + private int[] _array; + private PropertyConstraint _countPropertyConstraint; + private PropertyExistsConstraint _countPropertyExistsConstraint; + + [SetUp] + public void BeforeEveryTest() + { + _array = new[] { 1, 2, 3 }; + _countPropertyConstraint = new PropertyConstraint(nameof(IList.Count), new EqualConstraint(_array.Length)); + _countPropertyExistsConstraint = new PropertyExistsConstraint(nameof(IList.Count)); + } + + [Test] + public void ExplicitlyImplementedProperty_ShouldNotExist_ViaImplementingType() + { + var ex = Assert.Throws(() => _countPropertyConstraint.ApplyTo(_array)); + + Assert.That(ex, Has.Message.StartWith("Property Count was not found on System.Int32[]")); + + var actualExist = _countPropertyExistsConstraint.ApplyTo(_array); + Assert.That(actualExist.IsSuccess, Is.False); + } + + [Test] + public void PropertyDefinedInInterface_ShouldNotExist_WhenCastToObject() + { + var ex = Assert.Throws(() => _countPropertyConstraint.ApplyTo((object)_array)); + + Assert.That(ex, Has.Message.StartWith("Property Count was not found on System.Int32[]")); + + var actualExist = _countPropertyExistsConstraint.ApplyTo((object)_array); + Assert.That(actualExist.IsSuccess, Is.False); + } + + [Test] + public void PropertyDefinedInInterface_ShouldExist_WhenCastToICollection() + { + var actual = _countPropertyConstraint.ApplyTo((ICollection)_array); + + Assert.That(actual, Has.Property(nameof(ConstraintResult.Status)).EqualTo(ConstraintStatus.Success)); + + var actualExist = _countPropertyExistsConstraint.ApplyTo((ICollection)_array); + Assert.That(actualExist.IsSuccess, Is.True); + } + + [Test] + public void PropertyDefinedInInterface_ShouldExist_WhenCastToIList() + { + var actual = _countPropertyConstraint.ApplyTo((IList)_array); + + Assert.That(actual, Has.Property(nameof(ConstraintResult.Status)).EqualTo(ConstraintStatus.Success)); + + var actualExist = _countPropertyExistsConstraint.ApplyTo((IList)_array); + Assert.That(actualExist.IsSuccess, Is.True); + } + } +} diff --git a/src/NUnitFramework/tests/Constraints/PropertyTests.cs b/src/NUnitFramework/tests/Constraints/PropertyTests.cs index 06898672c9..e937783ab8 100644 --- a/src/NUnitFramework/tests/Constraints/PropertyTests.cs +++ b/src/NUnitFramework/tests/Constraints/PropertyTests.cs @@ -1,3 +1,4 @@ + // *********************************************************************** // Copyright (c) 2009 Charlie Poole, Rob Prouse // @@ -79,7 +80,7 @@ public void SetUp() static object[] SuccessData = new object[] { new int[0], "hello", typeof(Array) }; - static object[] FailureData = new object[] { + static object[] FailureData = new object[] { new TestCaseData( 42, "" ), new TestCaseData( new List(), "" ), new TestCaseData( typeof(Int32), "" ) }; @@ -103,7 +104,7 @@ public void SetUp() static object[] SuccessData = new object[] { new int[5], "hello" }; - static object[] FailureData = new object[] { + static object[] FailureData = new object[] { new TestCaseData( new int[3], "3" ), new TestCaseData( "goodbye", "7" ) }; @@ -184,7 +185,7 @@ public void MultipleProperties() Assert.That(r.Status, Is.EqualTo(ConstraintStatus.Failure)); Assert.That(r.Description, Is.EqualTo("property Foo and property Bar property Length equal to 5")); Assert.That(r.ActualValue, Is.EqualTo(inputObject)); - } + } [Test] public void FailureMessageContainsChainedConstraintMessage() @@ -193,7 +194,7 @@ public void FailureMessageContainsChainedConstraintMessage() //Property Constraint Message with chained Equivalent Constraint. var constraint = ((IResolveConstraint)Has.Property("Foo").EquivalentTo(new List { 2, 3, 5, 8 })).Resolve(); - + //Apply the constraint and write message. var result = constraint.ApplyTo(inputObject); var textMessageWriter = new TextMessageWriter(); diff --git a/src/NUnitFramework/tests/Constraints/RegexConstraintTests.cs b/src/NUnitFramework/tests/Constraints/RegexConstraintTests.cs new file mode 100644 index 0000000000..2a623d1e54 --- /dev/null +++ b/src/NUnitFramework/tests/Constraints/RegexConstraintTests.cs @@ -0,0 +1,89 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using System.Text.RegularExpressions; + +namespace NUnit.Framework.Constraints +{ + [TestFixture] + public class RegexConstraintTests + { + private static readonly string NL = Environment.NewLine; + + [Test] + public void RegExMatchSucceeds() + { + const string testMatcher = "Make.*tests.*pass"; + const string testPhrase = "Make your tests fail before passing!"; + + Assert.That(testPhrase, Does.Match(testMatcher)); + Assert.That(testPhrase, Does.Match(new Regex(testMatcher))); + } + + [Test] + public void RegExCaseInsensitiveMatchSucceeds() + { + const string testMatcher = "make.*tests.*PASS"; + const string testPhrase = "Make your tests fail before passing!"; + + Assert.That(testPhrase, Does.Match(testMatcher).IgnoreCase); + Assert.That(testPhrase, Does.Match(new Regex(testMatcher)).IgnoreCase); + Assert.That(testPhrase, Does.Match(new Regex(testMatcher, RegexOptions.IgnoreCase))); + } + + [Test] + public void RegExNegativeMatchSucceeds() + { + const string testMatcher = "make.*tests.*fail"; + const string testPhrase = "Make your tests fail before passing!"; + + Assert.That(testPhrase, Does.Not.Match(testMatcher)); + Assert.That(testPhrase, Does.Not.Match(new Regex(testMatcher))); + } + + [Test] + public void RegExConstraintExpressionMatchesSucceeds() + { + const string testMatcher = "Make.*tests.*pass"; + const string testPhrase = "Make your tests fail before passing!"; + + Assert.That(testPhrase, Is.Not.Null.And.Matches(testMatcher)); + Assert.That(testPhrase, Is.Not.Null.And.Matches(new Regex(testMatcher))); + } + + [Test] + public void RegExCaseMatchFails() + { + var expectedErrorMessage = $" Expected: String matching \"make.*tests.*fail\"{NL} But was: \"Make your tests fail before passing!\"{NL}"; + const string testMatcher = "make.*tests.*fail"; + const string testPhrase = "Make your tests fail before passing!"; + + var ex = Assert.Throws(() => Assert.That(testPhrase, Does.Match(testMatcher))); + Assert.That(ex.Message, Is.EqualTo(expectedErrorMessage)); + + ex = Assert.Throws(() => Assert.That(testPhrase, Does.Match(new Regex(testMatcher)))); + Assert.That(ex.Message, Is.EqualTo(expectedErrorMessage)); + } + } +} diff --git a/src/NUnitFramework/tests/Constraints/SubstringConstraintTests.cs b/src/NUnitFramework/tests/Constraints/SubstringConstraintTests.cs index 5f77448a48..725b4511b4 100644 --- a/src/NUnitFramework/tests/Constraints/SubstringConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/SubstringConstraintTests.cs @@ -54,7 +54,6 @@ public void SetUp() [TestCase(" SS ", "ß", StringComparison.CurrentCultureIgnoreCase)] [TestCase(" ss ", "s", StringComparison.CurrentCultureIgnoreCase)] [TestCase(" SS ", "s", StringComparison.CurrentCultureIgnoreCase)] -#if !NETCOREAPP1_1 [TestCase(" ss ", "ß", StringComparison.InvariantCulture)] [TestCase(" SS ", "ß", StringComparison.InvariantCulture)] [TestCase(" ss ", "s", StringComparison.InvariantCulture)] @@ -63,7 +62,6 @@ public void SetUp() [TestCase(" SS ", "ß", StringComparison.InvariantCultureIgnoreCase)] [TestCase(" ss ", "s", StringComparison.InvariantCultureIgnoreCase)] [TestCase(" SS ", "s", StringComparison.InvariantCultureIgnoreCase)] -#endif [TestCase(" ss ", "ß", StringComparison.Ordinal)] [TestCase(" SS ", "ß", StringComparison.Ordinal)] [TestCase(" ss ", "s", StringComparison.Ordinal)] @@ -91,12 +89,10 @@ public void UseDifferentComparisonTypes_ThrowsException() // Invoke Using method before IgnoreCase Assert.That(() => subStringConstraint.Using(StringComparison.CurrentCulture).IgnoreCase, Throws.TypeOf()); -#if !NETCOREAPP1_1 Assert.That(() => subStringConstraint.Using(StringComparison.InvariantCulture).IgnoreCase, Throws.TypeOf()); Assert.That(() => subStringConstraint.Using(StringComparison.InvariantCultureIgnoreCase).IgnoreCase, Throws.TypeOf()); -#endif Assert.That(() => subStringConstraint.Using(StringComparison.Ordinal).IgnoreCase, Throws.TypeOf()); Assert.That(() => subStringConstraint.Using(StringComparison.OrdinalIgnoreCase).IgnoreCase, @@ -105,12 +101,10 @@ public void UseDifferentComparisonTypes_ThrowsException() // Invoke IgnoreCase before Using method Assert.That(() => (subStringConstraint.IgnoreCase as SubstringConstraint).Using(StringComparison.CurrentCulture), Throws.TypeOf()); -#if !NETCOREAPP1_1 Assert.That(() => (subStringConstraint.IgnoreCase as SubstringConstraint).Using(StringComparison.InvariantCulture), Throws.TypeOf()); Assert.That(() => (subStringConstraint.IgnoreCase as SubstringConstraint).Using(StringComparison.InvariantCultureIgnoreCase), Throws.TypeOf()); -#endif Assert.That(() => (subStringConstraint.IgnoreCase as SubstringConstraint).Using(StringComparison.Ordinal).IgnoreCase, Throws.TypeOf()); Assert.That(() => (subStringConstraint.IgnoreCase as SubstringConstraint).Using(StringComparison.OrdinalIgnoreCase).IgnoreCase, @@ -135,7 +129,7 @@ public void UseSameComparisonTypes_DoesNotThrowException() } } - [TestFixture] + [TestFixture, SetCulture("en-US")] public class SubstringConstraintTestsIgnoringCase : StringConstraintTests { [SetUp] @@ -154,22 +148,4 @@ public void SetUp() new TestCaseData( string.Empty, "" ), new TestCaseData( null, "null" ) }; } - - //[TestFixture] - //public class EqualIgnoringCaseTest : ConstraintTest - //{ - // [SetUp] - // public void SetUp() - // { - // Matcher = new EqualConstraint("Hello World!").IgnoreCase; - // Description = "\"Hello World!\", ignoring case"; - // } - - // static object[] SuccessData = new object[] { "hello world!", "Hello World!", "HELLO world!" }; - - // static object[] FailureData = new object[] { "goodbye", "Hello Friends!", string.Empty, null }; - - - // string[] ActualValues = new string[] { "\"goodbye\"", "\"Hello Friends!\"", "", "null" }; - //} } diff --git a/src/NUnitFramework/tests/Constraints/ThrowsConstraintTests.cs b/src/NUnitFramework/tests/Constraints/ThrowsConstraintTests.cs index 18a08e9f92..660b283b43 100644 --- a/src/NUnitFramework/tests/Constraints/ThrowsConstraintTests.cs +++ b/src/NUnitFramework/tests/Constraints/ThrowsConstraintTests.cs @@ -72,7 +72,7 @@ public void SetUp() static object[] FailureData = new object[] { - new TestCaseData( new TestDelegate( TestDelegates.ThrowsArgumentException ), $"" } }; + static object[] FailureData = new object[] { new object[] { + new int[] { 1, 3, 17, 3, 34 }, + "< 1, 3, 17, 3, 34 >" + Environment.NewLine + " Not unique items: < 3 >" } + }; - [Test] - [TestCaseSource( nameof(IgnoreCaseData) )] - public void HonorsIgnoreCase( IEnumerable actual ) + [Test, SetCulture("")] + [TestCaseSource(nameof(IgnoreCaseData))] + public void HonorsIgnoreCase(IEnumerable actual) { - Assert.That( new UniqueItemsConstraint().IgnoreCase.ApplyTo( actual ).IsSuccess, Is.False, "{0} should not be unique ignoring case", actual ); + var constraint = new UniqueItemsConstraint().IgnoreCase; + var result = constraint.ApplyTo(actual); + + Assert.That(result.IsSuccess, Is.False, "{0} should not be unique ignoring case", actual); } private static readonly object[] IgnoreCaseData = @@ -57,19 +66,74 @@ public void HonorsIgnoreCase( IEnumerable actual ) new object[] {new[] {"a", "b", "c", "C"}} }; + private static readonly object[] DuplicateItemsData = + { + new object[] {new[] { 1, 2, 3, 2 }, new[] { 2 }}, + new object[] {new[] { 2, 1, 2, 3, 2 }, new[] { 2 }}, + new object[] {new[] { 2, 1, 2, 3, 3 }, new[] { 2, 3 }}, + new object[] {new[] { "x", null, "x" }, new[] { "x" }} + }; + static readonly IEnumerable RANGE = Enumerable.Range(0, 10000); - static readonly TestCaseData[] PerformanceData = + static readonly TestCaseData[] PerformanceData_FastPath = { + // Generic container new TestCaseData(RANGE, false), new TestCaseData(new List(RANGE), false), new TestCaseData(new List(RANGE.Select(v => (double)v)), false), new TestCaseData(new List(RANGE.Select(v => v.ToString())), false), - new TestCaseData(new List(RANGE.Select(v => v.ToString())), true) + new TestCaseData(new List(RANGE.Select(v => v.ToString())), true), + + // Non-generic container + new TestCaseData(new SimpleObjectCollection(RANGE), false) + { + ArgDisplayNames = new[] { "IEnumerable", "false" } + }, + new TestCaseData(new SimpleObjectCollection(RANGE.Cast()), false) + { + ArgDisplayNames = new[] { "IEnumerable", "false" }, + }, + new TestCaseData(new SimpleObjectCollection(RANGE.Select(v => (double)v).Cast()), false) + { + ArgDisplayNames = new[] { "IEnumerable", "false" } + }, + new TestCaseData(new SimpleObjectCollection(RANGE.Select(v => v.ToString()).Cast()), false) + { + ArgDisplayNames = new[] { "IEnumerable", "false" } + }, + new TestCaseData(new SimpleObjectCollection(RANGE.Select(v => v.ToString()).Cast()), true) + { + ArgDisplayNames = new[] { "IEnumerable", "true" } + }, + new TestCaseData(new SimpleObjectCollection(RANGE.Select(v => new TestReferenceType() { A = v }).Cast()), true) + { + ArgDisplayNames = new[] { "IEnumerable", "true" } + }, }; + static TestCaseData[] PerformanceData_FastPath_MixedTypes + { + get + { + var refTypes = RANGE.Take(5000).Select(o => new TestReferenceType() { A = o }).Cast(); + var valueTypes = RANGE.Skip(5000).Select(o => new TestValueType() { A = o }).Cast(); + var container = new List(); + + container.AddRange(refTypes); + container.AddRange(valueTypes); + + return new TestCaseData[] { + new TestCaseData(new SimpleObjectCollection(container.Cast()), true) + { + ArgDisplayNames = new[] { "IEnumerable", "true" } + } + }; + } + } - [TestCaseSource(nameof(PerformanceData))] - public void PerformanceTests(IEnumerable values, bool ignoreCase) + [TestCaseSource(nameof(PerformanceData_FastPath))] + [TestCaseSource(nameof(PerformanceData_FastPath_MixedTypes))] + public void PerformanceTests_FastPath(IEnumerable values, bool ignoreCase) { Warn.Unless(() => { @@ -79,5 +143,175 @@ public void PerformanceTests(IEnumerable values, bool ignoreCase) Assert.That(values, Is.Unique); }, HelperConstraints.HasMaxTime(100)); } + + private static IEnumerable RANGE_SLOWPATH = Enumerable.Range(0, 750); + private static readonly TestCaseData[] SlowpathData = + { + new TestCaseData(RANGE_SLOWPATH.Select(o => o.ToString()).Cast()) + { + ArgDisplayNames = new[] { "IEnumerable" } + }, + new TestCaseData(RANGE_SLOWPATH.Select(o => new DateTimeOffset(o, TimeSpan.Zero)).Cast()) + { + ArgDisplayNames = new[] { "IEnumerable" } + }, + new TestCaseData(RANGE_SLOWPATH.Select(o => (char)o).Cast()) + { + ArgDisplayNames = new[] { "IEnumerable" } + }, + new TestCaseData(RANGE_SLOWPATH.Select(o => (double)o).Cast()) + { + ArgDisplayNames = new[] { "IEnumerable" } + }, + new TestCaseData(RANGE_SLOWPATH.Select(o => new KeyValuePair(o, o)).Cast()) + { + ArgDisplayNames = new[] { "IEnumerable>" } + }, + new TestCaseData(RANGE_SLOWPATH.Select(o => new DictionaryEntry(o, o)).Cast()) + { + ArgDisplayNames = new[] { "IEnumerable" } + } + }; + + [TestCaseSource(nameof(SlowpathData))] + public void SlowPath_TakenWhenSpecialTypes(IEnumerable testData) + { + var allData = new List(); + allData.Add(new TestValueType() { A = 1 }); + allData.AddRange(testData); + + var items = new SimpleObjectCollection((IEnumerable)allData); + var constraint = new UniqueItemsConstraint(); + var stopwatch = new Stopwatch(); + + stopwatch.Start(); + constraint.ApplyTo(items); + stopwatch.Stop(); + + Assert.That(stopwatch.ElapsedMilliseconds, Is.GreaterThanOrEqualTo(50)); + } + + [TestCaseSource(nameof(DuplicateItemsData))] + public void DuplicateItemsTests(IEnumerable items, IEnumerable expectedFailures) + { + var constraint = new UniqueItemsConstraint().IgnoreCase; + var result = constraint.ApplyTo(items) as UniqueItemsConstraintResult; + + Assert.That(result, Is.Not.Null); + Assert.That(result.NonUniqueItems, Is.EqualTo(expectedFailures)); + } + + private static TestCaseData[] RequiresDefaultComparer + { + get + { + var sameRef = new TestReferenceType() { A = 1 }; + + return new TestCaseData[] { + new TestCaseData() { + Arguments = new object[] + { + new SimpleObjectCollection(new TestValueType() { A = 1 }, new TestValueType() { A = 2 }), + true + }, + ArgDisplayNames = new[] { "ValueTypes", "true" } + }, + + new TestCaseData() { + Arguments = new object[] + { + new SimpleObjectCollection(new TestValueType() { A = 1 }, new TestValueType() { A = 1 }), + false + }, + ArgDisplayNames = new[] { "ValueTypes", "false" } + }, + + new TestCaseData() { + Arguments = new object[] + { + new SimpleObjectCollection(new TestReferenceType() { A = 1 }, new TestReferenceType() { A = 1 }), + true + }, + ArgDisplayNames = new[] { "ReferenceTypes", "true" } + }, + + new TestCaseData() { + Arguments = new object[] + { + new SimpleObjectCollection(sameRef, sameRef), + false + }, + ArgDisplayNames = new[] { "ReferenceTypes", "false" } + }, + + new TestCaseData() { + Arguments = new object[] + { + new SimpleObjectCollection( + new TestReferenceType_OverridesEquals() { A = 1 }, + new TestReferenceType_OverridesEquals() { A = 1 } + ), + false + }, + ArgDisplayNames = new[] { "ReferenceTypesOverridesEquals", "false" } + }, + + new TestCaseData() { + Arguments = new object[] + { + new SimpleObjectCollection(new TestValueType() { A = 1 }, new TestReferenceType() { A = 1 }), + true + }, + ArgDisplayNames = new[] { "MixedTypes", "true" } + }, + + new TestCaseData() { + Arguments = new object[] + { + new SimpleObjectCollection(new TestValueType() { A = 1 }, sameRef, sameRef), + false + }, + ArgDisplayNames = new[] { "MixedTypes", "false" } + } + }; + } + } + + [TestCaseSource(nameof(RequiresDefaultComparer))] + public void DuplicateItemsTests_RequiresDefaultComparer(IEnumerable items, bool success) + { + var constraint = new UniqueItemsConstraint(); + var result = constraint.ApplyTo(items) as UniqueItemsConstraintResult; + + Assert.That(result, Is.Not.Null); + Assert.That(result.IsSuccess, Is.EqualTo(success)); + } + + private sealed class TestReferenceType + { + public int A { get; set; } + } + + private sealed class TestReferenceType_OverridesEquals + { + public int A { get; set; } + + public override bool Equals(object obj) + { + if (obj is TestReferenceType_OverridesEquals other) + return other.A == this.A; + return false; + } + + public override int GetHashCode() + { + return this.A.GetHashCode(); + } + } + + private struct TestValueType + { + public int A { get; set; } + } } } diff --git a/src/NUnitFramework/tests/Constraints/ValueTupleEqualityTests.cs b/src/NUnitFramework/tests/Constraints/ValueTupleEqualityTests.cs index cb0f180535..6d787f1dc9 100644 --- a/src/NUnitFramework/tests/Constraints/ValueTupleEqualityTests.cs +++ b/src/NUnitFramework/tests/Constraints/ValueTupleEqualityTests.cs @@ -33,35 +33,33 @@ public class ValueTupleEqualityTests [Test] public void SucceedsWhenTuplesAreTheSame() { - ValueTuple tuple1 = new ValueTuple("Hello", 3); - ValueTuple tuple2 = new ValueTuple("Hello", 3); + ValueTuple tuple1 = ("Hello", 3); + ValueTuple tuple2 = ("Hello", 3); Assert.That(tuple1, Is.EqualTo(tuple2)); } [Test] public void SucceedsWhenContentOfTuplesAreEquivalent() { - ValueTuple tuple1 = new ValueTuple("Hello", 3); - ValueTuple tuple2 = new ValueTuple("Hello", 3); + ValueTuple tuple1 = ("Hello", 3); + ValueTuple tuple2 = ("Hello", 3); Assert.That(tuple1, Is.EqualTo(tuple2)); } [Test] public void SucceedsWhenContentOfTuplesAreEquivalentWith8Elements() { - var tuple1 = ValueTuple.Create(1, 2, 3, 4, 5, 6, 7, 8); - var tuple2 = ValueTuple.Create(1, 2, 3, 4, 5, 6, 7, 8.0f); + var tuple1 = (1, 2, 3, 4, 5, 6, 7, 8); + var tuple2 = (1, 2, 3, 4, 5, 6, 7, 8.0f); + Assert.That(tuple1, Is.EqualTo(tuple2)); } [Test] public void SucceedsWhenContentOfTuplesAreEquivalentWith15Elements() { - var tuple1LastElements = ValueTuple.Create(8, 9, 10, 11, 12, 13, 14, 15); - var tuple1 = ValueTuple.Create(1, 2, 3, 4, 5, 6, 7, tuple1LastElements); - - var tuple2LastElements = ValueTuple.Create(8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f); - var tuple2 = ValueTuple.Create(1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, tuple2LastElements); + var tuple1 = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15); + var tuple2 = (1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, 9.0f, 10.0f, 11.0f, 12.0f, 13.0f, 14.0f, 15.0f); Assert.That(tuple1, Is.EqualTo(tuple2)); } @@ -69,24 +67,24 @@ public void SucceedsWhenContentOfTuplesAreEquivalentWith15Elements() [Test] public void FailsWhenTuplesAreOfDifferentLengths() { - ValueTuple tuple1 = new ValueTuple("Hello", 3); - ValueTuple tuple2 = new ValueTuple("Hello", 3, 1); + ValueTuple tuple1 = ("Hello", 3); + ValueTuple tuple2 = ("Hello", 3, 1); Assert.That(tuple1, Is.Not.EqualTo(tuple2)); } [Test] public void FailsWhenContentOfTuplesAreDifferent() { - ValueTuple tuple1 = new ValueTuple("Hello", 3); - ValueTuple tuple2 = new ValueTuple("Hello", 4); + ValueTuple tuple1 = ("Hello", 3); + ValueTuple tuple2 = ("Hello", 4); Assert.That(tuple1, Is.Not.EqualTo(tuple2)); } [Test] public void FailsWhenContentOfTuplesAreDifferentWith8Elements() { - var tuple1 = ValueTuple.Create(1, 2, 3, 4, 5, 6, 7, 8); - var tuple2 = ValueTuple.Create(1, 2, 3, 4, 5, 6, 7, 9); + var tuple1 = (1, 2, 3, 4, 5, 6, 7, 8); + var tuple2 = (1, 2, 3, 4, 5, 6, 7, 9); Assert.That(tuple1, Is.Not.EqualTo(tuple2)); } diff --git a/src/NUnitFramework/tests/Constraints/XmlSerializableTest.cs b/src/NUnitFramework/tests/Constraints/XmlSerializableTest.cs index daa413600f..a85455da56 100644 --- a/src/NUnitFramework/tests/Constraints/XmlSerializableTest.cs +++ b/src/NUnitFramework/tests/Constraints/XmlSerializableTest.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !NETCOREAPP1_1 using System; using System.Collections; using System.Collections.Generic; @@ -41,7 +40,7 @@ public void SetUp() static object[] SuccessData = new object[] { 1, "a", new List() }; - static object[] FailureData = new object[] { + static object[] FailureData = new object[] { new TestCaseData( new Dictionary(), "" ), new TestCaseData( new InternalClass(), "<" + typeof(InternalClass).FullName + ">" ), new TestCaseData( new InternalWithSerializableAttributeClass(), "<" + typeof(InternalWithSerializableAttributeClass).FullName + ">" ) @@ -62,4 +61,3 @@ internal class InternalClass internal class InternalWithSerializableAttributeClass { } } -#endif diff --git a/src/NUnitFramework/tests/ExecutionContextTests.cs b/src/NUnitFramework/tests/ExecutionContextTests.cs new file mode 100644 index 0000000000..f4f413f1bf --- /dev/null +++ b/src/NUnitFramework/tests/ExecutionContextTests.cs @@ -0,0 +1,69 @@ +// *********************************************************************** +// Copyright (c) 2020 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using NUnit.TestData; +using NUnit.TestUtilities; + +namespace NUnit.Framework +{ + public static class ExecutionContextTests + { + [Test] + public static void ExecutionContextIsNotSharedBetweenTestCases() + { + var result = TestBuilder.RunParameterizedMethodSuite( + typeof(ExecutionContextFixture), + nameof(ExecutionContextFixture.ExecutionContextIsNotSharedBetweenTestCases)); + + result.AssertPassed(); + } + + [Test] + public static void TestCaseSourceExecutionContextIsNotShared() + { + var result = TestBuilder.RunParameterizedMethodSuite( + typeof(ExecutionContextFixture), + nameof(ExecutionContextFixture.TestCaseSourceExecutionContextIsNotShared)); + + result.AssertPassed(); + } + + [Test] + public static void ValueSourceExecutionContextIsNotShared() + { + var result = TestBuilder.RunParameterizedMethodSuite( + typeof(ExecutionContextFixture), + nameof(ExecutionContextFixture.ValueSourceExecutionContextIsNotShared)); + + result.AssertPassed(); + } + + [Test] + public static void ExecutionContextFlow() + { + var result = TestBuilder.RunTestFixture(typeof(ExecutionContextFlowFixture)); + + result.AssertPassed(); + } + } +} diff --git a/src/NUnitFramework/tests/HelperConstraints.cs b/src/NUnitFramework/tests/HelperConstraints.cs index 868d1e60c3..c71c01fb85 100644 --- a/src/NUnitFramework/tests/HelperConstraints.cs +++ b/src/NUnitFramework/tests/HelperConstraints.cs @@ -52,9 +52,7 @@ protected override object GetTestObject(ActualValueDelegate de public override ConstraintResult ApplyTo(TActual actual) { - var @delegate = actual as Delegate; - if (@delegate == null) - throw new ArgumentException("Actual value must be a delegate.", nameof(actual)); + var @delegate = ConstraintUtils.RequireActual(actual, nameof(actual)); var invokeMethod = @delegate.GetType().GetTypeInfo().GetMethod("Invoke"); if (invokeMethod.GetParameters().Length != 0) diff --git a/src/NUnitFramework/tests/Internal/AsyncTestMethodTests.cs b/src/NUnitFramework/tests/Internal/AsyncTestMethodTests.cs index 4578ff8ed9..83624c501a 100644 --- a/src/NUnitFramework/tests/Internal/AsyncTestMethodTests.cs +++ b/src/NUnitFramework/tests/Internal/AsyncTestMethodTests.cs @@ -34,11 +34,7 @@ namespace NUnit.Framework.Internal [TestFixture] public class AsyncTestMethodTests { -#if !PLATFORM_DETECTION - private static readonly bool PLATFORM_IGNORE = true; -#else private static readonly bool PLATFORM_IGNORE = OSPlatform.CurrentPlatform.IsUnix; -#endif private DefaultTestCaseBuilder _builder; private object _testObject; diff --git a/src/NUnitFramework/tests/Internal/DefaultSuiteBuilderTests.cs b/src/NUnitFramework/tests/Internal/DefaultSuiteBuilderTests.cs index 2e02ae47a4..ff8e38390a 100644 --- a/src/NUnitFramework/tests/Internal/DefaultSuiteBuilderTests.cs +++ b/src/NUnitFramework/tests/Internal/DefaultSuiteBuilderTests.cs @@ -28,7 +28,7 @@ namespace NUnit.Framework.Internal { using Builders; - // NOTE: Because this fixture tests the IImplyFIxture interface, the attribute must be + // NOTE: Because this fixture tests the IImplyFixture interface, the attribute must be // present. Otherwise, if implied fixture recognition did not work, the tests would not run. [TestFixture] public static class DefaultSuiteBuilderTests diff --git a/src/NUnitFramework/tests/Internal/EventListenerTextWriterTests.cs b/src/NUnitFramework/tests/Internal/EventListenerTextWriterTests.cs index 37b0dd6a47..e7653d582c 100644 --- a/src/NUnitFramework/tests/Internal/EventListenerTextWriterTests.cs +++ b/src/NUnitFramework/tests/Internal/EventListenerTextWriterTests.cs @@ -48,11 +48,7 @@ public void SetUp() // Wrap the current listener, listening to events, and forwarding the original event ListenerResult = new TestListenerIntercepter(TestExecutionContext.CurrentContext.Listener); TestExecutionContext.CurrentContext.Listener = ListenerResult; -#if NETCOREAPP1_1 - ListenerWriter = new EventListenerTextWriter(STREAM_NAME, TextWriter.Null); -#else ListenerWriter = TextWriter.Synchronized(new EventListenerTextWriter(STREAM_NAME, TextWriter.Null)); -#endif } [TearDown] @@ -356,7 +352,7 @@ void ITestListener.TestOutput(TestOutput output) void ITestListener.SendMessage(TestMessage message) { - + } } diff --git a/src/NUnitFramework/tests/Internal/EventQueueTests.cs b/src/NUnitFramework/tests/Internal/EventQueueTests.cs index cc4cbca2f1..b898a56826 100644 --- a/src/NUnitFramework/tests/Internal/EventQueueTests.cs +++ b/src/NUnitFramework/tests/Internal/EventQueueTests.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System; using System.Collections; using System.Diagnostics; @@ -368,5 +367,3 @@ private void Produce() } } } - -#endif diff --git a/src/NUnitFramework/tests/Internal/ExceptionHelperOutputExceptionDataTests.cs b/src/NUnitFramework/tests/Internal/ExceptionHelperOutputExceptionDataTests.cs index 2832196e74..b229c1532c 100644 --- a/src/NUnitFramework/tests/Internal/ExceptionHelperOutputExceptionDataTests.cs +++ b/src/NUnitFramework/tests/Internal/ExceptionHelperOutputExceptionDataTests.cs @@ -60,5 +60,14 @@ public static void SkipsDataSectionOnEmptyData() Assert.That(message, Contains.Substring("blah")); Assert.That(message, !Contains.Substring("Data")); } + + [Test] + public static void NoTrailingNewline() + { + var exception = new Exception("blah") { Data = { ["Foo"] = "Bar" } }; + + var message = ExceptionHelper.BuildMessage(exception); + Assert.That(message, Does.Not.EndWith("\n")); + } } } diff --git a/src/NUnitFramework/tests/Internal/ExceptionHelperTests.cs b/src/NUnitFramework/tests/Internal/ExceptionHelperTests.cs index 74b3969377..e4aced87d3 100644 --- a/src/NUnitFramework/tests/Internal/ExceptionHelperTests.cs +++ b/src/NUnitFramework/tests/Internal/ExceptionHelperTests.cs @@ -21,6 +21,13 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +using System; +using System.Reflection; +using NUnit.Compatibility; +#if TASK_PARALLEL_LIBRARY_API +using System.Threading.Tasks; +#endif + namespace NUnit.Framework.Internal { public static class ExceptionHelperTests @@ -30,5 +37,96 @@ public static void BuildMessageThrowsForNullException() { Assert.That(() => ExceptionHelper.BuildMessage(null), Throws.ArgumentNullException.With.Property("ParamName").EqualTo("exception")); } + + [Test] + public static void RecordExceptionThrowsForNullDelegate() + { + Assert.That( + () => ExceptionHelper.RecordException(null, "someParamName"), + Throws.ArgumentNullException.With.Property("ParamName").EqualTo("someParamName")); + } + + [Test] + public static void RecordExceptionThrowsForDelegateThatRequiresParameters() + { + Assert.That( + () => ExceptionHelper.RecordException(new Action(_ => { }), "someParamName"), + Throws.ArgumentException.With.Property("ParamName").EqualTo("someParamName")); + } + + [Test] + public static void RecordExceptionHandlesDelegatesThatHaveOneFewerParameterThanTheBoundMethod() + { + Assert.That( + ExceptionHelper.RecordException(new TestDelegate(new Foo(null).ThrowingExtensionMethod), "someParamName"), + Is.Null); + + var exceptionToThrow = new Exception(); + + Assert.That( + ExceptionHelper.RecordException(new TestDelegate(new Foo(exceptionToThrow).ThrowingExtensionMethod), "someParamName"), + Is.SameAs(exceptionToThrow)); + } + + [Test] + public static void RecordExceptionThrowsProperExceptionForDelegatesThatHaveOneMoreParameterThanTheBoundMethod() + { + var methodInfo = typeof(Foo).GetMethod(nameof(Foo.DummyInstanceMethod)); + var delegateThatParameterizesTheInstance = (Action)methodInfo.CreateDelegate(typeof(Action)); + + Assert.That( + () => ExceptionHelper.RecordException(delegateThatParameterizesTheInstance, "someParamName"), + Throws.ArgumentException); + } + + private sealed class Foo + { + public Foo(Exception exceptionToThrow) + { + ExceptionToThrow = exceptionToThrow; + } + + public Exception ExceptionToThrow { get; } + + public void DummyInstanceMethod() + { + } + } + + private static void ThrowingExtensionMethod(this Foo foo) + { + if (foo.ExceptionToThrow != null) + throw foo.ExceptionToThrow; + } + +#if TASK_PARALLEL_LIBRARY_API + [Test] + public static void RecordExceptionReturnsExceptionThrownBeforeReturningAwaitableObject() + { + var exceptionToThrow = new Exception(); + + Assert.That( + ExceptionHelper.RecordException(new Func(() => throw exceptionToThrow), "someParamName"), + Is.SameAs(exceptionToThrow)); + } + + [Test] + public static void RecordExceptionReturnsExceptionFromAwaitableObjectResult() + { + var exceptionToThrow = new Exception(); + + Assert.That( + ExceptionHelper.RecordException(new Func(() => TaskFromException(exceptionToThrow)), "someParamName"), + Is.SameAs(exceptionToThrow)); + } + + // Task.FromException was added in .NET Framework 4.6 + private static Task TaskFromException(Exception exception) + { + var source = new TaskCompletionSource(); + source.SetException(exception); + return source.Task; + } +#endif } } diff --git a/src/NUnitFramework/tests/Internal/Execution/ParallelExecutionStrategyTests.cs b/src/NUnitFramework/tests/Internal/Execution/ParallelExecutionStrategyTests.cs index f0b4630802..94cccba2ad 100644 --- a/src/NUnitFramework/tests/Internal/Execution/ParallelExecutionStrategyTests.cs +++ b/src/NUnitFramework/tests/Internal/Execution/ParallelExecutionStrategyTests.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using NUnit.Framework.Interfaces; using NUnit.TestUtilities; @@ -87,15 +86,12 @@ public void TestFixtureStrategy(ParallelScope testScope, ParallelScope contextSc Assert.That(strategy, Is.EqualTo(expectedStrategy)); } - private WorkItem MakeWorkItem(ITest test, ParallelScope testScope, ParallelScope contextScope) + private WorkItem MakeWorkItem(Test test, ParallelScope testScope, ParallelScope contextScope) { test.Properties.Set(PropertyNames.ParallelScope, testScope); _context.ParallelScope = contextScope; - var work = WorkItemBuilder.CreateWorkItem(test, TestFilter.Empty); - work.InitializeContext(_context); - - return work; + return TestBuilder.CreateWorkItem(test, _context); } private void TestMethod() { } @@ -103,4 +99,3 @@ private WorkItem MakeWorkItem(ITest test, ParallelScope testScope, ParallelScope private class MyFixture { } } } -#endif diff --git a/src/NUnitFramework/tests/Internal/Execution/ParallelExecutionTests.cs b/src/NUnitFramework/tests/Internal/Execution/ParallelExecutionTests.cs index 7daf8f0e69..d766928143 100644 --- a/src/NUnitFramework/tests/Internal/Execution/ParallelExecutionTests.cs +++ b/src/NUnitFramework/tests/Internal/Execution/ParallelExecutionTests.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -417,10 +416,7 @@ static IEnumerable GetParallelSuites() That("TestFixture2_Test").RunsOn("NonParallelWorker"))) .SetName("Issue-2464"); -#if APARTMENT_STATE -#if NETCOREAPP2_0 if (new PlatformHelper().IsPlatformSupported(new PlatformAttribute { Include = "Win, Mono" })) -#endif { yield return new TestFixtureData( Suite("fake-assembly.dll").Parallelizable() @@ -431,7 +427,6 @@ static IEnumerable GetParallelSuites() That("STAFixture_Test").RunsOn("ParallelSTAWorker"))) .SetName("Issue-2467"); } -#endif } #endregion @@ -611,4 +606,3 @@ public void Verify(TestEvent e) #endregion } } -#endif diff --git a/src/NUnitFramework/tests/Internal/Execution/ParallelWorkItemDispatcherTests.cs b/src/NUnitFramework/tests/Internal/Execution/ParallelWorkItemDispatcherTests.cs index 50cfe8afb4..26fdfa5ecc 100644 --- a/src/NUnitFramework/tests/Internal/Execution/ParallelWorkItemDispatcherTests.cs +++ b/src/NUnitFramework/tests/Internal/Execution/ParallelWorkItemDispatcherTests.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System.Collections.Generic; using System.Linq; @@ -46,10 +45,8 @@ public void ConstructorCreatesQueues() { "ParallelQueue", "NonParallelQueue", -#if APARTMENT_STATE "ParallelSTAQueue", "NonParallelSTAQueue" -#endif })); } @@ -60,7 +57,6 @@ public void ConstructorCreatesShifts() Assert.That(shifts, Is.Unique); -#if APARTMENT_STATE Assert.That(shifts.Count, Is.EqualTo(3)); // Parallel Shift @@ -81,22 +77,6 @@ public void ConstructorCreatesShifts() Assert.That(shifts[2].Queues.Count, Is.EqualTo(1)); Assert.That(shifts[2].Queues[0].Name, Is.EqualTo("NonParallelSTAQueue")); Assert.That(shifts[2].Workers.Count, Is.EqualTo(1)); -#else - Assert.That(shifts.Count, Is.EqualTo(2)); - - // Parallel Shift - Assert.That(shifts[0].Name, Is.EqualTo("Parallel")); - Assert.That(shifts[0].Queues.Count, Is.EqualTo(1)); - Assert.That(shifts[0].Queues[0].Name, Is.EqualTo("ParallelQueue")); - Assert.That(shifts[0].Workers.Count, Is.EqualTo(LEVEL_OF_PARALLELISM)); - - // NonParallel Shift - Assert.That(shifts[1].Name, Is.EqualTo("NonParallel")); - Assert.That(shifts[1].Queues.Count, Is.EqualTo(1)); - Assert.That(shifts[1].Queues[0].Name, Is.EqualTo("NonParallelQueue")); - Assert.That(shifts[1].Workers.Count, Is.EqualTo(1)); -#endif } } } -#endif diff --git a/src/NUnitFramework/tests/Internal/Filters/DeMorganOnFiltersTest.cs b/src/NUnitFramework/tests/Internal/Filters/DeMorganOnFiltersTest.cs new file mode 100644 index 0000000000..7304067cf0 --- /dev/null +++ b/src/NUnitFramework/tests/Internal/Filters/DeMorganOnFiltersTest.cs @@ -0,0 +1,100 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System.Collections.Generic; +using NUnit.Framework.Interfaces; + +namespace NUnit.Framework.Internal.Filters { + public class DeMorganOnFiltersTest : TestFilterTests + { + private static List filterPairs; + + static DeMorganOnFiltersTest() + { + var filterParts = new TestFilter[] + { + new CategoryFilter("Dummy"), + new MethodNameFilter("Test1"), + new PropertyFilter("Priority", "High"), + new PropertyFilter("Priority", "Low") + }; + + filterPairs = new List(); + foreach (var part1 in filterParts) + foreach (var part2 in filterParts) + { + var and = new AndFilter(part1, new NotFilter(part2)); + var or = new OrFilter(new NotFilter(part1), part2); + filterPairs.Add(new TestFilter[2] {new NotFilter(and), or}); + filterPairs.Add(new TestFilter[2] {and, new NotFilter(or)}); + } + } + + private IEnumerable GetTests() + { + var q = new Queue(); + q.Enqueue(_topLevelSuite); + while (q.Count > 0) + { + var test = q.Dequeue(); + foreach (var child in test.Tests) + q.Enqueue(child); + yield return test; + } + } + + private string CaseErrorMessage(string method, TestFilter andFilter, TestFilter orFilter) + { + return $"Tests on which the {method} methods of the following filters fails to agree:\n{andFilter.ToXml(true).OuterXml}\n{orFilter.ToXml(true).OuterXml}"; + } + + [Test] + [TestCaseSource(nameof(filterPairs))] + public void DeMorganPassTests(TestFilter andFilter, TestFilter orFilter) + { + + var disagreements = new List(); + foreach (var test in GetTests()) + { + if (andFilter.Pass(test) != orFilter.Pass(test)) + disagreements.Add(test.FullName); + } + var message = CaseErrorMessage("Pass", andFilter, orFilter); + Assert.IsEmpty(disagreements, message); + } + + [Test] + [TestCaseSource(nameof(filterPairs))] + public void DeMorganMatchTests(TestFilter andFilter, TestFilter orFilter) + { + var disagreements = new List(); + foreach (var test in GetTests()) + { + if (andFilter.Match(test) != orFilter.Match(test)) + disagreements.Add(test.FullName); + } + var message = CaseErrorMessage("Match", andFilter, orFilter); + Assert.IsEmpty(disagreements, message); + } + } +} diff --git a/src/NUnitFramework/tests/Internal/Filters/MockTestFilter.cs b/src/NUnitFramework/tests/Internal/Filters/MockTestFilter.cs index 35db9db46b..2368b517a5 100644 --- a/src/NUnitFramework/tests/Internal/Filters/MockTestFilter.cs +++ b/src/NUnitFramework/tests/Internal/Filters/MockTestFilter.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2015 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -27,13 +27,13 @@ namespace NUnit.Framework.Internal.Filters { /// - /// Mocks a . Checks that only one specific match-function + /// Mocks a . Checks that only one specific match-function /// ( is called and only for a specific . - /// + /// /// For the specific test and match-function one could set the return value of the function. /// Furthermore one could read out the number of valid calls to the match-function through /// . - /// + /// /// It would be better to use a Mocking-Framework for this. /// public class MockTestFilter : TestFilter @@ -57,7 +57,7 @@ public enum MatchFunction /// /// The result of the match-function - /// for the teste . + /// for the test . /// private readonly bool _matchFunctionResult; @@ -92,7 +92,7 @@ public override bool IsExplicitMatch(ITest test) return AssertAndGetEquality(test, MatchFunction.IsExplicitMatch); } - public override bool Pass(ITest test) + public override bool Pass(ITest test, bool negated) { return AssertAndGetEquality(test, MatchFunction.Pass); } diff --git a/src/NUnitFramework/tests/Internal/Filters/MultipleFilterTests.cs b/src/NUnitFramework/tests/Internal/Filters/MultipleFilterTests.cs index d7bb01edef..815ad70373 100644 --- a/src/NUnitFramework/tests/Internal/Filters/MultipleFilterTests.cs +++ b/src/NUnitFramework/tests/Internal/Filters/MultipleFilterTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,6 +21,9 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** +using System; +using System.Collections; + namespace NUnit.Framework.Internal.Filters { public class MultipleFilterTests : TestFilterTests @@ -106,7 +109,7 @@ public void TestNestedOrNotFilters() } [Test] - public void TestLotsOfNestedOrFilters() + public void TestLotsOfNestedNotFilters() { var filter = new NotFilter( new NotFilter( @@ -125,5 +128,55 @@ public void TestLotsOfNestedOrFilters() Assert.False(filter.Match(_explicitFixture)); Assert.False(filter.IsExplicitMatch(_explicitFixture)); } + + [Test] + public void NotOrNotFilterTests() + { + var filter = new NotFilter( + new OrFilter( + new NotFilter( + new CategoryFilter("Dummy")), + new MethodNameFilter("Test1"))); + + Assert.That(filter.Pass(_topLevelSuite)); + Assert.False(filter.Match(_topLevelSuite)); + + Assert.That(filter.Pass(_fixtureWithMultipleTests)); + Assert.False(filter.Match(_fixtureWithMultipleTests)); + + var test1 = _fixtureWithMultipleTests.Tests[0]; + Assert.False(filter.Pass(test1)); + Assert.False(filter.Match(test1)); + + var test2 = _fixtureWithMultipleTests.Tests[1]; + Assert.That(filter.Pass(test2)); + Assert.False(filter.IsExplicitMatch(test2)); + Assert.That(filter.Match(test2)); + } + + [Test] + public void NotAndNotFilterTests() + { + var filter = new NotFilter( + new AndFilter( + new NotFilter( + new CategoryFilter("Dummy")), + new MethodNameFilter("Test1"))); + + Assert.That(filter.Pass(_topLevelSuite)); + Assert.That(filter.Match(_topLevelSuite)); + + Assert.That(filter.Pass(_fixtureWithMultipleTests)); + Assert.That(filter.Match(_fixtureWithMultipleTests)); + + var test1 = _fixtureWithMultipleTests.Tests[0]; + Assert.False(filter.Pass(test1)); + Assert.False(filter.Match(test1)); + + var test2 = _fixtureWithMultipleTests.Tests[1]; + Assert.That(filter.Pass(test2)); + Assert.False(filter.IsExplicitMatch(test2)); + Assert.That(filter.Match(test2)); + } } } diff --git a/src/NUnitFramework/tests/Internal/GenericMethodHelperTests.cs b/src/NUnitFramework/tests/Internal/GenericMethodHelperTests.cs index 500809aad2..b26c7d1b3c 100644 --- a/src/NUnitFramework/tests/Internal/GenericMethodHelperTests.cs +++ b/src/NUnitFramework/tests/Internal/GenericMethodHelperTests.cs @@ -98,7 +98,13 @@ public class GenericMethodHelperTests TypeArgs()), new TestCaseData("MethodWithNestedTypes", ArgList(new List>(), new Dictionary>() ), - TypeArgs()) + TypeArgs()), + new TestCaseData(nameof(MethodWithOneTypeUsedDirectlyAndAsAnArray), + ArgList(10, new int[40]), + TypeArgs()), + new TestCaseData(nameof(MethodWithOneTypeUsedAsAnArrayAndDirectly), + ArgList(new int[40], 10), + TypeArgs()) }; [TestCaseSource(nameof(TypeArgData))] @@ -106,8 +112,7 @@ public void GetTypeArgumentsForMethodTests(string methodName, object[] args, Typ { MethodInfo method = GetType().GetMethod(methodName, BindingFlags.Instance | BindingFlags.NonPublic); - Type[] typeArguments; - Assert.That(new GenericMethodHelper(method).TryGetTypeArguments(args, out typeArguments) ? typeArguments : null, Is.EqualTo(typeArgs)); + Assert.That(new GenericMethodHelper(method).TryGetTypeArguments(args, out var typeArguments) ? typeArguments : null, Is.EqualTo(typeArgs)); } private static object[] ArgList(params object[] args) { return args; } @@ -137,5 +142,8 @@ public void GetTypeArgumentsForMethodTests(string methodName, object[] args, Typ void MethodTakingDictionary(Dictionary d) { } void MethodWithNestedTypes(List> x, Dictionary> z) { } + + void MethodWithOneTypeUsedDirectlyAndAsAnArray(T value, T[] array) { } + void MethodWithOneTypeUsedAsAnArrayAndDirectly(T[] array, T value) { } } } diff --git a/src/NUnitFramework/tests/Internal/GenericTestFixtureTests.cs b/src/NUnitFramework/tests/Internal/GenericTestFixtureTests.cs index fe85584b8a..cea4f38b88 100644 --- a/src/NUnitFramework/tests/Internal/GenericTestFixtureTests.cs +++ b/src/NUnitFramework/tests/Internal/GenericTestFixtureTests.cs @@ -30,9 +30,7 @@ namespace NUnit.Framework.Internal { [TestFixture(typeof(List))] [TestFixture(TypeArgs=new Type[] {typeof(List)} )] -#if !NETCOREAPP1_1 [TestFixture(TypeArgs=new Type[] {typeof(ArrayList)} )] -#endif public class GenericTestFixture_IList where T : IList, new() { [Test] diff --git a/src/NUnitFramework/tests/Internal/PlatformDetectionTests.cs b/src/NUnitFramework/tests/Internal/PlatformDetectionTests.cs index c07be6be5a..7af41feabb 100644 --- a/src/NUnitFramework/tests/Internal/PlatformDetectionTests.cs +++ b/src/NUnitFramework/tests/Internal/PlatformDetectionTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -20,7 +20,7 @@ // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PLATFORM_DETECTION + using System; using System.Collections; @@ -32,18 +32,18 @@ namespace NUnit.Framework.Internal [TestFixture] public class PlatformDetectionTests { - private static readonly PlatformHelper win95Helper = new PlatformHelper( + private static readonly PlatformHelper win95Helper = new PlatformHelper( new OSPlatform( PlatformID.Win32Windows , new Version( 4, 0 ) ), - new RuntimeFramework( RuntimeType.Net, new Version( 1, 1, 4322, 0 ) ) ); + new RuntimeFramework( RuntimeType.NetFramework, new Version( 1, 1, 4322, 0 ) ) ); - private static readonly PlatformHelper winXPHelper = new PlatformHelper( + private static readonly PlatformHelper winXPHelper = new PlatformHelper( new OSPlatform( PlatformID.Win32NT , new Version( 5,1 ) ), - new RuntimeFramework( RuntimeType.Net, new Version( 1, 1, 4322, 0 ) ) ); + new RuntimeFramework( RuntimeType.NetFramework, new Version( 1, 1, 4322, 0 ) ) ); - private void CheckOSPlatforms( OSPlatform os, + private void CheckOSPlatforms( OSPlatform os, string expectedPlatforms ) { - Assert.That(expectedPlatforms, Is.SubsetOf(PlatformHelper.OSPlatforms).IgnoreCase, + Assert.That(expectedPlatforms, Is.SubsetOf(PlatformHelper.OSPlatforms).IgnoreCase, "Error in test: one or more expected platforms is not a valid OSPlatform."); CheckPlatforms( @@ -52,7 +52,7 @@ public class PlatformDetectionTests PlatformHelper.OSPlatforms ); } - private void CheckRuntimePlatforms( RuntimeFramework runtimeFramework, + private void CheckRuntimePlatforms( RuntimeFramework runtimeFramework, string expectedPlatforms ) { CheckPlatforms( @@ -61,7 +61,7 @@ public class PlatformDetectionTests PlatformHelper.RuntimePlatforms + ",NET-1.0,NET-1.1,NET-2.0,NET-3.0,NET-3.5,NET-4.0,NET-4.5,MONO-1.0,MONO-2.0,MONO-3.0,MONO-3.5,MONO-4.0,MONOTOUCH"); } - private void CheckPlatforms( PlatformHelper helper, + private void CheckPlatforms( PlatformHelper helper, string expectedPlatforms, string checkPlatforms ) { string[] expected = expectedPlatforms.Split( new char[] { ',' } ); @@ -76,7 +76,7 @@ public class PlatformDetectionTests break; bool didPass = helper.IsPlatformSupported( testPlatform ); - + if ( shouldPass && !didPass ) Assert.Fail( "Failed to detect {0}", testPlatform ); else if ( didPass && !shouldPass ) @@ -89,7 +89,7 @@ public class PlatformDetectionTests [Test] public void DetectWin95() { - CheckOSPlatforms( + CheckOSPlatforms( new OSPlatform( PlatformID.Win32Windows, new Version( 4, 0 ) ), "Win95,Win32Windows,Win32,Win" ); } @@ -97,7 +97,7 @@ public void DetectWin95() [Test] public void DetectWin98() { - CheckOSPlatforms( + CheckOSPlatforms( new OSPlatform( PlatformID.Win32Windows, new Version( 4, 10 ) ), "Win98,Win32Windows,Win32,Win" ); } @@ -105,7 +105,7 @@ public void DetectWin98() [Test] public void DetectWinMe() { - CheckOSPlatforms( + CheckOSPlatforms( new OSPlatform( PlatformID.Win32Windows, new Version( 4, 90 ) ), "WinMe,Win32Windows,Win32,Win" ); } @@ -113,7 +113,7 @@ public void DetectWinMe() [Test] public void DetectNT3() { - CheckOSPlatforms( + CheckOSPlatforms( new OSPlatform( PlatformID.Win32NT, new Version( 3, 51 ) ), "NT3,Win32NT,Win32,Win" ); } @@ -121,7 +121,7 @@ public void DetectNT3() [Test] public void DetectNT4() { - CheckOSPlatforms( + CheckOSPlatforms( new OSPlatform( PlatformID.Win32NT, new Version( 4, 0 ) ), "NT4,Win32NT,Win32,Win" ); } @@ -129,7 +129,7 @@ public void DetectNT4() [Test] public void DetectWin2K() { - CheckOSPlatforms( + CheckOSPlatforms( new OSPlatform( PlatformID.Win32NT, new Version( 5, 0 ) ), "Win2K,NT5,Win32NT,Win32,Win" ); } @@ -137,7 +137,7 @@ public void DetectWin2K() [Test] public void DetectWinXP() { - CheckOSPlatforms( + CheckOSPlatforms( new OSPlatform( PlatformID.Win32NT, new Version( 5, 1 ) ), "WinXP,NT5,Win32NT,Win32,Win" ); } @@ -145,7 +145,7 @@ public void DetectWinXP() [Test] public void DetectWinXPProfessionalX64() { - CheckOSPlatforms( + CheckOSPlatforms( new OSPlatform( PlatformID.Win32NT, new Version( 5, 2 ), OSPlatform.ProductType.WorkStation ), "WinXP,NT5,Win32NT,Win32,Win" ); } @@ -245,7 +245,7 @@ public void DetectUnixUnderMicrosoftDotNet() new OSPlatform(OSPlatform.UnixPlatformID_Microsoft, new Version(0,0)), "UNIX,Linux"); } - + [Test] public void DetectUnixUnderMono() { @@ -253,7 +253,7 @@ public void DetectUnixUnderMono() new OSPlatform(OSPlatform.UnixPlatformID_Mono, new Version(0,0)), "UNIX,Linux"); } - + [Test] public void DetectXbox() { @@ -274,7 +274,7 @@ public void DetectMacOSX() public void DetectNet35() { CheckRuntimePlatforms( - new RuntimeFramework(RuntimeType.Net, new Version(3, 5)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(3, 5)), "Net,Net-2.0,Net-3.0,Net-3.5"); } @@ -282,7 +282,7 @@ public void DetectNet35() public void DetectNet40() { CheckRuntimePlatforms( - new RuntimeFramework(RuntimeType.Net, new Version(4, 0, 30319, 0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(4, 0, 30319, 0)), "Net,Net-4.0"); } @@ -290,7 +290,7 @@ public void DetectNet40() public void DetectNet45() { CheckRuntimePlatforms( - new RuntimeFramework(RuntimeType.Net, new Version(4, 5, 0, 0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(4, 5, 0, 0)), "Net,Net-4.0,Net-4.5"); } @@ -325,7 +325,7 @@ public void DetectMono30() new RuntimeFramework(RuntimeType.Mono, new Version(3, 0)), "Mono,Mono-2.0,Mono-3.0"); } - + [Test] public void DetectMono35() { @@ -333,7 +333,7 @@ public void DetectMono35() new RuntimeFramework(RuntimeType.Mono, new Version(3, 5)), "Mono,Mono-2.0,Mono-3.0,Mono-3.5"); } - + [Test] public void DetectMono40() { @@ -357,7 +357,7 @@ public void DetectNetCore() new RuntimeFramework(RuntimeType.NetCore, new Version(0, 0, 0)), "NetCore"); } - + [Test] public void DetectExactVersion() { @@ -414,7 +414,7 @@ public void PlatformAttribute_InvalidPlatform() { PlatformAttribute attr = new PlatformAttribute( "Net-1.0,Net11,Mono" ); Assert.Throws( - () => winXPHelper.IsPlatformSupported(attr), + () => winXPHelper.IsPlatformSupported(attr), "Invalid platform name Net11"); } @@ -452,4 +452,3 @@ public void PlatformAttribute_OperatingSystemBitNess() #endif } } -#endif diff --git a/src/NUnitFramework/tests/Internal/RandomizerBiasTests.cs b/src/NUnitFramework/tests/Internal/RandomizerBiasTests.cs new file mode 100644 index 0000000000..9609ede5d0 --- /dev/null +++ b/src/NUnitFramework/tests/Internal/RandomizerBiasTests.cs @@ -0,0 +1,124 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +namespace NUnit.Framework.Internal +{ + public static class RandomizerBiasTests + { + private const int TrialCount = 100_000; + private const double ConfidenceInterval = 0.00965802342765787; + + /* Random generators wouldn’t be truly random if it was impossible to hit a highly biased sequence. + These tests fail as a false positive whenever an actually-*unbiased* generator provides a sequence at + random that turns out to be biased this time. + What we can do is quantify the *chance* of such a false positive and choose that chance to be sufficiently + small to not bother us with spurious failures, while maintaining a precise enough signal to fail loud + and clear if the generator is biased. + + Assuming around 100 CI runs per month, 6 invocations of the build script per CI run, 5 target frameworks + with these same tests, and 97 current tests in this class, a binomial trial + (https://en.wikipedia.org/wiki/Binomial_proportion_confidence_interval) is run 100×6×5×97 = ‭291,000‬ times + per month. + + Let’s see what happens if we set the chance of a false positive to one in a million, a 99.9999% chance of + success per trial. The chance of all 291,000 trials per month having absolutely no false positive is + 99.9999% ^ ‭291,000‬, or ~74.75%. The chance of there being at least one false positive per month is then + 100% - 74.75%, or 25.25‬%, or one false positive on average every four months. This seems too frequent. + + Rather than expecting at least one false positive every four months, we can drive that chance down by + choosing the per-trial confidence to be 99.9999999%, or a one-in-a-billion chance of false positive. + This brings the per-month chance of all trials having no false positive to 99.97%. One or more false + positives would then only occur on average only one month out of every 286 years. + + One in a billion, or 99.9999999% confidence, seems desirable. To obtain this confidence level, we need a + sufficiently high trial count or a sufficiently high tolerance. I chose a trial count of 100,000 because + that takes only about 10ms on my machine. That results in a confidence interval (see Wikipedia link above) + of ~±0.0097 around 0.5. 0.5 of the trials are expected to succeed, where success means something + like "the third bit is set"—something which we expect to happen half the time if the generator is not + biased. If we check whether the actual tested fraction of the time is within ~±0.0097 of 0.5, we will + have the desired 99.9999999% confidence level per trial. + + I used this tool to calculate the confidence interval for trial count 100,000, success count 50,000, + confidence 99.9999999%, and method Agresti-Coull: + http://epitools.ausvet.com.au/content.php?page=CIProportion&SampleSize=100000&Positive=50000&Conf=0.999999999&method=5&Digits=17 + The result is placed in the ConfidenceInterval constant above. + + I chose Agresti-Coull based a citation in https://stats.stackexchange.com/a/82724, Interval Estimation for + a Binomial Proportion by Brown, Cai and DasGupta in Statistical Science 2001, vol. 16, no. 2, pages 101–133. + > [W]e recommend the Wilson interval or the equal-tailed Jeffreys prior interval for small n and the + > interval suggested in Agresti and Coull for larger n. + */ + + private static void AssertProbability50PercentWithinConfidenceInterval(int successCount) + { + Assert.That( + successCount / (double)TrialCount, + Is.EqualTo(0.5).Within(ConfidenceInterval)); + } + + [Test] + public static void NextDecimalIsNotBiased([Range(0, 95)] int bit) + { + var randomizer = new Randomizer(); + + var bitSetCount = 0; + + var part = bit / 32; + var mask = 1 << (bit % 32); + + for (var i = 0; i < TrialCount; i++) + { + var value = randomizer.NextDecimal(); + var parts = decimal.GetBits(value); + + if ((parts[part] & mask) != 0) + { + bitSetCount++; + } + } + + AssertProbability50PercentWithinConfidenceInterval(bitSetCount); + } + + [Test] + public static void NextDecimalWithMaximumIsNotBiased() + { + var randomizer = new Randomizer(); + + const decimal wholeRange = decimal.MaxValue * (2 / 3m); + const decimal halfRange = wholeRange / 2; + + var countInTopHalf = 0; + + for (var i = 0; i < TrialCount; i++) + { + if (randomizer.NextDecimal(wholeRange) >= halfRange) + { + countInTopHalf++; + } + } + + AssertProbability50PercentWithinConfidenceInterval(countInTopHalf); + } + } +} diff --git a/src/NUnitFramework/tests/Internal/RandomizerTests.cs b/src/NUnitFramework/tests/Internal/RandomizerTests.cs index 8b7b67f185..29d86841f9 100644 --- a/src/NUnitFramework/tests/Internal/RandomizerTests.cs +++ b/src/NUnitFramework/tests/Internal/RandomizerTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -651,14 +651,14 @@ public void RandomDecimalsInRangeAreUnique() #endregion #region Strings - + [Test] [Description("Test that all generated strings are unique")] public void RandomStringsAreUnique() { UniqueValues.Check(() => _randomizer.GetString(), 10, 10); } - + [TestCase(30, "Theɋúickƃròwnf߀хjumpëԁoѵertհëlaȥydoɢ")] [TestCase(200, "aèí߀ù123456")] [TestCase(1000, Randomizer.DefaultStringChars)] @@ -667,7 +667,7 @@ public void RandomStringsAreUnique(int outputLength, string allowedChars) { UniqueValues.Check(() => _randomizer.GetString(outputLength, allowedChars), 10, 10); } - + #endregion #region Enums diff --git a/src/NUnitFramework/tests/Internal/ReflectTests.cs b/src/NUnitFramework/tests/Internal/ReflectTests.cs index 6b729d1b83..437ea2553d 100644 --- a/src/NUnitFramework/tests/Internal/ReflectTests.cs +++ b/src/NUnitFramework/tests/Internal/ReflectTests.cs @@ -41,16 +41,6 @@ private class InheritsFromExternalClass : List { } -#if NETCOREAPP1_1 - [Test] - public static void GetInterfaceMatchesByFullName() - { - Assert.That( - Reflect.GetInterface(typeof(List), typeof(IEnumerable).FullName), - Is.EqualTo(typeof(IEnumerable))); - } -#endif - [Test] public static void GetNonGenericPublicInstanceMethodSearchesBaseClasses() { @@ -241,23 +231,29 @@ public static void DynamicInvokeWithTransparentExceptionsDoesNotUnwrap() [Test] public static void InvokeWithTransparentExceptionsPreservesStackTrace() { - Assert.That( - () => typeof(ReflectTests) - .GetMethod(nameof(MethodThrowingTargetInvocationException), BindingFlags.Static | BindingFlags.NonPublic) - .InvokeWithTransparentExceptions(instance: null), - Throws.Exception - .With.Property(nameof(Exception.StackTrace)) - .Contains(nameof(MethodThrowingTargetInvocationException))); + PlatformInconsistency.MonoMethodInfoInvokeLosesStackTrace.IgnoreOnAffectedPlatform(() => + { + Assert.That( + () => typeof(ReflectTests) + .GetMethod(nameof(MethodThrowingTargetInvocationException), BindingFlags.Static | BindingFlags.NonPublic) + .InvokeWithTransparentExceptions(instance: null), + Throws.Exception + .With.Property(nameof(Exception.StackTrace)) + .Contains(nameof(MethodThrowingTargetInvocationException))); + }); } [Test] public static void DynamicInvokeWithTransparentExceptionsPreservesStackTrace() { - Assert.That( - () => new Func(MethodThrowingTargetInvocationException).DynamicInvokeWithTransparentExceptions(), - Throws.Exception - .With.Property(nameof(Exception.StackTrace)) - .Contains(nameof(MethodThrowingTargetInvocationException))); + PlatformInconsistency.MonoMethodInfoInvokeLosesStackTrace.IgnoreOnAffectedPlatform(() => + { + Assert.That( + () => new Func(MethodThrowingTargetInvocationException).DynamicInvokeWithTransparentExceptions(), + Throws.Exception + .With.Property(nameof(Exception.StackTrace)) + .Contains(nameof(MethodThrowingTargetInvocationException))); + }); } private static int MethodReturning42() => 42; diff --git a/src/NUnitFramework/tests/Internal/Results/TestResultFailureTests.cs b/src/NUnitFramework/tests/Internal/Results/TestResultFailureTests.cs index be1a3a19fc..88428e07ab 100644 --- a/src/NUnitFramework/tests/Internal/Results/TestResultFailureTests.cs +++ b/src/NUnitFramework/tests/Internal/Results/TestResultFailureTests.cs @@ -148,7 +148,7 @@ public void SuiteResultIsFailure() Assert.AreEqual(TestResult.CHILD_ERRORS_MESSAGE, _suiteResult.Message); Assert.That(_suiteResult.ResultState.Site, Is.EqualTo(FailureSite.Child)); Assert.That(_suiteResult.StackTrace, Is.Null); - + Assert.AreEqual(1, _suiteResult.TotalCount); Assert.AreEqual(0, _suiteResult.PassCount); Assert.AreEqual(1, _suiteResult.FailCount); Assert.AreEqual(0, _suiteResult.WarningCount); @@ -174,16 +174,16 @@ public void TestResultXmlNodeIsFailure() [Test] public void TestResultXmlNodeEscapesInvalidXmlCharacters() { - _testResult.SetResult( ResultState.Failure, "Invalid Characters: \u0001\u0008\u000b\u001f\ud800; Valid Characters: \u0009\u000a\u000d\u0020\ufffd\ud800\udc00" ); - TNode testNode = _testResult.ToXml( true ); - TNode failureNode = testNode.SelectSingleNode( "failure" ); + _testResult.SetResult(ResultState.Failure, "Invalid Characters: \u0001\u0008\u000b\u001f\ud800; Valid Characters: \u0009\u000a\u000d\u0020\ufffd\ud800\udc00"); + TNode testNode = _testResult.ToXml(true); + TNode failureNode = testNode.SelectSingleNode("failure"); - Assert.That( failureNode, Is.Not.Null, "No element found" ); + Assert.That(failureNode, Is.Not.Null, "No element found"); - TNode messageNode = failureNode.SelectSingleNode( "message" ); + TNode messageNode = failureNode.SelectSingleNode("message"); - Assert.That( messageNode, Is.Not.Null, "No element found" ); - Assert.That( messageNode.Value, Is.EqualTo( "Invalid Characters: \\u0001\\u0008\\u000b\\u001f\\ud800; Valid Characters: \u0009\u000a\u000d\u0020\ufffd\ud800\udc00" ) ); + Assert.That(messageNode, Is.Not.Null, "No element found"); + Assert.That(messageNode.Value, Is.EqualTo("Invalid Characters: \\u0001\\u0008\\u000b\\u001f\\ud800; Valid Characters: \u0009\u000a\u000d\u0020\ufffd\ud800\udc00")); } [Test] diff --git a/src/NUnitFramework/tests/Internal/Results/TestResultGeneralTests.cs b/src/NUnitFramework/tests/Internal/Results/TestResultGeneralTests.cs index 67a7b20035..74115b8d1d 100644 --- a/src/NUnitFramework/tests/Internal/Results/TestResultGeneralTests.cs +++ b/src/NUnitFramework/tests/Internal/Results/TestResultGeneralTests.cs @@ -160,8 +160,8 @@ public void TestResultXml_Duration() { TNode testNode = _testResult.ToXml(true); - Assert.AreEqual(EXPECTED_START.ToString("u"), testNode.Attributes["start-time"]); - Assert.AreEqual(EXPECTED_END.ToString("u"), testNode.Attributes["end-time"]); + Assert.AreEqual(EXPECTED_START.ToString("o"), testNode.Attributes["start-time"]); + Assert.AreEqual(EXPECTED_END.ToString("o"), testNode.Attributes["end-time"]); Assert.AreEqual(EXPECTED_DURATION.ToString("0.000000", CultureInfo.InvariantCulture), testNode.Attributes["duration"]); } @@ -199,8 +199,8 @@ public void SuiteResultXml_Duration() { TNode suiteNode = _suiteResult.ToXml(true); - Assert.AreEqual(EXPECTED_START.ToString("u"), suiteNode.Attributes["start-time"]); - Assert.AreEqual(EXPECTED_END.ToString("u"), suiteNode.Attributes["end-time"]); + Assert.AreEqual(EXPECTED_START.ToString("o"), suiteNode.Attributes["start-time"]); + Assert.AreEqual(EXPECTED_END.ToString("o"), suiteNode.Attributes["end-time"]); Assert.AreEqual(EXPECTED_DURATION.ToString("0.000000", CultureInfo.InvariantCulture), suiteNode.Attributes["duration"]); } } diff --git a/src/NUnitFramework/tests/Internal/Results/TestResultIgnoredTests.cs b/src/NUnitFramework/tests/Internal/Results/TestResultIgnoredTests.cs index c236ef0332..6cdba25a9a 100644 --- a/src/NUnitFramework/tests/Internal/Results/TestResultIgnoredTests.cs +++ b/src/NUnitFramework/tests/Internal/Results/TestResultIgnoredTests.cs @@ -84,7 +84,7 @@ public void SuiteResultIsIgnored() { Assert.AreEqual(ResultState.ChildIgnored, _suiteResult.ResultState); Assert.AreEqual(TestResult.CHILD_IGNORE_MESSAGE, _suiteResult.Message); - + Assert.AreEqual(1, _suiteResult.TotalCount); Assert.AreEqual(0, _suiteResult.PassCount); Assert.AreEqual(0, _suiteResult.FailCount); Assert.AreEqual(0, _suiteResult.WarningCount); diff --git a/src/NUnitFramework/tests/Internal/Results/TestResultInconclusiveTests.cs b/src/NUnitFramework/tests/Internal/Results/TestResultInconclusiveTests.cs index 9bdf64a88b..94050c5692 100644 --- a/src/NUnitFramework/tests/Internal/Results/TestResultInconclusiveTests.cs +++ b/src/NUnitFramework/tests/Internal/Results/TestResultInconclusiveTests.cs @@ -84,7 +84,7 @@ public void SuiteResultIsInconclusive() { Assert.AreEqual(ResultState.Inconclusive, _suiteResult.ResultState); Assert.Null(_suiteResult.Message); - + Assert.AreEqual(1, _suiteResult.TotalCount); Assert.AreEqual(0, _suiteResult.PassCount); Assert.AreEqual(0, _suiteResult.FailCount); Assert.AreEqual(0, _suiteResult.WarningCount); diff --git a/src/NUnitFramework/tests/Internal/Results/TestResultMixedResultTests.cs b/src/NUnitFramework/tests/Internal/Results/TestResultMixedResultTests.cs index aa9e9666b9..0125721624 100644 --- a/src/NUnitFramework/tests/Internal/Results/TestResultMixedResultTests.cs +++ b/src/NUnitFramework/tests/Internal/Results/TestResultMixedResultTests.cs @@ -45,6 +45,10 @@ public void SimulateTestRun() _testResult.SetResult(ResultState.Inconclusive, "inconclusive reason", "stacktrace"); _testResult.AssertCount = 0; _suiteResult.AddResult(_testResult); + + _testResult.SetResult(ResultState.Warning, "message", "warning"); + _testResult.AssertCount = 0; + _suiteResult.AddResult(_testResult); } [Test] @@ -55,10 +59,10 @@ public void SuiteResultIsFailure() Assert.AreEqual(TestResult.CHILD_ERRORS_MESSAGE, _suiteResult.Message); Assert.That(_suiteResult.ResultState.Site, Is.EqualTo(FailureSite.Child)); Assert.Null(_suiteResult.StackTrace, "There should be no stacktrace"); - + Assert.AreEqual(5, _suiteResult.TotalCount); Assert.AreEqual(2, _suiteResult.PassCount); Assert.AreEqual(1, _suiteResult.FailCount); - Assert.AreEqual(0, _suiteResult.WarningCount); + Assert.AreEqual(1, _suiteResult.WarningCount); Assert.AreEqual(0, _suiteResult.SkipCount); Assert.AreEqual(1, _suiteResult.InconclusiveCount); Assert.AreEqual(6, _suiteResult.AssertCount); @@ -79,10 +83,10 @@ public void SuiteResultXmlNodeIsFailure() TNode stacktraceNode = failureNode.SelectSingleNode("stacktrace"); Assert.Null(stacktraceNode, "There should be no stacktrace"); - + Assert.AreEqual("5", suiteNode.Attributes["total"]); Assert.AreEqual("2", suiteNode.Attributes["passed"]); Assert.AreEqual("1", suiteNode.Attributes["failed"]); - Assert.AreEqual("0", suiteNode.Attributes["warnings"]); + Assert.AreEqual("1", suiteNode.Attributes["warnings"]); Assert.AreEqual("0", suiteNode.Attributes["skipped"]); Assert.AreEqual("1", suiteNode.Attributes["inconclusive"]); Assert.AreEqual("6", suiteNode.Attributes["asserts"]); diff --git a/src/NUnitFramework/tests/Internal/Results/TestResultNotRunnableTests.cs b/src/NUnitFramework/tests/Internal/Results/TestResultNotRunnableTests.cs index 4d299df68c..6f601fd3dd 100644 --- a/src/NUnitFramework/tests/Internal/Results/TestResultNotRunnableTests.cs +++ b/src/NUnitFramework/tests/Internal/Results/TestResultNotRunnableTests.cs @@ -47,7 +47,7 @@ public void SuiteResultIsFailure() Assert.AreEqual(ResultState.ChildFailure, _suiteResult.ResultState); Assert.AreEqual(TestResult.CHILD_ERRORS_MESSAGE, _suiteResult.Message); Assert.That(_suiteResult.ResultState.Site, Is.EqualTo(FailureSite.Child)); - + Assert.AreEqual(1, _suiteResult.TotalCount); Assert.AreEqual(0, _suiteResult.PassCount); Assert.AreEqual(1, _suiteResult.FailCount); Assert.AreEqual(0, _suiteResult.WarningCount); diff --git a/src/NUnitFramework/tests/Internal/Results/TestResultSuccessTests.cs b/src/NUnitFramework/tests/Internal/Results/TestResultSuccessTests.cs index 6d635ed556..82be3a58b0 100644 --- a/src/NUnitFramework/tests/Internal/Results/TestResultSuccessTests.cs +++ b/src/NUnitFramework/tests/Internal/Results/TestResultSuccessTests.cs @@ -87,7 +87,7 @@ public void TestResultIsSuccess() public void SuiteResultIsSuccess() { Assert.True(_suiteResult.ResultState == ResultState.Success); - + Assert.AreEqual(1, _suiteResult.TotalCount); Assert.AreEqual(1, _suiteResult.PassCount); Assert.AreEqual(0, _suiteResult.FailCount); Assert.AreEqual(0, _suiteResult.WarningCount); @@ -117,6 +117,7 @@ public void SuiteResultXmlNodeIsSuccess() Assert.AreEqual("Passed", suiteNode.Attributes["result"]); Assert.AreEqual(null, suiteNode.Attributes["label"]); Assert.AreEqual(null, suiteNode.Attributes["site"]); + Assert.AreEqual("1", suiteNode.Attributes["total"]); Assert.AreEqual("1", suiteNode.Attributes["passed"]); Assert.AreEqual("0", suiteNode.Attributes["failed"]); Assert.AreEqual("0", suiteNode.Attributes["warnings"]); diff --git a/src/NUnitFramework/tests/Internal/Results/TestResultWarningTests.cs b/src/NUnitFramework/tests/Internal/Results/TestResultWarningTests.cs index 4664675d5d..5c2e70bbef 100644 --- a/src/NUnitFramework/tests/Internal/Results/TestResultWarningTests.cs +++ b/src/NUnitFramework/tests/Internal/Results/TestResultWarningTests.cs @@ -46,7 +46,7 @@ public void SuiteResultIsWarning() { Assert.AreEqual(ResultState.ChildWarning, _suiteResult.ResultState); Assert.AreEqual(TestResult.CHILD_WARNINGS_MESSAGE, _suiteResult.Message); - + Assert.AreEqual(1, _suiteResult.TotalCount); Assert.AreEqual(0, _suiteResult.PassCount); Assert.AreEqual(0, _suiteResult.FailCount); Assert.AreEqual(1, _suiteResult.WarningCount); diff --git a/src/NUnitFramework/tests/Internal/RuntimeFrameworkTests.cs b/src/NUnitFramework/tests/Internal/RuntimeFrameworkTests.cs index e014a4ac62..895fc13264 100644 --- a/src/NUnitFramework/tests/Internal/RuntimeFrameworkTests.cs +++ b/src/NUnitFramework/tests/Internal/RuntimeFrameworkTests.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !NETCOREAPP1_1 using System; using System.Reflection; @@ -30,13 +29,13 @@ namespace NUnit.Framework.Internal [TestFixture] public class RuntimeFrameworkTests { - static readonly RuntimeType currentRuntime = -#if NETCOREAPP2_0 - RuntimeType.NetCore; +#if NETCOREAPP + static readonly RuntimeType currentRuntime = RuntimeType.NetCore; #else + static readonly RuntimeType currentRuntime = Type.GetType("Mono.Runtime", false) != null ? RuntimeType.Mono - : RuntimeType.Net; + : RuntimeType.NetFramework; #endif [Test] @@ -79,12 +78,6 @@ public void RunsIn40CompatibilityModeWhenCompiled40() } #endif - [Test] - public void CurrentFrameworkHasBuildSpecified() - { - Assert.That(RuntimeFramework.CurrentFramework.ClrVersion.Build, Is.GreaterThan(0)); - } - [Test] [TestCaseSource(nameof(netcoreRuntimes))] public void SpecifyingNetCoreVersioningThrowsPlatformException(string netcoreRuntime) @@ -98,7 +91,7 @@ public void SpecifyingNetCoreWithoutVersioningSucceeds() { PlatformHelper platformHelper = new PlatformHelper(); bool isNetCore; -#if NETCOREAPP2_0 +#if NETCOREAPP isNetCore = true; #else isNetCore = false; @@ -153,76 +146,76 @@ public bool CanMatchRuntimes(RuntimeFramework f1, RuntimeFramework f2) internal static TestCaseData[] matchData = new TestCaseData[] { new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(3,5)), - new RuntimeFramework(RuntimeType.Net, new Version(2,0))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(3,5)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0))) .Returns(true), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0)), - new RuntimeFramework(RuntimeType.Net, new Version(3,5))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(3,5))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(3,5)), - new RuntimeFramework(RuntimeType.Net, new Version(3,5))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(3,5)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(3,5))) .Returns(true), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0)), - new RuntimeFramework(RuntimeType.Net, new Version(2,0))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0))) .Returns(true), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0)), - new RuntimeFramework(RuntimeType.Net, new Version(2,0,50727))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0,50727))) .Returns(true), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0,50727)), - new RuntimeFramework(RuntimeType.Net, new Version(2,0))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0,50727)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0))) .Returns(true), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0,50727)), - new RuntimeFramework(RuntimeType.Net, new Version(2,0))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0,50727)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0))) .Returns(true), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0)), new RuntimeFramework(RuntimeType.Mono, new Version(2,0))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0)), - new RuntimeFramework(RuntimeType.Net, new Version(1,1))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(1,1))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0,50727)), - new RuntimeFramework(RuntimeType.Net, new Version(2,0,40607))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0,50727)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0,40607))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(3,5)), - new RuntimeFramework(RuntimeType.Net, new Version(4,0))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(3,5)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,0))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(4,0)), - new RuntimeFramework(RuntimeType.Net, new Version(4,0))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,0))) .Returns(true), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(3,5)), - new RuntimeFramework(RuntimeType.Net, new Version(4,5))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(3,5)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,5))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(4,0)), - new RuntimeFramework(RuntimeType.Net, new Version(4,5))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,5))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0)), - new RuntimeFramework(RuntimeType.Net, new Version(4,5))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,5))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(4,5)), - new RuntimeFramework(RuntimeType.Net, new Version(2,0))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,5)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(4,5)), - new RuntimeFramework(RuntimeType.Net, new Version(3,5))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,5)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(3,5))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(4,5)), - new RuntimeFramework(RuntimeType.Net, new Version(4,0))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,5)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,0))) .Returns(true), new TestCaseData( new RuntimeFramework(RuntimeType.NetCore, new Version(1,0)), @@ -268,6 +261,22 @@ public bool CanMatchRuntimes(RuntimeFramework f1, RuntimeFramework f2) new RuntimeFramework(RuntimeType.NetCore, new Version(2,2)), new RuntimeFramework(RuntimeType.NetCore, new Version(2,1))) .Returns(true), + new TestCaseData( + new RuntimeFramework(RuntimeType.NetCore, new Version(5,0)), + new RuntimeFramework(RuntimeType.NetCore, new Version(3,1))) + .Returns(true), + new TestCaseData( + new RuntimeFramework(RuntimeType.NetCore, new Version(3,1)), + new RuntimeFramework(RuntimeType.NetCore, new Version(5,0))) + .Returns(false), + new TestCaseData( + new RuntimeFramework(RuntimeType.NetCore, new Version(5,0)), + new RuntimeFramework(RuntimeType.NetCore, new Version(3,1))) + .Returns(true), + new TestCaseData( + new RuntimeFramework(RuntimeType.NetCore, new Version(5,0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(4,8))) + .Returns(false), new TestCaseData( new RuntimeFramework(RuntimeType.Mono, new Version(1,1)), // non-existent version but it works new RuntimeFramework(RuntimeType.Mono, new Version(1,0))) @@ -289,19 +298,19 @@ public bool CanMatchRuntimes(RuntimeFramework f1, RuntimeFramework f2) new RuntimeFramework(RuntimeType.Any, new Version(4,0))) .Returns(false), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, RuntimeFramework.DefaultVersion), - new RuntimeFramework(RuntimeType.Net, new Version(2,0))) + new RuntimeFramework(RuntimeType.NetFramework, RuntimeFramework.DefaultVersion), + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0))) .Returns(true), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0)), - new RuntimeFramework(RuntimeType.Net, RuntimeFramework.DefaultVersion)) + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0)), + new RuntimeFramework(RuntimeType.NetFramework, RuntimeFramework.DefaultVersion)) .Returns(true), new TestCaseData( new RuntimeFramework(RuntimeType.Any, RuntimeFramework.DefaultVersion), - new RuntimeFramework(RuntimeType.Net, new Version(2,0))) + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0))) .Returns(true), new TestCaseData( - new RuntimeFramework(RuntimeType.Net, new Version(2,0)), + new RuntimeFramework(RuntimeType.NetFramework, new Version(2,0)), new RuntimeFramework(RuntimeType.Any, RuntimeFramework.DefaultVersion)) .Returns(true) }; @@ -331,21 +340,22 @@ public override string ToString() } internal static FrameworkData[] frameworkData = new FrameworkData[] { - new FrameworkData(RuntimeType.Net, new Version(1,0), new Version(1,0,3705), "net-1.0", "Net 1.0"), - // new FrameworkData(RuntimeType.Net, new Version(1,0,3705), new Version(1,0,3705), "net-1.0.3705", "Net 1.0.3705"), - // new FrameworkData(RuntimeType.Net, new Version(1,0), new Version(1,0,3705), "net-1.0.3705", "Net 1.0.3705"), - new FrameworkData(RuntimeType.Net, new Version(1,1), new Version(1,1,4322), "net-1.1", "Net 1.1"), - // new FrameworkData(RuntimeType.Net, new Version(1,1,4322), new Version(1,1,4322), "net-1.1.4322", "Net 1.1.4322"), - new FrameworkData(RuntimeType.Net, new Version(2,0), new Version(2,0,50727), "net-2.0", "Net 2.0"), - // new FrameworkData(RuntimeType.Net, new Version(2,0,40607), new Version(2,0,40607), "net-2.0.40607", "Net 2.0.40607"), - // new FrameworkData(RuntimeType.Net, new Version(2,0,50727), new Version(2,0,50727), "net-2.0.50727", "Net 2.0.50727"), - new FrameworkData(RuntimeType.Net, new Version(3,0), new Version(2,0,50727), "net-3.0", "Net 3.0"), - new FrameworkData(RuntimeType.Net, new Version(3,5), new Version(2,0,50727), "net-3.5", "Net 3.5"), - new FrameworkData(RuntimeType.Net, new Version(4,0), new Version(4,0,30319), "net-4.0", "Net 4.0"), - new FrameworkData(RuntimeType.Net, new Version(4,5), new Version(4,0,30319), "net-4.5", "Net 4.5"), - new FrameworkData(RuntimeType.Net, RuntimeFramework.DefaultVersion, RuntimeFramework.DefaultVersion, "net", "Net"), + new FrameworkData(RuntimeType.NetFramework, new Version(1,0), new Version(1,0,3705), "net-1.0", "Net 1.0"), + // new FrameworkData(RuntimeType.NetFramework, new Version(1,0,3705), new Version(1,0,3705), "net-1.0.3705", "Net 1.0.3705"), + // new FrameworkData(RuntimeType.NetFramework, new Version(1,0), new Version(1,0,3705), "net-1.0.3705", "Net 1.0.3705"), + new FrameworkData(RuntimeType.NetFramework, new Version(1,1), new Version(1,1,4322), "net-1.1", "Net 1.1"), + // new FrameworkData(RuntimeType.NetFramework, new Version(1,1,4322), new Version(1,1,4322), "net-1.1.4322", "Net 1.1.4322"), + new FrameworkData(RuntimeType.NetFramework, new Version(2,0), new Version(2,0,50727), "net-2.0", "Net 2.0"), + // new FrameworkData(RuntimeType.NetFramework, new Version(2,0,40607), new Version(2,0,40607), "net-2.0.40607", "Net 2.0.40607"), + // new FrameworkData(RuntimeType.NetFramework, new Version(2,0,50727), new Version(2,0,50727), "net-2.0.50727", "Net 2.0.50727"), + new FrameworkData(RuntimeType.NetFramework, new Version(3,0), new Version(2,0,50727), "net-3.0", "Net 3.0"), + new FrameworkData(RuntimeType.NetFramework, new Version(3,5), new Version(2,0,50727), "net-3.5", "Net 3.5"), + new FrameworkData(RuntimeType.NetFramework, new Version(4,0), new Version(4,0,30319), "net-4.0", "Net 4.0"), + new FrameworkData(RuntimeType.NetFramework, new Version(4,5), new Version(4,0,30319), "net-4.5", "Net 4.5"), + new FrameworkData(RuntimeType.NetFramework, RuntimeFramework.DefaultVersion, RuntimeFramework.DefaultVersion, "net", "Net"), new FrameworkData(RuntimeType.NetCore, new Version(2, 0), new Version(4,0,30319), "netcore-2.0", "NetCore 2.0"), new FrameworkData(RuntimeType.NetCore, new Version(2, 1), new Version(4,0,30319), "netcore-2.1", "NetCore 2.1"), + new FrameworkData(RuntimeType.NetCore, new Version(5, 0), new Version(4,0,30319), "net-5.0", "Net 5.0"), new FrameworkData(RuntimeType.NetCore, RuntimeFramework.DefaultVersion, RuntimeFramework.DefaultVersion, "netcore", "NetCore"), new FrameworkData(RuntimeType.Mono, new Version(1,0), new Version(1,1,4322), "mono-1.0", "Mono 1.0"), new FrameworkData(RuntimeType.Mono, new Version(2,0), new Version(2,0,50727), "mono-2.0", "Mono 2.0"), @@ -366,8 +376,9 @@ public override string ToString() "netcore-1.1", "netcore-2.0", "netcore-2.1", - "netcore-2.2" + "netcore-2.2", + "netcore-3.0", + "netcore-3.1", }; } } -#endif diff --git a/src/NUnitFramework/tests/Internal/SetUpFixtureTests.cs b/src/NUnitFramework/tests/Internal/SetUpFixtureTests.cs index 37679dbdb3..d663f0d5f9 100644 --- a/src/NUnitFramework/tests/Internal/SetUpFixtureTests.cs +++ b/src/NUnitFramework/tests/Internal/SetUpFixtureTests.cs @@ -87,7 +87,7 @@ public void NamespaceSetUpFixtureReplacesNamespaceNodeInTree() Assert.AreEqual(ASSEMBLY_PATH, suite.FullName); Assert.AreEqual(1, suite.Tests.Count, "Error in top level test count"); - string[] nameSpaceBits = nameSpace.Split('.'); + string[] nameSpaceBits = ("[default namespace]." + nameSpace).Split('.'); for (int i = 0; i < nameSpaceBits.Length; i++) { suite = suite.Tests[0] as TestSuite; @@ -137,7 +137,7 @@ public void InvalidAssemblySetUpFixtureIsLoadedCorrectly() Assert.AreEqual(1, suite.Tests.Count, "Error in top level test count"); Assert.AreEqual(RunState.Runnable, suite.RunState); - string[] nameSpaceBits = nameSpace.Split('.'); + string[] nameSpaceBits = ("[default namespace]." + nameSpace).Split('.'); for (int i = 0; i < nameSpaceBits.Length; i++) { suite = suite.Tests[0] as TestSuite; @@ -160,13 +160,32 @@ public void InvalidAssemblySetUpFixtureIsLoadedCorrectly() public void NamespaceSetUpFixtureWrapsExecutionOfSingleTest() { Assert.That(RunTests("NUnit.TestData.SetupFixture.Namespace1").ResultState.Status, Is.EqualTo(TestStatus.Passed)); - TestUtilities.SimpleEventRecorder.Verify("NS1.OneTimeSetup", + TestUtilities.SimpleEventRecorder.Verify("Assembly.OneTimeSetUp", + "NS1.OneTimeSetup", "NS1.Fixture.SetUp", "NS1.Test.SetUp", "NS1.Test", "NS1.Test.TearDown", "NS1.Fixture.TearDown", - "NS1.OneTimeTearDown"); + "NS1.OneTimeTearDown", + "Assembly.OneTimeTearDown"); + } + + [NUnit.Framework.Test] + public void MethodNameSetUpFixtureWrapsExecutionOfSingleTest() + { + Assert.That(RunTests("NUnit.TestData.SetupFixture.Namespace3.SubNamespace.SomeFixture.Test").ResultState.Status, Is.EqualTo(TestStatus.Passed)); + TestUtilities.SimpleEventRecorder.Verify("Assembly.OneTimeSetUp", + "NS3.OneTimeSetUp", + "NS3.SubNamespace.OneTimeSetUp", + "NS3.SubNamespace.Fixture.SetUp", + "NS3.SubNamespace.Test.SetUp", + "NS3.SubNamespace.Test", + "NS3.SubNamespace.Test.TearDown", + "NS3.SubNamespace.Fixture.TearDown", + "NS3.SubNamespace.OneTimeTearDown", + "NS3.OneTimeTearDown", + "Assembly.OneTimeTearDown"); } #endregion Simple @@ -175,26 +194,30 @@ public void NamespaceSetUpFixtureWrapsExecutionOfSingleTest() public void NamespaceSetUpMethodsMayBeStatic() { Assert.That(RunTests("NUnit.TestData.SetupFixture.Namespace5").ResultState.Status, Is.EqualTo(TestStatus.Passed)); - TestUtilities.SimpleEventRecorder.Verify("NS5.OneTimeSetUp", + TestUtilities.SimpleEventRecorder.Verify("Assembly.OneTimeSetUp", + "NS5.OneTimeSetUp", "NS5.Fixture.SetUp", "NS5.Test.SetUp", "NS5.Test", "NS5.Test.TearDown", "NS5.Fixture.TearDown", - "NS5.OneTimeTearDown"); + "NS5.OneTimeTearDown", + "Assembly.OneTimeTearDown"); } [Test] public void NamespaceSetUpFixtureMayBeStatic() { Assert.That(RunTests("NUnit.TestData.SetupFixture.StaticFixture").ResultState.Status, Is.EqualTo(TestStatus.Passed)); - TestUtilities.SimpleEventRecorder.Verify("StaticFixture.OneTimeSetUp", + TestUtilities.SimpleEventRecorder.Verify("Assembly.OneTimeSetUp", + "StaticFixture.OneTimeSetUp", "StaticFixture.Fixture.SetUp", "StaticFixture.Test.SetUp", "StaticFixture.Test", "StaticFixture.Test.TearDown", "StaticFixture.Fixture.TearDown", - "StaticFixture.OneTimeTearDown"); + "StaticFixture.OneTimeTearDown", + "Assembly.OneTimeTearDown"); } #endregion @@ -205,7 +228,8 @@ public void NamespaceSetUpFixtureWrapsExecutionOfTwoTests() Assert.That(RunTests("NUnit.TestData.SetupFixture.Namespace2").ResultState.Status, Is.EqualTo(TestStatus.Passed)); // There are two fixtures but we can't be sure of the order of execution so they use the same events - TestUtilities.SimpleEventRecorder.Verify("NS2.OneTimeSetUp", + TestUtilities.SimpleEventRecorder.Verify("Assembly.OneTimeSetUp", + "NS2.OneTimeSetUp", "NS2.Fixture.SetUp", "NS2.Test.SetUp", "NS2.Test", @@ -216,7 +240,8 @@ public void NamespaceSetUpFixtureWrapsExecutionOfTwoTests() "NS2.Test", "NS2.Test.TearDown", "NS2.Fixture.TearDown", - "NS2.OneTimeTearDown"); + "NS2.OneTimeTearDown", + "Assembly.OneTimeTearDown"); } #endregion TwoTestFixtures @@ -225,7 +250,8 @@ public void NamespaceSetUpFixtureWrapsExecutionOfTwoTests() public void NamespaceSetUpFixtureWrapsNestedNamespaceSetUpFixture() { Assert.That(RunTests("NUnit.TestData.SetupFixture.Namespace3").ResultState.Status, Is.EqualTo(TestStatus.Passed)); - TestUtilities.SimpleEventRecorder.Verify("NS3.OneTimeSetUp", + TestUtilities.SimpleEventRecorder.Verify("Assembly.OneTimeSetUp", + "NS3.OneTimeSetUp", "NS3.Fixture.SetUp", "NS3.Test.SetUp", "NS3.Test", @@ -238,7 +264,8 @@ public void NamespaceSetUpFixtureWrapsNestedNamespaceSetUpFixture() "NS3.SubNamespace.Test.TearDown", "NS3.SubNamespace.Fixture.TearDown", "NS3.SubNamespace.OneTimeTearDown", - "NS3.OneTimeTearDown"); + "NS3.OneTimeTearDown", + "Assembly.OneTimeTearDown"); } #endregion SubNamespace @@ -247,13 +274,15 @@ public void NamespaceSetUpFixtureWrapsNestedNamespaceSetUpFixture() public void WithTwoSetUpFixturesBothAreUsed() { Assert.That(RunTests("NUnit.TestData.SetupFixture.Namespace4").ResultState.Status, Is.EqualTo(TestStatus.Passed)); - TestUtilities.SimpleEventRecorder.ExpectEvents("NS4.OneTimeSetUp1", "NS4.OneTimeSetUp2") + TestUtilities.SimpleEventRecorder.ExpectEvents("Assembly.OneTimeSetUp") + .AndThen("NS4.OneTimeSetUp1", "NS4.OneTimeSetUp2") .AndThen("NS4.Fixture.SetUp") .AndThen("NS4.Test.SetUp") .AndThen("NS4.Test") .AndThen("NS4.Test.TearDown") .AndThen("NS4.Fixture.TearDown") .AndThen("NS4.OneTimeTearDown1", "NS4.OneTimeTearDown2") + .AndThen("Assembly.OneTimeTearDown") .Verify(); } #endregion TwoSetUpFixtures diff --git a/src/NUnitFramework/tests/Internal/SetUpTearDownTests.cs b/src/NUnitFramework/tests/Internal/SetUpTearDownTests.cs index 58773604ca..d99d661eb3 100644 --- a/src/NUnitFramework/tests/Internal/SetUpTearDownTests.cs +++ b/src/NUnitFramework/tests/Internal/SetUpTearDownTests.cs @@ -123,11 +123,15 @@ public void HandleExceptionInSetUp() fixture.SetupException = e; ITestResult suiteResult = TestBuilder.RunTestFixture(fixture); Assert.IsTrue(suiteResult.HasChildren, "Fixture test should have child result."); - TestResult result = (TestResult)suiteResult.Children.ToArray()[0]; + ITestResult result = suiteResult.Children.ToArray()[0]; Assert.AreEqual(ResultState.Error, result.ResultState, "Test should be in error state"); string expected = string.Format("{0} : {1}", e.GetType().FullName, e.Message); Assert.AreEqual(expected, result.Message); - Assert.That(result.StackTrace, Does.Contain(fixture.GetType().FullName)); // Sanity check + + PlatformInconsistency.MonoMethodInfoInvokeLosesStackTrace.SkipOnAffectedPlatform(() => + { + Assert.That(result.StackTrace, Does.Contain(fixture.GetType().FullName)); // Sanity check + }); } [Test] @@ -143,7 +147,11 @@ public void HandleExceptionInTearDown() string expected = string.Format("TearDown : {0} : {1}", e.GetType().FullName, e.Message); Assert.AreEqual(expected, result.Message); Assert.That(result.StackTrace, Does.StartWith("--TearDown")); - Assert.That(result.StackTrace, Does.Contain(fixture.GetType().FullName)); // Sanity check + + PlatformInconsistency.MonoMethodInfoInvokeLosesStackTrace.SkipOnAffectedPlatform(() => + { + Assert.That(result.StackTrace, Does.Contain(fixture.GetType().FullName)); // Sanity check + }); } [Test] @@ -162,7 +170,11 @@ public void HandleExceptionInBothSetUpAndTearDown() + string.Format("TearDown : {0} : {1}", e2.GetType().FullName, e2.Message); Assert.AreEqual(expected, result.Message); Assert.That(result.StackTrace, Does.Contain("--TearDown")); - Assert.That(result.StackTrace, Does.Contain(fixture.GetType().FullName)); // Sanity check + + PlatformInconsistency.MonoMethodInfoInvokeLosesStackTrace.SkipOnAffectedPlatform(() => + { + Assert.That(result.StackTrace, Does.Contain(fixture.GetType().FullName)); // Sanity check + }); } public class SetupCallBase diff --git a/src/NUnitFramework/tests/Internal/TestExecutionContextTests.cs b/src/NUnitFramework/tests/Internal/TestExecutionContextTests.cs index 99cea46d17..399bf16837 100644 --- a/src/NUnitFramework/tests/Internal/TestExecutionContextTests.cs +++ b/src/NUnitFramework/tests/Internal/TestExecutionContextTests.cs @@ -50,11 +50,9 @@ public class TestExecutionContextTests string originalDirectory; -#if !NETCOREAPP1_1 CultureInfo originalCulture; CultureInfo originalUICulture; IPrincipal originalPrincipal; -#endif readonly DateTime _fixtureCreateTime = DateTime.UtcNow; readonly long _fixtureCreateTicks = Stopwatch.GetTimestamp(); @@ -87,11 +85,9 @@ public void Initialize() originalDirectory = Directory.GetCurrentDirectory(); -#if !NETCOREAPP1_1 originalCulture = CultureInfo.CurrentCulture; originalUICulture = CultureInfo.CurrentUICulture; originalPrincipal = Thread.CurrentPrincipal; -#endif } [TearDown] @@ -99,11 +95,9 @@ public void Cleanup() { Directory.SetCurrentDirectory(originalDirectory); -#if !NETCOREAPP1_1 Thread.CurrentThread.CurrentCulture = originalCulture; Thread.CurrentThread.CurrentUICulture = originalUICulture; Thread.CurrentPrincipal = originalPrincipal; -#endif Assert.That( TestExecutionContext.CurrentContext.CurrentTest.FullName, @@ -317,7 +311,6 @@ public async Task AsyncTestCanAccessItsOwnArguments(int i, string s) } #endif -#if PARALLEL [Test] public void TestHasWorkerWhenParallel() { @@ -325,7 +318,6 @@ public void TestHasWorkerWhenParallel() var isRunningUnderTestWorker = TestExecutionContext.CurrentContext.Dispatcher is ParallelWorkItemDispatcher; Assert.That(worker != null || !isRunningUnderTestWorker); } -#endif #endregion @@ -591,7 +583,6 @@ public async Task CanAccessParallelScope_Async() #region TestWorker -#if PARALLEL [Test] public void CanAccessTestWorker() { @@ -613,7 +604,6 @@ public async Task CanAccessTestWorker_Async() Assert.That(TestExecutionContext.CurrentContext.TestWorker, Is.SameAs(worker)); } #endif -#endif #endregion @@ -744,7 +734,6 @@ public async Task CanAccessUpstreamAcxtions_Async() #region CurrentCulture and CurrentUICulture -#if !NETCOREAPP1_1 [Test] public void CanAccessCurrentCulture() { @@ -824,13 +813,11 @@ public void SetAndRestoreCurrentUICulture() Assert.AreEqual(CultureInfo.CurrentUICulture, originalUICulture, "UICulture was not restored"); Assert.AreEqual(_setupContext.CurrentUICulture, originalUICulture, "UICulture not in final context"); } -#endif #endregion #region CurrentPrincipal -#if !NETCOREAPP1_1 [Test] public void CanAccessCurrentPrincipal() { @@ -872,7 +859,6 @@ public void SetAndRestoreCurrentPrincipal() Assert.AreEqual(Thread.CurrentPrincipal, originalPrincipal, "Principal was not restored"); Assert.AreEqual(_setupContext.CurrentPrincipal, originalPrincipal, "Principal not in final context"); } -#endif #endregion diff --git a/src/NUnitFramework/tests/Internal/TestNameGeneratorTests.cs b/src/NUnitFramework/tests/Internal/TestNameGeneratorTests.cs index f3b5902c30..63d0e974fc 100644 --- a/src/NUnitFramework/tests/Internal/TestNameGeneratorTests.cs +++ b/src/NUnitFramework/tests/Internal/TestNameGeneratorTests.cs @@ -29,6 +29,7 @@ namespace NUnit.Framework.Internal public class TestNameGeneratorTests { private TestMethod _simpleTest; + private TestMethod _simpleTestWithArgs; private TestMethod _genericTest; [SetUp] @@ -36,8 +37,10 @@ public void InitializeMethodInfos() { Type thisType = GetType(); var simpleMethod = thisType.GetMethod("TestMethod", BindingFlags.NonPublic | BindingFlags.Instance); + var simpleMethodWithArgs = thisType.GetMethod("TestMethodWithArgs", BindingFlags.NonPublic | BindingFlags.Instance); var genericMethod = thisType.GetMethod("GenericTest", BindingFlags.NonPublic | BindingFlags.Instance); _simpleTest = new TestMethod(new MethodWrapper(thisType, simpleMethod)); + _simpleTestWithArgs = new TestMethod(new MethodWrapper(thisType, simpleMethodWithArgs)); _genericTest = new TestMethod(new MethodWrapper(thisType, genericMethod)); _simpleTest.Id = "THE_ID"; } @@ -86,6 +89,15 @@ public string ParameterizedTests(string pattern, object[] args) return new TestNameGenerator(pattern).GetDisplayName(_simpleTest, args); } + [TestCase("{m}{p}", new object[] { 1 }, ExpectedResult = "TestMethodWithArgs(a: 1)")] + [TestCase("{m}{p}", new object[] { 1, 2 }, ExpectedResult = "TestMethodWithArgs(a: 1, b: 2)")] + [TestCase("{m}{p}", new object[] { 1, 2, 3 }, ExpectedResult = "TestMethodWithArgs(a: 1, b: 2, c: 3)")] + [TestCase("{m}{p}", new object[] { 1, 2, 3, 4 }, ExpectedResult = "TestMethodWithArgs(a: 1, b: 2, c: 3, 4)")] + public string ParameterizedTestsWithArgs(string pattern, object[] args) + { + return new TestNameGenerator(pattern).GetDisplayName(_simpleTestWithArgs, args); + } + [TestCase("FIXED", ExpectedResult="FIXED")] [TestCase("{m}", ExpectedResult="GenericTest")] [TestCase("{n}", ExpectedResult = "NUnit.Framework.Internal")] @@ -148,6 +160,8 @@ public string SpecialNamedValues(object arg) private void TestMethod() { } + private void TestMethodWithArgs(Int32 a, Int32 b, Int32 c = 0) { } + private void GenericTest() { } #endregion diff --git a/src/NUnitFramework/tests/Internal/TestProgressReporterTests.cs b/src/NUnitFramework/tests/Internal/TestProgressReporterTests.cs index 41e005b945..f86712188b 100644 --- a/src/NUnitFramework/tests/Internal/TestProgressReporterTests.cs +++ b/src/NUnitFramework/tests/Internal/TestProgressReporterTests.cs @@ -22,10 +22,12 @@ // *********************************************************************** using System; +using System.Reflection; using System.Collections.Generic; using System.Linq; using NUnit.TestUtilities; using NUnit.TestData.TestFixtureTests; +using NUnit.Compatibility; namespace NUnit.Framework.Internal { @@ -63,6 +65,20 @@ public void TestStarted_AssemblyEmitsSingleStartSuiteElement() Assert.That(_listener.Reports.Count(x => x.StartsWith(" { sb.Append("Busy"); }; work.Executed += (s, ea) => { sb.Append("Exec"); }; - _worker.Idle += (s, ea) => { sb.Append ("Idle"); }; + _worker.Idle += (s, ea) => { sb.Append ("Idle"); }; _queue.Enqueue(work); _worker.Start(); _queue.Start(); Assert.That(() => sb.ToString(), Is.EqualTo("BusyExecIdle").After( - delayInMilliseconds: 10000, pollingInterval: 200)); + delayInMilliseconds: 10_000, pollingInterval: 200)); } private void FakeMethod() @@ -73,5 +68,3 @@ private void FakeMethod() } } } - -#endif diff --git a/src/NUnitFramework/tests/Internal/ThreadUtilityTests.cs b/src/NUnitFramework/tests/Internal/ThreadUtilityTests.cs index 52f2f8f000..ff29af443a 100644 --- a/src/NUnitFramework/tests/Internal/ThreadUtilityTests.cs +++ b/src/NUnitFramework/tests/Internal/ThreadUtilityTests.cs @@ -21,7 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL && PLATFORM_DETECTION && THREAD_ABORT +#if THREAD_ABORT using System.Runtime.InteropServices; using System.Threading; using NUnit.TestUtilities; @@ -62,7 +62,7 @@ public void AbortOrKillThreadWithMessagePump(bool kill) // I measured timer callbacks to be firing around ten times later than the 100 ms requested. // All this number does is manage how long we can tolerate waiting for a build if a bug // is introduced and the thread never aborts. - const int waitTime = 15000; + const int waitTime = 15_000; Assert.That(thread.Join(waitTime), "Native message pump was not able to be interrupted to enable a managed thread abort."); } diff --git a/src/NUnitFramework/tests/Internal/TypeHelperTests.cs b/src/NUnitFramework/tests/Internal/TypeHelperTests.cs index e7ca4d777e..6bf35767b9 100644 --- a/src/NUnitFramework/tests/Internal/TypeHelperTests.cs +++ b/src/NUnitFramework/tests/Internal/TypeHelperTests.cs @@ -37,8 +37,7 @@ public class TypeHelperTests [TestCase(typeof(int[]), typeof(IEnumerable), ExpectedResult=typeof(IEnumerable))] public Type BestCommonTypeTest(Type type1, Type type2) { - Type result; - return TypeHelper.TryGetBestCommonType(type1, type2, out result) ? result : null; + return TypeHelper.TryGetBestCommonType(type1, type2, out var result) ? result : null; } public class A diff --git a/src/NUnitFramework/tests/Internal/TypeNameDifferenceTests.cs b/src/NUnitFramework/tests/Internal/TypeNameDifferenceTests.cs index 63a341bfe9..fc9f86e03f 100644 --- a/src/NUnitFramework/tests/Internal/TypeNameDifferenceTests.cs +++ b/src/NUnitFramework/tests/Internal/TypeNameDifferenceTests.cs @@ -175,10 +175,9 @@ public void TestSetup() private void TestShortenedNameDifference(object objA, object objB, string expectedA, string expectedB) { - string actualA, actualB; _differenceGetter.ResolveTypeNameDifference( - objA, objB, out actualA, out actualB); + objA, objB, out var actualA, out var actualB); Assert.That(actualA, Is.EqualTo(expectedA)); Assert.That(actualB, Is.EqualTo(expectedB)); @@ -428,9 +427,8 @@ public void TestReconstructShortenedGenericTypeName() private void TestShortenTypeNames(object objA, object objB, string shortenedA, string shortenedB) { - string actualA, actualB; - _differenceGetter.ShortenTypeNames(objA.GetType(), objB.GetType(), out actualA, out actualB); + _differenceGetter.ShortenTypeNames(objA.GetType(), objB.GetType(), out var actualA, out var actualB); Assert.AreEqual(shortenedA, actualA); Assert.AreEqual(shortenedB, actualB); @@ -448,9 +446,8 @@ public void TestShortenTypeNamesDifferingNamespace() private void TestShortenGenericTopLevelTypeNames(object objA, object objB, string shortenedA, string shortenedB) { - string actualA, actualB; - _differenceGetter.GetShortenedGenericTypes(objA.GetType(), objB.GetType(), out actualA, out actualB); + _differenceGetter.GetShortenedGenericTypes(objA.GetType(), objB.GetType(), out var actualA, out var actualB); Assert.AreEqual(shortenedA, actualA); Assert.AreEqual(shortenedB, actualB); diff --git a/src/NUnitFramework/tests/Internal/UnexpectedExceptionTests.cs b/src/NUnitFramework/tests/Internal/UnexpectedExceptionTests.cs index 6d4d323e42..a44cb44275 100644 --- a/src/NUnitFramework/tests/Internal/UnexpectedExceptionTests.cs +++ b/src/NUnitFramework/tests/Internal/UnexpectedExceptionTests.cs @@ -1,5 +1,5 @@ // *********************************************************************** -// Copyright (c) 2009 Charlie Poole, Rob Prouse +// Copyright (c) 2009–2019 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining // a copy of this software and associated documentation files (the @@ -31,15 +31,18 @@ namespace NUnit.Framework.Internal [TestFixture] public class UnexpectedExceptionTests { + private static ITestResult RunDataTestCase(string methodName) + { + return TestBuilder.RunTestCase(typeof(UnexpectedExceptionFixture), methodName); + } + [Test] public void FailRecordsInnerException() { string expectedMessage = "System.Exception : Outer Exception" + Environment.NewLine + " ----> System.Exception : Inner Exception"; - ITestResult result = TestBuilder.RunTestCase( - typeof(UnexpectedExceptionFixture), - "ThrowsWithInnerException"); + ITestResult result = RunDataTestCase(nameof(UnexpectedExceptionFixture.ThrowsWithInnerException)); Assert.AreEqual(ResultState.Error, result.ResultState); Assert.AreEqual(expectedMessage, result.Message); @@ -53,9 +56,7 @@ public void FailRecordsNestedInnerException() " ----> System.Exception : Inner Exception" + Environment.NewLine + " ----> System.Exception : Inner Inner Exception"; - ITestResult result = TestBuilder.RunTestCase( - typeof(UnexpectedExceptionFixture), - "ThrowsWithNestedInnerException"); + ITestResult result = RunDataTestCase(nameof(UnexpectedExceptionFixture.ThrowsWithNestedInnerException)); Assert.AreEqual(ResultState.Error, result.ResultState); Assert.AreEqual(expectedMessage, result.Message); @@ -70,9 +71,7 @@ public void FailRecordsInnerExceptionsAsPartOfAggregateException() " ----> System.Exception : Inner Exception 1 of 2" + Environment.NewLine + " ----> System.Exception : Inner Exception 2 of 2"; - ITestResult result = TestBuilder.RunTestCase( - typeof(UnexpectedExceptionFixture), - "ThrowsWithAggregateException"); + ITestResult result = RunDataTestCase(nameof(UnexpectedExceptionFixture.ThrowsWithAggregateException)); Assert.AreEqual(ResultState.Error, result.ResultState); Assert.That(result.Message, Does.StartWith(expectedStartOfMessage)); @@ -87,9 +86,7 @@ public void FailRecordsNestedInnerExceptionAsPartOfAggregateException() " ----> System.Exception : Inner Exception" + Environment.NewLine + " ----> System.Exception : Inner Inner Exception"; - ITestResult result = TestBuilder.RunTestCase( - typeof(UnexpectedExceptionFixture), - "ThrowsWithAggregateExceptionContainingNestedInnerException"); + ITestResult result = RunDataTestCase(nameof(UnexpectedExceptionFixture.ThrowsWithAggregateExceptionContainingNestedInnerException)); Assert.AreEqual(ResultState.Error, result.ResultState); Assert.That(result.Message, Does.StartWith(expectedStartOfMessage)); @@ -100,24 +97,125 @@ public void FailRecordsNestedInnerExceptionAsPartOfAggregateException() [Test] public void BadStackTraceIsHandled() { - ITestResult result = TestBuilder.RunTestCase( - typeof(UnexpectedExceptionFixture), - "ThrowsWithBadStackTrace"); + ITestResult result = RunDataTestCase(nameof(UnexpectedExceptionFixture.ThrowsWithBadStackTrace)); Assert.AreEqual(ResultState.Error, result.ResultState); Assert.AreEqual("NUnit.TestData.UnexpectedExceptionFixture.ExceptionWithBadStackTrace : thrown by me", result.Message); - Assert.AreEqual("No stack trace available", result.StackTrace); + Assert.AreEqual("InvalidOperationException was thrown by the Exception.StackTrace property.", result.StackTrace); } [Test] public void CustomExceptionIsHandled() { - ITestResult result = TestBuilder.RunTestCase( - typeof(UnexpectedExceptionFixture), - "ThrowsCustomException"); + ITestResult result = RunDataTestCase(nameof(UnexpectedExceptionFixture.ThrowsCustomException)); Assert.AreEqual(ResultState.Error, result.ResultState); Assert.AreEqual("NUnit.TestData.UnexpectedExceptionFixture.CustomException : message", result.Message); } + + [Test] + public void AssertThatCanHandleRecursivelyThrowingExceptionAsActual() + { + var result = (ITestResult)null; + + var ex = RecordPossiblyDangerousException(() => + result = RunDataTestCase(nameof(UnexpectedExceptionFixture.AssertThatWithRecursivelyThrowingExceptionAsActual))); + + Assert.That(ex is null); // Careful not to pass ex to Assert.That and crash the test run rather than failing + + Assert.That(result, Has.Property("Message").StartWith( + " Expected: null" + Environment.NewLine + + " But was: ")); + } + + [Test] + public void AssertThatCanHandleRecursivelyThrowingExceptionAsExpected() + { + var result = (ITestResult)null; + + var ex = RecordPossiblyDangerousException(() => + result = RunDataTestCase(nameof(UnexpectedExceptionFixture.AssertThatWithRecursivelyThrowingExceptionAsExpected))); + + Assert.That(ex is null); // Careful not to pass ex to Assert.That and crash the test run rather than failing + + Assert.That(result, Has.Property("Message").StartWith( + " Expected: " + Environment.NewLine + + " But was: null")); + } + + [Test] + public void RecordExceptionCanHandleRecursivelyThrowingException() + { + var result = new TestCaseResult(new TestMethod(new MethodWrapper(typeof(UnexpectedExceptionTests), nameof(DummyMethod)))); + + var ex = RecordPossiblyDangerousException(() => + result.RecordException(new RecursivelyThrowingException())); + + Assert.That(ex is null); // Careful not to pass ex to Assert.That and crash the test run rather than failing + + Assert.That(result, Has.Property("Message").StartWith( + "NUnit.TestData.UnexpectedExceptionFixture.RecursivelyThrowingException : RecursivelyThrowingException was thrown by the Exception.Message property." + Environment.NewLine + + "RecursivelyThrowingException was thrown by the Exception.Data property.")); + + Assert.That(result, Has.Property("StackTrace").EqualTo( + "RecursivelyThrowingException was thrown by the Exception.StackTrace property.")); + } + + [Test] + public void RecordExceptionWithSiteCanHandleRecursivelyThrowingException() + { + var result = new TestCaseResult(new TestMethod(new MethodWrapper(typeof(UnexpectedExceptionTests), nameof(DummyMethod)))); + + var ex = RecordPossiblyDangerousException(() => + result.RecordException(new RecursivelyThrowingException(), FailureSite.Test)); + + Assert.That(ex is null); // Careful not to pass ex to Assert.That and crash the test run rather than failing + + Assert.That(result, Has.Property("Message").StartWith( + "NUnit.TestData.UnexpectedExceptionFixture.RecursivelyThrowingException : RecursivelyThrowingException was thrown by the Exception.Message property." + Environment.NewLine + + "RecursivelyThrowingException was thrown by the Exception.Data property.")); + + Assert.That(result, Has.Property("StackTrace").EqualTo( + "RecursivelyThrowingException was thrown by the Exception.StackTrace property.")); + } + + [Test] + public void RecordTearDownExceptionCanHandleRecursivelyThrowingException() + { + var result = new TestCaseResult(new TestMethod(new MethodWrapper(typeof(UnexpectedExceptionTests), nameof(DummyMethod)))); + + var ex = RecordPossiblyDangerousException(() => + result.RecordTearDownException(new RecursivelyThrowingException())); + + Assert.That(ex is null); // Careful not to pass ex to Assert.That and crash the test run rather than failing + + Assert.That(result, Has.Property("Message").StartWith( + "TearDown : NUnit.TestData.UnexpectedExceptionFixture.RecursivelyThrowingException : RecursivelyThrowingException was thrown by the Exception.Message property." + Environment.NewLine + + "RecursivelyThrowingException was thrown by the Exception.Data property.")); + + Assert.That(result, Has.Property("StackTrace").EqualTo( + "--TearDown" + Environment.NewLine + + "RecursivelyThrowingException was thrown by the Exception.StackTrace property.")); + } + + public void DummyMethod() + { + } + +#if !NET35 + [System.Runtime.ExceptionServices.HandleProcessCorruptedStateExceptions] +#endif + private static Exception RecordPossiblyDangerousException(Action action) + { + try + { + action.Invoke(); + return null; + } + catch (Exception ex) + { + return ex; + } + } } } diff --git a/src/NUnitFramework/tests/Internal/WorkItemQueueTests.cs b/src/NUnitFramework/tests/Internal/WorkItemQueueTests.cs index ee53103aac..fb4ead09c1 100644 --- a/src/NUnitFramework/tests/Internal/WorkItemQueueTests.cs +++ b/src/NUnitFramework/tests/Internal/WorkItemQueueTests.cs @@ -21,8 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System.Threading; +using NUnit.Framework.Internal.Abstractions; using NUnit.TestUtilities; namespace NUnit.Framework.Internal.Execution @@ -34,11 +34,7 @@ public class WorkItemQueueTests [SetUp] public void CreateQueue() { -#if APARTMENT_STATE _queue = new WorkItemQueue("TestQ", true, ApartmentState.MTA); -#else - _queue = new WorkItemQueue("TestQ", true); -#endif } [Test] @@ -167,8 +163,9 @@ public void PriorityIsHonored() public void OneTimeTearDownGetsPriority() { var testFixture = new TestFixture(new TypeWrapper(typeof(MyFixture))); - var fixtureItem = WorkItemBuilder.CreateWorkItem(testFixture, TestFilter.Empty) as CompositeWorkItem; - var tearDown = new CompositeWorkItem.OneTimeTearDownWorkItem(fixtureItem); + + var fixtureItem = WorkItemBuilder.CreateWorkItem(testFixture, TestFilter.Empty, new DebuggerProxy()); + var tearDown = new CompositeWorkItem.OneTimeTearDownWorkItem(fixtureItem as CompositeWorkItem); EnqueueWorkItem("Test1"); _queue.Enqueue(tearDown); EnqueueWorkItem("Test2"); @@ -215,5 +212,3 @@ private class MyFixture } } } - -#endif diff --git a/src/NUnitFramework/tests/Internal/WorkShiftTests.cs b/src/NUnitFramework/tests/Internal/WorkShiftTests.cs index 3050de4c03..991f2b79e0 100644 --- a/src/NUnitFramework/tests/Internal/WorkShiftTests.cs +++ b/src/NUnitFramework/tests/Internal/WorkShiftTests.cs @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if PARALLEL using System; using System.Collections.Generic; using System.Text; @@ -56,11 +55,7 @@ public void StartShift() private static WorkItemQueue CreateQueue(string name) { -#if APARTMENT_STATE return new WorkItemQueue(name, true, ApartmentState.MTA); -#else - return new WorkItemQueue(name, true); -#endif } [Test] @@ -119,4 +114,3 @@ public void HasWorkTest() private void Test1() { } } } -#endif diff --git a/src/NUnitFramework/tests/PlatformInconsistency.cs b/src/NUnitFramework/tests/PlatformInconsistency.cs new file mode 100644 index 0000000000..f9fc48623f --- /dev/null +++ b/src/NUnitFramework/tests/PlatformInconsistency.cs @@ -0,0 +1,105 @@ +// *********************************************************************** +// Copyright (c) 2019 Charlie Poole, Rob Prouse +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// *********************************************************************** + +using System; +using NUnit.Framework.Internal; + +namespace NUnit.Framework +{ + internal sealed class PlatformInconsistency + { + private readonly string _message; + private readonly RuntimeType _runtimeType; + private readonly Version _minVersion; + private readonly Version _maxVersion; + + private PlatformInconsistency(string message, RuntimeType runtimeType, Version minVersion, Version maxVersion) + { + if (string.IsNullOrEmpty(message)) + throw new ArgumentException("Descriptive message must be specified.", nameof(message)); + + _message = message; + _runtimeType = runtimeType; + _minVersion = minVersion; + _maxVersion = maxVersion; + } + + private static Version GetCurrentVersion() + { + if (RuntimeFramework.CurrentFramework.Runtime == RuntimeType.Mono) + { + var displayName = RuntimeFramework.CurrentFramework.DisplayName; + + return new Version(displayName.Substring(0, displayName.IndexOf(' '))); + } + + return RuntimeFramework.CurrentFramework.FrameworkVersion; + } + + private bool CurrentPlatformIsInconsistent + { + get + { + if (RuntimeFramework.CurrentFramework.Runtime != _runtimeType) return false; + + var version = GetCurrentVersion(); + if (_minVersion != null && version < _minVersion) return false; + if (_maxVersion != null && version > _maxVersion) return false; + + return true; + } + } + + /// + /// Does nothing if the current platform is affected; otherwise, executes the specified action. + /// + public void SkipOnAffectedPlatform(Action action) + { + Guard.ArgumentNotNull(action, nameof(action)); + + if (!CurrentPlatformIsInconsistent) action.Invoke(); + } + + /// + /// Reports the current test as ignored if the current platform is affected; otherwise, executes the specified action. + /// + public void IgnoreOnAffectedPlatform(Action action) + { + Guard.ArgumentNotNull(action, nameof(action)); + + if (CurrentPlatformIsInconsistent) + Assert.Ignore(_message); + else + action.Invoke(); + } + + /// + /// Mono's MethodInfo.Invoke loses the stack trace beginning in 5.20. + /// + public static PlatformInconsistency MonoMethodInfoInvokeLosesStackTrace { get; } = new PlatformInconsistency( + "Mono's MethodInfo.Invoke loses the stack trace beginning in 5.20.", + RuntimeType.Mono, + minVersion: new Version(5, 20), + maxVersion: null); + } +} diff --git a/src/NUnitFramework/tests/SynchronizationContextTests.cs b/src/NUnitFramework/tests/SynchronizationContextTests.cs index 2034735f7b..8cb76446c8 100644 --- a/src/NUnitFramework/tests/SynchronizationContextTests.cs +++ b/src/NUnitFramework/tests/SynchronizationContextTests.cs @@ -72,8 +72,7 @@ public static void SynchronizationContextIsRestoredBetweenTestCaseSources() public static IEnumerable ApiAdapters => AsyncExecutionApiAdapter.All; -#if APARTMENT_STATE -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [Apartment(ApartmentState.STA)] @@ -89,7 +88,7 @@ public static void ContinuationStaysOnStaThread(AsyncExecutionApiAdapter apiAdap }); } -#if NETCOREAPP2_0 +#if NETCOREAPP [Platform(Include = "Win, Mono")] #endif [Apartment(ApartmentState.STA)] @@ -104,9 +103,8 @@ public static void AsyncDelegatesAreExecutedOnStaThread(AsyncExecutionApiAdapter return TaskEx.FromResult(null); }); } -#endif -#if NET40 || NET45 +#if UseWindowsFormsAndWPF // TODO: test a custom awaitable type whose awaiter executes continuations on a brand new thread // to ensure that the message pump is shut down on the correct thread. @@ -136,7 +134,7 @@ private static SynchronizationContext CreateSynchronizationContext(Type knownSyn return (SynchronizationContext)Activator.CreateInstance(knownSynchronizationContextType); } - [Test, Timeout(10000)] + [Test, Timeout(10_000)] public static void ContinuationDoesNotDeadlockOnKnownSynchronizationContext( [ValueSource(nameof(KnownSynchronizationContextTypes))] Type knownSynchronizationContextType, [ValueSource(nameof(ApiAdapters))] AsyncExecutionApiAdapter apiAdapter) @@ -160,13 +158,13 @@ private static SynchronizationContext CreateSynchronizationContext(Type knownSyn { apiAdapter.Execute(() => { - Assert.That(SynchronizationContext.Current, Is.SameAs(createdOnThisThread)); + Assert.That(SynchronizationContext.Current, Is.TypeOf(knownSynchronizationContextType)); return TaskEx.FromResult(null); }); } } - [Test, Timeout(10000)] + [Test, Timeout(10_000)] public static void AwaitingContinuationDoesNotAlterSynchronizationContext( [ValueSource(nameof(KnownSynchronizationContextTypes))] Type knownSynchronizationContextType, [ValueSource(nameof(ApiAdapters))] AsyncExecutionApiAdapter apiAdapter) @@ -177,7 +175,7 @@ private static SynchronizationContext CreateSynchronizationContext(Type knownSyn { apiAdapter.Execute(async () => await TaskEx.Yield()); - Assert.That(SynchronizationContext.Current, Is.SameAs(createdOnThisThread)); + Assert.That(SynchronizationContext.Current, Is.TypeOf(knownSynchronizationContextType)); } } #endif diff --git a/src/NUnitFramework/tests/Syntax/InvalidCodeTests.cs b/src/NUnitFramework/tests/Syntax/InvalidCodeTests.cs index f49efe2830..36bc5bcd09 100644 --- a/src/NUnitFramework/tests/Syntax/InvalidCodeTests.cs +++ b/src/NUnitFramework/tests/Syntax/InvalidCodeTests.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,11 +21,8 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !(NETCOREAPP1_1 || NETCOREAPP2_0) -using System; -using System.Collections; +#if !NETCOREAPP using System.CodeDom.Compiler; -using NUnit.Framework.Constraints; namespace NUnit.Framework.Syntax { diff --git a/src/NUnitFramework/tests/Syntax/SerializableConstraints.cs b/src/NUnitFramework/tests/Syntax/SerializableConstraints.cs index b47ccc3bc8..0edb108caa 100644 --- a/src/NUnitFramework/tests/Syntax/SerializableConstraints.cs +++ b/src/NUnitFramework/tests/Syntax/SerializableConstraints.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -21,7 +21,6 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !NETCOREAPP1_1 namespace NUnit.Framework.Syntax { [TestFixture] @@ -48,4 +47,3 @@ public void SetUp() } } } -#endif diff --git a/src/NUnitFramework/tests/Syntax/TestCompiler.cs b/src/NUnitFramework/tests/Syntax/TestCompiler.cs index 915ea7b0dd..7b0164dd59 100644 --- a/src/NUnitFramework/tests/Syntax/TestCompiler.cs +++ b/src/NUnitFramework/tests/Syntax/TestCompiler.cs @@ -21,8 +21,7 @@ // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // *********************************************************************** -#if !(NETCOREAPP1_1 || NETCOREAPP2_0) -using System; +#if !NETCOREAPP using System.CodeDom.Compiler; namespace NUnit.Framework.Syntax diff --git a/src/NUnitFramework/tests/TestContextTests.cs b/src/NUnitFramework/tests/TestContextTests.cs index 3fef617479..5b2146a02b 100644 --- a/src/NUnitFramework/tests/TestContextTests.cs +++ b/src/NUnitFramework/tests/TestContextTests.cs @@ -319,7 +319,11 @@ public void TestContextStoresFailureInfoForTearDown() TestBuilder.RunTestFixture(fixture); Assert.That(fixture.FailCount, Is.EqualTo(1)); Assert.That(fixture.Message, Is.EqualTo("Deliberate failure")); - Assert.That(fixture.StackTrace, Does.Contain("NUnit.TestData.TestContextData.TestTestContextInTearDown.FailingTest")); + + PlatformInconsistency.MonoMethodInfoInvokeLosesStackTrace.SkipOnAffectedPlatform(() => + { + Assert.That(fixture.StackTrace, Does.Contain("NUnit.TestData.TestContextData.TestTestContextInTearDown.FailingTest")); + }); } [Test] @@ -395,9 +399,7 @@ public void FilePathAndDescriptionDoesNotThrow() } [TestCase(null)] -#if PLATFORM_DETECTION [TestCase("bad|path.png", IncludePlatform = "Win")] -#endif public void InvalidFilePathsThrowsArgumentException(string filePath) { Assert.That(() => TestContext.AddTestAttachment(filePath), Throws.InstanceOf()); diff --git a/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectComparer.cs b/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectComparer.cs index ee195d174d..a968592dad 100644 --- a/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectComparer.cs +++ b/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectComparer.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -38,11 +38,7 @@ public class ObjectComparer : IComparer int IComparer.Compare(object x, object y) { WasCalled = true; -#if NETCOREAPP1_1 - return Comparer.Default.Compare(x, y); -#else return Comparer.Default.Compare(x, y); -#endif } } } diff --git a/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectEqualityComparer.cs b/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectEqualityComparer.cs index e6c9e2e2c2..20437a1baf 100644 --- a/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectEqualityComparer.cs +++ b/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectEqualityComparer.cs @@ -8,10 +8,10 @@ // distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to // the following conditions: -// +// // The above copyright notice and this permission notice shall be // included in all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND @@ -37,11 +37,7 @@ public class ObjectEqualityComparer : IEqualityComparer bool IEqualityComparer.Equals(object x, object y) { Called = true; -#if NETCOREAPP1_1 - return Comparer.Default.Compare(x, y) == 0; -#else return Comparer.Default.Compare(x, y) == 0; -#endif } int IEqualityComparer.GetHashCode(object x) diff --git a/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectToStringComparer.cs b/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectToStringComparer.cs index c459bf86e2..6298e85c57 100644 --- a/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectToStringComparer.cs +++ b/src/NUnitFramework/tests/TestUtilities/Comparers/ObjectToStringComparer.cs @@ -1,4 +1,4 @@ -// *********************************************************************** +// *********************************************************************** // Copyright (c) 2017 Charlie Poole, Rob Prouse // // Permission is hereby granted, free of charge, to any person obtaining @@ -23,30 +23,47 @@ using System; using System.Collections; -using System.Collections.Generic; namespace NUnit.TestUtilities.Comparers { /// - /// ObjectToStringComparer is used in testing the when the object does not implement the interface. + /// ObjectToStringComparer is used in testing the when the object does not implement the interface. /// Compares them as numbers when both arguments are , else it uses . /// - public class ObjectToStringComparer : System.Collections.IComparer + public class ObjectToStringComparer : IComparer { - public bool WasCalled = false; - int System.Collections.IComparer.Compare(object x, object y) + public bool WasCalled { get; private set; } + + int IComparer.Compare(object x, object y) { WasCalled = true; - int intX = 0; - int intY = 0; - if(int.TryParse(x.ToString(), out intX) && int.TryParse(y.ToString(), out intY)) + string xAsString = x.ToString(); + string yAsString = y.ToString(); + if (int.TryParse(xAsString, out int intX) && int.TryParse(yAsString, out int intY)) { return intX.CompareTo(intY); } - return x.ToString().CompareTo(y.ToString()); + return xAsString.CompareTo(yAsString); + } + } + + public class ObjectToStringEqualityComparer : IEqualityComparer + { + private readonly IComparer _comparer = new ObjectToStringComparer(); + + public bool WasCalled { get; private set; } + + public new bool Equals(object x, object y) + { + WasCalled = true; + return _comparer.Compare(x, y) == 0; + } + public int GetHashCode(object obj) + { + return obj.ToString().GetHashCode(); } } @@ -56,7 +73,7 @@ int System.Collections.IComparer.Compare(object x, object y) /// public class NoComparer { - public readonly object _value; + private readonly object _value; public NoComparer(object value) { _value = value; @@ -66,4 +83,4 @@ public override string ToString() return _value.ToString(); } } -} \ No newline at end of file +} diff --git a/src/NUnitFramework/tests/TestUtilities/ResultSummary.cs b/src/NUnitFramework/tests/TestUtilities/ResultSummary.cs index cbdb1d8e0a..7f8c4a8088 100644 --- a/src/NUnitFramework/tests/TestUtilities/ResultSummary.cs +++ b/src/NUnitFramework/tests/TestUtilities/ResultSummary.cs @@ -62,7 +62,7 @@ private void Summarize(ITestResult result) if (result.HasChildren) { - foreach (TestResult childResult in result.Children) + foreach (var childResult in result.Children) Summarize(childResult); } else diff --git a/src/NUnitFramework/tests/TestUtilities/TestFinder.cs b/src/NUnitFramework/tests/TestUtilities/TestFinder.cs index 65845fcddc..53e621ba89 100644 --- a/src/NUnitFramework/tests/TestUtilities/TestFinder.cs +++ b/src/NUnitFramework/tests/TestUtilities/TestFinder.cs @@ -57,7 +57,7 @@ public static ITestResult Find(string name, ITestResult result, bool recursive) { if (result.HasChildren) { - foreach (TestResult childResult in result.Children) + foreach (var childResult in result.Children) { if (childResult.Name == name) return childResult; diff --git a/src/NUnitFramework/tests/WorkItemTests.cs b/src/NUnitFramework/tests/WorkItemTests.cs index be8364a062..47141bf693 100644 --- a/src/NUnitFramework/tests/WorkItemTests.cs +++ b/src/NUnitFramework/tests/WorkItemTests.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.Threading; using NUnit.Framework.Interfaces; +using NUnit.TestUtilities; namespace NUnit.Framework.Internal.Execution { @@ -36,11 +37,11 @@ public class WorkItemTests public void CreateWorkItems() { IMethodInfo method = new MethodWrapper(typeof(DummyFixture), "DummyTest"); - ITest test = new TestMethod(method); - _workItem = WorkItemBuilder.CreateWorkItem(test, TestFilter.Empty); + Test test = new TestMethod(method); _context = new TestExecutionContext(); - _workItem.InitializeContext(_context); + + _workItem = TestBuilder.CreateWorkItem(test, _context); } [Test] @@ -98,8 +99,6 @@ public static void DummyTest() } -#if APARTMENT_STATE - [TestCaseSource(nameof(GetTargetApartmentTestData))] public void GetsTargetApartmentFromParentTests(Test test, ApartmentState expected) { @@ -135,7 +134,7 @@ public static IEnumerable GetTargetApartmentTestData() { Parent = new FakeTest("Fixture", fixtureApartment) { - Parent = new FakeTest("Assembly", assemblyApartment) + Parent = new FakeTest("Assembly", assemblyApartment) } }; @@ -172,6 +171,8 @@ public FakeTestResult(ITest test) : base(test) { } + public override int TotalCount => 1; + public override int FailCount => 0; public override int WarningCount => 0; @@ -202,6 +203,5 @@ protected override void PerformWork() throw new System.NotImplementedException(); } } -#endif } } diff --git a/src/NUnitFramework/tests/app.config b/src/NUnitFramework/tests/app.config deleted file mode 100644 index e03ea0a5f0..0000000000 --- a/src/NUnitFramework/tests/app.config +++ /dev/null @@ -1,15 +0,0 @@ - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/NUnitFramework/tests/nunit.framework.tests.csproj b/src/NUnitFramework/tests/nunit.framework.tests.csproj index e9847b45d6..ac6c4d204b 100644 --- a/src/NUnitFramework/tests/nunit.framework.tests.csproj +++ b/src/NUnitFramework/tests/nunit.framework.tests.csproj @@ -1,14 +1,37 @@ - + + + true + $(DefineConstants);UseWindowsFormsAndWPF + true + true + + + + + + net35;net40;net45;net46;netcoreapp2.1;netcoreapp3.1;net5.0-windows + + + + net35;net46;netcoreapp2.1;netcoreapp3.1;net5.0 + - net35;net40;net45;netcoreapp1.1;netcoreapp2.0 NUnit.Framework - Full + + + true true + + + + + @@ -17,8 +40,11 @@ - - + + + + + @@ -28,13 +54,11 @@ - + - - @@ -56,4 +80,6 @@ + + diff --git a/tools/packages.config b/tools/packages.config index 05018882e8..a36acb6e55 100644 --- a/tools/packages.config +++ b/tools/packages.config @@ -1,4 +1,4 @@ - +