@@ -5,6 +5,7 @@ package software.aws.toolkits.jetbrains.services.codewhisperer
5
5
6
6
import com.intellij.openapi.application.ApplicationManager
7
7
import com.intellij.openapi.command.WriteCommandAction
8
+ import com.intellij.openapi.editor.Document
8
9
import com.intellij.openapi.editor.Editor
9
10
import com.intellij.openapi.editor.RangeMarker
10
11
import com.intellij.openapi.editor.event.DocumentEvent
@@ -24,6 +25,7 @@ import org.mockito.internal.verification.Times
24
25
import org.mockito.kotlin.any
25
26
import org.mockito.kotlin.argumentCaptor
26
27
import org.mockito.kotlin.atLeastOnce
28
+ import org.mockito.kotlin.doNothing
27
29
import org.mockito.kotlin.doReturn
28
30
import org.mockito.kotlin.mock
29
31
import org.mockito.kotlin.spy
@@ -46,6 +48,7 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.model.SessionConte
46
48
import software.aws.toolkits.jetbrains.services.codewhisperer.popup.CodeWhispererPopupManager.Companion.CODEWHISPERER_USER_ACTION_PERFORMED
47
49
import software.aws.toolkits.jetbrains.services.codewhisperer.service.RequestContext
48
50
import software.aws.toolkits.jetbrains.services.codewhisperer.service.ResponseContext
51
+ import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeCoverageTokens
49
52
import software.aws.toolkits.jetbrains.services.codewhisperer.telemetry.CodeWhispererCodeCoverageTracker
50
53
import software.aws.toolkits.jetbrains.services.codewhisperer.toolwindow.CodeWhispererCodeReferenceManager
51
54
import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhispererConstants.TOTAL_SECONDS_IN_MINUTE
@@ -55,16 +58,14 @@ import software.aws.toolkits.jetbrains.settings.AwsSettings
55
58
import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule
56
59
import software.aws.toolkits.telemetry.CodewhispererCompletionType
57
60
import software.aws.toolkits.telemetry.CodewhispererLanguage
58
- import java.util.concurrent.atomic.AtomicInteger
59
61
60
62
class CodeWhispererCodeCoverageTrackerTest {
61
63
private class TestCodePercentageTracker (
62
64
timeWindowInSec : Long ,
63
65
language : CodewhispererLanguage ,
64
- acceptedTokens : AtomicInteger = AtomicInteger (0),
65
- totalTokens : AtomicInteger = AtomicInteger (0),
66
- rangeMarkers : MutableList <RangeMarker > = mutableListOf()
67
- ) : CodeWhispererCodeCoverageTracker(timeWindowInSec, language, acceptedTokens, totalTokens, rangeMarkers)
66
+ rangeMarkers : MutableList <RangeMarker > = mutableListOf(),
67
+ codeCoverageTokens : MutableMap <Document , CodeCoverageTokens > = mutableMapOf()
68
+ ) : CodeWhispererCodeCoverageTracker(timeWindowInSec, language, rangeMarkers, codeCoverageTokens)
68
69
69
70
private class TestTelemetryService (
70
71
publisher : TelemetryPublisher = NoOpPublisher (),
@@ -153,7 +154,7 @@ class CodeWhispererCodeCoverageTrackerTest {
153
154
154
155
@Test
155
156
fun `test tracker is listening to document changes and increment totalTokens - add new code` () {
156
- val pythonTracker = spy(TestCodePercentageTracker (TOTAL_SECONDS_IN_MINUTE , CodewhispererLanguage .Python , AtomicInteger ( 0 ), AtomicInteger ( 0 ) ))
157
+ val pythonTracker = spy(TestCodePercentageTracker (TOTAL_SECONDS_IN_MINUTE , CodewhispererLanguage .Python ))
157
158
CodeWhispererCodeCoverageTracker .getInstancesMap()[CodewhispererLanguage .Python ] = pythonTracker
158
159
pythonTracker.activateTrackerIfNotActive()
159
160
@@ -180,9 +181,12 @@ class CodeWhispererCodeCoverageTrackerTest {
180
181
181
182
@Test
182
183
fun `test tracker is listening to document changes and increment totalTokens - delete code` () {
183
- fixture.configureByText(pythonFileName, pythonTestLeftContext)
184
- val pythonTracker =
185
- spy(TestCodePercentageTracker (TOTAL_SECONDS_IN_MINUTE , CodewhispererLanguage .Python , AtomicInteger (0 ), AtomicInteger (pythonTestLeftContext.length)))
184
+ val pythonTracker = TestCodePercentageTracker (
185
+ TOTAL_SECONDS_IN_MINUTE ,
186
+ CodewhispererLanguage .Python ,
187
+ codeCoverageTokens = mutableMapOf (fixture.editor.document to CodeCoverageTokens (pythonTestLeftContext.length, 0 ))
188
+ )
189
+
186
190
CodeWhispererCodeCoverageTracker .getInstancesMap()[CodewhispererLanguage .Python ] = pythonTracker
187
191
pythonTracker.activateTrackerIfNotActive()
188
192
assertThat(pythonTracker.totalTokensSize).isEqualTo(pythonTestLeftContext.length)
@@ -197,8 +201,6 @@ class CodeWhispererCodeCoverageTrackerTest {
197
201
assertThat(pythonTracker.totalTokensSize).isEqualTo(pythonTestLeftContext.length - 3 )
198
202
}
199
203
200
- // TODO: investigate what cause this test throw NPE when running whole test suite and enable test case
201
- // @Ignore
202
204
@Test
203
205
fun `test msg CODEWHISPERER_USER_ACTION_PERFORMED will add rangeMarker in the list` () {
204
206
val pythonTracker = spy(TestCodePercentageTracker (TOTAL_SECONDS_IN_MINUTE , language = CodewhispererLanguage .Python ))
@@ -222,22 +224,20 @@ class CodeWhispererCodeCoverageTrackerTest {
222
224
}
223
225
224
226
@Test
225
- fun `test 0 token will return 0 percent ` () {
227
+ fun `test 0 totalTokens will return null ` () {
226
228
val javaTracker = spy(TestCodePercentageTracker (TOTAL_SECONDS_IN_MINUTE , language = CodewhispererLanguage .Java ))
227
229
CodeWhispererCodeCoverageTracker .getInstancesMap()[CodewhispererLanguage .Java ] = javaTracker
228
230
assertThat(javaTracker.percentage).isNull()
229
231
}
230
232
231
233
@Test
232
234
fun `test flush() will reset tokens and reschedule next telemetry sending` () {
233
- val pythonTracker = spy(
234
- TestCodePercentageTracker (
235
- TOTAL_SECONDS_IN_MINUTE ,
236
- CodewhispererLanguage .Python ,
237
- AtomicInteger (" bar" .length),
238
- AtomicInteger (" foobar" .length)
239
- )
235
+ val pythonTracker = TestCodePercentageTracker (
236
+ TOTAL_SECONDS_IN_MINUTE ,
237
+ CodewhispererLanguage .Python ,
238
+ codeCoverageTokens = mutableMapOf (mock<Document >() to CodeCoverageTokens (" foobar" .length, " bar" .length))
240
239
)
240
+
241
241
pythonTracker.activateTrackerIfNotActive()
242
242
assertThat(pythonTracker.activeRequestCount()).isEqualTo(1 )
243
243
assertThat(pythonTracker.acceptedTokensSize).isEqualTo(" bar" .length)
@@ -251,71 +251,88 @@ class CodeWhispererCodeCoverageTrackerTest {
251
251
}
252
252
253
253
@Test
254
- fun `test flush() will emit correct telemetry event -- user delete whole accepted reommendation ` () {
254
+ fun `test when rangeMarker is not vaild, acceptedToken will not be updated ` () {
255
255
// when user delete whole recommendation, rangeMarker will be isValid = false
256
256
val rangeMarkerMock: RangeMarker = mock()
257
257
whenever(rangeMarkerMock.isValid).thenReturn(false )
258
+ val pythonTracker = spy(
259
+ TestCodePercentageTracker (
260
+ TOTAL_SECONDS_IN_MINUTE ,
261
+ CodewhispererLanguage .Python ,
262
+ mutableListOf (rangeMarkerMock),
263
+ )
264
+ ) {
265
+ onGeneric { getAcceptedTokensDelta(any(), any()) } doReturn 100
266
+ }
258
267
259
- val pythonTracker = TestCodePercentageTracker (
260
- TOTAL_SECONDS_IN_MINUTE ,
261
- CodewhispererLanguage .Python ,
262
- acceptedTokens = AtomicInteger (0 ),
263
- totalTokens = AtomicInteger (20 ),
264
- mutableListOf (rangeMarkerMock)
265
- )
266
268
pythonTracker.activateTrackerIfNotActive()
267
-
268
269
pythonTracker.forceTrackerFlush()
269
270
270
- val metricCaptor = argumentCaptor< MetricEvent >( )
271
- verify(batcher, Times ( 1 )).enqueue(metricCaptor.capture())
272
- CodeWhispererTelemetryTest .assertEventsContainsFieldsAndCount(
273
- metricCaptor.allValues,
274
- CODE_PERCENTAGE ,
275
- 1 ,
276
- CWSPR_PERCENTAGE to " 0 " ,
277
- CWSPR_Language to CodewhispererLanguage . Python .toString() ,
278
- CWSPR_ACCEPTED_TOKENS to " 0 " ,
279
- CWSPR_TOTAL_TOKENS to " 20 "
271
+ verify(pythonTracker, Times ( 0 )).getAcceptedTokensDelta(any(), any() )
272
+ }
273
+
274
+ @Test
275
+ fun `test flush() will call emitTelemetry automatically schedule next call` () {
276
+ val pythonTracker = spy(
277
+ TestCodePercentageTracker (
278
+ TOTAL_SECONDS_IN_MINUTE ,
279
+ CodewhispererLanguage . Python ,
280
+ )
280
281
)
282
+ doNothing().whenever(pythonTracker).emitCodeWhispererCodeContribution()
283
+
284
+ pythonTracker.activateTrackerIfNotActive()
285
+ assertThat(pythonTracker.activeRequestCount()).isEqualTo(1 )
286
+ pythonTracker.forceTrackerFlush()
287
+
288
+ verify(pythonTracker, Times (1 )).emitCodeWhispererCodeContribution()
289
+ assertThat(pythonTracker.activeRequestCount()).isEqualTo(1 )
281
290
}
282
291
283
292
@Test
284
- fun `test flush() will emit telemetry event ` () {
293
+ fun `test emitCodeWhispererCodeContribution ` () {
285
294
val rangeMarkerMock1 = mock<RangeMarker > {
286
295
on { isValid } doReturn true
287
296
on { getUserData(any<Key <String >>()) } doReturn " foo"
297
+ on { document } doReturn fixture.editor.document
288
298
}
289
299
290
300
val pythonTracker = spy(
291
301
TestCodePercentageTracker (
292
302
TOTAL_SECONDS_IN_MINUTE ,
293
303
CodewhispererLanguage .Python ,
294
- AtomicInteger (10 ),
295
- AtomicInteger (100 ),
296
- mutableListOf (rangeMarkerMock1)
304
+ mutableListOf (rangeMarkerMock1),
305
+ mutableMapOf (fixture.editor.document to CodeCoverageTokens (totalTokens = 100 , acceptedTokens = 0 ))
297
306
)
298
307
) {
299
308
onGeneric { extractRangeMarkerString(any()) } doReturn " fou"
309
+ onGeneric { getAcceptedTokensDelta(any(), any()) } doReturn 99
300
310
}
301
311
302
- pythonTracker.activateTrackerIfNotActive ()
303
- pythonTracker.forceTrackerFlush()
312
+ pythonTracker.emitCodeWhispererCodeContribution ()
313
+
304
314
val metricCaptor = argumentCaptor<MetricEvent >()
305
315
verify(batcher, Times (1 )).enqueue(metricCaptor.capture())
306
316
CodeWhispererTelemetryTest .assertEventsContainsFieldsAndCount(
307
317
metricCaptor.allValues,
308
318
CODE_PERCENTAGE ,
309
319
1 ,
320
+ CWSPR_PERCENTAGE to " 99" ,
321
+ CWSPR_ACCEPTED_TOKENS to " 99" ,
322
+ CWSPR_TOTAL_TOKENS to " 100"
310
323
)
311
324
}
312
325
313
326
@Test
314
327
fun `test flush() won't emit telemetry event when users not enabling telemetry` () {
315
328
AwsSettings .getInstance().isTelemetryEnabled = false
316
- val pythonTracker = TestCodePercentageTracker (TOTAL_SECONDS_IN_MINUTE , CodewhispererLanguage .Python , AtomicInteger (10 ), AtomicInteger (20 ))
329
+ val pythonTracker = spy(TestCodePercentageTracker (TOTAL_SECONDS_IN_MINUTE , CodewhispererLanguage .Python ))
330
+ doNothing().whenever(pythonTracker).emitCodeWhispererCodeContribution()
331
+
332
+ pythonTracker.activateTrackerIfNotActive()
317
333
pythonTracker.forceTrackerFlush()
318
- verify(batcher, Times (0 )).enqueue(any())
334
+
335
+ verify(pythonTracker, Times (0 )).emitCodeWhispererCodeContribution()
319
336
}
320
337
321
338
@Test
@@ -364,8 +381,8 @@ class CodeWhispererCodeCoverageTrackerTest {
364
381
}
365
382
366
383
@Test
367
- fun `test flush() won't emit telemetry when users are not editing the document` () {
368
- val pythonTracker = TestCodePercentageTracker (TOTAL_SECONDS_IN_MINUTE , CodewhispererLanguage .Python , AtomicInteger ( 0 ), AtomicInteger ( 0 ) )
384
+ fun `test flush() won't emit telemetry when users are not editing the document (totalTokens == 0) ` () {
385
+ val pythonTracker = TestCodePercentageTracker (TOTAL_SECONDS_IN_MINUTE , CodewhispererLanguage .Python )
369
386
pythonTracker.activateTrackerIfNotActive()
370
387
assertThat(pythonTracker.activeRequestCount()).isEqualTo(1 )
371
388
pythonTracker.forceTrackerFlush()
0 commit comments