Bind PackageServer to ephemeral port to avoid port conflicts (#227)

This is a comprehensive solution to the "flaky PackageServer tests"
problem. It rules out port conflicts and imposes no limits on test
parallelism. The same solution can be used for other test servers
in the future.

Major changes:
- Turn `PackageServer` from a singleton into a class that is
  instantiated per test class or test method.
- Start the server the first time its `port` property is read.
  Bind the server to an ephemeral port instead of port 12110.
- For every test that uses `PackageServer`, pass the server port to
  `--test-port`, `HttpClient.Builder.setTestPort`, the `CliBaseOptions`
  or `ExecutorOptions` constructor, or the Gradle plugin's `testPort` property.
  Wire all of these to `RequestRewritingClient`'s `testPort` constructor parameter.
- Enhance `RequestRewritingClient` to replace port 12110 with `testPort`
  in request URIs unless `testPort` is -1 (its default).
- Introduce `ExecutorOptions.Builder`.
  This makes executor options more comfortable to create
  and allows to hide options such as `testPort`.
- Deprecate the `ExecutorOptions` constructor to steer users towards the builder.
- Get rid of `ExecutorOptions2`, which is no longer needed.
- Clean up `EmbeddedExecutorTest` with the help of the builder.
This commit is contained in:
translatenix
2024-03-13 10:40:55 -07:00
committed by GitHub
parent 1e608b2aae
commit 014b3a8816
26 changed files with 756 additions and 581 deletions
@@ -4,25 +4,30 @@ import org.pkl.commons.test.PackageServer
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.Disabled
import org.junit.jupiter.api.Test
import org.pkl.core.http.HttpClient
class StackFrameTransformersTest {
// TODO figure out how to test this; right now this fails because there is no VM context.
@Test
@Disabled
fun replacePackageUriWithSourceCodeUrl() {
PackageServer.ensureStarted()
EvaluatorBuilder.preconfigured().build().use {
val frame = StackFrame(
"package://localhost:12110/birds@0.5.0#/Bird.pkl",
null,
listOf(),
1,
1,
2,
2)
val transformed =
StackFrameTransformers.replacePackageUriWithSourceCodeUrl.apply(frame)
assertThat(transformed.moduleUri).isEqualTo("https://example.com/birds/v0.5.0/blob/Bird.pkl#L1-L2")
PackageServer().use { server ->
val httpClient = HttpClient.builder().setTestPort(server.port).build()
EvaluatorBuilder.preconfigured()
.setHttpClient(httpClient)
.build().use {
val frame = StackFrame(
"package://localhost:12110/birds@0.5.0#/Bird.pkl",
null,
listOf(),
1,
1,
2,
2)
val transformed =
StackFrameTransformers.replacePackageUriWithSourceCodeUrl.apply(frame)
assertThat(transformed.moduleUri).isEqualTo("https://example.com/birds/v0.5.0/blob/Bird.pkl#L1-L2")
}
}
}
}
@@ -6,14 +6,13 @@ import org.junit.jupiter.api.Test
import java.net.URI
import java.net.http.HttpRequest
import java.net.http.HttpRequest.BodyPublishers
import java.net.http.HttpResponse
import java.net.http.HttpResponse.BodyHandlers
import java.time.Duration
import java.net.http.HttpClient as JdkHttpClient
class RequestRewritingClientTest {
private val captured = RequestCapturingClient()
private val client = RequestRewritingClient("Pkl", Duration.ofSeconds(42), captured)
private val client = RequestRewritingClient("Pkl", Duration.ofSeconds(42), -1, captured)
private val exampleUri = URI("https://example.com/foo/bar.html")
private val exampleRequest = HttpRequest.newBuilder(exampleUri).build()
@@ -103,6 +102,26 @@ class RequestRewritingClientTest {
assertThat(captured.request.bodyPublisher().get()).isSameAs(publisher)
}
@Test
fun `rewrites port 12110 if test port is set`() {
val captured = RequestCapturingClient()
val client = RequestRewritingClient("Pkl", Duration.ofSeconds(42), 5000, captured)
val request = HttpRequest.newBuilder(URI("https://example.com:12110")).build()
client.send(request, BodyHandlers.discarding())
assertThat(captured.request.uri().port).isEqualTo(5000)
}
@Test
fun `leaves port 12110 intact if no test port is set`() {
val request = HttpRequest.newBuilder(URI("https://example.com:12110")).build()
client.send(request, BodyHandlers.discarding())
assertThat(captured.request.uri().port).isEqualTo(12110)
}
}
@@ -6,8 +6,6 @@ import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.junit.jupiter.api.parallel.Execution
import org.junit.jupiter.api.parallel.ExecutionMode
import org.pkl.commons.deleteRecursively
import org.pkl.commons.readString
import org.pkl.commons.test.FileTestUtils
@@ -23,7 +21,6 @@ import kotlin.io.path.exists
import kotlin.io.path.readBytes
class PackageResolversTest {
@Execution(ExecutionMode.SAME_THREAD)
abstract class AbstractPackageResolverTest {
abstract val resolver: PackageResolver
@@ -31,15 +28,20 @@ class PackageResolversTest {
private val packageRoot = FileTestUtils.rootProjectDir.resolve("pkl-commons-test/src/main/files/packages")
companion object {
private val packageServer = PackageServer()
@JvmStatic
@BeforeAll
fun beforeAll() {
PackageServer.ensureStarted()
@AfterAll
fun afterAll() {
packageServer.close()
}
val httpClient: HttpClient = HttpClient.builder()
.addCertificates(FileTestUtils.selfSignedCertificate)
.build()
val httpClient: HttpClient by lazy {
HttpClient.builder()
.addCertificates(FileTestUtils.selfSignedCertificate)
.setTestPort(packageServer.port)
.build()
}
}
@Test
@@ -199,7 +201,6 @@ class PackageResolversTest {
@BeforeAll
@JvmStatic
fun beforeAll() {
PackageServer.ensureStarted()
cacheDir.deleteRecursively()
}
}
@@ -1,7 +1,7 @@
package org.pkl.core.project
import org.assertj.core.api.Assertions.assertThat
import org.junit.jupiter.api.BeforeAll
import org.junit.jupiter.api.AfterAll
import org.junit.jupiter.api.Test
import org.junit.jupiter.api.assertThrows
import org.pkl.commons.test.FileTestUtils
@@ -16,15 +16,20 @@ import java.nio.file.Path
class ProjectDependenciesResolverTest {
companion object {
private val packageServer = PackageServer()
@JvmStatic
@BeforeAll
fun beforeAll() {
PackageServer.ensureStarted()
@AfterAll
fun afterAll() {
packageServer.close()
}
val httpClient: HttpClient = HttpClient.builder()
.addCertificates(FileTestUtils.selfSignedCertificate)
.build()
val httpClient: HttpClient by lazy {
HttpClient.builder()
.addCertificates(FileTestUtils.selfSignedCertificate)
.setTestPort(packageServer.port)
.build()
}
}
@Test
@@ -136,19 +136,20 @@ class ProjectTest {
@Test
fun `evaluate project module -- invalid checksum`() {
PackageServer.ensureStarted()
val projectDir = Path.of(javaClass.getResource("badProjectChecksum2/")!!.path)
val project = Project.loadFromPath(projectDir.resolve("PklProject"))
val httpClient = HttpClient.builder()
.addCertificates(FileTestUtils.selfSignedCertificate)
.build()
val evaluator = EvaluatorBuilder.preconfigured()
.applyFromProject(project)
.setModuleCacheDir(null)
.setHttpClient(httpClient)
.build()
assertThatCode { evaluator.evaluate(ModuleSource.path(projectDir.resolve("bug.pkl"))) }
.hasMessageStartingWith("""
PackageServer().use { server ->
val projectDir = Path.of(javaClass.getResource("badProjectChecksum2/")!!.path)
val project = Project.loadFromPath(projectDir.resolve("PklProject"))
val httpClient = HttpClient.builder()
.addCertificates(FileTestUtils.selfSignedCertificate)
.setTestPort(server.port)
.build()
val evaluator = EvaluatorBuilder.preconfigured()
.applyFromProject(project)
.setModuleCacheDir(null)
.setHttpClient(httpClient)
.build()
assertThatCode { evaluator.evaluate(ModuleSource.path(projectDir.resolve("bug.pkl"))) }
.hasMessageStartingWith("""
–– Pkl Error ––
Cannot download package `package://localhost:12110/fruit@1.0.5` because the computed checksum for package metadata does not match the expected checksum.
@@ -159,5 +160,6 @@ class ProjectTest {
1 | import "@fruit/Fruit.pkl"
^^^^^^^^^^^^^^^^^^^^^^^^^
""".trimIndent())
}
}
}
@@ -39,4 +39,22 @@ class HttpUtilsTest {
HttpUtils.checkHasStatusCode200(response2)
}
}
@Test
fun setPort() {
assertThrows<IllegalArgumentException> {
HttpUtils.setPort(URI("https://example.com"), -1)
}
assertThrows<IllegalArgumentException> {
HttpUtils.setPort(URI("https://example.com"), 65536)
}
assertThat(HttpUtils.setPort(URI("http://example.com"), 123))
.isEqualTo(URI("http://example.com:123"))
assertThat(HttpUtils.setPort(URI("http://example.com:456"), 123))
.isEqualTo(URI("http://example.com:123"))
assertThat(HttpUtils.setPort(URI("https://example.com/foo/bar.baz?query=1#fragment"), 123))
.isEqualTo(URI("https://example.com:123/foo/bar.baz?query=1#fragment"))
assertThat(HttpUtils.setPort(URI("https://example.com:456/foo/bar.baz?query=1#fragment"), 123))
.isEqualTo(URI("https://example.com:123/foo/bar.baz?query=1#fragment"))
}
}