Skip to content

Commit

Permalink
Bug xunit theory (#1936)
Browse files Browse the repository at this point in the history
* Fix regression of AutoDiscoverTestsOnLoad behavior

Conditional was removed unintentionally. Perhaps clobbered in a merge?
61c4d71#r127656246

* Fix xunit theory not finishing in test explorer

* Catch and report exceptions during test run

* Construct full test name based on test framework

Each test framework has different ideas about the data that belongs in each name field.
The quirks are especially bad around parameterized tests.
So, I have to piece together a consistent fully qualified name
from different field combinations for different frameworks
  • Loading branch information
farlee2121 authored Oct 2, 2023
1 parent 0295f85 commit a06a859
Showing 1 changed file with 55 additions and 12 deletions.
67 changes: 55 additions & 12 deletions src/Components/TestExplorer.fs
Original file line number Diff line number Diff line change
Expand Up @@ -221,8 +221,12 @@ type TestResultOutcome =
type TestFrameworkId = string

module TestFrameworkId =
[<Literal>]
let NUnit = "NUnit"

[<Literal>]
let MsTest = "MSTest"

type TestResult =
{ FullTestName: string
Outcome: TestResultOutcome
Expand Down Expand Up @@ -261,6 +265,14 @@ module Path =

module TrxParser =

let adapterTypeNameToTestFramework adapterTypeName =
if String.startWith "executor://nunit" adapterTypeName then
Some TestFrameworkId.NUnit
else if String.startWith "executor://mstest" adapterTypeName then
Some TestFrameworkId.MsTest
else
None

type Execution = { Id: string }

type TestMethod =
Expand All @@ -269,11 +281,18 @@ module TrxParser =
Name: string }

type UnitTest =
{ Execution: Execution
{ Name: string
Execution: Execution
TestMethod: TestMethod }

member self.FullName =
TestName.fromPathAndTestName self.TestMethod.ClassName self.TestMethod.Name
// IMPORTANT: XUnit and MSTest don't include the parameterized test case data in the TestMethod.Name
// but NUnit and MSTest don't use fully qualified names in UnitTest.Name.
// Therefore, we have to conditionally build this full name based on the framework
match self.TestMethod.AdapterTypeName |> adapterTypeNameToTestFramework with
| Some TestFrameworkId.NUnit -> TestName.fromPathAndTestName self.TestMethod.ClassName self.TestMethod.Name
| Some TestFrameworkId.MsTest -> TestName.fromPathAndTestName self.TestMethod.ClassName self.Name
| _ -> self.Name

type ErrorInfo =
{ Message: string option
Expand Down Expand Up @@ -303,12 +322,6 @@ module TrxParser =

trxPath

let adapterTypeNameToTestFramework adapterTypeName =
if String.startWith "executor://nunit" adapterTypeName then
Some TestFrameworkId.NUnit
else
None

let trxSelector (trxPath: string) : XPath.XPathSelector =
let trxContent = node.fs.readFileSync (trxPath, "utf8")
let xmlDoc = mkDoc trxContent
Expand All @@ -318,15 +331,19 @@ module TrxParser =
let extractTestDef (node: XmlNode) : UnitTest =
let executionId = xpathSelector.SelectStringRelative(node, "t:Execution/@id")

// IMPORTANT: t:UnitTest/@name is not the same as t:TestMethod/@className + t:TestMethod/@name
// for theory tests in xUnit and MSTest https://github.com/ionide/ionide-vscode-fsharp/issues/1935
let fullTestName = xpathSelector.SelectStringRelative(node, "@name")
let className = xpathSelector.SelectStringRelative(node, "t:TestMethod/@className")
let testName = xpathSelector.SelectStringRelative(node, "t:TestMethod/@name")
let testMethodName = xpathSelector.SelectStringRelative(node, "t:TestMethod/@name")

let testAdapter =
xpathSelector.SelectStringRelative(node, "t:TestMethod/@adapterTypeName")

{ Execution = { Id = executionId }
{ Name = fullTestName
Execution = { Id = executionId }
TestMethod =
{ Name = testName
{ Name = testMethodName
ClassName = className
AdapterTypeName = testAdapter } }

Expand Down Expand Up @@ -1276,7 +1293,7 @@ module Interactions =
type MergeTestResultsToExplorer =
TestRun -> ProjectPath -> TargetFramework -> TestItem array -> TestResult array -> unit

let runTestProject
let private runTestProject_withoutExceptionHandling
(mergeResultsToExplorer: MergeTestResultsToExplorer)
(makeTrxPath: string -> string)
(testRun: TestRun)
Expand Down Expand Up @@ -1327,6 +1344,31 @@ module Interactions =
mergeResultsToExplorer testRun projectPath projectRunRequest.TargetFramework runnableTests testResults
}

let runTestProject
(mergeResultsToExplorer: MergeTestResultsToExplorer)
(makeTrxPath: string -> string)
(testRun: TestRun)
(cancellationToken: CancellationToken)
(projectRunRequest: ProjectRunRequest)
=
promise {
try
return!
runTestProject_withoutExceptionHandling
mergeResultsToExplorer
makeTrxPath
testRun
cancellationToken
projectRunRequest
with e ->
let message =
$"❌ Error running tests: \n project: {projectRunRequest.ProjectPath} \n\n error:\n {e.Message}"

TestRun.appendOutputLine testRun message
TestRun.showError testRun message projectRunRequest.Tests
}



let private filtersToProjectRunRequests (rootTestCollection: TestItemCollection) (runRequest: TestRunRequest) =
let testSelection =
Expand Down Expand Up @@ -1399,6 +1441,7 @@ module Interactions =
let runTestProject =
runTestProject mergeTestResultsToExplorer makeTrxPath testRun _ct


let buildProject testRun projectRunRequest =
promise {

Expand Down

0 comments on commit a06a859

Please sign in to comment.