Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"type" : "bugfix",
"description" : "Amazon Q: Fix cases where content may be incorrectly excluded from workspace."
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@

package software.aws.toolkits.jetbrains.services.amazonq.workspace.context

import com.intellij.openapi.vcs.changes.ChangeListManager
import com.intellij.openapi.vfs.VirtualFile
import com.intellij.openapi.vfs.refreshAndFindVirtualDirectory
import com.intellij.openapi.vfs.refreshAndFindVirtualFile
import com.intellij.testFramework.LightPlatformTestCase
import org.mockito.Mockito.mock
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
import software.aws.toolkits.jetbrains.services.amazonq.project.additionalGlobalIgnoreRules
import software.aws.toolkits.jetbrains.services.amazonq.project.additionalGlobalIgnoreRulesForStrictSources
import software.aws.toolkits.jetbrains.services.amazonq.project.findWorkspaceRoot
import software.aws.toolkits.jetbrains.services.amazonq.project.isContentInWorkspace
import software.aws.toolkits.jetbrains.services.amazonq.project.isWorkspaceSourceContent
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.createDirectories
Expand Down Expand Up @@ -106,4 +113,97 @@ class WorkspaceTest : LightPlatformTestCase() {

assertFalse(isContentInWorkspace(testPath, setOf(projectPath, modulePath)))
}

fun `test isWorkspaceSourceContent returns true for non-ignored file or directory with default ignore rules`() {
val projectPath = createDir("test/project")
val directoryPath = createDir("test/project/module")
val codeFilePath = createDir("test/project/module/example.java")
val nonCodeFilePath = createDir("test/project/module/example.bin")

val changeListManager = mock<ChangeListManager>().apply {
whenever(isIgnoredFile(directoryPath)) doReturn false
}

assertTrue(
isWorkspaceSourceContent(
directoryPath,
setOf(projectPath),
changeListManager,
additionalGlobalIgnoreRules
)
)

assertTrue(
isWorkspaceSourceContent(
codeFilePath,
setOf(projectPath),
changeListManager,
additionalGlobalIgnoreRules
)
)

assertTrue(
isWorkspaceSourceContent(
nonCodeFilePath,
setOf(projectPath),
changeListManager,
additionalGlobalIgnoreRules
)
)

assertTrue(
isWorkspaceSourceContent(
directoryPath,
setOf(projectPath),
changeListManager,
additionalGlobalIgnoreRulesForStrictSources
)
)

assertTrue(
isWorkspaceSourceContent(
codeFilePath,
setOf(projectPath),
changeListManager,
additionalGlobalIgnoreRulesForStrictSources
)
)
}

fun `test isWorkspaceSourceContent returns false for ignored file or directory with default ignore rules`() {
val projectPath = createDir("test/project")
val directoryPath = createDir("test/project/node_modules")
val nonCodeFilePath = createDir("test/project/module/example.bin")

val changeListManager = mock<ChangeListManager>().apply {
whenever(isIgnoredFile(directoryPath)) doReturn false
}

assertFalse(
isWorkspaceSourceContent(
directoryPath,
setOf(projectPath),
changeListManager,
additionalGlobalIgnoreRules
)
)

assertFalse(
isWorkspaceSourceContent(
directoryPath,
setOf(projectPath),
changeListManager,
additionalGlobalIgnoreRulesForStrictSources
)
)

assertFalse(
isWorkspaceSourceContent(
nonCodeFilePath,
setOf(projectPath),
changeListManager,
additionalGlobalIgnoreRulesForStrictSources
)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,9 @@ import software.aws.toolkits.jetbrains.utils.rules.HeavyJavaCodeInsightTestFixtu
import software.aws.toolkits.jetbrains.utils.rules.addFileToModule
import java.util.zip.ZipFile

class FeatureDevSessionContextTest : FeatureDevTestBase(HeavyJavaCodeInsightTestFixtureRule()) {

private fun addFilesToProjectModule(vararg path: String) {
val module = projectRule.module
path.forEach { projectRule.fixture.addFileToModule(module, it, it) }
}
data class FileCase(val path: String, val content: String = "", val shouldInclude: Boolean = true)

class FeatureDevSessionContextTest : FeatureDevTestBase(HeavyJavaCodeInsightTestFixtureRule()) {
@Rule
@JvmField
val ruleChain = RuleChain(projectRule, disposableRule)
Expand All @@ -35,90 +31,40 @@ class FeatureDevSessionContextTest : FeatureDevTestBase(HeavyJavaCodeInsightTest
featureDevSessionContext = FeatureDevSessionContext(featureDevService.project, 1024)
}

// FIXME: Add deeper tests, replacing previous shallow tests - BLOCKING

@Test
fun testZipProjectWithoutAutoDev() {
checkZipProject(
false,
setOf(
"src/MyClass.java",
"icons/menu.svg",
"assets/header.jpg",
"archive.zip",
"output.bin",
"gradle/wrapper/gradle-wrapper.jar",
"gradle/wrapper/gradle-wrapper.properties",
"images/logo.png",
"builder/GetTestBuilder.java",
"gradlew",
"README.md",
".gitignore",
"License.md",
"gradlew.bat",
"license.txt",
"build.gradle",
"settings.gradle"
)
)
}

@Test
fun testZipProjectWithAutoDev() {
checkZipProject(
true,
setOf(
"src/MyClass.java",
"icons/menu.svg",
"assets/header.jpg",
"archive.zip",
"output.bin",
"gradle/wrapper/gradle-wrapper.jar",
"gradle/wrapper/gradle-wrapper.properties",
"images/logo.png",
"builder/GetTestBuilder.java",
"gradlew",
"README.md",
".gitignore",
"License.md",
"gradlew.bat",
"license.txt",
"build.gradle",
"devfile.yaml",
"settings.gradle"
)
)
}
private fun fileCases(autoBuildEnabled: Boolean) = listOf(
FileCase(path = ".gitignore"),
FileCase(path = ".gradle/cached.jar", shouldInclude = false),
FileCase(path = "src/MyClass.java"),
FileCase(path = "gradlew"),
FileCase(path = "gradlew.bat"),
FileCase(path = "README.md"),
FileCase(path = "settings.gradle"),
FileCase(path = "build.gradle"),
FileCase(path = "gradle/wrapper/gradle-wrapper.properties"),
FileCase(path = "builder/GetTestBuilder.java"),
FileCase(path = ".aws-sam/build/function1", shouldInclude = false),
FileCase(path = ".gem/specs.rb", shouldInclude = false),
FileCase(path = "archive.zip"),
FileCase(path = "output.bin"),
FileCase(path = "images/logo.png"),
FileCase(path = "assets/header.jpg"),
FileCase(path = "icons/menu.svg"),
FileCase(path = "license.txt"),
FileCase(path = "License.md"),
FileCase(path = "node_modules/express", shouldInclude = false),
FileCase(path = "build/outputs", shouldInclude = false),
FileCase(path = "dist/bundle.js", shouldInclude = false),
FileCase(path = "gradle/wrapper/gradle-wrapper.jar"),
FileCase(path = "big-file.txt", content = "blob".repeat(1024 * 1024), shouldInclude = false),
FileCase(path = "devfile.yaml", shouldInclude = autoBuildEnabled),
)

fun checkZipProject(autoBuildEnabled: Boolean, expectedFiles: Set<String>) {
addFilesToProjectModule(
".gitignore",
".gradle/cached.jar",
"src/MyClass.java",
"gradlew",
"gradlew.bat",
"README.md",
"settings.gradle",
"build.gradle",
"gradle/wrapper/gradle-wrapper.properties",
"builder/GetTestBuilder.java", // check for false positives
".aws-sam/build/function1",
".gem/specs.rb",
"archive.zip",
"output.bin",
"images/logo.png",
"assets/header.jpg",
"icons/menu.svg",
"license.txt",
"License.md",
"node_modules/express",
"build/outputs",
"dist/bundle.js",
"gradle/wrapper/gradle-wrapper.jar",
"devfile.yaml",
)
private fun checkZipProject(autoBuildEnabled: Boolean, fileCases: Iterable<FileCase>, onBeforeZip: (() -> Unit)? = null) {
fileCases.forEach {
projectRule.fixture.addFileToModule(module, it.path, it.content)
}

projectRule.fixture.addFileToModule(module, "large-file.txt", "loblob".repeat(1024 * 1024))
onBeforeZip?.invoke()

val zipResult = featureDevSessionContext.getProjectZip(autoBuildEnabled)
val zipPath = zipResult.payload.path
Expand All @@ -132,6 +78,60 @@ class FeatureDevSessionContextTest : FeatureDevTestBase(HeavyJavaCodeInsightTest
}
}

assertEquals(zippedFiles, expectedFiles)
// The input file paths are relative to the workspaceRoot, however the zip content is relative to the addressableRoot:
val addressableRoot = featureDevSessionContext.addressableRoot.path
val workspaceRoot = featureDevSessionContext.workspaceRoot.path
val base = addressableRoot.removePrefix(workspaceRoot).removePrefix("/")
fun addressablePathOf(path: String) = path.removePrefix(base).removePrefix("/")

fileCases.forEach {
assertEquals(it.shouldInclude, zippedFiles.contains(addressablePathOf(it.path)))
}
}

@Test
fun `test zip with autoBuild enabled`() {
checkZipProject(autoBuildEnabled = true, fileCases(autoBuildEnabled = true))
}

@Test
fun `test zip with autoBuild disabled`() {
checkZipProject(autoBuildEnabled = false, fileCases(autoBuildEnabled = false))
}

@Test
fun `test content is included when selection root is workspace root`() {
val fileCases = listOf(
FileCase(path = "file.txt", shouldInclude = true),
FileCase(path = "project/file.txt", shouldInclude = true),
FileCase(path = "deep/nested/file.txt", shouldInclude = true)
)

checkZipProject(autoBuildEnabled = false, fileCases = fileCases, onBeforeZip = {
featureDevSessionContext.selectionRoot = featureDevSessionContext.workspaceRoot
})
}

@Test
fun `test content is included within selection root which is deeper than content root`() {
val fileCases = listOf(FileCase(path = "project/module/deep/file.txt", shouldInclude = true))

checkZipProject(autoBuildEnabled = false, fileCases = fileCases, onBeforeZip = {
featureDevSessionContext.selectionRoot = featureDevSessionContext.workspaceRoot.findFileByRelativePath("project/module/deep")
?: error("Failed to find fixture")
})
}

@Test
fun `test content is excluded outside of selection root`() {
val fileCases = listOf(
FileCase(path = "project/module/file.txt", shouldInclude = true),
FileCase(path = "project/outside/no.txt", shouldInclude = false),
)

checkZipProject(autoBuildEnabled = false, fileCases = fileCases, onBeforeZip = {
featureDevSessionContext.selectionRoot = featureDevSessionContext.workspaceRoot.findFileByRelativePath("project/module")
?: error("Failed to find fixture")
})
}
}
Loading
Loading