diff --git a/Tasks/VsTestV2/Strings/resources.resjson/en-US/resources.resjson b/Tasks/VsTestV2/Strings/resources.resjson/en-US/resources.resjson index 433cd69980ee..b90c26826d98 100644 --- a/Tasks/VsTestV2/Strings/resources.resjson/en-US/resources.resjson +++ b/Tasks/VsTestV2/Strings/resources.resjson/en-US/resources.resjson @@ -168,7 +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 agent version. https://github.com/Microsoft/vsts-agent/releases", - "loc.messages.VsTestVersionEmpty": "VsTestVersion is null or empty", - "loc.messages.UserProvidedSourceFilter": "Source filter: %s" + "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/VsTestV2/distributedtest.ts b/Tasks/VsTestV2/distributedtest.ts index 7291fade6cec..2a3945cda93e 100644 --- a/Tasks/VsTestV2/distributedtest.ts +++ b/Tasks/VsTestV2/distributedtest.ts @@ -15,7 +15,7 @@ import * as ci from './cieventlogger'; import { TestSelectorInvoker } from './testselectorinvoker'; import { writeFileSync } from 'fs'; import { TaskResult } from 'vso-node-api/interfaces/TaskAgentInterfaces'; -import * as uuid from 'uuid'; +const uuid = require('uuid'); const testSelector = new TestSelectorInvoker(); @@ -60,15 +60,16 @@ export class DistributedTest { // Temporary solution till this logic can move to the test platform itself if (this.inputDataContract.UsingXCopyTestPlatformPackage) { - envVars = utils.Helper.setProfilerVariables(envVars); + envVars = this.setProfilerVariables(envVars); } // Pass the acess token as an environment variable for security purposes - utils.Helper.addToProcessEnvVars(envVars, 'DTA.AccessToken', tl.getEndpointAuthorization('SystemVssConnection', true).parameters.AccessToken); + utils.Helper.addToProcessEnvVars(envVars, 'DTA.AccessToken', this.inputDataContract.AccessToken); + this.inputDataContract.AccessToken = null; // Invoke DtaExecutionHost with the input json file const inputFilePath = utils.Helper.GenerateTempFile('input_' + uuid.v1() + '.json'); - utils.Helper.removeEmptyNodes(this.inputDataContract); + DistributedTest.removeEmptyNodes(this.inputDataContract); try { writeFileSync(inputFilePath, JSON.stringify(this.inputDataContract)); @@ -98,10 +99,29 @@ export class DistributedTest { return code; } + // 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; + } + if (typeof obj !== 'object' && typeof obj !== undefined) { + return; + } + const keys = Object.keys(obj); + for (var index in Object.keys(obj)) { + if (obj[keys[index]] && obj[keys[index]] != {}) { + 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 createTestSourcesFile(): string { try { let sourceFilter = tl.getDelimitedInput('testAssemblyVer2', '\n', true); - console.log(tl.loc('UserProvidedSourceFilter', sourceFilter.toString())); if (this.inputDataContract.TestSelectionSettings.TestSelectionType.toLowerCase() !== 'testassemblies') { sourceFilter = ['**\\*', '!**\\obj\\*']; @@ -130,6 +150,41 @@ export class DistributedTest { } } + 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 inputDataContract: inputdatacontract.InputDataContract; private dtaPid: number; } \ No newline at end of file diff --git a/Tasks/VsTestV2/helpers.ts b/Tasks/VsTestV2/helpers.ts index 8476a8bc5ef9..82fc81c9d9e7 100644 --- a/Tasks/VsTestV2/helpers.ts +++ b/Tasks/VsTestV2/helpers.ts @@ -6,7 +6,6 @@ import * as Q from 'q'; import * as models from './models'; import * as os from 'os'; import * as ci from './cieventlogger'; -import * as constants from './constants'; const str = require('string'); const uuid = require('uuid'); @@ -205,41 +204,6 @@ export class Helper { return argument; } - public static 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 { - Helper.publishEventToCi(constants.AreaCodes.TOOLSINSTALLERCACHENOTFOUND, tl.loc('testImpactAndCCWontWork'), 1043, false); - tl.warning(tl.loc('testImpactAndCCWontWork')); - } - - 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 { - Helper.publishEventToCi(constants.AreaCodes.TOOLSINSTALLERCACHENOTFOUND, tl.loc('testImpactAndCCWontWork'), 1044, false); - tl.warning(tl.loc('testImpactAndCCWontWork')); - } - - return envVars; - } - // set the console code page to "UTF-8" public static setConsoleCodePage() { tl.debug("Changing active code page to UTF-8"); @@ -263,24 +227,4 @@ export class Helper { tl.debug(`Failed to upload file ${file} with error ${err}`); } } - - // 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; - } - if (typeof obj !== 'object' && typeof obj !== undefined) { - return; - } - const keys = Object.keys(obj); - for (var index in Object.keys(obj)) { - if (obj[keys[index]] && obj[keys[index]] != {}) { - Helper.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]]; - } - } - } } \ No newline at end of file diff --git a/Tasks/VsTestV2/inputdatacontract.ts b/Tasks/VsTestV2/inputdatacontract.ts index 0a1551bf4746..806b883edb29 100644 --- a/Tasks/VsTestV2/inputdatacontract.ts +++ b/Tasks/VsTestV2/inputdatacontract.ts @@ -1,7 +1,6 @@ export interface InputDataContract { AgentName : string; AccessToken : string; - AccessTokenType : string; CollectionUri : string; RunIdentifier : string; TeamProject : string; @@ -16,11 +15,13 @@ export interface InputDataContract { DistributionSettings : DistributionSettings; ExecutionSettings : ExecutionSettings; Logging : Logging; + TiaBaseLineBuildIdFile : string; + UseNewCollector : boolean; } export interface TestReportingSettings { TestRunTitle : string; - TestResultsDirectory : string; + TestResultDirectory : string; } export interface TestSelectionSettings { @@ -39,14 +40,12 @@ export interface TestPlanTestSuiteSettings { } export interface TfsSpecificSettings { - BuildDefinitionId : number; - ReleaseDefinitionId : number; + DefinitionId : number; BuildId : number; BuildUri : string; ReleaseId : number; ReleaseUri : string; ReleaseEnvironmentUri : string; - WorkFolder : string; } export interface TestSpecificSettings { @@ -81,19 +80,16 @@ export interface DistributionSettings { } export interface ExecutionSettings { - DefaultTestBatchSize : number; AssemblyLevelParallelism : boolean; CodeCoverageEnabled : boolean; - PathToCustomTestAdapters : string; + CustomTestAdapters : string; + ExecutionMode : string; IgnoreTestFailures : boolean; ProceedAfterAbortedTestCase : boolean; SettingsFile : string; - AdditionalConsoleParameters : string; OverridenParameters : string; RerunSettings : RerunSettings; - RunTestsInIsolation : boolean; TiaSettings : TiaSettings; - TempFolder : string; VideoDataCollectorEnabled : boolean; } @@ -104,7 +100,7 @@ export interface TiaSettings { SourcesDirectory : string; FileLevel : boolean; FilterPaths : string; - UserMapFile : string; + UserMapFile : string; IsPrFlow : boolean; } diff --git a/Tasks/VsTestV2/inputparser.ts b/Tasks/VsTestV2/inputparser.ts index affecd6d904c..f20303ddd81e 100644 --- a/Tasks/VsTestV2/inputparser.ts +++ b/Tasks/VsTestV2/inputparser.ts @@ -7,17 +7,12 @@ 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'; -import * as uuid from 'uuid'; +const uuid = require('uuid'); const regedit = require('regedit'); -let serverBasedRun = false; - -// TODO: refactor all log messages to a separate function -// replace else if ladders with switch if possible -// unravel long else if chains - -export function parseInputsForDistributedTestRun() : idc.InputDataContract { +export function getDistributedTestConfigurations() : idc.InputDataContract { let inputDataContract = {} as idc.InputDataContract; inputDataContract = getTestSelectionInputs(inputDataContract); @@ -30,40 +25,20 @@ export function parseInputsForDistributedTestRun() : idc.InputDataContract { inputDataContract = getDistributionSettings(inputDataContract); inputDataContract = getExecutionSettings(inputDataContract); - inputDataContract.TeamProject = tl.getVariable('System.TeamProject'); - inputDataContract.CollectionUri = tl.getVariable('System.TeamFoundationCollectionUri'); - inputDataContract.AgentName = tl.getVariable('Agent.MachineName') + '-' + tl.getVariable('Agent.Name') + '-' + tl.getVariable('Agent.Id'); - inputDataContract.AccessTokenType = 'jwt'; - inputDataContract.RunIdentifier = getRunIdentifier(); - - logWarningForWER(tl.getBoolInput('uiTests')); - ci.publishEvent({ 'UiTestsOptionSelected': tl.getBoolInput('uiTests')} ); + taskinputparser.logWarningForWER(tl.getBoolInput('uiTests')); - return inputDataContract; -} - -export function parseInputsForNonDistributedTestRun() : idc.InputDataContract { - let inputDataContract = {} as idc.InputDataContract; - - // hydra: should i create a separate function since testplan and testrun are never scenarios for local test? - inputDataContract = getTestSelectionInputs(inputDataContract); - inputDataContract = getTfsSpecificSettings(inputDataContract); - inputDataContract = getTargetBinariesSettings(inputDataContract); - inputDataContract = getTestReportingSettings(inputDataContract); - inputDataContract = getTestPlatformSettings(inputDataContract); - inputDataContract = getLoggingSettings(inputDataContract); - inputDataContract = getProxySettings(inputDataContract); - inputDataContract = getExecutionSettings(inputDataContract); + inputDataContract.UseNewCollector = false; + const useNewCollector = tl.getVariable('tia.useNewCollector'); + if (useNewCollector && useNewCollector.toUpperCase() === 'TRUE') { + inputDataContract.UseNewCollector = true; + } inputDataContract.TeamProject = tl.getVariable('System.TeamProject'); inputDataContract.CollectionUri = tl.getVariable('System.TeamFoundationCollectionUri'); - inputDataContract.AccessToken = tl.getEndpointAuthorization('SystemVssConnection', true).parameters.AccessToken; - inputDataContract.AccessTokenType = 'jwt'; + 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(); - logWarningForWER(tl.getBoolInput('uiTests')); - return inputDataContract; } @@ -128,14 +103,11 @@ function getTestSelectionInputs(inputDataContract : idc.InputDataContract) : idc function getTfsSpecificSettings(inputDataContract : idc.InputDataContract) : idc.InputDataContract { inputDataContract.TfsSpecificSettings = {}; - inputDataContract.TfsSpecificSettings.BuildDefinitionId = utils.Helper.isNullEmptyOrUndefined(tl.getVariable('Release.DefinitionId')) ? Number(tl.getVariable('System.DefinitionId')) : Number(tl.getVariable('Build.DefinitionId')); - inputDataContract.TfsSpecificSettings.ReleaseDefinitionId = utils.Helper.isNullEmptyOrUndefined(tl.getVariable('Release.DefinitionId')) ? null : Number(tl.getVariable('Release.DefinitionId')); + inputDataContract.TfsSpecificSettings.DefinitionId = utils.Helper.isNullEmptyOrUndefined(tl.getVariable('Release.DefinitionId')) ? Number(tl.getVariable('System.DefinitionId')) : Number(tl.getVariable('Release.DefinitionId')); 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.TfsSpecificSettings.ReleaseEnvironmentUri = tl.getVariable('Release.EnvironmentUri'); - inputDataContract.TfsSpecificSettings.WorkFolder = tl.getVariable('System.DefaultWorkingDirectory'); return inputDataContract; } @@ -162,6 +134,7 @@ function getTestReportingSettings(inputDataContract : idc.InputDataContract) : i inputDataContract.TestReportingSettings.TestRunTitle = `TestRun_${definitionName}_${buildOrReleaseName}`; } + return inputDataContract; } @@ -243,7 +216,6 @@ function getProxySettings(inputDataContract : idc.InputDataContract) : idc.Input 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; @@ -252,85 +224,69 @@ function getDistributionSettings(inputDataContract : idc.InputDataContract) : id const distributionType = tl.getInput('distributionBatchType'); - switch (distributionType) { - - case 'basedOnTestCases': - inputDataContract.DistributionSettings.DistributeTestsBasedOn = DistributionTypes.NUMBEROFTESTMETHODSBASED; - 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)); - } + 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)); } - break; - - case 'basedOnExecutionTime': - inputDataContract.DistributionSettings.DistributeTestsBasedOn = DistributionTypes.EXECUTIONTIMEBASED; - 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)); + } + // 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)); } - break; - - case 'basedOnAssembly': - inputDataContract.DistributionSettings.DistributeTestsBasedOn = DistributionTypes.ASSEMBLYBASED; - break; + 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'); - - inputDataContract.ExecutionSettings.TempFolder = utils.Helper.GetTempFolder(); + 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'); - tl.debug(`OverrideTestrunParameters set to ${inputDataContract.ExecutionSettings.OverridenParameters}`); inputDataContract.ExecutionSettings.AssemblyLevelParallelism = tl.getBoolInput('runInParallel'); console.log(tl.loc('runInParallelInput', inputDataContract.ExecutionSettings.AssemblyLevelParallelism)); - inputDataContract.ExecutionSettings.RunTestsInIsolation = tl.getBoolInput('runTestsInIsolation'); - console.log(tl.loc('runInIsolationInput', inputDataContract.ExecutionSettings.RunTestsInIsolation)); - - if (serverBasedRun && inputDataContract.ExecutionSettings.RunTestsInIsolation) { - inputDataContract.ExecutionSettings.RunTestsInIsolation = null; + // hydra: do we want to shoot this warning? + if (tl.getBoolInput('runTestsInIsolation')) { tl.warning(tl.loc('runTestInIsolationNotSupported')); } - inputDataContract.ExecutionSettings.PathToCustomTestAdapters = tl.getInput('pathtoCustomTestAdapters'); - - if (!utils.Helper.isNullOrWhitespace(inputDataContract.ExecutionSettings.PathToCustomTestAdapters)) { - inputDataContract.ExecutionSettings.PathToCustomTestAdapters = path.resolve(inputDataContract.ExecutionSettings.PathToCustomTestAdapters); + inputDataContract.ExecutionSettings.CustomTestAdapters = tl.getInput('pathtoCustomTestAdapters'); + if (!utils.Helper.isNullOrWhitespace(inputDataContract.ExecutionSettings.CustomTestAdapters)) { + inputDataContract.ExecutionSettings.CustomTestAdapters = path.resolve(inputDataContract.ExecutionSettings.CustomTestAdapters); } - - if (inputDataContract.ExecutionSettings.PathToCustomTestAdapters && - !utils.Helper.pathExistsAsDirectory(inputDataContract.ExecutionSettings.PathToCustomTestAdapters)) { - throw new Error(tl.loc('pathToCustomAdaptersInvalid', inputDataContract.ExecutionSettings.PathToCustomTestAdapters)); + 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.PathToCustomTestAdapters)); + console.log(tl.loc('pathToCustomAdaptersInput', inputDataContract.ExecutionSettings.CustomTestAdapters)); inputDataContract.ExecutionSettings.IgnoreTestFailures = utils.Helper.stringToBool(tl.getVariable('vstest.ignoretestfailures')); @@ -345,12 +301,8 @@ function getExecutionSettings(inputDataContract : idc.InputDataContract) : idc.I tl.warning(tl.loc('uitestsparallel')); } - inputDataContract.ExecutionSettings.AdditionalConsoleParameters = tl.getInput('otherConsoleOptions'); - console.log(tl.loc('otherConsoleOptionsInput', inputDataContract.ExecutionSettings.AdditionalConsoleParameters)); - - if (serverBasedRun && inputDataContract.ExecutionSettings.AdditionalConsoleParameters) { + if (tl.getInput('otherConsoleOptions')) { tl.warning(tl.loc('otherConsoleOptionsNotSupported')); - inputDataContract.ExecutionSettings.AdditionalConsoleParameters = null; } inputDataContract.ExecutionSettings.CodeCoverageEnabled = tl.getBoolInput('codeCoverageEnabled'); @@ -374,6 +326,7 @@ function getTiaSettings(inputDataContract : idc.InputDataContract) : idc.InputDa 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'); @@ -386,11 +339,12 @@ function getTiaSettings(inputDataContract : idc.InputDataContract) : idc.InputDa } 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.ExecutionSettings.TiaSettings.IsPrFlow = true; } else { inputDataContract.ExecutionSettings.TiaSettings.IsPrFlow = utils.Helper.stringToBool(tl.getVariable('tia.isPrFlow')); @@ -441,7 +395,7 @@ function getRerunSettings(inputDataContract : idc.InputDataContract) : idc.Input function getRunIdentifier(): string { let runIdentifier: string = ''; - const taskInstanceId = getDtaInstanceId(); + const taskInstanceId = taskinputparser.getDtaInstanceId(); const dontDistribute = tl.getBoolInput('dontDistribute'); const releaseId = tl.getVariable('Release.ReleaseId'); const jobId = tl.getVariable('System.JobPositionInPhase'); @@ -496,50 +450,4 @@ function getTestPlatformPath(inputDataContract : idc.InputDataContract) { tl.debug('Searching for Visual Studio ' + vsVersion.toString()); return versionfinder.getVSTestLocation(vsVersion); -} - -async function logWarningForWER(runUITests: boolean) { - if (!runUITests) { - return; - } - - const regPathHKLM = 'HKLM\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting'; - const regPathHKCU = 'HKCU\\SOFTWARE\\Microsoft\\Windows\\Windows Error Reporting'; - - const isEnabledInHKCU = await isDontShowUIRegKeySet(regPathHKCU); - const isEnabledInHKLM = await isDontShowUIRegKeySet(regPathHKLM); - - if (!isEnabledInHKCU && !isEnabledInHKLM) { - tl.warning(tl.loc('DontShowWERUIDisabledWarning')); - } -} - -function isDontShowUIRegKeySet(regPath: string): Q.Promise { - const defer = Q.defer(); - const regValue = 'DontShowUI'; - regedit.list(regPath).on('data', (entry) => { - if (entry && entry.data && entry.data.values && - entry.data.values[regValue] && (entry.data.values[regValue].value === 1)) { - defer.resolve(true); - } - defer.resolve(false); - }); - return defer.promise; -} - -export function setIsServerBasedRun(isServerBasedRun: boolean) { - serverBasedRun = isServerBasedRun; -} - -export function getDtaInstanceId(): number { - const taskInstanceIdString = tl.getVariable('DTA_INSTANCE_ID'); - let taskInstanceId: number = 1; - if (taskInstanceIdString) { - const instanceId: number = Number(taskInstanceIdString); - if (!isNaN(instanceId)) { - taskInstanceId = instanceId + 1; - } - } - tl.setVariable('DTA_INSTANCE_ID', taskInstanceId.toString()); - return taskInstanceId; } \ No newline at end of file diff --git a/Tasks/VsTestV2/make.json b/Tasks/VsTestV2/make.json index 62b94a445b49..d9f416d08acb 100644 --- a/Tasks/VsTestV2/make.json +++ b/Tasks/VsTestV2/make.json @@ -2,11 +2,11 @@ "externals": { "archivePackages": [ { - "url": "https://testselectorv2.blob.core.windows.net/testselector/6508861/TestSelector.zip", + "url": "https://testselectorv2.blob.core.windows.net/testselector/6175539/TestSelector.zip", "dest": "./" }, { - "url": "https://testexecution.blob.core.windows.net/testexecution/6508861/TestAgent.zip", + "url": "https://testexecution.blob.core.windows.net/testexecution/6408541/TestAgent.zip", "dest": "./Modules" }, { diff --git a/Tasks/VsTestV2/nondistributedtest.ts b/Tasks/VsTestV2/nondistributedtest.ts deleted file mode 100644 index 03f3c618019b..000000000000 --- a/Tasks/VsTestV2/nondistributedtest.ts +++ /dev/null @@ -1,143 +0,0 @@ -import * as tl from 'vsts-task-lib/task'; -import * as tr from 'vsts-task-lib/toolrunner'; -import * as path from 'path'; -import * as models from './models'; -import * as inputParser from './inputparser'; -import * as utils from './helpers'; -import * as outStream from './outputstream'; -import * as ci from './cieventlogger'; -import * as testselectorinvoker from './testselectorinvoker'; -import { AreaCodes, ResultMessages } from './constants'; -import { ToolRunner } from 'vsts-task-lib/toolrunner'; -import * as os from 'os'; -import * as uuid from 'uuid'; -import * as fs from 'fs'; -import * as process from 'process'; -import { InputDataContract } from './inputdatacontract'; - -const runSettingsExt = '.runsettings'; -const testSettingsExt = '.testsettings'; -const sourceFilter = tl.getDelimitedInput('testAssemblyVer2', '\n', true); - -let inputDataContract: InputDataContract = undefined; -let testAssemblyFiles = undefined; - -export function runNonDistributedTest(idc: InputDataContract) { - try { - - console.log(tl.loc('runTestsLocally', 'vstest.console.exe')); - console.log('========================================================'); - - inputDataContract = idc; - - testAssemblyFiles = getTestAssemblies(); - if (!testAssemblyFiles || testAssemblyFiles.length === 0) { - console.log('##vso[task.logissue type=warning;code=002004;]'); - tl.warning(tl.loc('NoMatchingTestAssemblies', sourceFilter)); - return; - } - - const disableTIA = tl.getVariable('DisableTestImpactAnalysis'); - if (disableTIA !== undefined && disableTIA.toLowerCase() === 'true') { - tl.debug('Disabling tia.'); - inputDataContract.ExecutionSettings.TiaSettings.Enabled = false; - } - - startDtaExecutionHost().then((code: number) => { - if (code !== 0) { - tl.setResult(tl.TaskResult.Failed, tl.loc('VstestFailed')); - return; - } - }); - - } catch (err) { - tl.error(err); - tl.setResult(tl.TaskResult.Failed, tl.loc('VstestFailedReturnCode')); - } -} - -async function startDtaExecutionHost() { - let dtaExecutionHostTool = tl.tool(path.join(inputDataContract.VsTestConsolePath, 'vstest.console.exe')); - - inputDataContract.TestSelectionSettings.TestSourcesFile = createTestSourcesFile(); - tl.cd(inputDataContract.TfsSpecificSettings.WorkFolder); - let envVars: { [key: string]: string; } = process.env; - dtaExecutionHostTool = tl.tool(path.join(__dirname, 'Modules/DTAExecutionHost.exe')); - - // Invoke DtaExecutionHost with the input json file - const inputFilePath = utils.Helper.GenerateTempFile('input_' + uuid.v1() + '.json'); - utils.Helper.removeEmptyNodes(inputDataContract); - - try { - fs.writeFileSync(inputFilePath, JSON.stringify(inputDataContract)); - } catch (e) { - tl.setResult(tl.TaskResult.Failed, `Failed to write to the input json file ${inputFilePath} with error ${e}`); - } - - if (utils.Helper.isDebugEnabled()) { - utils.Helper.uploadFile(inputFilePath); - } - - dtaExecutionHostTool.arg(['--inputFile', inputFilePath]); - - utils.Helper.addToProcessEnvVars(envVars, 'DTA.AccessToken', tl.getEndpointAuthorization('SystemVssConnection', true).parameters.AccessToken); - - // hydra: See which of these are required in C# layer. Do we want this for telemetry?? - // utils.Helper.addToProcessEnvVars(envVars, 'DTA.AgentVersion', tl.getVariable('AGENT.VERSION')); - - if (inputDataContract.UsingXCopyTestPlatformPackage) { - envVars = utils.Helper.setProfilerVariables(envVars); - } - - const execOptions: tr.IExecOptions = { - env: envVars, - failOnStdErr: false, - // In effect this will not be called as failOnStdErr is false - // Keeping this code in case we want to change failOnStdErr - errStream: new outStream.StringErrorWritable({ decodeStrings: false }) - }; - - // The error codes return below are not the same as tl.TaskResult which follows a different convention. - // Here we are returning the code as returned to us by vstest.console in case of complete run - // In case of a failure 1 indicates error to our calling function - try { - return await dtaExecutionHostTool.exec(execOptions); - } catch (err) { - tl.warning(tl.loc('VstestFailed')); - tl.error(err); - return 1; - } -} - -function getTestAssemblies(): string[] { - tl.debug('Searching for test assemblies in: ' + inputDataContract.TestSelectionSettings.SearchFolder); - return tl.findMatch(inputDataContract.TestSelectionSettings.SearchFolder, sourceFilter); -} - -function createTestSourcesFile(): string { - try { - const sourceFilter = tl.getDelimitedInput('testAssemblyVer2', '\n', true); - console.log(tl.loc('UserProvidedSourceFilter', sourceFilter.toString())); - - const sources = tl.findMatch(inputDataContract.TestSelectionSettings.SearchFolder, sourceFilter); - tl.debug('tl match count :' + sources.length); - const filesMatching = []; - sources.forEach(function (match: string) { - if (!fs.lstatSync(match).isDirectory()) { - filesMatching.push(match); - } - }); - - tl.debug('Files matching count :' + filesMatching.length); - if (filesMatching.length === 0) { - throw new Error(tl.loc('noTestSourcesFound', sourceFilter.toString())); - } - - const tempFile = utils.Helper.GenerateTempFile('testSources_' + uuid.v1() + '.src'); - fs.writeFileSync(tempFile, filesMatching.join(os.EOL)); - tl.debug('Test Sources file :' + tempFile); - return tempFile; - } catch (error) { - throw new Error(tl.loc('testSourcesFilteringFailed', error)); - } -} \ No newline at end of file diff --git a/Tasks/VsTestV2/runvstest.ts b/Tasks/VsTestV2/runvstest.ts index 46f40ecd8553..af338d11c1ad 100644 --- a/Tasks/VsTestV2/runvstest.ts +++ b/Tasks/VsTestV2/runvstest.ts @@ -1,7 +1,7 @@ import * as tl from 'vsts-task-lib/task'; import * as models from './models'; -import * as nondistributedtest from './nondistributedtest'; -import * as localtest from './vstest'; +import * as taskInputParser from './taskinputparser'; +import * as localTest from './vstest'; import * as path from 'path'; import * as distributedTest from './distributedtest'; import * as ci from './cieventlogger'; @@ -26,9 +26,8 @@ if (osPlat !== 'win32') { if (blockRun) { tl.setResult(tl.TaskResult.Failed, tl.loc('MultiConfigNotSupportedWithOnDemand')); } - const serverBasedRun = isServerBasedRun(); - inputParser.setIsServerBasedRun(serverBasedRun); - if (serverBasedRun) { + const useDtaExecutionEngine = isDtaEngineRequired(); + if (useDtaExecutionEngine) { ci.publishEvent({ runmode: 'distributedtest', parallelism: tl.getVariable('System.ParallelExecutionType'), testtype: tl.getInput('testSelector') @@ -36,23 +35,14 @@ if (osPlat !== 'win32') { console.log(tl.loc('distributedTestWorkflow')); console.log('======================================================'); - const inputDataContract = inputParser.parseInputsForDistributedTestRun(); + const inputDataContract = inputParser.getDistributedTestConfigurations(); console.log('======================================================'); const test = new distributedTest.DistributedTest(inputDataContract); test.runDistributedTest(); } else { ci.publishEvent({ runmode: 'vstest' }); - const inputDataContract = inputParser.parseInputsForNonDistributedTestRun(); - - if (inputDataContract.ExecutionSettings - && inputDataContract.ExecutionSettings.RerunSettings - && inputDataContract.ExecutionSettings.RerunSettings.RerunFailedTests - && (inputDataContract.ExecutionSettings.TiaSettings && !inputDataContract.ExecutionSettings.TiaSettings.Enabled)) { - nondistributedtest.runNonDistributedTest(inputDataContract); - } else { - localtest.startTest(); - } + localTest.startTest(); } } catch (error) { tl.setResult(tl.TaskResult.Failed, error); @@ -74,7 +64,7 @@ function isMultiConfigOnDemandRun(): boolean { return false; } -function isServerBasedRun(): boolean { +function isDtaEngineRequired(): boolean { const batchType = tl.getInput('distributionBatchType'); if (batchType && batchType === 'basedOnTestCases') { const batchSize = tl.getInput('batchingBasedOnAgentsOption'); diff --git a/Tasks/VsTestV2/task.json b/Tasks/VsTestV2/task.json index 1a62ab161169..2c1850eee69b 100644 --- a/Tasks/VsTestV2/task.json +++ b/Tasks/VsTestV2/task.json @@ -586,8 +586,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 agent version. https://github.com/Microsoft/vsts-agent/releases", - "VsTestVersionEmpty": "VsTestVersion is null or empty", - "UserProvidedSourceFilter": "Source filter: %s" + "UpgradeAgentMessage": "Please upgrade your vsts-agent version. https://github.com/Microsoft/vsts-agent/releases", + "VsTestVersionEmpty": "VsTestVersion is null or empty" } -} +} \ No newline at end of file diff --git a/Tasks/VsTestV2/task.loc.json b/Tasks/VsTestV2/task.loc.json index 9ea9f32dd624..5293cc5ecca9 100644 --- a/Tasks/VsTestV2/task.loc.json +++ b/Tasks/VsTestV2/task.loc.json @@ -587,7 +587,6 @@ "MultiConfigNotSupportedWithOnDemand": "ms-resource:loc.messages.MultiConfigNotSupportedWithOnDemand", "disabledRerun": "ms-resource:loc.messages.disabledRerun", "UpgradeAgentMessage": "ms-resource:loc.messages.UpgradeAgentMessage", - "VsTestVersionEmpty": "ms-resource:loc.messages.VsTestVersionEmpty", - "UserProvidedSourceFilter": "ms-resource:loc.messages.UserProvidedSourceFilter" + "VsTestVersionEmpty": "ms-resource:loc.messages.VsTestVersionEmpty" } } \ No newline at end of file diff --git a/Tasks/VsTestV2/taskinputparser.ts b/Tasks/VsTestV2/taskinputparser.ts index f73e5ba4847d..fe0758924215 100644 --- a/Tasks/VsTestV2/taskinputparser.ts +++ b/Tasks/VsTestV2/taskinputparser.ts @@ -10,7 +10,7 @@ import * as ci from './cieventlogger'; import * as versionFinder from './versionfinder'; import { AreaCodes, ResultMessages } from './constants'; import * as inputdatacontract from './inputdatacontract'; -import * as uuid from 'uuid'; +const uuid = require('uuid'); const regedit = require('regedit'); export function getvsTestConfigurations() { diff --git a/Tasks/VsTestV2/testselectorinvoker.ts b/Tasks/VsTestV2/testselectorinvoker.ts index 88a1b0743848..10245260c25f 100644 --- a/Tasks/VsTestV2/testselectorinvoker.ts +++ b/Tasks/VsTestV2/testselectorinvoker.ts @@ -107,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.ExecutionSettings.TiaSettings.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/VsTestV2/vstest.ts b/Tasks/VsTestV2/vstest.ts index ba315bfc6feb..338679c5e17c 100644 --- a/Tasks/VsTestV2/vstest.ts +++ b/Tasks/VsTestV2/vstest.ts @@ -1,24 +1,23 @@ -import * as tl from 'vsts-task-lib/task'; -import * as tr from 'vsts-task-lib/toolrunner'; -import * as path from 'path'; -import * as models from './models'; -import * as taskInputParser from './taskinputparser'; -import * as inputParser from './inputparser'; -import * as settingsHelper from './settingshelper'; -import * as vstestVersion from './vstestversion'; +import tl = require('vsts-task-lib/task'); +import tr = require('vsts-task-lib/toolrunner'); +import path = require('path'); +import models = require('./models'); +import taskInputParser = require('./taskinputparser'); +import settingsHelper = require('./settingshelper'); +import vstestVersion = require('./vstestversion'); import * as utils from './helpers'; import * as outStream from './outputstream'; import * as ci from './cieventlogger'; import * as testselectorinvoker from './testselectorinvoker'; import { AreaCodes, ResultMessages } from './constants'; import { ToolRunner } from 'vsts-task-lib/toolrunner'; -import * as os from 'os'; -import * as uuid from 'uuid'; -import * as fs from 'fs'; -import * as xml2js from 'xml2js'; -import * as perf from 'performance-now'; -import * as process from 'process'; -const regedit = require('regedit'); +let os = require('os'); +let regedit = require('regedit'); +let uuid = require('uuid'); +let fs = require('fs'); +let xml2js = require('xml2js'); +let perf = require('performance-now'); +let process = require('process'); const runSettingsExt = '.runsettings'; const testSettingsExt = '.testsettings';