/* * Copyright 2017, gRPC Authors All rights reserved. * * Licensed 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 Dispatch import Foundation @testable import SwiftGRPC import XCTest class gRPCTests: XCTestCase { // We have seen this test flake out in rare cases fairly often due to race conditions. // To detect such rare errors, we run the tests several times. // (By now, all known errors should have been fixed, but we'd still like to detect new ones.) let testRepetitions = 10 func testConnectivity() { for _ in 0.. () throws -> Void)] { return [ ("testConnectivity", testConnectivity), ("testConnectivitySecure", testConnectivitySecure) ] } } let address = "localhost:8085" let host = "example.com" let clientText = "hello, server!" let serverText = "hello, client!" let initialClientMetadata = [ "x": "xylophone", "y": "yu", "z": "zither" ] let initialServerMetadata = [ "a": "Apple", "b": "Banana", "c": "Cherry" ] let trailingServerMetadata = [ // We have more than ten entries here to ensure that even large metadata entries work // and aren't limited by e.g. a fixed-size entry buffer. "0": "zero", "1": "one", "2": "two", "3": "three", "4": "four", "5": "five", "6": "six", "7": "seven", "8": "eight", "9": "nine", "10": "ten", "11": "eleven", "12": "twelve" ] let steps = 100 let hello = "/hello.unary" let helloServerStream = "/hello.server-stream" let helloBiDiStream = "/hello.bidi-stream" // Return code/message for unary test let oddStatusMessage = "OK" let evenStatusMessage = "some other status message" func runTest(useSSL: Bool) { gRPC.initialize() var serverRunningSemaphore: DispatchSemaphore? // create the server let server: Server if useSSL { server = Server(address: address, key: String(data: keyForTests, encoding: .utf8)!, certs: String(data: certificateForTests, encoding: .utf8)!) } else { server = Server(address: address) } // start the server do { serverRunningSemaphore = try runServer(server: server) } catch { XCTFail("server error \(error)") } // run the client do { try runClient(useSSL: useSSL) } catch { XCTFail("client error \(error)") } // stop the server server.stop() // wait until the server has shut down _ = serverRunningSemaphore!.wait() } func verify_metadata(_ metadata: Metadata, expected: [String: String], file: StaticString = #file, line: UInt = #line) { XCTAssertGreaterThanOrEqual(metadata.count(), expected.count) var allPresentKeys = Set() for i in 0.. DispatchSemaphore { var requestCount = 0 let sem = DispatchSemaphore(value: 0) server.run { requestHandler in do { if let method = requestHandler.method { switch method { case hello: try handleUnary(requestHandler: requestHandler, requestCount: requestCount) case helloServerStream: try handleServerStream(requestHandler: requestHandler) case helloBiDiStream: try handleBiDiStream(requestHandler: requestHandler) default: XCTFail("Invalid method \(method)") } } requestCount += 1 } catch { XCTFail("error \(error)") } } server.onCompletion = { // return from runServer() sem.signal() } // wait for the server to exit return sem } func handleUnary(requestHandler: Handler, requestCount: Int) throws { XCTAssertEqual(requestHandler.host, host) XCTAssertEqual(requestHandler.method, hello) let initialMetadata = requestHandler.requestMetadata verify_metadata(initialMetadata, expected: initialClientMetadata) let initialMetadataToSend = Metadata(initialServerMetadata) try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { if let messageData = $0 { let messageString = String(data: messageData, encoding: .utf8) XCTAssertEqual(messageString, clientText) } else { XCTFail("handleUnary message missing") } } // We need to return status OK in both cases, as it seems like the server might never send out the last few messages // once it has been asked to send a non-OK status. Alternatively, we could send a non-OK status here, but then we // would need to sleep for a few milliseconds before sending the non-OK status. let replyMessage = serverText.data(using: .utf8)! if (requestCount % 2) == 0 { let trailingMetadataToSend = Metadata(trailingServerMetadata) try requestHandler.sendResponse(message: replyMessage, status: ServerStatus(code: .ok, message: evenStatusMessage, trailingMetadata: trailingMetadataToSend)) } else { let trailingMetadataToSend = Metadata(trailingServerMetadata) try requestHandler.sendStatus(ServerStatus(code: .ok, message: oddStatusMessage, trailingMetadata: trailingMetadataToSend)) } } func handleServerStream(requestHandler: Handler) throws { XCTAssertEqual(requestHandler.host, host) XCTAssertEqual(requestHandler.method, helloServerStream) let initialMetadata = requestHandler.requestMetadata verify_metadata(initialMetadata, expected: initialClientMetadata) let initialMetadataToSend = Metadata(initialServerMetadata) try requestHandler.receiveMessage(initialMetadata: initialMetadataToSend) { if let messageData = $0 { let messageString = String(data: messageData, encoding: .utf8) XCTAssertEqual(messageString, clientText) } else { XCTFail("handleServerStream message missing") } } let replyMessage = serverText.data(using: .utf8)! for _ in 0..