Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: new APIs for artifact access from test fixtures. #1102

Merged
merged 7 commits into from
Jan 15, 2024
10 changes: 6 additions & 4 deletions src/functionalTest/groovy/com/autonomousapps/AdviceHelper.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,9 @@ final class AdviceHelper {
return new ProjectCoordinates(projectPath, defaultGVI(capability), buildPath)
}

static Coordinates includedBuildCoordinates(String identifier, ProjectCoordinates resolvedProject,
String capability = null) {
static Coordinates includedBuildCoordinates(
String identifier, ProjectCoordinates resolvedProject, String capability = null
) {
return new IncludedBuildCoordinates(identifier, resolvedProject, defaultGVI(capability))
}

Expand All @@ -76,8 +77,9 @@ final class AdviceHelper {
return projectAdvice(projectPath, advice, pluginAdvice, false)
}

static ProjectAdvice projectAdvice(String projectPath, Set<Advice> advice, Set<PluginAdvice> pluginAdvice,
boolean shouldFail) {
static ProjectAdvice projectAdvice(
String projectPath, Set<Advice> advice, Set<PluginAdvice> pluginAdvice, boolean shouldFail
) {
return projectAdvice(projectPath, advice, pluginAdvice, [] as Set<ModuleAdvice>, shouldFail)
}

Expand Down
24 changes: 8 additions & 16 deletions src/functionalTest/groovy/com/autonomousapps/AdviceStrategy.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,14 @@
// SPDX-License-Identifier: Apache-2.0
package com.autonomousapps


import com.autonomousapps.internal.OutputPathsKt
import com.autonomousapps.internal.utils.MoshiUtils
import com.autonomousapps.kit.GradleProject
import com.autonomousapps.kit.utils.Files
import com.autonomousapps.model.Advice
import com.autonomousapps.model.BuildHealth
import com.autonomousapps.model.ProjectAdvice
import com.squareup.moshi.Types

abstract class AdviceStrategy {
abstract List<Advice> actualAdviceForFirstSubproject(GradleProject gradleProject)

abstract def actualBuildHealth(GradleProject gradleProject)

Expand Down Expand Up @@ -41,7 +37,8 @@ abstract class AdviceStrategy {

@Override
Map<String, Set<String>> getDuplicateDependenciesReport(GradleProject gradleProject) {
def json = Files.resolveFromRoot(gradleProject, OutputPathsKt.getDuplicateDependenciesReport()).text.trim()
def json = gradleProject.singleArtifact(':', OutputPathsKt.getDuplicateDependenciesReport())
.asPath.text.trim()
def set = Types.newParameterizedType(Set, String)
def map = Types.newParameterizedType(Map, String, set)
def adapter = MoshiUtils.MOSHI.<Map<String, Set<String>>> adapter(map)
Expand All @@ -50,25 +47,20 @@ abstract class AdviceStrategy {

@Override
List<String> getResolvedDependenciesReport(GradleProject gradleProject, String projectPath) {
File report = Files.resolveFromName(gradleProject, projectPath, OutputPathsKt.getResolvedDependenciesReport())
return report.text.trim().readLines()
def report = gradleProject.singleArtifact(projectPath, OutputPathsKt.getResolvedDependenciesReport())
return report.asPath.text.trim().readLines()
}

@Override
def actualBuildHealth(GradleProject gradleProject) {
File buildHealth = Files.resolveFromRoot(gradleProject, OutputPathsKt.getFinalAdvicePathV2())
return fromAllProjectAdviceJson(buildHealth.text)
def buildHealth = gradleProject.singleArtifact(':', OutputPathsKt.getFinalAdvicePathV2())
return fromAllProjectAdviceJson(buildHealth.asPath.text)
}

@Override
def actualComprehensiveAdviceForProject(GradleProject gradleProject, String projectName) {
File advice = Files.resolveFromName(gradleProject, projectName, OutputPathsKt.getAggregateAdvicePathV2())
return fromProjectAdvice(advice.text)
}

@Override
List<Advice> actualAdviceForFirstSubproject(GradleProject gradleProject) {
throw new IllegalStateException("Not yet implemented")
def advice = gradleProject.singleArtifact(projectName, OutputPathsKt.getAggregateAdvicePathV2())
return fromProjectAdvice(advice.asPath.text)
}
}
}
4 changes: 2 additions & 2 deletions testkit/gradle-testkit-support/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ dependencies {
api(platform(libs.kotlin.bom))
api(gradleTestKit())

implementation(libs.truth)

testImplementation(platform(libs.junit.bom))
testImplementation(libs.junit.api)
testImplementation(libs.truth)

testRuntimeOnly(libs.junit.engine)

dokkaHtmlPlugin(libs.kotlin.dokka)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ import com.autonomousapps.kit.android.AndroidColorRes
import com.autonomousapps.kit.android.AndroidManifest
import com.autonomousapps.kit.android.AndroidStyleRes
import com.autonomousapps.kit.android.AndroidSubproject
import com.autonomousapps.kit.artifacts.BuildArtifact
import com.autonomousapps.kit.artifacts.toBuildArtifact
import com.autonomousapps.kit.gradle.BuildScript
import com.autonomousapps.kit.gradle.GradleProperties
import com.autonomousapps.kit.gradle.SettingsScript
import com.autonomousapps.kit.internal.ensurePrefix
import com.autonomousapps.kit.utils.buildPathForName
import java.io.File
import java.nio.file.Files
import java.nio.file.Path
import kotlin.io.path.exists

/**
* A Gradle project consists of:
Expand Down Expand Up @@ -60,9 +66,7 @@ public class GradleProject(
}

/** Use ":" for the root project. */
public fun projectDir(projectName: String): Path {
return projectDir(forName(projectName))
}
public fun projectDir(projectName: String): Path = projectDir(forName(projectName))

/** Use [rootProject] for the root project. */
public fun projectDir(project: Subproject): Path {
Expand All @@ -72,14 +76,18 @@ public class GradleProject(
return rootDir.toPath().resolve("${project.includedBuild?.let { "$it/" } ?: ""}${project.name.replace(":", "/")}/")
}

/** Use ":" for the root project. */
public fun buildDir(projectName: String): Path {
return buildDir(forName(projectName))
/**
* Provides access to a build directory for one of the projects in your fixture. Use ":" for the root project.
*/
@JvmOverloads
public fun buildDir(projectName: String, buildDirName: String = "build"): Path {
return buildDir(project = forName(projectName), buildDirName = buildDirName)
}

/** Use [rootProject] for the root project. */
public fun buildDir(project: Subproject): Path {
return projectDir(project).resolve("build/")
@JvmOverloads
public fun buildDir(project: Subproject, buildDirName: String = "build"): Path {
return projectDir(project).resolve("${buildDirName}/")
}

public fun findIncludedBuild(path: String): GradleProject? {
Expand All @@ -103,13 +111,68 @@ public class GradleProject(
}
}

/**
* Returns the single artifact at [relativePath] from the build directory of project [projectName], failing if no such
* artifact exists. Uses "build" as the build directory name by default.
*/
@JvmOverloads
public fun singleArtifact(
projectName: String,
relativePath: String,
buildDirName: String = "build",
): BuildArtifact {
val artifact = buildPathForName(path = projectName, buildDirName = buildDirName).resolve(relativePath)
check(artifact.exists()) { "No artifact with path '$artifact'" }
return BuildArtifact(artifact)
}

/**
* Returns the single artifact at [relativePath] from the build directory of project [projectName], failing if no such
* artifact exists. An alias for [singleArtifact]. Uses "build" as the build directory name by default.
*/
@JvmOverloads
public fun getArtifact(projectName: String, relativePath: String, buildDirName: String = "build"): BuildArtifact {
return singleArtifact(projectName = projectName, relativePath = relativePath, buildDirName = buildDirName)
}

/**
* Returns the single artifact at [relativePath] from the build directory of project [projectName], or `null` if no such
* artifact exists. Uses "build" as the build directory name by default.
*/
@JvmOverloads
public fun findArtifact(
projectName: String,
relativePath: String,
buildDirName: String = "build",
): BuildArtifact? {
val artifact = buildPathForName(path = projectName, buildDirName = buildDirName).resolve(relativePath)
return if (artifact.exists()) {
artifact.toBuildArtifact()
} else {
null
}
}

/**
* Returns the directory at [relativePath] from the build directory of project [projectName], failing if no such
* directory exists, or if it is not a directory. Returned [Path] may be empty. Uses "build" as the build directory name
* by default.
*/
@JvmOverloads
public fun artifacts(projectName: String, relativePath: String, buildDirName: String = "build"): BuildArtifact {
val dir = buildPathForName(path = projectName, buildDirName = buildDirName).resolve(relativePath)
check(dir.exists()) { "No directory with path '$dir'" }
check(Files.isDirectory(dir)) { "Expected directory, was '$dir'" }
return dir.toBuildArtifact()
}

private fun forName(projectName: String): Subproject {
if (projectName == ":") {
return rootProject
}

return subprojects.find { it.name == projectName }
?: throw IllegalStateException("No subproject with name $projectName")
return subprojects.find { it.name.ensurePrefix() == projectName.ensurePrefix() }
?: throw IllegalStateException("No subproject with name '$projectName'")
}

public class Builder @JvmOverloads constructor(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,7 @@ public open class Subproject(
public val variant: String,
) {

/**
* We only care about the subproject's name for equality comparisons and hashing.
*/
/** We only care about the subproject's name for equality comparisons and hashing. */
override fun equals(other: Any?): Boolean {
if (this === other) return true
if (other !is Subproject) return false
Expand All @@ -34,9 +32,7 @@ public open class Subproject(
return true
}

/**
* We only care about the subproject's name for equality comparisons and hashing.
*/
/** We only care about the subproject's name for equality comparisons and hashing. */
override fun hashCode(): Int = name.hashCode()

public class Builder {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Copyright (c) 2024. Tony Robalik.
// SPDX-License-Identifier: Apache-2.0
package com.autonomousapps.kit.artifacts

import java.io.File
import java.nio.file.Path
import kotlin.io.path.*

/**
* Essentially a wrapper around [path], with the intention to provide an expanded API eventually.
*/
public class BuildArtifact(private val path: Path) {

/**
* The [Path] represented by this build artifact.
*/
public val asPath: Path get() = path

/**
* The [File] represented by this build artifact.
*/
public val asFile: File get() = path.toFile()

public fun exists(): Boolean = path.exists()
public fun notExists(): Boolean = path.notExists()
public fun isRegularFile(): Boolean = path.isRegularFile()
public fun isDirectory(): Boolean = path.isDirectory()
public fun isSymbolicLink(): Boolean = path.isSymbolicLink()

public val extension: String get() = path.extension
}

internal fun Path.toBuildArtifact(): BuildArtifact = BuildArtifact(this)
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) 2024. Tony Robalik.
// SPDX-License-Identifier: Apache-2.0
package com.autonomousapps.kit.artifacts

import java.nio.file.LinkOption
import java.nio.file.Path
import kotlin.io.path.isDirectory
import kotlin.io.path.isRegularFile
import kotlin.io.path.isSymbolicLink

public enum class FileType(public val humanReadableName: String) {
REGULAR_FILE("regular file"),
DIRECTORY("directory"),
SYMLINK("symbolic link"),
UNKNOWN("unknown");

public companion object {
public fun from(buildArtifact: BuildArtifact, vararg options: LinkOption): FileType {
return from(buildArtifact.asPath, *options)
}

public fun from(path: Path, vararg options: LinkOption): FileType = when {
path.isRegularFile(*options) -> REGULAR_FILE
path.isDirectory(*options) -> DIRECTORY
path.isSymbolicLink() -> SYMLINK
else -> UNKNOWN
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
package com.autonomousapps.kit.gradle

import com.autonomousapps.kit.GradleProject
import com.autonomousapps.kit.gradle.android.AndroidBlock
import com.autonomousapps.kit.render.Scribe
import org.intellij.lang.annotations.Language
Expand All @@ -19,6 +20,8 @@ public class BuildScript(
public val java: Java? = null,
public val kotlin: Kotlin? = null,
public val additions: String = "",
private val usesGroovy: Boolean = false,
private val usesKotlin: Boolean = false,
) {

private val groupVersion = GroupVersion(group = group, version = version)
Expand Down Expand Up @@ -59,6 +62,14 @@ public class BuildScript(
kotlin?.let { k -> appendLine(scribe.use { s -> k.render(s) }) }

if (additions.isNotBlank()) {
if (usesGroovy && scribe.dslKind != GradleProject.DslKind.GROOVY) {
error("You called withGroovy() but you're using Kotlin DSL")
}

if (usesKotlin && scribe.dslKind != GradleProject.DslKind.KOTLIN) {
error("You called withKotlin() but you're using Groovy DSL")
}

appendLine(additions)
}

Expand All @@ -80,8 +91,17 @@ public class BuildScript(
public var kotlin: Kotlin? = null
public var additions: String = ""

private var usesGroovy = false
private var usesKotlin = false

public fun withGroovy(@Language("Groovy") script: String) {
additions = script.trimIndent()
usesGroovy = true
}

public fun withKotlin(@Language("kt") script: String) {
additions = script.trimIndent()
usesKotlin = true
}

public fun dependencies(vararg dependencies: Dependency) {
Expand Down Expand Up @@ -120,7 +140,9 @@ public class BuildScript(
dependencies = Dependencies(dependencies),
java = java,
kotlin = kotlin,
additions = additions
additions = additions,
usesGroovy = usesGroovy,
usesKotlin = usesKotlin,
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
package com.autonomousapps.kit.gradle

import com.autonomousapps.kit.GradleProject.DslKind
import com.autonomousapps.kit.internal.ensurePrefix
import com.autonomousapps.kit.render.Element
import com.autonomousapps.kit.render.Scribe

Expand Down Expand Up @@ -182,8 +183,6 @@ public class Dependency @JvmOverloads constructor(
return Dependency(configuration, path.ensurePrefix(), capability = capability)
}

private fun String.ensurePrefix(prefix: String = ":"): String = if (startsWith(prefix)) this else "$prefix$this"

@JvmStatic
public fun raw(configuration: String, dependency: String): Dependency {
check(!dependency.contains(":")) { "Not meant for normal dependencies. Was '$dependency'." }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ package com.autonomousapps.kit.internal
import java.io.File
import java.nio.charset.Charset

internal fun File.writeAny(any: Any, charset: Charset = Charsets.UTF_8): Unit {
internal fun File.writeAny(any: Any, charset: Charset = Charsets.UTF_8) {
writeBytes(any.toString().toByteArray(charset))
}
Loading
Loading