Skip to content

Commit 1d79df7

Browse files
authored
test(amazonq): reset global state before each test (#6661)
## Problem previously we avoided resetting the global states because it didn't have it stubbed for integ/e2e tests ## Solution Now that it is we can allow global state to reset in the inline tests --- - Treat all work as PUBLIC. Private `feature/x` branches will not be squash-merged at release time. - Your code changes must meet the guidelines in [CONTRIBUTING.md](https://github.com/aws/aws-toolkit-vscode/blob/master/CONTRIBUTING.md#guidelines). - License: I confirm that my contribution is made under the terms of the Apache 2.0 license.
2 parents ac57f43 + 450d1a5 commit 1d79df7

File tree

4 files changed

+73
-17
lines changed

4 files changed

+73
-17
lines changed

buildspec/linuxE2ETests.yml

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ phases:
2626
# - '>/dev/null apt-get -qq install -y ca-certificates'
2727
# - 'apt-get install --reinstall ca-certificates'
2828
- bash buildspec/shared/linux-install.sh
29+
# increase file watcher count (ENOSPC error)
30+
- sysctl fs.inotify.max_user_watches=524288
2931

3032
pre_build:
3133
commands:

packages/amazonq/test/e2e/inline/inline.test.ts

+68-13
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,12 @@ import {
1515
using,
1616
} from 'aws-core-vscode/test'
1717
import { RecommendationHandler, RecommendationService, session } from 'aws-core-vscode/codewhisperer'
18-
import { Commands, globals, sleep, waitUntil } from 'aws-core-vscode/shared'
18+
import { Commands, globals, sleep, waitUntil, collectionUtil } from 'aws-core-vscode/shared'
1919
import { loginToIdC } from '../amazonq/utils/setup'
2020

2121
describe('Amazon Q Inline', async function () {
2222
let tempFolder: string
23+
const retries = 3
2324
const waitOptions = {
2425
interval: 500,
2526
timeout: 10000,
@@ -37,13 +38,24 @@ describe('Amazon Q Inline', async function () {
3738
const folder = await TestFolder.create()
3839
tempFolder = folder.path
3940
await closeAllEditors()
40-
await resetCodeWhispererGlobalVariables(false)
41+
await resetCodeWhispererGlobalVariables()
4142
})
4243

4344
afterEach(async function () {
4445
await closeAllEditors()
46+
if (this.currentTest?.state === undefined || this.currentTest?.isFailed() || this.currentTest?.isPending()) {
47+
logUserDecisionStatus()
48+
}
4549
})
4650

51+
function logUserDecisionStatus() {
52+
const events = getUserTriggerDecision()
53+
console.table({
54+
'telemetry events': JSON.stringify(events),
55+
'recommendation service status': RecommendationService.instance.isRunning,
56+
})
57+
}
58+
4759
async function setupEditor({ name, contents }: { name?: string; contents?: string } = {}) {
4860
const fileName = name ?? 'test.ts'
4961
const textContents =
@@ -58,16 +70,28 @@ describe('Amazon Q Inline', async function () {
5870
}
5971

6072
async function waitForRecommendations() {
61-
const ok = await waitUntil(
62-
async () =>
63-
RecommendationHandler.instance.isSuggestionVisible() || session.getSuggestionState(0) === 'Showed',
73+
const suggestionShown = await waitUntil(async () => session.getSuggestionState(0) === 'Showed', waitOptions)
74+
if (!suggestionShown) {
75+
throw new Error(`Suggestion did not show. Suggestion States: ${JSON.stringify(session.suggestionStates)}`)
76+
}
77+
const suggestionVisible = await waitUntil(
78+
async () => RecommendationHandler.instance.isSuggestionVisible(),
6479
waitOptions
6580
)
66-
if (!ok) {
67-
assert.fail(
81+
if (!suggestionVisible) {
82+
throw new Error(
6883
`Suggestions failed to become visible. Suggestion States: ${JSON.stringify(session.suggestionStates)}`
6984
)
7085
}
86+
console.table({
87+
'suggestions states': JSON.stringify(session.suggestionStates),
88+
'valid recommendation': RecommendationHandler.instance.isValidResponse(),
89+
'recommendation service status': RecommendationService.instance.isRunning,
90+
recommendations: session.recommendations,
91+
})
92+
if (!RecommendationHandler.instance.isValidResponse()) {
93+
throw new Error('Did not find a valid response')
94+
}
7195
}
7296

7397
/**
@@ -82,17 +106,23 @@ describe('Amazon Q Inline', async function () {
82106
})
83107
return events.some((event) => event.codewhispererSuggestionState === suggestionState)
84108
}, waitOptions)
85-
const events = globals.telemetry.logger.query({
86-
metricName,
87-
})
88109
if (!ok) {
89-
assert.fail(`Telemetry failed to be emitted. Current events: ${JSON.stringify(events)}`)
110+
assert.fail(`Telemetry for ${metricName} with suggestionState ${suggestionState} was not emitted`)
90111
}
112+
const events = getUserTriggerDecision()
91113
if (events.length > 1 && events[events.length - 1].codewhispererSuggestionState !== suggestionState) {
92-
assert.fail(`Telemetry events were emitted in the wrong order. Current events: ${JSON.stringify(events)}`)
114+
assert.fail(`Telemetry events were emitted in the wrong order`)
93115
}
94116
}
95117

118+
function getUserTriggerDecision() {
119+
return globals.telemetry.logger
120+
.query({
121+
metricName: 'codewhisperer_userTriggerDecision',
122+
})
123+
.map((e) => collectionUtil.partialClone(e, 3, ['credentialStartUrl'], '[omitted]'))
124+
}
125+
96126
for (const [name, invokeCompletion] of [
97127
['automatic', async () => await vscode.commands.executeCommand('type', { text: '\n' })],
98128
['manual', async () => Commands.tryExecute('aws.amazonq.invokeInlineCompletion')],
@@ -101,7 +131,7 @@ describe('Amazon Q Inline', async function () {
101131
let originalEditorContents: string | undefined
102132

103133
describe('supported filetypes', () => {
104-
beforeEach(async () => {
134+
async function setup() {
105135
await setupEditor()
106136

107137
/**
@@ -119,6 +149,31 @@ describe('Amazon Q Inline', async function () {
119149

120150
// wait until the ghost text appears
121151
await waitForRecommendations()
152+
}
153+
154+
beforeEach(async () => {
155+
/**
156+
* Every once and a while the backend won't respond with any recommendations.
157+
* In those cases, re-try the setup up-to ${retries} times
158+
*/
159+
let attempt = 0
160+
while (attempt < retries) {
161+
try {
162+
await setup()
163+
console.log(`test run ${attempt} succeeded`)
164+
logUserDecisionStatus()
165+
break
166+
} catch (e) {
167+
console.log(`test run ${attempt} failed`)
168+
console.log(e)
169+
logUserDecisionStatus()
170+
attempt++
171+
await resetCodeWhispererGlobalVariables()
172+
}
173+
}
174+
if (attempt === retries) {
175+
assert.fail(`Failed to invoke ${name} tests after ${attempt} attempts`)
176+
}
122177
})
123178

124179
it(`${name} invoke accept`, async function () {

packages/core/src/shared/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,4 @@ export { i18n } from './i18n-helper'
6262
export * from './icons'
6363
export * as textDocumentUtil from './utilities/textDocumentUtilities'
6464
export { TabTypeDataMap } from '../amazonq/webview/ui/tabs/constants'
65+
export * as collectionUtil from './utilities/collectionUtils'

packages/core/src/test/codewhisperer/testUtil.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,13 @@ import * as model from '../../codewhisperer/models/model'
2828
import { stub } from '../utilities/stubber'
2929
import { Dirent } from 'fs' // eslint-disable-line no-restricted-imports
3030

31-
export async function resetCodeWhispererGlobalVariables(clearGlobalState: boolean = true) {
31+
export async function resetCodeWhispererGlobalVariables() {
3232
vsCodeState.isIntelliSenseActive = false
3333
vsCodeState.isCodeWhispererEditing = false
3434
CodeWhispererCodeCoverageTracker.instances.clear()
3535
globals.telemetry.logger.clear()
3636
session.reset()
37-
if (clearGlobalState) {
38-
await globals.globalState.clear()
39-
}
37+
await globals.globalState.clear()
4038
await CodeSuggestionsState.instance.setSuggestionsEnabled(true)
4139
await RecommendationHandler.instance.clearInlineCompletionStates()
4240
}

0 commit comments

Comments
 (0)