Skip to content

Commit f0eff63

Browse files
authored
AzureTestPlan@V0 Code Refactoring. (#20816)
* Refactored manual flow * Changing Task Directory Structure * Modified Maven and Gradle Executors in new flow * Fixing Gradle User input GradlewFlow * Fixing PythonTestExecutor * Changed the old flow under a folder for efficient cleanup * Building Task and Fixing Generated Task Folder * Move discoverTests func to return IOperationResult * Renaming symbol and removing extra semicolon * Fixing variable to pass to Python Executor
1 parent f90ad8a commit f0eff63

File tree

91 files changed

+4822
-2083
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

91 files changed

+4822
-2083
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import { ITestExecutor } from "../../Interface/ITestExecutor";
2+
import { IOperationResult } from "../../Interface/IOperationResult";
3+
import { ciDictionary } from "../../Common/ciEventLogger";
4+
import * as constants from "../../Common/constants";
5+
import { removeParenthesesFromEnd, replaceLastDotWithHash } from "../../Common/utils";
6+
import * as tl from 'azure-pipelines-task-lib/task';
7+
import { ToolRunner } from "azure-pipelines-task-lib/toolrunner";
8+
import { SimpleTimer } from "../../Common/SimpleTimer";
9+
10+
export class GradleTestExecutor implements ITestExecutor {
11+
testRunnerCLI: string = constants.GRADLE_EXECUTABLE;
12+
toolRunnerPath: string;
13+
toolRunner: ToolRunner;
14+
gradlewFilePath: string;
15+
16+
/*
17+
* Setup the test executor
18+
*/
19+
async setup(): Promise<IOperationResult> {
20+
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' };
21+
this.gradlewFilePath = tl.getInput('gradleFilePath');
22+
23+
try {
24+
this.toolRunnerPath = tl.which(this.testRunnerCLI, true);
25+
this.toolRunner = tl.tool(this.toolRunnerPath);
26+
this.toolRunner.arg('-v');
27+
operationResult.returnCode = await this.toolRunner.execAsync();
28+
} catch (error) {
29+
operationResult.returnCode = 1;
30+
operationResult.errorMessage = error.message || String(error);
31+
tl.debug("Error in Gradle setup: " + operationResult.errorMessage);
32+
tl.debug("Looking for Gradlew file to install Gradle");
33+
}
34+
35+
if(operationResult.returnCode === 1){
36+
operationResult.returnCode = 0;
37+
operationResult.errorMessage = '';
38+
39+
try {
40+
this.toolRunnerPath = tl.which(this.gradlewFilePath, true);
41+
} catch (error) {
42+
operationResult.returnCode = 1;
43+
operationResult.errorMessage = error.message || String(error);
44+
tl.debug("Error while looking for user input Gradlew file: " + operationResult.errorMessage);
45+
tl.debug("Looking for gradlew file in the repository");
46+
}
47+
}
48+
49+
if(operationResult.returnCode === 1){
50+
operationResult.returnCode = 0;
51+
operationResult.errorMessage = '';
52+
53+
try {
54+
const gradlewExecFileSearchPattern: string = "**/gradlew";
55+
let workingDirectory = tl.getVariable('System.DefaultWorkingDirectory');
56+
let os = tl.getVariable('Agent.OS');
57+
const gradlewPath = tl.findMatch(workingDirectory, gradlewExecFileSearchPattern);
58+
this.toolRunnerPath = gradlewPath[0];
59+
60+
if (gradlewPath.length == 0) {
61+
operationResult.returnCode = 1;
62+
operationResult.errorMessage = tl.loc('GradlewNotFound');
63+
tl.debug("Gradlew file not found in the repository");
64+
return operationResult;
65+
}
66+
67+
if (gradlewPath.length > 1) {
68+
tl.warning(tl.loc('MultipleMatchingGradlewFound'));
69+
tl.debug(this.toolRunnerPath);
70+
}
71+
72+
if (os == 'Windows_NT') {
73+
tl.debug('Append .bat extension name to gradlew script for windows agent');
74+
this.toolRunnerPath += '.bat';
75+
}
76+
} catch (error) {
77+
operationResult.returnCode = 1;
78+
operationResult.errorMessage = error.message || String(error);
79+
tl.debug("Error while looking for gradlew file in the repository: " + operationResult.errorMessage);
80+
}
81+
82+
return operationResult;
83+
}
84+
85+
86+
try {
87+
operationResult.returnCode = await this.toolRunner.execAsync();
88+
} catch (error) {
89+
operationResult.errorMessage = error.message || String(error);
90+
}
91+
return operationResult;
92+
}
93+
94+
async discoverTests(listOfTestsToBeExecuted: string[], ciData: ciDictionary, listOfTestsToBeRan: string[]): Promise<IOperationResult> {
95+
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' };
96+
97+
listOfTestsToBeExecuted.forEach(element => {
98+
listOfTestsToBeRan.push(element);
99+
});
100+
101+
return operationResult;
102+
}
103+
104+
async executeTests(testsToBeExecuted: string[], ciData: ciDictionary): Promise<IOperationResult> {
105+
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' };
106+
let executionTimer = new SimpleTimer(constants.AUTOMATED_EXECUTION);
107+
executionTimer.start();
108+
109+
this.toolRunner = tl.tool(this.toolRunnerPath);
110+
const args = []
111+
112+
args.push('test');
113+
114+
for (let testcase of testsToBeExecuted) {
115+
// in some cases found that gradle is including () in test name
116+
testcase = removeParenthesesFromEnd(testcase);
117+
args.push('--tests');
118+
args.push(testcase);
119+
}
120+
121+
tl.debug("Executing gradle tests with args :" + args);
122+
this.toolRunner.arg(args);
123+
124+
try {
125+
operationResult.returnCode = await this.toolRunner.execAsync();
126+
} catch (error) {
127+
operationResult.errorMessage = error.message || String(error);
128+
}
129+
executionTimer.stop(ciData);
130+
131+
return operationResult;
132+
}
133+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
import { ITestExecutor } from "../../Interface/ITestExecutor";
2+
import { IOperationResult } from "../../Interface/IOperationResult";
3+
import { ciDictionary } from "../../Common/ciEventLogger";
4+
import * as constants from "../../Common/constants";
5+
import { replaceLastDotWithHash } from "../../Common/utils";
6+
import * as tl from 'azure-pipelines-task-lib/task';
7+
import { ToolRunner } from "azure-pipelines-task-lib/toolrunner";
8+
import { SimpleTimer } from "../../Common/SimpleTimer";
9+
10+
export class MavenTestExecutor implements ITestExecutor {
11+
testRunnerCLI: string = constants.MVN_EXECUTABLE;
12+
toolRunnerPath: string;
13+
toolRunner: ToolRunner;
14+
pomFilePath: string;
15+
16+
async setup(): Promise<IOperationResult> {
17+
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' };
18+
this.pomFilePath = tl.getInput('pomFilePath');
19+
20+
try {
21+
this.toolRunnerPath = tl.which(this.testRunnerCLI, true);
22+
} catch (error) {
23+
operationResult.returnCode = 1;
24+
operationResult.errorMessage = error.message || String(error);
25+
return operationResult;
26+
}
27+
28+
29+
this.toolRunner = tl.tool(this.toolRunnerPath);
30+
this.toolRunner.arg('-version');
31+
32+
try {
33+
operationResult.returnCode = await this.toolRunner.execAsync();
34+
} catch (error) {
35+
operationResult.errorMessage = error.message || String(error);
36+
}
37+
return operationResult;
38+
}
39+
40+
async discoverTests(listOfTestsToBeExecuted: string[], ciData: ciDictionary, listOfTestsToBeRan: string[]): Promise<IOperationResult> {
41+
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' };
42+
43+
listOfTestsToBeExecuted.forEach(element => {
44+
listOfTestsToBeRan.push(element);
45+
});
46+
47+
return operationResult;
48+
}
49+
50+
async executeTests(testsToBeExecuted: string[], ciData: ciDictionary): Promise<IOperationResult> {
51+
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' };
52+
let executionTimer = new SimpleTimer(constants.AUTOMATED_EXECUTION);
53+
executionTimer.start();
54+
55+
this.toolRunner = tl.tool(this.toolRunnerPath);
56+
const args = []
57+
const testsToRun =[]
58+
59+
for (let tests of testsToBeExecuted) {
60+
const modifiedTest = replaceLastDotWithHash(tests);
61+
testsToRun.push(modifiedTest);
62+
}
63+
64+
if (testsToRun.length > 0)
65+
{
66+
const testsList = testsToRun.join(',')
67+
const dtest = constants.MAVEN_DTEST;
68+
const combinedTestArgs = dtest + testsList;
69+
70+
args.push('test');
71+
args.push(combinedTestArgs);
72+
}
73+
74+
args.push('-ntp');
75+
76+
if (this.pomFilePath) {
77+
args.push('-f');
78+
args.push(this.pomFilePath);
79+
}
80+
81+
tl.debug("Executing java maven tests with args :" + args);
82+
this.toolRunner.arg(args);
83+
84+
try {
85+
operationResult.returnCode = await this.toolRunner.execAsync();
86+
} catch (error) {
87+
operationResult.errorMessage = error.message || String(error);
88+
}
89+
executionTimer.stop(ciData);
90+
91+
return operationResult;
92+
}
93+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { ITestExecutor } from "../../Interface/ITestExecutor";
2+
import { IOperationResult } from "../../Interface/IOperationResult";
3+
import { ciDictionary } from "../../Common/ciEventLogger";
4+
import * as constants from "../../Common/constants";
5+
import { replaceLastDotWithHash, extractPythonDiscoveredTests, getExecOptions, transformPythonTestStrings } from "../../Common/utils";
6+
import * as tl from 'azure-pipelines-task-lib/task';
7+
import { ToolRunner } from "azure-pipelines-task-lib/toolrunner";
8+
import { SimpleTimer } from "../../Common/SimpleTimer";
9+
10+
export class PythonTestExecutor implements ITestExecutor {
11+
testRunnerCLI: string = constants.PYTEST_EXECUTABLE;
12+
toolRunnerPath: string;
13+
toolRunner: ToolRunner;
14+
pomFilePath: string;
15+
16+
async setup(): Promise<IOperationResult> {
17+
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' };
18+
19+
try {
20+
this.toolRunnerPath = tl.which(this.testRunnerCLI, true);
21+
} catch (error) {
22+
operationResult.returnCode = 1;
23+
operationResult.errorMessage = error.message || String(error);
24+
return operationResult;
25+
}
26+
27+
this.toolRunner = tl.tool(this.toolRunnerPath);
28+
this.toolRunner.arg('--version');
29+
30+
try {
31+
operationResult.returnCode = await this.toolRunner.execAsync();
32+
} catch (error) {
33+
operationResult.errorMessage = error.message || String(error);
34+
}
35+
return operationResult;
36+
}
37+
38+
async discoverTests(testsToBeExecuted: string[], ciData: ciDictionary, listOfTestsToBeRan: string[]): Promise<IOperationResult> {
39+
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' };
40+
const args: string[] = ['--collect-only', '-q'];
41+
let discoveryResult = { stdout: ''};;
42+
this.toolRunner = tl.tool(this.toolRunnerPath);
43+
this.toolRunner.arg(args);
44+
45+
try {
46+
operationResult.returnCode = await this.toolRunner.execAsync(getExecOptions(discoveryResult));
47+
} catch (error) {
48+
operationResult.errorMessage = error.message || String(error);
49+
}
50+
51+
if(operationResult.returnCode === 0){
52+
// Extract discovered tests from stdout
53+
const discoveredTests: string[] = extractPythonDiscoveredTests(discoveryResult.stdout ?? '');
54+
var testStringtoFQNMap: Map<string, string> = new Map<string, string>();
55+
56+
for(let test of discoveredTests){
57+
testStringtoFQNMap.set(transformPythonTestStrings(test), test);
58+
}
59+
60+
for(let test of testsToBeExecuted){
61+
if(!testStringtoFQNMap.has(test)){
62+
tl.debug(`Test ${test} not found in discovered tests`);
63+
}
64+
else{
65+
listOfTestsToBeRan.push(testStringtoFQNMap.get(test));
66+
}
67+
}
68+
69+
// Variables for debug console logs
70+
const testsToBeExecutedString: string = testsToBeExecuted.join(", ");
71+
const testsToRunString: string = listOfTestsToBeRan.join(", ");
72+
73+
tl.debug(`Tests to executed are: ${testsToBeExecutedString}`);
74+
tl.debug(`Tests to run are: ${testsToRunString}`);
75+
console.log(`Found ${listOfTestsToBeRan.length} tests to run`);
76+
77+
return operationResult;
78+
}
79+
}
80+
81+
async executeTests(testsToBeExecuted: string[], ciData: ciDictionary): Promise<IOperationResult> {
82+
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' };
83+
let executionTimer = new SimpleTimer(constants.AUTOMATED_EXECUTION);
84+
executionTimer.start();
85+
86+
this.toolRunner = tl.tool(this.toolRunnerPath);
87+
88+
tl.debug("Executing python pytest tests with args :" + testsToBeExecuted);
89+
this.toolRunner.arg(testsToBeExecuted);
90+
this.toolRunner.arg('--junitxml=TEST-python-junit.xml');
91+
92+
try {
93+
operationResult.returnCode = await this.toolRunner.execAsync();
94+
} catch (error) {
95+
operationResult.errorMessage = error.message || String(error);
96+
}
97+
executionTimer.stop(ciData);
98+
99+
return operationResult;
100+
}
101+
102+
103+
}

0 commit comments

Comments
 (0)