THRIFT-5548: add kotlin code gen
Client: kotlin
Patch: Jiayu Liu
This closes #2556
diff --git a/lib/kotlin/cross-test-client/build.gradle.kts b/lib/kotlin/cross-test-client/build.gradle.kts
new file mode 100644
index 0000000..3e67abf
--- /dev/null
+++ b/lib/kotlin/cross-test-client/build.gradle.kts
@@ -0,0 +1,82 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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.
+ */
+
+plugins {
+ kotlin("jvm") version "1.5.31"
+ id("com.ncorti.ktfmt.gradle") version "0.4.0"
+ java
+ application
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ implementation(platform("org.jetbrains.kotlin:kotlin-bom"))
+ implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
+ // https://mvnrepository.com/artifact/org.jetbrains.kotlinx/kotlinx-coroutines-jdk8
+ implementation("org.jetbrains.kotlinx:kotlinx-coroutines-jdk8:1.6.1")
+ // https://mvnrepository.com/artifact/org.apache.thrift/libthrift
+ implementation("org.apache.thrift:libthrift:INCLUDED")
+ // https://mvnrepository.com/artifact/ch.qos.logback/logback-classic
+ implementation("ch.qos.logback:logback-classic:1.3.0-alpha14")
+ testImplementation("org.jetbrains.kotlin:kotlin-test")
+ testImplementation("org.jetbrains.kotlin:kotlin-test-junit")
+}
+
+tasks {
+ application {
+ applicationName = "TestClient"
+ mainClass.set("org.apache.thrift.test.TestClientKt")
+ }
+
+ ktfmt {
+ kotlinLangStyle()
+ }
+
+ task<Exec>("compileThrift") {
+ val thriftBin = if (hasProperty("thrift.compiler")) {
+ file(property("thrift.compiler"))
+ } else {
+ project.rootDir.resolve("../../compiler/cpp/thrift")
+ }
+ val outputDir = layout.buildDirectory.dir("generated-sources")
+ doFirst {
+ mkdir(outputDir)
+ }
+ commandLine = listOf(
+ thriftBin.absolutePath,
+ "-gen",
+ "kotlin",
+ "-out",
+ outputDir.get().toString(),
+ project.rootDir.resolve("../../test/ThriftTest.thrift").absolutePath
+ )
+ group = LifecycleBasePlugin.BUILD_GROUP
+ }
+
+ compileKotlin {
+ dependsOn("compileThrift")
+ }
+}
+
+sourceSets["main"].java {
+ srcDir(layout.buildDirectory.dir("generated-sources"))
+}
diff --git a/lib/kotlin/cross-test-client/src/main/kotlin/org/apache/thrift/test/TestClient.kt b/lib/kotlin/cross-test-client/src/main/kotlin/org/apache/thrift/test/TestClient.kt
new file mode 100644
index 0000000..10ca829
--- /dev/null
+++ b/lib/kotlin/cross-test-client/src/main/kotlin/org/apache/thrift/test/TestClient.kt
@@ -0,0 +1,1045 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you 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 org.apache.thrift.test
+
+import org.apache.http.impl.client.HttpClients
+import org.apache.thrift.TApplicationException
+import org.apache.thrift.TException
+import org.apache.thrift.TSerializer
+import org.apache.thrift.async.TAsyncClientManager
+import org.apache.thrift.protocol.TBinaryProtocol
+import org.apache.thrift.protocol.TCompactProtocol
+import org.apache.thrift.protocol.TJSONProtocol
+import org.apache.thrift.protocol.TMultiplexedProtocol
+import org.apache.thrift.protocol.TProtocol
+import org.apache.thrift.protocol.TSimpleJSONProtocol
+import org.apache.thrift.transport.THttpClient
+import org.apache.thrift.transport.TNonblockingSocket
+import org.apache.thrift.transport.TSSLTransportFactory
+import org.apache.thrift.transport.TTransport
+import org.apache.thrift.transport.TZlibTransport
+import org.apache.thrift.transport.layered.TFastFramedTransport
+import org.apache.thrift.transport.layered.TFramedTransport
+import thrift.test.Insanity
+import thrift.test.Numberz
+import thrift.test.ThriftTestClient
+import thrift.test.Xception
+import thrift.test.Xception2
+import thrift.test.Xtruct
+import thrift.test.Xtruct2
+import java.nio.ByteBuffer
+import kotlin.math.abs
+import kotlin.system.exitProcess
+
+/**
+ * Test Java client for thrift. Essentially just a copy of the C++ version, this makes a variety of
+ * requests to enable testing for both performance and correctness of the output.
+ */
+const val ERR_BASETYPES = 1
+const val ERR_STRUCTS = 2
+const val ERR_CONTAINERS = 4
+const val ERR_EXCEPTIONS = 8
+const val ERR_PROTOCOLS = 16
+const val ERR_UNKNOWN = 64
+
+suspend fun main(args: Array<String>) {
+ var host = "localhost"
+ var port = 9090
+ var numTests = 1
+ var protocolType = "binary"
+ var transportType = "buffered"
+ var ssl = false
+ var zlib = false
+ var httpClient = false
+ var socketTimeout = 1000
+ try {
+ for (i in args.indices) {
+ if (args[i].startsWith("--host")) {
+ host = args[i].split("=").toTypedArray()[1]
+ host.trim { it <= ' ' }
+ } else if (args[i].startsWith("--port")) {
+ port = Integer.valueOf(args[i].split("=").toTypedArray()[1])
+ } else if (args[i].startsWith("--n") || args[i].startsWith("--testloops")) {
+ numTests = Integer.valueOf(args[i].split("=").toTypedArray()[1])
+ } else if (args[i] == "--timeout") {
+ socketTimeout = Integer.valueOf(args[i].split("=").toTypedArray()[1])
+ } else if (args[i].startsWith("--protocol")) {
+ protocolType = args[i].split("=").toTypedArray()[1]
+ protocolType.trim { it <= ' ' }
+ } else if (args[i].startsWith("--transport")) {
+ transportType = args[i].split("=").toTypedArray()[1]
+ transportType.trim { it <= ' ' }
+ } else if (args[i] == "--ssl") {
+ ssl = true
+ } else if (args[i] == "--zlib") {
+ zlib = true
+ } else if (args[i] == "--client") {
+ httpClient = true
+ } else if (args[i] == "--help") {
+ println("Allowed options:")
+ println(" --help\t\t\tProduce help message")
+ println(" --host=arg (=$host)\tHost to connect")
+ println(" --port=arg (=$port)\tPort number to connect")
+ println(
+ " --transport=arg (=$transportType)\n\t\t\t\tTransport: buffered, framed, fastframed, http, zlib"
+ )
+ println(
+ " --protocol=arg (=$protocolType)\tProtocol: binary, compact, json, multi, multic, multij"
+ )
+ println(" --ssl\t\t\tEncrypted Transport using SSL")
+ println(" --zlib\t\t\tCompressed Transport using Zlib")
+ println(" --testloops[--n]=arg (=$numTests)\tNumber of Tests")
+ exitProcess(0)
+ }
+ }
+ } catch (x: Exception) {
+ System.err.println("Can not parse arguments! See --help")
+ exitProcess(ERR_UNKNOWN)
+ }
+ try {
+ checkProtocolType(protocolType)
+ checkTransportType(transportType)
+ if (transportType == "http" && ssl) {
+ throw Exception("SSL is not supported over http.")
+ }
+ } catch (e: Exception) {
+ System.err.println("Error: " + e.message)
+ exitProcess(ERR_UNKNOWN)
+ }
+ val transport: TTransport
+ try {
+ transport = getTTransport(transportType, host, port, httpClient, ssl, socketTimeout, zlib)
+ } catch (x: Exception) {
+ x.printStackTrace()
+ exitProcess(ERR_UNKNOWN)
+ }
+ var tProtocol = getTProtocol(protocolType, transport)
+ var tProtocol2: TProtocol? = null
+ if (protocolType.startsWith("multi")) {
+ tProtocol2 = TMultiplexedProtocol(tProtocol, "SecondService")
+ tProtocol = TMultiplexedProtocol(tProtocol, "ThriftTest")
+ }
+ println("$tProtocol, $transport")
+
+ val clientFactory = {
+ ThriftTestClient(
+ { TBinaryProtocol(it) },
+ TAsyncClientManager(),
+ TNonblockingSocket(host, port, socketTimeout)
+ )
+ }
+
+ var testClient = clientFactory()
+ val insane = Insanity()
+ var timeMin: Long = 0
+ var timeMax: Long = 0
+ var timeTot: Long = 0
+ var returnCode = 0
+ for (test in 0 until numTests) {
+ // transport.startConnect()
+ try {
+ /** CONNECT TEST */
+ println("Test #${test + 1}, connect $host:$port")
+ // if (!transport.isOpen) {
+ // try {
+ // transport.open()
+ // } catch (ttx: TTransportException) {
+ // ttx.printStackTrace()
+ // println("Connect failed: " + ttx.message)
+ // exitProcess(ERR_UNKNOWN)
+ // }
+ // }
+ val start = System.nanoTime()
+ /** VOID TEST */
+ try {
+ print("testVoid()")
+ testClient.testVoid()
+ print(" = void\n")
+ } catch (tax: TApplicationException) {
+ tax.printStackTrace()
+ returnCode = returnCode or ERR_BASETYPES
+ }
+ /** STRING TEST */
+ print("testString(\"Test\")")
+ val s: String = testClient.testString("Test")
+ print(" = \"$s\"\n")
+ if (s != "Test") {
+ returnCode = returnCode or ERR_BASETYPES
+ println("*** FAILURE ***\n")
+ }
+ /** Multiplexed test */
+ if (protocolType.startsWith("multi")) {
+ throw UnsupportedOperationException("multi protocol is not yet supported")
+ // val secondClient: SecondServiceClient =
+ // SecondServiceClient(tProtocol2)
+ // print("secondtestString(\"Test2\")")
+ // s = secondClient.secondtestString("Test2")
+ // print(" = \"$s\"\n")
+ // if (s != "testString(\"Test2\")") {
+ // returnCode = returnCode or ERR_PROTOCOLS
+ // println("*** FAILURE ***\n")
+ // }
+ }
+ /** BYTE TEST */
+ print("testByte(1)")
+ val i8: Byte = testClient.testByte(1.toByte())
+ print(" = $i8\n")
+ if (i8.toInt() != 1) {
+ returnCode = returnCode or ERR_BASETYPES
+ println("*** FAILURE ***\n")
+ }
+ /** I32 TEST */
+ print("testI32(-1)")
+ val i32: Int = testClient.testI32(-1)
+ print(" = $i32\n")
+ if (i32 != -1) {
+ returnCode = returnCode or ERR_BASETYPES
+ println("*** FAILURE ***\n")
+ }
+ /** I64 TEST */
+ print("testI64(-34359738368)")
+ val i64: Long = testClient.testI64(-34359738368L)
+ print(" = $i64\n")
+ if (i64 != -34359738368L) {
+ returnCode = returnCode or ERR_BASETYPES
+ println("*** FAILURE ***\n")
+ }
+ /** DOUBLE TEST */
+ print("testDouble(-5.325098235)")
+ val dub: Double = testClient.testDouble(-5.325098235)
+ print(" = $dub\n")
+ if (abs(dub - -5.325098235) > 0.001) {
+ returnCode = returnCode or ERR_BASETYPES
+ println("*** FAILURE ***\n")
+ }
+ /** BINARY TEST */
+ try {
+ print("testBinary(-128...127) = ")
+ val data =
+ byteArrayOf(
+ -128,
+ -127,
+ -126,
+ -125,
+ -124,
+ -123,
+ -122,
+ -121,
+ -120,
+ -119,
+ -118,
+ -117,
+ -116,
+ -115,
+ -114,
+ -113,
+ -112,
+ -111,
+ -110,
+ -109,
+ -108,
+ -107,
+ -106,
+ -105,
+ -104,
+ -103,
+ -102,
+ -101,
+ -100,
+ -99,
+ -98,
+ -97,
+ -96,
+ -95,
+ -94,
+ -93,
+ -92,
+ -91,
+ -90,
+ -89,
+ -88,
+ -87,
+ -86,
+ -85,
+ -84,
+ -83,
+ -82,
+ -81,
+ -80,
+ -79,
+ -78,
+ -77,
+ -76,
+ -75,
+ -74,
+ -73,
+ -72,
+ -71,
+ -70,
+ -69,
+ -68,
+ -67,
+ -66,
+ -65,
+ -64,
+ -63,
+ -62,
+ -61,
+ -60,
+ -59,
+ -58,
+ -57,
+ -56,
+ -55,
+ -54,
+ -53,
+ -52,
+ -51,
+ -50,
+ -49,
+ -48,
+ -47,
+ -46,
+ -45,
+ -44,
+ -43,
+ -42,
+ -41,
+ -40,
+ -39,
+ -38,
+ -37,
+ -36,
+ -35,
+ -34,
+ -33,
+ -32,
+ -31,
+ -30,
+ -29,
+ -28,
+ -27,
+ -26,
+ -25,
+ -24,
+ -23,
+ -22,
+ -21,
+ -20,
+ -19,
+ -18,
+ -17,
+ -16,
+ -15,
+ -14,
+ -13,
+ -12,
+ -11,
+ -10,
+ -9,
+ -8,
+ -7,
+ -6,
+ -5,
+ -4,
+ -3,
+ -2,
+ -1,
+ 0,
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+ 6,
+ 7,
+ 8,
+ 9,
+ 10,
+ 11,
+ 12,
+ 13,
+ 14,
+ 15,
+ 16,
+ 17,
+ 18,
+ 19,
+ 20,
+ 21,
+ 22,
+ 23,
+ 24,
+ 25,
+ 26,
+ 27,
+ 28,
+ 29,
+ 30,
+ 31,
+ 32,
+ 33,
+ 34,
+ 35,
+ 36,
+ 37,
+ 38,
+ 39,
+ 40,
+ 41,
+ 42,
+ 43,
+ 44,
+ 45,
+ 46,
+ 47,
+ 48,
+ 49,
+ 50,
+ 51,
+ 52,
+ 53,
+ 54,
+ 55,
+ 56,
+ 57,
+ 58,
+ 59,
+ 60,
+ 61,
+ 62,
+ 63,
+ 64,
+ 65,
+ 66,
+ 67,
+ 68,
+ 69,
+ 70,
+ 71,
+ 72,
+ 73,
+ 74,
+ 75,
+ 76,
+ 77,
+ 78,
+ 79,
+ 80,
+ 81,
+ 82,
+ 83,
+ 84,
+ 85,
+ 86,
+ 87,
+ 88,
+ 89,
+ 90,
+ 91,
+ 92,
+ 93,
+ 94,
+ 95,
+ 96,
+ 97,
+ 98,
+ 99,
+ 100,
+ 101,
+ 102,
+ 103,
+ 104,
+ 105,
+ 106,
+ 107,
+ 108,
+ 109,
+ 110,
+ 111,
+ 112,
+ 113,
+ 114,
+ 115,
+ 116,
+ 117,
+ 118,
+ 119,
+ 120,
+ 121,
+ 122,
+ 123,
+ 124,
+ 125,
+ 126,
+ 127
+ )
+ val bin: ByteBuffer = ByteBuffer.wrap(testClient.testBinary(data))
+ bin.mark()
+ val bytes = ByteArray(bin.limit() - bin.position())
+ bin[bytes]
+ bin.reset()
+ print("{")
+ var first = true
+ for (i in bytes.indices) {
+ if (first) first = false else print(", ")
+ print(bytes[i])
+ }
+ println("}")
+ if (ByteBuffer.wrap(data) != bin) {
+ returnCode = returnCode or ERR_BASETYPES
+ println("*** FAILURE ***\n")
+ }
+ } catch (ex: Exception) {
+ returnCode = returnCode or ERR_BASETYPES
+ println("\n*** FAILURE ***\n")
+ ex.printStackTrace(System.out)
+ }
+ /** STRUCT TEST */
+ print("testStruct({\"Zero\", 1, -3, -5})")
+ val out = Xtruct()
+ out.string_thing = "Zero"
+ out.byte_thing = 1.toByte()
+ out.i32_thing = -3
+ out.i64_thing = -5
+ var `in`: Xtruct = testClient.testStruct(out)
+ print(
+ """ = {"${`in`.string_thing}",${`in`.byte_thing}, ${`in`.i32_thing}, ${`in`.i64_thing}}
+"""
+ )
+ if (`in` != out) {
+ returnCode = returnCode or ERR_STRUCTS
+ println("*** FAILURE ***\n")
+ }
+ /** NESTED STRUCT TEST */
+ print("testNest({1, {\"Zero\", 1, -3, -5}), 5}")
+ val out2 = Xtruct2()
+ out2.byte_thing = 1.toShort().toByte()
+ out2.struct_thing = out
+ out2.i32_thing = 5
+ val in2: Xtruct2 = testClient.testNest(out2)
+ `in` = in2.struct_thing!!
+ print(
+ """ = {${in2.byte_thing}, {"${`in`.string_thing}", ${`in`.byte_thing}, ${`in`.i32_thing}, ${`in`.i64_thing}}, ${in2.i32_thing}}
+"""
+ )
+ if (in2 != out2) {
+ returnCode = returnCode or ERR_STRUCTS
+ println("*** FAILURE ***\n")
+ }
+ /** MAP TEST */
+ val mapout: MutableMap<Int, Int> = HashMap()
+ for (i in 0..4) {
+ mapout[i] = i - 10
+ }
+ print("testMap({")
+ var first = true
+ for (key in mapout.keys) {
+ if (first) {
+ first = false
+ } else {
+ print(", ")
+ }
+ print(key.toString() + " => " + mapout[key])
+ }
+ print("})")
+ val mapin: Map<Int, Int> = testClient.testMap(mapout)
+ print(" = {")
+ first = true
+ for (key in mapin.keys) {
+ if (first) {
+ first = false
+ } else {
+ print(", ")
+ }
+ print(key.toString() + " => " + mapout[key])
+ }
+ print("}\n")
+ if (mapout != mapin) {
+ returnCode = returnCode or ERR_CONTAINERS
+ println("*** FAILURE ***\n")
+ }
+ /** STRING MAP TEST */
+ try {
+ val smapout: MutableMap<String, String> = HashMap()
+ smapout["a"] = "2"
+ smapout["b"] = "blah"
+ smapout["some"] = "thing"
+ for (key in smapout.keys) {
+ if (first) {
+ first = false
+ } else {
+ print(", ")
+ }
+ print(key + " => " + smapout[key])
+ }
+ print("})")
+ val smapin: Map<String, String> = testClient.testStringMap(smapout)
+ print(" = {")
+ first = true
+ for (key in smapin.keys) {
+ if (first) {
+ first = false
+ } else {
+ print(", ")
+ }
+ print(key + " => " + smapout[key])
+ }
+ print("}\n")
+ if (smapout != smapin) {
+ returnCode = returnCode or ERR_CONTAINERS
+ println("*** FAILURE ***\n")
+ }
+ } catch (ex: Exception) {
+ returnCode = returnCode or ERR_CONTAINERS
+ println("*** FAILURE ***\n")
+ ex.printStackTrace(System.out)
+ }
+ /** SET TEST */
+ val setout: MutableSet<Int> = HashSet()
+ for (i in -2..2) {
+ setout.add(i)
+ }
+ print("testSet({")
+ first = true
+ for (elem in setout) {
+ if (first) {
+ first = false
+ } else {
+ print(", ")
+ }
+ print(elem)
+ }
+ print("})")
+ val setin: Set<Int> = testClient.testSet(setout)
+ print(" = {")
+ first = true
+ for (elem in setin) {
+ if (first) {
+ first = false
+ } else {
+ print(", ")
+ }
+ print(elem)
+ }
+ print("}\n")
+ if (setout != setin) {
+ returnCode = returnCode or ERR_CONTAINERS
+ println("*** FAILURE ***\n")
+ }
+ /** LIST TEST */
+ val listout: MutableList<Int> = ArrayList()
+ for (i in -2..2) {
+ listout.add(i)
+ }
+ print("testList({")
+ first = true
+ for (elem in listout) {
+ if (first) {
+ first = false
+ } else {
+ print(", ")
+ }
+ print(elem)
+ }
+ print("})")
+ val listin: List<Int> = testClient.testList(listout)
+ print(" = {")
+ first = true
+ for (elem in listin) {
+ if (first) {
+ first = false
+ } else {
+ print(", ")
+ }
+ print(elem)
+ }
+ print("}\n")
+ if (listout != listin) {
+ returnCode = returnCode or ERR_CONTAINERS
+ println("*** FAILURE ***\n")
+ }
+ /** ENUM TEST */
+ print("testEnum(ONE)")
+ var ret: Numberz = testClient.testEnum(Numberz.ONE)
+ print(" = $ret\n")
+ if (ret !== Numberz.ONE) {
+ returnCode = returnCode or ERR_STRUCTS
+ println("*** FAILURE ***\n")
+ }
+ print("testEnum(TWO)")
+ ret = testClient.testEnum(Numberz.TWO)
+ print(" = $ret\n")
+ if (ret !== Numberz.TWO) {
+ returnCode = returnCode or ERR_STRUCTS
+ println("*** FAILURE ***\n")
+ }
+ print("testEnum(THREE)")
+ ret = testClient.testEnum(Numberz.THREE)
+ print(" = $ret\n")
+ if (ret !== Numberz.THREE) {
+ returnCode = returnCode or ERR_STRUCTS
+ println("*** FAILURE ***\n")
+ }
+ print("testEnum(FIVE)")
+ ret = testClient.testEnum(Numberz.FIVE)
+ print(" = $ret\n")
+ if (ret !== Numberz.FIVE) {
+ returnCode = returnCode or ERR_STRUCTS
+ println("*** FAILURE ***\n")
+ }
+ print("testEnum(EIGHT)")
+ ret = testClient.testEnum(Numberz.EIGHT)
+ print(" = $ret\n")
+ if (ret !== Numberz.EIGHT) {
+ returnCode = returnCode or ERR_STRUCTS
+ println("*** FAILURE ***\n")
+ }
+ /** TYPEDEF TEST */
+ print("testTypedef(309858235082523)")
+ val uid: Long = testClient.testTypedef(309858235082523L)
+ print(" = $uid\n")
+ if (uid != 309858235082523L) {
+ returnCode = returnCode or ERR_BASETYPES
+ println("*** FAILURE ***\n")
+ }
+ /** NESTED MAP TEST */
+ print("testMapMap(1)")
+ val mm: Map<Int, Map<Int, Int>> = testClient.testMapMap(1)
+ print(" = {")
+ for (key in mm.keys) {
+ print("$key => {")
+ val m2 = mm[key]!!
+ for (k2 in m2.keys) {
+ print(k2.toString() + " => " + m2[k2] + ", ")
+ }
+ print("}, ")
+ }
+ print("}\n")
+ if (mm.size != 2 || !mm.containsKey(4) || !mm.containsKey(-4)) {
+ returnCode = returnCode or ERR_CONTAINERS
+ println("*** FAILURE ***\n")
+ } else {
+ val m1 = mm[4]!!
+ val m2 = mm[-4]!!
+ if (m1[1] != 1 ||
+ m1[2] != 2 ||
+ m1[3] != 3 ||
+ m1[4] != 4 ||
+ m2[-1] != -1 ||
+ m2[-2] != -2 ||
+ m2[-3] != -3 ||
+ m2[-4] != -4
+ ) {
+ returnCode = returnCode or ERR_CONTAINERS
+ println("*** FAILURE ***\n")
+ }
+ }
+ /** INSANITY TEST */
+ var insanityFailed = true
+ try {
+ val hello = Xtruct()
+ hello.string_thing = "Hello2"
+ hello.byte_thing = 2
+ hello.i32_thing = 2
+ hello.i64_thing = 2
+ val goodbye = Xtruct()
+ goodbye.string_thing = "Goodbye4"
+ goodbye.byte_thing = 4.toByte()
+ goodbye.i32_thing = 4
+ goodbye.i64_thing = 4L
+ insane.userMap =
+ HashMap<Numberz, Long>().apply {
+ put(Numberz.EIGHT, 8L)
+ put(Numberz.FIVE, 5L)
+ }
+
+ insane.xtructs =
+ ArrayList<Xtruct>().apply {
+ add(goodbye)
+ add(hello)
+ }
+
+ print("testInsanity()")
+ val whoa: Map<Long, Map<Numberz, Insanity>> = testClient.testInsanity(insane)
+ print(" = {")
+ for (key in whoa.keys) {
+ val `val` = whoa[key]!!
+ print("$key => {")
+ for (k2 in `val`.keys) {
+ val v2 = `val`[k2]
+ print("$k2 => {")
+ val userMap = v2!!.userMap
+ print("{")
+ if (userMap != null) {
+ for (k3 in userMap.keys) {
+ print(k3.toString() + " => " + userMap[k3] + ", ")
+ }
+ }
+ print("}, ")
+ val xtructs = v2.xtructs
+ print("{")
+ if (xtructs != null) {
+ for ((string_thing, byte_thing, i32_thing, i64_thing) in xtructs) {
+ print("{\"$string_thing\", $byte_thing, $i32_thing, $i64_thing}, ")
+ }
+ }
+ print("}")
+ print("}, ")
+ }
+ print("}, ")
+ }
+ print("}\n")
+ if (whoa.size == 2 && whoa.containsKey(1L) && whoa.containsKey(2L)) {
+ val first_map = whoa[1L]!!
+ val second_map = whoa[2L]!!
+ if (first_map.size == 2 &&
+ first_map.containsKey(Numberz.TWO) &&
+ first_map.containsKey(Numberz.THREE) &&
+ second_map.size == 1 &&
+ second_map.containsKey(Numberz.SIX) &&
+ insane == first_map[Numberz.TWO] &&
+ insane == first_map[Numberz.THREE]
+ ) {
+ val six = second_map[Numberz.SIX]!!
+ // Cannot use "new Insanity().equals(six)" because as of now,
+ // struct/container
+ // fields with default requiredness have isset=false for local instances and
+ // yet
+ // received empty values from other languages like C++ have isset=true .
+ if ((six.userMap?.size ?: 0) == 0 && (six.xtructs?.size ?: 0) == 0) {
+ // OK
+ insanityFailed = false
+ }
+ }
+ }
+ } catch (ex: Exception) {
+ returnCode = returnCode or ERR_STRUCTS
+ println("*** FAILURE ***\n")
+ ex.printStackTrace(System.out)
+ insanityFailed = false
+ }
+ if (insanityFailed) {
+ returnCode = returnCode or ERR_STRUCTS
+ println("*** FAILURE ***\n")
+ }
+ /** EXECPTION TEST */
+ try {
+ print("testClient.testException(\"Xception\") =>")
+ testClient.testException("Xception")
+ print(" void\n*** FAILURE ***\n")
+ returnCode = returnCode or ERR_EXCEPTIONS
+ } catch (e: Xception) {
+ System.out.printf(" {%d, \"%s\"}\n", e.errorCode, e.message)
+ testClient = clientFactory()
+ }
+ try {
+ print("testClient.testException(\"TException\") =>")
+ testClient.testException("TException")
+ print(" void\n*** FAILURE ***\n")
+ returnCode = returnCode or ERR_EXCEPTIONS
+ } catch (e: TException) {
+ System.out.printf(" {\"%s\"}\n", e.message)
+ testClient = clientFactory()
+ }
+ try {
+ print("testClient.testException(\"success\") =>")
+ testClient.testException("success")
+ print(" void\n")
+ } catch (e: Exception) {
+ System.out.printf(" exception\n*** FAILURE ***\n")
+ returnCode = returnCode or ERR_EXCEPTIONS
+ }
+ /** MULTI EXCEPTION TEST */
+ try {
+ System.out.printf("testClient.testMultiException(\"Xception\", \"test 1\") =>")
+ testClient.testMultiException("Xception", "test 1")
+ print(" result\n*** FAILURE ***\n")
+ returnCode = returnCode or ERR_EXCEPTIONS
+ } catch (e: Xception) {
+ System.out.printf(" {%d, \"%s\"}\n", e.errorCode, e.message)
+ testClient = clientFactory()
+ }
+ try {
+ System.out.printf("testClient.testMultiException(\"Xception2\", \"test 2\") =>")
+ testClient.testMultiException("Xception2", "test 2")
+ print(" result\n*** FAILURE ***\n")
+ returnCode = returnCode or ERR_EXCEPTIONS
+ } catch (e: Xception2) {
+ System.out.printf(" {%d, {\"%s\"}}\n", e.errorCode, e.struct_thing!!.string_thing)
+ testClient = clientFactory()
+ }
+ try {
+ print("testClient.testMultiException(\"success\", \"test 3\") =>")
+ val result: Xtruct = testClient.testMultiException("success", "test 3")
+ System.out.printf(" {{\"%s\"}}\n", result.string_thing)
+ } catch (e: Exception) {
+ System.out.printf(" exception\n*** FAILURE ***\n")
+ returnCode = returnCode or ERR_EXCEPTIONS
+ }
+ /** ONEWAY TEST */
+ print("testOneway(3)...")
+ val startOneway = System.nanoTime()
+ testClient.testOneway(3)
+ val onewayElapsedMillis = (System.nanoTime() - startOneway) / 1000000
+ if (onewayElapsedMillis > 200) {
+ println(
+ "Oneway test took too long to execute failed: took " +
+ onewayElapsedMillis +
+ "ms"
+ )
+ println(
+ "oneway calls are 'fire and forget' and therefore should not cause blocking."
+ )
+ println(
+ "Some transports (HTTP) have a required response, and typically this failure"
+ )
+ println("means the transport response was delayed until after the execution")
+ println(
+ "of the RPC. The server should post the transport response immediately and"
+ )
+ println("before executing the RPC.")
+ println("*** FAILURE ***")
+ returnCode = returnCode or ERR_BASETYPES
+ } else {
+ println("Success - fire and forget only took " + onewayElapsedMillis + "ms")
+ }
+ val stop = System.nanoTime()
+ val tot = stop - start
+ println("Total time: " + tot / 1000 + "us")
+ if (timeMin == 0L || tot < timeMin) {
+ timeMin = tot
+ }
+ if (tot > timeMax) {
+ timeMax = tot
+ }
+ timeTot += tot
+ // transport.close()
+ } catch (x: Exception) {
+ System.out.printf("*** FAILURE ***\n")
+ x.printStackTrace()
+ returnCode = returnCode or ERR_UNKNOWN
+ }
+ }
+ val timeAvg = timeTot / numTests
+ println("Min time: " + timeMin / 1000 + "us")
+ println("Max time: " + timeMax / 1000 + "us")
+ println("Avg time: " + timeAvg / 1000 + "us")
+ try {
+ val json = TSerializer(TSimpleJSONProtocol.Factory()).toString(insane)
+ println("\nSample TSimpleJSONProtocol output:\n$json")
+ } catch (x: TException) {
+ println("*** FAILURE ***")
+ x.printStackTrace()
+ returnCode = returnCode or ERR_BASETYPES
+ }
+ exitProcess(returnCode)
+}
+
+private fun getTProtocol(protocol_type: String, transport: TTransport) =
+ when (protocol_type) {
+ "json", "multij" -> {
+ TJSONProtocol(transport)
+ }
+ "compact", "multic" -> {
+ TCompactProtocol(transport)
+ }
+ else -> {
+ TBinaryProtocol(transport)
+ }
+ }
+
+private fun checkTransportType(transport_type: String) {
+ when (transport_type) {
+ "buffered" -> {}
+ "framed" -> {}
+ "fastframed" -> {}
+ "http" -> {}
+ "zlib" -> {}
+ else -> {
+ throw Exception("Unknown transport type! $transport_type")
+ }
+ }
+}
+
+private fun checkProtocolType(protocol_type: String) {
+ when (protocol_type) {
+ "binary" -> {}
+ "compact" -> {}
+ "json" -> {}
+ "multi" -> {}
+ "multic" -> {}
+ "multij" -> {}
+ else -> {
+ throw Exception("Unknown protocol type! $protocol_type")
+ }
+ }
+}
+
+private fun getTTransport(
+ transport_type: String,
+ host: String,
+ port: Int,
+ http_client: Boolean,
+ ssl: Boolean,
+ socketTimeout: Int,
+ zlib: Boolean
+): TTransport {
+ when (transport_type) {
+ "http" -> {
+ val url = "http://$host:$port/test/service"
+ return if (http_client) {
+ THttpClient(url, HttpClients.createDefault())
+ } else {
+ THttpClient(url)
+ }
+ }
+ else -> {
+ val socket = if (ssl) {
+ TSSLTransportFactory.getClientSocket(host, port, socketTimeout)
+ } else {
+ println("using non-blocking socket $host:$port")
+ TNonblockingSocket(host, port, socketTimeout)
+ }
+ if (transport_type == "zlib") {
+ return TZlibTransport(socket)
+ } else {
+ val wrapped = when (transport_type) {
+ "buffered" -> {
+ socket
+ }
+ "framed" -> {
+ TFramedTransport(socket)
+ }
+ "fastframed" -> {
+ TFastFramedTransport(socket)
+ }
+ else -> {
+ socket
+ }
+ }
+ return if (zlib) {
+ TZlibTransport(wrapped)
+ } else {
+ wrapped
+ }
+ }
+ }
+ }
+}
diff --git a/lib/kotlin/cross-test-client/src/main/resources/logback.xml b/lib/kotlin/cross-test-client/src/main/resources/logback.xml
new file mode 100644
index 0000000..2a2fc0a
--- /dev/null
+++ b/lib/kotlin/cross-test-client/src/main/resources/logback.xml
@@ -0,0 +1,33 @@
+<!--
+ ~ Licensed to the Apache Software Foundation (ASF) under one
+ ~ or more contributor license agreements. See the NOTICE file
+ ~ distributed with this work for additional information
+ ~ regarding copyright ownership. The ASF licenses this file
+ ~ to you 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.
+ -->
+
+<configuration>
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <!-- encoders are assigned the type
+ ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
+ <encoder>
+ <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <root level="debug">
+ <appender-ref ref="STDOUT"/>
+ </root>
+</configuration>
+