diff options
26 files changed, 1501 insertions, 39 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 99c92aad0..2187776d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -183,6 +183,66 @@ jobs: lib/kotlin/cross-test-server/build/install/TestServer/ retention-days: 3 + lib-swift: + needs: compiler + runs-on: ubuntu-20.04 + steps: + - uses: actions/checkout@v3 + + - name: Run bootstrap + run: ./bootstrap.sh + + - name: Run configure + run: | + ./configure \ + --disable-debug \ + --disable-tests \ + --disable-dependency-tracking \ + --without-cpp \ + --without-c_glib \ + --without-java \ + --without-kotlin \ + --without-python \ + --without-py3 \ + --without-ruby \ + --without-haxe \ + --without-netstd \ + --without-perl \ + --without-php \ + --without-php_extension \ + --without-dart \ + --without-erlang \ + --without-go \ + --without-d \ + --without-nodejs \ + --without-nodets \ + --without-lua \ + --without-rs \ + --with-swift + + - uses: actions/download-artifact@v3 + with: + name: thrift-compiler + path: compiler/cpp + + - name: Run thrift-compiler + run: | + chmod a+x compiler/cpp/thrift + compiler/cpp/thrift -version + + - name: Run make precross for swift + run: make -C test/swift precross + + - name: Upload swift precross artifacts + uses: actions/upload-artifact@v3 + with: + name: swift-precross + if-no-files-found: error + path: | + test/swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug/TestServer + test/swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug/TestClient + retention-days: 3 + lib-rust: needs: compiler runs-on: ubuntu-20.04 @@ -275,6 +335,7 @@ jobs: cross-test: needs: - lib-java-kotlin + - lib-swift - lib-rust runs-on: ubuntu-20.04 steps: @@ -301,6 +362,12 @@ jobs: name: kotlin-precross path: lib/kotlin + - name: Download swift precross artifacts + uses: actions/download-artifact@v3 + with: + name: swift-precross + path: test/swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug + - name: Download rust precross artifacts uses: actions/download-artifact@v3 with: @@ -313,12 +380,13 @@ jobs: lib/java/build/run* \ lib/kotlin/cross-test-client/build/install/TestClient/bin/* \ lib/kotlin/cross-test-server/build/install/TestServer/bin/* \ + test/swift/CrossTests/.build/x86_64-unknown-linux-gnu/debug/* \ test/rs/bin/* - name: Run cross test env: THRIFT_CROSSTEST_CONCURRENCY: 4 - PRECROSS_LANGS: java,kotlin,rs + PRECROSS_LANGS: java,kotlin,rs,swift run: | python test/test.py \ --retry-count 5 \ diff --git a/.gitignore b/.gitignore index a469311a3..d4c5dd792 100644 --- a/.gitignore +++ b/.gitignore @@ -49,6 +49,7 @@ test-driver erl_crash.dump project.lock.json +.Dockerfile.sha512 .sonar .DS_Store .svn @@ -388,6 +389,7 @@ project.lock.json /test/rs/target/ /test/rs/*.iml /test/rs/**/*.iml +/test/swift/CrossTests/.build /lib/cl/backport-update.zip /lib/cl/lib /tutorial/cl/quicklisp.lisp diff --git a/Makefile.am b/Makefile.am index 95c610e56..505951bf2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -78,7 +78,7 @@ empty := space := $(empty) $(empty) comma := , -CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_CL@ @MAYBE_D@ @MAYBE_JAVA@ @MAYBE_PYTHON@ @MAYBE_PY3@ @MAYBE_RUBY@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@ @MAYBE_LUA@ @MAYBE_RS@ @MAYBE_NETSTD@ @MAYBE_NODETS@ @MAYBE_KOTLIN@ +CROSS_LANGS = @MAYBE_CPP@ @MAYBE_C_GLIB@ @MAYBE_CL@ @MAYBE_D@ @MAYBE_JAVA@ @MAYBE_PYTHON@ @MAYBE_PY3@ @MAYBE_RUBY@ @MAYBE_PERL@ @MAYBE_PHP@ @MAYBE_GO@ @MAYBE_NODEJS@ @MAYBE_DART@ @MAYBE_ERLANG@ @MAYBE_LUA@ @MAYBE_RS@ @MAYBE_NETSTD@ @MAYBE_NODETS@ @MAYBE_KOTLIN@ @MAYBE_SWIFT@ CROSS_LANGS_COMMA_SEPARATED = $(subst $(space),$(comma),$(CROSS_LANGS)) if WITH_PY3 diff --git a/compiler/cpp/src/thrift/generate/t_swift_generator.cc b/compiler/cpp/src/thrift/generate/t_swift_generator.cc index 834e31dc8..78fb1d853 100644 --- a/compiler/cpp/src/thrift/generate/t_swift_generator.cc +++ b/compiler/cpp/src/thrift/generate/t_swift_generator.cc @@ -1067,6 +1067,7 @@ void t_swift_generator::generate_swift_union_reader(ostream& out, t_struct* tstr switch (tbase) { case t_base_type::TYPE_STRING: case t_base_type::TYPE_DOUBLE: + case t_base_type::TYPE_UUID: padding = " "; break; @@ -1172,6 +1173,7 @@ void t_swift_generator::generate_swift_struct_reader(ostream& out, switch (tbase) { case t_base_type::TYPE_STRING: case t_base_type::TYPE_DOUBLE: + case t_base_type::TYPE_UUID: padding = " "; break; @@ -2591,6 +2593,8 @@ string t_swift_generator::base_type_name(t_base_type* type) { return "Int64"; case t_base_type::TYPE_DOUBLE: return "Double"; + case t_base_type::TYPE_UUID: + return "UUID"; default: throw "compiler error: no Swift name for base type " + t_base_type::t_base_name(tbase); } @@ -2629,6 +2633,9 @@ void t_swift_generator::render_const_value(ostream& out, } out << ")"; break; + case t_base_type::TYPE_UUID: + out << "UUID(uuidString: \"" << get_escaped_string(value) << "\")"; + break; default: throw "compiler error: no const of base type " + t_base_type::t_base_name(tbase); } @@ -3136,6 +3143,8 @@ string t_swift_generator::type_to_enum(t_type* type, bool qualified) { return result + "i64"; case t_base_type::TYPE_DOUBLE: return result + "double"; + case t_base_type::TYPE_UUID: + return result + "uuid"; default: throw "compiler error: unhandled type"; } @@ -3170,6 +3179,8 @@ string t_swift_generator::type_to_enum(t_type* type, bool qualified) { return result + "I64"; case t_base_type::TYPE_DOUBLE: return result + "DOUBLE"; + case t_base_type::TYPE_UUID: + return result + "UUID"; default: throw "compiler error: unhandled type"; } diff --git a/configure.ac b/configure.ac index 9a71fc7e4..12c20de0b 100644 --- a/configure.ac +++ b/configure.ac @@ -821,6 +821,8 @@ AC_CONFIG_FILES([ test/py.tornado/Makefile test/rb/Makefile test/rs/Makefile + test/swift/Makefile + test/swift/CrossTests/Makefile tutorial/Makefile tutorial/c_glib/Makefile tutorial/cl/Makefile diff --git a/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java b/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java index 8d0c7aa57..6657df6cc 100644 --- a/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java +++ b/lib/java/src/test/java/org/apache/thrift/server/ServerTestBase.java @@ -246,7 +246,7 @@ public abstract class ServerTestBase { first_map.put(Numberz.TWO, argument); first_map.put(Numberz.THREE, argument); - Insanity looney = new Insanity(); + Insanity looney = new Insanity(new HashMap<>(), Arrays.asList()); second_map.put(Numberz.SIX, looney); Map<Long, Map<Numberz, Insanity>> insane = new HashMap<>(); diff --git a/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt b/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt index ab415b00b..b7f38d7fe 100644 --- a/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt +++ b/lib/kotlin/cross-test-server/src/main/kotlin/org/apache/thrift/test/TestHandler.kt @@ -183,7 +183,11 @@ class TestHandler : ThriftTest { val secondMap = mutableMapOf<Numberz, Insanity>() firstMap[Numberz.TWO] = argument firstMap[Numberz.THREE] = argument - val looney = Insanity() + val looney = + Insanity().apply { + userMap = HashMap() + xtructs = listOf() + } secondMap[Numberz.SIX] = looney val insane: MutableMap<Long, Map<Numberz, Insanity>> = HashMap() insane[1L] = firstMap @@ -241,11 +245,20 @@ class TestHandler : ThriftTest { } else if (arg0 == "Xception2") { val x = Xception2() x.errorCode = 2002 - x.struct_thing = Xtruct().apply { string_thing = "This is an Xception2" } + x.struct_thing = + Xtruct().apply { + string_thing = "This is an Xception2" + byte_thing = 0 + i32_thing = 0 + i64_thing = 0 + } throw x } val result = Xtruct() result.string_thing = arg1 + result.byte_thing = 0 + result.i32_thing = 0 + result.i64_thing = 0 return result } diff --git a/lib/swift/Makefile.am b/lib/swift/Makefile.am index 6b88b06a7..6a6644ac0 100644 --- a/lib/swift/Makefile.am +++ b/lib/swift/Makefile.am @@ -30,7 +30,7 @@ clean-local: rm -rf .build precross: - swift + swift build check-local: swift test diff --git a/lib/swift/Sources/LinuxHelper.swift b/lib/swift/Sources/LinuxHelper.swift index 66d92bb4d..83603f879 100644 --- a/lib/swift/Sources/LinuxHelper.swift +++ b/lib/swift/Sources/LinuxHelper.swift @@ -24,10 +24,6 @@ import CoreFoundation #if os(Linux) /// Extensions for Linux for incomplete Foundation API's. /// swift-corelibs-foundation is not yet 1:1 with OSX/iOS Foundation - -extension CFSocketError { - public static let success = kCFSocketSuccess -} extension UInt { public static func &(lhs: UInt, rhs: Int) -> UInt { diff --git a/lib/swift/Sources/TBinaryProtocol.swift b/lib/swift/Sources/TBinaryProtocol.swift index 85acce045..766027e72 100644 --- a/lib/swift/Sources/TBinaryProtocol.swift +++ b/lib/swift/Sources/TBinaryProtocol.swift @@ -191,6 +191,16 @@ public class TBinaryProtocol: TProtocol { return buff[0] } + public func read() throws -> Int8 { + var buff = Data() + try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { + buff = try self.transport.readAll(size: 1) + } + return buff.withUnsafeBytes { pntr in + return pntr.load(as: Int8.self) + } + } + public func read() throws -> Int16 { var buff = Data() try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { @@ -246,6 +256,20 @@ public class TBinaryProtocol: TProtocol { return data } + public func read() throws -> UUID { + let data = try self.transport.readAll(size: 16) + let lsb = data[0..<8] + let msb = data[8..<16] + + var id = UUID().uuid + withUnsafeMutableBytes(of: &id) { pntr in + var copyData = msb + copyData.append(lsb) + copyData.copyBytes(to: pntr) + } + return UUID(uuid: id) + } + // Write methods public func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws { @@ -332,6 +356,14 @@ public class TBinaryProtocol: TProtocol { } } + public func write(_ value: Int8) throws { + var value = value + let buff = Data(bytes: &value, count: MemoryLayout<Int8>.size(ofValue: value)) + try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { + try self.transport.write(data: buff) + } + } + public func write(_ value: Int16) throws { var buff = Data() buff.append(Data([UInt8(0xff & (value >> 8))])) @@ -381,4 +413,18 @@ public class TBinaryProtocol: TProtocol { try self.transport.write(data: data) } } + + public func write(_ value: UUID) throws { + let data = withUnsafePointer(to: value.uuid) { + Data(bytes: $0, count: MemoryLayout.size(ofValue: value.uuid)) + } + let msb = data[0..<8] + let lsb = data[8..<16] + + var buff = Data() + buff.append(lsb) + buff.append(msb) + + try self.transport.write(data: buff) + } } diff --git a/lib/swift/Sources/TCompactProtocol.swift b/lib/swift/Sources/TCompactProtocol.swift index 81a51f551..482178e8e 100644 --- a/lib/swift/Sources/TCompactProtocol.swift +++ b/lib/swift/Sources/TCompactProtocol.swift @@ -34,6 +34,7 @@ public enum TCType: UInt8 { case set = 0x0A case map = 0x0B case `struct` = 0x0C + case uuid = 0x0D public static let typeMask: UInt8 = 0xE0 // 1110 0000 public static let typeBits: UInt8 = 0x07 // 0000 0111 @@ -188,6 +189,7 @@ public class TCompactProtocol: TProtocol { case .set: return .set; case .map: return .map; case .struct: return .struct; + case .uuid: return .uuid; } } @@ -207,7 +209,8 @@ public class TCompactProtocol: TProtocol { case .set: return .set case .list: return .list case .utf8: return .binary - case .utf16: return .binary + //case .utf16: return .binary + case .uuid: return .uuid } } @@ -261,7 +264,8 @@ public class TCompactProtocol: TProtocol { guard let mtype = TMessageType(rawValue: Int32(type)) else { throw TProtocolError(message: "Unknown TMessageType value: \(type)") } - let sequenceId = try readVarint32() + let varint = zigZagToi32(try readVarint32()) + let sequenceId = Int32(varint) let name: String = try read() return (name, mtype, Int32(sequenceId)) @@ -351,6 +355,16 @@ public class TCompactProtocol: TProtocol { return buff } + public func read() throws -> Int8 { + var buff = Data() + try ProtocolTransportTry(error: TProtocolError(message: "Transport Read Failed")) { + buff = try self.transport.readAll(size: 1) + } + return buff.withUnsafeBytes { pntr in + return pntr.load(as: Int8.self) + } + } + public func read() throws -> Int16 { let v = try readVarint32() return Int16(zigZagToi32(v)) @@ -381,6 +395,20 @@ public class TCompactProtocol: TProtocol { return try readBinary(Int(length)) } + public func read() throws -> UUID { + let data = try self.transport.readAll(size: 16) + let lsb = data[0..<data.count/2] + let msb = data[(data.count/2)..<data.count] + + var id = UUID().uuid + withUnsafeMutableBytes(of: &id) { pntr in + var copyData = msb + copyData.append(lsb) + copyData.copyBytes(to: pntr) + } + return UUID(uuid: id) + } + public func readMapBegin() throws -> (TType, TType, Int32) { var keyAndValueType: UInt8 = 8 let size = try readVarint32() @@ -423,7 +451,7 @@ public class TCompactProtocol: TProtocol { (UInt8((UInt32(messageType.rawValue) << UInt32(TCType.typeShiftAmount))) & TCType.typeMask) try writebyteDirect(nextByte) - try writeVarint32(UInt32(sequenceID)) + try writeVarint32(i32ToZigZag(sequenceID)) try write(name) currentMessageName = name @@ -536,7 +564,15 @@ public class TCompactProtocol: TProtocol { public func write(_ value: UInt8) throws { try writebyteDirect(value) } - + + public func write(_ value: Int8) throws { + var value = value + let buff = Data(bytes: &value, count: MemoryLayout<Int8>.size(ofValue: value)) + try ProtocolTransportTry(error: TProtocolError(message: "Transport write failed")) { + try self.transport.write(data: buff) + } + } + public func write(_ value: Int16) throws { try writeVarint32(i32ToZigZag(Int32(value))) } @@ -565,4 +601,18 @@ public class TCompactProtocol: TProtocol { try self.transport.write(data: data) } } + + public func write(_ value: UUID) throws { + let data = withUnsafePointer(to: value.uuid) { + Data(bytes: $0, count: MemoryLayout.size(ofValue: value.uuid)) + } + let msb = data[0..<data.count/2] + let lsb = data[(data.count/2)..<data.count] + + var buff = Data() + buff.append(lsb) + buff.append(msb) + + try self.transport.write(data: buff) + } } diff --git a/lib/swift/Sources/TMap.swift b/lib/swift/Sources/TMap.swift index 8f520673b..7a936301d 100644 --- a/lib/swift/Sources/TMap.swift +++ b/lib/swift/Sources/TMap.swift @@ -180,5 +180,5 @@ public func ==<Key, Value>(lhs: TMap<Key,Value>, rhs: TMap<Key, Value>) -> Bool if lhs.count != rhs.count { return false } - return lhs.storage.elementsEqual(rhs.storage) { $0.key == $1.key && $0.value == $1.value } + return lhs.storage == rhs.storage } diff --git a/lib/swift/Sources/TProtocol.swift b/lib/swift/Sources/TProtocol.swift index b4e5dbe73..87e3bc5c1 100644 --- a/lib/swift/Sources/TProtocol.swift +++ b/lib/swift/Sources/TProtocol.swift @@ -41,7 +41,8 @@ public enum TType: Int32 { case set = 14 case list = 15 case utf8 = 16 - case utf16 = 17 + //case utf16 = 17 + case uuid = 17 } public protocol TProtocol { @@ -65,12 +66,14 @@ public protocol TProtocol { func read() throws -> String func read() throws -> Bool func read() throws -> UInt8 + func read() throws -> Int8 func read() throws -> Int16 func read() throws -> Int32 func read() throws -> Int64 func read() throws -> Double func read() throws -> Data - + func read() throws -> UUID + // Writing methods func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws @@ -90,11 +93,13 @@ public protocol TProtocol { func write(_ value: String) throws func write(_ value: Bool) throws func write(_ value: UInt8) throws + func write(_ value: Int8) throws func write(_ value: Int16) throws func write(_ value: Int32) throws func write(_ value: Int64) throws func write(_ value: Double) throws func write(_ value: Data) throws + func write(_ value: UUID) throws } public extension TProtocol { @@ -132,12 +137,13 @@ public extension TProtocol { func skip(type: TType) throws { switch type { case .bool: _ = try read() as Bool - case .i8: _ = try read() as UInt8 + case .i8: _ = try read() as Int8 case .i16: _ = try read() as Int16 case .i32: _ = try read() as Int32 case .i64: _ = try read() as Int64 case .double: _ = try read() as Double case .string: _ = try read() as String + case .uuid: _ = try read() as UUID case .struct: _ = try readStructBegin() diff --git a/lib/swift/Sources/TProtocolDecorator.swift b/lib/swift/Sources/TProtocolDecorator.swift index b1b1480db..e831f27a8 100644 --- a/lib/swift/Sources/TProtocolDecorator.swift +++ b/lib/swift/Sources/TProtocolDecorator.swift @@ -92,6 +92,10 @@ class TProtocolDecorator: TProtocol { func read() throws -> UInt8 { return try proto.read() } + + func read() throws -> Int8 { + return try proto.read() + } func read() throws -> Int16 { return try proto.read() @@ -112,6 +116,10 @@ class TProtocolDecorator: TProtocol { func read() throws -> Data { return try proto.read() } + + func read() throws -> UUID { + return try proto.read() + } func writeMessageBegin(name: String, type messageType: TMessageType, sequenceID: Int32) throws { try proto.writeMessageBegin(name: name, type: messageType, sequenceID: sequenceID) @@ -176,6 +184,10 @@ class TProtocolDecorator: TProtocol { func write(_ value: UInt8) throws { try proto.write(value) } + + func write(_ value: Int8) throws { + try proto.write(value) + } func write(_ value: Int16) throws { try proto.write(value) @@ -196,4 +208,8 @@ class TProtocolDecorator: TProtocol { func write(_ value: Data) throws { try proto.write(value) } + + func write(_ value: UUID) throws { + try proto.write(value) + } } diff --git a/lib/swift/Sources/TSerializable.swift b/lib/swift/Sources/TSerializable.swift index 1374700ca..ff976eb9a 100644 --- a/lib/swift/Sources/TSerializable.swift +++ b/lib/swift/Sources/TSerializable.swift @@ -61,11 +61,11 @@ extension Int8 : TSerializable { public static var thriftType: TType { return .i8 } public static func read(from proto: TProtocol) throws -> Int8 { - return Int8(try proto.read() as UInt8) + return try proto.read() as Int8 } public func write(to proto: TProtocol) throws { - try proto.write(UInt8(self)) + try proto.write(Int8(self)) } } @@ -129,3 +129,15 @@ extension String : TSerializable { try proto.write(self) } } + +extension UUID : TSerializable { + public static var thriftType: TType { .uuid } + + public static func read(from proto: TProtocol) throws -> UUID { + return try proto.read() + } + + public func write(to proto: TProtocol) throws { + try proto.write(self) + } +} diff --git a/lib/swift/Sources/TSocketServer.swift b/lib/swift/Sources/TSocketServer.swift index 7debe9fa9..6cd9adfe4 100644 --- a/lib/swift/Sources/TSocketServer.swift +++ b/lib/swift/Sources/TSocketServer.swift @@ -96,26 +96,21 @@ open class TSocketServer<InProtocol: TProtocol, OutProtocol: TProtocol, Processo // throw away our socket CFSocketInvalidate(sock) - - // register for notifications of accepted incoming connections - _ = NotificationCenter.default.addObserver(forName: .NSFileHandleConnectionAccepted, - object: nil, queue: nil) { - [weak self] notification in - guard let strongSelf = self else { return } - guard let clientSocket = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { return } - strongSelf.connectionAccepted(clientSocket) - } - - // tell socket to listen - socketFileHandle.acceptConnectionInBackgroundAndNotify() - + print("TSocketServer: Listening on TCP port \(port)") + + // tell socket to listen + acceptConnectionInBackgroundAndNotify(handle: socketFileHandle) } - - deinit { - NotificationCenter.default.removeObserver(self) + + private func acceptConnectionInBackgroundAndNotify(handle: FileHandle) { + DispatchQueue(label: "TSocketServer.connectionAccept").async { + let acceptedFD = accept(handle.fileDescriptor, nil, nil) + DispatchQueue.main.async { + self.connectionAccepted(FileHandle(fileDescriptor: acceptedFD)) + } + } } - func connectionAccepted(_ clientSocket: FileHandle) { // Now that we have a client connected, handle the request on queue processingQueue.async { @@ -123,7 +118,7 @@ open class TSocketServer<InProtocol: TProtocol, OutProtocol: TProtocol, Processo } // continue accepting connections - socketFileHandle.acceptConnectionInBackgroundAndNotify() + acceptConnectionInBackgroundAndNotify(handle: socketFileHandle) } open func createTransport(fileHandle: FileHandle) -> TTransport { @@ -145,7 +140,7 @@ open class TSocketServer<InProtocol: TProtocol, OutProtocol: TProtocol, Processo DispatchQueue.main.async { NotificationCenter.default .post(name: Notification.Name(rawValue: TSocketServerClientConnectionFinished), - object: self, + object: nil, userInfo: [TSocketServerProcessorKey: self.processor, TSocketServerTransportKey: transport]) } diff --git a/lib/swift/Sources/TWrappedProtocol.swift b/lib/swift/Sources/TWrappedProtocol.swift index 8e8577bb5..8e47bd58e 100644 --- a/lib/swift/Sources/TWrappedProtocol.swift +++ b/lib/swift/Sources/TWrappedProtocol.swift @@ -101,6 +101,10 @@ open class TWrappedProtocol<Protocol: TProtocol> : TProtocol { return try concreteProtocol.read() } + public func read() throws -> Int8 { + return try concreteProtocol.read() + } + public func read() throws -> Int16 { return try concreteProtocol.read() } @@ -120,6 +124,10 @@ open class TWrappedProtocol<Protocol: TProtocol> : TProtocol { public func read() throws -> Data { return try concreteProtocol.read() } + + public func read() throws -> UUID { + return try concreteProtocol.read() + } // Write methods @@ -185,6 +193,10 @@ open class TWrappedProtocol<Protocol: TProtocol> : TProtocol { public func write(_ value: UInt8) throws { try concreteProtocol.write(value) } + + public func write(_ value: Int8) throws { + try concreteProtocol.write(value) + } public func write(_ value: Int16) throws { try concreteProtocol.write(value) @@ -205,4 +217,8 @@ open class TWrappedProtocol<Protocol: TProtocol> : TProtocol { public func write(_ data: Data) throws { try concreteProtocol.write(data) } + + public func write(_ value: UUID) throws { + try concreteProtocol.write(value) + } } 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"] + } } ] |