[maven] Workspace import: make ContentRootCollector API more readable and safe (IDEA-288545)

GitOrigin-RevId: d2b2cdf10c7454cf53f8255ba04b35e4068e6267
This commit is contained in:
Anton Makeev
2022-07-01 17:27:34 +00:00
committed by intellij-monorepo-bot
parent ae28599833
commit b17f14593f
3 changed files with 239 additions and 229 deletions
@@ -8,14 +8,16 @@ import org.jetbrains.jps.model.java.JavaSourceRootType
import org.jetbrains.jps.model.module.JpsModuleSourceRootType
object ContentRootCollector {
fun collect(folders: List<Folder>): Collection<Result> {
val result = mutableListOf<Result>()
fun collect(folders: List<ImportedFolder>): Collection<ContentRootResult> {
class ContentRootWithFolders(val path: String, val folders: MutableList<ImportedFolder> = mutableListOf())
val result = mutableListOf<ContentRootWithFolders>()
folders.sorted().forEach { curr ->
// 1. ADD CONTENT ROOT, IF NEEDED:
var nearestRoot = result.lastOrNull()
if (nearestRoot != null && FileUtil.isAncestor(nearestRoot.path, curr.path, false)) {
if (curr is ContentRootFolder) {
if (curr is ProjectRootFolder) {
// don't add nested content roots
return@forEach
}
@@ -30,7 +32,7 @@ object ContentRootCollector {
// don't add root when there is only an exclude folder under it
return@forEach
}
nearestRoot = Result(curr.path)
nearestRoot = ContentRootWithFolders(curr.path)
result.add(nearestRoot)
}
@@ -48,7 +50,7 @@ object ContentRootCollector {
// don't add sub source folders
return@forEach
}
else if (prev is UserOrGeneratedSourceFolder && curr is OptionalGeneratedSourceFolder) {
else if (prev is UserOrGeneratedSourceFolder && curr is AnnotationSourceFolder) {
// don't add optional generated folder under another source folder (including generated)
return@forEach
}
@@ -56,7 +58,7 @@ object ContentRootCollector {
// don't add generated folder when there are sub source folder
nearestRoot.folders.removeLast()
}
else if (prev is ExcludedFolderAndPreventGeneratedSubfolders && curr is UserOrGeneratedSourceFolder) {
else if (prev is ExcludedFolderAndPreventSubfolders && curr is UserOrGeneratedSourceFolder) {
// don't add source folders under corresponding exclude folders
return@forEach
}
@@ -78,7 +80,7 @@ object ContentRootCollector {
val root = rootIterator.next()
val folderIterator = root.folders.iterator()
var prev: Folder? = null
var prev: ImportedFolder? = null
while (folderIterator.hasNext()) {
val curr = folderIterator.next()
if (prev is BaseExcludedFolder && curr is BaseExcludedFolder
@@ -91,11 +93,19 @@ object ContentRootCollector {
}
}
return result
return result.map { root ->
val sourceFolders = root.folders.asSequence().filterIsInstance<UserOrGeneratedSourceFolder>().map { folder ->
SourceFolderResult(folder.path, folder.type, folder is BaseGeneratedSourceFolder)
}
val excludeFolders = root.folders.asSequence().filterIsInstance<BaseExcludedFolder>().map { folder ->
ExcludedFolderResult(folder.path)
}
ContentRootResult(root.path, sourceFolders.toList(), excludeFolders.toList())
}
}
sealed class Folder(val path: String, internal val rank: Int) : Comparable<Folder> {
override fun compareTo(other: Folder): Int {
sealed class ImportedFolder(val path: String, internal val rank: Int) : Comparable<ImportedFolder> {
override fun compareTo(other: ImportedFolder): Int {
val result = FileUtil.comparePaths(path, other.path)
if (result != 0) return result
return Comparing.compare(rank, other.rank)
@@ -106,8 +116,8 @@ object ContentRootCollector {
}
}
abstract class UserOrGeneratedSourceFolder(path: String, val type: JpsModuleSourceRootType<*>, rank: Int) : Folder(path, rank) {
override fun compareTo(other: Folder): Int {
abstract class UserOrGeneratedSourceFolder(path: String, val type: JpsModuleSourceRootType<*>, rank: Int) : ImportedFolder(path, rank) {
override fun compareTo(other: ImportedFolder): Int {
val result = super.compareTo(other)
if (result != 0 || other !is UserOrGeneratedSourceFolder) return result
return Comparing.compare(rootTypeRank, other.rootTypeRank)
@@ -127,22 +137,28 @@ object ContentRootCollector {
}
}
abstract class BaseExcludedFolder(path: String, rank: Int) : Folder(path, rank)
abstract class BaseExcludedFolder(path: String, rank: Int) : ImportedFolder(path, rank)
abstract class BaseGeneratedSourceFolder(path: String, type: JpsModuleSourceRootType<*>, rank: Int)
: UserOrGeneratedSourceFolder(path, type, rank)
class ContentRootFolder(path: String) : Folder(path, 0)
class ProjectRootFolder(path: String) : ImportedFolder(path, 0)
class SourceFolder(path: String, type: JpsModuleSourceRootType<*>) : UserOrGeneratedSourceFolder(path, type, 1)
class ExcludedFolderAndPreventGeneratedSubfolders(path: String) : BaseExcludedFolder(path, 2)
class ExplicitGeneratedSourceFolder(path: String, type: JpsModuleSourceRootType<*>) : BaseGeneratedSourceFolder(path, type, 3)
class OptionalGeneratedSourceFolder(path: String, type: JpsModuleSourceRootType<*>) : BaseGeneratedSourceFolder(path, type, 4)
class ExcludedFolderAndPreventSubfolders(path: String) : BaseExcludedFolder(path, 2)
class GeneratedSourceFolder(path: String, type: JpsModuleSourceRootType<*>) : BaseGeneratedSourceFolder(path, type, 3)
class AnnotationSourceFolder(path: String, type: JpsModuleSourceRootType<*>) : BaseGeneratedSourceFolder(path, type, 4)
class ExcludedFolder(path: String) : BaseExcludedFolder(path, 5)
class Result(val path: String) {
val folders = mutableListOf<Folder>()
class ContentRootResult(val path: String,
val sourceFolders: List<SourceFolderResult>,
val excludeFolders: List<ExcludedFolderResult>) {
override fun toString() = path
}
override fun toString(): String {
return path
}
class SourceFolderResult(val path: String, val type: JpsModuleSourceRootType<*>, val isGenerated: Boolean) {
override fun toString() = "$path ${if (isGenerated) "generated" else ""} rootType='${if (type.isForTests) "test" else "main"} ${type.javaClass.simpleName}'"
}
class ExcludedFolderResult(val path: String) {
override fun toString() = path
}
}
@@ -32,7 +32,7 @@ class WorkspaceFolderImporter(
private val importingContext: FolderImportingContext) {
fun createContentRoots(module: ModuleEntity, importData: MavenModuleImportData): CachedProjectFolders {
val allFolders = mutableListOf<ContentRootCollector.Folder>()
val allFolders = mutableListOf<ContentRootCollector.ImportedFolder>()
val cachedFolders = importingContext.projectIdToCachedFolders.getOrPut(importData.mavenProject.mavenId.key) {
collectMavenFolders(importData)
@@ -42,17 +42,16 @@ class WorkspaceFolderImporter(
addSourceFolders(importData, allFolders)
addCachedFolders(importData, cachedFolders, allFolders)
for (rootInfo in ContentRootCollector.collect(allFolders)) {
if (!File(rootInfo.path).exists()) continue
for (root in ContentRootCollector.collect(allFolders)) {
if (!File(root.path).exists()) continue
val excludedUrls = rootInfo.folders.filterIsInstance<ContentRootCollector.BaseExcludedFolder>().map {
virtualFileUrlManager.fromPath(it.path)
}
val contentRootEntity = builder.addContentRootEntity(virtualFileUrlManager.fromPath(rootInfo.path),
val excludedUrls = root.excludeFolders.map { exclude -> virtualFileUrlManager.fromPath(exclude.path) }
val contentRootEntity = builder.addContentRootEntity(virtualFileUrlManager.fromPath(root.path),
excludedUrls,
emptyList(), module)
rootInfo.folders.forEach {
if (it is ContentRootCollector.UserOrGeneratedSourceFolder) registerSourceRootFolder(contentRootEntity, it)
root.sourceFolders.forEach { folder ->
if (!File(folder.path).exists()) return@forEach
registerSourceRootFolder(contentRootEntity, folder)
}
}
@@ -60,19 +59,19 @@ class WorkspaceFolderImporter(
}
private fun addContentRoot(cachedFolders: CachedProjectFolders,
allFolders: MutableList<ContentRootCollector.Folder>) {
allFolders: MutableList<ContentRootCollector.ImportedFolder>) {
val contentRoot = cachedFolders.projectContentRootPath
// make sure we don't have overlapping content roots in different modules
val alreadyRegisteredRoot = importingContext.alreadyRegisteredContentRoots.contains(contentRoot)
if (alreadyRegisteredRoot) return
allFolders.add(ContentRootCollector.ContentRootFolder(contentRoot))
allFolders.add(ContentRootCollector.ProjectRootFolder(contentRoot))
importingContext.alreadyRegisteredContentRoots.add(contentRoot)
}
private fun addSourceFolders(importData: MavenModuleImportData,
allFolders: MutableList<ContentRootCollector.Folder>) {
allFolders: MutableList<ContentRootCollector.ImportedFolder>) {
val sourceFolders = when (importData.moduleData.type) {
MavenModuleType.MAIN -> MavenFoldersImporter.getMainSourceFolders(importData.mavenProject)
MavenModuleType.TEST -> MavenFoldersImporter.getTestSourceFolders(importData.mavenProject)
@@ -86,14 +85,14 @@ class WorkspaceFolderImporter(
private fun addCachedFolders(importData: MavenModuleImportData,
cachedFolders: CachedProjectFolders,
allFolders: MutableList<ContentRootCollector.Folder>) {
fun includeIf(it: ContentRootCollector.Folder, forTests: Boolean) =
allFolders: MutableList<ContentRootCollector.ImportedFolder>) {
fun includeIf(it: ContentRootCollector.ImportedFolder, forTests: Boolean) =
when (it) {
is ContentRootCollector.UserOrGeneratedSourceFolder -> it.type.isForTests == forTests
else -> true
}
fun exceptSources(it: ContentRootCollector.Folder) = it !is ContentRootCollector.UserOrGeneratedSourceFolder
fun exceptSources(it: ContentRootCollector.ImportedFolder) = it !is ContentRootCollector.UserOrGeneratedSourceFolder
allFolders.addAll(when (importData.moduleData.type) {
MavenModuleType.MAIN -> cachedFolders.folders.filter { includeIf(it, forTests = false) }
@@ -104,9 +103,7 @@ class WorkspaceFolderImporter(
}
private fun registerSourceRootFolder(contentRootEntity: ContentRootEntity,
folder: ContentRootCollector.UserOrGeneratedSourceFolder) {
if (!File(folder.path).exists()) return
folder: ContentRootCollector.SourceFolderResult) {
val rootType = when (folder.type) {
JavaSourceRootType.SOURCE -> JpsModuleRootModelSerializer.JAVA_SOURCE_ROOT_TYPE_ID
JavaSourceRootType.TEST_SOURCE -> JpsModuleRootModelSerializer.JAVA_TEST_ROOT_TYPE_ID
@@ -123,13 +120,11 @@ class WorkspaceFolderImporter(
val isResource = JpsJavaModelSerializerExtension.JAVA_RESOURCE_ROOT_ID == rootType
|| JpsJavaModelSerializerExtension.JAVA_TEST_RESOURCE_ROOT_ID == rootType
val isGenerated = folder is ContentRootCollector.BaseGeneratedSourceFolder
if (isResource) {
builder.addJavaResourceRootEntity(sourceRootEntity, isGenerated, "")
builder.addJavaResourceRootEntity(sourceRootEntity, folder.isGenerated, "")
}
else {
builder.addJavaSourceRootEntity(sourceRootEntity, isGenerated, "")
builder.addJavaSourceRootEntity(sourceRootEntity, folder.isGenerated, "")
}
}
@@ -141,12 +136,12 @@ class WorkspaceFolderImporter(
val testOutputPath = toAbsolutePath(mavenProject.testOutputDirectory)
val targetDirPath = toAbsolutePath(mavenProject.buildDirectory)
val folders = mutableListOf<ContentRootCollector.Folder>()
val folders = mutableListOf<ContentRootCollector.ImportedFolder>()
if (importingSettings.isExcludeTargetFolder) {
folders.add(ContentRootCollector.ExcludedFolder(targetDirPath))
}
if (!FileUtil.isAncestor(targetDirPath, outputPath, false)) { //todo remove ifs?
if (!FileUtil.isAncestor(targetDirPath, outputPath, false)) {
folders.add(ContentRootCollector.ExcludedFolder(outputPath))
}
if (!FileUtil.isAncestor(targetDirPath, testOutputPath, false)) {
@@ -156,15 +151,15 @@ class WorkspaceFolderImporter(
for (each in importData.mavenProject.suitableImporters) {
val excludes = mutableListOf<String>()
each.collectExcludedFolders(importData.mavenProject, excludes)
excludes.forEach { folders.add(ContentRootCollector.ExcludedFolderAndPreventGeneratedSubfolders(toAbsolutePath(it))) }
excludes.forEach { folders.add(ContentRootCollector.ExcludedFolderAndPreventSubfolders(toAbsolutePath(it))) }
}
val generatedSourceFolders = GeneratedFoldersCollector(folders, JavaSourceRootType.SOURCE)
val generatedTestSourceFolders = GeneratedFoldersCollector(folders, JavaSourceRootType.TEST_SOURCE)
if (importingSettings.generatedSourcesFolder != MavenImportingSettings.GeneratedSourcesFolder.IGNORE) {
generatedSourceFolders.addOptional(File(mavenProject.getAnnotationProcessorDirectory(false)))
generatedTestSourceFolders.addOptional(File(mavenProject.getAnnotationProcessorDirectory(true)))
generatedSourceFolders.addAnnotationSources(File(mavenProject.getAnnotationProcessorDirectory(false)))
generatedTestSourceFolders.addAnnotationSources(File(mavenProject.getAnnotationProcessorDirectory(true)))
val generatedDir = mavenProject.getGeneratedSourcesDirectory(false)
val generatedDirTest = mavenProject.getGeneratedSourcesDirectory(true)
@@ -176,10 +171,10 @@ class WorkspaceFolderImporter(
return CachedProjectFolders(importData.mavenProject.directory, outputPath, testOutputPath, folders)
}
private class GeneratedFoldersCollector(val result: MutableList<ContentRootCollector.Folder>,
private class GeneratedFoldersCollector(val result: MutableList<ContentRootCollector.ImportedFolder>,
val type: JpsModuleSourceRootType<*>) {
fun addExcplicit(dir: File) = doAdd(dir, ContentRootCollector.ExplicitGeneratedSourceFolder(dir.path, type))
fun addOptional(dir: File) = doAdd(dir, ContentRootCollector.OptionalGeneratedSourceFolder(dir.path, type))
fun addGeneratedSources(dir: File) = doAdd(dir, ContentRootCollector.GeneratedSourceFolder(dir.path, type))
fun addAnnotationSources(dir: File) = doAdd(dir, ContentRootCollector.AnnotationSourceFolder(dir.path, type))
private fun doAdd(dir: File, info: ContentRootCollector.BaseGeneratedSourceFolder) {
val isNotEmptyDirectory = !dir.listFiles().isNullOrEmpty()
@@ -190,15 +185,15 @@ class WorkspaceFolderImporter(
}
private fun addTargetFolders(targetDir: File, result: GeneratedFoldersCollector) {
fun addAllSubDirs(dir: File) = dir.listFiles()?.forEach { result.addExcplicit(it) } // todo add optional?
fun addAllSubDirs(dir: File) = dir.listFiles()?.forEach { result.addGeneratedSources(it) }
when (importingSettings.generatedSourcesFolder) {
GENERATED_SOURCE_FOLDER -> result.addExcplicit(targetDir)
GENERATED_SOURCE_FOLDER -> result.addGeneratedSources(targetDir)
SUBFOLDER -> addAllSubDirs(targetDir)
AUTODETECT -> {
for (it in JavaSourceRootDetectionUtil.suggestRoots(targetDir)) {
val suggestedDir = it.directory
result.addExcplicit(suggestedDir)
result.addGeneratedSources(suggestedDir)
val suggestedRootPointAtTargetDir = FileUtil.filesEqual(suggestedDir, targetDir)
if (suggestedRootPointAtTargetDir) return
@@ -218,6 +213,6 @@ class WorkspaceFolderImporter(
val projectContentRootPath: String,
val outputPath: String,
val testOutputPath: String,
val folders: List<ContentRootCollector.Folder>
val folders: List<ContentRootCollector.ImportedFolder>
)
}
@@ -23,18 +23,18 @@ class ContentRootCollectorTest : MavenTestCase() {
val annotationProcessorDirectory = "/home/a/b/c/maven/target/annotation-processor/java"
val annotationProcessorTestDirectory = "/home/a/b/c/maven/target/annotation-processor-test/java"
val contentRoots = collect(contentRoots = listOf(baseContentRoot),
val contentRoots = collect(projectRoots = listOf(baseContentRoot),
mainSourceFolders = listOf(sourceMain),
mainResourceFolders = listOf(resourceMain),
testSourceFolders = listOf(sourceTest),
mainGeneratedSourceFolders = listOf(generatedSourceFolder),
mainOptionalGeneratedFolders = listOf(annotationProcessorDirectory),
testGeneratedSourceFolders = listOf(generatedTestSourceFolder),
testOptionalGeneratedFolders = listOf(annotationProcessorTestDirectory),
mainAnnotationSourceFolders = listOf(annotationProcessorDirectory),
testAnnotationSourceFolders = listOf(annotationProcessorTestDirectory),
excludeFolders = listOf(target))
assertContentRoots(contentRoots,
listOf(RootTestData(
listOf(ContentRootTestData(
expectedPath = baseContentRoot,
expectedMainSourceFolders = listOf(sourceMain),
expectedMainResourcesFolders = listOf(resourceMain),
@@ -53,12 +53,12 @@ class ContentRootCollectorTest : MavenTestCase() {
val source = "/home/source"
val nestedSource = "/home/source/dir/nested"
val contentRoots = collect(contentRoots = listOf(baseContentRoot),
val contentRoots = collect(projectRoots = listOf(baseContentRoot),
mainSourceFolders = listOf(source, nestedSource))
assertContentRoots(contentRoots,
listOf(RootTestData(expectedPath = baseContentRoot,
expectedMainSourceFolders = listOf(source)))
listOf(ContentRootTestData(expectedPath = baseContentRoot,
expectedMainSourceFolders = listOf(source)))
)
}
@@ -68,14 +68,14 @@ class ContentRootCollectorTest : MavenTestCase() {
val sourceMain = "/home/main/source"
val sourceTest = "/home/test/source"
val contentRoots = collect(contentRoots = listOf(root),
val contentRoots = collect(projectRoots = listOf(root),
mainSourceFolders = listOf(sourceMain),
mainResourceFolders = listOf(sourceMain),
testSourceFolders = listOf(sourceTest),
testResourceFolders = listOf(sourceTest))
assertContentRoots(contentRoots,
listOf(RootTestData(
listOf(ContentRootTestData(
expectedPath = root,
expectedMainSourceFolders = listOf(sourceMain),
expectedMainResourcesFolders = listOf(),
@@ -92,18 +92,18 @@ class ContentRootCollectorTest : MavenTestCase() {
val generatedSourceFolder = "/home/main/generated-sources"
val annotationProcessorDirectory = "/home/main/annotation-sources"
val contentRoots = collect(contentRoots = listOf(root),
val contentRoots = collect(projectRoots = listOf(root),
mainSourceFolders = listOf(sourceMain),
mainResourceFolders = listOf(resourceMain),
testSourceFolders = listOf(sourceMain),
testResourceFolders = listOf(resourceMain),
mainGeneratedSourceFolders = listOf(generatedSourceFolder),
mainOptionalGeneratedFolders = listOf(annotationProcessorDirectory),
testGeneratedSourceFolders = listOf(generatedSourceFolder),
testOptionalGeneratedFolders = listOf(annotationProcessorDirectory))
mainAnnotationSourceFolders = listOf(annotationProcessorDirectory),
testAnnotationSourceFolders = listOf(annotationProcessorDirectory))
assertContentRoots(contentRoots,
listOf(RootTestData(
listOf(ContentRootTestData(
expectedPath = root,
expectedMainSourceFolders = listOf(sourceMain),
expectedMainResourcesFolders = listOf(resourceMain),
@@ -122,12 +122,12 @@ class ContentRootCollectorTest : MavenTestCase() {
val root2 = "/home/source/dir/nested"
val source2 = "/home/source/dir/nested/source"
val contentRoots = collect(contentRoots = listOf(root1, root2),
val contentRoots = collect(projectRoots = listOf(root1, root2),
mainSourceFolders = listOf(source1, source2))
assertContentRoots(contentRoots,
listOf(RootTestData(expectedPath = root1,
expectedMainSourceFolders = listOf(source1)))
listOf(ContentRootTestData(expectedPath = root1,
expectedMainSourceFolders = listOf(source1)))
)
}
@@ -137,14 +137,14 @@ class ContentRootCollectorTest : MavenTestCase() {
val source = "/project/source"
val nestedGeneratedFolder = "/project/source/generated"
val nestedOptionalGeneratedFolder = "/project/source/optional-generated"
val nestedAnnotationProcessorFolder = "/project/source/annotation"
val contentRoots = collect(contentRoots = listOf(root),
val contentRoots = collect(projectRoots = listOf(root),
mainSourceFolders = listOf(source),
mainGeneratedSourceFolders = listOf(nestedGeneratedFolder),
mainOptionalGeneratedFolders = listOf(nestedOptionalGeneratedFolder))
mainAnnotationSourceFolders = listOf(nestedAnnotationProcessorFolder))
assertContentRoots(contentRoots,
listOf(RootTestData(
listOf(ContentRootTestData(
expectedPath = root,
expectedMainSourceFolders = listOf(source),
expectedMainGeneratedFolders = listOf()))
@@ -163,7 +163,7 @@ class ContentRootCollectorTest : MavenTestCase() {
val generatedNoNestedFolders = "/project/target/generated-no-subsources/"
val contentRoots = collect(contentRoots = listOf(root),
val contentRoots = collect(projectRoots = listOf(root),
mainSourceFolders = listOf(source),
mainGeneratedSourceFolders = listOf(generatedWithNestedSourceFolder,
generatedWithNestedGeneratedFolder,
@@ -171,7 +171,7 @@ class ContentRootCollectorTest : MavenTestCase() {
generatedNoNestedFolders))
assertContentRoots(contentRoots,
listOf(RootTestData(
listOf(ContentRootTestData(
expectedPath = root,
expectedMainSourceFolders = listOf(source),
expectedMainGeneratedFolders = listOf(generatedNestedFoldersHolder,
@@ -190,7 +190,7 @@ class ContentRootCollectorTest : MavenTestCase() {
val generatedNestedFoldersHolder = "/generated-with-generated/generated"
val contentRoots = collect(
contentRoots = listOf(root),
projectRoots = listOf(root),
mainSourceFolders = listOf(source),
mainGeneratedSourceFolders = listOf(generatedWithNestedSourceFolder,
generatedWithNestedGeneratedFolder,
@@ -198,44 +198,43 @@ class ContentRootCollectorTest : MavenTestCase() {
)
assertContentRoots(contentRoots,
listOf(RootTestData(expectedPath = root,
expectedMainSourceFolders = listOf(),
expectedMainGeneratedFolders = listOf()),
RootTestData(expectedPath = generatedWithNestedSourceFolder,
expectedMainSourceFolders = listOf(source),
expectedMainGeneratedFolders = listOf()),
RootTestData(expectedPath = generatedWithNestedGeneratedFolder,
expectedMainSourceFolders = listOf(),
expectedMainGeneratedFolders = listOf(generatedNestedFoldersHolder)))
listOf(ContentRootTestData(expectedPath = root,
expectedMainSourceFolders = listOf(),
expectedMainGeneratedFolders = listOf()),
ContentRootTestData(expectedPath = generatedWithNestedSourceFolder,
expectedMainSourceFolders = listOf(source),
expectedMainGeneratedFolders = listOf()),
ContentRootTestData(expectedPath = generatedWithNestedGeneratedFolder,
expectedMainSourceFolders = listOf(),
expectedMainGeneratedFolders = listOf(generatedNestedFoldersHolder)))
)
}
@Test
fun `test do not register optional generated folder when there is parent generated or source folder`() {
fun `test do not register annotation processor folder when there is parent generated or source folder`() {
val root = "/project/"
val source = "/project/source"
val generated = "/project/generated"
val optionalGenerated = "/project/optional-generated"
val annotation = "/project/annotation"
val optionalGeneratedUnderSource = "/project/source/optional-generated"
val optionalGeneratedUnderGenerated = "/project/generated/optional-generated"
val optionalGeneratedUnderOptionalGenerated = "/project/optional-generated/optional-generated"
val annotationUnderSource = "/project/source/annotation"
val annotationUnderGenerated = "/project/generated/annotation"
val annotationUnderAnnotation = "/project/annotation/annotation"
val contentRoots = collect(listOf(root),
listOf(source),
mainGeneratedSourceFolders = listOf(generated),
testGeneratedSourceFolders = listOf(),
mainOptionalGeneratedFolders = listOf(optionalGenerated,
optionalGeneratedUnderSource,
optionalGeneratedUnderGenerated,
optionalGeneratedUnderOptionalGenerated))
mainAnnotationSourceFolders = listOf(annotation,
annotationUnderSource,
annotationUnderGenerated,
annotationUnderAnnotation))
assertContentRoots(contentRoots,
listOf(RootTestData(
listOf(ContentRootTestData(
expectedPath = root,
expectedMainSourceFolders = listOf(source),
expectedMainGeneratedFolders = listOf(generated, optionalGenerated)))
expectedMainGeneratedFolders = listOf(generated, annotation)))
)
}
@@ -246,37 +245,37 @@ class ContentRootCollectorTest : MavenTestCase() {
val generated = "/home/generated"
val target = "/home/target" // will not be registered
val contentRoots = collect(contentRoots = listOf(baseContentRoot),
val contentRoots = collect(projectRoots = listOf(baseContentRoot),
mainSourceFolders = listOf(source),
excludeFolders = listOf(target),
mainGeneratedSourceFolders = listOf(generated))
mainGeneratedSourceFolders = listOf(generated),
excludeFolders = listOf(target))
assertContentRoots(contentRoots,
listOf(RootTestData(expectedPath = baseContentRoot),
RootTestData(expectedPath = source, expectedMainSourceFolders = listOf(source)),
RootTestData(expectedPath = generated, expectedMainGeneratedFolders = listOf(generated)))
listOf(ContentRootTestData(expectedPath = baseContentRoot),
ContentRootTestData(expectedPath = source, expectedMainSourceFolders = listOf(source)),
ContentRootTestData(expectedPath = generated, expectedMainGeneratedFolders = listOf(generated)))
)
}
@Test
fun `test exclude folders`() {
val contentRoots = collect(contentRoots = listOf("/home"),
val contentRoots = collect(projectRoots = listOf("/home"),
mainSourceFolders = listOf("/home/src",
"/home/exclude1/src",
"/home/exclude6"),
mainGeneratedSourceFolders = listOf("/home/exclude2/annotations", "/home/exclude4/generated"),
testGeneratedSourceFolders = listOf("/home/exclude3/annotations-test", "/home/exclude5/generated-test"),
excludeFolders = listOf("/home/exclude1",
"/home/exclude2",
"/home/exclude3",
"/home/exclude4",
"/home/exclude5",
"/home/exclude6",
"/home/exclude7"),
mainGeneratedSourceFolders = listOf("/home/exclude2/annotations", "/home/exclude4/generated"),
testGeneratedSourceFolders = listOf("/home/exclude3/annotations-test", "/home/exclude5/generated-test")
"/home/exclude7")
)
assertContentRoots(contentRoots,
listOf(RootTestData(
listOf(ContentRootTestData(
expectedPath = "/home",
expectedMainSourceFolders = listOf("/home/src",
"/home/exclude1/src",
@@ -295,19 +294,19 @@ class ContentRootCollectorTest : MavenTestCase() {
}
@Test
fun `test do not register sole exclude folder`() {
val contentRoots = collect(contentRoots = listOf(),
fun `test do not register content root for a single exclude folder`() {
val contentRoots = collect(projectRoots = listOf(),
mainSourceFolders = listOf("/root/src"),
excludeFolders = listOf("/root/exclude"),
mainGeneratedSourceFolders = listOf("/root/generated"))
mainGeneratedSourceFolders = listOf("/root/generated"),
excludeFolders = listOf("/root/exclude"))
assertContentRoots(contentRoots,
listOf(
RootTestData(
ContentRootTestData(
expectedPath = "/root/src",
expectedMainSourceFolders = listOf("/root/src"),
expectedMainGeneratedFolders = listOf(),
expectedExcludes = listOf()),
RootTestData(
ContentRootTestData(
expectedPath = "/root/generated",
expectedMainSourceFolders = listOf(),
expectedMainGeneratedFolders = listOf("/root/generated"),
@@ -320,37 +319,37 @@ class ContentRootCollectorTest : MavenTestCase() {
val root = "/root"
val exclude = "/root/exclude"
val nestedExclude = "/root/exclude/exclude"
val nestedExcludeNoGenerated = "/root/exclude/exclude-no-generated"
val nestedExcludeAndPreventSubfolders = "/root/exclude/exclude-no-subfolders"
val excludeNoGenerated = "/root/exclude-no-generated"
val excludeNoGeneratedNestedExclude = "/root/exclude-no-generated/exclude"
val excludeNoGeneratedNestedNoGenerated = "/root/exclude-no-generated/exclude-no-generated"
val excludeAndPreventSubfolders = "/root/exclude-no-subfolders"
val excludeAndPreventSubfolders_NestedExclude = "/root/exclude-no-subfolders/exclude"
val excludeAndPreventSubfolders_NestedExcludeAndPreventSubfolders = "/root/exclude-no-subfolders/exclude-no-subfolders"
val contentRoots = collect(contentRoots = listOf(root),
val contentRoots = collect(projectRoots = listOf(root),
excludeFolders = listOf(exclude,
nestedExclude,
excludeNoGeneratedNestedExclude),
excludeNoSubSourceFolders = listOf(nestedExcludeNoGenerated,
excludeNoGenerated,
excludeNoGeneratedNestedNoGenerated))
excludeAndPreventSubfolders_NestedExclude),
excludeAndPreventSubfoldersFolders = listOf(nestedExcludeAndPreventSubfolders,
excludeAndPreventSubfolders,
excludeAndPreventSubfolders_NestedExcludeAndPreventSubfolders))
assertContentRoots(contentRoots,
listOf(RootTestData(
listOf(ContentRootTestData(
expectedPath = root,
expectedExcludes = listOf(exclude, excludeNoGenerated)))
expectedExcludes = listOf(exclude, excludeAndPreventSubfolders)))
)
}
@Test
fun `test do not register exclude folder pointing to a root`() {
val contentRoots = collect(contentRoots = listOf("/root"),
val contentRoots = collect(projectRoots = listOf("/root"),
mainSourceFolders = listOf("/root/src"),
excludeFolders = listOf("/root"),
mainGeneratedSourceFolders = listOf("/root/generated"))
mainGeneratedSourceFolders = listOf("/root/generated"),
excludeFolders = listOf("/root"))
assertContentRoots(contentRoots,
listOf(
RootTestData(
ContentRootTestData(
expectedPath = "/root",
expectedMainSourceFolders = listOf("/root/src"),
expectedMainGeneratedFolders = listOf("/root/generated"),
@@ -359,7 +358,7 @@ class ContentRootCollectorTest : MavenTestCase() {
}
@Test
fun `test exclude prevent nested source and generated folders`() {
fun `test exclude and prevent nested folders`() {
val root = "/home"
val excludeWithSource = "/home/exclude-with-source"
@@ -370,25 +369,25 @@ class ContentRootCollectorTest : MavenTestCase() {
val excludeWithTestGenerated = "/home/exclude-with-test-generated"
val testGeneratedUnderExcluded = "/home/exclude-with-test-generated/test-generated"
val excludeWithOptionalGenerated = "/home/exclude-with-optional-generated"
val optionalGeneratedUnderExcluded = "/home/exclude-with-optional-generated/optional-generated"
val excludeWithTestOptionalGenerated = "/home/exclude-with-test-optional-generated"
val testOptionalGeneratedUnderExcluded = "/home/exclude-with-test-optional-generated/test-optional-generated"
val excludeWithAnnotation = "/home/exclude-with-annotation"
val annotationUnderExcluded = "/home/exclude-with-annotation/annotation"
val excludeWithTestAnnotation = "/home/exclude-with-test-annotation"
val testAnnotationUnderExcluded = "/home/exclude-with-test-annotation/test-annotation"
val contentRoots = collect(contentRoots = listOf(root),
val contentRoots = collect(projectRoots = listOf(root),
mainSourceFolders = listOf(sourcesUnderExcluded),
excludeNoSubSourceFolders = listOf(excludeWithSource,
excludeWithGenerated,
excludeWithTestGenerated,
excludeWithOptionalGenerated,
excludeWithTestOptionalGenerated),
mainGeneratedSourceFolders = listOf(generatedUnderExcluded),
mainOptionalGeneratedFolders = listOf(optionalGeneratedUnderExcluded),
testGeneratedSourceFolders = listOf(testGeneratedUnderExcluded),
testOptionalGeneratedFolders = listOf(testOptionalGeneratedUnderExcluded))
mainAnnotationSourceFolders = listOf(annotationUnderExcluded),
testAnnotationSourceFolders = listOf(testAnnotationUnderExcluded),
excludeAndPreventSubfoldersFolders = listOf(excludeWithSource,
excludeWithGenerated,
excludeWithTestGenerated,
excludeWithAnnotation,
excludeWithTestAnnotation))
assertContentRoots(contentRoots,
listOf(RootTestData(
listOf(ContentRootTestData(
expectedPath = root,
expectedMainSourceFolders = listOf(),
expectedTestSourceFolders = listOf(),
@@ -396,9 +395,9 @@ class ContentRootCollectorTest : MavenTestCase() {
expectedTestGeneratedFolders = listOf(),
expectedExcludes = listOf(excludeWithSource,
excludeWithGenerated,
excludeWithOptionalGenerated,
excludeWithAnnotation,
excludeWithTestGenerated,
excludeWithTestOptionalGenerated)))
excludeWithTestAnnotation)))
)
}
@@ -410,29 +409,30 @@ class ContentRootCollectorTest : MavenTestCase() {
val sourceMain2 = "/home/java2"
val target = "/home/target"
val generatedSourceFolder = "/home/target/generated-sources/java"
val generatedTestSourceFolder = "/home/target/generated-sources-test/java"
val optionalGeneratedSourceFolder = "/home/target/optional-generated-sources/java"
val optionalGeneratedTestSourceFolder = "/home/target/optional-generated-sources-test/java"
val testGeneratedSourceFolder = "/home/target/test-generated-sources/java"
val annotationSourceFolder = "/home/target/annotation-sources/java"
val testAnnotationSourceFolder = "/home/target/test-annotation-sources/java"
val contentRoots = collect(contentRoots = listOf(baseContentRoot),
val contentRoots = collect(projectRoots = listOf(baseContentRoot),
mainSourceFolders = listOf(sourceMain, sourceMain2),
excludeFolders = listOf(target),
excludeNoSubSourceFolders = emptyList(),
mainGeneratedSourceFolders = listOf(generatedSourceFolder),
mainOptionalGeneratedFolders = listOf(optionalGeneratedSourceFolder),
testGeneratedSourceFolders = listOf(generatedTestSourceFolder),
testOptionalGeneratedFolders = listOf(optionalGeneratedTestSourceFolder))
testGeneratedSourceFolders = listOf(testGeneratedSourceFolder),
mainAnnotationSourceFolders = listOf(annotationSourceFolder),
testAnnotationSourceFolders = listOf(testAnnotationSourceFolder),
excludeFolders = listOf(target),
excludeAndPreventSubfoldersFolders = emptyList())
assertContentRoots(
contentRoots,
listOf(
RootTestData(expectedPath = baseContentRoot, expectedMainSourceFolders = listOf(sourceMain)),
RootTestData(expectedPath = sourceMain2, expectedMainSourceFolders = listOf(sourceMain2)),
RootTestData(expectedPath = generatedSourceFolder, expectedMainGeneratedFolders = listOf(generatedSourceFolder)),
RootTestData(expectedPath = generatedTestSourceFolder, expectedTestGeneratedFolders = listOf(generatedTestSourceFolder)),
RootTestData(expectedPath = optionalGeneratedSourceFolder, expectedMainGeneratedFolders = listOf(optionalGeneratedSourceFolder)),
RootTestData(expectedPath = optionalGeneratedTestSourceFolder,
expectedTestGeneratedFolders = listOf(optionalGeneratedTestSourceFolder)))
ContentRootTestData(expectedPath = baseContentRoot, expectedMainSourceFolders = listOf(sourceMain)),
ContentRootTestData(expectedPath = sourceMain2, expectedMainSourceFolders = listOf(sourceMain2)),
ContentRootTestData(expectedPath = generatedSourceFolder, expectedMainGeneratedFolders = listOf(generatedSourceFolder)),
ContentRootTestData(expectedPath = testGeneratedSourceFolder, expectedTestGeneratedFolders = listOf(testGeneratedSourceFolder)),
ContentRootTestData(expectedPath = annotationSourceFolder,
expectedMainGeneratedFolders = listOf(annotationSourceFolder)),
ContentRootTestData(expectedPath = testAnnotationSourceFolder,
expectedTestGeneratedFolders = listOf(testAnnotationSourceFolder)))
)
}
@@ -452,7 +452,7 @@ class ContentRootCollectorTest : MavenTestCase() {
val annotationProcessorDirectory = "/home/a/b/c/maven/target/annotation-processor/java"
val annotationProcessorTestDirectory = "/home/a/b/c/maven/target/annotation-processor-test/java"
val contentRoots = collect(contentRoots = listOf(baseContentRoot),
val contentRoots = collect(projectRoots = listOf(baseContentRoot),
mainSourceFolders = listOf(
sourceMain4,
sourceMain3,
@@ -464,85 +464,84 @@ class ContentRootCollectorTest : MavenTestCase() {
resourceMain2,
resourceMain,
),
mainOptionalGeneratedFolders = listOf(annotationProcessorDirectory),
testOptionalGeneratedFolders = listOf(annotationProcessorTestDirectory))
mainAnnotationSourceFolders = listOf(annotationProcessorDirectory),
testAnnotationSourceFolders = listOf(annotationProcessorTestDirectory))
assertContentRoots(contentRoots, listOf(
RootTestData(expectedPath = baseContentRoot,
expectedMainSourceFolders = listOf(sourceMain),
expectedMainResourcesFolders = listOf(resourceMain)),
RootTestData(expectedPath = sourceMain2, expectedMainSourceFolders = listOf(sourceMain2)),
RootTestData(expectedPath = sourceMain3, expectedMainSourceFolders = listOf(sourceMain3)),
RootTestData(expectedPath = sourceMain4, expectedMainSourceFolders = listOf(sourceMain4)),
RootTestData(expectedPath = resourceMain2, expectedMainResourcesFolders = listOf(resourceMain2)),
RootTestData(expectedPath = resourceMain3, expectedMainResourcesFolders = listOf(resourceMain3)),
RootTestData(expectedPath = resourceMain4, expectedMainResourcesFolders = listOf(resourceMain4)),
RootTestData(expectedPath = annotationProcessorDirectory,
expectedMainGeneratedFolders = listOf(annotationProcessorDirectory)),
RootTestData(expectedPath = annotationProcessorTestDirectory,
expectedTestGeneratedFolders = listOf(annotationProcessorTestDirectory)))
ContentRootTestData(expectedPath = baseContentRoot,
expectedMainSourceFolders = listOf(sourceMain),
expectedMainResourcesFolders = listOf(resourceMain)),
ContentRootTestData(expectedPath = sourceMain2, expectedMainSourceFolders = listOf(sourceMain2)),
ContentRootTestData(expectedPath = sourceMain3, expectedMainSourceFolders = listOf(sourceMain3)),
ContentRootTestData(expectedPath = sourceMain4, expectedMainSourceFolders = listOf(sourceMain4)),
ContentRootTestData(expectedPath = resourceMain2, expectedMainResourcesFolders = listOf(resourceMain2)),
ContentRootTestData(expectedPath = resourceMain3, expectedMainResourcesFolders = listOf(resourceMain3)),
ContentRootTestData(expectedPath = resourceMain4, expectedMainResourcesFolders = listOf(resourceMain4)),
ContentRootTestData(expectedPath = annotationProcessorDirectory,
expectedMainGeneratedFolders = listOf(annotationProcessorDirectory)),
ContentRootTestData(expectedPath = annotationProcessorTestDirectory,
expectedTestGeneratedFolders = listOf(annotationProcessorTestDirectory)))
)
}
fun collect(contentRoots: List<String> = emptyList(),
mainSourceFolders: List<String> = emptyList(),
mainResourceFolders: List<String> = emptyList(),
testSourceFolders: List<String> = emptyList(),
testResourceFolders: List<String> = emptyList(),
excludeFolders: List<String> = emptyList(),
excludeNoSubSourceFolders: List<String> = emptyList(),
mainGeneratedSourceFolders: List<String> = emptyList(),
testGeneratedSourceFolders: List<String> = emptyList(),
mainOptionalGeneratedFolders: List<String> = emptyList(),
testOptionalGeneratedFolders: List<String> = emptyList()): Collection<ContentRootCollector.Result> {
val foldersData = mutableListOf<ContentRootCollector.Folder>()
private fun collect(projectRoots: List<String> = emptyList(),
mainSourceFolders: List<String> = emptyList(),
mainResourceFolders: List<String> = emptyList(),
testSourceFolders: List<String> = emptyList(),
testResourceFolders: List<String> = emptyList(),
mainGeneratedSourceFolders: List<String> = emptyList(),
testGeneratedSourceFolders: List<String> = emptyList(),
mainAnnotationSourceFolders: List<String> = emptyList(),
testAnnotationSourceFolders: List<String> = emptyList(),
excludeFolders: List<String> = emptyList(),
excludeAndPreventSubfoldersFolders: List<String> = emptyList()): Collection<ContentRootCollector.ContentRootResult> {
val folders = mutableListOf<ContentRootCollector.ImportedFolder>()
contentRoots.forEach { foldersData.add(ContentRootCollector.ContentRootFolder(it)) }
projectRoots.forEach { folders.add(ContentRootCollector.ProjectRootFolder(it)) }
mainSourceFolders.forEach { foldersData.add(ContentRootCollector.SourceFolder(it, JavaSourceRootType.SOURCE)) }
mainResourceFolders.forEach { foldersData.add(ContentRootCollector.SourceFolder(it, JavaResourceRootType.RESOURCE)) }
mainSourceFolders.forEach { folders.add(ContentRootCollector.SourceFolder(it, JavaSourceRootType.SOURCE)) }
mainResourceFolders.forEach { folders.add(ContentRootCollector.SourceFolder(it, JavaResourceRootType.RESOURCE)) }
mainGeneratedSourceFolders.forEach {
foldersData.add(ContentRootCollector.ExplicitGeneratedSourceFolder(it, JavaSourceRootType.SOURCE))
folders.add(ContentRootCollector.GeneratedSourceFolder(it, JavaSourceRootType.SOURCE))
}
mainOptionalGeneratedFolders.forEach {
foldersData.add(ContentRootCollector.OptionalGeneratedSourceFolder(it, JavaSourceRootType.SOURCE))
mainAnnotationSourceFolders.forEach {
folders.add(ContentRootCollector.AnnotationSourceFolder(it, JavaSourceRootType.SOURCE))
}
testSourceFolders.forEach { foldersData.add(ContentRootCollector.SourceFolder(it, JavaSourceRootType.TEST_SOURCE)) }
testResourceFolders.forEach { foldersData.add(ContentRootCollector.SourceFolder(it, JavaResourceRootType.TEST_RESOURCE)) }
testSourceFolders.forEach { folders.add(ContentRootCollector.SourceFolder(it, JavaSourceRootType.TEST_SOURCE)) }
testResourceFolders.forEach { folders.add(ContentRootCollector.SourceFolder(it, JavaResourceRootType.TEST_RESOURCE)) }
testGeneratedSourceFolders.forEach {
foldersData.add(ContentRootCollector.ExplicitGeneratedSourceFolder(it, JavaSourceRootType.TEST_SOURCE))
folders.add(ContentRootCollector.GeneratedSourceFolder(it, JavaSourceRootType.TEST_SOURCE))
}
testOptionalGeneratedFolders.forEach {
foldersData.add(ContentRootCollector.OptionalGeneratedSourceFolder(it, JavaSourceRootType.TEST_SOURCE))
testAnnotationSourceFolders.forEach {
folders.add(ContentRootCollector.AnnotationSourceFolder(it, JavaSourceRootType.TEST_SOURCE))
}
excludeFolders.forEach { foldersData.add(ContentRootCollector.ExcludedFolder(it)) }
excludeNoSubSourceFolders.forEach { foldersData.add(ContentRootCollector.ExcludedFolderAndPreventGeneratedSubfolders(it)) }
excludeFolders.forEach { folders.add(ContentRootCollector.ExcludedFolder(it)) }
excludeAndPreventSubfoldersFolders.forEach { folders.add(ContentRootCollector.ExcludedFolderAndPreventSubfolders(it)) }
return collect(foldersData)
return collect(folders)
}
private fun assertContentRoots(actualRoots: Collection<ContentRootCollector.Result>,
expectedRoots: Collection<RootTestData>) {
fun mapPaths(result: ContentRootCollector.Result,
clazz: Class<out ContentRootCollector.UserOrGeneratedSourceFolder>,
type: JpsModuleSourceRootType<*>) =
result.folders.filterIsInstance(clazz).filter { it.type == type }.map { it.path }.sorted()
private fun assertContentRoots(actualRoots: Collection<ContentRootCollector.ContentRootResult>,
expectedRoots: Collection<ContentRootTestData>) {
fun mapPaths(result: ContentRootCollector.ContentRootResult,
type: JpsModuleSourceRootType<*>,
isGenerated: Boolean) =
result.sourceFolders.filter { it.type == type && it.isGenerated == isGenerated }.map { it.path }.sorted()
val actualSorted = actualRoots.map {
RootTestData(
ContentRootTestData(
it.path,
expectedMainSourceFolders = mapPaths(it, ContentRootCollector.SourceFolder::class.java, JavaSourceRootType.SOURCE),
expectedMainResourcesFolders = mapPaths(it, ContentRootCollector.SourceFolder::class.java, JavaResourceRootType.RESOURCE),
expectedMainGeneratedFolders = mapPaths(it, ContentRootCollector.BaseGeneratedSourceFolder::class.java, JavaSourceRootType.SOURCE),
expectedMainSourceFolders = mapPaths(it, JavaSourceRootType.SOURCE, isGenerated = false),
expectedMainResourcesFolders = mapPaths(it, JavaResourceRootType.RESOURCE, isGenerated = false),
expectedMainGeneratedFolders = mapPaths(it, JavaSourceRootType.SOURCE, isGenerated = true),
expectedTestSourceFolders = mapPaths(it, ContentRootCollector.SourceFolder::class.java, JavaSourceRootType.TEST_SOURCE),
expectedTestResourcesFolders = mapPaths(it, ContentRootCollector.SourceFolder::class.java, JavaResourceRootType.TEST_RESOURCE),
expectedTestGeneratedFolders = mapPaths(it, ContentRootCollector.BaseGeneratedSourceFolder::class.java,
JavaSourceRootType.TEST_SOURCE),
expectedTestSourceFolders = mapPaths(it, JavaSourceRootType.TEST_SOURCE, isGenerated = false),
expectedTestResourcesFolders = mapPaths(it, JavaResourceRootType.TEST_RESOURCE, isGenerated = false),
expectedTestGeneratedFolders = mapPaths(it, JavaSourceRootType.TEST_SOURCE, isGenerated = true),
expectedExcludes = it.folders.filterIsInstance<ContentRootCollector.BaseExcludedFolder>().map { it.path }.sorted(),
expectedExcludes = it.excludeFolders.map { it.path }.sorted(),
)
}.sortedBy { it.expectedPath }
@@ -563,14 +562,14 @@ class ContentRootCollectorTest : MavenTestCase() {
TestCase.assertEquals(expectedSorted, actualSorted)
}
private data class RootTestData(val expectedPath: String,
val expectedMainSourceFolders: List<String> = emptyList(),
val expectedTestSourceFolders: List<String> = emptyList(),
val expectedMainResourcesFolders: List<String> = emptyList(),
val expectedTestResourcesFolders: List<String> = emptyList(),
val expectedMainGeneratedFolders: List<String> = emptyList(),
val expectedTestGeneratedFolders: List<String> = emptyList(),
val expectedExcludes: List<String> = emptyList()) {
private data class ContentRootTestData(val expectedPath: String,
val expectedMainSourceFolders: List<String> = emptyList(),
val expectedTestSourceFolders: List<String> = emptyList(),
val expectedMainResourcesFolders: List<String> = emptyList(),
val expectedTestResourcesFolders: List<String> = emptyList(),
val expectedMainGeneratedFolders: List<String> = emptyList(),
val expectedTestGeneratedFolders: List<String> = emptyList(),
val expectedExcludes: List<String> = emptyList()) {
override fun toString(): String {
val result = StringBuilder()
result.appendLine("{")