summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
authorKino Roy <kinoroy@users.noreply.github.com>2022-10-07 23:13:01 -0700
committerGitHub <noreply@github.com>2022-10-08 14:13:01 +0800
commita9da9eb05993a235df999226303bc594e4778805 (patch)
tree173200fc08231818b9bdea1943fda0be11cd560f /test
parented542c63fc4360ef6d397df3cd92d6623d713e1e (diff)
downloadthrift-a9da9eb05993a235df999226303bc594e4778805.tar.gz
THRIFT-4547: Swift crosstests (#2670)
Co-authored-by: Jiayu Liu <Jimexist@users.noreply.github.com>
Diffstat (limited to 'test')
-rw-r--r--test/Makefile.am6
-rw-r--r--test/swift/CrossTests/Makefile.am34
-rw-r--r--test/swift/CrossTests/Package.swift44
-rw-r--r--test/swift/CrossTests/Sources/Common/Parameters.swift256
-rw-r--r--test/swift/CrossTests/Sources/TestClient/main.swift400
-rw-r--r--test/swift/CrossTests/Sources/TestServer/ThriftTestService.swift389
-rw-r--r--test/swift/CrossTests/Sources/TestServer/main.swift59
-rw-r--r--test/swift/Makefile.am24
-rw-r--r--test/tests.json17
9 files changed, 1229 insertions, 0 deletions
diff --git a/test/Makefile.am b/test/Makefile.am
index 478fb2cb6..d5f143402 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -92,6 +92,11 @@ SUBDIRS += rs
PRECROSS_TARGET += precross-rs
endif
+if WITH_SWIFT
+SUBDIRS += swift
+PRECROSS_TARGET += precross-swift
+endif
+
#
# generate html for ThriftTest.thrift AND validate it!
#
@@ -133,6 +138,7 @@ EXTRA_DIST = \
py.twisted \
rb \
rs \
+ swift \
threads \
partial \
AnnotationTest.thrift \
diff --git a/test/swift/CrossTests/Makefile.am b/test/swift/CrossTests/Makefile.am
new file mode 100644
index 000000000..b7d8fbb49
--- /dev/null
+++ b/test/swift/CrossTests/Makefile.am
@@ -0,0 +1,34 @@
+#
+# 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.
+#
+
+TESTTHRIFT=../../ThriftTest.thrift
+
+stubs: $(THRIFT) $(TESTTHRIFT)
+ $(THRIFT) -o Sources/Common --gen swift $(TESTTHRIFT)
+
+precross: stubs
+ swift build
+
+check: stubs
+
+clean-local:
+ $(RM) -r Sources/Common/gen-swift/
+
+dist-hook:
+ $(RM) -r $(distdir)/gen-swift/
diff --git a/test/swift/CrossTests/Package.swift b/test/swift/CrossTests/Package.swift
new file mode 100644
index 000000000..4b3f6cbee
--- /dev/null
+++ b/test/swift/CrossTests/Package.swift
@@ -0,0 +1,44 @@
+// swift-tools-version:5.1
+// 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.
+
+import PackageDescription
+
+let package = Package(
+ name: "CrossTests",
+ products: [
+ .executable(name: "TestServer", targets: ["TestServer"]),
+ .executable(name: "TestClient", targets: ["TestClient"]),
+ ],
+ dependencies: [
+ // Dependencies declare other packages that this package depends on.
+ .package(path: "../../../lib/swift")
+ ],
+ targets: [
+ // Targets are the basic building blocks of a package. A target can define a module or a test suite.
+ // Targets can depend on other targets in this package, and on products in packages which this package depends on.
+ .target(
+ name: "Common",
+ dependencies: ["Thrift"]),
+ .target(
+ name: "TestServer",
+ dependencies: ["Thrift", "Common"]),
+ .target(
+ name: "TestClient",
+ dependencies: ["Thrift", "Common"])
+ ]
+)
diff --git a/test/swift/CrossTests/Sources/Common/Parameters.swift b/test/swift/CrossTests/Sources/Common/Parameters.swift
new file mode 100644
index 000000000..4ef1dcd81
--- /dev/null
+++ b/test/swift/CrossTests/Sources/Common/Parameters.swift
@@ -0,0 +1,256 @@
+// 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.
+
+import Foundation
+import Thrift
+
+public enum Protocol: String {
+ case binary
+ case compact
+ case header
+ case json
+}
+
+public enum Transport: String {
+ case buffered
+ case framed
+ case http
+ case anonpipe
+ case zlib
+}
+
+public enum ServerType: String {
+ case simple
+ case threadPool = "thread-pool"
+ case threaded
+ case nonblocking
+}
+
+public enum ParserError: Error {
+ case unknownArgument(argument: String)
+ case missingParameter(argument: String)
+ case invalidParameter(argument: String, parameter: String)
+
+ case unsupportedOption
+}
+
+public class ParametersBase {
+ public var showHelp = false
+ public var port: Int?
+ public var domainSocket: String?
+ public var namedPipe: String?
+ public var proto: Protocol?
+ public var transport: Transport?
+ public var multiplex = false
+ public var abstractNamespace = false
+ public var ssl = false
+ public var zlib = false
+
+ public init (arguments: [String]) throws {
+ if arguments.count > 1 {
+ for argument in arguments[1...] {
+ let equalSignPos = argument.firstIndex(of: "=") ?? argument.endIndex
+ let name = String(argument[..<equalSignPos])
+ let value: String? = (equalSignPos < argument.endIndex) ? String(argument[argument.index(equalSignPos, offsetBy: 1)..<argument.endIndex]) : nil
+
+ try processArgument(name: name, value: value)
+ }
+ }
+
+ fillDefaults()
+ try checkSupported()
+ }
+
+ open func processArgument(name: String, value: String?) throws {
+ switch name {
+ case "-h", "--help":
+ showHelp = true
+ case "--port":
+ guard value != nil else { throw ParserError.missingParameter(argument: name) }
+ port = Int(value!)
+ guard port != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+ case "--domain-socket":
+ guard value != nil else { throw ParserError.missingParameter(argument: name) }
+ domainSocket = value!
+ case "--named-pipe":
+ guard value != nil else { throw ParserError.missingParameter(argument: name) }
+ namedPipe = value!
+ case "--transport":
+ guard value != nil else { throw ParserError.missingParameter(argument: name) }
+ transport = Transport(rawValue: value!)
+ guard transport != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+ case "--protocol":
+ guard value != nil else { throw ParserError.missingParameter(argument: name) }
+ proto = Protocol(rawValue: value!)
+ guard proto != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+ case "--multiplex":
+ multiplex = true
+ case "--abstract-namespace":
+ abstractNamespace = true
+ case "--ssl":
+ ssl = true
+ case "--zlib":
+ zlib = true
+ default:
+ throw ParserError.unknownArgument(argument: name)
+ }
+ }
+
+ open func fillDefaults() {
+ if port == nil && domainSocket == nil && namedPipe == nil {
+ port = 9090
+ }
+
+ if transport == nil {
+ transport = .buffered
+ }
+
+ if proto == nil {
+ proto = .binary
+ }
+ }
+
+ open func checkSupported() throws {
+ guard transport == .buffered || transport == .framed else { throw ParserError.unsupportedOption }
+ guard proto == .binary || proto == .compact else { throw ParserError.unsupportedOption }
+ }
+}
+
+public class TestClientParameters: ParametersBase {
+ public var host: String?
+ public var testLoops: Int?
+ public var threads: Int?
+
+ public func printHelp() {
+ print("""
+Allowed options:
+ -h | --help produce help message
+ --host=arg (localhost) Host to connect
+ --port=arg (9090) Port number to connect
+ --domain-socket=arg Domain Socket (e.g. /tmp/ThriftTest.thrift),
+ instead of host and port
+ --named-pipe=arg Windows Named Pipe (e.g. MyThriftPipe)
+ --anon-pipes hRead hWrite Windows Anonymous Pipes pair (handles)
+ --abstract-namespace Create the domain socket in the Abstract Namespace
+ (no connection with filesystem pathnames)
+ --transport=arg (buffered) Transport: buffered, framed, http, evhttp, zlib
+ --protocol=arg (binary) Protocol: binary, compact, header, json
+ --multiplex Add TMultiplexedProtocol service name "ThriftTest"
+ --ssl Encrypted Transport using SSL
+ --zlib Wrap Transport with Zlib
+ -n=arg | --testloops=arg (1) Number of Tests
+ -t=arg | --threads=arg (1) Number of Test threads
+""")
+ }
+
+ open override func processArgument(name: String, value: String?) throws {
+ switch name {
+ case "--host":
+ guard value != nil else { throw ParserError.missingParameter(argument: name) }
+ host = value!
+ case "-n", "--testloops":
+ guard value != nil else { throw ParserError.missingParameter(argument: name) }
+ testLoops = Int(value!)
+ guard testLoops != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+ case "-t", "--threads":
+ guard value != nil else { throw ParserError.missingParameter(argument: name) }
+ threads = Int(value!)
+ guard threads != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+ default:
+ try super.processArgument(name: name, value: value)
+ }
+ }
+
+ open override func fillDefaults() {
+ super.fillDefaults()
+
+ if host == nil {
+ host = "localhost"
+ }
+
+ if testLoops == nil {
+ testLoops = 1
+ }
+
+ if threads == nil {
+ threads = 4
+ }
+ }
+}
+
+public class TestServerParameters: ParametersBase {
+ public var serverType: ServerType?
+ public var processorEvents = false
+ public var workers: Int?
+
+ public func printHelp() {
+ print("""
+Allowed options:
+ -h | --help produce help message
+ --port=arg (=9090) Port number to listen
+ --domain-socket=arg Unix Domain Socket (e.g. /tmp/ThriftTest.thrift)
+ --named-pipe=arg Windows Named Pipe (e.g. MyThriftPipe)
+ --server-type=arg (=simple) type of server, "simple", "thread-pool",
+ "threaded", or "nonblocking"
+ --transport=arg (=buffered) transport: buffered, framed, http, anonpipe, zlib
+ --protocol=arg (=binary) protocol: binary, compact, header, json
+ --multiplex Add TMultiplexedProtocol service name "ThriftTest"
+ --abstract-namespace Create the domain socket in the Abstract Namespace
+ (no connection with filesystem pathnames)
+ --ssl Encrypted Transport using SSL
+ --zlib Wrapped Transport using Zlib
+ --processor-events processor-events
+ -n=arg | --workers=arg (=4) Number of thread pools workers. Only valid for
+ thread-pool server type
+""")
+ }
+
+ open override func processArgument(name: String, value: String?) throws {
+ switch name {
+ case "--server-type":
+ guard value != nil else { throw ParserError.missingParameter(argument: name) }
+ serverType = ServerType(rawValue: value!)
+ guard serverType != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+ case "--processor-events":
+ processorEvents = true
+ case "-n", "--workers":
+ guard value != nil else { throw ParserError.missingParameter(argument: name) }
+ workers = Int(value!)
+ guard workers != nil else { throw ParserError.invalidParameter(argument: name, parameter: value!) }
+ default:
+ try super.processArgument(name: name, value: value)
+ }
+ }
+
+ open override func fillDefaults() {
+ super.fillDefaults()
+
+ if serverType == nil {
+ serverType = .simple
+ }
+
+ if workers == nil {
+ workers = 4
+ }
+ }
+
+ open override func checkSupported() throws {
+ try super.checkSupported()
+ guard serverType == .simple else { throw ParserError.unsupportedOption }
+ }
+}
+
diff --git a/test/swift/CrossTests/Sources/TestClient/main.swift b/test/swift/CrossTests/Sources/TestClient/main.swift
new file mode 100644
index 000000000..681798126
--- /dev/null
+++ b/test/swift/CrossTests/Sources/TestClient/main.swift
@@ -0,0 +1,400 @@
+// 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.
+
+import Foundation
+import Thrift
+import Common
+import XCTest
+
+enum Error: Int32 {
+ case baseTypes = 1
+ case structs = 2
+ case containers = 4
+ case exceptions = 8
+ case unknown = 64
+ case timeout = 128
+}
+
+class TestClient {
+ var client: ThriftTestClient
+ var resultCode: Int32 = 0
+
+ public init(parameters: TestClientParameters) throws {
+ let transport = try TestClient.getTransport(parameters: parameters)
+ let proto = try TestClient.getProtocol(parameters: parameters, transport: transport)
+ client = ThriftTestClient(inoutProtocol: proto)
+ }
+
+ static func getTransport(parameters: TestClientParameters) throws -> TTransport {
+ let socketTransport = try TSocketTransport(hostname: parameters.host!, port: parameters.port!)
+ if parameters.transport == .framed {
+ return TFramedTransport(transport: socketTransport)
+ }
+
+ if parameters.transport == .buffered {
+ return socketTransport
+ }
+
+ throw ParserError.unsupportedOption
+ }
+
+ static func getProtocol(parameters: TestClientParameters, transport: TTransport) throws -> TProtocol {
+ if parameters.proto == .binary {
+ return TBinaryProtocol(on: transport)
+ }
+
+ if parameters.proto == .compact {
+ return TCompactProtocol(on: transport)
+ }
+
+ throw ParserError.unsupportedOption
+ }
+
+ func run() throws {
+ do {
+ try testVoid()
+ try testString()
+ try testBool()
+ try testByte()
+ try testI32()
+ try testI64()
+ try testDouble()
+ try testBinary()
+ try testStruct()
+ try testNest()
+ try testMap()
+ try testSet()
+ try testList()
+ try testEnum()
+ try testTypedef()
+ try testMapMap()
+ try testInsanity()
+ try testMulti()
+ try testException()
+ try testMultiException()
+ // Swift generator doesn't yet support one way functions (THRIFT-5468)
+ /*try testOneway()
+ try testOnewayThenNormal()*/
+ try testUuid()
+
+ } catch let error {
+ print("\(error)")
+ resultCode |= Error.unknown.rawValue
+ }
+ exit(resultCode)
+ }
+
+ func testVoid() throws {
+ print("testVoid")
+ try client.testVoid()
+ }
+
+
+ func testString1(_ s1: String) throws {
+ print("testString(\(s1))")
+ let r1 = try client.testString(thing: s1)
+ print(r1)
+ if s1 != r1 {
+ resultCode |= Error.baseTypes.rawValue
+ }
+ }
+
+ func testString() throws {
+ try testString1(String(repeating: "Python", count: 20))
+ try testString1("")
+ try testString1("\t\n/\\\\\r{}:パイソン")
+ try testString1("""
+Afrikaans, Alemannisch, Aragonés, العربية, مصرى,
+Asturianu, Aymar aru, Azərbaycan, Башҡорт, Boarisch, Žemaitėška,
+Беларуская, Беларуская (тарашкевіца), Български, Bamanankan,
+বাংলা, Brezhoneg, Bosanski, Català, Mìng-dĕ̤ng-ngṳ̄, Нохчийн,
+Cebuano, ᏣᎳᎩ, Česky, Словѣ́ньскъ / ⰔⰎⰑⰂⰡⰐⰠⰔⰍⰟ, Чӑвашла, Cymraeg,
+Dansk, Zazaki, ދިވެހިބަސް, Ελληνικά, Emiliàn e rumagnòl, English,
+Esperanto, Español, Eesti, Euskara, فارسی, Suomi, Võro, Føroyskt,
+Français, Arpetan, Furlan, Frysk, Gaeilge, 贛語, Gàidhlig, Galego,
+Avañe'ẽ, ગુજરાતી, Gaelg, עברית, हिन्दी, Fiji Hindi, Hrvatski,
+Kreyòl ayisyen, Magyar, Հայերեն, Interlingua, Bahasa Indonesia,
+Ilokano, Ido, Íslenska, Italiano, 日本語, Lojban, Basa Jawa,
+ქართული, Kongo, Kalaallisut, ಕನ್ನಡ, 한국어, Къарачай-Малкъар,
+Ripoarisch, Kurdî, Коми, Kernewek, Кыргызча, Latina, Ladino,
+Lëtzebuergesch, Limburgs, Lingála, ລາວ, Lietuvių, Latviešu, Basa
+Banyumasan, Malagasy, Македонски, മലയാളം, मराठी, مازِرونی, Bahasa
+Melayu, Nnapulitano, Nedersaksisch, नेपाल भाषा, Nederlands, ‪
+Norsk (nynorsk)‬, ‪Norsk (bokmål)‬, Nouormand, Diné bizaad,
+Occitan, Иронау, Papiamentu, Deitsch, Polski, پنجابی, پښتو,
+Norfuk / Pitkern, Português, Runa Simi, Rumantsch, Romani, Română,
+Русский, Саха тыла, Sardu, Sicilianu, Scots, Sámegiella, Simple
+English, Slovenčina, Slovenščina, Српски / Srpski, Seeltersk,
+Svenska, Kiswahili, தமிழ், తెలుగు, Тоҷикӣ, ไทย, Türkmençe, Tagalog,
+Türkçe, Татарча/Tatarça, Українська, اردو, Tiếng Việt, Volapük,
+Walon, Winaray, 吴语, isiXhosa, ייִדיש, Yorùbá, Zeêuws, 中文,
+Bân-lâm-gú, 粵語
+""")
+ }
+
+ func testBool1(_ s1: Bool) throws {
+ print("testBool(\(s1))")
+ let r1 = try client.testBool(thing: s1)
+ print(r1)
+ if s1 != r1 {
+ resultCode |= Error.baseTypes.rawValue
+ }
+ }
+
+ func testBool() throws {
+ try testBool1(true)
+ try testBool1(false)
+ }
+
+ func testByte() throws {
+ print("testByte")
+ if try client.testByte(thing: 63) != 63 {
+ resultCode |= Error.baseTypes.rawValue
+ }
+ if try client.testByte(thing: -127) != -127 {
+ resultCode |= Error.baseTypes.rawValue
+ }
+ }
+
+ func testI32() throws {
+ print("testI32")
+ if try client.testI32(thing: -1) != -1 {
+ resultCode |= Error.baseTypes.rawValue
+ }
+ if try client.testI32(thing: 0) != 0 {
+ resultCode |= Error.baseTypes.rawValue
+ }
+ }
+
+ func testI64() throws {
+ print("testI64")
+ if try client.testI64(thing: 1) != 1 ||
+ client.testI64(thing: -34359738368) != -34359738368 {
+ resultCode |= Error.baseTypes.rawValue
+ }
+ }
+
+ func testDouble() throws {
+ print("testDouble")
+ for testValue in [-5.235098235, 0, -1, -0.000341012439638598279] {
+ if try client.testDouble(thing: testValue) != testValue {
+ resultCode |= Error.baseTypes.rawValue
+ }
+ }
+ }
+
+ func testBinary() throws {
+ print("testBinary")
+ let val = Data(Array(0...255))
+ if try client.testBinary(thing: val) != val {
+ resultCode |= Error.baseTypes.rawValue
+ }
+ }
+
+ func testStruct() throws {
+ print("testStruct")
+ let x = Xtruct(string_thing: "Zero", byte_thing: 1, i32_thing: -3, i64_thing: -5)
+ if try client.testStruct(thing: x) != x {
+ resultCode |= Error.structs.rawValue
+ }
+ }
+
+ func testNest() throws {
+ print("testNest")
+ let inner = Xtruct(string_thing: "Zero", byte_thing: 1, i32_thing: -3, i64_thing: -5)
+ let x = Xtruct2(byte_thing: 0, struct_thing: inner, i32_thing: 0)
+ if try client.testNest(thing: x) != x {
+ resultCode |= Error.structs.rawValue
+ }
+ }
+
+ func testMap() throws {
+ print("testMap")
+ let x = TMap<Int32, Int32>([0: 1, 1: 2, 2: 3, 3: 4, -1: -2])
+ if try client.testMap(thing: x) != x {
+ resultCode |= Error.containers.rawValue
+ }
+ }
+
+ func testSet() throws {
+ print("testSet")
+ let x = TSet<Int32>([8, 1, 42])
+ if try client.testSet(thing: x) != x {
+ resultCode |= Error.containers.rawValue
+ }
+ }
+
+ func testList() throws {
+ print("testList")
+ let x = TList<Int32>([1, 4, 9, -42])
+ if try client.testList(thing: x) != x {
+ resultCode |= Error.containers.rawValue
+ }
+ }
+
+ func testEnum() throws {
+ print("testEnum")
+ let x = Numberz.five
+ if try client.testEnum(thing: x) != x {
+ resultCode |= Error.containers.rawValue
+ }
+ }
+
+ func testTypedef() throws {
+ print("testTypedef")
+ let x = UserId(bitPattern: 0xffffffffffffff)
+ if try client.testTypedef(thing: x) != x {
+ resultCode |= Error.containers.rawValue
+ }
+ }
+
+ func testMapMap() throws {
+ print("testMapMap")
+ let x = TMap<Int32, TMap<Int32, Int32>>([
+ -4: [-4: -4, -3: -3, -2: -2, -1: -1],
+ 4: [4: 4, 3: 3, 2: 2, 1: 1]
+ ])
+ if try client.testMapMap(hello: 42) != x {
+ resultCode |= Error.containers.rawValue
+ }
+ }
+
+ func testInsanity() throws {
+ print("testInsanity()")
+ let argument = Insanity(userMap: [.eight: 8], xtructs: [])
+ let expected = TMap<UserId, TMap<Numberz, Insanity>>([
+ 1: [
+ .two: argument,
+ .three: argument
+ ],
+ 2: [
+ .six: Insanity(userMap: [:], xtructs: [])
+ ]
+ ])
+ if try client.testInsanity(argument: argument) != expected {
+ resultCode |= Error.containers.rawValue
+ }
+ }
+
+ func testMulti() throws {
+ print("testMulti")
+ let x = Xtruct(string_thing: "Hello2", byte_thing: 74, i32_thing: 0xff00ff, i64_thing: 0xffffffffd0d0)
+ if try client.testMulti(arg0: x.byte_thing, arg1: x.i32_thing, arg2: x.i64_thing, arg3: .init([0: "abc"]), arg4: Numberz.five, arg5: 0xf0f0f0) != x {
+ resultCode |= Error.containers.rawValue
+ }
+ }
+
+ func testException() throws {
+ print("testException")
+ try client.testException(arg: "Safe")
+ do {
+ try client.testException(arg: "Xception")
+ resultCode |= Error.exceptions.rawValue
+ } catch let error as Xception {
+ guard error.errorCode == 1001, error.message == "Xception" else {
+ resultCode |= Error.exceptions.rawValue
+ return
+ }
+ } catch {
+ resultCode |= Error.exceptions.rawValue
+ }
+
+ do {
+ try client.testException(arg: "TException")
+ resultCode |= Error.exceptions.rawValue
+ } catch is TError {
+
+ } catch {
+ resultCode |= Error.exceptions.rawValue
+ }
+
+ try client.testException(arg: "success")
+ }
+
+ func testMultiException() throws {
+ print("testMultiException")
+ do {
+ _ = try client.testMultiException(arg0: "Xception", arg1: "ignore")
+ } catch let error as Xception {
+ guard error.errorCode == 1001, error.message == "This is an Xception" else {
+ resultCode |= Error.exceptions.rawValue
+ return
+ }
+ } catch {
+ resultCode |= Error.exceptions.rawValue
+ }
+
+ do {
+ _ = try client.testMultiException(arg0: "Xception2", arg1: "ignore")
+ } catch let error as Xception2 {
+ guard error.errorCode == 2002, error.struct_thing.string_thing == "This is an Xception2" else {
+ resultCode |= Error.exceptions.rawValue
+ return
+ }
+ }
+
+ let y = try client.testMultiException(arg0: "success", arg1: "foobar")
+ if y.string_thing != "foobar" {
+ resultCode |= Error.exceptions.rawValue
+ }
+ }
+
+ // Swift generator doesn't yet support one way functions (THRIFT-5468)
+ /*func testOneway() throws {
+ print("testOneway")
+ let start = CACurrentMediaTime()
+ try client.testOneway(secondsToSleep: 1)
+ let end = CACurrentMediaTime()
+ let duration = end - start
+ let delta = abs(1 - duration)
+ print("oneway sleep took \(end - start) sec")
+
+ guard delta < 0.5 else {
+ print("oneway sleep took \(end - start) sec")
+ resultCode |= Error.unknown.rawValue
+ return
+ }
+ }
+
+ func testOnewayThenNormal() throws {
+ print("testOnewayThenNormal")
+ try client.testOneway(secondsToSleep: 1)
+ if try client.testString(thing: "Swift") != "Swift" {
+ resultCode |= Error.baseTypes.rawValue
+ }
+ }*/
+
+ func testUuid() throws {
+ let uuid = UUID()
+ guard try client.testUuid(thing: uuid) == uuid else {
+ resultCode |= Error.baseTypes.rawValue
+ return
+ }
+ }
+}
+
+
+let parameters = try TestClientParameters(arguments: CommandLine.arguments)
+
+if parameters.showHelp {
+ parameters.printHelp()
+ exit(0)
+}
+
+Thread.sleep(forTimeInterval: 1)
+
+try TestClient(parameters: parameters).run()
diff --git a/test/swift/CrossTests/Sources/TestServer/ThriftTestService.swift b/test/swift/CrossTests/Sources/TestServer/ThriftTestService.swift
new file mode 100644
index 000000000..5548e9030
--- /dev/null
+++ b/test/swift/CrossTests/Sources/TestServer/ThriftTestService.swift
@@ -0,0 +1,389 @@
+// 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.
+
+import Foundation
+import Thrift
+import Common
+
+class ThriftTestImpl : ThriftTest {
+
+ /// Prints "testVoid()" and returns nothing.
+ ///
+ /// - Throws:
+ func testVoid() throws {
+ print("testVoid()")
+ }
+
+ /// Prints 'testString("%s")' with thing as '%s'
+ /// @param string thing - the string to print
+ /// @return string - returns the string 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: String
+ /// - Throws:
+ func testString(thing: String) throws -> String {
+ print("testString(\"\(thing)\")")
+ return thing
+ }
+
+ /// Prints 'testBool("%s")' where '%s' with thing as 'true' or 'false'
+ /// @param bool thing - the bool data to print
+ /// @return bool - returns the bool 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: Bool
+ /// - Throws:
+ func testBool(thing: Bool) throws -> Bool {
+ print("testBool\"(\(thing ? "true" : "false")\")")
+ return thing
+ }
+
+ /// Prints 'testByte("%d")' with thing as '%d'
+ /// The types i8 and byte are synonyms, use of i8 is encouraged, byte still exists for the sake of compatibility.
+ /// @param byte thing - the i8/byte to print
+ /// @return i8 - returns the i8/byte 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: Int8
+ /// - Throws:
+ func testByte(thing: Int8) throws -> Int8 {
+ print("testByte(\"\(thing)\")")
+ return thing
+ }
+
+
+ /// Prints 'testI32("%d")' with thing as '%d'
+ /// @param i32 thing - the i32 to print
+ /// @return i32 - returns the i32 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: Int32
+ /// - Throws:
+ func testI32(thing: Int32) throws -> Int32 {
+ print("testI32(\"\(thing)\")")
+ return thing
+ }
+
+
+ /// Prints 'testI64("%d")' with thing as '%d'
+ /// @param i64 thing - the i64 to print
+ /// @return i64 - returns the i64 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: Int64
+ /// - Throws:
+ func testI64(thing: Int64) throws -> Int64 {
+ print("testI64(\"\(thing)\")")
+ return thing
+ }
+
+
+ /// Prints 'testDouble("%f")' with thing as '%f'
+ /// @param double thing - the double to print
+ /// @return double - returns the double 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: Double
+ /// - Throws:
+ func testDouble(thing: Double) throws -> Double {
+ print("testDouble(\"\(thing)\")")
+ return thing
+ }
+
+
+ /// Prints 'testBinary("%s")' where '%s' is a hex-formatted string of thing's data
+ /// @param binary thing - the binary data to print
+ /// @return binary - returns the binary 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: Data
+ /// - Throws:
+ func testBinary(thing: Data) throws -> Data {
+ print("testBinary(\"\(thing)\")")
+ return thing
+ }
+
+
+ /// Prints 'testStruct("{%s}")' where thing has been formatted into a string of comma separated values
+ /// @param Xtruct thing - the Xtruct to print
+ /// @return Xtruct - returns the Xtruct 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: Xtruct
+ /// - Throws:
+ func testStruct(thing: Xtruct) throws -> Xtruct {
+ print("testStruct({\([thing.string_thing, "\(thing.byte_thing)", "\(thing.i32_thing)", "\(thing.i64_thing)"].joined(separator: ", "))})")
+ return thing
+ }
+
+
+ /// Prints 'testNest("{%s}")' where thing has been formatted into a string of the nested struct
+ /// @param Xtruct2 thing - the Xtruct2 to print
+ /// @return Xtruct2 - returns the Xtruct2 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: Xtruct2
+ /// - Throws:
+ func testNest(thing: Xtruct2) throws -> Xtruct2 {
+ print("testNest(\(thing)")
+ return thing
+ }
+
+
+ /// Prints 'testMap("{%s")' where thing has been formatted into a string of 'key => value' pairs
+ /// separated by commas and new lines
+ /// @param map<i32,i32> thing - the map<i32,i32> to print
+ /// @return map<i32,i32> - returns the map<i32,i32> 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: TMap<Int32, Int32>
+ /// - Throws:
+ func testMap(thing: TMap<Int32, Int32>) throws -> TMap<Int32, Int32> {
+ print("testMap(\(thing)")
+ return thing
+ }
+
+
+ /// Prints 'testStringMap("{%s}")' where thing has been formatted into a string of 'key => value' pairs
+ /// separated by commas and new lines
+ /// @param map<string,string> thing - the map<string,string> to print
+ /// @return map<string,string> - returns the map<string,string> 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: TMap<String, String>
+ /// - Throws:
+ func testStringMap(thing: TMap<String, String>) throws -> TMap<String, String> {
+ print("testStringMap(\(thing)")
+ return thing
+ }
+
+
+ /// Prints 'testSet("{%s}")' where thing has been formatted into a string of values
+ /// separated by commas and new lines
+ /// @param set<i32> thing - the set<i32> to print
+ /// @return set<i32> - returns the set<i32> 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: TSet<Int32>
+ /// - Throws:
+ func testSet(thing: TSet<Int32>) throws -> TSet<Int32> {
+ print("testSet\(thing)")
+ return thing
+ }
+
+
+ /// Prints 'testList("{%s}")' where thing has been formatted into a string of values
+ /// separated by commas and new lines
+ /// @param list<i32> thing - the list<i32> to print
+ /// @return list<i32> - returns the list<i32> 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: TList<Int32>
+ /// - Throws:
+ func testList(thing: TList<Int32>) throws -> TList<Int32> {
+ print("testList\(thing)")
+ return thing
+ }
+
+
+ /// Prints 'testEnum("%d")' where thing has been formatted into its numeric value
+ /// @param Numberz thing - the Numberz to print
+ /// @return Numberz - returns the Numberz 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: Numberz
+ /// - Throws:
+ func testEnum(thing: Numberz) throws -> Numberz {
+ print("testEnum\(thing.rawValue)")
+ return thing
+ }
+
+
+ /// Prints 'testTypedef("%d")' with thing as '%d'
+ /// @param UserId thing - the UserId to print
+ /// @return UserId - returns the UserId 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: UserId
+ /// - Throws:
+ func testTypedef(thing: UserId) throws -> UserId {
+ print("testTypedef(\(thing)")
+ return thing
+ }
+
+
+ /// Prints 'testMapMap("%d")' with hello as '%d'
+ /// @param i32 hello - the i32 to print
+ /// @return map<i32,map<i32,i32>> - returns a dictionary with these values:
+ /// {-4 => {-4 => -4, -3 => -3, -2 => -2, -1 => -1, }, 4 => {1 => 1, 2 => 2, 3 => 3, 4 => 4, }, }
+ ///
+ /// - Parameters:
+ /// - hello:
+ /// - Returns: TMap<Int32, TMap<Int32, Int32>>
+ /// - Throws:
+ func testMapMap(hello: Int32) throws -> TMap<Int32, TMap<Int32, Int32>> {
+ print("testMapMap(\(hello)")
+ return TMap<Int32, TMap<Int32, Int32>>([
+ -4: [-4: -4, -3: -3, -2: -2, -1: -1],
+ 4: [4: 4, 3: 3, 2: 2, 1: 1]
+ ])
+ }
+
+
+ /// So you think you've got this all worked out, eh?
+ /// Creates a map with these values and prints it out:
+ /// { 1 => { 2 => argument,
+ /// 3 => argument,
+ /// },
+ /// 2 => { 6 => <empty Insanity struct>, },
+ /// }
+ /// @return map<UserId, map<Numberz,Insanity>> - a map with the above values
+ ///
+ /// - Parameters:
+ /// - argument:
+ /// - Returns: TMap<UserId, TMap<Numberz, Insanity>>
+ /// - Throws:
+ func testInsanity(argument: Insanity) throws -> TMap<UserId, TMap<Numberz, Insanity>> {
+ return TMap<UserId, TMap<Numberz, Insanity>>([
+ 1: [
+ .two: argument,
+ .three: argument
+ ],
+ 2: [
+ .six: Insanity(userMap: [:], xtructs: [])
+ ]
+ ])
+ }
+
+
+ /// Prints 'testMulti()'
+ /// @param i8 arg0 -
+ /// @param i32 arg1 -
+ /// @param i64 arg2 -
+ /// @param map<i16, string> arg3 -
+ /// @param Numberz arg4 -
+ /// @param UserId arg5 -
+ /// @return Xtruct - returns an Xtruct with string_thing = "Hello2, byte_thing = arg0, i32_thing = arg1
+ /// and i64_thing = arg2
+ ///
+ /// - Parameters:
+ /// - arg0:
+ /// - arg1:
+ /// - arg2:
+ /// - arg3:
+ /// - arg4:
+ /// - arg5:
+ /// - Returns: Xtruct
+ /// - Throws:
+ func testMulti(arg0: Int8, arg1: Int32, arg2: Int64, arg3: TMap<Int16, String>, arg4: Numberz, arg5: UserId) throws -> Xtruct {
+ print("testMulti()")
+ return Xtruct(string_thing: "Hello2", byte_thing: arg0, i32_thing: arg1, i64_thing: arg2)
+ }
+
+
+ /// Print 'testException(%s)' with arg as '%s'
+ /// @param string arg - a string indication what type of exception to throw
+ /// if arg == "Xception" throw Xception with errorCode = 1001 and message = arg
+ /// else if arg == "TException" throw TException
+ /// else do not throw anything
+ ///
+ /// - Parameters:
+ /// - arg:
+ /// - Throws: Xception
+ func testException(arg: String) throws {
+ print("testException(\(arg)")
+ if arg == "Xception" {
+ throw Xception(errorCode: 1001, message: arg)
+ } else if arg == "TException" {
+ throw TApplicationError() // is type TError (TException Swift equiv)
+ }
+ }
+
+
+ /// Print 'testMultiException(%s, %s)' with arg0 as '%s' and arg1 as '%s'
+ /// @param string arg - a string indicating what type of exception to throw
+ /// if arg0 == "Xception" throw Xception with errorCode = 1001 and message = "This is an Xception"
+ /// else if arg0 == "Xception2" throw Xception2 with errorCode = 2002 and struct_thing.string_thing = "This is an Xception2"
+ /// else do not throw anything
+ /// @return Xtruct - an Xtruct with string_thing = arg1
+ ///
+ /// - Parameters:
+ /// - arg0:
+ /// - arg1:
+ /// - Returns: Xtruct
+ /// - Throws: Xception, Xception2
+ func testMultiException(arg0: String, arg1: String) throws -> Xtruct {
+ print("testMultiException(\(arg0), \(arg1)")
+ if arg0 == "Xception" {
+ throw Xception(errorCode: 1001, message: "This is an Xception")
+ } else if arg0 == "Xception2" {
+ throw Xception2(errorCode: 2002, struct_thing: Xtruct(string_thing: "This is an Xception2", byte_thing: 0, i32_thing: 0, i64_thing: 0))
+ }
+ return Xtruct(string_thing: arg1, byte_thing: 0, i32_thing: 0, i64_thing: 0)
+ }
+
+
+ /// Print 'testOneway(%d): Sleeping...' with secondsToSleep as '%d'
+ /// sleep 'secondsToSleep'
+ /// Print 'testOneway(%d): done sleeping!' with secondsToSleep as '%d'
+ /// @param i32 secondsToSleep - the number of seconds to sleep
+ ///
+ /// - Parameters:
+ /// - secondsToSleep:
+ /// - Throws:
+ func testOneway(secondsToSleep: Int32) throws {
+ print("testOneway(\(secondsToSleep): Sleeping...")
+ Thread.sleep(forTimeInterval: TimeInterval(secondsToSleep))
+ }
+
+ func testUuid(thing: UUID) throws -> UUID {
+ print("testUuid(\(thing))")
+ return thing
+ }
+}
+
+class SecondServiceImpl : SecondService {
+
+ /// Prints 'testString("%s")' with thing as '%s'
+ /// @param string thing - the string to print
+ /// @return string - returns the string 'thing'
+ ///
+ /// - Parameters:
+ /// - thing:
+ /// - Returns: String
+ /// - Throws:
+ func secondtestString(thing: String) throws -> String {
+ print("testString(\"\(thing)\")")
+ return thing
+ }
+}
+
diff --git a/test/swift/CrossTests/Sources/TestServer/main.swift b/test/swift/CrossTests/Sources/TestServer/main.swift
new file mode 100644
index 000000000..15564d3e6
--- /dev/null
+++ b/test/swift/CrossTests/Sources/TestServer/main.swift
@@ -0,0 +1,59 @@
+// 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.
+
+import Foundation
+import Thrift
+import Common
+
+class TestServer {
+ var server: Any?
+
+ func run() throws {
+ let parameters = try TestServerParameters(arguments: CommandLine.arguments)
+
+ if parameters.showHelp {
+ parameters.printHelp()
+ return
+ }
+
+ let service = ThriftTestImpl()
+ let processor = ThriftTestProcessor(service: service)
+
+
+ switch (parameters.proto, parameters.transport) {
+ case (.binary, .buffered):
+ let proto = TBinaryProtocol.self
+ server = try TSocketServer(port: parameters.port!, inProtocol: proto, outProtocol: proto, processor: processor)
+ case (.binary, .framed):
+ let proto = TBinaryProtocol.self
+ server = try TFramedSocketServer(port: parameters.port!, inProtocol: proto, outProtocol: proto, processor: processor)
+ case (.compact, .buffered):
+ let proto = TCompactProtocol.self
+ server = try TSocketServer(port: parameters.port!, inProtocol: proto, outProtocol: proto, processor: processor)
+ case (.compact, .framed):
+ let proto = TCompactProtocol.self
+ server = try TFramedSocketServer(port: parameters.port!, inProtocol: proto, outProtocol: proto, processor: processor)
+ default:
+ throw ParserError.unsupportedOption
+ }
+ }
+}
+
+let server = TestServer()
+try server.run()
+
+RunLoop.main.run()
diff --git a/test/swift/Makefile.am b/test/swift/Makefile.am
new file mode 100644
index 000000000..20c2fe5c9
--- /dev/null
+++ b/test/swift/Makefile.am
@@ -0,0 +1,24 @@
+#
+# 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.
+#
+
+SUBDIRS = CrossTests
+
+precross:
+ $(MAKE) -C CrossTests precross
+
diff --git a/test/tests.json b/test/tests.json
index cce774a4b..7eb9f65b9 100644
--- a/test/tests.json
+++ b/test/tests.json
@@ -757,5 +757,22 @@
"buffered"
],
"workdir": "../lib/nodets/test"
+ },
+ {
+ "name": "swift",
+ "server": {
+ "command": ["TestServer"],
+ "workdir": "swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug",
+ "protocols": ["binary", "compact"],
+ "transports": ["buffered", "framed"],
+ "sockets": ["ip"]
+ },
+ "client": {
+ "command": ["TestClient"],
+ "workdir": "swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug",
+ "protocols": ["binary", "compact"],
+ "transports": ["buffered", "framed"],
+ "sockets": ["ip"]
+ }
}
]