Skip to content

Commit 7ad783b

Browse files
authored
Add missing tests (#12)
1 parent 10179d2 commit 7ad783b

28 files changed

+1060
-135
lines changed

build.gradle.kts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,12 @@ publishing {
110110
}
111111
}
112112

113+
testing {
114+
suites.withType<JvmTestSuite> {
115+
useKotlinTest()
116+
}
117+
}
118+
113119
dependencies {
114120
api("com.squareup.moshi:moshi:1.14.0")
115121
implementation("com.squareup.moshi:moshi-kotlin:1.14.0")
@@ -118,4 +124,7 @@ dependencies {
118124
implementation("com.squareup.retrofit2:converter-moshi:2.9.0")
119125
implementation("com.squareup.retrofit2:converter-scalars:2.9.0")
120126
api("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4")
127+
testImplementation("com.squareup.okhttp3:mockwebserver:4.10.0")
128+
testImplementation("com.squareup.okio:okio:3.3.0")
129+
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
121130
}

src/main/kotlin/com/gabrielfeo/gradle/enterprise/api/ApiExtensions.kt renamed to src/main/kotlin/com/gabrielfeo/gradle/enterprise/api/GradleEnterpriseApiExtensions.kt

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
@file:Suppress("unused")
2+
13
package com.gabrielfeo.gradle.enterprise.api
24

35
import com.gabrielfeo.gradle.enterprise.api.internal.API_MAX_BUILDS
@@ -23,26 +25,30 @@ fun GradleEnterpriseApi.getBuildsFlow(
2325
sinceBuild: String? = null,
2426
fromInstant: Long? = null,
2527
fromBuild: String? = null,
26-
): Flow<Build> = flow {
27-
val firstBuilds = getBuilds(
28-
since = since,
29-
sinceBuild = sinceBuild,
30-
fromInstant = fromInstant,
31-
fromBuild = fromBuild,
32-
maxBuilds = API_MAX_BUILDS,
33-
)
34-
val pagedBuilds = firstBuilds.asFlow().pagedUntilLastBuild(maxPerRequest = API_MAX_BUILDS)
35-
emitAll(pagedBuilds)
28+
buildsPerPage: Int = API_MAX_BUILDS,
29+
): Flow<Build> {
30+
val api = this
31+
return flow {
32+
val firstBuilds = getBuilds(
33+
since = since,
34+
sinceBuild = sinceBuild,
35+
fromInstant = fromInstant,
36+
fromBuild = fromBuild,
37+
maxBuilds = buildsPerPage,
38+
)
39+
val pagedBuilds = firstBuilds.asFlow().pagedUntilLastBuild(api, buildsPerPage)
40+
emitAll(pagedBuilds)
41+
}
3642
}
3743

3844
/**
3945
* Gets [GradleAttributes] of all builds from a given date. Queries [GradleEnterpriseApi.getBuilds]
4046
* first, since it's the only endpoint providing a timeline of builds, then maps each to
4147
* [GradleEnterpriseApi.getGradleAttributes].
4248
*
43-
* Don't expect client-side filtering to be efficient. Does as many concurrent calls
44-
* as it can, requesting attributes in an eager coroutine, in [scope]. For other params,
45-
* see [getBuildsFlow] and [GradleEnterpriseApi.getBuilds].
49+
* Don't expect client-side filtering to be efficient. Will request up to [Int.MAX_VALUE]
50+
* builds and their attributes concurrently and eagerly, with a buffer, in coroutines started in
51+
* [scope]. For other params, see [getBuildsFlow] and [GradleEnterpriseApi.getBuilds].
4652
*
4753
* @param scope CoroutineScope in which to create coroutines. Defaults to [GlobalScope].
4854
*/
@@ -59,6 +65,6 @@ fun GradleEnterpriseApi.getGradleAttributesFlow(
5965
sinceBuild = sinceBuild,
6066
fromInstant = fromInstant,
6167
fromBuild = fromBuild
62-
).withGradleAttributes(scope).map { (_, attrs) ->
68+
).withGradleAttributes(scope, api = this).map { (_, attrs) ->
6369
attrs
6470
}

src/main/kotlin/com/gabrielfeo/gradle/enterprise/api/Options.kt

Lines changed: 51 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,67 @@
1-
@file:Suppress("MemberVisibilityCanBePrivate")
1+
@file:Suppress("MemberVisibilityCanBePrivate", "unused")
22

33
package com.gabrielfeo.gradle.enterprise.api
44

5-
import com.gabrielfeo.gradle.enterprise.api.Options.Cache.clear
6-
import com.gabrielfeo.gradle.enterprise.api.Options.Cache.longTermCacheUrlPattern
7-
import com.gabrielfeo.gradle.enterprise.api.Options.Cache.shortTermCacheUrlPattern
8-
import com.gabrielfeo.gradle.enterprise.api.internal.buildCache
9-
import com.gabrielfeo.gradle.enterprise.api.internal.requireBaseUrl
10-
import com.gabrielfeo.gradle.enterprise.api.internal.requireToken
5+
import com.gabrielfeo.gradle.enterprise.api.internal.*
116
import java.io.File
127
import kotlin.time.Duration.Companion.days
138

9+
val options = Options(env = RealEnv, keychain = RealKeychain(RealEnv))
10+
1411
/**
1512
* Library configuration options. Should not be changed after accessing the [api] object for the
1613
* first time.
14+
*
15+
* Use the global [options] instance.
1716
*/
18-
object Options {
17+
class Options internal constructor(
18+
env: Env,
19+
keychain: Keychain,
20+
) {
21+
22+
val gradleEnterpriseInstance = GradleEnterpriseInstanceOptions(env, keychain)
23+
val concurrency = ConcurrencyOptions(env)
24+
val cache = CacheOptions(env)
25+
val debugging = DebuggingOptions(env)
1926

2027
/**
2128
* Options about the GE instance, such as URL and API token.
29+
*
30+
* Access via the global [options] instance.
2231
*/
23-
object GradleEnterpriseInstance {
32+
class GradleEnterpriseInstanceOptions internal constructor(
33+
private val env: Env,
34+
private val keychain: Keychain,
35+
) {
2436

2537
/**
2638
* Provides the URL of a Gradle Enterprise API instance (without `/api`). By default, uses
2739
* environment variable `GRADLE_ENTERPRISE_URL`.
2840
*/
2941
var url: () -> String = {
30-
requireBaseUrl(envName = "GRADLE_ENTERPRISE_URL")
42+
env["GRADLE_ENTERPRISE_URL"]
43+
?: error("GE instance URL is required")
3144
}
3245

3346
/**
3447
* Provides the access token for a Gradle Enterprise API instance. By default, uses keychain entry
3548
* `gradle-enterprise-api-token` or environment variable `GRADLE_ENTERPRISE_URL`.
3649
*/
3750
var token: () -> String = {
38-
requireToken(
39-
keychainName = "gradle-enterprise-api-token",
40-
envName = "GRADLE_ENTERPRISE_API_TOKEN",
41-
)
51+
keychain["gradle-enterprise-api-token"]
52+
?: env["GRADLE_ENTERPRISE_API_TOKEN"]
53+
?: error("GE token is required")
4254
}
4355
}
4456

4557
/**
4658
* Concurrency options.
59+
*
60+
* Access via the global [options] instance.
4761
*/
48-
object Concurrency {
62+
class ConcurrencyOptions internal constructor(
63+
env: Env,
64+
) {
4965

5066
/**
5167
* Maximum amount of concurrent requests allowed. Further requests will be queued. By default,
@@ -54,7 +70,7 @@ object Options {
5470
* https://square.github.io/okhttp/4.x/okhttp/okhttp3/-dispatcher
5571
*/
5672
var maxConcurrentRequests =
57-
System.getenv("GRADLE_ENTERPRISE_API_MAX_CONCURRENT_REQUESTS")?.toInt()
73+
env["GRADLE_ENTERPRISE_API_MAX_CONCURRENT_REQUESTS"]?.toInt()
5874
?: 15
5975
}
6076

@@ -63,6 +79,8 @@ object Options {
6379
* API disallows HTTP caching, but this library forcefully enables it by overwriting
6480
* cache-related headers in API responses. Enable with [cacheEnabled].
6581
*
82+
* Access via the global [options] instance.
83+
*
6684
* Responses can be:
6785
*
6886
* - cached short-term: default max-age of 1 day
@@ -97,36 +115,38 @@ object Options {
97115
* endpoints such as `/api/build/{id}/gradle-attributes`. Thus, whenever the GE version
98116
* itself is upgraded, cache should be [clear]ed.
99117
*/
100-
object Cache {
118+
class CacheOptions internal constructor(
119+
env: Env,
120+
) {
101121

102122
/**
103123
* Whether caching is enabled. By default, uses environment variable
104124
* `GRADLE_ENTERPRISE_API_CACHE_ENABLED` or `false`.
105125
*/
106126
var cacheEnabled: Boolean =
107-
System.getenv("GRADLE_ENTERPRISE_API_CACHE_ENABLED").toBoolean()
127+
env["GRADLE_ENTERPRISE_API_CACHE_ENABLED"].toBoolean()
108128

109129
/**
110130
* Clears [cacheDir] including files that weren't created by the cache.
111131
*/
112132
fun clear() {
113-
buildCache().delete()
133+
buildCache(options).delete()
114134
}
115135

116136
/**
117137
* HTTP cache location. By default, uses environment variable `GRADLE_ENTERPRISE_API_CACHE_DIR`
118138
* or the system temporary folder (`java.io.tmpdir` / gradle-enterprise-api-kotlin-cache).
119139
*/
120140
var cacheDir =
121-
System.getenv("GRADLE_ENTERPRISE_API_CACHE_DIR")?.let(::File)
141+
env["GRADLE_ENTERPRISE_API_CACHE_DIR"]?.let(::File)
122142
?: File(System.getProperty("java.io.tmpdir"), "gradle-enterprise-api-kotlin-cache")
123143

124144
/**
125145
* Max size of the HTTP cache. By default, uses environment variable
126146
* `GRADLE_ENTERPRISE_API_MAX_CACHE_SIZE` or ~1 GB.
127147
*/
128148
var maxCacheSize =
129-
System.getenv("GRADLE_ENTERPRISE_API_MAX_CACHE_SIZE")?.toLong()
149+
env["GRADLE_ENTERPRISE_API_MAX_CACHE_SIZE"]?.toLong()
130150
?: 1_000_000_000L
131151

132152
/**
@@ -139,15 +159,15 @@ object Options {
139159
* Use `|` to define multiple patterns in one, e.g. `.*gradle-attributes|.*test-distribution`.
140160
*/
141161
var longTermCacheUrlPattern: Regex =
142-
System.getenv("GRADLE_ENTERPRISE_API_LONG_TERM_CACHE_URL_PATTERN")?.toRegex()
162+
env["GRADLE_ENTERPRISE_API_LONG_TERM_CACHE_URL_PATTERN"]?.toRegex()
143163
?: """.*/api/builds/[\d\w]+/(?:gradle|maven)-attributes""".toRegex()
144164

145165
/**
146166
* Max age in seconds for URLs to be cached long-term (matched by [longTermCacheUrlPattern]).
147167
* By default, uses environment variable `GRADLE_ENTERPRISE_API_LONG_TERM_CACHE_MAX_AGE` or 1 year.
148168
*/
149169
var longTermCacheMaxAge: Long =
150-
System.getenv("GRADLE_ENTERPRISE_API_SHORT_TERM_CACHE_MAX_AGE")?.toLong()
170+
env["GRADLE_ENTERPRISE_API_SHORT_TERM_CACHE_MAX_AGE"]?.toLong()
151171
?: 365.days.inWholeSeconds
152172

153173
/**
@@ -159,28 +179,32 @@ object Options {
159179
* Use `|` to define multiple patterns in one, e.g. `.*gradle-attributes|.*test-distribution`.
160180
*/
161181
var shortTermCacheUrlPattern: Regex =
162-
System.getenv("GRADLE_ENTERPRISE_API_SHORT_TERM_CACHE_URL_PATTERN")?.toRegex()
182+
env["GRADLE_ENTERPRISE_API_SHORT_TERM_CACHE_URL_PATTERN"]?.toRegex()
163183
?: """.*/builds(?:\?.*|\Z)""".toRegex()
164184

165185
/**
166186
* Max age in seconds for URLs to be cached short-term (matched by [shortTermCacheUrlPattern]).
167187
* By default, uses environment variable `GRADLE_ENTERPRISE_API_SHORT_TERM_CACHE_MAX_AGE` or 1 day.
168188
*/
169189
var shortTermCacheMaxAge: Long =
170-
System.getenv("GRADLE_ENTERPRISE_API_SHORT_TERM_CACHE_MAX_AGE")?.toLong()
190+
env["GRADLE_ENTERPRISE_API_SHORT_TERM_CACHE_MAX_AGE"]?.toLong()
171191
?: 1.days.inWholeSeconds
172192
}
173193

174194
/**
175195
* Library debugging options.
196+
*
197+
* Access via the global [options] instance.
176198
*/
177-
object Debugging {
199+
class DebuggingOptions internal constructor(
200+
env: Env,
201+
) {
178202

179203
/**
180204
* Enables debug logging from the library. All logging is output to stderr. By default, uses
181205
* environment variable `GRADLE_ENTERPRISE_API_DEBUG_LOGGING` or `false`.
182206
*/
183207
var debugLoggingEnabled =
184-
System.getenv("GRADLE_ENTERPRISE_API_DEBUG_LOGGING").toBoolean()
208+
env["GRADLE_ENTERPRISE_API_DEBUG_LOGGING"].toBoolean()
185209
}
186210
}

src/main/kotlin/com/gabrielfeo/gradle/enterprise/api/internal/Authentication.kt

Lines changed: 0 additions & 43 deletions
This file was deleted.

src/main/kotlin/com/gabrielfeo/gradle/enterprise/api/internal/BaseUrl.kt

Lines changed: 0 additions & 13 deletions
This file was deleted.
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package com.gabrielfeo.gradle.enterprise.api.internal
2+
3+
interface Env {
4+
operator fun get(name: String): String?
5+
}
6+
7+
object RealEnv : Env {
8+
override fun get(name: String): String? = System.getenv(name)
9+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.gabrielfeo.gradle.enterprise.api.internal
2+
3+
import com.gabrielfeo.gradle.enterprise.api.options
4+
import java.util.logging.Level
5+
import java.util.logging.Logger
6+
7+
interface Keychain {
8+
operator fun get(entry: String): String?
9+
}
10+
11+
class RealKeychain(
12+
private val env: Env,
13+
) : Keychain {
14+
override fun get(entry: String): String? {
15+
val login = env["LOGNAME"]
16+
val process = ProcessBuilder(
17+
"security", "find-generic-password", "-w", "-a", login, "-s", entry
18+
).start()
19+
val status = process.waitFor()
20+
if (status == 0) {
21+
return process.inputStream.bufferedReader().use {
22+
it.readText().trim()
23+
}
24+
} else if (options.debugging.debugLoggingEnabled) {
25+
Logger.getGlobal().log(Level.INFO, "Failed to get key from keychain (exit $status)")
26+
}
27+
return null
28+
}
29+
}

0 commit comments

Comments
 (0)