diff --git a/.setup_protoscope.sh b/.setup_protoscope.sh new file mode 100644 index 000000000..6a9d3588f --- /dev/null +++ b/.setup_protoscope.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# +# Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. +# + +echo "Installing protoscope" + +brew install go +go install github.com/protocolbuffers/protoscope/cmd/protoscope...@latest + +PROTOSCOPE_PATH=~/go/bin/protoscope +if [ -f "$PROTOSCOPE_PATH" ]; then + if grep -q "protoscope_path=" local.properties; then + sed -i '' "s|protoscope_path=.*|protoscope_path=$PROTOSCOPE_PATH|" local.properties + else + echo "protoscope_path=$PROTOSCOPE_PATH" >> local.properties + fi +else + echo "Error: protoscope not found at $PROTOSCOPE_PATH" + exit 1 +fi diff --git a/docs/environment.md b/docs/environment.md index b2e4e3c8e..9243da1c0 100644 --- a/docs/environment.md +++ b/docs/environment.md @@ -363,6 +363,39 @@ TODO: write a guide about kRPC API check tests. Use `./publishLocal.sh` script. All artifacts will be in the local directory of `/build/repo/` . +## How to debug tests/protobuf-conformance + +Prerequisite (Only macOS for now): +```bash +./setup_protoscope.sh +``` +It will install the `protoscope` utility: https://github.com/protocolbuffers/protoscope + +Now you can run tests: +```bash +gradle :tests:protobuf-conformance:runConformanceTest -Pconformance.test='' +``` + +In the [manual](../tests/protobuf-conformance/build/protobuf-conformance/manual) directory +you will find files: +- dump_conformance_input.bin.txt - decoded ConformanceRequest protobuf message +- dump_conformance_output.bin.txt - decoded ConformanceResponse protobuf message +- dump_payload_input.bin.txt - decoded ConformanceRequest.payload (if protobuf) +- dump_payload_output.bin.txt - decoded ConformanceResponse.result (if protobuf) + +IMPORTANT: `protoscope` only works with proto3 and not 'editions' messages. +For proto2 and editions this won't work. + +To debug tests, +use [ConformanceClient.kt](../tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/ConformanceClient.kt) +and this command: +```bash +gradle :tests:protobuf-conformance:runConformanceTest -Pconformance.test.debug='true' -Pconformance.test='' +``` + +Then use IntelliJ 'Attach to Process' feature to attach to the process on port 5005. +(kill the process if port is already in use: `kill $(lsof -t -i:5005)`) + ## Troubleshooting Nothing works? Well, you are onto a journey! diff --git a/gradle-conventions/src/main/kotlin/util/other/localProperties.kt b/gradle-conventions/src/main/kotlin/util/other/localProperties.kt new file mode 100644 index 000000000..1c41fdf92 --- /dev/null +++ b/gradle-conventions/src/main/kotlin/util/other/localProperties.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package util.other + +import org.gradle.api.Project +import org.gradle.internal.extensions.core.extra +import org.gradle.kotlin.dsl.provideDelegate +import java.util.Properties +import java.util.concurrent.atomic.AtomicReference +import kotlin.io.path.Path +import kotlin.io.path.inputStream + +private val ref = AtomicReference() + +fun Project.localProperties(): Properties { + if (ref.get() == null) { + ref.compareAndSet(null, Properties().apply { + val globalRootDir: String by extra + + load(Path(globalRootDir, "local.properties").inputStream()) + }) + } + + return ref.get() +} diff --git a/gradle-conventions/src/main/kotlin/util/tasks/protobufConformanceUpdate.kt b/gradle-conventions/src/main/kotlin/util/tasks/protobufConformanceUpdate.kt index 8679e22b3..0ca9dba10 100644 --- a/gradle-conventions/src/main/kotlin/util/tasks/protobufConformanceUpdate.kt +++ b/gradle-conventions/src/main/kotlin/util/tasks/protobufConformanceUpdate.kt @@ -6,10 +6,14 @@ package util.tasks import org.gradle.api.DefaultTask import org.gradle.api.Project +import org.gradle.api.file.FileTree +import org.gradle.api.provider.ListProperty import org.gradle.api.provider.Property import org.gradle.api.tasks.Copy +import org.gradle.api.tasks.Exec import org.gradle.api.tasks.Input import org.gradle.api.tasks.InputFile +import org.gradle.api.tasks.InputFiles import org.gradle.api.tasks.OutputFile import org.gradle.api.tasks.TaskAction import org.gradle.kotlin.dsl.accessors.runtime.addExternalModuleDependencyTo @@ -21,8 +25,40 @@ import util.other.libs import java.io.File const val CONFORMANCE_TEST_RUNNER_CONFIGURATION = "conformanceTestRunner" +const val PROTOC_TESTING_CONFIGURATION = "protoc_internalTesting" const val UNZIP_PROTOBUF_CONFORMANCE_TASK = "unzipProtobufConformance" const val WRITE_CONFORMANCE_EXECUTABLE_PATH_TASK = "writeConformanceExecutablePath" +const val PAYLOAD_PB = "payload.pb" +const val CONFORMANCE_PB = "conformance.pb" + +private fun Project.getBinFrom(configuration: String): File { + return configurations.getByName(configuration).map { + zipTree(it).matching { include("bin/**") }.files.first() + }.single() +} + +private fun Project.getIncludeFrom(configuration: String): List { + return configurations.getByName(configuration).map { + zipTree(it).matching { include("include/**") } + } +} + +private fun List.commonPrefix(): String { + return fold(first()) { acc, s -> acc.commonPrefixWith(s) } +} + +private fun Project.pbFile(name: String): File { + return layout.buildDirectory.get() + .dir("protobuf-conformance") + .file(name) + .asFile + .apply { + if (!exists()) { + parentFile.mkdirs() + createNewFile() + } + } +} abstract class ConformanceExecutablePathWriter : DefaultTask() { @get:Input @@ -64,6 +100,48 @@ abstract class ConformanceExecutablePathWriter : DefaultTask() { } } +abstract class GenerateConformanceFileDescriptorSet : Exec() { + @get:InputFiles + abstract val wktFilesCollection: ListProperty + + @get:InputFiles + abstract val conformanceFilesCollection: ListProperty + + @get:InputFile + abstract val bin: Property + + @get:OutputFile + abstract val outputFile: Property + + @TaskAction + fun generate() { + val wktFiles = wktFilesCollection.get().map { it.absolutePath } + val conformanceFiles = conformanceFilesCollection.get().map { it.absolutePath } + + val wktProtoPath = if (wktFiles.isEmpty()) { + emptyList() + } else { + listOf("--proto_path=${wktFiles.commonPrefix().substringBefore("/google/protobuf/")}") + } + + val conformanceIncludeDir = conformanceFiles + .commonPrefix() + .substringBefore("/google/protobuf/") + .substringBefore("/conformance/") + + val conformanceProtoPath = "--proto_path=$conformanceIncludeDir" + + commandLine( + bin.get().absolutePath, + *wktProtoPath.toTypedArray(), + conformanceProtoPath, + "-o", outputFile.get().absolutePath, + *wktFiles.toTypedArray(), + *conformanceFiles.toTypedArray(), + ) + } +} + fun Project.setupProtobufConformanceResources() { val os = System.getProperty("os.name").lowercase() val osPart = when { @@ -79,7 +157,7 @@ fun Project.setupProtobufConformanceResources() { // https://stackoverflow.com/questions/23023069/gradle-download-and-unzip-file-from-url repositories.ivy { - name = "protobuf-conformance-github" + name = "github" url = uri("https://github.com") patternLayout { @@ -93,6 +171,7 @@ fun Project.setupProtobufConformanceResources() { } configurations.create(CONFORMANCE_TEST_RUNNER_CONFIGURATION) + configurations.create(PROTOC_TESTING_CONFIGURATION) // https://docs.gradle.org/current/javadoc/org/gradle/api/artifacts/dsl/DependencyHandler.html dependencies { @@ -115,10 +194,28 @@ fun Project.setupProtobufConformanceResources() { } } + dependencies { + addExternalModuleDependencyTo( + this, + PROTOC_TESTING_CONFIGURATION, + group = "protocolbuffers", + name = "protobuf", + version = libs.versions.protobuf.asProvider().get().substringAfter("."), + classifier = null, + ext = null, + configuration = null, + ) { + artifact { + name = "protoc" + type = "zip" + extension = "zip" + classifier = "$osPart-$archPart" + } + } + } + val unzipProtobufConformance = tasks.register(UNZIP_PROTOBUF_CONFORMANCE_TASK) { - from(configurations.getByName(CONFORMANCE_TEST_RUNNER_CONFIGURATION).map { - zipTree(it).matching { include("include/**") } - }) + from(getIncludeFrom(CONFORMANCE_TEST_RUNNER_CONFIGURATION)) val destDir = project.layout.projectDirectory .dir("src") @@ -140,6 +237,34 @@ fun Project.setupProtobufConformanceResources() { } } + tasks.register("generateConformanceFileDescriptorSet_conformance") { + wktFilesCollection.set(emptyList()) + + val conformanceFiles = project.getIncludeFrom(CONFORMANCE_TEST_RUNNER_CONFIGURATION).flatMap { it.files } + .filter { it.name == "conformance.proto" } + + conformanceFilesCollection.set(conformanceFiles) + + bin.set(getBinFrom(PROTOC_TESTING_CONFIGURATION)) + + outputFile.set(project.pbFile(CONFORMANCE_PB)) + } + + tasks.register("generateConformanceFileDescriptorSet_payload") { + val wktFiles = project.getIncludeFrom(PROTOC_TESTING_CONFIGURATION).flatMap { it.files } + wktFilesCollection.set(wktFiles) + + // editions are not supported in protoscope and proto2 fails + val conformanceFiles = project.getIncludeFrom(CONFORMANCE_TEST_RUNNER_CONFIGURATION).flatMap { it.files } + .filter { it.name == "test_messages_proto3.proto" } + + conformanceFilesCollection.set(conformanceFiles) + + bin.set(getBinFrom(PROTOC_TESTING_CONFIGURATION)) + + outputFile.set(project.pbFile(PAYLOAD_PB)) + } + val writeConformanceExecutablePath = tasks.register(WRITE_CONFORMANCE_EXECUTABLE_PATH_TASK) { outputDir.set( @@ -156,11 +281,7 @@ fun Project.setupProtobufConformanceResources() { .asFile ) - executable.set( - configurations.getByName(CONFORMANCE_TEST_RUNNER_CONFIGURATION).map { - zipTree(it).matching { include("bin/**") }.files.first() - }.single() - ) + executable.set(getBinFrom(CONFORMANCE_TEST_RUNNER_CONFIGURATION)) destination.set( project.layout.buildDirectory.get() diff --git a/tests/protobuf-conformance/build.gradle.kts b/tests/protobuf-conformance/build.gradle.kts index dfaf53c85..f4d723337 100644 --- a/tests/protobuf-conformance/build.gradle.kts +++ b/tests/protobuf-conformance/build.gradle.kts @@ -8,6 +8,10 @@ import kotlinx.rpc.buf.tasks.BufGenerateTask import kotlinx.rpc.internal.InternalRpcApi import kotlinx.rpc.internal.configureLocalProtocGenDevelopmentDependency import org.jetbrains.kotlin.gradle.dsl.ExplicitApiMode +import util.other.localProperties +import util.tasks.CONFORMANCE_PB +import util.tasks.GenerateConformanceFileDescriptorSet +import util.tasks.PAYLOAD_PB import util.tasks.setupProtobufConformanceResources plugins { @@ -86,6 +90,58 @@ val generateConformanceTests = tasks.register("generateConformanceTest mainClass.set("kotlinx.rpc.protoc.gen.test.GenerateConformanceTestsKt") } +val conformanceTest = properties.getOrDefault("conformance.test", "").toString() +val conformanceTestDebug = properties.getOrDefault("conformance.test.debug", "false").toString().toBooleanStrictOrNull() ?: false + +val generateConformanceFileDescriptorSet = tasks + .withType() + +tasks.register("runConformanceTest") { + classpath = sourceSets.main.get().runtimeClasspath + + dependsOn(mockClientJar) + dependsOn(tasks.named("bufGenerateMain")) + dependsOn(generateConformanceFileDescriptorSet) + + args = listOfNotNull( + mockClientJar.get().archiveFile.get().asFile.absolutePath, + conformanceTest, + if (conformanceTestDebug) "--debug" else null + ) + + mainClass.set("kotlinx.rpc.protoc.gen.test.RunConformanceTestKt") + + val protoscope = localProperties().getProperty("protoscope_path") + ?: throw GradleException("protoscope_path property is not set. Run ./setup_protoscope.sh") + + environment("PROTOSCOPE_PATH", protoscope) + + val pbFiles = generateConformanceFileDescriptorSet.map { + it.outputFile.get() + } + + environment( + "CONFORMANCE_PB_PATH", + pbFiles.single { it.name == CONFORMANCE_PB }.absolutePath + ) + environment( + "TEST_ALL_TYPES_PROTO3_PB_PATH", + pbFiles.single { it.name == PAYLOAD_PB }.absolutePath + ) + + doFirst { + if (!File(protoscope).exists()) { + throw GradleException( + """ + Protoscope is not found. Use the following command to install it: + + $ brew install go + $ go install github.com/protocolbuffers/protoscope/cmd/protoscope...@latest + """.trimIndent() + ) + } + } +} tasks.test { environment("MOCK_CLIENT_JAR", mockClientJar.get().archiveFile.get().asFile.absolutePath) diff --git a/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/ConformanceClient.kt b/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/ConformanceClient.kt index 8d4b67116..c01973057 100644 --- a/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/ConformanceClient.kt +++ b/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/ConformanceClient.kt @@ -17,6 +17,9 @@ import kotlinx.rpc.grpc.codec.MessageCodec import kotlinx.rpc.grpc.codec.WithCodec import kotlinx.rpc.protobuf.internal.InternalMessage import kotlinx.rpc.protobuf.internal.ProtobufException +import java.nio.file.Path +import kotlin.io.path.Path +import kotlin.io.path.writeBytes import kotlin.reflect.KClass // Adapted from @@ -211,7 +214,10 @@ internal class ConformanceClient { } } - private fun doTest(test: (ConformanceRequest) -> ConformanceResponse): Boolean { + private fun doTest( + dumpConfig: DumpConfig = DumpConfig.EMPTY, + test: (ConformanceRequest) -> ConformanceResponse, + ): Boolean { val bytes = readLittleEndianIntFromStdin() if (bytes == -1) { @@ -224,6 +230,8 @@ internal class ConformanceClient { throw RuntimeException("Unexpected EOF from test program.") } + dumpConfig.dumpConformanceInputFile?.writeBytes(serializedInput) + val request: ConformanceRequest = ConformanceRequestInternal.CODEC .decode(serializedInput.inputStream()) @@ -245,6 +253,8 @@ internal class ConformanceClient { val serializedOutput = ConformanceResponseInternal.CODEC .encode(response).readBytes() + dumpConfig.dumpConformanceOutputFile?.writeBytes(serializedOutput) + writeLittleEndianIntToStdout(serializedOutput.size) writeToStdout(serializedOutput) @@ -308,7 +318,7 @@ internal class ConformanceClient { -46, 41, 3, 97, 98, 99, -48, 41, 123, -46, 41, 3, 100, 101, 102, -48, 41, -56, 3 ) - fun runConformanceTest(): Boolean { + fun runConformanceTest(dumpConfig: DumpConfig): Boolean { // typeRegistry = // TypeRegistry.newBuilder() // .add(TestMessagesProto3.TestAllTypesProto3.getDescriptor()) @@ -318,11 +328,16 @@ internal class ConformanceClient { // ) // .build() - return doTest { request -> + return doTest(dumpConfig) { request -> val responseResult = runCatching { doTest(request) } + if (request.payload is ConformanceRequest.Payload.ProtobufPayload) { + dumpConfig.dumpPayloadInputFile + ?.writeBytes((request.payload as ConformanceRequest.Payload.ProtobufPayload).value) + } + responseResult.fold( onSuccess = { it }, onFailure = { @@ -338,18 +353,41 @@ internal class ConformanceClient { } } } - ) + ).apply { + if (result is ConformanceResponse.Result.ProtobufPayload) { + dumpConfig.dumpPayloadOutputFile + ?.writeBytes((result as ConformanceResponse.Result.ProtobufPayload).value) + } + } } } } +class DumpConfig( + val dumpConformanceInputFile: Path?, + val dumpConformanceOutputFile: Path?, + val dumpPayloadInputFile: Path?, + val dumpPayloadOutputFile: Path?, +) { + companion object { + val EMPTY = DumpConfig(null, null, null, null) + } +} + fun main(args: Array) { val client = ConformanceClient() - if (args.size != 1) { - error("Expected one argument: run mode (mock|conformance).") + if (args.isEmpty()) { + error("Expected at least one argument: run mode (mock|conformance).") } + val dumpConfig = DumpConfig( + getDumpFile("DUMP_CONFORMANCE_INPUT_FILE"), + getDumpFile("DUMP_CONFORMANCE_OUTPUT_FILE"), + getDumpFile("DUMP_PAYLOAD_INPUT_FILE"), + getDumpFile("DUMP_PAYLOAD_OUTPUT_FILE"), + ) + if (args[0] == "mock") { do { // test @@ -357,8 +395,17 @@ fun main(args: Array) { } else if (args[0] == "conformance") { do { // test - } while (client.runConformanceTest()) + } while (client.runConformanceTest(dumpConfig)) } else { error("Invalid run mode: ${args[0]}") } } + +private fun getDumpFile(propName: String): Path? { + val dumpFileProp = System.getenv(propName) + return if (dumpFileProp != null) { + Path(dumpFileProp) + } else { + null + } +} diff --git a/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/GenerateConformanceTests.kt b/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/GenerateConformanceTests.kt index 6a222b060..802ee6ff1 100644 --- a/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/GenerateConformanceTests.kt +++ b/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/GenerateConformanceTests.kt @@ -14,10 +14,10 @@ import kotlin.io.path.absolutePathString fun main(args: Array) { val jarPath = args[0] - val executable = getJavaClient(jarPath, "mock") - val outputDir = Path(CONFORMANCE_OUTPUT_DIR).resolve("mock") + val executable = getJavaClient(jarPath, "mock", outputDir) + val (failingTestsFile, textFormatFailingTestsFile) = createConformanceTestFiles(outputDir) execConformanceTestRunner( diff --git a/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/RunConformanceTest.kt b/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/RunConformanceTest.kt new file mode 100644 index 000000000..995413154 --- /dev/null +++ b/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/RunConformanceTest.kt @@ -0,0 +1,47 @@ +/* + * Copyright 2023-2025 JetBrains s.r.o and contributors. Use of this source code is governed by the Apache 2.0 license. + */ + +package kotlinx.rpc.protoc.gen.test + +import CONFORMANCE_OUTPUT_DIR +import kotlinx.rpc.protoc.gen.test.runner.createConformanceTestFiles +import kotlinx.rpc.protoc.gen.test.runner.execConformanceTestRunner +import kotlinx.rpc.protoc.gen.test.runner.getJavaClient +import kotlin.io.path.Path +import kotlin.io.path.absolutePathString + +fun main(args: Array) { + val jarPath = args[0] + val testName = args[1] + val debug = args.size > 2 && args[2] == "--debug" + + println("Running conformance test: $testName with --debug=$debug") + + val outputDir = Path(CONFORMANCE_OUTPUT_DIR).resolve("manual") + + val executable = getJavaClient(jarPath, "conformance", outputDir, testName, debug = debug) + + val (failingTestsFile, textFormatFailingTestsFile) = createConformanceTestFiles(outputDir) + + execConformanceTestRunner( + outputDir = outputDir, + failingTestsFile = failingTestsFile, + textFormatFailingTestsFile = textFormatFailingTestsFile, + executable = executable.absolutePathString(), + testName = testName, + ).onFailure { + throw it + }.onSuccess { + if (it.exitCode != 0) { + println(""" + |stdout: + | ${it.stdout.joinToString("${System.lineSeparator()}| ")} + |stderr: + | ${it.stderr.joinToString("${System.lineSeparator()}| ")} + """.trimMargin()) + + error("Conformance test failed with non 0 exit code: ${it.exitCode}") + } + } +} diff --git a/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/runner/javaClient.kt b/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/runner/javaClient.kt index dd9b3a322..7b7f9434b 100644 --- a/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/runner/javaClient.kt +++ b/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/runner/javaClient.kt @@ -7,19 +7,88 @@ package kotlinx.rpc.protoc.gen.test.runner import java.nio.file.Files import java.nio.file.Path import java.nio.file.attribute.PosixFilePermission +import kotlin.io.path.absolutePathString import kotlin.io.path.writeText -fun getJavaClient(jarPath: String, mode: String): Path { +fun getJavaClient( + jarPath: String, + mode: String, + outputDir: Path, + testName: String? = null, + debug: Boolean = false, +): Path { val executable = Files.createTempFile("clientRunner", ".sh") Files.setPosixFilePermissions(executable, PosixFilePermission.entries.toSet()) + val debugFlag = if (debug) { + "-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=*:5005" + } else { + "" + } + + val dumpConformanceInputFilePath = Files.createTempFile("dump_conformance_input", ".bin").absolutePathString() + val dumpConformanceInputFilePathTxt = outputDir.resolve("dump_conformance_input.bin.txt").absolutePathString() + + val dumpConformanceOutputFilePath = Files.createTempFile("dump_conformance_output", ".bin").absolutePathString() + val dumpConformanceOutputFilePathTxt = outputDir.resolve("dump_conformance_output.bin.txt").absolutePathString() + + val dumpPayloadInputFilePath = Files.createTempFile("dump_payload_input", ".bin").absolutePathString() + val dumpPayloadInputFilePathTxt = outputDir.resolve("dump_payload_input.bin.txt").absolutePathString() + + val dumpPayloadOutputFilePath = Files.createTempFile("dump_payload_output", ".bin").absolutePathString() + val dumpPayloadOutputFilePathTxt = outputDir.resolve("dump_payload_output.bin.txt").absolutePathString() + + val protoscopePath = System.getenv("PROTOSCOPE_PATH") + + // protoscope doesn't support proto2 and editions tests + val protoscope = protoscopePath != null && + testName != null && + !testName.contains("Proto2") && + !testName.contains("Editions") + + val conformancePbPath = System.getenv("CONFORMANCE_PB_PATH") + val testAllTypesProto3PbPath = System.getenv("TEST_ALL_TYPES_PROTO3_PB_PATH") + + if (protoscope && conformancePbPath == null) { + error("Expected environment variable 'CONFORMANCE_PB_PATH' to be set") + } + + if (protoscope && testAllTypesProto3PbPath == null) { + error("Expected environment variable 'TEST_ALL_TYPES_PROTO3_PB_PATH' to be set") + } + + val conformanceDescriptorSetInput = "-descriptor-set $conformancePbPath -message-type conformance.ConformanceRequest -print-field-names -print-enum-names" + val conformanceDescriptorSetOutput = "-descriptor-set $conformancePbPath -message-type conformance.ConformanceResponse -print-field-names -print-enum-names" + + val testAllTypesDescriptorSetInput = "-descriptor-set $testAllTypesProto3PbPath -message-type protobuf_test_messages.proto3.TestAllTypesProto3 -print-field-names -print-enum-names" + val testAllTypesDescriptorSetOutput = "-descriptor-set $testAllTypesProto3PbPath -message-type protobuf_test_messages.proto3.TestAllTypesProto3 -print-field-names -print-enum-names" + + fun protoscope(cmd: String): String { + if (protoscope) { + return cmd + } + + return "" + } + executable.writeText( """ #!/bin/bash - java -jar $jarPath $mode + ${protoscope("export DUMP_CONFORMANCE_INPUT_FILE=$dumpConformanceInputFilePath")} + ${protoscope("export DUMP_CONFORMANCE_OUTPUT_FILE=$dumpConformanceOutputFilePath")} + + ${protoscope("export DUMP_PAYLOAD_INPUT_FILE=$dumpPayloadInputFilePath")} + ${protoscope("export DUMP_PAYLOAD_OUTPUT_FILE=$dumpPayloadOutputFilePath")} + + java $debugFlag -jar $jarPath $mode + ${protoscope("$protoscopePath $conformanceDescriptorSetInput $dumpConformanceInputFilePath > $dumpConformanceInputFilePathTxt")} + ${protoscope("$protoscopePath $conformanceDescriptorSetOutput $dumpConformanceOutputFilePath > $dumpConformanceOutputFilePathTxt")} + + ${protoscope("$protoscopePath $testAllTypesDescriptorSetInput $dumpPayloadInputFilePath > $dumpPayloadInputFilePathTxt")} + ${protoscope("$protoscopePath $testAllTypesDescriptorSetOutput $dumpPayloadOutputFilePath > $dumpPayloadOutputFilePathTxt")} """.trimIndent() ) diff --git a/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/runner/runner.kt b/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/runner/runner.kt index 58efe5b4a..9c0363fc3 100644 --- a/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/runner/runner.kt +++ b/tests/protobuf-conformance/src/main/kotlin/kotlinx/rpc/protoc/gen/test/runner/runner.kt @@ -23,13 +23,17 @@ fun execConformanceTestRunner( failingTestsFile: Path?, textFormatFailingTestsFile: Path?, executable: String, + testName: String? = null, ): Result { val stdoutStream = Files.createTempFile("stdout", ".log") val errorStream = Files.createTempFile("error", ".log") + val testNameFilter = testName?.let { listOf("--test", it) } ?: emptyList() + return runCatching { val process = ProcessBuilder( CONFORMANCE_EXECUTABLE_PATH, + *testNameFilter.toTypedArray(), "--maximum_edition", "MAX", "--enforce_recommended", "--failure_list", failingTestsFile.toString(), diff --git a/tests/protobuf-conformance/src/test/kotlin/kotlinx/rpc/protoc/gen/test/ConformanceTest.kt b/tests/protobuf-conformance/src/test/kotlin/kotlinx/rpc/protoc/gen/test/ConformanceTest.kt index 060b640e6..e2e21c640 100644 --- a/tests/protobuf-conformance/src/test/kotlin/kotlinx/rpc/protoc/gen/test/ConformanceTest.kt +++ b/tests/protobuf-conformance/src/test/kotlin/kotlinx/rpc/protoc/gen/test/ConformanceTest.kt @@ -24,10 +24,10 @@ class ConformanceTest { val jarPath = System.getenv("MOCK_CLIENT_JAR") ?: error("Expected environment variable 'MOCK_CLIENT_JAR' to be set") - val executable = getJavaClient(jarPath, "conformance") - val outputDir = Path(CONFORMANCE_OUTPUT_DIR).resolve("conformance") + val executable = getJavaClient(jarPath, "conformance", outputDir) + val (failingTestsFile, textFormatFailingTestsFile) = createConformanceTestFiles(outputDir) val result = execConformanceTestRunner(