@@ -5,21 +5,15 @@ package software.aws.toolkits.jetbrains.services.codewhisperer.codescan
5
5
6
6
import com.intellij.openapi.fileEditor.FileEditor
7
7
import com.intellij.openapi.fileEditor.FileEditorManager
8
- import com.intellij.openapi.vfs.VirtualFile
9
8
import com.intellij.psi.PsiFile
10
- import com.intellij.testFramework.replaceService
11
9
import kotlinx.coroutines.runBlocking
12
10
import kotlinx.coroutines.test.runTest
13
11
import org.apache.commons.codec.digest.DigestUtils
14
12
import org.assertj.core.api.Assertions.assertThat
15
13
import org.junit.Before
16
14
import org.junit.Test
17
15
import org.mockito.ArgumentMatchers.anyString
18
- import org.mockito.Mockito
19
16
import org.mockito.Mockito.mock
20
- import org.mockito.Mockito.times
21
- import org.mockito.Mockito.`when`
22
- import org.mockito.internal.verification.Times
23
17
import org.mockito.kotlin.any
24
18
import org.mockito.kotlin.argumentCaptor
25
19
import org.mockito.kotlin.doNothing
@@ -28,7 +22,9 @@ import org.mockito.kotlin.inOrder
28
22
import org.mockito.kotlin.isNull
29
23
import org.mockito.kotlin.spy
30
24
import org.mockito.kotlin.stub
25
+ import org.mockito.kotlin.times
31
26
import org.mockito.kotlin.verify
27
+ import org.mockito.kotlin.whenever
32
28
import software.amazon.awssdk.awscore.exception.AwsErrorDetails
33
29
import software.amazon.awssdk.services.codewhisperer.model.CodeWhispererException
34
30
import software.amazon.awssdk.services.codewhispererruntime.model.CreateUploadUrlRequest
@@ -42,7 +38,6 @@ import software.aws.toolkits.jetbrains.utils.isInstanceOf
42
38
import software.aws.toolkits.jetbrains.utils.isInstanceOfSatisfying
43
39
import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule
44
40
import software.aws.toolkits.telemetry.CodewhispererLanguage
45
- import java.io.File
46
41
import java.io.FileInputStream
47
42
import java.lang.management.ManagementFactory
48
43
import java.util.Base64
@@ -52,55 +47,19 @@ import kotlin.io.path.relativeTo
52
47
import kotlin.test.assertNotNull
53
48
54
49
class 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()
73
51
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
77
56
private lateinit var codeScanSessionSpy: CodeWhispererCodeScanSession
78
- private lateinit var codeScanSessionSpy2: CodeWhispererCodeScanSession
79
- private lateinit var codeScanSessionSpy3: CodeWhispererCodeScanSession
80
- private val codeScanName = UUID .randomUUID().toString()
81
57
82
58
@Before
83
59
override fun setup () {
84
60
super .setup()
85
61
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(
104
63
" /test.py" ,
105
64
""" import numpy as np
106
65
import from module1 import helper
@@ -110,9 +69,8 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
110
69
111
70
""" .trimMargin()
112
71
)
113
- file = psifile.virtualFile.toNioPath().toFile()
114
72
115
- psifile3 = projectRule.fixture.addFileToProject(
73
+ ktPsiFile = projectRule.fixture.addFileToProject(
116
74
" /test.kt" ,
117
75
// write simple addition function in kotlin
118
76
"""
@@ -124,96 +82,68 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
124
82
}
125
83
""" .trimMargin()
126
84
)
127
- virtualFile3 = psifile3.virtualFile
128
- file3 = virtualFile3.toNioPath().toFile()
129
85
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;
139
91
}
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()
179
100
)
180
101
181
- sessionConfigSpy2 = spy(
102
+ pySession = spy(
182
103
CodeScanSessionConfig .create(
183
- virtualFile4 ,
104
+ pyPsiFile.virtualFile ,
184
105
project,
185
106
CodeWhispererConstants .CodeAnalysisScope .FILE
186
107
)
187
108
)
109
+ setupResponse(pyPsiFile.virtualFile.toNioPath().relativeTo(pySession.projectRoot.toNioPath()))
188
110
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()))
193
113
}
194
114
195
115
// 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())
203
119
204
- codeScanSessionContext3 = CodeScanSessionContext (project, sessionConfigSpy4, CodeWhispererConstants .CodeAnalysisScope .FILE )
205
- codeScanSessionSpy3 = spy(CodeWhispererCodeScanSession (codeScanSessionContext3))
206
- doNothing().`when `(codeScanSessionSpy3).uploadArtifactToS3(any(), any(), any(), any(), isNull(), any())
207
120
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 }
212
126
}
213
127
}
214
128
215
129
@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
+
217
147
// Set up CPU and Memory monitoring
218
148
val runtime = Runtime .getRuntime()
219
149
val bean = ManagementFactory .getThreadMXBean()
@@ -223,7 +153,7 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
223
153
224
154
// Run the code scan
225
155
runBlocking {
226
- codeScanSessionSpy2 .run ()
156
+ session .run ()
227
157
}
228
158
229
159
// Calculate CPU and memory usage
@@ -248,6 +178,22 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
248
178
249
179
@Test
250
180
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
+
251
197
// Set up CPU and Memory monitoring
252
198
val runtime = Runtime .getRuntime()
253
199
val bean = ManagementFactory .getThreadMXBean()
@@ -257,7 +203,7 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
257
203
258
204
// Run the code scan
259
205
runBlocking {
260
- codeScanSessionSpy3 .run ()
206
+ session .run ()
261
207
}
262
208
263
209
// Calculate CPU and memory usage
@@ -282,6 +228,7 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
282
228
283
229
@Test
284
230
fun `test createUploadUrlAndUpload()` () {
231
+ val file = pyPsiFile.virtualFile.toNioPath().toFile()
285
232
val fileMd5: String = Base64 .getEncoder().encodeToString(DigestUtils .md5(FileInputStream (file)))
286
233
codeScanSessionSpy.stub {
287
234
onGeneric { codeScanSessionSpy.createUploadUrl(any(), any(), any()) }
@@ -327,7 +274,9 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
327
274
328
275
@Test
329
276
fun `test run() - happypath` () = runTest {
330
- assertNotNull(sessionConfigSpy)
277
+ val file = pyPsiFile.virtualFile.toNioPath().toFile()
278
+
279
+ assertNotNull(pySession)
331
280
val codeScanResponse = codeScanSessionSpy.run ()
332
281
assertThat(codeScanResponse).isInstanceOfSatisfying<CodeScanResponse .Success > {
333
282
assertThat(it.issues).hasSize(2 )
@@ -336,14 +285,38 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
336
285
}
337
286
338
287
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 ))
343
292
}
344
293
345
294
@Test
346
295
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
+
347
320
val payload = sessionConfigSpy2.createPayload()
348
321
assertNotNull(payload)
349
322
val payloadZipFile = ZipFile (payload.srcZip)
@@ -354,16 +327,13 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
354
327
355
328
@Test
356
329
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)
363
333
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 )
367
337
368
338
scanManagerSpy.runCodeScan(CodeWhispererConstants .CodeAnalysisScope .FILE )
369
339
// verify that function was run but none of the results/error handling methods were called.
@@ -374,7 +344,7 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
374
344
375
345
@Test
376
346
fun `test run() - file scans limit reached` () = runTest {
377
- assertNotNull(sessionConfigSpy )
347
+ assertNotNull(pySession )
378
348
379
349
mockClient.stub {
380
350
onGeneric { codeScanSessionSpy.createUploadUrlAndUpload(any(), any(), any()) }.thenThrow(
@@ -441,7 +411,7 @@ class CodeWhispererCodeFileScanTest : CodeWhispererCodeScanTestBase(PythonCodeIn
441
411
442
412
@Test
443
413
fun `test run() - getCodeScan pending timeout` () = runTest {
444
- sessionConfigSpy .stub {
414
+ pySession .stub {
445
415
onGeneric { overallJobTimeoutInSeconds() }.thenReturn(5 )
446
416
}
447
417
mockClient.stub {
0 commit comments