diff options
author | Kino Roy <kinoroy@users.noreply.github.com> | 2022-10-07 23:13:01 -0700 |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-10-08 14:13:01 +0800 |
commit | a9da9eb05993a235df999226303bc594e4778805 (patch) | |
tree | 173200fc08231818b9bdea1943fda0be11cd560f /test | |
parent | ed542c63fc4360ef6d397df3cd92d6623d713e1e (diff) | |
download | thrift-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.am | 6 | ||||
-rw-r--r-- | test/swift/CrossTests/Makefile.am | 34 | ||||
-rw-r--r-- | test/swift/CrossTests/Package.swift | 44 | ||||
-rw-r--r-- | test/swift/CrossTests/Sources/Common/Parameters.swift | 256 | ||||
-rw-r--r-- | test/swift/CrossTests/Sources/TestClient/main.swift | 400 | ||||
-rw-r--r-- | test/swift/CrossTests/Sources/TestServer/ThriftTestService.swift | 389 | ||||
-rw-r--r-- | test/swift/CrossTests/Sources/TestServer/main.swift | 59 | ||||
-rw-r--r-- | test/swift/Makefile.am | 24 | ||||
-rw-r--r-- | test/tests.json | 17 |
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"] + } } ] |