@@ -11,6 +11,7 @@ import com.intellij.openapi.editor.RangeMarker
11
11
import com.intellij.openapi.editor.event.DocumentEvent
12
12
import com.intellij.openapi.project.Project
13
13
import com.intellij.openapi.util.Key
14
+ import com.intellij.psi.codeStyle.CodeStyleManager
14
15
import com.intellij.testFramework.DisposableRule
15
16
import com.intellij.testFramework.fixtures.CodeInsightTestFixture
16
17
import com.intellij.testFramework.replaceService
@@ -56,54 +57,50 @@ import software.aws.toolkits.jetbrains.services.codewhisperer.util.CodeWhisperer
56
57
import software.aws.toolkits.jetbrains.services.telemetry.NoOpPublisher
57
58
import software.aws.toolkits.jetbrains.services.telemetry.TelemetryService
58
59
import software.aws.toolkits.jetbrains.settings.AwsSettings
60
+ import software.aws.toolkits.jetbrains.utils.rules.CodeInsightTestFixtureRule
61
+ import software.aws.toolkits.jetbrains.utils.rules.JavaCodeInsightTestFixtureRule
59
62
import software.aws.toolkits.jetbrains.utils.rules.PythonCodeInsightTestFixtureRule
60
63
import software.aws.toolkits.telemetry.CodewhispererCompletionType
61
64
import software.aws.toolkits.telemetry.CodewhispererLanguage
62
65
63
- class CodeWhispererCodeCoverageTrackerTest {
64
- private class TestCodePercentageTracker (
66
+ internal abstract class CodeWhispererCodeCoverageTrackerTestBase ( myProjectRule : CodeInsightTestFixtureRule ) {
67
+ protected class TestCodePercentageTracker (
65
68
timeWindowInSec : Long ,
66
69
language : CodewhispererLanguage ,
67
70
rangeMarkers : MutableList <RangeMarker > = mutableListOf(),
68
71
codeCoverageTokens : MutableMap <Document , CodeCoverageTokens > = mutableMapOf()
69
72
) : CodeWhispererCodeCoverageTracker(timeWindowInSec, language, rangeMarkers, codeCoverageTokens)
70
73
71
- private class TestTelemetryService (
74
+ protected class TestTelemetryService (
72
75
publisher : TelemetryPublisher = NoOpPublisher (),
73
76
batcher : TelemetryBatcher
74
77
) : TelemetryService(publisher, batcher)
75
-
76
78
@Rule
77
79
@JvmField
78
- var projectRule = PythonCodeInsightTestFixtureRule ()
80
+ val projectRule: CodeInsightTestFixtureRule
79
81
80
82
@Rule
81
83
@JvmField
82
- val mockClientManagerRule = MockClientManagerRule ()
84
+ val disposableRule = DisposableRule ()
83
85
84
86
@Rule
85
87
@JvmField
86
- val disposableRule = DisposableRule ()
88
+ val mockClientManagerRule = MockClientManagerRule ()
87
89
88
- private lateinit var project: Project
89
- private lateinit var fixture: CodeInsightTestFixture
90
- private lateinit var telemetryServiceSpy: TelemetryService
91
- private lateinit var batcher: TelemetryBatcher
92
- private lateinit var exploreActionManagerMock: CodeWhispererExplorerActionManager
90
+ protected lateinit var project: Project
91
+ protected lateinit var fixture: CodeInsightTestFixture
92
+ protected lateinit var telemetryServiceSpy: TelemetryService
93
+ protected lateinit var batcher: TelemetryBatcher
94
+ protected lateinit var exploreActionManagerMock: CodeWhispererExplorerActionManager
93
95
94
- private lateinit var invocationContext: InvocationContext
95
- private lateinit var sessionContext: SessionContext
96
+ init {
97
+ this .projectRule = myProjectRule
98
+ }
96
99
97
- @Before
98
- fun setup () {
100
+ open fun setup () {
101
+ this .project = projectRule.project
102
+ this .fixture = projectRule.fixture
99
103
AwsSettings .getInstance().isTelemetryEnabled = true
100
- project = projectRule.project
101
- fixture = projectRule.fixture
102
- fixture.configureByText(pythonFileName, pythonTestLeftContext)
103
- runInEdtAndWait {
104
- projectRule.fixture.editor.caretModel.primaryCaret.moveToOffset(projectRule.fixture.editor.document.textLength)
105
- }
106
-
107
104
batcher = mock()
108
105
telemetryServiceSpy = spy(TestTelemetryService (batcher = batcher))
109
106
exploreActionManagerMock = mock {
@@ -112,7 +109,32 @@ class CodeWhispererCodeCoverageTrackerTest {
112
109
113
110
ApplicationManager .getApplication().replaceService(CodeWhispererExplorerActionManager ::class .java, exploreActionManagerMock, disposableRule.disposable)
114
111
ApplicationManager .getApplication().replaceService(TelemetryService ::class .java, telemetryServiceSpy, disposableRule.disposable)
115
- project.replaceService(CodeWhispererCodeReferenceManager ::class .java, mock(), disposableRule.disposable)
112
+ }
113
+
114
+ @After
115
+ fun tearDown () {
116
+ CodeWhispererCodeCoverageTracker .getInstancesMap().clear()
117
+ }
118
+
119
+ protected companion object {
120
+ const val CODE_PERCENTAGE = " codewhisperer_codePercentage"
121
+ const val CWSPR_PERCENTAGE = " codewhispererPercentage"
122
+ const val CWSPR_Language = " codewhispererLanguage"
123
+ const val CWSPR_ACCEPTED_TOKENS = " codewhispererAcceptedTokens"
124
+ const val CWSPR_TOTAL_TOKENS = " codewhispererTotalTokens"
125
+ }
126
+ }
127
+
128
+ internal class CodeWhispererCodeCoverageTrackerTestPython : CodeWhispererCodeCoverageTrackerTestBase (PythonCodeInsightTestFixtureRule ()) {
129
+ private lateinit var invocationContext: InvocationContext
130
+ private lateinit var sessionContext: SessionContext
131
+ @Before
132
+ override fun setup () {
133
+ super .setup()
134
+ fixture.configureByText(pythonFileName, pythonTestLeftContext)
135
+ runInEdtAndWait {
136
+ projectRule.fixture.editor.caretModel.primaryCaret.moveToOffset(projectRule.fixture.editor.document.textLength)
137
+ }
116
138
117
139
val requestContext = RequestContext (
118
140
project,
@@ -131,11 +153,9 @@ class CodeWhispererCodeCoverageTrackerTest {
131
153
)
132
154
invocationContext = InvocationContext (requestContext, responseContext, recommendationContext, mock())
133
155
sessionContext = SessionContext ()
134
- }
135
156
136
- @After
137
- fun tearDown () {
138
- CodeWhispererCodeCoverageTracker .getInstancesMap().clear()
157
+ // it is needed because referenceManager is listening to CODEWHISPERER_USER_ACTION_PERFORMED topic
158
+ project.replaceService(CodeWhispererCodeReferenceManager ::class .java, mock(), disposableRule.disposable)
139
159
}
140
160
141
161
@Test
@@ -207,6 +227,26 @@ class CodeWhispererCodeCoverageTrackerTest {
207
227
assertThat(pythonTracker.totalTokensSize).isEqualTo(pythonTestLeftContext.length - 3 )
208
228
}
209
229
230
+ @Test
231
+ fun `test tracker documentChanged - will not increment tokens on blank string of length greater than 1` () {
232
+ val pythonTracker = TestCodePercentageTracker (
233
+ TOTAL_SECONDS_IN_MINUTE ,
234
+ CodewhispererLanguage .Python ,
235
+ codeCoverageTokens = mutableMapOf (fixture.editor.document to CodeCoverageTokens (pythonTestLeftContext.length, 0 ))
236
+ )
237
+ CodeWhispererCodeCoverageTracker .getInstancesMap()[CodewhispererLanguage .Python ] = pythonTracker
238
+ pythonTracker.activateTrackerIfNotActive()
239
+ assertThat(pythonTracker.totalTokensSize).isEqualTo(pythonTestLeftContext.length)
240
+
241
+ runInEdtAndWait {
242
+ WriteCommandAction .runWriteCommandAction(project) {
243
+ fixture.editor.document.insertString(fixture.editor.caretModel.offset, " \t " )
244
+ }
245
+ }
246
+
247
+ assertThat(pythonTracker.totalTokensSize).isEqualTo(pythonTestLeftContext.length)
248
+ }
249
+
210
250
@Test
211
251
fun `test msg CODEWHISPERER_USER_ACTION_PERFORMED will add rangeMarker in the list` () {
212
252
val pythonTracker = spy(TestCodePercentageTracker (TOTAL_SECONDS_IN_MINUTE , language = CodewhispererLanguage .Python ))
@@ -400,12 +440,68 @@ class CodeWhispererCodeCoverageTrackerTest {
400
440
document.insertString(currentOffset, string)
401
441
caretModel.moveToOffset(currentOffset + string.length)
402
442
}
443
+ }
403
444
404
- private companion object {
405
- const val CODE_PERCENTAGE = " codewhisperer_codePercentage"
406
- const val CWSPR_PERCENTAGE = " codewhispererPercentage"
407
- const val CWSPR_Language = " codewhispererLanguage"
408
- const val CWSPR_ACCEPTED_TOKENS = " codewhispererAcceptedTokens"
409
- const val CWSPR_TOTAL_TOKENS = " codewhispererTotalTokens"
445
+ internal class CodeWhispererCodeCoverageTrackerTestJava : CodeWhispererCodeCoverageTrackerTestBase (JavaCodeInsightTestFixtureRule ()) {
446
+ @Before
447
+ override fun setup () {
448
+ super .setup()
449
+ }
450
+
451
+ @Test
452
+ fun `tracker should not update totalTokens if documentChanged events are fired by code reformatting` () {
453
+ val codeNeedToBeReformatted = """
454
+ class Answer {
455
+ private int knapsack(int[] w, int[] v, int c) {
456
+ int[][] dp = new int[w.length + 1][c + 1];
457
+ for (int i = 0; i < w.length; i++) {for (int j = 0; j <= c; j++) {
458
+ if (j < w[i]) {
459
+ dp[i + 1][j] = dp[i][j];
460
+ }
461
+ else {
462
+ dp[i + 1][j] = Math.max(dp[i][j], dp[i][j - w[i]] + v[i]);
463
+ }
464
+ }
465
+ }
466
+ return dp[w.length][c];
467
+ }
468
+ }
469
+ """ .trimIndent()
470
+ val file = fixture.configureByText(" test.java" , codeNeedToBeReformatted)
471
+ val tracker = spy(
472
+ TestCodePercentageTracker (
473
+ TOTAL_SECONDS_IN_MINUTE ,
474
+ language = CodewhispererLanguage .Java ,
475
+ codeCoverageTokens = mutableMapOf (fixture.editor.document to CodeCoverageTokens (totalTokens = codeNeedToBeReformatted.length))
476
+ )
477
+ )
478
+ CodeWhispererCodeCoverageTracker .getInstancesMap()[CodewhispererLanguage .Java ] = tracker
479
+ runInEdtAndWait {
480
+ WriteCommandAction .runWriteCommandAction(project) {
481
+ CodeStyleManager .getInstance(project).reformatText(file, 0 , fixture.editor.document.textLength)
482
+ }
483
+ }
484
+ // reformat should fire documentChanged events, but tracker should not update token from these events
485
+ verify(tracker, atLeastOnce()).documentChanged(any())
486
+ assertThat(tracker.totalTokensSize).isEqualTo(codeNeedToBeReformatted.length)
487
+
488
+ val formatted = """
489
+ class Answer {
490
+ private int knapsack(int[] w, int[] v, int c) {
491
+ int[][] dp = new int[w.length + 1][c + 1];
492
+ for (int i = 0; i < w.length; i++) {
493
+ for (int j = 0; j <= c; j++) {
494
+ if (j < w[i]) {
495
+ dp[i + 1][j] = dp[i][j];
496
+ } else {
497
+ dp[i + 1][j] = Math.max(dp[i][j], dp[i][j - w[i]] + v[i]);
498
+ }
499
+ }
500
+ }
501
+ return dp[w.length][c];
502
+ }
503
+ }
504
+ """ .trimIndent()
505
+ assertThat(fixture.editor.document.text.trimEnd()).isEqualTo(formatted)
410
506
}
411
507
}
0 commit comments