blob: 375126a818f70beaf971659ddb3dd1f3b9838d81 [file] [log] [blame]
/*
* Copyright 2018 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.build.dependencyTracker
import org.gradle.api.Project
import org.gradle.api.plugins.ExtraPropertiesExtension
import org.gradle.testfixtures.ProjectBuilder
import org.hamcrest.CoreMatchers
import org.hamcrest.MatcherAssert
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
import java.io.File
@RunWith(JUnit4::class)
class AffectedModuleDetectorImplTest {
@Rule
@JvmField
val attachLogsRule = AttachLogsTestRule()
private val logger = attachLogsRule.logger
@Rule
@JvmField
val tmpFolder = TemporaryFolder()
@Rule
@JvmField
val tmpFolder2 = TemporaryFolder()
private lateinit var root: Project
private lateinit var p1: Project
private lateinit var p2: Project
private lateinit var p3: Project
private lateinit var p4: Project
private lateinit var p5: Project
private lateinit var p6: Project
private lateinit var p7: Project
private lateinit var p8: Project
private lateinit var p9: Project
private lateinit var p10: Project
private lateinit var p11: Project
private val projectGraph by lazy {
ProjectGraph(root)
}
private val dependencyTracker by lazy {
DependencyTracker(root, logger)
}
private val cobuiltTestPaths = setOf(setOf(":cobuilt1", ":cobuilt2"))
private val ignoredPaths = setOf("ignored/")
@Before
fun init() {
val tmpDir = tmpFolder.root
/*
Dummy project file tree:
root -----------------
/ | \ | | | |
p1 p7 p2 p8 p9 p10 p11
/ \
p3 p5
/ \
p4 p6
Dependency forest:
p1 p2 p7 p8 p9 p10 p11
/ \ / \
p3 p5 p6
/
p4
*/
root = ProjectBuilder.builder()
.withProjectDir(tmpDir)
.withName("root")
.build()
// Project Graph expects supportRootFolder.
(root.properties.get("ext") as ExtraPropertiesExtension).set("supportRootFolder", tmpDir)
p1 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p1"))
.withName("p1")
.withParent(root)
.build()
p2 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p2"))
.withName("p2")
.withParent(root)
.build()
p3 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p1:p3"))
.withName("p3")
.withParent(p1)
.build()
val p3config = p3.configurations.create("p3config")
p3config.dependencies.add(p3.dependencies.project(mutableMapOf("path" to ":p1")))
p4 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p1:p3:p4"))
.withName("p4")
.withParent(p3)
.build()
val p4config = p4.configurations.create("p4config")
p4config.dependencies.add(p4.dependencies.project(mutableMapOf("path" to ":p1:p3")))
p5 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p2:p5"))
.withName("p5")
.withParent(p2)
.build()
val p5config = p5.configurations.create("p5config")
p5config.dependencies.add(p5.dependencies.project(mutableMapOf("path" to ":p2")))
p5config.dependencies.add(p5.dependencies.project(mutableMapOf("path" to ":p1:p3")))
p6 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p1:p3:p6"))
.withName("p6")
.withParent(p3)
.build()
val p6config = p6.configurations.create("p6config")
p6config.dependencies.add(p6.dependencies.project(mutableMapOf("path" to ":p2")))
p7 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p7"))
.withName("p7")
.withParent(root)
.build()
p8 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p8"))
.withName("cobuilt1")
.withParent(root)
.build()
p9 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p9"))
.withName("cobuilt2")
.withParent(root)
.build()
p10 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p10"))
.withName("benchmark")
.withParent(root)
.build()
p11 = ProjectBuilder.builder()
.withProjectDir(tmpDir.resolve("p11"))
.withName("placeholder-tests")
.withParent(root)
.build()
}
@Test
fun noChangeCLs() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
changedFilesProvider = {
emptyList()
}
)
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
MatcherAssert.assertThat(
detector.dependentProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
true
)
)
}
@Test
fun changeInOne() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
changedFilesProvider = {
listOf(convertToFilePath("p1", "foo.java"))
}
)
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p1.path, p11.path)
)
)
MatcherAssert.assertThat(
detector.dependentProjects,
CoreMatchers.`is`(
setOf(p3.path, p4.path, p5.path, p11.path)
)
)
}
@Test
fun changeInTwo() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
changedFilesProvider = {
listOf(
convertToFilePath("p1", "foo.java"),
convertToFilePath("p2", "bar.java")
)
}
)
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p1.path, p2.path, p11.path)
)
)
MatcherAssert.assertThat(
detector.dependentProjects,
CoreMatchers.`is`(
setOf(p3.path, p4.path, p5.path, p6.path, p11.path)
)
)
}
@Test
fun changeInRoot() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
changedFilesProvider = {
listOf("foo.java")
}
)
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
MatcherAssert.assertThat(
detector.dependentProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
true
)
)
}
@Test
fun changeInRootAndSubproject() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
changedFilesProvider = {
listOf("foo.java", convertToFilePath("p7", "bar.java"))
}
)
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p7.path, p11.path)
)
)
MatcherAssert.assertThat(
detector.dependentProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
true
)
)
}
@Test
fun changeInCobuilt() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
changedFilesProvider = {
listOf(
convertToFilePath(
"p8", "foo.java"
)
)
}
)
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p8.path, p9.path, p11.path)
)
)
MatcherAssert.assertThat(
detector.dependentProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
}
@Test(expected = IllegalStateException::class)
fun changeInCobuiltMissingCobuilt() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = setOf(setOf(":cobuilt1", ":cobuilt2", ":cobuilt3")),
changedFilesProvider = {
listOf(
convertToFilePath(
"p8", "foo.java"
)
)
}
)
// This should trigger IllegalStateException due to missing cobuilt3
detector.changedProjects
}
@Test
fun changeInCobuiltAllCobuiltsMissing() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = setOf(setOf("cobuilt3", "cobuilt4", "cobuilt5")),
changedFilesProvider = {
listOf(
convertToFilePath(
"p8", "foo.java"
)
)
}
)
// There should be no exception thrown here because *all* cobuilts are missing.
detector.changedProjects
}
@Test
fun projectSubset() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
changedFilesProvider = {
listOf(convertToFilePath("p1", "foo.java"))
}
)
// Verify expectations on affected projects
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p1.path, p11.path)
)
)
MatcherAssert.assertThat(
detector.dependentProjects,
CoreMatchers.`is`(
setOf(p3.path, p4.path, p5.path, p11.path)
)
)
// Test changed
MatcherAssert.assertThat(
detector.getSubset(p1.path),
CoreMatchers.`is`(
ProjectSubset.CHANGED_PROJECTS
)
)
// Test dependent
MatcherAssert.assertThat(
detector.getSubset(p3.path),
CoreMatchers.`is`(
ProjectSubset.DEPENDENT_PROJECTS
)
)
// Random unrelated project should return none
MatcherAssert.assertThat(
detector.getSubset(p7.path),
CoreMatchers.`is`(
ProjectSubset.NONE
)
)
}
@Test
fun projectSubset_noChangedFiles() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
changedFilesProvider = {
emptyList()
}
)
// Verify expectations on affected projects
MatcherAssert.assertThat(
detector.affectedProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
// No changed files in postsubmit -> return all
MatcherAssert.assertThat(
detector.getSubset(p1.path),
CoreMatchers.`is`(
ProjectSubset.NONE
)
)
MatcherAssert.assertThat(
detector.getSubset(p3.path),
CoreMatchers.`is`(
ProjectSubset.NONE
)
)
// Only the placeholder test should return CHANGED_PROJECTS
MatcherAssert.assertThat(
detector.getSubset(p11.path),
CoreMatchers.`is`(
ProjectSubset.CHANGED_PROJECTS
)
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
true
)
)
}
@Test
fun projectSubset_unknownChangedFiles() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
changedFilesProvider = {
listOf(convertToFilePath("unknown", "file.java"))
}
)
// Verify expectations on affected projects
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
MatcherAssert.assertThat(
detector.dependentProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
// Everything should return NONE in buildall case
MatcherAssert.assertThat(
detector.getSubset(p1.path),
CoreMatchers.`is`(
ProjectSubset.NONE
)
)
MatcherAssert.assertThat(
detector.getSubset(p3.path),
CoreMatchers.`is`(
ProjectSubset.NONE
)
)
// Only the placeholder test should return CHANGED_PROJECTS
MatcherAssert.assertThat(
detector.getSubset(p11.path),
CoreMatchers.`is`(
ProjectSubset.CHANGED_PROJECTS
)
)
}
@Test
fun changeInPlaygroundCommon() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
changedFilesProvider = {
listOf("playground-common/tmp.kt")
}
)
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
false
)
)
}
@Test
fun changeInGithubConfig() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
changedFilesProvider = {
listOf(".github/workflows/bar.txt")
}
)
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p11.path)
)
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
false
)
)
}
@Test
fun changeInPlaygroundCommonAndRoot() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
changedFilesProvider = {
listOf("playground-common/tmp.kt", "root.txt")
}
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
true
)
)
}
@Test
fun `Only ignored file`() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
ignoredPaths = ignoredPaths,
changedFilesProvider = {
listOf(convertToFilePath("ignored", "example.txt"))
}
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
false
)
)
}
@Test
fun `Ignored file and changed file`() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
ignoredPaths = ignoredPaths,
changedFilesProvider = {
listOf(
convertToFilePath("ignored", "example.txt"),
convertToFilePath("p1", "foo.kt")
)
}
)
MatcherAssert.assertThat(
detector.changedProjects,
CoreMatchers.`is`(
setOf(p1.path, p11.path)
)
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
false
)
)
}
@Test
fun `Ignored file and unknown file`() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
ignoredPaths = ignoredPaths,
changedFilesProvider = {
listOf(
convertToFilePath("ignored", "example.txt"),
convertToFilePath("unknown", "foo.kt")
)
}
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
true
)
)
}
@Test
fun `Ignored file, unknown file, and changed file`() {
val detector = AffectedModuleDetectorImpl(
projectGraph = projectGraph,
dependencyTracker = dependencyTracker,
logger = logger,
ignoreUnknownProjects = false,
cobuiltTestPaths = cobuiltTestPaths,
ignoredPaths = ignoredPaths,
changedFilesProvider = {
listOf(
"ignored/eg.txt",
convertToFilePath("unknown", "foo.kt"),
convertToFilePath("p1", "bar.kt")
)
}
)
MatcherAssert.assertThat(
detector.buildAll,
CoreMatchers.`is`(
true
)
)
}
// For both Linux/Windows
fun convertToFilePath(vararg list: String): String {
return list.toList().joinToString(File.separator)
}
}