Skip to content

Commit c445e55

Browse files
authored
Publishing Automated Test Results using Command (#19580)
* Initial Publish Function * Generated Files * Updating Calling place * Updating Task Version * Addressing Build Failures * Removing extra lines * Updating data structures for passing list of AutomatedTestPoints * updating generated files * Making nit changes * Updating the files for publishing * Nit Changes
1 parent 2c43475 commit c445e55

25 files changed

+674
-157
lines changed

Tasks/AzureTestPlanV0/Invokers/pythonivoker.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export async function executepythontests(testsToBeExecuted: string[]) {
1414
args.push(testcase);
1515
}
1616

17-
args.push('--junitxml=junit.xml')
17+
args.push('--junitxml=TEST-python-junit.xml')
1818

1919
tl.debug("Executing python tests with executable : " + executable);
2020
tl.debug("Executing python tests with args :" + args);

Tasks/AzureTestPlanV0/Strings/resources.resjson/en-US/resources.resjson

+10
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,16 @@
1313
"loc.input.help.testConfiguration": "Select Test Configuration.",
1414
"loc.input.label.testLanguageInput": "Select Test framework language",
1515
"loc.input.help.testLanguageInput": "Test Framework Language of automated tests in test plan",
16+
"loc.input.label.mergeTestResults": "Merge test results",
17+
"loc.input.help.mergeTestResults": "A test run is created for each results file. Check this option to merge results into a single test run. To optimize for better performance, results will be merged into a single run if there are more than 100 result files, irrespective of this option.",
18+
"loc.input.label.publishRunAttachments": "Upload test results files",
19+
"loc.input.help.publishRunAttachments": "Upload logs and other files containing diagnostic information collected when the tests were run.",
20+
"loc.input.label.failTaskOnFailedTests": "Fail if there are test failures",
21+
"loc.input.help.failTaskOnFailedTests": "Fail the task if there are any test failures. Check this option to fail the task if test failures are detected in the result files.",
22+
"loc.input.label.failTaskOnFailureToPublishResults": "Fail if there is failure in publishing test results",
23+
"loc.input.help.failTaskOnFailureToPublishResults": "Fail if there is failure in publishing test results. Check this option to fail the task if publishing test results is failed partially.",
24+
"loc.input.label.failTaskOnMissingResultsFile": "Fail if no result files are found",
25+
"loc.input.help.failTaskOnMissingResultsFile": "Fail the task if no result files are found.",
1626
"loc.messages.testPlanInput": "Test plan Id : %s",
1727
"loc.messages.testplanConfigInput": "Test plan configuration Id : %s",
1828
"loc.messages.testSuiteSelected": "Test suite Id selected: %s",

Tasks/AzureTestPlanV0/automatedTests.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import tl = require('azure-pipelines-task-lib/task');
22
import { testInvoker } from './automatedTestInvoker'
33
import { TestPlanData } from './testPlanData'
4-
import { TestCase } from 'azure-devops-node-api/interfaces/TestPlanInterfaces';
4+
import { publishAutomatedTestResult } from './publishAutomatedTests'
55

66

77
export async function automatedTestsFlow(testPlanInfo: TestPlanData, testSelectorInput: string) {
@@ -13,6 +13,7 @@ export async function automatedTestsFlow(testPlanInfo: TestPlanData, testSelecto
1313
if (listOfTestsToBeExecuted !== null && listOfTestsToBeExecuted !== undefined && listOfTestsToBeExecuted.length > 0) {
1414
tl.debug("Invoking test execution for tests: " + listOfTestsToBeExecuted);
1515
testInvoker(listOfTestsToBeExecuted);
16+
publishAutomatedTestResult(JSON.stringify(testPlanInfo.listOfAutomatedTestPoints));
1617
}
1718
else {
1819
console.log("No automated tests found for given test plan inputs ");

Tasks/AzureTestPlanV0/constants.ts

+1
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ export const AUTOMATED_TEST_NAME = "Microsoft.VSTS.TCM.AutomatedTestName";
77
export const AUTOMATED_TEST_STORAGE = "Microsoft.VSTS.TCM.AutomatedTestStorage";
88
export const AUTOMATION_STATUS = 'Microsoft.VSTS.TCM.AutomationStatus';
99
export const NOT_AUTOMATED = 'Not Automated'
10+
export const MERGE_THRESHOLD = 100;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import tl = require('azure-pipelines-task-lib/task');
2+
import * as path from 'path';
3+
import constants = require('./constants');
4+
5+
function publish(testRunner, resultFiles, mergeResults, failTaskOnFailedTests, platform, publishRunAttachments, testRunSystem , failTaskOnFailureToPublishResults, listOfAutomatedTestPoints) {
6+
var properties = <{ [key: string]: string }>{};
7+
properties['type'] = testRunner;
8+
properties['mergeResults'] = mergeResults;
9+
properties['platform'] = platform;
10+
properties['publishRunAttachments'] = publishRunAttachments;
11+
properties['resultFiles'] = resultFiles;
12+
properties['failTaskOnFailedTests'] = failTaskOnFailedTests;
13+
properties['failTaskOnFailureToPublishResults'] = failTaskOnFailureToPublishResults;
14+
properties['testRunSystem'] = testRunSystem;
15+
properties['listOfAutomatedTestPoints'] = listOfAutomatedTestPoints;
16+
properties['testPlanId'] = tl.getVariable('TestPlanId');
17+
18+
tl.command('results.publish', properties, '');
19+
}
20+
21+
export async function publishAutomatedTestResult(listOfAutomatedTestPoints: string) {
22+
try{
23+
const testRunner = "JUnit";
24+
const testResultsFiles: string[] = ["**/TEST-*.xml"];
25+
const mergeResults = tl.getInput('mergeTestResults');
26+
const platform = "any cpu";
27+
const publishRunAttachments = tl.getInput('publishRunAttachments');
28+
const failTaskOnFailedTests = tl.getInput('failTaskOnFailedTests');
29+
const failTaskOnMissingResultsFile: boolean = tl.getBoolInput('failTaskOnMissingResultsFile');
30+
const failTaskOnFailureToPublishResults = tl.getInput('failTaskOnFailureToPublishResults');
31+
const testRunSystem = "AzureTestPlan : " + tl.getInput("testLanguageInput");
32+
33+
let searchFolder = tl.getVariable('System.DefaultWorkingDirectory');
34+
35+
tl.debug('testRunner: ' + testRunner);
36+
tl.debug('testResultsFiles: ' + testResultsFiles);
37+
tl.debug('mergeResults: ' + mergeResults);
38+
tl.debug('platform: ' + platform);
39+
tl.debug('publishRunAttachments: ' + publishRunAttachments);
40+
tl.debug('failTaskOnFailedTests: ' + failTaskOnFailedTests);
41+
tl.debug('failTaskOnMissingResultsFile: ' + failTaskOnMissingResultsFile);
42+
tl.debug('failTaskOnFailureToPublishResults: ' + failTaskOnFailureToPublishResults);
43+
44+
if(tl.getVariable('System.DefaultWorkingDirectory') && (!path.isAbsolute(searchFolder)))
45+
{
46+
searchFolder = path.join(tl.getVariable('System.DefaultWorkingDirectory'),searchFolder);
47+
}
48+
49+
const findOptions = <tl.FindOptions>{
50+
allowBrokenSymbolicLinks: true,
51+
followSpecifiedSymbolicLink: true,
52+
followSymbolicLinks: true
53+
};
54+
55+
const matchingTestResultsFiles = tl.findMatch(searchFolder, testResultsFiles, findOptions);
56+
57+
const testResultsFilesCount = matchingTestResultsFiles ? matchingTestResultsFiles.length : 0;
58+
59+
tl.debug(`Detected ${testResultsFilesCount} test result files`);
60+
61+
const forceMerge = testResultsFilesCount > constants.MERGE_THRESHOLD;
62+
if (forceMerge) {
63+
tl.debug('Detected large number of test result files. Merged all of them into a single file and published a single test run to optimize for test result publish performance instead of publishing hundreds of test runs');
64+
}
65+
66+
if (testResultsFilesCount === 0) {
67+
if (failTaskOnMissingResultsFile) {
68+
tl.setResult(tl.TaskResult.Failed, tl.loc('NoMatchingFilesFound', testResultsFiles));
69+
} else {
70+
tl.warning(tl.loc('NoMatchingFilesFound', testResultsFiles));
71+
}
72+
} else {
73+
publish(testRunner, matchingTestResultsFiles,
74+
forceMerge ? true.toString() : mergeResults,
75+
failTaskOnFailedTests,
76+
platform,
77+
publishRunAttachments,
78+
testRunSystem,
79+
failTaskOnFailureToPublishResults,
80+
listOfAutomatedTestPoints);
81+
}
82+
} catch (err) {
83+
tl.setResult(tl.TaskResult.Failed, err);
84+
}
85+
}
86+

Tasks/AzureTestPlanV0/task.json

+42-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"version": {
1515
"Major": 0,
1616
"Minor": 236,
17-
"Patch": 0
17+
"Patch": 1
1818
},
1919
"preview": true,
2020
"demands": [],
@@ -84,6 +84,47 @@
8484
"properties": {
8585
"MultiSelectFlatList": "True"
8686
}
87+
},
88+
{
89+
"name": "mergeTestResults",
90+
"type": "boolean",
91+
"label": "Merge test results",
92+
"defaultValue": "false",
93+
"required": false,
94+
"helpMarkDown": "A test run is created for each results file. Check this option to merge results into a single test run. To optimize for better performance, results will be merged into a single run if there are more than 100 result files, irrespective of this option."
95+
},
96+
{
97+
"name": "publishRunAttachments",
98+
"type": "boolean",
99+
"label": "Upload test results files",
100+
"defaultValue": "true",
101+
"required": false,
102+
"helpMarkDown": "Upload logs and other files containing diagnostic information collected when the tests were run.",
103+
"groupName": "advanced"
104+
},
105+
{
106+
"name": "failTaskOnFailedTests",
107+
"type": "boolean",
108+
"label": "Fail if there are test failures",
109+
"defaultValue": "false",
110+
"required": false,
111+
"helpMarkDown": "Fail the task if there are any test failures. Check this option to fail the task if test failures are detected in the result files."
112+
},
113+
{
114+
"name": "failTaskOnFailureToPublishResults",
115+
"type": "boolean",
116+
"label": "Fail if there is failure in publishing test results",
117+
"defaultValue": false,
118+
"required": false,
119+
"helpMarkDown": "Fail if there is failure in publishing test results. Check this option to fail the task if publishing test results is failed partially."
120+
},
121+
{
122+
"name": "failTaskOnMissingResultsFile",
123+
"type": "boolean",
124+
"label": "Fail if no result files are found",
125+
"defaultValue": false,
126+
"required": false,
127+
"helpMarkDown": "Fail the task if no result files are found."
87128
}
88129
],
89130
"dataSourceBindings": [

Tasks/AzureTestPlanV0/task.loc.json

+46-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"version": {
1515
"Major": 0,
1616
"Minor": 236,
17-
"Patch": 0
17+
"Patch": 1
1818
},
1919
"preview": true,
2020
"demands": [],
@@ -84,6 +84,47 @@
8484
"properties": {
8585
"MultiSelectFlatList": "True"
8686
}
87+
},
88+
{
89+
"name": "mergeTestResults",
90+
"type": "boolean",
91+
"label": "ms-resource:loc.input.label.mergeTestResults",
92+
"defaultValue": "false",
93+
"required": false,
94+
"helpMarkDown": "ms-resource:loc.input.help.mergeTestResults"
95+
},
96+
{
97+
"name": "publishRunAttachments",
98+
"type": "boolean",
99+
"label": "ms-resource:loc.input.label.publishRunAttachments",
100+
"defaultValue": "true",
101+
"required": false,
102+
"helpMarkDown": "ms-resource:loc.input.help.publishRunAttachments",
103+
"groupName": "advanced"
104+
},
105+
{
106+
"name": "failTaskOnFailedTests",
107+
"type": "boolean",
108+
"label": "ms-resource:loc.input.label.failTaskOnFailedTests",
109+
"defaultValue": "false",
110+
"required": false,
111+
"helpMarkDown": "ms-resource:loc.input.help.failTaskOnFailedTests"
112+
},
113+
{
114+
"name": "failTaskOnFailureToPublishResults",
115+
"type": "boolean",
116+
"label": "ms-resource:loc.input.label.failTaskOnFailureToPublishResults",
117+
"defaultValue": false,
118+
"required": false,
119+
"helpMarkDown": "ms-resource:loc.input.help.failTaskOnFailureToPublishResults"
120+
},
121+
{
122+
"name": "failTaskOnMissingResultsFile",
123+
"type": "boolean",
124+
"label": "ms-resource:loc.input.label.failTaskOnMissingResultsFile",
125+
"defaultValue": false,
126+
"required": false,
127+
"helpMarkDown": "ms-resource:loc.input.help.failTaskOnMissingResultsFile"
87128
}
88129
],
89130
"dataSourceBindings": [
@@ -133,5 +174,9 @@
133174
"ErrorFailTaskOnExecutingTests": "ms-resource:loc.messages.ErrorFailTaskOnExecutingTests",
134175
"ErrorFailTaskOnAPIFailure": "ms-resource:loc.messages.ErrorFailTaskOnAPIFailure",
135176
"ErrorFailTaskOnCreateRunFailure": "ms-resource:loc.messages.ErrorFailTaskOnCreateRunFailure"
177+
},
178+
"_buildConfigMapping": {
179+
"Default": "0.236.1",
180+
"Node20-225": "0.236.2"
136181
}
137182
}

0 commit comments

Comments
 (0)