@@ -5,21 +5,15 @@ package software.aws.toolkits.jetbrains.services.codewhisperer.codescan
55
66import com.intellij.openapi.fileEditor.FileEditor
77import com.intellij.openapi.fileEditor.FileEditorManager
8- import com.intellij.openapi.vfs.VirtualFile
98import com.intellij.psi.PsiFile
10- import com.intellij.testFramework.replaceService
119import kotlinx.coroutines.runBlocking
1210import kotlinx.coroutines.test.runTest
1311import org.apache.commons.codec.digest.DigestUtils
1412import org.assertj.core.api.Assertions.assertThat
1513import org.junit.Before
1614import org.junit.Test
1715import org.mockito.ArgumentMatchers.anyString
18- import org.mockito.Mockito
1916import org.mockito.Mockito.mock
20- import org.mockito.Mockito.times
21- import org.mockito.Mockito.`when`
22- import org.mockito.internal.verification.Times
2317import org.mockito.kotlin.any
2418import org.mockito.kotlin.argumentCaptor
2519import org.mockito.kotlin.doNothing
@@ -28,7 +22,9 @@ import org.mockito.kotlin.inOrder
2822import org.mockito.kotlin.isNull
2923import org.mockito.kotlin.spy
3024import org.mockito.kotlin.stub
25+ import org.mockito.kotlin.times
3126import org.mockito.kotlin.verify
27+ import org.mockito.kotlin.whenever
3228import software.amazon.awssdk.awscore.exception.AwsErrorDetails
3329import software.amazon.awssdk.services.codewhisperer.model.CodeWhispererException
3430import software.amazon.awssdk.services.codewhispererruntime.model.CreateUploadUrlRequest
@@ -42,7 +38,6 @@ import software.aws.toolkits.jetbrains.utils.isInstanceOf
4238import software.aws.toolkits.jetbrains.utils.isInstanceOfSatisfying
4339import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule
4440import software.aws.toolkits.telemetry.CodewhispererLanguage
45- import java.io.File
4641import java.io.FileInputStream
4742import java.lang.management.ManagementFactory
4843import java.util.Base64
@@ -52,55 +47,19 @@ import kotlin.io.path.relativeTo
5247import kotlin.test.assertNotNull
5348
5449class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase (PythonCodeInsightTestFixtureRule ()) {
55- private lateinit var psifile: PsiFile
56- private lateinit var psifile2: PsiFile
57- private lateinit var psifile3: PsiFile
58- private lateinit var psifile4: PsiFile
59- private lateinit var psifilePerformanceTest: PsiFile
60- private lateinit var psifilePerformanceTest2: PsiFile
61- private lateinit var file: File
62- private lateinit var file2: File
63- private lateinit var file3: File
64- private lateinit var file4: File
65- private lateinit var performanceTestfileWithPayload200KB: File
66- private lateinit var performanceTestfileWithPayload150KB: File
67- private lateinit var virtualFile3: VirtualFile
68- private lateinit var virtualFile4: VirtualFile
69- private lateinit var sessionConfigSpy: CodeScanSessionConfig
70- private lateinit var sessionConfigSpy2: CodeScanSessionConfig
71- private lateinit var sessionConfigSpy3: CodeScanSessionConfig
72- private lateinit var sessionConfigSpy4: CodeScanSessionConfig
50+ private val codeScanName = UUID .randomUUID().toString()
7351 private val payloadContext = PayloadContext (CodewhispererLanguage .Python , 1 , 1 , 10 , listOf (), 600 , 200 )
74- private lateinit var codeScanSessionContext: CodeScanSessionContext
75- private lateinit var codeScanSessionContext2: CodeScanSessionContext
76- private lateinit var codeScanSessionContext3: CodeScanSessionContext
52+
53+ private lateinit var pyPsiFile: PsiFile
54+ private lateinit var ktPsiFile: PsiFile
55+ private lateinit var pySession: CodeScanSessionConfig
7756 private lateinit var codeScanSessionSpy: CodeWhispererCodeScanSession
78- private lateinit var codeScanSessionSpy2: CodeWhispererCodeScanSession
79- private lateinit var codeScanSessionSpy3: CodeWhispererCodeScanSession
80- private val codeScanName = UUID .randomUUID().toString()
8157
8258 @Before
8359 override fun setup () {
8460 super .setup()
8561
86- psifile2 = projectRule.fixture.addFileToProject(
87- " /subtract.java" ,
88- """ public class MathOperations {
89- public static int subtract(int a, int b) {
90- return a - b;
91- }
92- public static void main(String[] args) {
93- int num1 = 10;
94- int num2 = 5;
95- int result = subtract(num1, num2);
96- System.out.println(result);
97- }
98- }
99- """ .trimMargin()
100- )
101- file2 = psifile2.virtualFile.toNioPath().toFile()
102-
103- psifile = projectRule.fixture.addFileToProject(
62+ pyPsiFile = projectRule.fixture.addFileToProject(
10463 " /test.py" ,
10564 """ import numpy as np
10665 import from module1 import helper
@@ -110,9 +69,8 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
11069
11170 """ .trimMargin()
11271 )
113- file = psifile.virtualFile.toNioPath().toFile()
11472
115- psifile3 = projectRule.fixture.addFileToProject(
73+ ktPsiFile = projectRule.fixture.addFileToProject(
11674 " /test.kt" ,
11775 // write simple addition function in kotlin
11876 """
@@ -124,96 +82,68 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
12482 }
12583 """ .trimMargin()
12684 )
127- virtualFile3 = psifile3.virtualFile
128- file3 = virtualFile3.toNioPath().toFile()
12985
130- psifile4 = projectRule.fixture.addFileToProject(
131- " ../test.java" ,
132- """
133- public class Addition {
134- public static void main(String[] args) {
135- int a = 1;
136- int b = 2;
137- int c = a + b;
138- System.out.println(c);
86+ projectRule.fixture.addFileToProject(
87+ " /subtract.java" ,
88+ """ public class MathOperations {
89+ public static int subtract(int a, int b) {
90+ return a - b;
13991 }
140- }
141- """
142- )
143- virtualFile4 = psifile4.virtualFile
144- file4 = virtualFile4.toNioPath().toFile()
145-
146- // Create a 200KB file
147- val content = " a" .repeat(200 * 1024 )
148- psifilePerformanceTest = projectRule.fixture.addFileToProject(" test.txt" , content)
149- performanceTestfileWithPayload200KB = psifilePerformanceTest.virtualFile.toNioPath().toFile()
150-
151- sessionConfigSpy3 = spy(
152- CodeScanSessionConfig .create(
153- psifilePerformanceTest.virtualFile,
154- project,
155- CodeWhispererConstants .CodeAnalysisScope .FILE
156- )
157- )
158- setupResponse(psifilePerformanceTest.virtualFile.toNioPath().relativeTo(sessionConfigSpy3.projectRoot.toNioPath()))
159-
160- // Create a 150KB file
161- val codeContentForPayload = " a" .repeat(150 * 1024 )
162- psifilePerformanceTest2 = projectRule.fixture.addFileToProject(" test.txt" , codeContentForPayload)
163- performanceTestfileWithPayload150KB = psifilePerformanceTest2.virtualFile.toNioPath().toFile()
164-
165- sessionConfigSpy4 = spy(
166- CodeScanSessionConfig .create(
167- psifilePerformanceTest2.virtualFile,
168- project,
169- CodeWhispererConstants .CodeAnalysisScope .FILE
170- )
171- )
172- setupResponse(psifilePerformanceTest2.virtualFile.toNioPath().relativeTo(sessionConfigSpy4.projectRoot.toNioPath()))
173- sessionConfigSpy = spy(
174- CodeScanSessionConfig .create(
175- psifile.virtualFile,
176- project,
177- CodeWhispererConstants .CodeAnalysisScope .FILE
178- )
92+ public static void main(String[] args) {
93+ int num1 = 10;
94+ int num2 = 5;
95+ int result = subtract(num1, num2);
96+ System.out.println(result);
97+ }
98+ }
99+ """ .trimMargin()
179100 )
180101
181- sessionConfigSpy2 = spy(
102+ pySession = spy(
182103 CodeScanSessionConfig .create(
183- virtualFile4 ,
104+ pyPsiFile.virtualFile ,
184105 project,
185106 CodeWhispererConstants .CodeAnalysisScope .FILE
186107 )
187108 )
109+ setupResponse(pyPsiFile.virtualFile.toNioPath().relativeTo(pySession.projectRoot.toNioPath()))
188110
189- setupResponse(psifile.virtualFile.toNioPath().relativeTo(sessionConfigSpy.projectRoot.toNioPath()))
190-
191- sessionConfigSpy.stub {
192- onGeneric { sessionConfigSpy.createPayload() }.thenReturn(Payload (payloadContext, file))
111+ pySession.stub {
112+ onGeneric { pySession.createPayload() }.thenReturn(Payload (payloadContext, pyPsiFile.virtualFile.toNioPath().toFile()))
193113 }
194114
195115 // Mock CodeWhispererClient needs to be setup before initializing CodeWhispererCodeScanSession
196- codeScanSessionContext = CodeScanSessionContext (project, sessionConfigSpy, CodeWhispererConstants .CodeAnalysisScope .FILE )
197- codeScanSessionSpy = spy(CodeWhispererCodeScanSession (codeScanSessionContext))
198- doNothing().`when `(codeScanSessionSpy).uploadArtifactToS3(any(), any(), any(), any(), isNull(), any())
199-
200- codeScanSessionContext2 = CodeScanSessionContext (project, sessionConfigSpy3, CodeWhispererConstants .CodeAnalysisScope .FILE )
201- codeScanSessionSpy2 = spy(CodeWhispererCodeScanSession (codeScanSessionContext2))
202- doNothing().`when `(codeScanSessionSpy2).uploadArtifactToS3(any(), any(), any(), any(), isNull(), any())
116+ val pySessionContext = CodeScanSessionContext (project, pySession, CodeWhispererConstants .CodeAnalysisScope .FILE )
117+ codeScanSessionSpy = spy(CodeWhispererCodeScanSession (pySessionContext))
118+ doNothing().whenever(codeScanSessionSpy).uploadArtifactToS3(any(), any(), any(), any(), isNull(), any())
203119
204- codeScanSessionContext3 = CodeScanSessionContext (project, sessionConfigSpy4, CodeWhispererConstants .CodeAnalysisScope .FILE )
205- codeScanSessionSpy3 = spy(CodeWhispererCodeScanSession (codeScanSessionContext3))
206- doNothing().`when `(codeScanSessionSpy3).uploadArtifactToS3(any(), any(), any(), any(), isNull(), any())
207120 mockClient.stub {
208- onGeneric { createUploadUrl(any()) }.thenReturn(fakeCreateUploadUrlResponse)
209- onGeneric { createCodeScan(any(), any()) }.thenReturn(fakeCreateCodeScanResponse)
210- onGeneric { getCodeScan(any(), any()) }.thenReturn(fakeGetCodeScanResponse)
211- onGeneric { listCodeScanFindings(any(), any()) }.thenReturn(fakeListCodeScanFindingsResponse)
121+ // setupResponse dynamically modifies these fake responses so this is very hard to follow and makes me question if we even need this
122+ onGeneric { createUploadUrl(any()) }.thenAnswer { fakeCreateUploadUrlResponse }
123+ onGeneric { createCodeScan(any(), any()) }.thenAnswer { fakeCreateCodeScanResponse }
124+ onGeneric { getCodeScan(any(), any()) }.thenAnswer { fakeGetCodeScanResponse }
125+ onGeneric { listCodeScanFindings(any(), any()) }.thenAnswer { fakeListCodeScanFindingsResponse }
212126 }
213127 }
214128
215129 @Test
216- fun `test run() - measure CPU and memory usage` () {
130+ fun `test run() - measure CPU and memory usage with payload of 200KB` () {
131+ // Create a 200KB file
132+ val content = " a" .repeat(200 * 1024 )
133+ val psiFile = projectRule.fixture.addFileToProject(" test.txt" , content)
134+
135+ val sessionConfig = spy(
136+ CodeScanSessionConfig .create(
137+ psiFile.virtualFile,
138+ project,
139+ CodeWhispererConstants .CodeAnalysisScope .FILE
140+ )
141+ )
142+ setupResponse(psiFile.virtualFile.toNioPath().relativeTo(sessionConfig.projectRoot.toNioPath()))
143+ val sessionContext = CodeScanSessionContext (project, sessionConfig, CodeWhispererConstants .CodeAnalysisScope .FILE )
144+ val session = spy(CodeWhispererCodeScanSession (sessionContext))
145+ doNothing().whenever(session).uploadArtifactToS3(any(), any(), any(), any(), isNull(), any())
146+
217147 // Set up CPU and Memory monitoring
218148 val runtime = Runtime .getRuntime()
219149 val bean = ManagementFactory .getThreadMXBean()
@@ -223,7 +153,7 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
223153
224154 // Run the code scan
225155 runBlocking {
226- codeScanSessionSpy2 .run ()
156+ session .run ()
227157 }
228158
229159 // Calculate CPU and memory usage
@@ -248,6 +178,22 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
248178
249179 @Test
250180 fun `test run() - measure CPU and memory usage with payload of 150KB` () {
181+ // Create a 150KB file
182+ val codeContentForPayload = " a" .repeat(150 * 1024 )
183+ val psiFile = projectRule.fixture.addFileToProject(" test.txt" , codeContentForPayload)
184+
185+ val sessionConfig = spy(
186+ CodeScanSessionConfig .create(
187+ psiFile.virtualFile,
188+ project,
189+ CodeWhispererConstants .CodeAnalysisScope .FILE
190+ )
191+ )
192+ setupResponse(psiFile.virtualFile.toNioPath().relativeTo(sessionConfig.projectRoot.toNioPath()))
193+ val sessionContext = CodeScanSessionContext (project, sessionConfig, CodeWhispererConstants .CodeAnalysisScope .FILE )
194+ val session = spy(CodeWhispererCodeScanSession (sessionContext))
195+ doNothing().whenever(session).uploadArtifactToS3(any(), any(), any(), any(), isNull(), any())
196+
251197 // Set up CPU and Memory monitoring
252198 val runtime = Runtime .getRuntime()
253199 val bean = ManagementFactory .getThreadMXBean()
@@ -257,7 +203,7 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
257203
258204 // Run the code scan
259205 runBlocking {
260- codeScanSessionSpy3 .run ()
206+ session .run ()
261207 }
262208
263209 // Calculate CPU and memory usage
@@ -282,6 +228,7 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
282228
283229 @Test
284230 fun `test createUploadUrlAndUpload()` () {
231+ val file = pyPsiFile.virtualFile.toNioPath().toFile()
285232 val fileMd5: String = Base64 .getEncoder().encodeToString(DigestUtils .md5(FileInputStream (file)))
286233 codeScanSessionSpy.stub {
287234 onGeneric { codeScanSessionSpy.createUploadUrl(any(), any(), any()) }
@@ -327,7 +274,9 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
327274
328275 @Test
329276 fun `test run() - happypath` () = runTest {
330- assertNotNull(sessionConfigSpy)
277+ val file = pyPsiFile.virtualFile.toNioPath().toFile()
278+
279+ assertNotNull(pySession)
331280 val codeScanResponse = codeScanSessionSpy.run ()
332281 assertThat(codeScanResponse).isInstanceOfSatisfying<CodeScanResponse .Success > {
333282 assertThat(it.issues).hasSize(2 )
@@ -336,14 +285,38 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
336285 }
337286
338287 val inOrder = inOrder(codeScanSessionSpy)
339- inOrder.verify(codeScanSessionSpy, Times (1 )).createUploadUrlAndUpload(eq(file), eq(" SourceCode" ), anyString())
340- inOrder.verify(codeScanSessionSpy, Times (1 )).createCodeScan(eq(CodewhispererLanguage .Python .toString()), anyString())
341- inOrder.verify(codeScanSessionSpy, Times (1 )).getCodeScan(any())
342- inOrder.verify(codeScanSessionSpy, Times (1 )).listCodeScanFindings(eq(" jobId" ), eq(null ))
288+ inOrder.verify(codeScanSessionSpy, times (1 )).createUploadUrlAndUpload(eq(file), eq(" SourceCode" ), anyString())
289+ inOrder.verify(codeScanSessionSpy, times (1 )).createCodeScan(eq(CodewhispererLanguage .Python .toString()), anyString())
290+ inOrder.verify(codeScanSessionSpy, times (1 )).getCodeScan(any())
291+ inOrder.verify(codeScanSessionSpy, times (1 )).listCodeScanFindings(eq(" jobId" ), eq(null ))
343292 }
344293
345294 @Test
346295 fun `test createPayload for files outside Project Root` () {
296+ val externalFile = projectRule.fixture.addFileToProject(
297+ " ../test.java" ,
298+ """
299+ public class Addition {
300+ public static void main(String[] args) {
301+ int a = 1;
302+ int b = 2;
303+ int c = a + b;
304+ System.out.println(c);
305+ }
306+ }
307+ """
308+ )
309+
310+ val sessionConfigSpy2 = spy(
311+ CodeScanSessionConfig .create(
312+ externalFile.virtualFile,
313+ project,
314+ CodeWhispererConstants .CodeAnalysisScope .FILE
315+ )
316+ )
317+
318+ setupResponse(pyPsiFile.virtualFile.toNioPath().relativeTo(pySession.projectRoot.toNioPath()))
319+
347320 val payload = sessionConfigSpy2.createPayload()
348321 assertNotNull(payload)
349322 val payloadZipFile = ZipFile (payload.srcZip)
@@ -354,16 +327,13 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
354327
355328 @Test
356329 fun `unsupported languages file scan fail` () = runTest {
357- scanManagerSpy = Mockito .spy(CodeWhispererCodeScanManager .getInstance(projectRule.project))
358- projectRule.project.replaceService(CodeWhispererCodeScanManager ::class .java, scanManagerSpy, disposableRule.disposable)
359-
360- val fileEditorManager = mock(FileEditorManager ::class .java)
361- val selectedEditor = mock(FileEditor ::class .java)
362- val editorList: MutableList <FileEditor > = mutableListOf (selectedEditor)
330+ val fileEditorManager = mock<FileEditorManager >()
331+ val selectedEditor = mock<FileEditor >()
332+ val editorList = mutableListOf (selectedEditor)
363333
364- ` when ` (fileEditorManager.selectedEditorWithRemotes).thenReturn(editorList)
365- ` when ` (fileEditorManager.selectedEditor).thenReturn(selectedEditor)
366- ` when ` (selectedEditor.file).thenReturn(virtualFile3 )
334+ whenever (fileEditorManager.selectedEditorWithRemotes).thenReturn(editorList)
335+ whenever (fileEditorManager.selectedEditor).thenReturn(selectedEditor)
336+ whenever (selectedEditor.file).thenReturn(ktPsiFile.virtualFile )
367337
368338 scanManagerSpy.runCodeScan(CodeWhispererConstants .CodeAnalysisScope .FILE )
369339 // verify that function was run but none of the results/error handling methods were called.
@@ -374,7 +344,7 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
374344
375345 @Test
376346 fun `test run() - file scans limit reached` () = runTest {
377- assertNotNull(sessionConfigSpy )
347+ assertNotNull(pySession )
378348
379349 mockClient.stub {
380350 onGeneric { codeScanSessionSpy.createUploadUrlAndUpload(any(), any(), any()) }.thenThrow(
@@ -441,7 +411,7 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
441411
442412 @Test
443413 fun `test run() - getCodeScan pending timeout` () = runTest {
444- sessionConfigSpy .stub {
414+ pySession .stub {
445415 onGeneric { overallJobTimeoutInSeconds() }.thenReturn(5 )
446416 }
447417 mockClient.stub {
0 commit comments