Skip to content

Commit 17b2054

Browse files
authored
Adopt swift-testing for status tests (grpc#2049)
Motivation: swift-testing has a number of advantages over XCTest (parameterisation, organisation, failure messages etc.), we should start using it instead of XCTest. Modifications: - Convert the Status tests - Add a dependency on swift-testing on Linux (this can be removed when it's included as part of the toolchain) Results: Better tests
1 parent d3ef09d commit 17b2054

File tree

4 files changed

+91
-85
lines changed

4 files changed

+91
-85
lines changed

[email protected]

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,10 @@ let packageDependencies: [Package.Dependency] = [
7272
url: "https://github.com/apple/swift-distributed-tracing.git",
7373
from: "1.0.0"
7474
),
75+
.package(
76+
url: "https://github.com/swiftlang/swift-testing.git",
77+
branch: "release/6.0"
78+
),
7579
].appending(
7680
.package(
7781
url: "https://github.com/apple/swift-nio-ssl.git",
@@ -147,6 +151,13 @@ extension Target.Dependency {
147151
static var dequeModule: Self { .product(name: "DequeModule", package: "swift-collections") }
148152
static var atomics: Self { .product(name: "Atomics", package: "swift-atomics") }
149153
static var tracing: Self { .product(name: "Tracing", package: "swift-distributed-tracing") }
154+
static var testing: Self {
155+
.product(
156+
name: "Testing",
157+
package: "swift-testing",
158+
condition: .when(platforms: [.linux]) // Already included in the toolchain on Darwin
159+
)
160+
}
150161

151162
static var grpcCore: Self { .target(name: "GRPCCore") }
152163
static var grpcInProcessTransport: Self { .target(name: "GRPCInProcessTransport") }
@@ -401,6 +412,7 @@ extension Target {
401412
.grpcInProcessTransport,
402413
.dequeModule,
403414
.protobuf,
415+
.testing,
404416
],
405417
swiftSettings: [.swiftLanguageMode(.v6), .enableUpcomingFeature("ExistentialAny")]
406418
)

Sources/GRPCCore/RPCError.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,25 @@ extension RPCError {
107107
public var description: String {
108108
String(describing: self.wrapped)
109109
}
110+
111+
package static let all: [Self] = [
112+
.cancelled,
113+
.unknown,
114+
.invalidArgument,
115+
.deadlineExceeded,
116+
.notFound,
117+
.alreadyExists,
118+
.permissionDenied,
119+
.resourceExhausted,
120+
.failedPrecondition,
121+
.aborted,
122+
.outOfRange,
123+
.unimplemented,
124+
.internalError,
125+
.unavailable,
126+
.dataLoss,
127+
.unauthenticated,
128+
]
110129
}
111130
}
112131

Sources/GRPCCore/Status.swift

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,26 @@ extension Status {
166166
public var description: String {
167167
String(describing: self.wrapped)
168168
}
169+
170+
package static let all: [Self] = [
171+
.ok,
172+
.cancelled,
173+
.unknown,
174+
.invalidArgument,
175+
.deadlineExceeded,
176+
.notFound,
177+
.alreadyExists,
178+
.permissionDenied,
179+
.resourceExhausted,
180+
.failedPrecondition,
181+
.aborted,
182+
.outOfRange,
183+
.unimplemented,
184+
.internalError,
185+
.unavailable,
186+
.dataLoss,
187+
.unauthenticated,
188+
]
169189
}
170190
}
171191

Tests/GRPCCoreTests/StatusTests.swift

Lines changed: 40 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -13,105 +13,60 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import GRPCCore
17-
import XCTest
18-
19-
final class StatusTests: XCTestCase {
20-
private static let statusCodeRawValue: [(Status.Code, Int)] = [
21-
(.ok, 0),
22-
(.cancelled, 1),
23-
(.unknown, 2),
24-
(.invalidArgument, 3),
25-
(.deadlineExceeded, 4),
26-
(.notFound, 5),
27-
(.alreadyExists, 6),
28-
(.permissionDenied, 7),
29-
(.resourceExhausted, 8),
30-
(.failedPrecondition, 9),
31-
(.aborted, 10),
32-
(.outOfRange, 11),
33-
(.unimplemented, 12),
34-
(.internalError, 13),
35-
(.unavailable, 14),
36-
(.dataLoss, 15),
37-
(.unauthenticated, 16),
38-
]
3916

40-
func testCustomStringConvertible() {
41-
XCTAssertDescription(Status(code: .ok, message: ""), #"ok: """#)
42-
XCTAssertDescription(Status(code: .dataLoss, message: "message"), #"dataLoss: "message""#)
43-
XCTAssertDescription(Status(code: .unknown, message: "message"), #"unknown: "message""#)
44-
XCTAssertDescription(Status(code: .aborted, message: "message"), #"aborted: "message""#)
45-
}
17+
import GRPCCore
18+
import Testing
4619

47-
func testStatusCodeRawValues() {
48-
for (code, expected) in Self.statusCodeRawValue {
49-
XCTAssertEqual(code.rawValue, expected, "\(code) had unexpected raw value")
20+
@Suite("Status")
21+
struct StatusTests {
22+
@Suite("Code")
23+
struct Code {
24+
@Test("rawValue", arguments: zip(Status.Code.all, 0 ... 16))
25+
func rawValueOfStatusCodes(code: Status.Code, expected: Int) {
26+
#expect(code.rawValue == expected)
5027
}
51-
}
52-
53-
func testStatusCodeFromErrorCode() throws {
54-
XCTAssertEqual(Status.Code(RPCError.Code.cancelled), .cancelled)
55-
XCTAssertEqual(Status.Code(RPCError.Code.unknown), .unknown)
56-
XCTAssertEqual(Status.Code(RPCError.Code.invalidArgument), .invalidArgument)
57-
XCTAssertEqual(Status.Code(RPCError.Code.deadlineExceeded), .deadlineExceeded)
58-
XCTAssertEqual(Status.Code(RPCError.Code.notFound), .notFound)
59-
XCTAssertEqual(Status.Code(RPCError.Code.alreadyExists), .alreadyExists)
60-
XCTAssertEqual(Status.Code(RPCError.Code.permissionDenied), .permissionDenied)
61-
XCTAssertEqual(Status.Code(RPCError.Code.resourceExhausted), .resourceExhausted)
62-
XCTAssertEqual(Status.Code(RPCError.Code.failedPrecondition), .failedPrecondition)
63-
XCTAssertEqual(Status.Code(RPCError.Code.aborted), .aborted)
64-
XCTAssertEqual(Status.Code(RPCError.Code.outOfRange), .outOfRange)
65-
XCTAssertEqual(Status.Code(RPCError.Code.unimplemented), .unimplemented)
66-
XCTAssertEqual(Status.Code(RPCError.Code.internalError), .internalError)
67-
XCTAssertEqual(Status.Code(RPCError.Code.unavailable), .unavailable)
68-
XCTAssertEqual(Status.Code(RPCError.Code.dataLoss), .dataLoss)
69-
XCTAssertEqual(Status.Code(RPCError.Code.unauthenticated), .unauthenticated)
70-
}
7128

72-
func testStatusCodeFromValidRawValue() {
73-
for (expected, rawValue) in Self.statusCodeRawValue {
74-
XCTAssertEqual(
75-
Status.Code(rawValue: rawValue),
76-
expected,
77-
"\(rawValue) didn't convert to expected code \(expected)"
29+
@Test(
30+
"Initialize from RPCError.Code",
31+
arguments: zip(
32+
RPCError.Code.all,
33+
Status.Code.all.dropFirst() // Drop '.ok', there is no '.ok' error code.
7834
)
35+
)
36+
func initFromRPCErrorCode(errorCode: RPCError.Code, expected: Status.Code) {
37+
#expect(Status.Code(errorCode) == expected)
7938
}
80-
}
8139

82-
func testStatusCodeFromInvalidRawValue() {
83-
// Internally represented as a `UInt8`; try all other values.
84-
for rawValue in UInt8(17) ... UInt8.max {
85-
XCTAssertNil(Status.Code(rawValue: Int(rawValue)))
40+
@Test("Initialize from rawValue", arguments: zip(0 ... 16, Status.Code.all))
41+
func initFromRawValue(rawValue: Int, expected: Status.Code) {
42+
#expect(Status.Code(rawValue: rawValue) == expected)
8643
}
8744

88-
// API accepts `Int` so try invalid `Int` values too.
89-
XCTAssertNil(Status.Code(rawValue: -1))
90-
XCTAssertNil(Status.Code(rawValue: 1000))
91-
XCTAssertNil(Status.Code(rawValue: .max))
45+
@Test("Initialize from invalid rawValue", arguments: [-1, 17, 100, .max])
46+
func initFromInvalidRawValue(rawValue: Int) {
47+
#expect(Status.Code(rawValue: rawValue) == nil)
48+
}
9249
}
9350

94-
func testEquatableConformance() {
95-
XCTAssertEqual(Status(code: .ok, message: ""), Status(code: .ok, message: ""))
96-
XCTAssertEqual(Status(code: .ok, message: "message"), Status(code: .ok, message: "message"))
97-
98-
XCTAssertNotEqual(
99-
Status(code: .ok, message: ""),
100-
Status(code: .ok, message: "message")
101-
)
51+
@Test("CustomStringConvertible conformance")
52+
func customStringConvertible() {
53+
#expect("\(Status(code: .ok, message: ""))" == #"ok: """#)
54+
#expect("\(Status(code: .dataLoss, message: "oh no"))" == #"dataLoss: "oh no""#)
55+
}
10256

103-
XCTAssertNotEqual(
104-
Status(code: .ok, message: "message"),
105-
Status(code: .internalError, message: "message")
106-
)
57+
@Test("Equatable conformance")
58+
func equatable() {
59+
let ok = Status(code: .ok, message: "")
60+
let okWithMessage = Status(code: .ok, message: "message")
61+
let internalError = Status(code: .internalError, message: "")
10762

108-
XCTAssertNotEqual(
109-
Status(code: .ok, message: "message"),
110-
Status(code: .ok, message: "different message")
111-
)
63+
#expect(ok == ok)
64+
#expect(ok != okWithMessage)
65+
#expect(ok != internalError)
11266
}
11367

114-
func testFitsInExistentialContainer() {
115-
XCTAssertLessThanOrEqual(MemoryLayout<Status>.size, 24)
68+
@Test("Fits in existential container")
69+
func fitsInExistentialContainer() {
70+
#expect(MemoryLayout<Status>.size <= 24)
11671
}
11772
}

0 commit comments

Comments
 (0)