diff --git a/Tasks/VsTest/Strings/resources.resjson/en-US/resources.resjson b/Tasks/VsTest/Strings/resources.resjson/en-US/resources.resjson index 4ff8da7a5ddd..eadc92603891 100644 --- a/Tasks/VsTest/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/VsTest/Strings/resources.resjson/en-US/resources.resjson @@ -168,5 +168,6 @@ "loc.messages.OnlyWindowsOsSupported": "This task is supported only on Windows agents and cannot be used on other platforms.", "loc.messages.MultiConfigNotSupportedWithOnDemand": "On demand runs are not supported with Multi-Configuration option. Please use 'None' or 'Multi-agent' parallelism option.", "loc.messages.disabledRerun": "Disabling the rerun of failed tests as the rerun threshold provided is %s", - "loc.messages.UpgradeAgentMessage": "Please upgrade your vsts-agent version. https://github.com/Microsoft/vsts-agent/releases" + "loc.messages.UpgradeAgentMessage": "Please upgrade your vsts-agent version. https://github.com/Microsoft/vsts-agent/releases", + "loc.messages.VsTestVersionEmpty": "VsTestVersion is null or empty" } \ No newline at end of file diff --git a/Tasks/VsTest/Tests/L0.ts b/Tasks/VsTest/Tests/L0.ts index 1890349cb564..f7cd06503ca2 100644 --- a/Tasks/VsTest/Tests/L0.ts +++ b/Tasks/VsTest/Tests/L0.ts @@ -45,7 +45,7 @@ describe('VsTest Suite', function() { interfacesDictionary[interfaceName] = interfacePropertiesDictionary; }); - checkParity(inputDataContractParityToolOutput, interfacesDictionary, interfacesDictionary['InputDataContract']); + checkParity(inputDataContractParityToolOutput, interfacesDictionary, interfacesDictionary.InputDataContract); function checkParity(dataContractObject: any, interfacesDictionary: any, subInterface: any) { diff --git a/Tasks/VsTest/constants.ts b/Tasks/VsTest/constants.ts index b0bfd28a7b0c..628746da1866 100644 --- a/Tasks/VsTest/constants.ts +++ b/Tasks/VsTest/constants.ts @@ -25,4 +25,10 @@ export module ResultMessages { export module VsTestToolsInstaller { export const PathToVsTestToolVariable = 'VsTestToolsInstallerInstalledToolLocation'; +} + +export module DistributionTypes { + export const EXECUTIONTIMEBASED = 'TestExecutionTimes'; + export const ASSEMBLYBASED = 'TestAssemblies'; + export const NUMBEROFTESTMETHODSBASED = 'numberoftestmethods'; } \ No newline at end of file diff --git a/Tasks/VsTest/distributedtest.ts b/Tasks/VsTest/distributedtest.ts index da4441df5fc2..f48cb927adf7 100644 --- a/Tasks/VsTest/distributedtest.ts +++ b/Tasks/VsTest/distributedtest.ts @@ -4,6 +4,7 @@ import * as ps from 'child_process'; import * as tl from 'vsts-task-lib/task'; import * as tr from 'vsts-task-lib/toolrunner'; import * as models from './models'; +import * as constants from './constants'; import * as inputdatacontract from './inputdatacontract'; import * as settingsHelper from './settingshelper'; import * as utils from './helpers'; @@ -13,16 +14,15 @@ import * as os from 'os'; import * as ci from './cieventlogger'; import { TestSelectorInvoker } from './testselectorinvoker'; import { writeFileSync } from 'fs'; - +import { TaskResult } from 'vso-node-api/interfaces/TaskAgentInterfaces'; const uuid = require('uuid'); const testSelector = new TestSelectorInvoker(); export class DistributedTest { - constructor(dtaTestConfig: models.DtaTestConfigurations) { + constructor(inputDataContract: inputdatacontract.InputDataContract) { this.dtaPid = -1; - this.dtaTestConfig = dtaTestConfig; - this.testSourcesFile = null; + this.inputDataContract = inputDataContract; } public runDistributedTest() { @@ -31,8 +31,12 @@ export class DistributedTest { } private publishCodeChangesIfRequired(): void { - if (this.dtaTestConfig.tiaConfig.tiaEnabled) { - const code = testSelector.publishCodeChanges(this.dtaTestConfig.tiaConfig, this.dtaTestConfig.proxyConfiguration, null, this.dtaTestConfig.taskInstanceIdentifier); + if (this.inputDataContract.ExecutionSettings + && this.inputDataContract.ExecutionSettings.TiaSettings + && this.inputDataContract.ExecutionSettings.TiaSettings.Enabled) { + + // hydra: fix this + const code = testSelector.publishCodeChangesInDistributedMode(this.inputDataContract); //todo: enable custom engine if (code !== 0) { @@ -54,208 +58,52 @@ export class DistributedTest { tl.setResult(tl.TaskResult.Succeeded, 'Task succeeded'); } } catch (error) { - ci.publishEvent({ environmenturi: this.dtaTestConfig.dtaEnvironment.environmentUri, error: error }); + ci.publishEvent({ environmenturi: this.inputDataContract.RunIdentifier, error: error }); tl.error(error); tl.setResult(tl.TaskResult.Failed, error); } } private async startDtaExecutionHost(): Promise { - this.testSourcesFile = this.createTestSourcesFile(); - - const inputDataContract = {}; - - // CoreInputs - inputDataContract.AgentName = this.dtaTestConfig.dtaEnvironment.agentName; - inputDataContract.CollectionUri = this.dtaTestConfig.dtaEnvironment.tfsCollectionUrl; - inputDataContract.EnvironmentUri = this.dtaTestConfig.dtaEnvironment.environmentUri; - inputDataContract.TeamProject = tl.getVariable('System.TeamProject'); - - // InputDataContract.TestSelectionSettings - inputDataContract.TestSelectionSettings = {}; - inputDataContract.TestSelectionSettings.TestCaseFilter = this.dtaTestConfig.testcaseFilter; - inputDataContract.TestSelectionSettings.SearchFolder = this.dtaTestConfig.testDropLocation; - inputDataContract.TestSelectionSettings.TestSelectionType = this.dtaTestConfig.testSelection; - - // InputDataContract.TestSelectionSettings.AssemblyBasedTestSelection - inputDataContract.TestSelectionSettings.AssemblyBasedTestSelection = {}; - inputDataContract.TestSelectionSettings.AssemblyBasedTestSelection.SourceFilter = this.dtaTestConfig.sourceFilter.join('|'); - - // InputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings - inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings = {}; - inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.Testplan = this.dtaTestConfig.testplan; - inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.TestPlanConfigId = this.dtaTestConfig.testPlanConfigId; - inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.OnDemandTestRunId = utils.Helper.isNullEmptyOrUndefined(this.dtaTestConfig.onDemandTestRunId) ? null : Number(this.dtaTestConfig.onDemandTestRunId); - if (!utils.Helper.isNullOrUndefined(this.dtaTestConfig.testSuites)) { - inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.TestSuites = this.dtaTestConfig.testSuites; - } - // InputDataContract.TestReportingSettings - inputDataContract.TestReportingSettings = {}; - inputDataContract.TestReportingSettings.TestRunTitle = this.dtaTestConfig.testRunTitle; - - // InputDataContract.TfsSpecificSettings - inputDataContract.TfsSpecificSettings = {}; - inputDataContract.TfsSpecificSettings.BuildId = utils.Helper.isNullEmptyOrUndefined(tl.getVariable('Build.Buildid')) ? null : Number(tl.getVariable('Build.Buildid')); - inputDataContract.TfsSpecificSettings.BuildUri = tl.getVariable('Build.BuildUri'); - inputDataContract.TfsSpecificSettings.ReleaseId = utils.Helper.isNullEmptyOrUndefined(tl.getVariable('Release.ReleaseId')) ? null : Number(tl.getVariable('Release.ReleaseId')); - inputDataContract.TfsSpecificSettings.ReleaseUri = tl.getVariable('Release.ReleaseUri'); - - // InputDataContract.TargetBinariesSettings - inputDataContract.TargetBinariesSettings = {}; - inputDataContract.TargetBinariesSettings.BuildConfig = this.dtaTestConfig.buildConfig; - inputDataContract.TargetBinariesSettings.BuildPlatform = this.dtaTestConfig.buildPlatform; - - // InputDataContract.ProxySettings - if (!utils.Helper.isNullEmptyOrUndefined(this.dtaTestConfig.proxyConfiguration.proxyUrl)) { - inputDataContract.ProxySettings = {}; - inputDataContract.ProxySettings.ProxyUrl = this.dtaTestConfig.proxyConfiguration.proxyUrl; - inputDataContract.ProxySettings.ProxyUsername = this.dtaTestConfig.proxyConfiguration.proxyUserName; - inputDataContract.ProxySettings.ProxyPassword = this.dtaTestConfig.proxyConfiguration.proxyPassword; - inputDataContract.ProxySettings.ProxyBypassHosts = this.dtaTestConfig.proxyConfiguration.proxyBypassHosts; - } + // let envVars: { [key: string]: string; } = <{ [key: string]: string; }>{}; + let envVars: { [key: string]: string; } = process.env; // This is a temporary solution where we are passing parent process env vars, we should get away from this - // InputDataContract.DistributionSettings - inputDataContract.DistributionSettings = {}; - if (this.dtaTestConfig.batchingType === models.BatchingType.AssemblyBased) { - inputDataContract.DistributionSettings.TestCaseLevelSlicingEnabled = false; - } else { - inputDataContract.DistributionSettings.TestCaseLevelSlicingEnabled = true; - } - tl.debug('Type of batching' + this.dtaTestConfig.batchingType); - inputDataContract.DistributionSettings.NumberOfTestAgents = this.dtaTestConfig.numberOfAgentsInPhase; - const isTimeBasedBatching = (this.dtaTestConfig.batchingType === models.BatchingType.TestExecutionTimeBased); - tl.debug('isTimeBasedBatching : ' + isTimeBasedBatching); - inputDataContract.DistributionSettings.IsTimeBasedSlicing = isTimeBasedBatching; - if (isTimeBasedBatching && this.dtaTestConfig.runningTimePerBatchInMs) { - tl.debug('[RunStatistics] Run Time per batch' + this.dtaTestConfig.runningTimePerBatchInMs); - inputDataContract.DistributionSettings.RunTimePerSlice = utils.Helper.isNullEmptyOrUndefined(this.dtaTestConfig.runningTimePerBatchInMs) ? null : Number(this.dtaTestConfig.runningTimePerBatchInMs); - } - if (this.dtaTestConfig.numberOfTestCasesPerSlice) { - inputDataContract.DistributionSettings.NumberOfTestCasesPerSlice = this.dtaTestConfig.numberOfTestCasesPerSlice; - } + // Overriding temp with agent temp + utils.Helper.addToProcessEnvVars(envVars, 'temp', utils.Helper.GetTempFolder()); - // InputDataContract.ExecutionSettings - inputDataContract.ExecutionSettings = {}; - inputDataContract.ExecutionSettings.VideoDataCollectorEnabled = this.dtaTestConfig.videoCoverageEnabled; - inputDataContract.ExecutionSettings.IsToolsInstallerFlow = utils.Helper.isToolsInstallerFlow(this.dtaTestConfig); - inputDataContract.ExecutionSettings.OverridenParameters = this.dtaTestConfig.overrideTestrunParameters; - inputDataContract.ExecutionSettings.ProceedAfterAbortedTestCase = this.dtaTestConfig.proceedAfterAbortedTestCase; - inputDataContract.ExecutionSettings.SettingsFile = this.dtaTestConfig.settingsFile; - inputDataContract.ExecutionSettings.IgnoreTestFailures = utils.Helper.stringToBool(this.dtaTestConfig.ignoreTestFailures); - inputDataContract.ExecutionSettings.CodeCoverageEnabled = this.dtaTestConfig.codeCoverageEnabled; - if (this.dtaTestConfig.pathtoCustomTestAdapters) { - const testAdapters = tl.findMatch(this.dtaTestConfig.pathtoCustomTestAdapters, '**\\*TestAdapter.dll'); - if (!testAdapters || (testAdapters && testAdapters.length === 0)) { - tl.warning(tl.loc('pathToCustomAdaptersContainsNoAdapters', this.dtaTestConfig.pathtoCustomTestAdapters)); - } - inputDataContract.ExecutionSettings.CustomTestAdapters = this.dtaTestConfig.pathtoCustomTestAdapters; - } + this.inputDataContract.TestSelectionSettings.TestSourcesFile = this.createTestSourcesFile(); - // InputDataContract.ExecutionSettings.TiaSettings - inputDataContract.ExecutionSettings.TiaSettings = {}; - inputDataContract.ExecutionSettings.TiaSettings.Enabled = this.dtaTestConfig.tiaConfig.tiaEnabled; - inputDataContract.ExecutionSettings.TiaSettings.RebaseLimit = utils.Helper.isNullEmptyOrUndefined(this.dtaTestConfig.tiaConfig.tiaRebaseLimit) ? null : Number(this.dtaTestConfig.tiaConfig.tiaRebaseLimit); - inputDataContract.ExecutionSettings.TiaSettings.SourcesDirectory = this.dtaTestConfig.tiaConfig.sourcesDir; - inputDataContract.ExecutionSettings.TiaSettings.FileLevel = utils.Helper.isNullEmptyOrUndefined(this.dtaTestConfig.tiaConfig.fileLevel) || this.dtaTestConfig.tiaConfig.fileLevel.toLowerCase() !== 'false'; - inputDataContract.ExecutionSettings.TiaSettings.FilterPaths = this.dtaTestConfig.tiaConfig.tiaFilterPaths; - inputDataContract.ExecutionSettings.TiaSettings.UserMapFile = this.dtaTestConfig.tiaConfig.userMapFile; - - // InputDataContract.ExecutionSettings.RerunSettings - inputDataContract.ExecutionSettings.RerunSettings = {}; - if (this.dtaTestConfig.rerunFailedTests) { - inputDataContract.ExecutionSettings.RerunSettings.RerunFailedTests = true; - tl.debug('Type of rerun: ' + this.dtaTestConfig.rerunType); - if (this.dtaTestConfig.rerunType === 'basedOnTestFailureCount') { - inputDataContract.ExecutionSettings.RerunSettings.RerunFailedTestCasesMaxLimit = this.dtaTestConfig.rerunFailedTestCasesMaxLimit; - } else { - inputDataContract.ExecutionSettings.RerunSettings.RerunFailedThreshold = this.dtaTestConfig.rerunFailedThreshold; - } - inputDataContract.ExecutionSettings.RerunSettings.RerunMaxAttempts = this.dtaTestConfig.rerunMaxAttempts; + // Temporary solution till this logic can move to the test platform itself + if (this.inputDataContract.UsingXCopyTestPlatformPackage) { + envVars = this.setProfilerVariables(envVars); } - // InputDataContract.TfsSpecificSettings - inputDataContract.TestSpecificSettings = {}; - inputDataContract.TestSpecificSettings.TestCaseAccessToken = tl.getVariable('Test.TestCaseAccessToken'); + // Pass the acess token as an environment variable for security purposes + utils.Helper.addToProcessEnvVars(envVars, 'DTA.AccessToken', this.inputDataContract.AccessToken); + this.inputDataContract.AccessToken = null; - // InputDataContract.Logging - inputDataContract.Logging = {}; - inputDataContract.Logging.EnableConsoleLogs = true; - if (utils.Helper.isDebugEnabled()) { - inputDataContract.Logging.DebugLogging = true; - } + // Invoke DtaExecutionHost with the input json file + const inputFilePath = utils.Helper.GenerateTempFile('input_' + uuid.v1() + '.json'); + DistributedTest.removeEmptyNodes(this.inputDataContract); - // TemporaryInputs - inputDataContract.UseNewCollector = this.dtaTestConfig.tiaConfig.useNewCollector; - inputDataContract.IsPrFlow = utils.Helper.stringToBool(this.dtaTestConfig.tiaConfig.isPrFlow); - inputDataContract.UseTestCaseFilterInResponseFile = utils.Helper.stringToBool(this.dtaTestConfig.tiaConfig.useTestCaseFilterInResponseFile); - inputDataContract.DisableEnablingDataCollector = this.dtaTestConfig.tiaConfig.disableEnablingDataCollector; - inputDataContract.TiaBaseLineBuildIdFile = this.dtaTestConfig.tiaConfig.baseLineBuildIdFile; - inputDataContract.VsVersion = this.dtaTestConfig.vsTestVersionDetails.majorVersion + '.' + this.dtaTestConfig.vsTestVersionDetails.minorversion + '.' + this.dtaTestConfig.vsTestVersionDetails.patchNumber; - inputDataContract.VsVersionIsTestSettingsPropertiesSupported = this.dtaTestConfig.vsTestVersionDetails.isTestSettingsPropertiesSupported(); - inputDataContract.MiniMatchTestSourcesFile = this.testSourcesFile; - inputDataContract.UseVsTestConsole = utils.Helper.stringToBool(this.dtaTestConfig.useVsTestConsole); - inputDataContract.TestPlatformVersion = this.dtaTestConfig.vsTestVersion; - if (utils.Helper.isToolsInstallerFlow(this.dtaTestConfig)) { - inputDataContract.COR_PROFILER_PATH_32 = this.dtaTestConfig.toolsInstallerConfig.x86ProfilerProxyDLLLocation; - inputDataContract.COR_PROFILER_PATH_64 = this.dtaTestConfig.toolsInstallerConfig.x64ProfilerProxyDLLLocation; - inputDataContract.ForcePlatformV2 = true; - } - // If we are setting the path version is not needed - const exelocation = path.dirname(this.dtaTestConfig.vsTestVersionDetails.vstestExeLocation); - tl.debug('Adding env var DTA.TestWindow.Path = ' + exelocation); - const testWindowRelativeDir = 'CommonExtensions\\Microsoft\\TestWindow'; - if (exelocation && exelocation.indexOf(testWindowRelativeDir) !== -1) { - const ideLocation = exelocation.split(testWindowRelativeDir)[0]; - tl.debug('Adding env var DTA.VisualStudio.Path = ' + ideLocation); - inputDataContract.VisualStudioPath = ideLocation; - } else { - inputDataContract.VisualStudioPath = exelocation; - } - inputDataContract.TestWindowPath = exelocation; - - const settingsFile = this.dtaTestConfig.settingsFile; - if (utils.Helper.pathExistsAsFile(settingsFile)) { - tl.debug('Final runsettings file being used:'); - utils.Helper.readFileContents(settingsFile, 'utf-8').then(function (settings) { - tl.debug('Running VsTest with settings : '); - utils.Helper.printMultiLineLog(settings, (logLine) => { console.log('##vso[task.debug]' + logLine); }); - }); + try { + writeFileSync(inputFilePath, JSON.stringify(this.inputDataContract)); + } catch (e) { + tl.setResult(tl.TaskResult.Failed, `Failed to write to the input json file ${inputFilePath} with error ${e}`); } - // Pass the acess token as an environment variable for security purposes - // let envVars: { [key: string]: string; } = <{ [key: string]: string; }>{}; - const envVars: { [key: string]: string; } = process.env; // This is a temporary solutio where we are passing parent process env vars, we should get away from this - utils.Helper.addToProcessEnvVars(envVars, 'DTA.AccessToken', this.dtaTestConfig.dtaEnvironment.patToken); + if (utils.Helper.isDebugEnabled()) { + utils.Helper.uploadFile(inputFilePath); + } - // Invoke DtaExecutionHost with the input json file - const inputFilePath = utils.Helper.GenerateTempFile('input_' + uuid.v1() + '.json'); - DistributedTest.removeEmptyNodes(inputDataContract); - writeFileSync(inputFilePath, JSON.stringify(inputDataContract)); const dtaExecutionHostTool = tl.tool(path.join(__dirname, 'Modules/DTAExecutionHost.exe')); dtaExecutionHostTool.arg(['--inputFile', inputFilePath]); const code = await dtaExecutionHostTool.exec({ env: envVars }); + //hydra: add consolidated ci for inputs in C# layer for now const consolidatedCiData = { - agentFailure: false, - agentPhaseSettings: tl.getVariable('System.ParallelExecutionType'), - batchingType: models.BatchingType[this.dtaTestConfig.batchingType], - batchSize: this.dtaTestConfig.numberOfTestCasesPerSlice, - codeCoverageEnabled: this.dtaTestConfig.codeCoverageEnabled, - dontDistribute: tl.getBoolInput('dontDistribute'), - environmentUri: this.dtaTestConfig.dtaEnvironment.environmentUri, - numberOfAgentsInPhase: this.dtaTestConfig.numberOfAgentsInPhase, - overrideTestrunParameters: utils.Helper.isNullOrUndefined(this.dtaTestConfig.overrideTestrunParameters) ? 'false' : 'true', - pipeline: tl.getVariable('release.releaseUri') != null ? 'release' : 'build', - runTestsInIsolation: this.dtaTestConfig.runTestsInIsolation, - runInParallel: this.dtaTestConfig.runInParallel, - settingsType: !utils.Helper.isNullOrUndefined(this.dtaTestConfig.settingsFile) ? this.dtaTestConfig.settingsFile.endsWith('.runsettings') ? 'runsettings' : this.dtaTestConfig.settingsFile.endsWith('.testsettings') ? 'testsettings' : 'none' : 'none', - task: 'VsTestDistributedFlow', - testSelection: this.dtaTestConfig.testSelection, - tiaEnabled: this.dtaTestConfig.tiaConfig.tiaEnabled, - vsTestVersion: this.dtaTestConfig.vsTestVersionDetails.majorVersion + '.' + this.dtaTestConfig.vsTestVersionDetails.minorversion + '.' + this.dtaTestConfig.vsTestVersionDetails.patchNumber, - rerunEnabled: this.dtaTestConfig.rerunFailedTests, - rerunType: utils.Helper.isNullEmptyOrUndefined(this.dtaTestConfig.rerunType) ? '' : this.dtaTestConfig.rerunType + agentFailure: false }; if (code !== 0) { @@ -263,13 +111,11 @@ export class DistributedTest { } else { tl.debug('Modules/DTAExecutionHost.exe exited'); } - ci.publishEvent(consolidatedCiData); - this.cleanUpDtaExeHost(); return code; } - // Utility function used to remove empty or spurios nodes from the input json file + // Utility function used to remove empty or spurious nodes from the input json file public static removeEmptyNodes(obj: any) { if (obj === null || obj === undefined ) { return; @@ -283,25 +129,21 @@ export class DistributedTest { DistributedTest.removeEmptyNodes(obj[keys[index]]); } if (obj[keys[index]] == undefined || obj[keys[index]] == null || (typeof obj[keys[index]] == "object" && Object.keys(obj[keys[index]]).length == 0)) { + tl.debug(`Removing node ${keys[index]} as its value is ${obj[keys[index]]}.`); delete obj[keys[index]]; } } } - private cleanUpDtaExeHost() { + private createTestSourcesFile(): string { try { - if (this.testSourcesFile) { - tl.rmRF(this.testSourcesFile); + let sourceFilter = tl.getDelimitedInput('testAssemblyVer2', '\n', true); + + if (this.inputDataContract.TestSelectionSettings.TestSelectionType.toLowerCase() !== 'testassemblies') { + sourceFilter = ['**\\*', '!**\\obj\\*']; } - } catch (error) { - //Ignore. - } - this.dtaPid = -1; - } - private createTestSourcesFile(): string { - try { - const sources = tl.findMatch(this.dtaTestConfig.testDropLocation, this.dtaTestConfig.sourceFilter); + const sources = tl.findMatch(this.inputDataContract.TestSelectionSettings.SearchFolder, sourceFilter); tl.debug('tl match count :' + sources.length); const filesMatching = []; sources.forEach(function (match: string) { @@ -312,7 +154,7 @@ export class DistributedTest { tl.debug('Files matching count :' + filesMatching.length); if (filesMatching.length === 0) { - throw new Error(tl.loc('noTestSourcesFound', this.dtaTestConfig.sourceFilter.toString())); + throw new Error(tl.loc('noTestSourcesFound', sourceFilter.toString())); } const tempFile = utils.Helper.GenerateTempFile('testSources_' + uuid.v1() + '.src'); @@ -324,18 +166,41 @@ export class DistributedTest { } } - private async cleanUp(temporarySettingsFile: string) { - //cleanup the runsettings file - if (temporarySettingsFile && this.dtaTestConfig.settingsFile !== temporarySettingsFile) { - try { - tl.rmRF(temporarySettingsFile); - } catch (error) { - //Ignore. + private setProfilerVariables(envVars: { [key: string]: string; }) : { [key: string]: string; } { + const vsTestPackageLocation = tl.getVariable(constants.VsTestToolsInstaller.PathToVsTestToolVariable); + + // get path to Microsoft.IntelliTrace.ProfilerProxy.dll (amd64) + let amd64ProfilerProxy = tl.findMatch(vsTestPackageLocation, '**\\amd64\\Microsoft.IntelliTrace.ProfilerProxy.dll'); + if (amd64ProfilerProxy && amd64ProfilerProxy.length !== 0) { + + envVars['COR_PROFILER_PATH_64'] = amd64ProfilerProxy[0]; + } else { + // Look in x64 also for Microsoft.IntelliTrace.ProfilerProxy.dll (x64) + amd64ProfilerProxy = tl.findMatch(vsTestPackageLocation, '**\\x64\\Microsoft.IntelliTrace.ProfilerProxy.dll'); + if (amd64ProfilerProxy && amd64ProfilerProxy.length !== 0) { + + envVars['COR_PROFILER_PATH_64'] = amd64ProfilerProxy[0]; + } else { + utils.Helper.publishEventToCi(constants.AreaCodes.TOOLSINSTALLERCACHENOTFOUND, tl.loc('testImpactAndCCWontWork'), 1043, false); + tl.warning(tl.loc('testImpactAndCCWontWork')); } + + utils.Helper.publishEventToCi(constants.AreaCodes.TOOLSINSTALLERCACHENOTFOUND, tl.loc('testImpactAndCCWontWork'), 1042, false); + tl.warning(tl.loc('testImpactAndCCWontWork')); + } + + // get path to Microsoft.IntelliTrace.ProfilerProxy.dll (x86) + const x86ProfilerProxy = tl.findMatch(vsTestPackageLocation, '**\\x86\\Microsoft.IntelliTrace.ProfilerProxy.dll'); + if (x86ProfilerProxy && x86ProfilerProxy.length !== 0) { + envVars['COR_PROFILER_PATH_32'] = x86ProfilerProxy[0]; + } else { + utils.Helper.publishEventToCi(constants.AreaCodes.TOOLSINSTALLERCACHENOTFOUND, tl.loc('testImpactAndCCWontWork'), 1044, false); + tl.warning(tl.loc('testImpactAndCCWontWork')); } + + return envVars; } - private dtaTestConfig: models.DtaTestConfigurations; + private inputDataContract: inputdatacontract.InputDataContract; private dtaPid: number; - private testSourcesFile: string; } \ No newline at end of file diff --git a/Tasks/VsTest/helpers.ts b/Tasks/VsTest/helpers.ts index ad5de220c853..82fc81c9d9e7 100644 --- a/Tasks/VsTest/helpers.ts +++ b/Tasks/VsTest/helpers.ts @@ -215,4 +215,16 @@ export class Helper { public static stringToBool(inputString : string) : boolean { return !this.isNullEmptyOrUndefined(inputString) && inputString.toLowerCase() === 'true'; } + + public static uploadFile(file: string): void { + try { + if (Helper.pathExistsAsFile(file)) { + const stats = fs.statSync(file); + tl.debug('File exists. Size: ' + stats.size + ' Bytes'); + console.log('##vso[task.uploadfile]' + file); + } + } catch (err) { + tl.debug(`Failed to upload file ${file} with error ${err}`); + } + } } \ No newline at end of file diff --git a/Tasks/VsTest/inputdatacontract.ts b/Tasks/VsTest/inputdatacontract.ts index 4e627bebf17b..125362833ca0 100644 --- a/Tasks/VsTest/inputdatacontract.ts +++ b/Tasks/VsTest/inputdatacontract.ts @@ -2,9 +2,11 @@ export interface InputDataContract { AgentName : string; AccessToken : string; CollectionUri : string; - EnvironmentUri : string; + RunIdentifier : string; TeamProject : string; TestSelectionSettings : TestSelectionSettings; + VsTestConsolePath : string; + UsingXCopyTestPlatformPackage : boolean; TestReportingSettings : TestReportingSettings; TfsSpecificSettings : TfsSpecificSettings; TargetBinariesSettings : TargetBinariesSettings; @@ -13,27 +15,9 @@ export interface InputDataContract { DistributionSettings : DistributionSettings; ExecutionSettings : ExecutionSettings; Logging : Logging; - UseVsTestConsole : boolean; - TestPlatformVersion : string; - COR_PROFILER_PATH_32 : string; - COR_PROFILER_PATH_64 : string; - ForcePlatformV2 : boolean; - VisualStudioPath : string; - TestWindowPath : string; - TiaRunIdFile : string; - ResponseFile : string; - ResponseSupplementryFilePath : string; TiaBaseLineBuildIdFile : string; - VsVersion : string; - VsVersionIsTestSettingsPropertiesSupported : boolean; - RerunIterationCount : number; - AgentVersion : string; - VstestTaskInstanceIdentifier : string; - MiniMatchTestSourcesFile : string; UseNewCollector : boolean; IsPrFlow : boolean; - UseTestCaseFilterInResponseFile : boolean; - DisableEnablingDataCollector : boolean; } export interface TestReportingSettings { @@ -43,14 +27,10 @@ export interface TestReportingSettings { export interface TestSelectionSettings { TestSelectionType : string; - AssemblyBasedTestSelection : AssemblyBasedTestSelection; TestPlanTestSuiteSettings : TestPlanTestSuiteSettings; SearchFolder : string; TestCaseFilter : string; -} - -export interface AssemblyBasedTestSelection { - SourceFilter : string; + TestSourcesFile : string; } export interface TestPlanTestSuiteSettings { @@ -86,15 +66,15 @@ export interface ProxySettings { export interface RerunSettings { RerunFailedTests : boolean; + RerunType : string; RerunFailedTestCasesMaxLimit : number; RerunFailedThreshold : number; RerunMaxAttempts : number; } export interface DistributionSettings { - TestCaseLevelSlicingEnabled : boolean; + DistributeTestsBasedOn : string; NumberOfTestAgents : number; - IsTimeBasedSlicing : boolean; RunTimePerSlice : number; NumberOfTestCasesPerSlice : number; } @@ -109,14 +89,13 @@ export interface ExecutionSettings { SettingsFile : string; OverridenParameters : string; RerunSettings : RerunSettings; - IsToolsInstallerFlow : boolean; - VstestConsolePath : string; TiaSettings : TiaSettings; VideoDataCollectorEnabled : boolean; } export interface TiaSettings { Enabled : boolean; + DisableDataCollection : boolean; RebaseLimit : number; SourcesDirectory : string; FileLevel : boolean; diff --git a/Tasks/VsTest/inputparser.ts b/Tasks/VsTest/inputparser.ts new file mode 100644 index 000000000000..c5c2d73dfddd --- /dev/null +++ b/Tasks/VsTest/inputparser.ts @@ -0,0 +1,451 @@ +import * as path from 'path'; +import * as tl from 'vsts-task-lib/task'; +import * as tr from 'vsts-task-lib/toolrunner'; +import * as utils from './helpers'; +import * as constants from './constants'; +import * as os from 'os'; +import * as ci from './cieventlogger'; +import { AreaCodes, ResultMessages, DistributionTypes } from './constants'; +import * as idc from './inputdatacontract'; +import * as taskinputparser from './taskinputparser'; +import * as versionfinder from './versionfinder'; +const uuid = require('uuid'); +const regedit = require('regedit'); + +export function getDistributedTestConfigurations() : idc.InputDataContract { + let inputDataContract = {} as idc.InputDataContract; + + inputDataContract = getTestSelectionInputs(inputDataContract); + inputDataContract = getTfsSpecificSettings(inputDataContract); + inputDataContract = getTargetBinariesSettings(inputDataContract); + inputDataContract = getTestReportingSettings(inputDataContract); + inputDataContract = getTestPlatformSettings(inputDataContract); + inputDataContract = getLoggingSettings(inputDataContract); + inputDataContract = getProxySettings(inputDataContract); + inputDataContract = getDistributionSettings(inputDataContract); + inputDataContract = getExecutionSettings(inputDataContract); + + taskinputparser.logWarningForWER(tl.getBoolInput('uiTests')); + + inputDataContract.UseNewCollector = false; + const useNewCollector = tl.getVariable('tia.useNewCollector'); + if (useNewCollector && useNewCollector.toUpperCase() === 'TRUE') { + inputDataContract.UseNewCollector = true; + } + + const buildReason = tl.getVariable('Build.Reason'); + + // https://www.visualstudio.com/en-us/docs/build/define/variables + // PullRequest -> This is the case for TfsGit PR flow + // CheckInShelveset -> This is the case for TFVC Gated Checkin + if (buildReason && (buildReason === 'PullRequest' || buildReason === 'CheckInShelveset')) { + // hydra: Should this become a first class input or should we identify if it is a pr flow from the managed layer? First class input + inputDataContract.IsPrFlow = true; + } else { + inputDataContract.IsPrFlow = utils.Helper.stringToBool(tl.getVariable('tia.isPrFlow')); + } + + inputDataContract.TeamProject = tl.getVariable('System.TeamProject'); + inputDataContract.CollectionUri = tl.getVariable('System.TeamFoundationCollectionUri'); + inputDataContract.AccessToken = tl.getEndpointAuthorization('SystemVssConnection', true).parameters['AccessToken']; + inputDataContract.AgentName = tl.getVariable('Agent.MachineName') + '-' + tl.getVariable('Agent.Name') + '-' + tl.getVariable('Agent.Id'); + inputDataContract.RunIdentifier = getRunIdentifier(); + + return inputDataContract; +} + +function getTestSelectionInputs(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + inputDataContract.TestSelectionSettings = {}; + inputDataContract.TestSelectionSettings.TestSelectionType = tl.getInput('testSelector').toLowerCase(); + switch (inputDataContract.TestSelectionSettings.TestSelectionType) { + + case 'testplan': + inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings = {}; + console.log(tl.loc('testSelectorInput', tl.loc('testPlanSelector'))); + + inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.Testplan = parseInt(tl.getInput('testPlan')); + console.log(tl.loc('testPlanInput', inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.Testplan)); + + inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.TestPlanConfigId = parseInt(tl.getInput('testConfiguration')); + console.log(tl.loc('testplanConfigInput', inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.TestPlanConfigId)); + + const testSuiteStrings = tl.getDelimitedInput('testSuite', ',', true); + inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.TestSuites = new Array(); + testSuiteStrings.forEach(element => { + const testSuiteId = parseInt(element); + console.log(tl.loc('testSuiteSelected', testSuiteId)); + inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.TestSuites.push(testSuiteId); + }); + + break; + + case 'testassemblies': + console.log(tl.loc('testSelectorInput', tl.loc('testAssembliesSelector'))); + + inputDataContract.TestSelectionSettings.TestCaseFilter = tl.getInput('testFiltercriteria'); + console.log(tl.loc('testFilterCriteriaInput', inputDataContract.TestSelectionSettings.TestCaseFilter)); + break; + + case 'testrun': + inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings = {}; + + console.log(tl.loc('testSelectorInput', tl.loc('testRunSelector'))); + inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.OnDemandTestRunId = parseInt(tl.getInput('tcmTestRun')); + + if (inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.OnDemandTestRunId <= 0) { + throw new Error(tl.loc('testRunIdInvalid', inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.OnDemandTestRunId)); + } + console.log(tl.loc('testRunIdInput', inputDataContract.TestSelectionSettings.TestPlanTestSuiteSettings.OnDemandTestRunId)); + + break; + } + + inputDataContract.TestSelectionSettings.SearchFolder = tl.getInput('searchFolder'); + if (!utils.Helper.isNullOrWhitespace(inputDataContract.TestSelectionSettings.SearchFolder)) { + inputDataContract.TestSelectionSettings.SearchFolder = path.resolve(inputDataContract.TestSelectionSettings.SearchFolder); + } + + if (inputDataContract.TestSelectionSettings.SearchFolder && !utils.Helper.pathExistsAsDirectory(inputDataContract.TestSelectionSettings.SearchFolder)) { + throw new Error(tl.loc('searchLocationNotDirectory', inputDataContract.TestSelectionSettings.SearchFolder)); + } + console.log(tl.loc('searchFolderInput', inputDataContract.TestSelectionSettings.SearchFolder)); + + return inputDataContract; +} + +function getTfsSpecificSettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + inputDataContract.TfsSpecificSettings = {}; + inputDataContract.TfsSpecificSettings.BuildId = utils.Helper.isNullEmptyOrUndefined(tl.getVariable('Build.Buildid')) ? null : Number(tl.getVariable('Build.Buildid')); + inputDataContract.TfsSpecificSettings.BuildUri = tl.getVariable('Build.BuildUri'); + inputDataContract.TfsSpecificSettings.ReleaseId = utils.Helper.isNullEmptyOrUndefined(tl.getVariable('Release.ReleaseId')) ? null : Number(tl.getVariable('Release.ReleaseId')); + inputDataContract.TfsSpecificSettings.ReleaseUri = tl.getVariable('Release.ReleaseUri'); + return inputDataContract; +} + +function getTargetBinariesSettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + inputDataContract.TargetBinariesSettings = {}; + inputDataContract.TargetBinariesSettings.BuildConfig = tl.getInput('configuration'); + inputDataContract.TargetBinariesSettings.BuildPlatform = tl.getInput('platform'); + return inputDataContract; +} + +function getTestReportingSettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + inputDataContract.TestReportingSettings = {}; + inputDataContract.TestReportingSettings.TestRunTitle = tl.getInput('testRunTitle'); + + if (utils.Helper.isNullEmptyOrUndefined(inputDataContract.TestReportingSettings.TestRunTitle)) { + + let definitionName = tl.getVariable('BUILD_DEFINITIONNAME'); + let buildOrReleaseName = tl.getVariable('BUILD_BUILDNUMBER'); + + if (inputDataContract.TfsSpecificSettings.ReleaseUri) { + definitionName = tl.getVariable('RELEASE_DEFINITIONNAME'); + buildOrReleaseName = tl.getVariable('RELEASE_RELEASENAME'); + } + + inputDataContract.TestReportingSettings.TestRunTitle = `TestRun_${definitionName}_${buildOrReleaseName}`; + } + + return inputDataContract; +} + +function getTestPlatformSettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + const vsTestLocationMethod = tl.getInput('vstestLocationMethod'); + if (vsTestLocationMethod === utils.Constants.vsTestVersionString) { + const vsTestVersion = tl.getInput('vsTestVersion'); + if (utils.Helper.isNullEmptyOrUndefined(vsTestVersion)) { + console.log(tl.loc('VsTestVersionEmpty')); + throw new Error(tl.loc('VsTestVersionEmpty')); + } else if (vsTestVersion.toLowerCase() === 'toolsinstaller') { + tl.debug('Trying VsTest installed by tools installer.'); + ci.publishEvent({ subFeature: 'ToolsInstallerSelected', isToolsInstallerPackageLocationSet: !utils.Helper.isNullEmptyOrUndefined(tl.getVariable(constants.VsTestToolsInstaller.PathToVsTestToolVariable)) }); + + inputDataContract.UsingXCopyTestPlatformPackage = true; + + const vsTestPackageLocation = tl.getVariable(constants.VsTestToolsInstaller.PathToVsTestToolVariable); + tl.debug('Path to VsTest from tools installer: ' + vsTestPackageLocation); + + // get path to vstest.console.exe + const matches = tl.findMatch(vsTestPackageLocation, '**\\vstest.console.exe'); + if (matches && matches.length !== 0) { + inputDataContract.VsTestConsolePath = path.dirname(matches[0]); + } else { + utils.Helper.publishEventToCi(AreaCodes.TOOLSINSTALLERCACHENOTFOUND, tl.loc('toolsInstallerPathNotSet'), 1041, false); + throw new Error(tl.loc('toolsInstallerPathNotSet')); + } + + // if Tools installer is not there throw. + if (utils.Helper.isNullOrWhitespace(inputDataContract.VsTestConsolePath)) { + ci.publishEvent({ subFeature: 'ToolsInstallerInstallationError' }); + utils.Helper.publishEventToCi(AreaCodes.SPECIFIEDVSVERSIONNOTFOUND, 'Tools installer task did not complete successfully.', 1040, true); + throw new Error(tl.loc('ToolsInstallerInstallationError')); + } + + ci.publishEvent({ subFeature: 'ToolsInstallerInstallationSuccessful' }); + + } else if ((vsTestVersion !== '15.0') && (vsTestVersion !== '14.0') + && (vsTestVersion.toLowerCase() !== 'latest')) { + throw new Error(tl.loc('vstestVersionInvalid', vsTestVersion)); + } else if (vsTestLocationMethod === utils.Constants.vsTestVersionString && vsTestVersion === '12.0') { + throw (tl.loc('vs2013NotSupportedInDta')); + } else { + console.log(tl.loc('vsVersionSelected', vsTestVersion)); + inputDataContract.VsTestConsolePath = getTestPlatformPath(inputDataContract); + } + } else { + // hydra: should it be full path or directory above? + inputDataContract.VsTestConsolePath = tl.getInput('vsTestLocation'); + console.log(tl.loc('vstestLocationSpecified', 'vstest.console.exe', inputDataContract.VsTestConsolePath)); + if (inputDataContract.VsTestConsolePath.endsWith('vstest.console.exe')) { + inputDataContract.VsTestConsolePath = path.dirname(inputDataContract.VsTestConsolePath); + } + } + + return inputDataContract; +} + +function getLoggingSettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + // InputDataContract.Logging + inputDataContract.Logging = {}; + inputDataContract.Logging.EnableConsoleLogs = true; + if (utils.Helper.isDebugEnabled()) { + inputDataContract.Logging.DebugLogging = true; + } + return inputDataContract; +} + +function getProxySettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + // Get proxy details + inputDataContract.ProxySettings = {}; + inputDataContract.ProxySettings.ProxyUrl = tl.getVariable('agent.proxyurl'); + inputDataContract.ProxySettings.ProxyUsername = tl.getVariable('agent.proxyusername'); + inputDataContract.ProxySettings.ProxyPassword = tl.getVariable('agent.proxypassword'); + inputDataContract.ProxySettings.ProxyBypassHosts = tl.getVariable('agent.proxybypasslist'); + return inputDataContract; +} + +function getDistributionSettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + inputDataContract.DistributionSettings = {}; + inputDataContract.DistributionSettings.NumberOfTestAgents = 1; + const totalJobsInPhase = parseInt(tl.getVariable('SYSTEM_TOTALJOBSINPHASE')); + if (!isNaN(totalJobsInPhase)) { + inputDataContract.DistributionSettings.NumberOfTestAgents = totalJobsInPhase; + } + console.log(tl.loc('dtaNumberOfAgents', inputDataContract.DistributionSettings.NumberOfTestAgents)); + + const distributionType = tl.getInput('distributionBatchType'); + + if (distributionType && distributionType === 'basedOnTestCases') { + inputDataContract.DistributionSettings.DistributeTestsBasedOn = DistributionTypes.NUMBEROFTESTMETHODSBASED; + // flow if the batch type = based on agents/custom batching + const distributeByAgentsOption = tl.getInput('batchingBasedOnAgentsOption'); + if (distributeByAgentsOption && distributeByAgentsOption === 'customBatchSize') { + const batchSize = parseInt(tl.getInput('customBatchSizeValue')); + if (!isNaN(batchSize) && batchSize > 0) { + inputDataContract.DistributionSettings.NumberOfTestCasesPerSlice = batchSize; + console.log(tl.loc('numberOfTestCasesPerSlice', inputDataContract.DistributionSettings.NumberOfTestCasesPerSlice)); + } else { + throw new Error(tl.loc('invalidTestBatchSize', batchSize)); + } + } + // by default we set the distribution = number of agents + } else if (distributionType && distributionType === 'basedOnExecutionTime') { + inputDataContract.DistributionSettings.DistributeTestsBasedOn = DistributionTypes.EXECUTIONTIMEBASED; + // flow if the batch type = based on agents/custom batching + const batchBasedOnExecutionTimeOption = tl.getInput('batchingBasedOnExecutionTimeOption'); + if (batchBasedOnExecutionTimeOption && batchBasedOnExecutionTimeOption === 'customTimeBatchSize') { + const batchExecutionTimeInSec = parseInt(tl.getInput('customRunTimePerBatchValue')); + if (isNaN(batchExecutionTimeInSec) || batchExecutionTimeInSec <= 0) { + throw new Error(tl.loc('invalidRunTimePerBatch', batchExecutionTimeInSec)); + } + inputDataContract.DistributionSettings.RunTimePerSlice = batchExecutionTimeInSec; + console.log(tl.loc('RunTimePerBatch', inputDataContract.DistributionSettings.RunTimePerSlice)); + } + } else if (distributionType && distributionType === 'basedOnAssembly') { + inputDataContract.DistributionSettings.DistributeTestsBasedOn = DistributionTypes.ASSEMBLYBASED; + } + return inputDataContract; +} + +function getExecutionSettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + inputDataContract.ExecutionSettings = {}; + + inputDataContract.ExecutionSettings.SettingsFile = tl.getPathInput('runSettingsFile'); + if (!utils.Helper.isNullOrWhitespace(inputDataContract.ExecutionSettings.SettingsFile)) { + inputDataContract.ExecutionSettings.SettingsFile = path.resolve(inputDataContract.ExecutionSettings.SettingsFile); + } + if (inputDataContract.ExecutionSettings.SettingsFile === tl.getVariable('System.DefaultWorkingDirectory')) { + delete inputDataContract.ExecutionSettings.SettingsFile; + } + console.log(tl.loc('runSettingsFileInput', inputDataContract.ExecutionSettings.SettingsFile)); + + inputDataContract.ExecutionSettings.OverridenParameters = tl.getInput('overrideTestrunParameters'); + + inputDataContract.ExecutionSettings.AssemblyLevelParallelism = tl.getBoolInput('runInParallel'); + console.log(tl.loc('runInParallelInput', inputDataContract.ExecutionSettings.AssemblyLevelParallelism)); + + // hydra: do we want to shoot this warning? + if (tl.getBoolInput('runTestsInIsolation')) { + tl.warning(tl.loc('runTestInIsolationNotSupported')); + } + + inputDataContract.ExecutionSettings.CustomTestAdapters = tl.getInput('pathtoCustomTestAdapters'); + if (!utils.Helper.isNullOrWhitespace(inputDataContract.ExecutionSettings.CustomTestAdapters)) { + inputDataContract.ExecutionSettings.CustomTestAdapters = path.resolve(inputDataContract.ExecutionSettings.CustomTestAdapters); + } + if (inputDataContract.ExecutionSettings.CustomTestAdapters && + !utils.Helper.pathExistsAsDirectory(inputDataContract.ExecutionSettings.CustomTestAdapters)) { + throw new Error(tl.loc('pathToCustomAdaptersInvalid', inputDataContract.ExecutionSettings.CustomTestAdapters)); + } + console.log(tl.loc('pathToCustomAdaptersInput', inputDataContract.ExecutionSettings.CustomTestAdapters)); + + inputDataContract.ExecutionSettings.IgnoreTestFailures = utils.Helper.stringToBool(tl.getVariable('vstest.ignoretestfailures')); + + inputDataContract.ExecutionSettings.ProceedAfterAbortedTestCase = false; + if (tl.getVariable('ProceedAfterAbortedTestCase') && tl.getVariable('ProceedAfterAbortedTestCase').toUpperCase() === 'TRUE') { + inputDataContract.ExecutionSettings.ProceedAfterAbortedTestCase = true; + } + tl.debug('ProceedAfterAbortedTestCase is set to : ' + inputDataContract.ExecutionSettings.ProceedAfterAbortedTestCase); + + // hydra: Maybe move all warnings to a diff function + if (tl.getBoolInput('uiTests') && inputDataContract.ExecutionSettings.AssemblyLevelParallelism) { + tl.warning(tl.loc('uitestsparallel')); + } + + if (tl.getInput('otherConsoleOptions')) { + tl.warning(tl.loc('otherConsoleOptionsNotSupported')); + } + + inputDataContract.ExecutionSettings.CodeCoverageEnabled = tl.getBoolInput('codeCoverageEnabled'); + console.log(tl.loc('codeCoverageInput', inputDataContract.ExecutionSettings.CodeCoverageEnabled)); + + inputDataContract = getTiaSettings(inputDataContract); + inputDataContract = getRerunSettings(inputDataContract); + + return inputDataContract; +} + +function getTiaSettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + // TIA stuff + if (tl.getBoolInput('runOnlyImpactedTests') === false) { + return inputDataContract; + } + + inputDataContract.ExecutionSettings.TiaSettings = {}; + inputDataContract.ExecutionSettings.TiaSettings.Enabled = tl.getBoolInput('runOnlyImpactedTests'); + inputDataContract.ExecutionSettings.TiaSettings.RebaseLimit = +tl.getInput('runAllTestsAfterXBuilds'); + inputDataContract.ExecutionSettings.TiaSettings.FileLevel = getTIALevel(tl.getVariable('tia.filelevel')); + inputDataContract.ExecutionSettings.TiaSettings.SourcesDirectory = tl.getVariable('build.sourcesdirectory'); + inputDataContract.ExecutionSettings.TiaSettings.FilterPaths = tl.getVariable('TIA_IncludePathFilters'); + inputDataContract.TiaBaseLineBuildIdFile = path.join(os.tmpdir(), uuid.v1() + '.txt'); + + // User map file + inputDataContract.ExecutionSettings.TiaSettings.UserMapFile = tl.getVariable('tia.usermapfile'); + + // disable editing settings file to switch on data collector + if (tl.getVariable('tia.disabletiadatacollector') && tl.getVariable('tia.disabletiadatacollector').toUpperCase() === 'TRUE') { + inputDataContract.ExecutionSettings.TiaSettings.DisableDataCollection = true; + } else { + inputDataContract.ExecutionSettings.TiaSettings.DisableDataCollection = false; + } + return inputDataContract; +} + +function getRerunSettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { + // Rerun settings + if (tl.getBoolInput('rerunFailedTests') === false) { + return inputDataContract; + } + + inputDataContract.ExecutionSettings.RerunSettings = {}; + inputDataContract.ExecutionSettings.RerunSettings.RerunFailedTests = tl.getBoolInput('rerunFailedTests'); + console.log(tl.loc('rerunFailedTests', inputDataContract.ExecutionSettings.RerunSettings.RerunFailedTests)); + const rerunType = tl.getInput('rerunType') || 'basedOnTestFailurePercentage'; + inputDataContract.ExecutionSettings.RerunSettings.RerunType = rerunType; + + if (rerunType === 'basedOnTestFailureCount') { + const rerunFailedTestCasesMaxLimit = parseInt(tl.getInput('rerunFailedTestCasesMaxLimit')); + if (!isNaN(rerunFailedTestCasesMaxLimit)) { + inputDataContract.ExecutionSettings.RerunSettings.RerunFailedTestCasesMaxLimit = rerunFailedTestCasesMaxLimit; + console.log(tl.loc('rerunFailedTestCasesMaxLimit', inputDataContract.ExecutionSettings.RerunSettings.RerunFailedTestCasesMaxLimit)); + } else { + tl.warning(tl.loc('invalidRerunFailedTestCasesMaxLimit')); + } + } else { + const rerunFailedThreshold = parseInt(tl.getInput('rerunFailedThreshold')); + if (!isNaN(rerunFailedThreshold)) { + inputDataContract.ExecutionSettings.RerunSettings.RerunFailedThreshold = rerunFailedThreshold; + console.log(tl.loc('rerunFailedThreshold', inputDataContract.ExecutionSettings.RerunSettings.RerunFailedThreshold)); + } else { + tl.warning(tl.loc('invalidRerunFailedThreshold')); + } + } + + const rerunMaxAttempts = parseInt(tl.getInput('rerunMaxAttempts')); + if (!isNaN(rerunMaxAttempts)) { + inputDataContract.ExecutionSettings.RerunSettings.RerunMaxAttempts = rerunMaxAttempts; + console.log(tl.loc('rerunMaxAttempts', inputDataContract.ExecutionSettings.RerunSettings.RerunMaxAttempts)); + } else { + tl.warning(tl.loc('invalidRerunMaxAttempts')); + } + return inputDataContract; +} + +function getRunIdentifier(): string { + let runIdentifier: string = ''; + const taskInstanceId = taskinputparser.getDtaInstanceId(); + const dontDistribute = tl.getBoolInput('dontDistribute'); + const releaseId = tl.getVariable('Release.ReleaseId'); + const jobId = tl.getVariable('System.JobPositionInPhase'); + const parallelExecution = tl.getVariable('System.ParallelExecutionType'); + const phaseId = utils.Helper.isNullEmptyOrUndefined(releaseId) ? + tl.getVariable('System.PhaseId') : tl.getVariable('Release.DeployPhaseId'); + if ((!utils.Helper.isNullEmptyOrUndefined(parallelExecution) && parallelExecution.toLowerCase() === 'multiconfiguration') + || dontDistribute) { + runIdentifier = `${phaseId}/${jobId}/${taskInstanceId}`; + } else { + runIdentifier = `${phaseId}/${taskInstanceId}`; + } + + return runIdentifier; +} + +// hydra: rename function and maybe refactor and add logic inline +function getTIALevel(fileLevel: string) { + if (fileLevel && fileLevel.toUpperCase() === 'FALSE') { + return false; + } + return true; +} + +function getTestPlatformPath(inputDataContract : idc.InputDataContract) { + let vsTestVersion = tl.getInput('vsTestVersion'); + if (vsTestVersion.toLowerCase() === 'latest') { + // latest + tl.debug('Searching for latest Visual Studio'); + const vstestconsole15Path = versionfinder.getVSTestConsole15Path(); + if (vstestconsole15Path) { + vsTestVersion = '15.0'; + return vstestconsole15Path; + } + + // fallback + tl.debug('Unable to find an instance of Visual Studio 2017..'); + tl.debug('Searching for Visual Studio 2015..'); + vsTestVersion = '14.0'; + return versionfinder.getVSTestLocation(14); + } + + const vsVersion: number = parseFloat(vsTestVersion); + + if (vsVersion === 15.0) { + const vstestconsole15Path = versionfinder.getVSTestConsole15Path(); + if (vstestconsole15Path) { + return vstestconsole15Path; + } + throw (new Error(tl.loc('VstestNotFound', utils.Helper.getVSVersion(vsVersion)))); + } + + tl.debug('Searching for Visual Studio ' + vsVersion.toString()); + return versionfinder.getVSTestLocation(vsVersion); +} \ No newline at end of file diff --git a/Tasks/VsTest/make.json b/Tasks/VsTest/make.json index a3bccf77dd55..191fdcdb1fe9 100644 --- a/Tasks/VsTest/make.json +++ b/Tasks/VsTest/make.json @@ -2,11 +2,11 @@ "externals": { "archivePackages": [ { - "url": "https://testselectorv2.blob.core.windows.net/testselector/5802917/TestSelector.zip", + "url": "https://testselectorv2.blob.core.windows.net/testselector/6067676/TestSelector.zip", "dest": "./" }, { - "url": "https://testexecution.blob.core.windows.net/testexecution/6035132/TestAgent.zip", + "url": "https://testexecution.blob.core.windows.net/testexecution/6067676/TestAgent.zip", "dest": "./Modules" }, { diff --git a/Tasks/VsTest/runvstest.ts b/Tasks/VsTest/runvstest.ts index 8f025b7306d7..af338d11c1ad 100644 --- a/Tasks/VsTest/runvstest.ts +++ b/Tasks/VsTest/runvstest.ts @@ -6,6 +6,7 @@ import * as path from 'path'; import * as distributedTest from './distributedtest'; import * as ci from './cieventlogger'; import * as utils from './helpers'; +import * as inputParser from './inputparser'; import * as os from 'os'; const osPlat: string = os.platform(); @@ -34,10 +35,10 @@ if (osPlat !== 'win32') { console.log(tl.loc('distributedTestWorkflow')); console.log('======================================================'); - const dtaTestConfig = taskInputParser.getDistributedTestConfigurations(); + const inputDataContract = inputParser.getDistributedTestConfigurations(); console.log('======================================================'); - const test = new distributedTest.DistributedTest(dtaTestConfig); + const test = new distributedTest.DistributedTest(inputDataContract); test.runDistributedTest(); } else { ci.publishEvent({ runmode: 'vstest' }); diff --git a/Tasks/VsTest/settingshelper.ts b/Tasks/VsTest/settingshelper.ts index bde248a7565a..ad89ac51e31f 100644 --- a/Tasks/VsTest/settingshelper.ts +++ b/Tasks/VsTest/settingshelper.ts @@ -55,7 +55,7 @@ const runSettingsTemplate = ` { const defer = Q.defer(); let result: any; - + if (!isParallelRun && !videoCollector && !tiaConfig.tiaEnabled && !overrideParametersString && !codeCoverageToolsInstallerFlow) { defer.resolve(settingsFile); return defer.promise; @@ -82,7 +82,7 @@ export async function updateSettingsFileAsRequired(settingsFile: string, isParal if (settingsExt === testSettingsExtension && result.TestSettings && result.TestSettings.Properties && result.TestSettings.Properties[0] && result.TestSettings.Properties[0].Property && vsVersion && !vsVersion.isTestSettingsPropertiesSupported()) { - tl.warning(tl.loc('testSettingPropertiesNotSupported')) + tl.warning(tl.loc('testSettingPropertiesNotSupported')); } if (overrideParametersString) { @@ -142,7 +142,7 @@ export async function updateSettingsFileAsRequired(settingsFile: string, isParal if (tiaConfig.useNewCollector) { testImpactCollectorNode[0].DataCollector[0].$.codebase = getTraceCollectorUri(vsVersion.majorVersion); } - + testImpactCollectorNode[0].DataCollector[0].Configuration[0].ImpactLevel = getTIALevel(tiaConfig); testImpactCollectorNode[0].DataCollector[0].Configuration[0].LogFilePath = 'true'; @@ -154,13 +154,13 @@ export async function updateSettingsFileAsRequired(settingsFile: string, isParal }); if (settingsExt === testSettingsExtension) { - tl.debug('Enabling Test Impact collector by editing given testsettings.') + tl.debug('Enabling Test Impact collector by editing given testsettings.'); result = updateTestSettingsWithDataCollector(result, testImpactFriendlyName, testImpactCollectorNode); } else if (settingsExt === runSettingsExtension) { - tl.debug('Enabling Test Impact collector by editing given runsettings.') + tl.debug('Enabling Test Impact collector by editing given runsettings.'); result = updateRunSettingsWithDataCollector(result, testImpactFriendlyName, testImpactCollectorNode); } else { - tl.debug('Enabling test impact data collection by creating new runsettings.') + tl.debug('Enabling test impact data collection by creating new runsettings.'); settingsExt = runSettingsExtension; result = await CreateSettings(runSettingsTemplate); result = updateRunSettingsWithDataCollector(result, testImpactFriendlyName, testImpactCollectorNode); @@ -204,7 +204,7 @@ export async function updateSettingsFileAsRequired(settingsFile: string, isParal tl.debug('Enabling code coverage by creating new run settings.'); settingsExt = runSettingsExtension; result = await CreateSettings(runSettingsTemplate); - result = updateRunSettingsWithCodeCoverageDetails(result, codeCoverageNode, settingsFile) + result = updateRunSettingsWithCodeCoverageDetails(result, codeCoverageNode, settingsFile); tl.debug('Successfully added code coverage settings details to runsettings file.'); } } @@ -232,8 +232,8 @@ function updateRunSettingsWithCodeCoverageDetails(result: any, codeCoverageNode: tl.debug('Updating runsettings file from DataCollectors node'); result.RunSettings.DataCollectionRunSettings[0] = { DataCollectors: codeCoverageNode }; } else { - var dataCollectorArray; - dataCollectorArray = result.RunSettings.DataCollectionRunSettings[0].DataCollectors[0].DataCollector; + var dataCollectorArray; + dataCollectorArray = result.RunSettings.DataCollectionRunSettings[0].DataCollectors[0].DataCollector; if (!dataCollectorArray) { tl.debug('Updating runsettings file from DataCollectors node'); result.RunSettings.DataCollectionRunSettings[0] = { DataCollectors: codeCoverageNode }; diff --git a/Tasks/VsTest/task.json b/Tasks/VsTest/task.json index 0ed1205617b4..69719b13cf50 100644 --- a/Tasks/VsTest/task.json +++ b/Tasks/VsTest/task.json @@ -16,8 +16,8 @@ "author": "Microsoft Corporation", "version": { "Major": 2, - "Minor": 5, - "Patch": 13 + "Minor": 135, + "Patch": 0 }, "demands": [ "vstest" @@ -580,6 +580,7 @@ "OnlyWindowsOsSupported": "This task is supported only on Windows agents and cannot be used on other platforms.", "MultiConfigNotSupportedWithOnDemand": "On demand runs are not supported with Multi-Configuration option. Please use 'None' or 'Multi-agent' parallelism option.", "disabledRerun": "Disabling the rerun of failed tests as the rerun threshold provided is %s", - "UpgradeAgentMessage": "Please upgrade your vsts-agent version. https://github.com/Microsoft/vsts-agent/releases" + "UpgradeAgentMessage": "Please upgrade your vsts-agent version. https://github.com/Microsoft/vsts-agent/releases", + "VsTestVersionEmpty": "VsTestVersion is null or empty" } } diff --git a/Tasks/VsTest/task.loc.json b/Tasks/VsTest/task.loc.json index 023363ad61be..485c3ef3ef8e 100644 --- a/Tasks/VsTest/task.loc.json +++ b/Tasks/VsTest/task.loc.json @@ -16,8 +16,8 @@ "author": "Microsoft Corporation", "version": { "Major": 2, - "Minor": 5, - "Patch": 13 + "Minor": 135, + "Patch": 0 }, "demands": [ "vstest" @@ -580,6 +580,7 @@ "OnlyWindowsOsSupported": "ms-resource:loc.messages.OnlyWindowsOsSupported", "MultiConfigNotSupportedWithOnDemand": "ms-resource:loc.messages.MultiConfigNotSupportedWithOnDemand", "disabledRerun": "ms-resource:loc.messages.disabledRerun", - "UpgradeAgentMessage": "ms-resource:loc.messages.UpgradeAgentMessage" + "UpgradeAgentMessage": "ms-resource:loc.messages.UpgradeAgentMessage", + "VsTestVersionEmpty": "ms-resource:loc.messages.VsTestVersionEmpty" } } diff --git a/Tasks/VsTest/taskinputparser.ts b/Tasks/VsTest/taskinputparser.ts index 8f2dfed1e86c..fe0758924215 100644 --- a/Tasks/VsTest/taskinputparser.ts +++ b/Tasks/VsTest/taskinputparser.ts @@ -9,58 +9,10 @@ import * as os from 'os'; import * as ci from './cieventlogger'; import * as versionFinder from './versionfinder'; import { AreaCodes, ResultMessages } from './constants'; +import * as inputdatacontract from './inputdatacontract'; const uuid = require('uuid'); const regedit = require('regedit'); -export function getDistributedTestConfigurations() { - const dtaConfiguration = {} as models.DtaTestConfigurations; - initTestConfigurations(dtaConfiguration); - dtaConfiguration.useVsTestConsole = 'false'; - - if (dtaConfiguration.vsTestLocationMethod === utils.Constants.vsTestVersionString && dtaConfiguration.vsTestVersion === '12.0') { - throw (tl.loc('vs2013NotSupportedInDta')); - } - - if (dtaConfiguration.tiaConfig.tiaEnabled) { - dtaConfiguration.tiaConfig = getTiaConfiguration(); - } - if (dtaConfiguration.runTestsInIsolation) { - tl.warning(tl.loc('runTestInIsolationNotSupported')); - } - if (dtaConfiguration.otherConsoleOptions) { - tl.warning(tl.loc('otherConsoleOptionsNotSupported')); - } - - dtaConfiguration.numberOfAgentsInPhase = 1; - const totalJobsInPhase = parseInt(tl.getVariable('SYSTEM_TOTALJOBSINPHASE')); - if (!isNaN(totalJobsInPhase)) { - dtaConfiguration.numberOfAgentsInPhase = totalJobsInPhase; - } - console.log(tl.loc('dtaNumberOfAgents', dtaConfiguration.numberOfAgentsInPhase)); - - getDistributionBatchSize(dtaConfiguration); - - let useVsTestConsole = tl.getVariable('UseVsTestConsole'); - if (useVsTestConsole) { - dtaConfiguration.useVsTestConsole = useVsTestConsole; - } - - // VsTest Console cannot be used for Dev14 - if (dtaConfiguration.useVsTestConsole.toUpperCase() === 'TRUE' && dtaConfiguration.vsTestVersion !== '15.0') { - console.log(tl.loc('noVstestConsole')); - dtaConfiguration.useVsTestConsole = 'false'; - } - - dtaConfiguration.proceedAfterAbortedTestCase = false; - if (tl.getVariable('ProceedAfterAbortedTestCase') && tl.getVariable('ProceedAfterAbortedTestCase').toUpperCase() === 'TRUE') { - dtaConfiguration.proceedAfterAbortedTestCase = true; - } - tl.debug('ProceedAfterAbortedTestCase is set to : ' + dtaConfiguration.proceedAfterAbortedTestCase); - - dtaConfiguration.dtaEnvironment = initDtaEnvironment(); - return dtaConfiguration; -} - export function getvsTestConfigurations() { const vsTestConfiguration = {} as models.VsTestConfigurations; initTestConfigurations(vsTestConfiguration); @@ -109,7 +61,7 @@ function getEnvironmentUri(): string { return environmentUri; } -function getDtaInstanceId(): number { +export function getDtaInstanceId(): number { const taskInstanceIdString = tl.getVariable('DTA_INSTANCE_ID'); let taskInstanceId: number = 1; if (taskInstanceIdString) { @@ -192,7 +144,7 @@ function initTestConfigurations(testConfiguration: models.TestConfigurations) { throw new Error('vsTestVersion is null or empty'); } if (testConfiguration.vsTestVersion.toLowerCase() === 'toolsinstaller') { - tl.debug("Trying VsTest installed by tools installer."); + tl.debug('Trying VsTest installed by tools installer.'); ci.publishEvent({ subFeature: 'ToolsInstallerSelected', isToolsInstallerPackageLocationSet: !utils.Helper.isNullEmptyOrUndefined(tl.getVariable(constants.VsTestToolsInstaller.PathToVsTestToolVariable)) }); testConfiguration.toolsInstallerConfig = getToolsInstallerConfiguration(); @@ -210,8 +162,7 @@ function initTestConfigurations(testConfiguration: models.TestConfigurations) { testConfiguration.vsTestLocation = testConfiguration.toolsInstallerConfig.vsTestConsolePathFromPackageLocation; testConfiguration.toolsInstallerConfig.isToolsInstallerInUse = true; - } - else if ((testConfiguration.vsTestVersion !== '15.0') && (testConfiguration.vsTestVersion !== '14.0') + } else if ((testConfiguration.vsTestVersion !== '15.0') && (testConfiguration.vsTestVersion !== '14.0') && (testConfiguration.vsTestVersion.toLowerCase() !== 'latest')) { throw new Error(tl.loc('vstestVersionInvalid', testConfiguration.vsTestVersion)); } @@ -293,7 +244,7 @@ function getProxyConfiguration(): models.ProxyConfiguration { return proxyConfiguration; } -async function logWarningForWER(runUITests: boolean) { +export async function logWarningForWER(runUITests: boolean) { if (!runUITests) { return; } @@ -309,7 +260,7 @@ async function logWarningForWER(runUITests: boolean) { } } -function isDontShowUIRegKeySet(regPath: string): Q.Promise { +export function isDontShowUIRegKeySet(regPath: string): Q.Promise { const defer = Q.defer(); const regValue = 'DontShowUI'; regedit.list(regPath).on('data', (entry) => { @@ -382,20 +333,19 @@ function getTiaConfiguration(): models.TiaConfiguration { tiaConfiguration.useNewCollector = true; } - var buildReason = tl.getVariable('Build.Reason'); + const buildReason = tl.getVariable('Build.Reason'); // https://www.visualstudio.com/en-us/docs/build/define/variables // PullRequest -> This is the case for TfsGit PR flow // CheckInShelveset -> This is the case for TFVC Gated Checkin - if (buildReason && (buildReason === "PullRequest" || buildReason === "CheckInShelveset")) { - tiaConfiguration.isPrFlow = "true"; - } - else { + if (buildReason && (buildReason === 'PullRequest' || buildReason === 'CheckInShelveset')) { + tiaConfiguration.isPrFlow = 'true'; + } else { tiaConfiguration.isPrFlow = tl.getVariable('tia.isPrFlow'); } tiaConfiguration.useTestCaseFilterInResponseFile = tl.getVariable('tia.useTestCaseFilterInResponseFile'); - const releaseuri = tl.getVariable('release.releaseUri') + const releaseuri = tl.getVariable('release.releaseUri'); tiaConfiguration.context = 'CI'; if (releaseuri) { tiaConfiguration.context = 'CD'; diff --git a/Tasks/VsTest/testselectorinvoker.ts b/Tasks/VsTest/testselectorinvoker.ts index 1d3ccfe55191..fd23a7cc7700 100644 --- a/Tasks/VsTest/testselectorinvoker.ts +++ b/Tasks/VsTest/testselectorinvoker.ts @@ -2,8 +2,9 @@ import models = require('./models'); import tl = require('vsts-task-lib/task'); import tr = require('vsts-task-lib/toolrunner'); import path = require('path'); -import { Helper } from './helpers' - +import * as inputdatacontract from './inputdatacontract'; +import { Helper } from './helpers'; +const uuid = require('uuid'); let perf = require('performance-now'); export class TestSelectorInvoker { @@ -106,6 +107,109 @@ export class TestSelectorInvoker { return output.code; } + public publishCodeChangesInDistributedMode(inputDataContract: inputdatacontract.InputDataContract): number { + tl.debug('Entered publish code changes'); + + const startTime = perf(); + let endTime: number; + let elapsedTime: number; + let pathFilters: string; + let definitionRunId: string; + let definitionId: string; + let prFlow: string; + let rebaseLimit: string; + let sourcesDirectory: string; + let context: string; + + let newprovider = 'true'; + if (!inputDataContract.ExecutionSettings.TiaSettings.FileLevel) { + newprovider = 'false'; + } + + const selectortool = tl.tool(this.getTestSelectorLocation()); + selectortool.arg('PublishCodeChanges'); + + if (tl.getVariable('release.releaseUri')) { + // Release context. Passing Release Id. + context = 'CD'; + definitionRunId = tl.getVariable('Release.ReleaseId'); + definitionId = tl.getVariable('release.DefinitionId'); + } else { + // Build context. Passing build id. + context = 'CI'; + definitionRunId = tl.getVariable('Build.BuildId'); + definitionId = tl.getVariable('System.DefinitionId'); + } + + if (inputDataContract.IsPrFlow) { + prFlow = 'true'; + } else { + prFlow = 'false'; + } + + //hydra: check if this conversion works fine (number to string) + if (inputDataContract.ExecutionSettings.TiaSettings.RebaseLimit) { + rebaseLimit = inputDataContract.ExecutionSettings.TiaSettings.RebaseLimit.toString(); + } + + if (inputDataContract.ExecutionSettings.TiaSettings.FilterPaths) { + pathFilters = inputDataContract.ExecutionSettings.TiaSettings.FilterPaths.trim(); + } else { + pathFilters = ''; + } + + if (inputDataContract.ExecutionSettings.TiaSettings.SourcesDirectory) { + sourcesDirectory = inputDataContract.ExecutionSettings.TiaSettings.SourcesDirectory.trim(); + } else { + sourcesDirectory = ''; + } + + const output = selectortool.execSync({ + cwd: null, + env: { + 'collectionurl': tl.getVariable('System.TeamFoundationCollectionUri'), + 'projectid': tl.getVariable('System.TeamProject'), + 'definitionrunid': definitionRunId, + 'definitionid': definitionId, + 'token': tl.getEndpointAuthorizationParameter('SystemVssConnection', 'AccessToken', false), + 'sourcesdir': sourcesDirectory, + 'newprovider': newprovider, + 'prflow': prFlow, + 'rebaselimit': rebaseLimit, + 'baselinefile': inputDataContract.TiaBaseLineBuildIdFile, + 'context': context, + 'filter': pathFilters, + 'userMapFile': inputDataContract.ExecutionSettings.TiaSettings.UserMapFile ? inputDataContract.ExecutionSettings.TiaSettings.UserMapFile : '', + 'testCaseFilterResponseFile': '', + 'proxyurl': inputDataContract.ProxySettings.ProxyUrl, + 'proxyusername': inputDataContract.ProxySettings.ProxyUsername, + 'proxypassword': inputDataContract.ProxySettings.ProxyPassword, + 'proxybypasslist': inputDataContract.ProxySettings.ProxyBypassHosts, + 'AGENT_VERSION': tl.getVariable('AGENT.VERSION'), + 'VsTest_TaskInstanceIdentifier': uuid.v1(), + 'VSTS_HTTP_RETRY': tl.getVariable('VSTS_HTTP_RETRY'), + 'VSTS_HTTP_TIMEOUT': tl.getVariable('VSTS_HTTP_TIMEOUT'), + 'DebugLogging': this.isDebugEnabled() + }, + silent: null, + outStream: null, + errStream: null, + windowsVerbatimArguments: null + }); + + endTime = perf(); + elapsedTime = endTime - startTime; + console.log('##vso[task.logissue type=warning;SubTaskName=PublishCodeChanges;SubTaskDuration=' + elapsedTime + ']'); + tl.debug(tl.loc('PublishCodeChangesPerfTime', elapsedTime)); + + if (output.code !== 0) { + tl.warning(output.stderr); + } + + tl.debug('completed publish code changes'); + return output.code; + } + public generateResponseFile(tiaConfig: models.TiaConfiguration, vstestConfig: models.VsTestConfigurations, discoveredTests: string, testCaseFilterOutputFile: string): number { const startTime = perf(); let endTime: number; diff --git a/Tasks/VsTest/versionfinder.ts b/Tasks/VsTest/versionfinder.ts index 0308609c38cb..ceab45db48eb 100644 --- a/Tasks/VsTest/versionfinder.ts +++ b/Tasks/VsTest/versionfinder.ts @@ -111,7 +111,7 @@ function locateTestWindow(testConfig: models.TestConfigurations): string { return getVSTestLocation(vsVersion); } -function getVSTestConsole15Path(): string { +export function getVSTestConsole15Path(): string { const vswhereTool = tl.tool(path.join(__dirname, 'vswhere.exe')); vswhereTool.line('-version [15.0,16.0) -latest -products * -requires Microsoft.VisualStudio.PackageGroup.TestTools.Core -property installationPath'); let vsPath = vswhereTool.execSync({ silent: true } as tr.IExecSyncOptions).stdout; @@ -123,7 +123,7 @@ function getVSTestConsole15Path(): string { return null; } -function getVSTestLocation(vsVersion: number): string { +export function getVSTestLocation(vsVersion: number): string { const vsCommon: string = tl.getVariable('VS' + vsVersion + '0COMNTools'); if (!vsCommon) { throw (new Error(tl.loc('VstestNotFound', utils.Helper.getVSVersion(vsVersion))));