Skip to content

Commit

Permalink
Update watchOS/tvOS annotations & test on CI
Browse files Browse the repository at this point in the history
- Updates CI to run tests against macOS, iOS, tvOS, and watchOS rather than only the default environment (macOS)
- Switches from using `swift test` to `xcodebuild test` since the former does not allow for specifying a target SDK/environment (see [this question](https://stackoverflow.com/questions/60245159/how-can-i-build-a-swift-package-for-ios-over-command-line) and [these notes](https://www.jessesquires.com/blog/2021/11/03/swift-package-ios-tests/))
- Updates `@available` annotations to properly accomodate these additional platforms

Follow-up to #227 and related to #226.
  • Loading branch information
rebello95 committed Dec 11, 2023
1 parent e4f2174 commit e06d320
Show file tree
Hide file tree
Showing 26 changed files with 111 additions and 94 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,14 @@ jobs:
brew unlink [email protected] && brew link --overwrite [email protected]
brew install docker colima
colima delete && colima start
- name: Run tests
run: make test
- name: Run iOS tests
run: make testios
- name: Run macOS tests
run: make testmacos
- name: Run tvOS tests
run: make testtvos
- name: Run watchOS tests
run: make testwatchos
run-swiftlint:
runs-on: ubuntu-latest
container:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,18 +16,18 @@ import SwiftProtobuf
internal protocol Connectrpc_Eliza_V1_ElizaServiceClientInterface: Sendable {

/// Say is a unary RPC. Eliza responds to the prompt with a single sentence.
@available(iOS 13, *)
@available(iOS 13.0, *)
func `say`(request: Connectrpc_Eliza_V1_SayRequest, headers: Connect.Headers) async -> ResponseMessage<Connectrpc_Eliza_V1_SayResponse>

/// Converse is a bidirectional RPC. The caller may exchange multiple
/// back-and-forth messages with Eliza over a long-lived connection. Eliza
/// responds to each ConverseRequest with a ConverseResponse.
@available(iOS 13, *)
@available(iOS 13.0, *)
func `converse`(headers: Connect.Headers) -> any Connect.BidirectionalAsyncStreamInterface<Connectrpc_Eliza_V1_ConverseRequest, Connectrpc_Eliza_V1_ConverseResponse>

/// Introduce is a server streaming RPC. Given the caller's name, Eliza
/// returns a stream of sentences to introduce itself.
@available(iOS 13, *)
@available(iOS 13.0, *)
func `introduce`(headers: Connect.Headers) -> any Connect.ServerOnlyAsyncStreamInterface<Connectrpc_Eliza_V1_IntroduceRequest, Connectrpc_Eliza_V1_IntroduceResponse>
}

Expand All @@ -39,17 +39,17 @@ internal final class Connectrpc_Eliza_V1_ElizaServiceClient: Connectrpc_Eliza_V1
self.client = client
}

@available(iOS 13, *)
@available(iOS 13.0, *)
internal func `say`(request: Connectrpc_Eliza_V1_SayRequest, headers: Connect.Headers = [:]) async -> ResponseMessage<Connectrpc_Eliza_V1_SayResponse> {
return await self.client.unary(path: "/connectrpc.eliza.v1.ElizaService/Say", idempotencyLevel: .noSideEffects, request: request, headers: headers)
}

@available(iOS 13, *)
@available(iOS 13.0, *)
internal func `converse`(headers: Connect.Headers = [:]) -> any Connect.BidirectionalAsyncStreamInterface<Connectrpc_Eliza_V1_ConverseRequest, Connectrpc_Eliza_V1_ConverseResponse> {
return self.client.bidirectionalStream(path: "/connectrpc.eliza.v1.ElizaService/Converse", headers: headers)
}

@available(iOS 13, *)
@available(iOS 13.0, *)
internal func `introduce`(headers: Connect.Headers = [:]) -> any Connect.ServerOnlyAsyncStreamInterface<Connectrpc_Eliza_V1_IntroduceRequest, Connectrpc_Eliza_V1_IntroduceResponse> {
return self.client.serverOnlyStream(path: "/connectrpc.eliza.v1.ElizaService/Introduce", headers: headers)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import SwiftProtobuf
/// Concrete implementation of `BidirectionalAsyncStreamInterface`.
/// Provides the necessary wiring to bridge from closures/callbacks to Swift's `AsyncStream`
/// to work with async/await.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
final class BidirectionalAsyncStream<
Input: ProtobufMessage, Output: ProtobufMessage
>: @unchecked Sendable {
Expand Down Expand Up @@ -79,7 +79,7 @@ final class BidirectionalAsyncStream<
}
}

@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
extension BidirectionalAsyncStream: BidirectionalAsyncStreamInterface {
@discardableResult
func send(_ input: Input) throws -> Self {
Expand All @@ -101,5 +101,5 @@ extension BidirectionalAsyncStream: BidirectionalAsyncStreamInterface {
}

// Conforms to the client-only interface since it matches exactly and the implementation is internal
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
extension BidirectionalAsyncStream: ClientOnlyAsyncStreamInterface {}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import SwiftProtobuf

/// Concrete implementation of `ServerOnlyAsyncStreamInterface`.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
final class ServerOnlyAsyncStream<Input: ProtobufMessage, Output: ProtobufMessage>: Sendable {
private let bidirectionalStream: BidirectionalAsyncStream<Input, Output>

Expand All @@ -24,7 +24,7 @@ final class ServerOnlyAsyncStream<Input: ProtobufMessage, Output: ProtobufMessag
}
}

@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
extension ServerOnlyAsyncStream: ServerOnlyAsyncStreamInterface {
func send(_ input: Input) throws {
try self.bidirectionalStream.send(input)
Expand Down
2 changes: 1 addition & 1 deletion Libraries/Connect/Internal/Unary/UnaryAsyncWrapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import SwiftProtobuf
/// For discussions on why this is necessary, see:
/// https://forums.swift.org/t/how-to-use-withtaskcancellationhandler-properly/54341/37
/// https://stackoverflow.com/q/71898080
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
actor UnaryAsyncWrapper<Output: ProtobufMessage>: Sendable {
private var cancelable: Cancelable?
private let sendUnary: PerformClosure
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,7 @@ extension ProtocolClient: ProtocolClientInterface {

// MARK: - Async/await

@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
public func unary<Input: ProtobufMessage, Output: ProtobufMessage>(
path: String,
idempotencyLevel: IdempotencyLevel,
Expand All @@ -196,7 +196,7 @@ extension ProtocolClient: ProtocolClientInterface {
}.send()
}

@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
public func bidirectionalStream<Input: ProtobufMessage, Output: ProtobufMessage>(
path: String,
headers: Headers
Expand All @@ -208,7 +208,7 @@ extension ProtocolClient: ProtocolClientInterface {
return bidirectionalAsync.configureForSending(with: callbacks)
}

@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
public func clientOnlyStream<Input: ProtobufMessage, Output: ProtobufMessage>(
path: String,
headers: Headers
Expand All @@ -220,7 +220,7 @@ extension ProtocolClient: ProtocolClientInterface {
return bidirectionalAsync.configureForSending(with: callbacks)
}

@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
public func serverOnlyStream<Input: ProtobufMessage, Output: ProtobufMessage>(
path: String,
headers: Headers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ public protocol ProtocolClientInterface: Sendable {
/// - parameter headers: The outbound request headers to include.
///
/// - returns: The response which is returned asynchronously.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
func unary<Input: ProtobufMessage, Output: ProtobufMessage>(
path: String,
idempotencyLevel: IdempotencyLevel,
Expand All @@ -129,7 +129,7 @@ public protocol ProtocolClientInterface: Sendable {
/// - parameter headers: The outbound request headers to include.
///
/// - returns: An interface for sending and receiving data over the stream using async/await.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
func bidirectionalStream<Input: ProtobufMessage, Output: ProtobufMessage>(
path: String,
headers: Headers
Expand All @@ -147,7 +147,7 @@ public protocol ProtocolClientInterface: Sendable {
/// - parameter headers: The outbound request headers to include.
///
/// - returns: An interface for sending and receiving data over the stream using async/await.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
func clientOnlyStream<Input: ProtobufMessage, Output: ProtobufMessage>(
path: String,
headers: Headers
Expand All @@ -165,7 +165,7 @@ public protocol ProtocolClientInterface: Sendable {
/// - parameter headers: The outbound request headers to include.
///
/// - returns: An interface for sending and receiving data over the stream using async/await.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
func serverOnlyStream<Input: ProtobufMessage, Output: ProtobufMessage>(
path: String,
headers: Headers
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import SwiftProtobuf

/// Represents a bidirectional stream that can be interacted with using async/await.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
public protocol BidirectionalAsyncStreamInterface<Input, Output> {
/// The input (request) message type.
associatedtype Input: ProtobufMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import SwiftProtobuf

/// Represents a client-only stream (a stream where the client streams data to the server and
/// eventually receives a response) that can be interacted with using async/await.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
public protocol ClientOnlyAsyncStreamInterface<Input, Output> {
/// The input (request) message type.
associatedtype Input: ProtobufMessage
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import SwiftProtobuf

/// Represents a server-only stream (a stream where the server streams data to the client after
/// receiving an initial request) that can be interacted with using async/await.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
public protocol ServerOnlyAsyncStreamInterface<Input, Output> {
/// The input (request) message type.
associatedtype Input: ProtobufMessage
Expand Down
2 changes: 1 addition & 1 deletion Libraries/ConnectMocks/MockBidirectionalAsyncStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import SwiftProtobuf
///
/// To return data over the stream, outputs can be specified using `init(outputs: ...)` or by
/// subclassing and overriding `results()`.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
open class MockBidirectionalAsyncStream<
Input: ProtobufMessage,
Output: ProtobufMessage
Expand Down
2 changes: 1 addition & 1 deletion Libraries/ConnectMocks/MockBidirectionalStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import SwiftProtobuf
/// or by subclassing the type and overriding functions such as `send()`.
///
/// To return data over the stream, outputs can be specified using `init(outputs: ...)`.
@available(iOS 13.0, *)
@available(iOS 13.0, watchOS 6.0, *)
open class MockBidirectionalStream<
Input: ProtobufMessage,
Output: ProtobufMessage
Expand Down
2 changes: 1 addition & 1 deletion Libraries/ConnectMocks/MockClientOnlyAsyncStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import SwiftProtobuf
///
/// To return data over the stream, outputs can be specified using `init(outputs: ...)` or by
/// subclassing and overriding `results()`.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
open class MockClientOnlyAsyncStream<
Input: ProtobufMessage,
Output: ProtobufMessage
Expand Down
2 changes: 1 addition & 1 deletion Libraries/ConnectMocks/MockClientOnlyStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import SwiftProtobuf
/// or by subclassing the type and overriding functions such as `send()`.
///
/// To return data over the stream, outputs can be specified using `init(outputs: ...)`.
@available(iOS 13.0, *)
@available(iOS 13.0, watchOS 6.0, *)
open class MockClientOnlyStream<
Input: ProtobufMessage,
Output: ProtobufMessage
Expand Down
2 changes: 1 addition & 1 deletion Libraries/ConnectMocks/MockServerOnlyAsyncStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import SwiftProtobuf
///
/// To return data over the stream, outputs can be specified using `init(outputs: ...)` or by
/// subclassing and overriding `results()`.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
open class MockServerOnlyAsyncStream<
Input: ProtobufMessage,
Output: ProtobufMessage
Expand Down
2 changes: 1 addition & 1 deletion Libraries/ConnectMocks/MockServerOnlyStream.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import SwiftProtobuf
/// or by subclassing the type and overriding functions such as `send()`.
///
/// To return data over the stream, outputs can be specified using `init(outputs: ...)`.
@available(iOS 13.0, *)
@available(iOS 13.0, watchOS 6.0, *)
open class MockServerOnlyStream<
Input: ProtobufMessage,
Output: ProtobufMessage
Expand Down
21 changes: 18 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,22 @@ $(BIN)/license-headers: Makefile
mkdir -p $(@D)
GOBIN=$(abspath $(BIN)) go install github.com/bufbuild/buf/private/pkg/licenseheader/cmd/license-header@$(LICENSE_HEADER_VERSION)

.PHONY: test
test: conformanceserverrun ## Run all tests
swift test
.PHONY: testios
testios: conformanceserverrun ## Run iOS tests
xcodebuild -scheme Connect-Package -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.0.1' test
$(MAKE) conformanceserverstop

.PHONY: testmacos
testmacos: conformanceserverrun ## Run macOS tests
xcodebuild -scheme Connect-Package -destination 'platform=macOS,arch=arm64' test
$(MAKE) conformanceserverstop

.PHONY: testtvos
testtvos: conformanceserverrun ## Run tvOS tests
xcodebuild -scheme Connect-Package -destination 'platform=tvOS Simulator,name=Apple TV,OS=17.0' test
$(MAKE) conformanceserverstop

.PHONY: testwatchos
testwatchos: conformanceserverrun ## Run watchOS tests
xcodebuild -scheme Connect-Package -destination 'platform=watchOS Simulator,name=Apple Watch Series 9 (45mm),OS=10.0' test
$(MAKE) conformanceserverstop
2 changes: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.6
// swift-tools-version:5.9

// Copyright 2022-2023 Buf Technologies, Inc.
//
Expand Down
2 changes: 1 addition & 1 deletion Plugins/ConnectMocksPlugin/ConnectMockGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ final class ConnectMockGenerator: Generator {
self.printLine("///")
self.printLine("/// Note: This class does not handle thread-safe locking, but provides")
self.printLine("/// `@unchecked Sendable` conformance to simplify testing and mocking.")
self.printLine("@available(iOS 13, *)")
self.printLine("@available(iOS 13.0, watchOS 6.0, *)")
self.printLine(
"""
\(self.typeVisibility) class \(service.mockName(using: self.namer)): \
Expand Down
4 changes: 2 additions & 2 deletions Plugins/ConnectSwiftPlugin/ConnectClientGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ final class ConnectClientGenerator: Generator {
private func printAsyncAwaitMethodInterface(for method: MethodDescriptor) {
self.printLine()
self.printCommentsIfNeeded(for: method)
self.printLine("@available(iOS 13, *)")
self.printLine("@available(iOS 13.0, watchOS 6.0, *)")
self.printLine(
method.asyncAwaitSignature(
using: self.namer, includeDefaults: false, options: self.options
Expand Down Expand Up @@ -161,7 +161,7 @@ final class ConnectClientGenerator: Generator {

private func printAsyncAwaitMethodImplementation(for method: MethodDescriptor) {
self.printLine()
self.printLine("@available(iOS 13, *)")
self.printLine("@available(iOS 13.0, watchOS 6.0, *)")
self.printLine(
"\(self.visibility) "
+ method.asyncAwaitSignature(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ private typealias UnimplementedServiceClient = Connectrpc_Conformance_V1_Unimple
/// Tests are based on https://github.com/connectrpc/conformance
///
/// Tests are written using async/await APIs.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
final class AsyncAwaitConformanceTests: XCTestCase {
private func executeTestWithClients(
timeout: TimeInterval = 60,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -179,11 +179,7 @@ final class CallbackConformanceTests: XCTestCase {
}
}

// Minimum requirements for runtime support for parameterized protocol types
@available(iOS 16, *)
@available(macOS 13, *)
@available(tvOS 16, *)
@available(watchOS 9, *)
@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) // Runtime parameterized protocol types
func testPingPong() {
func createPayload(
requestSize: Int,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import SwiftProtobuf
import XCTest

/// Test suite that validates the behavior of generated mock classes.
@available(iOS 13, *)
@available(iOS 13.0, watchOS 6.0, *)
final class ConnectMocksTests: XCTestCase {
// MARK: - Unary

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@
import SwiftProtobuf
import XCTest

@available(iOS 16, *)
@available(tvOS 16, *)
@available(watchOS 9, *)
@available(iOS 13.0, watchOS 6.0, *)
final class InterceptorIntegrationTests: XCTestCase {
func testUnaryInterceptorSuccess() async {
let trackedSteps = Locked([InterceptorStep]())
Expand Down Expand Up @@ -112,6 +110,7 @@ final class InterceptorIntegrationTests: XCTestCase {
])
}

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) // Required for .contains()
func testUnaryInterceptorIsCalledWithMetrics() async {
let trackedSteps = Locked([InterceptorStep]())
let client = self.createClient(interceptors: [
Expand Down Expand Up @@ -139,6 +138,7 @@ final class InterceptorIntegrationTests: XCTestCase {
))
}

@available(macOS 13.0, iOS 16.0, watchOS 9.0, tvOS 16.0, *) // Required for .contains()
func testStreamInterceptorIsCalledWithMetrics() async throws {
let trackedSteps = Locked([InterceptorStep]())
let client = self.createClient(interceptors: [
Expand Down
Loading

0 comments on commit e06d320

Please sign in to comment.