@@ -520,144 +520,202 @@ class ValueObservationTests: GRDBTestCase {
520520 }
521521
522522#if swift(>=5.5)
523+ // MARK: - Async Await
524+
523525 @available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
524526 func testAsyncAwait_values_prefix( ) async throws {
525- let dbQueue = try makeDatabaseQueue ( )
526-
527- // We need something to change
528- try await dbQueue. write { try $0. execute ( sql: " CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT) " ) }
529-
530- let cancellationExpectation = expectation ( description: " cancelled " )
531- let observation = ValueObservation
532- . tracking { try Int . fetchOne ( $0, sql: " SELECT COUNT(*) FROM t " ) ! }
533- . handleEvents ( didCancel: { cancellationExpectation. fulfill ( ) } )
534-
535- let task = Task { ( ) -> [ Int ] in
536- var counts : [ Int ] = [ ]
527+ func test( writer: DatabaseWriter ) async throws {
528+ // We need something to change
529+ try await writer. write { try $0. execute ( sql: " CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT) " ) }
530+
531+ let cancellationExpectation = expectation ( description: " cancelled " )
532+ let observation = ValueObservation
533+ . trackingConstantRegion { try Int . fetchOne ( $0, sql: " SELECT COUNT(*) FROM t " ) ! }
534+ . handleEvents ( didCancel: { cancellationExpectation. fulfill ( ) } )
537535
538- for try await count in observation. values ( in: dbQueue) . prefix ( 3 ) {
539- counts. append ( count)
540- try await dbQueue. write { try $0. execute ( sql: " INSERT INTO t DEFAULT VALUES " ) }
536+ let task = Task { ( ) -> [ Int ] in
537+ var counts : [ Int ] = [ ]
538+
539+ for try await count in try observation. values ( in: writer) . prefix ( while: { $0 < 3 } ) {
540+ counts. append ( count)
541+ try await writer. write { try $0. execute ( sql: " INSERT INTO t DEFAULT VALUES " ) }
542+ }
543+ return counts
541544 }
542- return counts
545+
546+ let counts = try await task. value
547+
548+ // All values were published
549+ assertValueObservationRecordingMatch ( recorded: counts, expected: [ 0 , 1 , 2 ] )
550+
551+ // Observation was ended
552+ wait ( for: [ cancellationExpectation] , timeout: 2 )
543553 }
544554
545- let counts = try await task. value
546-
547- // All values were published
548- XCTAssertEqual ( counts, [ 0 , 1 , 2 ] )
549-
550- // Observation was ended
551- wait ( for: [ cancellationExpectation] , timeout: 2 )
555+ try await AsyncTest ( test)
556+ . run { DatabaseQueue ( ) }
557+ . runAtTemporaryDatabasePath { try DatabaseQueue ( path: $0) }
558+ . runAtTemporaryDatabasePath { try DatabasePool ( path: $0) }
552559 }
553560
554561 @available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
555562 func testAsyncAwait_values_prefix_immediate_scheduling( ) async throws {
556- let dbQueue = try makeDatabaseQueue ( )
557-
558- // We need something to change
559- try await dbQueue. write { try $0. execute ( sql: " CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT) " ) }
560-
561- let cancellationExpectation = expectation ( description: " cancelled " )
562- let observation = ValueObservation
563- . tracking { try Int . fetchOne ( $0, sql: " SELECT COUNT(*) FROM t " ) ! }
564- . handleEvents ( didCancel: { cancellationExpectation. fulfill ( ) } )
565-
566- let task = Task { @MainActor ( ) -> [ Int ] in
567- var counts : [ Int ] = [ ]
563+ func test( writer: DatabaseWriter ) async throws {
564+ // We need something to change
565+ try await writer. write { try $0. execute ( sql: " CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT) " ) }
568566
569- for try await count in observation. values ( in: dbQueue, scheduling: . immediate) . prefix ( 3 ) {
570- counts. append ( count)
571- try await dbQueue. write { try $0. execute ( sql: " INSERT INTO t DEFAULT VALUES " ) }
567+ let cancellationExpectation = expectation ( description: " cancelled " )
568+ let observation = ValueObservation
569+ . trackingConstantRegion { try Int . fetchOne ( $0, sql: " SELECT COUNT(*) FROM t " ) ! }
570+ . handleEvents ( didCancel: { cancellationExpectation. fulfill ( ) } )
571+
572+ let task = Task { @MainActor ( ) -> [ Int ] in
573+ var counts : [ Int ] = [ ]
574+
575+ for try await count in try observation. values ( in: writer, scheduling: . immediate) . prefix ( while: { $0 < 3 } ) {
576+ counts. append ( count)
577+ try await writer. write { try $0. execute ( sql: " INSERT INTO t DEFAULT VALUES " ) }
578+ }
579+ return counts
572580 }
573- return counts
581+
582+ let counts = try await task. value
583+
584+ // All values were published
585+ assertValueObservationRecordingMatch ( recorded: counts, expected: [ 0 , 1 , 2 ] )
586+
587+ // Observation was ended
588+ wait ( for: [ cancellationExpectation] , timeout: 2 )
574589 }
575590
576- let counts = try await task. value
577-
578- // All values were published
579- XCTAssertEqual ( counts, [ 0 , 1 , 2 ] )
580-
581- // Observation was ended
582- wait ( for: [ cancellationExpectation] , timeout: 2 )
591+ try await AsyncTest ( test)
592+ . run { DatabaseQueue ( ) }
593+ . runAtTemporaryDatabasePath { try DatabaseQueue ( path: $0) }
594+ . runAtTemporaryDatabasePath { try DatabasePool ( path: $0) }
583595 }
584596
585597 @available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
586598 func testAsyncAwait_values_break( ) async throws {
587- let dbQueue = try makeDatabaseQueue ( )
588-
589- // We need something to change
590- try await dbQueue. write { try $0. execute ( sql: " CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT) " ) }
591-
592- let cancellationExpectation = expectation ( description: " cancelled " )
593- let observation = ValueObservation
594- . tracking { try Int . fetchOne ( $0, sql: " SELECT COUNT(*) FROM t " ) ! }
595- . handleEvents ( didCancel: { cancellationExpectation. fulfill ( ) } )
599+ func test( writer: DatabaseWriter ) async throws {
600+ // We need something to change
601+ try await writer. write { try $0. execute ( sql: " CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT) " ) }
602+
603+ let cancellationExpectation = expectation ( description: " cancelled " )
604+ let observation = ValueObservation
605+ . trackingConstantRegion { try Int . fetchOne ( $0, sql: " SELECT COUNT(*) FROM t " ) ! }
606+ . handleEvents ( didCancel: { cancellationExpectation. fulfill ( ) } )
607+
608+ let task = Task { ( ) -> [ Int ] in
609+ var counts : [ Int ] = [ ]
610+
611+ for try await count in observation. values ( in: writer) {
612+ counts. append ( count)
613+ if count == 2 {
614+ break
615+ } else {
616+ try await writer. write { try $0. execute ( sql: " INSERT INTO t DEFAULT VALUES " ) }
617+ }
618+ }
619+ return counts
620+ }
621+
622+ let counts = try await task. value
623+
624+ // All values were published
625+ assertValueObservationRecordingMatch ( recorded: counts, expected: [ 0 , 1 , 2 ] )
626+
627+ // Observation was ended
628+ wait ( for: [ cancellationExpectation] , timeout: 2 )
629+ }
596630
597- let task = Task { ( ) -> [ Int ] in
598- var counts : [ Int ] = [ ]
631+ try await AsyncTest ( test)
632+ . run { DatabaseQueue ( ) }
633+ . runAtTemporaryDatabasePath { try DatabaseQueue ( path: $0) }
634+ . runAtTemporaryDatabasePath { try DatabasePool ( path: $0) }
635+ }
636+
637+ @available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
638+ func testAsyncAwait_values_immediate_break( ) async throws {
639+ func test( writer: DatabaseWriter ) async throws {
640+ // We need something to change
641+ try await writer. write { try $0. execute ( sql: " CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT) " ) }
642+
643+ let cancellationExpectation = expectation ( description: " cancelled " )
644+ let observation = ValueObservation
645+ . trackingConstantRegion { try Int . fetchOne ( $0, sql: " SELECT COUNT(*) FROM t " ) ! }
646+ . handleEvents ( didCancel: { cancellationExpectation. fulfill ( ) } )
599647
600- for try await count in observation. values ( in: dbQueue) {
601- counts. append ( count)
602- if count == 2 {
648+ let task = Task { @MainActor ( ) -> [ Int ] in
649+ var counts : [ Int ] = [ ]
650+
651+ for try await count in observation. values ( in: writer, scheduling: . immediate) {
652+ counts. append ( count)
603653 break
604- } else {
605- try await dbQueue. write { try $0. execute ( sql: " INSERT INTO t DEFAULT VALUES " ) }
606654 }
655+ return counts
607656 }
608- return counts
657+
658+ let counts = try await task. value
659+
660+ // A single value was published
661+ assertValueObservationRecordingMatch ( recorded: counts, expected: [ 0 ] )
662+
663+ // Observation was ended
664+ wait ( for: [ cancellationExpectation] , timeout: 2 )
609665 }
610666
611- let counts = try await task. value
612-
613- // All values were published
614- XCTAssertEqual ( counts, [ 0 , 1 , 2 ] )
615-
616- // Observation was ended
617- wait ( for: [ cancellationExpectation] , timeout: 2 )
667+ try await AsyncTest ( test)
668+ . run { DatabaseQueue ( ) }
669+ . runAtTemporaryDatabasePath { try DatabaseQueue ( path: $0) }
670+ . runAtTemporaryDatabasePath { try DatabasePool ( path: $0) }
618671 }
619672
620673 @available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
621674 func testAsyncAwait_values_cancelled( ) async throws {
622- let dbQueue = try makeDatabaseQueue ( )
623-
624- // We need something to change
625- try await dbQueue . write { try $0 . execute ( sql : " CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT) " ) }
626-
627- let cancellationExpectation = expectation ( description: " cancelled " )
628- let valueExpectation = expectation ( description : " value " )
629- valueExpectation . assertForOverFulfill = false
630- let observation = ValueObservation
631- . tracking { try Int . fetchOne ( $0 , sql : " SELECT COUNT(*) FROM t " ) ! }
632- . handleEvents (
633- didReceiveValue : { _ in valueExpectation . fulfill ( ) } ,
634- didCancel : { cancellationExpectation . fulfill ( ) } )
635-
636- struct TestError : Error { }
637- do {
638- try await withThrowingTaskGroup ( of : Void . self ) { group in
639- group . addTask {
640- // Infinite loop
641- for try await _ in observation . values ( in : dbQueue ) {
642- try await dbQueue . write { try $0 . execute ( sql : " INSERT INTO t DEFAULT VALUES " ) }
675+ func test ( writer : DatabaseWriter ) async throws {
676+ // We need something to change
677+ try await writer . write { try $0 . execute ( sql : " CREATE TABLE t(id INTEGER PRIMARY KEY AUTOINCREMENT) " ) }
678+
679+ let cancellationExpectation = expectation ( description : " cancelled " )
680+ let valueExpectation = expectation ( description: " value " )
681+ valueExpectation. assertForOverFulfill = false
682+ let observation = ValueObservation
683+ . trackingConstantRegion { try Int . fetchOne ( $0 , sql : " SELECT COUNT(*) FROM t " ) ! }
684+ . handleEvents (
685+ didReceiveValue : { _ in valueExpectation . fulfill ( ) } ,
686+ didCancel : { cancellationExpectation . fulfill ( ) } )
687+
688+ struct TestError : Error { }
689+ do {
690+ try await withThrowingTaskGroup ( of : Void . self ) { group in
691+ group. addTask {
692+ // Infinite loop
693+ for try await _ in observation . values ( in : writer ) {
694+ try await writer . write { try $0 . execute ( sql : " INSERT INTO t DEFAULT VALUES " ) }
695+ }
643696 }
697+ group. addTask {
698+ // Throw after a delay
699+ try await Task . sleep ( nanoseconds: 1_000_000 )
700+ throw TestError ( )
701+ }
702+
703+ for try await _ in group { }
644704 }
645- group. addTask {
646- // Throw after a delay
647- try await Task . sleep ( nanoseconds: 1_000_000 )
648- throw TestError ( )
649- }
650-
651- for try await _ in group { }
705+ XCTFail ( " Expected error " )
706+ } catch is TestError {
707+ } catch {
708+ XCTFail ( " Unexpected error \( error) " )
652709 }
653- XCTFail ( " Expected error " )
654- } catch is TestError {
655- } catch {
656- XCTFail ( " Unexpected error \( error) " )
710+
711+ // A value was observed, and observation was ended
712+ wait ( for: [ valueExpectation, cancellationExpectation] , timeout: 2 )
657713 }
658714
659- // A value was observed, and observation was ended
660- wait ( for: [ valueExpectation, cancellationExpectation] , timeout: 2 )
715+ try await AsyncTest ( test)
716+ . run { DatabaseQueue ( ) }
717+ . runAtTemporaryDatabasePath { try DatabaseQueue ( path: $0) }
718+ . runAtTemporaryDatabasePath { try DatabasePool ( path: $0) }
661719 }
662720#endif
663721}
0 commit comments