Skip to content

Commit 1153658

Browse files
authored
test(sam): slow SamCliLocalInvokeInvocation, ChildProcess tests #5964
## Problem - The test failure is due to timeout on Windows when first searching for the SAM cli path in `src/shared/sam/cli/samCliLocator.ts`. This is a known slow process. - The verification of SAM is also slow (100-200ms), even if result is cached and we do this each time we read from cache. ## Solution - Verify the cache once, rather than on each read. - Find sam cli in the before hook to allow all sam tests to run under same conditions. Otherwise, the first test is then responsible for actually finding the path causing it to take way longer than the rest of the tests. - Allow 3x retries on before hook on windows to avoid flakiness. ## Notes - An alternative solution could involve manually inserting into the cache, or mocking a piece of the component, but wanted to keep the test coverage as is. - This approach also allows us to isolate the finding code into its own test (rather than whatever the first test is) to see if there are performance regressions. - Additionally, retry mechanism was more effective than increasing the timeout in reducing flakiness.
1 parent 0abac1f commit 1153658

File tree

4 files changed

+28
-9
lines changed

4 files changed

+28
-9
lines changed

packages/core/src/shared/sam/cli/samCliLocalInvoke.ts

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import * as proc from 'child_process'
77
import { pushIf } from '../../utilities/collectionUtils'
88
import * as nls from 'vscode-nls'
9-
import { fileExists } from '../../filesystemUtilities'
109
import { getLogger, getDebugConsoleLogger, Logger } from '../../logger'
1110
import { ChildProcess } from '../../utilities/processUtils'
1211
import { Timeout } from '../../utilities/timeoutUtils'
@@ -15,6 +14,7 @@ import * as vscode from 'vscode'
1514
import globals from '../../extensionGlobals'
1615
import { SamCliSettings } from './samCliSettings'
1716
import { addTelemetryEnvVar, collectSamErrors, SamCliError } from './samCliInvokerUtils'
17+
import { fs } from '../../fs/fs'
1818

1919
const localize = nls.loadMessageBundle()
2020

@@ -232,7 +232,6 @@ export class SamCliLocalInvokeInvocation {
232232

233233
public async execute(timeout?: Timeout): Promise<ChildProcess> {
234234
await this.validate()
235-
236235
const sam = await this.config.getOrDetectSamCli()
237236
if (!sam.path) {
238237
getLogger().warn('SAM CLI not found and not configured')
@@ -288,11 +287,11 @@ export class SamCliLocalInvokeInvocation {
288287
throw new Error('template resource name is missing or empty')
289288
}
290289

291-
if (!(await fileExists(this.args.templatePath))) {
290+
if (!(await fs.exists(this.args.templatePath))) {
292291
throw new Error(`template path does not exist: ${this.args.templatePath}`)
293292
}
294293

295-
if (this.args.eventPath !== undefined && !(await fileExists(this.args.eventPath))) {
294+
if (this.args.eventPath !== undefined && !(await fs.exists(this.args.eventPath))) {
296295
throw new Error(`event path does not exist: ${this.args.eventPath}`)
297296
}
298297
}

packages/core/src/shared/sam/cli/samCliLocator.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,20 @@ import { PerfLog } from '../../logger/perfLogger'
1313
import { tryRun } from '../../utilities/pathFind'
1414
import { mergeResolvedShellPath } from '../../env/resolveEnv'
1515

16+
interface SamLocation {
17+
path: string
18+
version: string
19+
}
1620
export class SamCliLocationProvider {
1721
private static samCliLocator: BaseSamCliLocator | undefined
18-
protected static cachedSamLocation: { path: string; version: string } | undefined
22+
protected static cachedSamLocation: SamLocation | undefined
23+
private static samLocationValid: boolean = false
1924

2025
/** Checks that the given `sam` actually works by invoking `sam --version`. */
2126
private static async isValidSamLocation(samPath: string) {
22-
return await tryRun(samPath, ['--version'], 'no', 'SAM CLI')
27+
const isValid = await tryRun(samPath, ['--version'], 'no', 'SAM CLI')
28+
this.samLocationValid = isValid
29+
return isValid
2330
}
2431

2532
/**
@@ -31,11 +38,14 @@ export class SamCliLocationProvider {
3138
const cachedLoc = forceSearch ? undefined : SamCliLocationProvider.cachedSamLocation
3239

3340
// Avoid searching the system for `sam` (especially slow on Windows).
34-
if (cachedLoc && (await SamCliLocationProvider.isValidSamLocation(cachedLoc.path))) {
41+
if (
42+
cachedLoc &&
43+
(SamCliLocationProvider.samLocationValid ||
44+
(await SamCliLocationProvider.isValidSamLocation(cachedLoc.path)))
45+
) {
3546
perflog.done()
3647
return cachedLoc
3748
}
38-
3949
SamCliLocationProvider.cachedSamLocation = await SamCliLocationProvider.getSamCliLocator().getLocation()
4050
perflog.done()
4151
return SamCliLocationProvider.cachedSamLocation

packages/core/src/shared/sam/cli/samCliSettings.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,6 @@ export class SamCliSettings extends fromExtensionManifest('aws.samcli', descript
8383
SamCliSettings.logIfChanged(`SAM CLI location (from settings): ${fromConfig}`)
8484
return { path: fromConfig, autoDetected: false }
8585
}
86-
8786
const fromSearch = await this.locationProvider.getLocation(forceSearch)
8887
SamCliSettings.logIfChanged(`SAM CLI location (version: ${fromSearch?.version}): ${fromSearch?.path}`)
8988
return { path: fromSearch?.path, autoDetected: true }

packages/core/src/test/shared/sam/cli/samCliLocalInvoke.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ import {
1414
import { ChildProcess } from '../../../../shared/utilities/processUtils'
1515
import { assertArgIsPresent, assertArgNotPresent, assertArgsContainArgument } from './samCliTestUtils'
1616
import { fs } from '../../../../shared'
17+
import { SamCliSettings } from '../../../../shared/sam/cli/samCliSettings'
18+
import { isWin } from '../../../../shared/vscode/env'
1719

1820
describe('SamCliLocalInvokeInvocation', async function () {
1921
class TestSamLocalInvokeCommand implements SamLocalInvokeCommand {
@@ -30,6 +32,15 @@ describe('SamCliLocalInvokeInvocation', async function () {
3032
let placeholderEventFile: string
3133
const nonRelevantArg = 'arg is not of interest to this test'
3234

35+
before(async function () {
36+
// File system search on windows can take a while.
37+
if (isWin()) {
38+
this.retries(3)
39+
}
40+
// This will place the result in the cache allowing all tests to run under same conditions.
41+
await SamCliSettings.instance.getOrDetectSamCli()
42+
})
43+
3344
beforeEach(async function () {
3445
tempFolder = await makeTemporaryToolkitFolder()
3546
placeholderTemplateFile = path.join(tempFolder, 'template.yaml')

0 commit comments

Comments
 (0)