Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions Normal/Services/TimedUnblockService.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ final class TimedUnblockService {
cancelMonitoring(activityName: activityName)

let start = clock()
let endDate = start.addingTimeInterval(duration.timeInterval)
let endDate = Self.endDate(of: duration, from: start)
try scheduleActivity(name: activityName, start: start, endDate: endDate)

try persist(
Expand Down Expand Up @@ -95,7 +95,7 @@ final class TimedUnblockService {
cancelMonitoring(activityName: activityName)

let start = clock()
let endDate = start.addingTimeInterval(duration.timeInterval)
let endDate = Self.endDate(of: duration, from: start)
try scheduleActivity(name: activityName, start: start, endDate: endDate)

try persist(
Expand Down Expand Up @@ -280,6 +280,11 @@ final class TimedUnblockService {
}
}

private static func endDate(of duration: TimedUnblockDuration, from start: Date) -> Date {
let end = start.addingTimeInterval(duration.timeInterval).timeIntervalSinceReferenceDate
return Date(timeIntervalSinceReferenceDate: end.rounded(.down))
}

private func scheduleActivity(name: String, start: Date, endDate: Date) throws {
let schedule = DeviceActivityScheduleFactory.window(from: start, to: endDate)
try activityCenter.startMonitoring(
Expand Down
21 changes: 21 additions & 0 deletions NormalTests/Services/BlockingScenarioTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,27 @@ struct BlockingScenarioTests {
#expect(h.blocked == h.apps(1, 2))
}

@Test func timedUnblockAllStartedMidSecondReblocksAtItsRegisteredEnd() throws {
let h = BlockingHarness()
h.blockAll()
h.passTime(to: h.at(10).addingTimeInterval(0.6))
try h.unblockAllTimed(.thirtyMinutes)

h.wake(at: try h.registeredEnd(of: SharedConstants.mainTimedUnblockActivityName))
#expect(h.blocked == h.apps(1, 2, 3))
}

@Test func groupTimedUnblockStartedMidSecondReblocksAtItsRegisteredEnd() throws {
let h = BlockingHarness()
h.blockAll()
let social = h.group([1, 2])
h.passTime(to: h.at(10).addingTimeInterval(0.6))
try h.unblockGroupTimed(social, .oneHour)

h.wake(at: try h.registeredEnd(of: SharedConstants.groupTimedUnblockActivityName(for: social.id)))
#expect(h.blocked == h.apps(1, 2, 3))
}

@Test func blockingAGroupDuringTimedUnblockAllAppliesNowAndAfter() throws {
let h = BlockingHarness()
h.blockAll()
Expand Down
3 changes: 2 additions & 1 deletion NormalTests/Services/TimedUnblockServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,8 @@ struct TimedUnblockServiceTests {
let dto = try #require(store.timedUnblocks.first)
let start = try #require(dto.startDate)
#expect(start >= before)
#expect(dto.endDate.timeIntervalSince(start) == 5700)
let length = dto.endDate.timeIntervalSince(start)
#expect(length > 5699 && length <= 5700)
#expect(service.activeUnblockStartDates()[TimedUnblockService.mainID] == start)
}

Expand Down
7 changes: 7 additions & 0 deletions NormalTests/TestSupport/BlockingHarness.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import DeviceActivity
import FamilyControls
import Foundation
import ManagedSettings
@testable import Normal
import Testing

@MainActor
final class BlockingHarness {
Expand Down Expand Up @@ -81,6 +83,11 @@ final class BlockingHarness {
Set(ids.map(TokenFixtures.app))
}

func registeredEnd(of activityName: String) throws -> Date {
let schedule = try #require(activity.registeredSchedule(for: DeviceActivityName(activityName)))
return try #require(Calendar.current.date(from: schedule.intervalEnd))
}

func passTime(to date: Date) {
clock.now = date
}
Expand Down
Loading