Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NUnit 3 support #1064

Merged
merged 1 commit into from
Jan 11, 2016
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/app/FakeLib/DotCover.fs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ open System
open System.IO
open System.Text
open Fake.Testing.XUnit2
open Fake.Testing.NUnit3
open Fake.MSTest

type DotCoverReportType =
Expand Down Expand Up @@ -194,6 +195,36 @@ let DotCoverNUnit (setDotCoverParams: DotCoverParams -> DotCoverParams) (setNUni

traceEndTask "DotCoverNUnit" details

/// Runs the dotCover "cover" command against the NUnit test runner.
/// ## Parameters
///
/// - `setDotCoverParams` - Function used to overwrite the dotCover report default parameters.
/// - `setNUnitParams` - Function used to overwrite the NUnit default parameters.
///
/// ## Sample
///
/// !! (buildDir @@ buildMode @@ "/*.Unit.Tests.dll")
/// |> DotCoverNUnit
/// (fun dotCoverOptions -> { dotCoverOptions with
/// Output = artifactsDir @@ "NUnitDotCoverSnapshot.dcvr" })
/// (fun nUnitOptions -> { nUnitOptions with
/// DisableShadowCopy = true })
let DotCoverNUnit3 (setDotCoverParams: DotCoverParams -> DotCoverParams) (setNUnitParams: NUnit3Params -> NUnit3Params) (assemblies: string seq) =
let assemblies = assemblies |> Seq.toArray
let details = assemblies |> separated ", "
traceStartTask "DotCoverNUnit" details

let parameters = NUnit3Defaults |> setNUnitParams
let args = buildNUnit3Args parameters assemblies

DotCover (fun p ->
{p with
TargetExecutable = parameters.ToolPath
TargetArguments = args
} |> setDotCoverParams)

traceEndTask "DotCoverNUnit" details

/// Runs the dotCover "cover" command against the XUnit2 test runner.
/// ## Parameters
///
Expand Down
1 change: 1 addition & 0 deletions src/app/FakeLib/FakeLib.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@
<Compile Include="UnitTest\NUnit\Common.fs" />
<Compile Include="UnitTest\NUnit\Sequential.fs" />
<Compile Include="UnitTest\NUnit\Parallel.fs" />
<Compile Include="UnitTest\NUnit\NUnit3.fs" />
<Compile Include="UnitTest\XUnit\XUnit.fs" />
<Compile Include="UnitTest\XUnit\XUnitHelper.fs" />
<Compile Include="UnitTest\XUnit\XUnit2.fs" />
Expand Down
4 changes: 2 additions & 2 deletions src/app/FakeLib/UnitTest/NUnit/Common.fs
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,11 @@ type NUnitParams =
/// ## Defaults
/// - `IncludeCategory` - `""`
/// - `ExcludeCategory` - `""`
/// - `ToolPath` - `""`
/// - `ToolPath` - The `nunit-console.exe` path if it exists in a subdirectory of the current directory.
/// - `ToolName` - `"nunit-console.exe"`
/// - `DontTestInNewThread`- `false`
/// - `StopOnError` - `false`
/// - `OutputFile` - The `nunit-console.exe` path if it exists in a subdirectory of the current directory.
/// - `OutputFile` - `"TestResult.xml"`
/// - `Out` - `""`
/// - `ErrorOutputFile` - `""`
/// - `WorkingDir` - `""`
Expand Down
280 changes: 280 additions & 0 deletions src/app/FakeLib/UnitTest/NUnit/NUnit3.fs
Original file line number Diff line number Diff line change
@@ -0,0 +1,280 @@
[<AutoOpen>]
module Fake.Testing.NUnit3

open System
open System.Text
open System.IO
open Fake

/// Process model for NUnit 3 to use.
type NUnit3ProcessModel =
| DefaultProcessModel
| SingleProcessModel
| SeparateProcessModel
| MultipleProcessModel with
member x.ParamString =
match x with
| DefaultProcessModel -> ""
| SingleProcessModel -> "Single"
| SeparateProcessModel -> "Separate"
| MultipleProcessModel -> "Multiple"
/// The --domain option controls of the creation of AppDomains for running tests. See [NUnit-Console Command Line Options](http://www.nunit.org/index.php?p=consoleCommandLine&r=2.6.4)
type NUnit3DomainModel =
/// The default is to use multiple domains if multiple assemblies are listed on the command line. Otherwise a single domain is used.
| DefaultDomainModel
/// No domain is created - the tests are run in the primary domain. This normally requires copying the NUnit assemblies into the same directory as your tests.
| NoDomainModel
/// A test domain is created - this is how NUnit worked prior to version 2.4
| SingleDomainModel
/// A separate test domain is created for each assembly
| MultipleDomainModel with
member x.ParamString =
match x with
| DefaultDomainModel -> ""
| NoDomainModel -> "None"
| SingleDomainModel -> "Single"
| MultipleDomainModel -> "Multiple"

/// The --framework option in running NUnit 3. There are three kinds - VXY, which means either .NET framework or Mono, NetXY (use .NET framework with given version)
/// and MonoXY (Mono framework with given version). You can use Net or Mono to let NUnit select the version.
/// You can pass any value using Other.
type NUnit3Runtime =
/// Uses the runtime under which the assembly was built.
| Default
| V20
| V30
| V35
| V40
| V45
/// NUnit should use .NET framework but can select it's version
| Net
| Net20
| Net30
| Net35
| Net40
| Net45
/// NUnit should use Mono framework but can select it's version
| Mono
| Mono20
| Mono30
| Mono35
| Mono40
/// NUnit should use runtime specified by this value
| Other of string with
member x.ParamString =
match x with
| Default -> ""
| V20 -> "v2.0"
| V30 -> "v3.0"
| V35 -> "v3.5"
| V40 -> "v4.0"
| V45 -> "v4.5"
| Net -> "net"
| Net20 -> "net-2.0"
| Net30 -> "net-3.0"
| Net35 -> "net-3.5"
| Net40 -> "net-4.0"
| Net45 -> "net-4.5"
| Mono20 -> "mono-2.0"
| Mono30 -> "mono-3.0"
| Mono35 -> "mono-3.5"
| Mono40 -> "mono-4.0"
| Other(name) -> name

/// Option which allows to specify if a NUnit error should break the build.
type NUnit3ErrorLevel = TestRunnerErrorLevel

/// The NUnit 3 Console Parameters type. FAKE will use [NUnit3Defaults](fake-testing-nunit3.html) for values not provided.
///
/// For reference, see: [NUnit3 command line options](https://github.com/nunit/nunit/wiki/Console-Command-Line)
type NUnit3Params =
{ /// The path to the NUnit3 console runner: `nunit3-console.exe`
ToolPath : string

/// The name (or path) of a file containing a list of tests to run or explore, one per line.
Testlist : string

/// An expression indicating which tests to run. It may specify test names, classes, methods,
/// catgories or properties comparing them to actual values with the operators ==, !=, =~ and !~.
/// See [NUnit documentation](https://github.com/nunit/nunit/wiki/Test-Selection-Language) for a full description of the syntax.
Where : string

/// Name of a project configuration to load (e.g.: Debug)
Config : string

/// Controls how NUnit loads tests in processes. See [NUnit3ProcessModel](fake-testing-nunit3-nunit3processmodel.html)
ProcessModel : NUnit3ProcessModel

/// Number of agents that may be allowed to run simultaneously assuming you are not running inprocess.
/// If not specified, all agent processes run tests at the same time, whatever the number of assemblies.
/// This setting is used to control running your assemblies in parallel.
Agents : int option

/// Controls how NUnit loads tests in processes. See: [NUnit3ProcessModel](fake-testing-nunit3-nunit3domainmodel.html).
Domain : NUnit3DomainModel

/// Allows you to specify the version of the runtime to be used in executing tests.
/// Default value is runtime under which the assembly was built. See: [NUnit3Runtime](fake-testing-nunit3-nunit3runtime.html).
Framework : NUnit3Runtime

/// Run tests in a 32-bit process on 64-bit systems.
Force32bit : bool

/// Dispose each test runner after it has finished running its tests
DisposeRunners : bool

/// The default timeout to be used for test cases. If any test exceeds the timeout value, it is cancelled and reported as an error.
TimeOut : TimeSpan

/// Set the random seed used to generate test cases
Seed : int

/// Specify the NUMBER of worker threads to be used in running tests.
/// This setting is used to control running your tests in parallel and is used in conjunction with the Parallelizable Attribute.
/// If not specified, workers defaults to the number of processors on the machine, or 2, whichever is greater.
Workers : int option

/// Causes execution of the test run to terminate immediately on the first test failure or error.
StopOnError : bool

/// Path of the directory to use for output files.
WorkingDir : string

/// File path to contain text output from the tests.
OutputDir : string

/// File path to contain error output from the tests.
ErrorDir : string

/// Output specs for saving the test results. Default value is TestResult.xml
/// Passing empty list does not save any result (--noresult option in nunit)
/// For more information, see: [NUnit3 command line options](https://github.com/nunit/nunit/wiki/Console-Command-Line)
ResultSpecs : string list

/// Tells .NET to copy loaded assemblies to the shadowcopy directory.
ShadowCopy : bool

/// Turns on use of TeamCity service messages.
TeamCity : bool

/// Default: [TestRunnerErrorLevel](fake-unittestcommon-testrunnererrorlevel.html).Error
ErrorLevel : NUnit3ErrorLevel
}

/// The [NUnit3Params](fake-testing-nunit3-nunit3params.html) default parameters.
///
/// - `ToolPath` - The `nunit-console.exe` path if it exists in a subdirectory of the current directory.
/// - `Testlist` - `""`
/// - `Where` - `""`
/// - `Config` - `""`
/// - `ProcessModel` - `DefaultProcessModel`
/// - `Agents` - `None`
/// - `Domain` - `DefaultDomainModel`
/// - `Framework` - `""`
/// - `Force32bit` - `false`
/// - `DisposeRunners` - `false`
/// - `Timeout` - `2147483647 milliseconds`
/// - `Seed` - `-1` (negative seed is ignored by NUnit and is not sent to it)
/// - `Workers` - `None`
/// - `StopOnError` - `false`
/// - `WorkingDir` - `""`
/// - `OutputDir` - `""`
/// - `ErrorDir` - `""`
/// - `ResultSpecs` - `"TestResult.xml"`
/// - `ShadowCopy` - `false`
/// - `TeamCity` - `false`
/// - `ErrorLevel` - `Error`
/// ## Defaults
let NUnit3Defaults =
{
ToolPath = findToolInSubPath "nunit3-console.exe" (currentDirectory @@ "tools" @@ "Nunit")
Testlist = ""
Where = ""
Config = ""
ProcessModel = DefaultProcessModel
Agents = None
Domain = DefaultDomainModel
Framework = Default
Force32bit = false
DisposeRunners = false
TimeOut = TimeSpan.FromMilliseconds((float)Int32.MaxValue)
Seed = -1
Workers = None
StopOnError = false
WorkingDir = ""
OutputDir = ""
ErrorDir = ""
ResultSpecs = [currentDirectory @@ "TestResult.xml"]
ShadowCopy = false
TeamCity = false
ErrorLevel = Error
}

/// Tries to detect the working directory as specified in the parameters or via TeamCity settings
/// [omit]
let getWorkingDir parameters =
Seq.find isNotNullOrEmpty [ parameters.WorkingDir
environVar ("teamcity.build.workingDir")
"." ]
|> Path.GetFullPath

let buildNUnit3Args parameters assemblies =
let appendResultString results sb =
match results, sb with
| [], sb -> append "--noresult" sb
| x, sb when x = NUnit3Defaults.ResultSpecs -> sb
| results, sb -> (sb, results) ||> Seq.fold (fun builder str -> append (sprintf "--result=%s" str) builder)

new StringBuilder()
|> append "--noheader"
|> appendIfNotNullOrEmpty parameters.Testlist "--testlist="
|> appendIfNotNullOrEmpty parameters.Where "--where="
|> appendIfNotNullOrEmpty parameters.Config "--config="
|> appendIfNotNullOrEmpty parameters.ProcessModel.ParamString "--process="
|> appendIfSome parameters.Agents (sprintf "--agents=%i")
|> appendIfNotNullOrEmpty parameters.Domain.ParamString "--domain="
|> appendIfNotNullOrEmpty parameters.Framework.ParamString "--framework="
|> appendIfTrue parameters.Force32bit "--x86"
|> appendIfTrue parameters.DisposeRunners "--dispose-runners"
|> appendIfTrue (parameters.TimeOut <> NUnit3Defaults.TimeOut) (sprintf "--timeout=%i" (int parameters.TimeOut.TotalMilliseconds))
|> appendIfTrue (parameters.Seed >= 0) (sprintf "--seed=%i" parameters.Seed)
|> appendIfSome parameters.Workers (sprintf "--workers=%i")
|> appendIfTrue parameters.StopOnError "--stoponerror"
|> appendIfNotNullOrEmpty parameters.WorkingDir "--work="
|> appendIfNotNullOrEmpty parameters.OutputDir "--output="
|> appendIfNotNullOrEmpty parameters.ErrorDir "--err="
|> appendResultString parameters.ResultSpecs
|> appendIfTrue parameters.ShadowCopy "--shadowcopy"
|> appendIfTrue parameters.TeamCity "--teamcity"
|> appendFileNamesIfNotNull assemblies
|> toText

let NUnit3 (setParams : NUnit3Params -> NUnit3Params) (assemblies : string seq) =
let details = assemblies |> separated ", "
traceStartTask "NUnit" details
let parameters = NUnit3Defaults |> setParams
let assemblies = assemblies |> Seq.toArray
if Array.isEmpty assemblies then failwith "NUnit: cannot run tests (the assembly list is empty)."
let tool = parameters.ToolPath
let args = buildNUnit3Args parameters assemblies
trace (tool + " " + args)
let result =
ExecProcess (fun info ->
info.FileName <- tool
info.WorkingDirectory <- getWorkingDir parameters
info.Arguments <- args) parameters.TimeOut
let errorDescription error =
match error with
| OK -> "OK"
| TestsFailed -> sprintf "NUnit test failed (%d)." error
| FatalError x -> sprintf "NUnit test failed. Process finished with exit code %s (%d)." x error

match parameters.ErrorLevel with
| DontFailBuild ->
match result with
| OK | TestsFailed -> traceEndTask "NUnit" details
| _ -> raise (FailedTestsException(errorDescription result))
| Error | FailOnFirstError ->
match result with
| OK -> traceEndTask "NUnit" details
| _ -> raise (FailedTestsException(errorDescription result))