-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
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
- Loading branch information
1 parent
f90ad8a
commit f0eff63
Showing
91 changed files
with
4,822 additions
and
2,083 deletions.
There are no files selected for viewing
133 changes: 133 additions & 0 deletions
133
Tasks/AzureTestPlanV0/Automated Flow/TestExecutors/GradleTestExecutor.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
import { ITestExecutor } from "../../Interface/ITestExecutor"; | ||
import { IOperationResult } from "../../Interface/IOperationResult"; | ||
import { ciDictionary } from "../../Common/ciEventLogger"; | ||
import * as constants from "../../Common/constants"; | ||
import { removeParenthesesFromEnd, replaceLastDotWithHash } from "../../Common/utils"; | ||
import * as tl from 'azure-pipelines-task-lib/task'; | ||
import { ToolRunner } from "azure-pipelines-task-lib/toolrunner"; | ||
import { SimpleTimer } from "../../Common/SimpleTimer"; | ||
|
||
export class GradleTestExecutor implements ITestExecutor { | ||
testRunnerCLI: string = constants.GRADLE_EXECUTABLE; | ||
toolRunnerPath: string; | ||
toolRunner: ToolRunner; | ||
gradlewFilePath: string; | ||
|
||
/* | ||
* Setup the test executor | ||
*/ | ||
async setup(): Promise<IOperationResult> { | ||
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' }; | ||
this.gradlewFilePath = tl.getInput('gradleFilePath'); | ||
|
||
try { | ||
this.toolRunnerPath = tl.which(this.testRunnerCLI, true); | ||
this.toolRunner = tl.tool(this.toolRunnerPath); | ||
this.toolRunner.arg('-v'); | ||
operationResult.returnCode = await this.toolRunner.execAsync(); | ||
} catch (error) { | ||
operationResult.returnCode = 1; | ||
operationResult.errorMessage = error.message || String(error); | ||
tl.debug("Error in Gradle setup: " + operationResult.errorMessage); | ||
tl.debug("Looking for Gradlew file to install Gradle"); | ||
} | ||
|
||
if(operationResult.returnCode === 1){ | ||
operationResult.returnCode = 0; | ||
operationResult.errorMessage = ''; | ||
|
||
try { | ||
this.toolRunnerPath = tl.which(this.gradlewFilePath, true); | ||
} catch (error) { | ||
operationResult.returnCode = 1; | ||
operationResult.errorMessage = error.message || String(error); | ||
tl.debug("Error while looking for user input Gradlew file: " + operationResult.errorMessage); | ||
tl.debug("Looking for gradlew file in the repository"); | ||
} | ||
} | ||
|
||
if(operationResult.returnCode === 1){ | ||
operationResult.returnCode = 0; | ||
operationResult.errorMessage = ''; | ||
|
||
try { | ||
const gradlewExecFileSearchPattern: string = "**/gradlew"; | ||
let workingDirectory = tl.getVariable('System.DefaultWorkingDirectory'); | ||
let os = tl.getVariable('Agent.OS'); | ||
const gradlewPath = tl.findMatch(workingDirectory, gradlewExecFileSearchPattern); | ||
this.toolRunnerPath = gradlewPath[0]; | ||
|
||
if (gradlewPath.length == 0) { | ||
operationResult.returnCode = 1; | ||
operationResult.errorMessage = tl.loc('GradlewNotFound'); | ||
tl.debug("Gradlew file not found in the repository"); | ||
return operationResult; | ||
} | ||
|
||
if (gradlewPath.length > 1) { | ||
tl.warning(tl.loc('MultipleMatchingGradlewFound')); | ||
tl.debug(this.toolRunnerPath); | ||
} | ||
|
||
if (os == 'Windows_NT') { | ||
tl.debug('Append .bat extension name to gradlew script for windows agent'); | ||
this.toolRunnerPath += '.bat'; | ||
} | ||
} catch (error) { | ||
operationResult.returnCode = 1; | ||
operationResult.errorMessage = error.message || String(error); | ||
tl.debug("Error while looking for gradlew file in the repository: " + operationResult.errorMessage); | ||
} | ||
|
||
return operationResult; | ||
} | ||
|
||
|
||
try { | ||
operationResult.returnCode = await this.toolRunner.execAsync(); | ||
} catch (error) { | ||
operationResult.errorMessage = error.message || String(error); | ||
} | ||
return operationResult; | ||
} | ||
|
||
async discoverTests(listOfTestsToBeExecuted: string[], ciData: ciDictionary, listOfTestsToBeRan: string[]): Promise<IOperationResult> { | ||
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' }; | ||
|
||
listOfTestsToBeExecuted.forEach(element => { | ||
listOfTestsToBeRan.push(element); | ||
}); | ||
|
||
return operationResult; | ||
} | ||
|
||
async executeTests(testsToBeExecuted: string[], ciData: ciDictionary): Promise<IOperationResult> { | ||
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' }; | ||
let executionTimer = new SimpleTimer(constants.AUTOMATED_EXECUTION); | ||
executionTimer.start(); | ||
|
||
this.toolRunner = tl.tool(this.toolRunnerPath); | ||
const args = [] | ||
|
||
args.push('test'); | ||
|
||
for (let testcase of testsToBeExecuted) { | ||
// in some cases found that gradle is including () in test name | ||
testcase = removeParenthesesFromEnd(testcase); | ||
args.push('--tests'); | ||
args.push(testcase); | ||
} | ||
|
||
tl.debug("Executing gradle tests with args :" + args); | ||
this.toolRunner.arg(args); | ||
|
||
try { | ||
operationResult.returnCode = await this.toolRunner.execAsync(); | ||
} catch (error) { | ||
operationResult.errorMessage = error.message || String(error); | ||
} | ||
executionTimer.stop(ciData); | ||
|
||
return operationResult; | ||
} | ||
} |
93 changes: 93 additions & 0 deletions
93
Tasks/AzureTestPlanV0/Automated Flow/TestExecutors/MavenTestExecutor.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { ITestExecutor } from "../../Interface/ITestExecutor"; | ||
import { IOperationResult } from "../../Interface/IOperationResult"; | ||
import { ciDictionary } from "../../Common/ciEventLogger"; | ||
import * as constants from "../../Common/constants"; | ||
import { replaceLastDotWithHash } from "../../Common/utils"; | ||
import * as tl from 'azure-pipelines-task-lib/task'; | ||
import { ToolRunner } from "azure-pipelines-task-lib/toolrunner"; | ||
import { SimpleTimer } from "../../Common/SimpleTimer"; | ||
|
||
export class MavenTestExecutor implements ITestExecutor { | ||
testRunnerCLI: string = constants.MVN_EXECUTABLE; | ||
toolRunnerPath: string; | ||
toolRunner: ToolRunner; | ||
pomFilePath: string; | ||
|
||
async setup(): Promise<IOperationResult> { | ||
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' }; | ||
this.pomFilePath = tl.getInput('pomFilePath'); | ||
|
||
try { | ||
this.toolRunnerPath = tl.which(this.testRunnerCLI, true); | ||
} catch (error) { | ||
operationResult.returnCode = 1; | ||
operationResult.errorMessage = error.message || String(error); | ||
return operationResult; | ||
} | ||
|
||
|
||
this.toolRunner = tl.tool(this.toolRunnerPath); | ||
this.toolRunner.arg('-version'); | ||
|
||
try { | ||
operationResult.returnCode = await this.toolRunner.execAsync(); | ||
} catch (error) { | ||
operationResult.errorMessage = error.message || String(error); | ||
} | ||
return operationResult; | ||
} | ||
|
||
async discoverTests(listOfTestsToBeExecuted: string[], ciData: ciDictionary, listOfTestsToBeRan: string[]): Promise<IOperationResult> { | ||
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' }; | ||
|
||
listOfTestsToBeExecuted.forEach(element => { | ||
listOfTestsToBeRan.push(element); | ||
}); | ||
|
||
return operationResult; | ||
} | ||
|
||
async executeTests(testsToBeExecuted: string[], ciData: ciDictionary): Promise<IOperationResult> { | ||
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' }; | ||
let executionTimer = new SimpleTimer(constants.AUTOMATED_EXECUTION); | ||
executionTimer.start(); | ||
|
||
this.toolRunner = tl.tool(this.toolRunnerPath); | ||
const args = [] | ||
const testsToRun =[] | ||
|
||
for (let tests of testsToBeExecuted) { | ||
const modifiedTest = replaceLastDotWithHash(tests); | ||
testsToRun.push(modifiedTest); | ||
} | ||
|
||
if (testsToRun.length > 0) | ||
{ | ||
const testsList = testsToRun.join(',') | ||
const dtest = constants.MAVEN_DTEST; | ||
const combinedTestArgs = dtest + testsList; | ||
|
||
args.push('test'); | ||
args.push(combinedTestArgs); | ||
} | ||
|
||
args.push('-ntp'); | ||
|
||
if (this.pomFilePath) { | ||
args.push('-f'); | ||
args.push(this.pomFilePath); | ||
} | ||
|
||
tl.debug("Executing java maven tests with args :" + args); | ||
this.toolRunner.arg(args); | ||
|
||
try { | ||
operationResult.returnCode = await this.toolRunner.execAsync(); | ||
} catch (error) { | ||
operationResult.errorMessage = error.message || String(error); | ||
} | ||
executionTimer.stop(ciData); | ||
|
||
return operationResult; | ||
} | ||
} |
103 changes: 103 additions & 0 deletions
103
Tasks/AzureTestPlanV0/Automated Flow/TestExecutors/PythonTestExecutor.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
import { ITestExecutor } from "../../Interface/ITestExecutor"; | ||
import { IOperationResult } from "../../Interface/IOperationResult"; | ||
import { ciDictionary } from "../../Common/ciEventLogger"; | ||
import * as constants from "../../Common/constants"; | ||
import { replaceLastDotWithHash, extractPythonDiscoveredTests, getExecOptions, transformPythonTestStrings } from "../../Common/utils"; | ||
import * as tl from 'azure-pipelines-task-lib/task'; | ||
import { ToolRunner } from "azure-pipelines-task-lib/toolrunner"; | ||
import { SimpleTimer } from "../../Common/SimpleTimer"; | ||
|
||
export class PythonTestExecutor implements ITestExecutor { | ||
testRunnerCLI: string = constants.PYTEST_EXECUTABLE; | ||
toolRunnerPath: string; | ||
toolRunner: ToolRunner; | ||
pomFilePath: string; | ||
|
||
async setup(): Promise<IOperationResult> { | ||
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' }; | ||
|
||
try { | ||
this.toolRunnerPath = tl.which(this.testRunnerCLI, true); | ||
} catch (error) { | ||
operationResult.returnCode = 1; | ||
operationResult.errorMessage = error.message || String(error); | ||
return operationResult; | ||
} | ||
|
||
this.toolRunner = tl.tool(this.toolRunnerPath); | ||
this.toolRunner.arg('--version'); | ||
|
||
try { | ||
operationResult.returnCode = await this.toolRunner.execAsync(); | ||
} catch (error) { | ||
operationResult.errorMessage = error.message || String(error); | ||
} | ||
return operationResult; | ||
} | ||
|
||
async discoverTests(testsToBeExecuted: string[], ciData: ciDictionary, listOfTestsToBeRan: string[]): Promise<IOperationResult> { | ||
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' }; | ||
const args: string[] = ['--collect-only', '-q']; | ||
let discoveryResult = { stdout: ''};; | ||
this.toolRunner = tl.tool(this.toolRunnerPath); | ||
this.toolRunner.arg(args); | ||
|
||
try { | ||
operationResult.returnCode = await this.toolRunner.execAsync(getExecOptions(discoveryResult)); | ||
} catch (error) { | ||
operationResult.errorMessage = error.message || String(error); | ||
} | ||
|
||
if(operationResult.returnCode === 0){ | ||
// Extract discovered tests from stdout | ||
const discoveredTests: string[] = extractPythonDiscoveredTests(discoveryResult.stdout ?? ''); | ||
var testStringtoFQNMap: Map<string, string> = new Map<string, string>(); | ||
|
||
for(let test of discoveredTests){ | ||
testStringtoFQNMap.set(transformPythonTestStrings(test), test); | ||
} | ||
|
||
for(let test of testsToBeExecuted){ | ||
if(!testStringtoFQNMap.has(test)){ | ||
tl.debug(`Test ${test} not found in discovered tests`); | ||
} | ||
else{ | ||
listOfTestsToBeRan.push(testStringtoFQNMap.get(test)); | ||
} | ||
} | ||
|
||
// Variables for debug console logs | ||
const testsToBeExecutedString: string = testsToBeExecuted.join(", "); | ||
const testsToRunString: string = listOfTestsToBeRan.join(", "); | ||
|
||
tl.debug(`Tests to executed are: ${testsToBeExecutedString}`); | ||
tl.debug(`Tests to run are: ${testsToRunString}`); | ||
console.log(`Found ${listOfTestsToBeRan.length} tests to run`); | ||
|
||
return operationResult; | ||
} | ||
} | ||
|
||
async executeTests(testsToBeExecuted: string[], ciData: ciDictionary): Promise<IOperationResult> { | ||
let operationResult: IOperationResult = { returnCode: 0, errorMessage: '' }; | ||
let executionTimer = new SimpleTimer(constants.AUTOMATED_EXECUTION); | ||
executionTimer.start(); | ||
|
||
this.toolRunner = tl.tool(this.toolRunnerPath); | ||
|
||
tl.debug("Executing python pytest tests with args :" + testsToBeExecuted); | ||
this.toolRunner.arg(testsToBeExecuted); | ||
this.toolRunner.arg('--junitxml=TEST-python-junit.xml'); | ||
|
||
try { | ||
operationResult.returnCode = await this.toolRunner.execAsync(); | ||
} catch (error) { | ||
operationResult.errorMessage = error.message || String(error); | ||
} | ||
executionTimer.stop(ciData); | ||
|
||
return operationResult; | ||
} | ||
|
||
|
||
} |
Oops, something went wrong.