diff --git a/.changes/next-release/feature-272cb837-f551-485a-9f37-43ca37aef75c.json b/.changes/next-release/feature-272cb837-f551-485a-9f37-43ca37aef75c.json new file mode 100644 index 00000000000..752ab588a58 --- /dev/null +++ b/.changes/next-release/feature-272cb837-f551-485a-9f37-43ca37aef75c.json @@ -0,0 +1,4 @@ +{ + "type" : "feature", + "description" : "Add back build and execute for Internal Amazon Customers." +} \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 00c76551542..5e9e32f120b 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -27,7 +27,7 @@ mockitoKotlin = "5.4.0" mockk = "1.13.10" nimbus-jose-jwt = "9.40" node-gradle = "7.0.2" -telemetryGenerator = "1.0.297" +telemetryGenerator = "1.0.299" testLogger = "4.0.0" testRetry = "1.5.10" # test-only; platform provides slf4j transitively at runtime diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeTestChatItems.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeTestChatItems.kt index 4ed35e35065..5c419eee681 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeTestChatItems.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeTestChatItems.kt @@ -36,3 +36,9 @@ fun testGenProgressField(value: Int) = ProgressField( valueText = "$value%", actions = listOf(cancelTestGenButton) ) + +val buildAndExecuteProgrogressField = ProgressField( + status = "default", + text = message("testgen.progressbar.build_and_execute"), + actions = listOf(cancelTestGenButton) +) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeWhispererUTGChatManager.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeWhispererUTGChatManager.kt index 8b02c019c21..d33b86fa533 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeWhispererUTGChatManager.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/CodeWhispererUTGChatManager.kt @@ -37,7 +37,6 @@ import software.aws.toolkits.jetbrains.services.amazonqCodeTest.model.PreviousUT import software.aws.toolkits.jetbrains.services.amazonqCodeTest.model.ShortAnswer import software.aws.toolkits.jetbrains.services.amazonqCodeTest.session.BuildAndExecuteProgressStatus import software.aws.toolkits.jetbrains.services.amazonqCodeTest.session.Session -import software.aws.toolkits.jetbrains.services.amazonqCodeTest.utils.combineBuildAndExecuteLogFiles import software.aws.toolkits.jetbrains.services.codemodernizer.utils.calculateTotalLatency import software.aws.toolkits.jetbrains.services.codewhisperer.codetest.CodeTestException import software.aws.toolkits.jetbrains.services.codewhisperer.codetest.fileTooLarge @@ -63,6 +62,7 @@ import java.nio.file.Path import java.nio.file.Paths import java.time.Duration import java.time.Instant +import java.util.UUID import java.util.concurrent.atomic.AtomicBoolean import java.util.zip.ZipInputStream @@ -89,12 +89,24 @@ class CodeWhispererUTGChatManager(val project: Project, private val cs: Coroutin val session = codeTestChatHelper.getActiveSession() session.isGeneratingTests = true session.iteration++ + if (session.testGenerationJobGroupName.isNullOrEmpty()) { + session.testGenerationJobGroupName = UUID.randomUUID().toString() + } + val final = session.testGenerationJobGroupName + + if (session.iteration == 1) { + codeTestChatHelper.updateUI( + promptInputDisabledState = true, + promptInputProgress = testGenProgressField(0), + ) + } else { + codeTestChatHelper.updateUI( + promptInputDisabledState = true, + promptInputProgress = buildAndExecuteProgrogressField, + ) + } // Set the Progress bar to "Generating unit tests..." - codeTestChatHelper.updateUI( - promptInputDisabledState = true, - promptInputProgress = testGenProgressField(0), - ) val codeTestResponseContext = createUploadUrl(codeTestChatHelper, previousIterationContext) session.srcPayloadSize = codeTestResponseContext.payloadContext.srcPayloadSize @@ -137,7 +149,8 @@ class CodeWhispererUTGChatManager(val project: Project, private val cs: Coroutin ) .build() ), - userInput = prompt + userInput = prompt, + testGenerationJobGroupName = final ) delay(200) response?.testGenerationJob() != null @@ -198,6 +211,7 @@ class CodeWhispererUTGChatManager(val project: Project, private val cs: Coroutin // Setting default value to 0 if the value is null or invalid session.numberOfUnitTestCasesGenerated = shortAnswer.numberOfTestMethods session.testFileRelativePathToProjectRoot = getTestFilePathRelativeToRoot(shortAnswer) + session.shortAnswer = shortAnswer // update test summary card in success case if (previousIterationContext == null) { @@ -261,7 +275,11 @@ class CodeWhispererUTGChatManager(val project: Project, private val cs: Coroutin } codeTestChatHelper.updateUI( promptInputDisabledState = true, - promptInputProgress = testGenProgressField(progressRate), + promptInputProgress = if (session.iteration == 1) { + testGenProgressField(0) + } else { + buildAndExecuteProgrogressField + } ) } @@ -469,10 +487,8 @@ class CodeWhispererUTGChatManager(val project: Project, private val cs: Coroutin previousIterationContext.selectedFile } - val combinedBuildAndExecuteLogFile = combineBuildAndExecuteLogFiles( - previousIterationContext?.buildLogFile, - previousIterationContext?.testLogFile - ) + val combinedBuildAndExecuteLogFile = + previousIterationContext?.buildLogFile val codeTestSessionConfig = CodeTestSessionConfig(file, project, combinedBuildAndExecuteLogFile) codeTestChatHelper.getActiveSession().projectRoot = codeTestSessionConfig.projectRoot.path @@ -481,8 +497,13 @@ class CodeWhispererUTGChatManager(val project: Project, private val cs: Coroutin return codeWhispererCodeTestSession.run(codeTestChatHelper, previousIterationContext) } - private fun startTestGeneration(uploadId: String, targetCode: List, userInput: String): StartTestGenerationResponse = - CodeWhispererClientAdaptor.getInstance(project).startTestGeneration(uploadId, targetCode, userInput) + private fun startTestGeneration( + uploadId: String, + targetCode: List, + userInput: String, + testGenerationJobGroupName: String, + ): StartTestGenerationResponse = + CodeWhispererClientAdaptor.getInstance(project).startTestGeneration(uploadId, targetCode, userInput, testGenerationJobGroupName) private fun getTestGenerationStatus(jobId: String, jobGroupName: String): GetTestGenerationResponse = CodeWhispererClientAdaptor.getInstance(project).getTestGeneration(jobId, jobGroupName) @@ -566,25 +587,44 @@ class CodeWhispererUTGChatManager(val project: Project, private val cs: Coroutin canBeVoted = false ) ) + if (session.iteration == 1) { + AmazonqTelemetry.utgGenerateTests( + cwsprChatProgrammingLanguage = session.programmingLanguage.languageId, + hasUserPromptSupplied = session.hasUserPromptSupplied, + isFileInWorkspace = true, + isSupportedLanguage = true, + credentialStartUrl = getStartUrl(project), + jobGroup = session.testGenerationJobGroupName, + jobId = session.testGenerationJob, + result = if (e.message == message("testgen.message.cancelled")) MetricResult.Cancelled else MetricResult.Failed, + reason = (e as CodeTestException).code ?: "DefaultError", + reasonDesc = if (e.message == message("testgen.message.cancelled")) "${e.code}: ${e.message}" else e.message, + perfClientLatency = (Instant.now().toEpochMilli() - session.startTimeOfTestGeneration), + isCodeBlockSelected = session.isCodeBlockSelected, + artifactsUploadDuration = session.artifactUploadDuration, + buildPayloadBytes = session.srcPayloadSize, + buildZipFileBytes = session.srcZipFileSize, + requestId = session.startTestGenerationRequestId + ) + } else { + AmazonqTelemetry.unitTestGeneration( + cwsprChatProgrammingLanguage = session.programmingLanguage.languageId, + hasUserPromptSupplied = session.hasUserPromptSupplied, + isSupportedLanguage = true, + credentialStartUrl = getStartUrl(project), + jobGroup = session.testGenerationJobGroupName, + jobId = session.testGenerationJob, + result = if (e.message == message("testgen.message.cancelled")) MetricResult.Cancelled else MetricResult.Failed, + reason = (e as CodeTestException).code ?: "DefaultError", + reasonDesc = if (e.message == message("testgen.message.cancelled")) "${e.code}: ${e.message}" else e.message, + perfClientLatency = (Instant.now().toEpochMilli() - session.startTimeOfTestGeneration), + isCodeBlockSelected = session.isCodeBlockSelected, + artifactsUploadDuration = session.artifactUploadDuration, + buildZipFileBytes = session.srcZipFileSize, + requestId = session.startTestGenerationRequestId + ) + } - AmazonqTelemetry.utgGenerateTests( - cwsprChatProgrammingLanguage = session.programmingLanguage.languageId, - hasUserPromptSupplied = session.hasUserPromptSupplied, - isFileInWorkspace = true, - isSupportedLanguage = true, - credentialStartUrl = getStartUrl(project), - jobGroup = session.testGenerationJobGroupName, - jobId = session.testGenerationJob, - result = if (e.message == message("testgen.message.cancelled")) MetricResult.Cancelled else MetricResult.Failed, - reason = (e as CodeTestException).code ?: "DefaultError", - reasonDesc = if (e.message == message("testgen.message.cancelled")) "${e.code}: ${e.message}" else e.message, - perfClientLatency = (Instant.now().toEpochMilli() - session.startTimeOfTestGeneration), - isCodeBlockSelected = session.isCodeBlockSelected, - artifactsUploadDuration = session.artifactUploadDuration, - buildPayloadBytes = session.srcPayloadSize, - buildZipFileBytes = session.srcZipFileSize, - requestId = session.startTestGenerationRequestId - ) session.isGeneratingTests = false } finally { // Reset the flow if there is any error diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt index 2807e2cf9c2..cc54a939e61 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/controller/CodeTestChatController.kt @@ -53,6 +53,7 @@ import software.aws.toolkits.jetbrains.core.AwsClientManager import software.aws.toolkits.jetbrains.core.coroutines.EDT import software.aws.toolkits.jetbrains.core.credentials.ToolkitConnectionManager import software.aws.toolkits.jetbrains.core.credentials.pinning.QConnection +import software.aws.toolkits.jetbrains.core.credentials.sono.isInternalUser import software.aws.toolkits.jetbrains.services.amazonq.apps.AmazonQAppInitContext import software.aws.toolkits.jetbrains.services.amazonq.auth.AuthController import software.aws.toolkits.jetbrains.services.amazonq.project.RelevantDocument @@ -67,6 +68,7 @@ import software.aws.toolkits.jetbrains.services.amazonqCodeTest.messages.Incomin import software.aws.toolkits.jetbrains.services.amazonqCodeTest.model.PreviousUTGIterationContext import software.aws.toolkits.jetbrains.services.amazonqCodeTest.session.BuildAndExecuteProgressStatus import software.aws.toolkits.jetbrains.services.amazonqCodeTest.session.Session +import software.aws.toolkits.jetbrains.services.amazonqCodeTest.session.UTG_CHAT_MAX_ITERATION import software.aws.toolkits.jetbrains.services.amazonqCodeTest.storage.ChatSessionStorage import software.aws.toolkits.jetbrains.services.amazonqCodeTest.utils.constructBuildAndExecutionSummaryText import software.aws.toolkits.jetbrains.services.amazonqCodeTest.utils.runBuildOrTestCommand @@ -114,6 +116,7 @@ class CodeTestChatController( private val authController: AuthController = AuthController(), private val cs: CoroutineScope, ) : InboundAppMessagesHandler { + var buildResult = false val messenger = context.messagesFromAppToUi private val codeTestChatHelper = CodeTestChatHelper(context.messagesFromAppToUi, chatSessionStorage) private val supportedLanguage = setOf("python", "java") @@ -617,6 +620,8 @@ class CodeTestChatController( "utg_accept" -> { // open the file at test path relative to the project root val testFileAbsolutePath = Paths.get(session.projectRoot, session.testFileRelativePathToProjectRoot) + val startUrl = getStartUrl(context.project) + val isInternalUser = isInternalUser(startUrl) openOrCreateTestFileAndApplyDiff(context.project, testFileAbsolutePath, session.generatedTestDiffs.values.first(), session.openedDiffFile) session.codeReferences?.let { references -> LOG.debug { "Accepted unit tests with references: $references" } @@ -662,28 +667,31 @@ class CodeTestChatController( UiTelemetry.click(null as Project?, "unitTestGeneration_acceptDiff") - AmazonqTelemetry.utgGenerateTests( - cwsprChatProgrammingLanguage = session.programmingLanguage.languageId, - hasUserPromptSupplied = session.hasUserPromptSupplied, - isFileInWorkspace = true, - isSupportedLanguage = true, - credentialStartUrl = getStartUrl(project = context.project), - jobGroup = session.testGenerationJobGroupName, - jobId = session.testGenerationJob, - acceptedCount = session.numberOfUnitTestCasesGenerated?.toLong(), - generatedCount = session.numberOfUnitTestCasesGenerated?.toLong(), - acceptedLinesCount = session.linesOfCodeGenerated?.toLong(), - generatedLinesCount = session.linesOfCodeGenerated?.toLong(), - acceptedCharactersCount = session.charsOfCodeGenerated?.toLong(), - generatedCharactersCount = session.charsOfCodeGenerated?.toLong(), - result = MetricResult.Succeeded, - perfClientLatency = session.latencyOfTestGeneration, - isCodeBlockSelected = session.isCodeBlockSelected, - artifactsUploadDuration = session.artifactUploadDuration, - buildPayloadBytes = session.srcPayloadSize, - buildZipFileBytes = session.srcZipFileSize, - requestId = session.startTestGenerationRequestId - ) + if (session.iteration == 1) { + AmazonqTelemetry.utgGenerateTests( + cwsprChatProgrammingLanguage = session.programmingLanguage.languageId, + hasUserPromptSupplied = session.hasUserPromptSupplied, + isFileInWorkspace = true, + isSupportedLanguage = true, + credentialStartUrl = getStartUrl(project = context.project), + jobGroup = session.testGenerationJobGroupName, + jobId = session.testGenerationJob, + acceptedCount = session.numberOfUnitTestCasesGenerated?.toLong(), + generatedCount = session.numberOfUnitTestCasesGenerated?.toLong(), + acceptedLinesCount = session.linesOfCodeGenerated?.toLong(), + generatedLinesCount = session.linesOfCodeGenerated?.toLong(), + acceptedCharactersCount = session.charsOfCodeGenerated?.toLong(), + generatedCharactersCount = session.charsOfCodeGenerated?.toLong(), + result = MetricResult.Succeeded, + perfClientLatency = session.latencyOfTestGeneration, + isCodeBlockSelected = session.isCodeBlockSelected, + artifactsUploadDuration = session.artifactUploadDuration, + buildPayloadBytes = session.srcPayloadSize, + buildZipFileBytes = session.srcZipFileSize, + requestId = session.startTestGenerationRequestId + ) + } + codeTestChatHelper.addAnswer( CodeTestChatMessageContent( message = message("testgen.message.success"), @@ -691,16 +699,20 @@ class CodeTestChatController( canBeVoted = false ) ) - sessionCleanUp(session.tabId) + if (!isInternalUser) { + sessionCleanUp(message.tabId) + return + } + codeTestChatHelper.updateUI( promptInputDisabledState = false, promptInputPlaceholder = message("testgen.placeholder.enter_slash_quick_actions"), ) - /* + val taskContext = session.buildAndExecuteTaskContext if (session.iteration < 2) { - taskContext.buildCommand = getBuildCommand(message.tabId) - taskContext.executionCommand = getExecutionCommand(message.tabId) + taskContext.buildCommand = codeTestChatHelper.getSession(message.tabId).shortAnswer.buildCommand.toString() + codeTestChatHelper.addAnswer( CodeTestChatMessageContent( message = """ @@ -708,7 +720,6 @@ class CodeTestChatController( ```sh ${taskContext.buildCommand} - ${taskContext.executionCommand} ``` """.trimIndent(), type = ChatMessageType.Answer, @@ -777,11 +788,10 @@ class CodeTestChatController( ) } else { // TODO: change this hardcoded string - val monthlyLimitString = "25 out of 30" codeTestChatHelper.addAnswer( CodeTestChatMessageContent( message = """ - You have gone through all three iterations and this unit test generation workflow is complete. You have $monthlyLimitString Amazon Q Developer Agent invocations left this month. + You have gone through both iterations and this unit test generation workflow is complete. """.trimIndent(), type = ChatMessageType.Answer, ) @@ -789,11 +799,32 @@ class CodeTestChatController( codeTestChatHelper.updateUI( promptInputPlaceholder = message("testgen.placeholder.newtab") ) + AmazonqTelemetry.unitTestGeneration( + count = session.iteration.toLong() - 1, + cwsprChatProgrammingLanguage = session.programmingLanguage.languageId, + hasUserPromptSupplied = session.hasUserPromptSupplied, + isSupportedLanguage = true, + credentialStartUrl = getStartUrl(project = context.project), + jobGroup = session.testGenerationJobGroupName, + jobId = session.testGenerationJob, + acceptedCount = session.numberOfUnitTestCasesGenerated?.toLong(), + generatedCount = session.numberOfUnitTestCasesGenerated?.toLong(), + acceptedLinesCount = session.linesOfCodeGenerated?.toLong(), + generatedLinesCount = session.linesOfCodeGenerated?.toLong(), + acceptedCharactersCount = session.charsOfCodeGenerated?.toLong(), + generatedCharactersCount = session.charsOfCodeGenerated?.toLong(), + result = MetricResult.Failed, + perfClientLatency = session.latencyOfTestGeneration, + isCodeBlockSelected = session.isCodeBlockSelected, + artifactsUploadDuration = session.artifactUploadDuration, + buildZipFileBytes = session.srcZipFileSize, + requestId = session.startTestGenerationRequestId, + update = session.updateBuildCommands, + ) } - */ } - /* - //TODO: this is for unit test regeneration build iteration loop + + // TODO: this is for unit test regeneration build iteration loop "utg_regenerate" -> { // close the existing open diff in the editor. ApplicationManager.getApplication().invokeLater { @@ -810,6 +841,7 @@ class CodeTestChatController( session.testGenerationJob, session.testGenerationJobGroupName, session.programmingLanguage, + IdeCategory.JETBRAINS, session.numberOfUnitTestCasesGenerated, 0, session.linesOfCodeGenerated, @@ -821,13 +853,11 @@ class CodeTestChatController( "Successfully sent test generation telemetry. RequestId: ${ testGenerationEventResponse.responseMetadata().requestId()}" } - sessionCleanUp(session.tabId) codeTestChatHelper.updateUI( promptInputDisabledState = false, promptInputPlaceholder = message("testgen.placeholder.waiting_on_your_inputs"), ) } - */ "utg_reject" -> { ApplicationManager.getApplication().invokeLater { @@ -858,37 +888,85 @@ class CodeTestChatController( } UiTelemetry.click(null as Project?, "unitTestGeneration_rejectDiff") - AmazonqTelemetry.utgGenerateTests( + if (session.iteration == 1) { + AmazonqTelemetry.utgGenerateTests( + cwsprChatProgrammingLanguage = session.programmingLanguage.languageId, + hasUserPromptSupplied = session.hasUserPromptSupplied, + isFileInWorkspace = true, + isSupportedLanguage = true, + credentialStartUrl = getStartUrl(project = context.project), + jobGroup = session.testGenerationJobGroupName, + jobId = session.testGenerationJob, + acceptedCount = 0, + generatedCount = session.numberOfUnitTestCasesGenerated?.toLong(), + acceptedLinesCount = 0, + generatedLinesCount = session.linesOfCodeGenerated?.toLong(), + acceptedCharactersCount = 0, + generatedCharactersCount = session.charsOfCodeGenerated?.toLong(), + result = MetricResult.Succeeded, + perfClientLatency = session.latencyOfTestGeneration, + isCodeBlockSelected = session.isCodeBlockSelected, + artifactsUploadDuration = session.artifactUploadDuration, + buildPayloadBytes = session.srcPayloadSize, + buildZipFileBytes = session.srcZipFileSize, + requestId = session.startTestGenerationRequestId + ) + } else { + AmazonqTelemetry.unitTestGeneration( + count = session.iteration.toLong() - 1, + cwsprChatProgrammingLanguage = session.programmingLanguage.languageId, + hasUserPromptSupplied = session.hasUserPromptSupplied, + isSupportedLanguage = true, + credentialStartUrl = getStartUrl(project = context.project), + jobGroup = session.testGenerationJobGroupName, + jobId = session.testGenerationJob, + acceptedCount = 0, + generatedCount = session.numberOfUnitTestCasesGenerated?.toLong(), + acceptedLinesCount = 0, + generatedLinesCount = session.linesOfCodeGenerated?.toLong(), + acceptedCharactersCount = 0, + generatedCharactersCount = session.charsOfCodeGenerated?.toLong(), + result = MetricResult.Succeeded, + perfClientLatency = session.latencyOfTestGeneration, + isCodeBlockSelected = session.isCodeBlockSelected, + artifactsUploadDuration = session.artifactUploadDuration, + buildZipFileBytes = session.srcZipFileSize, + requestId = session.startTestGenerationRequestId, + update = session.updateBuildCommands, + ) + } + + sessionCleanUp(message.tabId) + } + "utg_skip_and_finish" -> { + codeTestChatHelper.addAnswer( + CodeTestChatMessageContent( + message = message("testgen.message.success"), + type = ChatMessageType.Answer, + canBeVoted = false + ) + ) + AmazonqTelemetry.unitTestGeneration( + count = session.iteration.toLong() - 1, cwsprChatProgrammingLanguage = session.programmingLanguage.languageId, hasUserPromptSupplied = session.hasUserPromptSupplied, - isFileInWorkspace = true, isSupportedLanguage = true, credentialStartUrl = getStartUrl(project = context.project), jobGroup = session.testGenerationJobGroupName, jobId = session.testGenerationJob, - acceptedCount = 0, + acceptedCount = session.numberOfUnitTestCasesGenerated?.toLong(), generatedCount = session.numberOfUnitTestCasesGenerated?.toLong(), - acceptedLinesCount = 0, + acceptedLinesCount = session.linesOfCodeGenerated?.toLong(), generatedLinesCount = session.linesOfCodeGenerated?.toLong(), - acceptedCharactersCount = 0, + acceptedCharactersCount = session.charsOfCodeGenerated?.toLong(), generatedCharactersCount = session.charsOfCodeGenerated?.toLong(), - result = MetricResult.Succeeded, + result = MetricResult.Failed, perfClientLatency = session.latencyOfTestGeneration, isCodeBlockSelected = session.isCodeBlockSelected, artifactsUploadDuration = session.artifactUploadDuration, - buildPayloadBytes = session.srcPayloadSize, buildZipFileBytes = session.srcZipFileSize, - requestId = session.startTestGenerationRequestId - ) - sessionCleanUp(message.tabId) - } - "utg_skip_and_finish" -> { - codeTestChatHelper.addAnswer( - CodeTestChatMessageContent( - message = message("testgen.message.success"), - type = ChatMessageType.Answer, - canBeVoted = false - ) + requestId = session.startTestGenerationRequestId, + update = session.updateBuildCommands, ) sessionCleanUp(message.tabId) } @@ -917,43 +995,21 @@ class CodeTestChatController( "tmpFile for build logs:\n ${buildLogsFile.path}" } - runBuildOrTestCommand(taskContext.buildCommand, buildLogsFile, context.project, isBuildCommand = true, taskContext) + runBuildOrTestCommand( + taskContext.buildCommand, + buildLogsFile, + context.project, + isBuildCommand = true, + taskContext, + session.testFileRelativePathToProjectRoot + ) while (taskContext.buildExitCode < 0) { // wait until build command finished delay(1000) } - // TODO: only go to future iterations when buildExitCode or testExitCode > 0, right now iterate regardless - if (taskContext.buildExitCode > 0) { - // TODO: handle build failure case - // ... -// return - } - taskContext.progressStatus = BuildAndExecuteProgressStatus.RUN_EXECUTION_TESTS - updateBuildAndExecuteProgressCard(taskContext.progressStatus, messageId, session.iteration) - - val testLogsFile = VirtualFileManager.getInstance().findFileByNioPath( - withContext(currentCoroutineContext()) { - Files.createTempFile(null, null) - } - ) - if (testLogsFile == null) { - // TODO: handle no log file case - return - } - LOG.debug { - "Q TestGen session: ${codeTestChatHelper.getActiveCodeTestTabId()}: " + - "tmpFile for test logs:\n ${buildLogsFile.path}" - } - delay(1000) - runBuildOrTestCommand(taskContext.executionCommand, testLogsFile, context.project, isBuildCommand = false, taskContext) - while (taskContext.testExitCode < 0) { - // wait until test command finished - delay(1000) - } - - if (taskContext.testExitCode == 0) { - taskContext.progressStatus = BuildAndExecuteProgressStatus.TESTS_EXECUTED + if (taskContext.buildExitCode == 0) { + taskContext.progressStatus = BuildAndExecuteProgressStatus.PROCESS_TEST_RESULTS updateBuildAndExecuteProgressCard(taskContext.progressStatus, messageId, session.iteration) codeTestChatHelper.addAnswer( CodeTestChatMessageContent( @@ -962,30 +1018,67 @@ class CodeTestChatController( canBeVoted = false ) ) + AmazonqTelemetry.unitTestGeneration( + count = session.iteration.toLong() - 1, + cwsprChatProgrammingLanguage = session.programmingLanguage.languageId, + hasUserPromptSupplied = session.hasUserPromptSupplied, + isSupportedLanguage = true, + credentialStartUrl = getStartUrl(project = context.project), + jobGroup = session.testGenerationJobGroupName, + jobId = session.testGenerationJob, + acceptedCount = session.numberOfUnitTestCasesGenerated?.toLong(), + generatedCount = session.numberOfUnitTestCasesGenerated?.toLong(), + acceptedLinesCount = session.linesOfCodeGenerated?.toLong(), + generatedLinesCount = session.linesOfCodeGenerated?.toLong(), + acceptedCharactersCount = session.charsOfCodeGenerated?.toLong(), + generatedCharactersCount = session.charsOfCodeGenerated?.toLong(), + result = MetricResult.Succeeded, + perfClientLatency = session.latencyOfTestGeneration, + isCodeBlockSelected = session.isCodeBlockSelected, + artifactsUploadDuration = session.artifactUploadDuration, + buildZipFileBytes = session.srcZipFileSize, + requestId = session.startTestGenerationRequestId, + update = session.updateBuildCommands, + ) sessionCleanUp(message.tabId) return } - // has test failure, we will zip the latest project and invoke backend again - taskContext.progressStatus = BuildAndExecuteProgressStatus.FIXING_TEST_CASES - val buildAndExecuteMessageId = updateBuildAndExecuteProgressCard(taskContext.progressStatus, messageId, session.iteration) + // TODO: only go to future iterations when buildExitCode or testExitCode > 0, right now iterate regardless + if (taskContext.buildExitCode > 0) { + // handle build failure case - val previousUTGIterationContext = PreviousUTGIterationContext( - buildLogFile = buildLogsFile, - testLogFile = testLogsFile, - selectedFile = session.selectedFile, - buildAndExecuteMessageId = buildAndExecuteMessageId - ) + taskContext.progressStatus = BuildAndExecuteProgressStatus.BUILD_FAILED + updateBuildAndExecuteProgressCard(taskContext.progressStatus, messageId, session.iteration) + taskContext.progressStatus = BuildAndExecuteProgressStatus.FIXING_TEST_CASES + val buildAndExecuteMessageId = updateBuildAndExecuteProgressCard( + taskContext.progressStatus, + messageId, + session.iteration + 1 + ) - val job = CodeWhispererUTGChatManager.getInstance(context.project).generateTests("", codeTestChatHelper, previousUTGIterationContext, null) - job?.join() + val previousUTGIterationContext = PreviousUTGIterationContext( + buildLogFile = buildLogsFile, + testLogFile = null, + selectedFile = session.selectedFile, + buildAndExecuteMessageId = buildAndExecuteMessageId + ) + + session.isGeneratingTests = false + session.conversationState = ConversationState.IDLE + + val job = CodeWhispererUTGChatManager.getInstance( + context.project + ).generateTests(session.userPrompt, codeTestChatHelper, previousUTGIterationContext, null) + job?.join() - taskContext.progressStatus = BuildAndExecuteProgressStatus.PROCESS_TEST_RESULTS - // session.iteration already updated in generateTests - updateBuildAndExecuteProgressCard(taskContext.progressStatus, messageId, session.iteration - 1) + return + } + // TO-DO ADD execute loop in the furture } "utg_modify_command" -> { // TODO allow user input to modify the command + session.updateBuildCommands = true codeTestChatHelper.addAnswer( CodeTestChatMessageContent( message = """ diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/model/BuildAndExecuteStatusIcon.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/model/BuildAndExecuteStatusIcon.kt index 0847cec649a..641a715fc4e 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/model/BuildAndExecuteStatusIcon.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/model/BuildAndExecuteStatusIcon.kt @@ -9,6 +9,7 @@ enum class BuildAndExecuteStatusIcon(val icon: String) { WAIT(""), CURRENT(""), DONE(""), + FAILED(""), } fun getBuildIcon(progressStatus: BuildAndExecuteProgressStatus) = @@ -16,6 +17,8 @@ fun getBuildIcon(progressStatus: BuildAndExecuteProgressStatus) = BuildAndExecuteStatusIcon.WAIT.icon } else if (progressStatus == BuildAndExecuteProgressStatus.RUN_BUILD) { BuildAndExecuteStatusIcon.CURRENT.icon + } else if (progressStatus == BuildAndExecuteProgressStatus.BUILD_FAILED || progressStatus == BuildAndExecuteProgressStatus.FIXING_TEST_CASES) { + BuildAndExecuteStatusIcon.FAILED.icon } else { BuildAndExecuteStatusIcon.DONE.icon } @@ -25,15 +28,19 @@ fun getExecutionIcon(progressStatus: BuildAndExecuteProgressStatus) = BuildAndExecuteStatusIcon.WAIT.icon } else if (progressStatus == BuildAndExecuteProgressStatus.RUN_EXECUTION_TESTS) { BuildAndExecuteStatusIcon.CURRENT.icon + } else if (progressStatus == BuildAndExecuteProgressStatus.BUILD_FAILED || progressStatus == BuildAndExecuteProgressStatus.FIXING_TEST_CASES) { + BuildAndExecuteStatusIcon.FAILED.icon } else { BuildAndExecuteStatusIcon.DONE.icon } fun getFixingTestCasesIcon(progressStatus: BuildAndExecuteProgressStatus) = - if (progressStatus < BuildAndExecuteProgressStatus.FIXING_TEST_CASES) { - BuildAndExecuteStatusIcon.WAIT.icon + if (progressStatus == BuildAndExecuteProgressStatus.BUILD_FAILED) { + BuildAndExecuteStatusIcon.FAILED.icon } else if (progressStatus == BuildAndExecuteProgressStatus.FIXING_TEST_CASES) { BuildAndExecuteStatusIcon.CURRENT.icon - } else { + } else if (progressStatus >= BuildAndExecuteProgressStatus.PROCESS_TEST_RESULTS) { BuildAndExecuteStatusIcon.DONE.icon + } else { + BuildAndExecuteStatusIcon.WAIT.icon } diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/model/PreviousUTGIterationContext.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/model/PreviousUTGIterationContext.kt index 4576a0ea0c8..9ee4c0fe336 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/model/PreviousUTGIterationContext.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/model/PreviousUTGIterationContext.kt @@ -7,7 +7,7 @@ import com.intellij.openapi.vfs.VirtualFile data class PreviousUTGIterationContext( val buildLogFile: VirtualFile, - val testLogFile: VirtualFile, + val testLogFile: VirtualFile?, val selectedFile: VirtualFile?, val buildAndExecuteMessageId: String?, ) diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/session/Session.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/session/Session.kt index f7c067b5b86..d126e2fd8ab 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/session/Session.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/session/Session.kt @@ -33,8 +33,10 @@ data class Session(val tabId: String) { var srcPayloadSize: Long = 0 var srcZipFileSize: Long = 0 var artifactUploadDuration: Long = 0 + var updateBuildCommands: Boolean = false // First iteration will have a value of 1 + var userPrompt: String = "" var iteration: Int = 0 var projectRoot: String = "/" var shortAnswer: ShortAnswer = ShortAnswer() @@ -62,6 +64,7 @@ enum class BuildAndExecuteProgressStatus { START_STEP, INSTALL_DEPENDENCIES, RUN_BUILD, + BUILD_FAILED, RUN_EXECUTION_TESTS, TESTS_EXECUTED, FIXING_TEST_CASES, diff --git a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/utils/UTGChatUtil.kt b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/utils/UTGChatUtil.kt index 9b1615c461d..e547412465c 100644 --- a/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/utils/UTGChatUtil.kt +++ b/plugins/amazonq/chat/jetbrains-community/src/software/aws/toolkits/jetbrains/services/amazonqCodeTest/utils/UTGChatUtil.kt @@ -4,11 +4,11 @@ package software.aws.toolkits.jetbrains.services.amazonqCodeTest.utils import com.intellij.build.BuildContentManager +import com.intellij.execution.configurations.GeneralCommandLine import com.intellij.execution.impl.ConsoleViewImpl import com.intellij.execution.process.OSProcessHandler import com.intellij.execution.process.ProcessAdapter import com.intellij.execution.process.ProcessEvent -import com.intellij.execution.process.ProcessHandler import com.intellij.execution.ui.ConsoleView import com.intellij.execution.ui.ConsoleViewContentType import com.intellij.openapi.application.ApplicationManager @@ -17,32 +17,33 @@ import com.intellij.openapi.util.Key import com.intellij.openapi.vfs.VirtualFile import com.intellij.openapi.vfs.VirtualFileManager import com.intellij.ui.content.impl.ContentImpl -import kotlinx.coroutines.currentCoroutineContext -import kotlinx.coroutines.withContext -import software.aws.toolkits.jetbrains.core.coroutines.EDT +import software.aws.toolkits.jetbrains.services.amazonqCodeTest.model.BuildAndExecuteStatusIcon import software.aws.toolkits.jetbrains.services.amazonqCodeTest.model.getBuildIcon import software.aws.toolkits.jetbrains.services.amazonqCodeTest.model.getExecutionIcon import software.aws.toolkits.jetbrains.services.amazonqCodeTest.model.getFixingTestCasesIcon import software.aws.toolkits.jetbrains.services.amazonqCodeTest.session.BuildAndExecuteProgressStatus import software.aws.toolkits.jetbrains.services.amazonqCodeTest.session.BuildAndExecuteTaskContext import java.io.File -import java.nio.charset.StandardCharsets -import java.nio.file.Files +import java.io.FileWriter fun constructBuildAndExecutionSummaryText(currentStatus: BuildAndExecuteProgressStatus, iterationNum: Int): String { val progressMessages = mutableListOf() if (currentStatus >= BuildAndExecuteProgressStatus.RUN_BUILD) { - val verb = if (currentStatus == BuildAndExecuteProgressStatus.RUN_BUILD) "in progress" else "complete" - progressMessages.add("${getBuildIcon(currentStatus)}: Build $verb") + val verb = when (currentStatus) { + BuildAndExecuteProgressStatus.RUN_BUILD -> "in progress" + BuildAndExecuteProgressStatus.BUILD_FAILED -> "failed" + else -> "complete" + } + progressMessages.add("${getBuildIcon(currentStatus)}: Project compiled $verb") } if (currentStatus >= BuildAndExecuteProgressStatus.RUN_EXECUTION_TESTS) { val verb = if (currentStatus == BuildAndExecuteProgressStatus.RUN_EXECUTION_TESTS) "Executing" else "Executed" - progressMessages.add("${getExecutionIcon(currentStatus)}: $verb passed tests") + progressMessages.add("${getExecutionIcon(currentStatus)}: $verb Ran tests") } - if (currentStatus >= BuildAndExecuteProgressStatus.FIXING_TEST_CASES) { + if (currentStatus >= BuildAndExecuteProgressStatus.FIXING_TEST_CASES || currentStatus == BuildAndExecuteProgressStatus.BUILD_FAILED) { val verb = if (currentStatus == BuildAndExecuteProgressStatus.FIXING_TEST_CASES) "Fixing" else "Fixed" progressMessages.add("${getFixingTestCasesIcon(currentStatus)}: $verb errors in tests") } @@ -51,9 +52,8 @@ fun constructBuildAndExecutionSummaryText(currentStatus: BuildAndExecuteProgress progressMessages.add("\n") progressMessages.add("**Test case summary**") progressMessages.add("\n") - progressMessages.add("Unit test coverage X%") - progressMessages.add("Build fails Y") - progressMessages.add("Assertion fails Z") + progressMessages.add(BuildAndExecuteStatusIcon.DONE.icon + "Build Success") + progressMessages.add(BuildAndExecuteStatusIcon.DONE.icon + "Assertion Success") } val prefix = @@ -85,18 +85,38 @@ fun runBuildOrTestCommand( project: Project, isBuildCommand: Boolean, buildAndExecuteTaskContext: BuildAndExecuteTaskContext, + testFileRelativePathToProjectRoot: String, ) { + val brazilPath = "${System.getProperty("user.home")}/.toolbox/bin:/usr/local/bin:/usr/bin:/bin:/sbin" if (localCommand.isEmpty()) { buildAndExecuteTaskContext.testExitCode = 0 return } - val repositoryPath = project.basePath ?: return - val commandParts = localCommand.split(" ") - val command = commandParts.first() - val args = commandParts.drop(1) + val projectRoot = File(project.basePath ?: return) + val testFileAbsolutePath = File(projectRoot, testFileRelativePathToProjectRoot) val file = File(tmpFile.path) - // Create Console View for Build Output + // Find the nearest Gradle root directory + var packageRoot: File? = testFileAbsolutePath.parentFile + var foundGradleRoot = false + while (packageRoot != null && packageRoot != projectRoot) { + if (File(packageRoot, "settings.gradle.kts").exists() || File(packageRoot, "build.gradle.kts").exists() || + File(packageRoot, "settings.gradle").exists() || File(packageRoot, "build.gradle").exists() + ) { + foundGradleRoot = true + break// Store the last valid Gradle root found + } + packageRoot = packageRoot.parentFile + } + var workingDir = if(foundGradleRoot){ + packageRoot ?: testFileAbsolutePath.parentFile + }else { + testFileAbsolutePath.parentFile + } + + + // If no valid Gradle directory is found, fallback to the project root + val gradleWrapper = File(packageRoot ?: projectRoot, "gradlew") val console: ConsoleView = ConsoleViewImpl(project, true) // Attach Console View to Build Tool Window @@ -104,27 +124,32 @@ fun runBuildOrTestCommand( val tabName = if (isBuildCommand) "Q TestGen Build Output" else "Q Test Gen Test Execution Output" val content = ContentImpl(console.component, tabName, true) BuildContentManager.getInstance(project).addContent(content) - // TODO: remove these tabs when they are not needed BuildContentManager.getInstance(project).setSelectedContent(content, false, false, true, null) } - val processBuilder = ProcessBuilder() - .command(listOf(command) + args) - .directory(File(repositoryPath)) - .redirectErrorStream(true) + val commandLine = when { + System.getProperty("os.name").lowercase().contains("win") -> { + GeneralCommandLine("cmd.exe", "/c", "set PATH=%PATH%;$brazilPath && $localCommand") + } + else -> { + GeneralCommandLine("sh", "-c", "export PATH=\"$brazilPath\" && $localCommand") + } + }.withWorkDirectory(workingDir) try { - val process = processBuilder.start() - val processHandler: ProcessHandler = OSProcessHandler(process, localCommand, null) + // val process = processBuilder.start() + val processHandler = OSProcessHandler(commandLine) // Attach Process Listener for Output Handling processHandler.addProcessListener(object : ProcessAdapter() { override fun onTextAvailable(event: ProcessEvent, outputType: Key<*>) { val cleanedText = cleanText(event.text) + FileWriter(file, true).use { writer -> + writer.append(cleanedText) + writer.append("\n") + } ApplicationManager.getApplication().invokeLater { - ApplicationManager.getApplication().runWriteAction { - file.appendText(cleanedText) - } + VirtualFileManager.getInstance().refreshAndFindFileByNioPath(file.toPath())?.refresh(false, false) } } @@ -173,28 +198,3 @@ private fun cleanText(input: String): String { } return cleaned.toString() } - -suspend fun combineBuildAndExecuteLogFiles( - buildLogFile: VirtualFile?, - testLogFile: VirtualFile?, -): VirtualFile? { - if (buildLogFile == null || testLogFile == null) return null - val buildLogFileContent = String(buildLogFile.contentsToByteArray(), StandardCharsets.UTF_8) - val testLogFileContent = String(testLogFile.contentsToByteArray(), StandardCharsets.UTF_8) - - val combinedContent = "Build Output:\n$buildLogFileContent\nTest Execution Output:\n$testLogFileContent" - - // Create a new virtual file and write combined content - val newFile = VirtualFileManager.getInstance().findFileByNioPath( - withContext(currentCoroutineContext()) { - Files.createTempFile(null, null) - } - ) - withContext(EDT) { - ApplicationManager.getApplication().runWriteAction { - newFile?.setBinaryContent(combinedContent.toByteArray(StandardCharsets.UTF_8)) - } - } - - return newFile -} diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codetest/sessionconfig/CodeTestSessionConfig.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codetest/sessionconfig/CodeTestSessionConfig.kt index a565da482c1..6f49ed55326 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codetest/sessionconfig/CodeTestSessionConfig.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/codetest/sessionconfig/CodeTestSessionConfig.kt @@ -29,7 +29,6 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.codetest.noFileOpe import software.aws.toolkits.jetbrains.services.codewhisperer.language.CodeWhispererProgrammingLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.language.languages.CodeWhispererUnknownLanguage import software.aws.toolkits.jetbrains.services.codewhisperer.language.programmingLanguage -import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.CODE_SCAN_CREATE_PAYLOAD_TIMEOUT_IN_SECONDS import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.DEFAULT_CODE_SCAN_TIMEOUT_IN_SECONDS import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.DEFAULT_PAYLOAD_LIMIT_IN_BYTES @@ -156,7 +155,7 @@ class CodeTestSessionConfig( it.putNextEntry(utgEntry) // 3. Add the three empty subdirectories - val buildAndExecuteLogDir = "buildAndExecuteLogDir" + val buildAndExecuteLogDir = "buildAndExecuteLogDir/" val subDirs = listOf(buildAndExecuteLogDir, "repoMapData", "testCoverageDir") subDirs.forEach { subDir -> val subDirPathString = Path.of(utgDir, subDir).toString() + "/" // Added trailing slash similar to utgRequiredArtifactsDir @@ -165,7 +164,9 @@ class CodeTestSessionConfig( it.putNextEntry(zipEntry) } if (buildAndExecuteLogFile != null) { - it.putNextEntry(Path.of(utgDir, buildAndExecuteLogDir, "buildAndExecuteLog").name, buildAndExecuteLogFile.inputStream) + val buildLogFilePath = Path.of(utgDir, buildAndExecuteLogDir, "buildAndExecuteLog") + .toString().replace("\\", "/") // Ensures consistent path format + it.putNextEntry(buildLogFilePath, buildAndExecuteLogFile.inputStream) } if (payloadMetadata.payloadLimitCrossed == true) { // write payloadMetadata.payloadManifest into a json file in repoMapData directory. manifest is Set> write into file first or ina byte stream @@ -218,7 +219,7 @@ class CodeTestSessionConfig( for (module in project.modules) { val changeListManager = ChangeListManager.getInstance(module.project) module.guessModuleDir()?.let { moduleDir -> - val gitIgnoreFilteringUtil = GitIgnoreFilteringUtil(moduleDir, CodeWhispererConstants.FeatureName.TEST_GENERATION) + val gitIgnoreFilteringUtil = GitIgnoreFilteringUtil(moduleDir) stack.push(moduleDir) while (stack.isNotEmpty()) { val current = stack.pop() diff --git a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt index e2e55b86430..a373e95c7e0 100644 --- a/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt +++ b/plugins/amazonq/codewhisperer/jetbrains-community/src/software/aws/toolkits/jetbrains/services/codewhisperer/credentials/CodeWhispererClientAdaptor.kt @@ -77,6 +77,10 @@ interface CodeWhispererClientAdaptor : Disposable { firstRequest: GenerateCompletionsRequest, ): Sequence + fun generateCompletions( + firstRequest: GenerateCompletionsRequest, + ): GenerateCompletionsResponse + fun createUploadUrl( request: CreateUploadUrlRequest, ): CreateUploadUrlResponse @@ -102,7 +106,7 @@ interface CodeWhispererClientAdaptor : Disposable { fun listAvailableCustomizations(): List - fun startTestGeneration(uploadId: String, targetCode: List, userInput: String): StartTestGenerationResponse + fun startTestGeneration(uploadId: String, targetCode: List, userInput: String, testGenerationJobGroupName: String): StartTestGenerationResponse fun getTestGeneration(jobId: String, jobGroupName: String): GetTestGenerationResponse @@ -323,6 +327,8 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW yield(response) } while (!nextToken.isNullOrEmpty()) } + override fun generateCompletions(firstRequest: GenerateCompletionsRequest): GenerateCompletionsResponse = + bearerClient().generateCompletions(firstRequest) override fun createUploadUrl(request: CreateUploadUrlRequest): CreateUploadUrlResponse = bearerClient().createUploadUrl(request) @@ -372,11 +378,17 @@ open class CodeWhispererClientAdaptorImpl(override val project: Project) : CodeW } } - override fun startTestGeneration(uploadId: String, targetCode: List, userInput: String): StartTestGenerationResponse = + override fun startTestGeneration( + uploadId: String, + targetCode: List, + userInput: String, + testGenerationJobGroupName: String, + ): StartTestGenerationResponse = bearerClient().startTestGeneration { builder -> builder.uploadId(uploadId) builder.targetCodeList(targetCode) builder.userInput(userInput) + builder.testGenerationJobGroupName(testGenerationJobGroupName) // TODO: client token } diff --git a/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties b/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties index ed823f2b891..fbd7dc82f0c 100644 --- a/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties +++ b/plugins/core/resources/resources/software/aws/toolkits/resources/MessagesBundle.properties @@ -2077,6 +2077,7 @@ testgen.placeholder.newtab=Ask any coding question or type \u0022/\u0022 for act testgen.placeholder.select_an_option = Please select an action to proceed (Accept or Reject) testgen.placeholder.view_diff=Select View Diff to see the generated unit tests testgen.placeholder.waiting_on_your_inputs=Waiting on your inputs... +testgen.progressbar.build_and_execute=Fixing test errors... testgen.progressbar.generate_unit_tests=Generating unit tests... toolkit.login.aws_builder_id.already_connected.cancel=Use existing AWS Builder ID toolkit.login.aws_builder_id.already_connected.message=You already signed in with an AWS Builder ID.\nSign out to add another?