anchor.diff:      | x when x < 0 -> FatalError "FatalError"
       | _ -> TestsFailed
   
  -/// Run NUnit on a group of assemblies.
  +/// Runs NUnit on a group of assemblies.
  +/// ## Parameters
  +/// 
  +///  - `setParams` - Function used to manipulate the default NUnitParams value.
  +///  - `assemblies` - Sequence of one or more assemblies containing NUnit unit tests.
  +/// 
  +/// ## Sample usage
  +///
  +///     Target "Test" (fun _ ->
  +///         !! (testDir + @"\Test.*.dll") 
  +///           |> NUnit (fun p -> { p with ErrorLevel = DontFailBuild })
  +///     )
   let NUnit setParams (assemblies: string seq) =
       let details = assemblies |> separated ", "
       traceStartTask "NUnit" details
  
positive.code:     |> Seq.map (fun x -> x <> "Failure")
      |> Seq.reduce (&&)
  
  type NUnitErrorLevel =
      | Error
      | DontFailBuild
  
  type NUnitParams =
      { IncludeCategory:string;
        ExcludeCategory:string;
        ToolPath:string;
        ToolName:string;
        TestInNewThread:bool;
        OutputFile:string;
        Out: string;
        ErrorOutputFile:string;
        Framework:string;
        ShowLabels: bool;
        WorkingDir:string; 
        XsltTransformFile:string;
        TimeOut: TimeSpan;
        DisableShadowCopy:bool
        Domain:string
        ErrorLevel:NUnitErrorLevel}
  
  /// NUnit default params  
  let NUnitDefaults =
      let toolname = "nunit-console.exe"
  
      { IncludeCategory = null;
        ExcludeCategory = null;
        ToolPath = findToolFolderInSubPath toolname (currentDirectory @@ "tools" @@ "Nunit")
        ToolName = toolname;
        TestInNewThread = false;
        OutputFile = currentDirectory @@ "TestResult.xml";
        Out = null;
        ErrorOutputFile = null;
        WorkingDir = null;
        Framework = null;
        ShowLabels = true;
        XsltTransformFile = null;
        TimeOut = TimeSpan.FromMinutes 5.
        DisableShadowCopy = false;
        Domain = null;
        ErrorLevel = Error}
  
  let commandLineBuilder parameters assemblies =
      let cl = new StringBuilder()
                |> append "-nologo"
                |> appendIfTrue parameters.DisableShadowCopy "-noshadow" 
                |> appendIfTrue parameters.ShowLabels "-labels" 
                |> appendIfTrue parameters.TestInNewThread "-thread" 
                |> appendFileNamesIfNotNull assemblies
                |> appendIfNotNull parameters.IncludeCategory "-include:"
                |> appendIfNotNull parameters.ExcludeCategory "-exclude:"
                |> appendIfNotNull parameters.XsltTransformFile "-transform:"
                |> appendIfNotNull parameters.OutputFile  "-xml:"
                |> appendIfNotNull parameters.Out "-out:"
                |> appendIfNotNull parameters.Framework  "-framework:"
                |> appendIfNotNull parameters.ErrorOutputFile "-err:"
                |> appendIfNotNull parameters.Domain "-domain:"
      cl.ToString()
  
  let getWorkingDir parameters =
      Seq.find (fun s -> s <> null && s <> "") [parameters.WorkingDir; environVar("teamcity.build.workingDir"); "."]
      |> Path.GetFullPath
  
  // NUnit console returns negative error codes for errors and sum of failed/ignored/exceptional 
  // tests otherwise. Zero means that all tests passed.
  let (|OK|TestsFailed|FatalError|) = function
      | 0 -> OK
      | -1 -> FatalError "InvalidArg"
      | -2 -> FatalError "FileNotFound"
  
negative.code:     if hasFailed then          
          failwith "NUnitParallel test runs failed."
      else
          traceEndTask "NUnitParallel" details