Skip to content

Commit 450e416

Browse files
Merge branch 'feature/q-lsp' into samgst/lsp-WSmessages
2 parents 62adbc1 + 25f6e19 commit 450e416

File tree

12 files changed

+547
-20
lines changed

12 files changed

+547
-20
lines changed

buildspec/linuxUiTests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ phases:
4747
- chmod +x gradlew
4848

4949
- ffmpeg -loglevel quiet -nostdin -f x11grab -video_size ${SCREEN_WIDTH}x${SCREEN_HEIGHT} -i ${DISPLAY} -codec:v libx264 -pix_fmt yuv420p -vf drawtext="fontsize=48:box=1:[email protected]:boxborderw=5:fontcolor=white:x=0:y=h-text_h:text='%{gmtime\:%H\\\\\:%M\\\\\:%S}'" -framerate 12 -g 12 /tmp/screen_recording.mp4 &
50-
- ./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME :ui-tests-starter:test coverageReport --console plain --info
50+
- ./gradlew -PideProfileName=$ALTERNATIVE_IDE_PROFILE_NAME :ui-tests-starter:uiTest coverageReport --console plain --info
5151

5252
post_build:
5353
commands:

noop/build.gradle.kts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,4 @@
33

44
// project that does nothing
55
tasks.register("test")
6+
tasks.register("uiTest")
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package software.aws.toolkits.jetbrains.services.amazonq.lsp.util
5+
6+
import com.intellij.openapi.vfs.VfsUtilCore
7+
import com.intellij.openapi.vfs.VirtualFile
8+
import software.aws.toolkits.core.utils.getLogger
9+
import software.aws.toolkits.core.utils.warn
10+
import java.io.File
11+
import java.net.URI
12+
import java.net.URISyntaxException
13+
14+
object FileUriUtil {
15+
16+
fun toUriString(virtualFile: VirtualFile): String? {
17+
val protocol = virtualFile.fileSystem.protocol
18+
val uri = when (protocol) {
19+
"jar" -> VfsUtilCore.convertToURL(virtualFile.url)?.toExternalForm()
20+
"jrt" -> virtualFile.url
21+
else -> toUri(VfsUtilCore.virtualToIoFile(virtualFile)).toASCIIString()
22+
} ?: return null
23+
24+
return if (virtualFile.isDirectory) {
25+
uri.trimEnd('/', '\\')
26+
} else {
27+
uri
28+
}
29+
}
30+
31+
private fun toUri(file: File): URI {
32+
try {
33+
// URI scheme specified by language server protocol
34+
return URI("file", "", file.absoluteFile.toURI().path, null)
35+
} catch (e: URISyntaxException) {
36+
LOG.warn { "${e.localizedMessage}: $e" }
37+
return file.absoluteFile.toURI()
38+
}
39+
}
40+
41+
private val LOG = getLogger<FileUriUtil>()
42+
}
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
// Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
package software.aws.toolkits.jetbrains.services.amazonq.lsp.util
4+
5+
import com.intellij.openapi.vfs.VirtualFile
6+
import com.intellij.testFramework.fixtures.BasePlatformTestCase
7+
import io.mockk.every
8+
import io.mockk.mockk
9+
import org.junit.Test
10+
11+
class FileUriUtilTest : BasePlatformTestCase() {
12+
13+
private fun createMockVirtualFile(path: String, protocol: String = "file", isDirectory: Boolean = false): VirtualFile =
14+
mockk<VirtualFile> {
15+
every { fileSystem } returns mockk {
16+
every { this@mockk.protocol } returns protocol
17+
}
18+
every { url } returns path
19+
every { this@mockk.isDirectory } returns isDirectory
20+
}
21+
22+
private fun normalizeFileUri(uri: String): String {
23+
if (!System.getProperty("os.name").lowercase().contains("windows")) {
24+
return uri
25+
}
26+
27+
if (!uri.startsWith("file:///")) {
28+
return uri
29+
}
30+
31+
val path = uri.substringAfter("file:///")
32+
return "file:///C:/$path"
33+
}
34+
35+
@Test
36+
fun `test basic unix path`() {
37+
val virtualFile = createMockVirtualFile("/path/to/file.txt")
38+
val uri = FileUriUtil.toUriString(virtualFile)
39+
val expected = normalizeFileUri("file:///path/to/file.txt")
40+
assertEquals(expected, uri)
41+
}
42+
43+
@Test
44+
fun `test unix directory path`() {
45+
val virtualFile = createMockVirtualFile("/path/to/directory/", isDirectory = true)
46+
val uri = FileUriUtil.toUriString(virtualFile)
47+
val expected = normalizeFileUri("file:///path/to/directory")
48+
assertEquals(expected, uri)
49+
}
50+
51+
@Test
52+
fun `test path with spaces`() {
53+
val virtualFile = createMockVirtualFile("/path/with spaces/file.txt")
54+
val uri = FileUriUtil.toUriString(virtualFile)
55+
val expected = normalizeFileUri("file:///path/with%20spaces/file.txt")
56+
assertEquals(expected, uri)
57+
}
58+
59+
@Test
60+
fun `test root path`() {
61+
val virtualFile = createMockVirtualFile("/")
62+
val uri = FileUriUtil.toUriString(virtualFile)
63+
val expected = normalizeFileUri("file:///")
64+
assertEquals(expected, uri)
65+
}
66+
67+
@Test
68+
fun `test path with multiple separators`() {
69+
val virtualFile = createMockVirtualFile("/path//to///file.txt")
70+
val uri = FileUriUtil.toUriString(virtualFile)
71+
val expected = normalizeFileUri("file:///path/to/file.txt")
72+
assertEquals(expected, uri)
73+
}
74+
75+
@Test
76+
fun `test very long path`() {
77+
val longPath = "/a".repeat(256) + "/file.txt"
78+
val virtualFile = createMockVirtualFile(longPath)
79+
val uri = FileUriUtil.toUriString(virtualFile)
80+
if (uri != null) {
81+
assertTrue(uri.startsWith("file:///"))
82+
assertTrue(uri.endsWith("/file.txt"))
83+
}
84+
}
85+
86+
@Test
87+
fun `test relative path`() {
88+
val virtualFile = createMockVirtualFile("./relative/path/file.txt")
89+
val uri = FileUriUtil.toUriString(virtualFile)
90+
if (uri != null) {
91+
assertTrue(uri.contains("file.txt"))
92+
assertTrue(uri.startsWith("file:///"))
93+
}
94+
}
95+
96+
@Test
97+
fun `test jar protocol conversion`() {
98+
val virtualFile = createMockVirtualFile(
99+
"jar:file:///path/to/archive.jar!/com/example/Test.class",
100+
"jar"
101+
)
102+
val result = FileUriUtil.toUriString(virtualFile)
103+
val expected = normalizeFileUri("jar:file:///path/to/archive.jar!/com/example/Test.class")
104+
assertEquals(expected, result)
105+
}
106+
107+
@Test
108+
fun `test jrt protocol conversion`() {
109+
val virtualFile = createMockVirtualFile(
110+
"jrt://java.base/java/lang/String.class",
111+
"jrt"
112+
)
113+
val result = FileUriUtil.toUriString(virtualFile)
114+
val expected = normalizeFileUri("jrt://java.base/java/lang/String.class")
115+
assertEquals(expected, result)
116+
}
117+
118+
@Test
119+
fun `test invalid jar url returns null`() {
120+
val virtualFile = createMockVirtualFile(
121+
"invalid:url:format",
122+
"jar"
123+
)
124+
val result = FileUriUtil.toUriString(virtualFile)
125+
assertNull(result)
126+
}
127+
128+
@Test
129+
fun `test jar protocol with directory`() {
130+
val virtualFile = createMockVirtualFile(
131+
"jar:file:///path/to/archive.jar!/com/example/",
132+
"jar",
133+
true
134+
)
135+
val result = FileUriUtil.toUriString(virtualFile)
136+
val expected = normalizeFileUri("jar:file:///path/to/archive.jar!/com/example")
137+
assertEquals(expected, result)
138+
}
139+
140+
@Test
141+
fun `test empty url in jar protocol`() {
142+
val virtualFile = createMockVirtualFile(
143+
"",
144+
"jar",
145+
true
146+
)
147+
val result = FileUriUtil.toUriString(virtualFile)
148+
assertNull(result)
149+
}
150+
}

settings.gradle.kts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -100,10 +100,12 @@ include("sandbox-all")
100100
include("ui-tests-starter")
101101
when (providers.gradleProperty("ideProfileName").get()) {
102102
// FIX_WHEN_MIN_IS_242: `tmp-all` test module no longer needed in 242+
103-
"2023.3", "2024.1" -> {
103+
"2024.1" -> {
104104
include("tmp-all")
105-
106-
// only available 242+
105+
project(":ui-tests-starter").projectDir = file("noop")
106+
}
107+
"2024.2" -> {
108+
// only available 243+
107109
project(":ui-tests-starter").projectDir = file("noop")
108110
}
109111
}

ui-tests-starter/build.gradle.kts

Lines changed: 48 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,26 @@ plugins {
99
id("toolkit-kotlin-conventions")
1010
id("toolkit-intellij-plugin")
1111

12-
id("org.jetbrains.intellij.platform.base")
12+
id("org.jetbrains.intellij.platform")
1313
}
1414

1515
val ideProfile = IdeVersions.ideProfile(project)
16+
val testPlugins by configurations.registering
1617

17-
// Add our source sets per IDE profile version (i.e. src-211)
1818
sourceSets {
1919
test {
20-
java.srcDirs(findFolders(project, "tst", ideProfile))
21-
resources.srcDirs(findFolders(project, "tst-resources", ideProfile))
20+
java.setSrcDirs(findFolders(project, "tst-prep", ideProfile))
21+
resources.setSrcDirs(findFolders(project, "tst-resources", ideProfile))
22+
}
23+
}
24+
25+
val uiTestSource = sourceSets.create("uiTest") {
26+
java.setSrcDirs(findFolders(project, "tst", ideProfile))
27+
}
28+
29+
idea {
30+
module {
31+
testSources.from(uiTestSource.allSource.srcDirs)
2232
}
2333
}
2434

@@ -27,35 +37,57 @@ intellijPlatform {
2737
instrumentCode = false
2838
}
2939

30-
tasks.initializeIntellijPlatformPlugin {
31-
enabled = false
32-
}
40+
val uiTestImplementation by configurations.getting
3341

34-
tasks.verifyPluginProjectConfiguration {
35-
runtimeDirectory.set(null as File?)
36-
enabled = false
42+
configurations.getByName(uiTestSource.compileClasspathConfigurationName) {
43+
extendsFrom(uiTestImplementation)
3744
}
3845

39-
val testPlugins by configurations.registering
46+
configurations.getByName(uiTestSource.runtimeClasspathConfigurationName) {
47+
extendsFrom(uiTestImplementation)
48+
}
4049

4150
dependencies {
4251
// should really be set by the BOM, but too much work to figure out right now
43-
testImplementation("org.kodein.di:kodein-di-jvm:7.20.2")
52+
uiTestImplementation("org.kodein.di:kodein-di-jvm:7.20.2")
53+
uiTestImplementation(platform(libs.junit5.bom))
54+
uiTestImplementation(libs.junit5.jupiter)
55+
4456
intellijPlatform {
45-
// shouldn't be needed? but IsolationException
4657
val version = ideProfile.community.sdkVersion
4758
intellijIdeaCommunity(version, !version.contains("SNAPSHOT"))
48-
testFramework(TestFrameworkType.Starter)
59+
60+
localPlugin(project(":plugin-core"))
61+
testImplementation(project(":plugin-core:core"))
62+
testImplementation(project(":plugin-core:jetbrains-community"))
63+
testImplementation(testFixtures(project(":plugin-core:jetbrains-community")))
64+
65+
testFramework(TestFrameworkType.Bundled)
66+
testFramework(TestFrameworkType.JUnit5)
67+
68+
testFramework(TestFrameworkType.Starter, configurationName = uiTestImplementation.name)
4969
}
5070

5171
testPlugins(project(":plugin-amazonq", "pluginZip"))
5272
testPlugins(project(":plugin-core", "pluginZip"))
5373
}
5474

5575
tasks.test {
56-
dependsOn(testPlugins)
76+
enabled = false
77+
}
5778

58-
useJUnitPlatform()
79+
val prepareAmazonQTest by intellijPlatformTesting.testIde.registering {
80+
task {
81+
useJUnitPlatform()
82+
}
83+
}
84+
85+
tasks.register<Test>("uiTest") {
86+
testClassesDirs = uiTestSource.output.classesDirs
87+
classpath = uiTestSource.runtimeClasspath
88+
89+
dependsOn(prepareAmazonQTest)
90+
dependsOn(testPlugins)
5991

6092
systemProperty("ui.test.plugins", testPlugins.get().asPath)
6193
systemProperty("org.gradle.project.ideProfileName", ideProfile.name)

0 commit comments

Comments
 (0)