XCTest+AsyncAwait.swift 2.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071
  1. /*
  2. * Copyright 2021, gRPC Authors All rights reserved.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import XCTest
  17. @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
  18. internal func XCTAssertThrowsError<T>(
  19. _ expression: @autoclosure () async throws -> T,
  20. verify: (Error) -> Void = { _ in },
  21. file: StaticString = #filePath,
  22. line: UInt = #line
  23. ) async {
  24. do {
  25. _ = try await expression()
  26. XCTFail("Expression did not throw error", file: file, line: line)
  27. } catch {
  28. verify(error)
  29. }
  30. }
  31. private enum TaskResult<Result> {
  32. case operation(Result)
  33. case cancellation
  34. }
  35. @available(macOS 12, iOS 15, tvOS 15, watchOS 8, *)
  36. func withTaskCancelledAfter<Result>(
  37. nanoseconds: UInt64,
  38. operation: @escaping @Sendable () async -> Result
  39. ) async throws {
  40. try await withThrowingTaskGroup(of: TaskResult<Result>.self) { group in
  41. group.addTask {
  42. return .operation(await operation())
  43. }
  44. group.addTask {
  45. try await Task.sleep(nanoseconds: nanoseconds)
  46. return .cancellation
  47. }
  48. // Only the sleeping task can throw if it's cancelled, in which case we want to throw.
  49. let firstResult = try await group.next()
  50. // A task completed, cancel the rest.
  51. group.cancelAll()
  52. // Check which task completed.
  53. switch firstResult {
  54. case .cancellation:
  55. () // Fine, what we expect.
  56. case .operation:
  57. XCTFail("Operation completed before cancellation")
  58. case .none:
  59. XCTFail("No tasks completed")
  60. }
  61. // Wait for the other task. The operation cannot, only the sleeping task can.
  62. try await group.waitForAll()
  63. }
  64. }