Skip to content

Commit 27ed563

Browse files
author
vijaysharm
committed
Merge remote-tracking branch 'origin/main' into vijays/make-entities-optionally-sendable
2 parents b44167b + 789608f commit 27ed563

File tree

6 files changed

+55
-59
lines changed

6 files changed

+55
-59
lines changed

.github/workflows/test.yml

+5-4
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ jobs:
1919
2020
lucid_tests:
2121
name: Lucid-iOS Tests
22-
runs-on: macos-14
22+
runs-on: macos-15
2323
timeout-minutes: 30
2424
env:
2525
FASTLANE_LOGS: fastlane/test_output
2626
FASTLANE_FRAGILE_LOGS: fastlane/fragile_test_output
2727
GITHUB_ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
28-
FRAGILE_TESTS: LucidTests/APIClientQueueProcessorTests/test_processor_does_attempt_to_process_request_if_already_running_concurrent_request,LucidTests/CoreManagerPropertyTests/test_that_delegate_gets_called_when_observers_are_released,LucidTests/CoreManagerTests/test_continuous_observer_should_receive_all_updates_in_order,LucidTests/CoreManagerTests/test_manager_should_send_entity_update_to_provider_when_entity_is_set,LucidTests/RelationshipControllerTests/test_relationship_controller_should_continuously_send_events_when_first_event_comes_from_continuous_signal,LucidTests/RelationshipControllerTests/test_relationship_controller_should_continuously_send_events_when_first_event_comes_from_once_signal,LucidTests/StoreStackTests/test_should_fail_to_remove_in_remote_store_only_with_memory_store_first,LucidTests/RecoverableStoreTests/test_store_should_overwrite_a_non_empty_recovery_store_with_a_non_empty_main_store_at_init,LucidTests/RecoverableStoreTests/test_store_only_reflects_main_store_in_get_operations
28+
FRAGILE_TESTS: LucidTests/APIClientQueueProcessorTests/test_processor_does_attempt_to_process_request_if_already_running_concurrent_request,LucidTests/CoreManagerPropertyTests/test_that_delegate_gets_called_when_observers_are_released,LucidTests/CoreManagerTests/test_continuous_observer_should_receive_all_updates_in_order,LucidTests/CoreManagerTests/test_manager_should_send_entity_update_to_provider_when_entity_is_set,LucidTests/RelationshipControllerTests/test_relationship_controller_should_continuously_send_events_when_first_event_comes_from_continuous_signal,LucidTests/RelationshipControllerTests/test_relationship_controller_should_continuously_send_events_when_first_event_comes_from_once_signal,LucidTests/StoreStackTests/test_should_fail_to_remove_in_remote_store_only_with_memory_store_first,LucidTests/RecoverableStoreTests/test_store_should_overwrite_a_non_empty_recovery_store_with_a_non_empty_main_store_at_init,LucidTests/RecoverableStoreTests/test_store_only_reflects_main_store_in_get_operations,LucidTests/RecoverableStoreTests/test_store_affects_both_inner_stores_in_remove_operations_async,LucidTests/CoreManagerTests/test_manager_should_send_entity_update_to_provider_when_entity_is_removed,LucidTests/RecoverableStoreTests/test_store_only_reflects_main_store_in_get_operations_asyncLucidTests/BaseStoreTests/test_store_should_set_1000_entities_in_under_1_second,LucidTests/CoreManagerContractTests/test_continuous_obvserver_should_get_filtered_results_matching_entities_that_meet_contract_requirements,LucidTests/CoreManagerContractTests/test_core_manager_get_should_filter_results_when_enity_does_not_meet_contract_requirements,LucidTests/CoreManagerContractTests/test_core_manager_get_should_return_complete_results_when_entity_meets_contract_requirements,LucidTests/CoreManagerContractTests/test_core_manager_get_should_return_empty_results_when_no_entities_meet_contract_requirements_in_remote_store_for_remote_data_source,LucidTests/CoreManagerContractTests/test_core_manager_get_should_return_local_result_when_no_entities_meet_contract_requirements_in_remote_store_for_remote_or_local_data_source,LucidTestsCoreManagerTests/test_manager_should_send_entity_update_to_provider_with_different_query_when_entity_is_not_found
29+
2930
steps:
3031
- name: Clone Project
3132
uses: actions/checkout@v4
@@ -42,14 +43,14 @@ jobs:
4243

4344
- name: Run Lucid-iOS Tests
4445
run: |
45-
fastlane scan --scheme Lucid-iOS --skip_testing "$FRAGILE_TESTS" --device "iPhone 15" --output_directory $FASTLANE_LOGS --result_bundle true
46+
fastlane scan --scheme Lucid-iOS --skip_testing "$FRAGILE_TESTS" --device "iPhone 16" --output_directory $FASTLANE_LOGS --result_bundle true
4647
4748
# Some tests need to be reworked. Don't forget about them, but don't crash the build either
4849
# https://scribdjira.atlassian.net/browse/IPT-4387
4950
- name: Run Fragile Tests
5051
continue-on-error: true
5152
run: |
52-
fastlane scan --scheme Lucid-iOS --only_testing "$FRAGILE_TESTS" --device "iPhone 15" --output_directory $FASTLANE_FRAGILE_LOGS --result_bundle true
53+
fastlane scan --scheme Lucid-iOS --only_testing "$FRAGILE_TESTS" --device "iPhone 16" --output_directory $FASTLANE_FRAGILE_LOGS --result_bundle true
5354
5455
- name: Bundle Log Files
5556
run: |

.xcode-version

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
15.3
1+
16.1

Lucid/Utils/BackgroundTaskManager.swift

+7-3
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,15 @@ import Foundation
1212
import UIKit
1313

1414
protocol CoreBackgroundTaskManaging: AnyObject {
15-
func beginBackgroundTask(expirationHandler: (() -> Void)?) -> UIBackgroundTaskIdentifier
15+
func startBackgroundTask(expirationHandler: (@MainActor @Sendable () -> Void)?) -> UIBackgroundTaskIdentifier
1616
func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier)
1717
}
1818

19-
extension UIApplication: CoreBackgroundTaskManaging {}
19+
extension UIApplication: CoreBackgroundTaskManaging {
20+
func startBackgroundTask(expirationHandler: (@MainActor () -> Void)?) -> UIBackgroundTaskIdentifier {
21+
self.beginBackgroundTask(expirationHandler: expirationHandler)
22+
}
23+
}
2024

2125
/// In charge of keeping one background task alive as long as needed.
2226
protocol BackgroundTaskManaging: AnyObject {
@@ -91,7 +95,7 @@ final class BackgroundTaskManager: BackgroundTaskManaging {
9195
}
9296
RunLoop.main.add(timer, forMode: .default)
9397

94-
_taskID = coreManager.beginBackgroundTask {
98+
_taskID = coreManager.startBackgroundTask {
9599
timer.invalidate()
96100
self.asyncTaskQueue.async {
97101
Logger.log(.warning, "\(BackgroundTaskManager.self): Background task timed out: \(self._taskID)")

LucidTests/Doubles/BackgroundTaskManagerSpy.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public final class CoreBackgroundTaskManagerSpy: CoreBackgroundTaskManaging {
1616

1717
// MARK: - Records
1818

19-
public private(set) var expirationHandlerRecords = [UIBackgroundTaskIdentifier: (() -> Void)]()
19+
public private(set) var expirationHandlerRecords = [UIBackgroundTaskIdentifier: (@MainActor @Sendable () -> Void)]()
2020
public private(set) var beginBackgroundTaskCallCountRecord = 0
2121

2222
public private(set) var endBackgroundTaskRecords = [UIBackgroundTaskIdentifier]()
@@ -31,7 +31,7 @@ public final class CoreBackgroundTaskManagerSpy: CoreBackgroundTaskManaging {
3131
// no-op
3232
}
3333

34-
public func beginBackgroundTask(expirationHandler: (() -> Void)?) -> UIBackgroundTaskIdentifier {
34+
public func startBackgroundTask(expirationHandler: (@MainActor @Sendable () -> Void)?) -> UIBackgroundTaskIdentifier {
3535
let identifier = UIBackgroundTaskIdentifier(rawValue: backgroundTaskIDRawValueStub)
3636
if let expirationHandler = expirationHandler {
3737
expirationHandlerRecords[identifier] = expirationHandler
@@ -40,7 +40,7 @@ public final class CoreBackgroundTaskManagerSpy: CoreBackgroundTaskManaging {
4040
return identifier
4141
}
4242

43-
public func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) {
43+
nonisolated public func endBackgroundTask(_ identifier: UIBackgroundTaskIdentifier) {
4444
endBackgroundTaskRecords.append(identifier)
4545
}
4646
}

LucidTests/Utils/AsyncTaskQueueTests.swift

+36-45
Original file line numberDiff line numberDiff line change
@@ -456,67 +456,35 @@ final class AsyncTaskQueueTests: XCTestCase {
456456
wait(for: [setUpExpectation], timeout: 1)
457457
}
458458

459-
func test_that_it_runs_in_parallel_until_it_finds_a_barrier_then_it_waits_for_barrier_to_end_to_continue() {
459+
func test_that_it_runs_in_parallel_until_it_finds_a_barrier_then_it_waits_for_barrier_to_end_to_continue() async {
460460
let asyncTaskQueue = AsyncTaskQueue(maxConcurrentTasks: 2)
461-
462461
let operation1 = BlockingOperation()
463462
let operation2 = BlockingOperation()
464463
let operation3 = BlockingOperation()
465464
let operation4 = BlockingOperation()
466465

467-
Task(priority: .first) {
468-
do {
466+
// Create a task group to manage all operations
467+
await withThrowingTaskGroup(of: Void.self) { group in
468+
// First concurrent operations
469+
group.addTask {
469470
try await asyncTaskQueue.enqueue(operation: {
470471
await operation1.setUp()
471472
await operation1.perform()
472473
})
473-
} catch {
474-
XCTFail("unexpected error thrown: \(error)")
475474
}
476-
}
477475

478-
Task(priority: .second) {
479-
do {
476+
group.addTask {
480477
try await asyncTaskQueue.enqueue(operation: {
481478
await operation2.setUp()
482479
await operation2.perform()
483480
})
484-
} catch {
485-
XCTFail("unexpected error thrown: \(error)")
486-
}
487-
}
488-
489-
Task(priority: .third) {
490-
do {
491-
try await asyncTaskQueue.enqueueBarrier(operation: { completion in
492-
defer { completion() }
493-
494-
await operation3.setUp()
495-
await operation3.perform()
496-
})
497-
} catch {
498-
XCTFail("unexpected error thrown: \(error)")
499-
}
500-
}
501-
502-
Task(priority: .fourth) {
503-
do {
504-
try await asyncTaskQueue.enqueue(operation: {
505-
await operation4.setUp()
506-
await operation4.perform()
507-
})
508-
} catch {
509-
XCTFail("unexpected error thrown: \(error)")
510481
}
511-
}
512-
513-
let setUpExpectation = expectation(description: "set_up_expectation")
514482

515-
Task { @MainActor in
483+
// Wait for first two operations to be set up
516484
await operation1.waitForSetUp()
517485
await operation2.waitForSetUp()
518-
try? await Task.sleep(nanoseconds: 1000)
519-
486+
487+
// Verify initial state
520488
let operation1HasStarted = await operation1.hasStarted
521489
let operation2HasStarted = await operation2.hasStarted
522490
var operation3HasStarted = await operation3.hasStarted
@@ -529,10 +497,23 @@ final class AsyncTaskQueueTests: XCTestCase {
529497
XCTAssertFalse(operation4HasStarted)
530498
XCTAssertEqual(runningTasks, 2)
531499

500+
// Complete first two operations
532501
await operation1.resume()
533502
await operation2.resume()
503+
504+
// Add barrier operation
505+
group.addTask {
506+
try await asyncTaskQueue.enqueueBarrier(operation: { completion in
507+
defer { completion() }
508+
await operation3.setUp()
509+
await operation3.perform()
510+
})
511+
}
512+
513+
// Wait for barrier operation to start
534514
await operation3.waitForSetUp()
535515

516+
// Verify state after barrier starts
536517
let operation1HasCompleted = await operation1.hasCompleted
537518
let operation2HasCompleted = await operation2.hasCompleted
538519
operation3HasStarted = await operation3.hasStarted
@@ -545,9 +526,21 @@ final class AsyncTaskQueueTests: XCTestCase {
545526
XCTAssertFalse(operation4HasStarted)
546527
XCTAssertEqual(runningTasks, 1)
547528

529+
// Complete barrier operation
548530
await operation3.resume()
531+
532+
// Add final concurrent operation
533+
group.addTask {
534+
try await asyncTaskQueue.enqueue(operation: {
535+
await operation4.setUp()
536+
await operation4.perform()
537+
})
538+
}
539+
540+
// Wait for final operation to start
549541
await operation4.waitForSetUp()
550542

543+
// Verify final state
551544
let operation3HasCompleted = await operation3.hasCompleted
552545
operation4HasStarted = await operation4.hasStarted
553546
runningTasks = await asyncTaskQueue.runningTasks
@@ -558,14 +551,12 @@ final class AsyncTaskQueueTests: XCTestCase {
558551
XCTAssertTrue(operation4HasStarted)
559552
XCTAssertEqual(runningTasks, 1)
560553

561-
setUpExpectation.fulfill()
554+
// Complete final operation
555+
await operation4.resume()
562556
}
563-
564-
wait(for: [setUpExpectation], timeout: 1)
565557
}
566558

567559
func test_that_enqueue_continues_to_work_even_if_previous_operation_threw_an_error() {
568-
569560
let asyncTaskQueue = AsyncTaskQueue(maxConcurrentTasks: 1)
570561

571562
Task(priority: .first) {

LucidTests/Utils/BackgroundTaskManagerTests.swift

+3-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ final class BackgroundTaskManagerTests: XCTestCase {
2020
override func setUp() {
2121
super.setUp()
2222
coreManagerSpy = CoreBackgroundTaskManagerSpy()
23-
manager = BackgroundTaskManager(coreManagerSpy, timeout: 0.25)
23+
manager = BackgroundTaskManager(coreManagerSpy, timeout: 0.3)
2424
}
2525

2626
override func tearDown() {
@@ -87,12 +87,12 @@ final class BackgroundTaskManagerTests: XCTestCase {
8787
let id = manager.start {}
8888
_ = manager.stop(id)
8989

90-
DispatchQueue.main.asyncAfter(deadline: .now() + 0.4) {
90+
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
9191
XCTAssertEqual(self.coreManagerSpy.beginBackgroundTaskCallCountRecord, 1)
9292
XCTAssertEqual(self.coreManagerSpy.endBackgroundTaskRecords.count, 1)
9393
expectation.fulfill()
9494
}
9595

96-
waitForExpectations(timeout: 1)
96+
waitForExpectations(timeout: 2)
9797
}
9898
}

0 commit comments

Comments
 (0)