Skip to content

Commit

Permalink
Refactored to support NUnit v2+v3
Browse files Browse the repository at this point in the history
  • Loading branch information
bryanbcook committed Nov 12, 2023
1 parent 2fcc95f commit b6c7990
Show file tree
Hide file tree
Showing 4 changed files with 387 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/helpers/helper.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ const configured_parser = new XMLParser({
return true;
}
// handle nunit deep hierarchy
else if (jpath.startsWith("test-results")) {
else if (jpath.startsWith("test-results") || jpath.startsWith("test-run")) {
let parts = jpath.split(".");
switch(parts[parts.length - 1]) {
case "category":
Expand Down
143 changes: 106 additions & 37 deletions src/parsers/nunit.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,32 @@ const TestCase = require('../models/TestCase');
const SUITE_TYPES_WITH_TESTCASES = [
"TestFixture",
"ParameterizedTest",
"GenericFixture"
"GenericFixture",
"ParameterizedMethod" // v3
]

const RESULTMAP = {
Success: "PASS",
Failure: "FAIL",
Ignored: "SKIP",
NotRunnable: "SKIP",
Error: "ERROR",
Inconclusive: "FAIL"
Success: "PASS", // v2
Failure: "FAIL", // v2
Ignored: "SKIP", // v2
NotRunnable: "SKIP", // v2
Error: "ERROR", // v2
Inconclusive: "FAIL", // v2

Passed: "PASS", // v3
Failed: "FAIL", // v3
Skipped: "SKIP", // v3
}

function mergeMeta(map1, map2) {
for(let kvp of map1) {
map2.set(kvp[0], kvp[1]);
}
}

function populateMetaData(raw, map) {

// v2 supports categories
if (raw.categories) {
let categories = raw.categories.category;
for (let i = 0; i < categories.length; i++) {
Expand All @@ -34,30 +47,82 @@ function populateMetaData(raw, map) {
}
}
}

// v2/v3 support properties
if (raw.properties) {
let properties = raw.properties.property;
for (let i = 0; i < properties.length; i++) {
let property = properties[i];
map.set(property["@_name"], property["@_value"]);
let propName = property["@_name"];
let propValue = property["@_value"];

// v3 treats 'Categories' as property "Category"
if (propName == "Category") {

if (map.has("Categories")) {
map.set("Categories", map.get("Categories").concat(",", propValue));
} else {
map.set("Categories", propValue);
}
map.set(propValue, "");

} else {
map.set(propName, propValue);
}
}
}
}

function getNestedTestCases(rawSuite) {
if (rawSuite.results) {
return rawSuite.results["test-case"];
} else {
return rawSuite["test-case"];
}
}

function hasNestedSuite(rawSuite) {
return getNestedSuite(rawSuite) !== null;
}

function getNestedSuite(rawSuite) {
// nunit v2 nests test-suite inside 'results'
if (rawSuite.results && rawSuite.results["test-suite"]) {
return rawSuite.results["test-suite"];
} else {
// nunit v3 nests test-suites as immediate children
if (rawSuite["test-suite"]) {
return rawSuite["test-suite"];
}
else {
// not nested
return null;
}
}
}

function getTestCases(rawSuite, parent_meta) {
var cases = [];

let rawTestCases = rawSuite.results["test-case"];
let rawTestCases = getNestedTestCases(rawSuite);
if (rawTestCases) {
for (let i = 0; i < rawTestCases.length; i++) {
let rawCase = rawTestCases[i];
let testCase = new TestCase();
let result = rawCase["@_result"]
testCase.name = rawCase["@_name"];
testCase.id = rawCase["@_id"] ?? "";
testCase.name = rawCase["@_fullname"] ?? rawCase["@_name"];
testCase.duration = rawCase["@_time"] * 1000; // in milliseconds
testCase.status = RESULTMAP[result];

// v2 : non-executed should be tests should be Ignored
if (rawCase["@_executed"] == "False") {
testCase.status = "SKIP"; // exclude failures that weren't executed.
}
// v3 : failed tests with error label should be Error
if (rawCase["@_label"] == "Error") {
testCase.status = "ERROR";
}
let errorDetails = rawCase.reason ?? rawCase.failure;
if (errorDetails !== undefined) {
testCase.setFailure(errorDetails.message);
Expand All @@ -66,9 +131,7 @@ function getTestCases(rawSuite, parent_meta) {
}
}
// copy parent_meta data to test case
for( let kvp of parent_meta.entries()) {
testCase.meta_data.set(kvp[0], kvp[1]);
}
mergeMeta(parent_meta, testCase.meta_data);
populateMetaData(rawCase, testCase.meta_data);

cases.push( testCase );
Expand All @@ -78,22 +141,30 @@ function getTestCases(rawSuite, parent_meta) {
return cases;
}

function getTestSuites(rawSuites) {
function getTestSuites(rawSuites, assembly_meta) {
var suites = [];

for(let i = 0; i < rawSuites.length; i++) {
let rawSuite = rawSuites[i];

if (rawSuite["@_type"] == "Assembly") {
assembly_meta = new Map();
populateMetaData(rawSuite, assembly_meta);
}

if (rawSuite.results["test-suite"]) {
if (hasNestedSuite(rawSuite)) {
// handle nested test-suites
suites.push(...getTestSuites(rawSuite.results["test-suite"]));
suites.push(...getTestSuites(getNestedSuite(rawSuite), assembly_meta));
} else if (SUITE_TYPES_WITH_TESTCASES.indexOf(rawSuite["@_type"]) !== -1) {

let suite = new TestSuite();
suite.id = rawSuite["@_id"] ?? '';
suite.name = rawSuite["@_fullname"] ?? rawSuite["@_name"];
suite.duration = rawSuite["@_time"] * 1000; // in milliseconds
suite.status = RESULTMAP[rawSuite["@_result"]];

var meta_data = new Map();
mergeMeta(assembly_meta, meta_data);
populateMetaData(rawSuite, meta_data);
suite.cases.push(...getTestCases(rawSuite, meta_data));

Expand All @@ -113,29 +184,27 @@ function getTestSuites(rawSuites) {


function getTestResult(json) {
const rawResult = json["test-results"];
const rawSuite = rawResult["test-suite"][0];

const nunitVersion = (json["test-results"] !== undefined) ? "v2" :
(json["test-run"] !== undefined) ? "v3" : null;

if (nunitVersion == null) {
throw new Error("Unrecognized xml format");
}

const result = new TestResult();
result.name = rawResult["@_name"];
const rawResult = json["test-results"] ?? json["test-run"];
const rawSuite = rawResult["test-suite"][0];

result.name = rawResult["@_fullname"] ?? rawResult["@_name"];
result.duration = rawSuite["@_time"] * 1000; // in milliseconds
// test-results attributes related to totals
// total = executed=True
// errors = result="Error"
// failures = result="Failure"
// not-run = executed=False
// inconclusive = result="Inconclusive"
// ignored = result="Ignored"
// skipped = sample has zero?
// invalid = result="NotRunable"
result.total = rawResult["@_total"] + rawResult["@_not-run"]; // total executed and not executed
result.errors = rawResult["@_errors"];
result.failed = rawResult["@_failures"];
result.skipped = rawResult["@_not-run"]; // Ignored, NotRunnable
// assume inconclusive is neither a pass or failure to prevent religious wars, total's won't match as a result.
result.passed = rawResult["@_total"] - (result.errors + result.failed + rawResult["@_inconclusive"]);

result.suites.push(getTestSuites( [ rawSuite ]) );

result.suites.push(...getTestSuites( [ rawSuite ], null));

result.total = result.suites.reduce( (total, suite) => { return total + suite.cases.length}, 0);
result.passed = result.suites.reduce( (total, suite) => { return total + suite.passed}, 0);
result.failed = result.suites.reduce( (total, suite) => { return total + suite.failed}, 0);
result.skipped = result.suites.reduce( (total, suite) => { return total + suite.skipped}, 0);
result.errors = result.suites.reduce( (total, suite) => { return total + suite.errors}, 0);

return result;
}
Expand Down
125 changes: 125 additions & 0 deletions tests/data/nunit/nunit_v3.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<test-run id="2" name="mock-assembly.dll" fullname="D:\Dev\NUnit\nunit-3.0\work\bin\vs2008\Debug\mock-assembly.dll" testcasecount="25" result="Failed" time="0.154" total="18" passed="12" failed="2" inconclusive="1" skipped="3" asserts="2" run-date="2011-07-26" start-time="11:34:27">
<environment nunit-version="1.0.0.0" clr-version="2.0.50727.4961" os-version="Microsoft Windows NT 6.1.7600.0" platform="Win32NT" cwd="D:\Dev\NUnit\nunit-3.0\work\bin\vs2008\Debug" machine-name="CHARLIE-LAPTOP" user="charlie" user-domain="charlie-laptop" culture="en-US" uiculture="en-US" />
<test-suite type="Assembly" id="1036" name="mock-assembly.dll" fullname="D:\Dev\NUnit\nunit-3.0\work\bin\vs2008\Debug\mock-assembly.dll" testcasecount="25" result="Failed" time="0.154" total="18" passed="12" failed="2" inconclusive="1" skipped="3" asserts="2">
<properties>
<property name="_PID" value="11928" />
<property name="_APPDOMAIN" value="test-domain-mock-assembly.dll" />
</properties>
<failure>
<message><![CDATA[Child test failed]]></message>
</failure>
<test-suite type="TestFixture" id="1000" name="MockTestFixture" fullname="NUnit.Tests.Assemblies.MockTestFixture" testcasecount="11" result="Failed" time="0.119" total="10" passed="4" failed="2" inconclusive="1" skipped="3" asserts="0">
<properties>
<property name="Category" value="FixtureCategory" />
<property name="Description" value="Fake Test Fixture" />
</properties>
<failure>
<message><![CDATA[Child test failed]]></message>
</failure>
<test-case id="1005" name="FailingTest" fullname="NUnit.Tests.Assemblies.MockTestFixture.FailingTest" result="Failed" time="0.023" asserts="0">
<failure>
<message><![CDATA[Intentional failure]]></message>
<stack-trace><![CDATA[ at NUnit.Framework.Assert.Fail(String message, Object[] args) in D:\Dev\NUnit\nunit-3.0\work\NUnitFramework\src\framework\Assert.cs:line 142
at NUnit.Framework.Assert.Fail(String message) in D:\Dev\NUnit\nunit-3.0\work\NUnitFramework\src\framework\Assert.cs:line 152
at NUnit.Tests.Assemblies.MockTestFixture.FailingTest() in D:\Dev\NUnit\nunit-3.0\work\NUnitFramework\src\mock-assembly\MockAssembly.cs:line 121]]></stack-trace>
</failure>
</test-case>
<test-case id="1010" name="InconclusiveTest" fullname="NUnit.Tests.Assemblies.MockTestFixture.InconclusiveTest" result="Inconclusive" time="0.001" asserts="0" />
<test-case id="1001" name="MockTest1" fullname="NUnit.Tests.Assemblies.MockTestFixture.MockTest1" result="Passed" time="0.000" asserts="0">
<properties>
<property name="Description" value="Mock Test #1" />
</properties>
</test-case>
<test-case id="1002" name="MockTest2" fullname="NUnit.Tests.Assemblies.MockTestFixture.MockTest2" result="Passed" time="0.000" asserts="0">
<properties>
<property name="Severity" value="Critical" />
<property name="Description" value="This is a really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really, really long description" />
<property name="Category" value="MockCategory" />
</properties>
</test-case>
<test-case id="1003" name="MockTest3" fullname="NUnit.Tests.Assemblies.MockTestFixture.MockTest3" result="Passed" time="0.000" asserts="0">
<properties>
<property name="Category" value="AnotherCategory" />
<property name="Category" value="MockCategory" />
</properties>
</test-case>
<test-case id="1007" name="MockTest4" fullname="NUnit.Tests.Assemblies.MockTestFixture.MockTest4" result="Skipped" label="Ignored" time="0.000" asserts="0">
<properties>
<property name="Category" value="Foo" />
<property name="_SKIPREASON" value="ignoring this test method for now" />
</properties>
<reason>
<message><![CDATA[ignoring this test method for now]]></message>
</reason>
</test-case>
<test-case id="1004" name="MockTest5" fullname="NUnit.Tests.Assemblies.MockTestFixture.MockTest5" result="Skipped" label="Invalid" time="0.000" asserts="0">
<properties>
<property name="_SKIPREASON" value="Method is not public" />
</properties>
<reason>
<message><![CDATA[Method is not public]]></message>
</reason>
</test-case>
<test-case id="1009" name="NotRunnableTest" fullname="NUnit.Tests.Assemblies.MockTestFixture.NotRunnableTest" result="Skipped" label="Invalid" time="0.000" asserts="0">
<properties>
<property name="_SKIPREASON" value="No arguments were provided" />
</properties>
<reason>
<message><![CDATA[No arguments were provided]]></message>
</reason>
</test-case>
<test-case id="1011" name="TestWithException" fullname="NUnit.Tests.Assemblies.MockTestFixture.TestWithException" result="Failed" label="Error" time="0.002" asserts="0">
<failure>
<message><![CDATA[System.ApplicationException : Intentional Exception]]></message>
<stack-trace><![CDATA[ at NUnit.Tests.Assemblies.MockTestFixture.MethodThrowsException() in D:\Dev\NUnit\nunit-3.0\work\NUnitFramework\src\mock-assembly\MockAssembly.cs:line 158
at NUnit.Tests.Assemblies.MockTestFixture.TestWithException() in D:\Dev\NUnit\nunit-3.0\work\NUnitFramework\src\mock-assembly\MockAssembly.cs:line 153]]></stack-trace>
</failure>
</test-case>
<test-case id="1006" name="TestWithManyProperties" fullname="NUnit.Tests.Assemblies.MockTestFixture.TestWithManyProperties" result="Passed" time="0.000" asserts="0">
<properties>
<property name="TargetMethod" value="SomeClassName" />
<property name="Size" value="5" />
</properties>
</test-case>
</test-suite>
<test-suite type="TestFixture" id="1023" name="BadFixture" fullname="NUnit.Tests.BadFixture" testcasecount="1" result="Skipped" label="Invalid" time="0.000" total="0" passed="0" failed="0" inconclusive="0" skipped="0" asserts="0">
<properties>
<property name="_SKIPREASON" value="No suitable constructor was found" />
</properties>
<reason>
<message><![CDATA[No suitable constructor was found]]></message>
</reason>
</test-suite>
<test-suite type="TestFixture" id="1025" name="FixtureWithTestCases" fullname="NUnit.Tests.FixtureWithTestCases" testcasecount="2" result="Passed" time="0.010" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="2">
<test-suite type="ParameterizedMethod" id="1026" name="MethodWithParameters" fullname="NUnit.Tests.FixtureWithTestCases.MethodWithParameters" testcasecount="2" result="Passed" time="0.009" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="2">
<test-case id="1027" name="MethodWithParameters(2,2)" fullname="NUnit.Tests.FixtureWithTestCases.MethodWithParameters(2,2)" result="Passed" time="0.006" asserts="1" />
<test-case id="1028" name="MethodWithParameters(9,11)" fullname="NUnit.Tests.FixtureWithTestCases.MethodWithParameters(9,11)" result="Passed" time="0.000" asserts="1" />
</test-suite>
</test-suite>
<test-suite type="TestFixture" id="1016" name="IgnoredFixture" fullname="NUnit.Tests.IgnoredFixture" testcasecount="3" result="Skipped" label="Ignored" time="0.000" total="0" passed="0" failed="0" inconclusive="0" skipped="0" asserts="0">
<properties>
<property name="_SKIPREASON" value="" />
</properties>
<reason>
<message><![CDATA[]]></message>
</reason>
</test-suite>
<test-suite type="ParameterizedFixture" id="1029" name="ParameterizedFixture" fullname="NUnit.Tests.ParameterizedFixture" testcasecount="4" result="Passed" time="0.007" total="4" passed="4" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-suite type="TestFixture" id="1030" name="ParameterizedFixture(42)" fullname="NUnit.Tests.ParameterizedFixture(42)" testcasecount="2" result="Passed" time="0.003" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-case id="1031" name="Test1" fullname="NUnit.Tests.ParameterizedFixture(42).Test1" result="Passed" time="0.000" asserts="0" />
<test-case id="1032" name="Test2" fullname="NUnit.Tests.ParameterizedFixture(42).Test2" result="Passed" time="0.000" asserts="0" />
</test-suite>
<test-suite type="TestFixture" id="1033" name="ParameterizedFixture(5)" fullname="NUnit.Tests.ParameterizedFixture(5)" testcasecount="2" result="Passed" time="0.002" total="2" passed="2" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-case id="1034" name="Test1" fullname="NUnit.Tests.ParameterizedFixture(5).Test1" result="Passed" time="0.000" asserts="0" />
<test-case id="1035" name="Test2" fullname="NUnit.Tests.ParameterizedFixture(5).Test2" result="Passed" time="0.000" asserts="0" />
</test-suite>
</test-suite>
<test-suite type="TestFixture" id="1012" name="OneTestCase" fullname="NUnit.Tests.Singletons.OneTestCase" testcasecount="1" result="Passed" time="0.001" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-case id="1013" name="TestCase" fullname="NUnit.Tests.Singletons.OneTestCase.TestCase" result="Passed" time="0.000" asserts="0" />
</test-suite>
<test-suite type="TestFixture" id="1014" name="MockTestFixture" fullname="NUnit.Tests.TestAssembly.MockTestFixture" testcasecount="1" result="Passed" time="0.001" total="1" passed="1" failed="0" inconclusive="0" skipped="0" asserts="0">
<test-case id="1015" name="MyTest" fullname="NUnit.Tests.TestAssembly.MockTestFixture.MyTest" result="Passed" time="0.001" asserts="0" />
</test-suite>
</test-suite>
</test-run>
Loading

0 comments on commit b6c7990

Please sign in to comment.