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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
Test Suite 'FocusedEventShiftNudgeTests' failed at 2026-11-12 08:59:44.322.
Test Suite 'NativeCalendarRootModelAllDayTests' failed at 2026-11-12 08:59:51.205.
Test Suite 'NativeCalendarRootModelInitialWeekAnchorTests' failed at 2026-11-12 09:00:02.882.
Test Suite 'CompassDataPackageTests.xctest' failed at 2026-11-12 09:00:29.633.
Test Suite 'All tests' failed at 2026-11-12 09:00:29.633.
Executed 327 tests, with 3 tests skipped and 6 failures (0 unexpected) in 55.567 (55.666) seconds
Executed 327 tests, with 3 tests skipped and 6 failures (0 unexpected) in 55.567 (55.671) seconds
◇ Test run started.
↳ Testing Library Version: 1902
✗ ExampleSuite.example() failed after 0.001 seconds
✔ Test run with 0 tests in 0 suites passed after 0.001 seconds.
41 changes: 41 additions & 0 deletions .github/scripts/selftest-swift-packages-exit.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#!/usr/bin/env bash
# Verify swift-packages log parsing catches XCTest failures even when swift exits 0.
set -euo pipefail

ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
# shellcheck source=swift-packages-log-detect.sh
source "${ROOT}/.github/scripts/swift-packages-log-detect.sh"

fixture="${ROOT}/.github/scripts/fixtures/swift-test-xctest-failure-43186705883-sample.txt"
if ! swift_package_log_has_test_failures "$fixture"; then
echo "selftest: expected fixture log to count as failures" >&2
exit 1
fi

pass_log=$(mktemp)
trap 'rm -f "$pass_log"' EXIT
cat >"$pass_log" <<'EOF'
Test Suite 'All tests' passed at 2026-01-01 00:00:00.000.
Executed 12 tests, with 0 failures (0 unexpected) in 0.010 (0.012) seconds
◇ Test run started.
↳ Testing Library Version: 1902
✔ Test run with 0 tests in 0 suites passed after 0.001 seconds.
compiled with 3 issues in other contexts
EOF
if swift_package_log_has_test_failures "$pass_log"; then
echo "selftest: pass log must not count as failures" >&2
exit 1
fi

skipped_pass_log=$(mktemp)
trap 'rm -f "$pass_log" "$skipped_pass_log"' EXIT
cat >"$skipped_pass_log" <<'EOF'
Test Suite 'All tests' passed at 2026-01-01 00:00:00.000.
Executed 100 tests, with 3 tests skipped and 0 failures (0 unexpected) in 1.0 (1.1) seconds
EOF
if swift_package_log_has_test_failures "$skipped_pass_log"; then
echo "selftest: skipped-only summary must not count as failures" >&2
exit 1
fi

echo "selftest-swift-packages-exit: ok"
23 changes: 23 additions & 0 deletions .github/scripts/swift-packages-log-detect.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/usr/bin/env bash
# Shared swift-packages XCTest / Swift Testing failure detection (sourced, not executed).
swift_package_log_has_test_failures() {
local log="$1"
# XCTest aggregate line, including "with N tests skipped and M failures".
if grep -qE 'Executed [0-9]+ tests?, with ([0-9]+ tests? skipped and )?[1-9][0-9]* failures' "$log"; then
return 0
fi
if grep -qE "Test Suite 'All tests' failed" "$log"; then
return 0
fi
if grep -qE "Test Suite 'CompassDataPackageTests.xctest' failed" "$log"; then
return 0
fi
# Swift Testing failed runs (exclude "0 tests" pass summaries).
if grep -qE 'Test run with [1-9][0-9]* tests.*failed' "$log"; then
return 0
fi
if grep -qE '✗.*failed' "$log"; then
return 0
fi
return 1
}
23 changes: 22 additions & 1 deletion .github/scripts/test-macos-swift-packages.sh
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ export NSUnbufferedIO=YES
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
cd "$ROOT"

# shellcheck source=swift-packages-log-detect.sh
source "${ROOT}/.github/scripts/swift-packages-log-detect.sh"

bash "${ROOT}/.github/scripts/selftest-swift-packages-exit.sh"

# CompassKitTests in `swift test --package-path CompassKit` mirror the
# `apps/calendar-macos/CompassKitTests/` tree (diff -rq reports no differences).
# macos-build already runs those cases via xcodebuild -only-testing:CompassKitTests
Expand Down Expand Up @@ -47,8 +52,24 @@ run_package_tests() {
echo "── swift ${args[*]} ──"
local start
start=$(date +%s)
swift "${args[@]}"
local log
log=$(mktemp)
local swift_ec=0
set +e
swift "${args[@]}" 2>&1 | tee "$log"
swift_ec=${PIPESTATUS[0]}
set -e
local parsed_fail=0
if swift_package_log_has_test_failures "$log"; then
echo "swift-packages: ${name} reported test failures in log (swift exit ${swift_ec})" >&2
parsed_fail=1
fi
rm -f "$log"
echo "── ${name} tests finished in $(( $(date +%s) - start ))s ──"
if [ "$swift_ec" -ne 0 ] || [ "$parsed_fail" -ne 0 ]; then
return 1
fi
return 0
}

job_start=$(date +%s)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ public final class NativeCalendarRootModel {
? CompassDevicePreferences.readPinnedTimeZoneResolvingAccidentalUtc()
: nil
EffectiveTimeZone.identifier =
demoTimeZone ?? pinnedTimeZone ?? TimeZone.current.identifier
demoTimeZone ?? pinnedTimeZone ?? CompassDeviceTimeZone.currentIdentifier
eventsStore = environment.eventsStore
hiddenEventsStore = environment.hiddenEventsStore
calendarVisibilityStore = CalendarVisibilityStore()
Expand All @@ -229,7 +229,7 @@ public final class NativeCalendarRootModel {
anchorDate: anchor,
visibleDayCount: CalendarWindowMath.weekDayCount,
pinnedTimeZone: pinnedTimeZone,
browserTimeZone: demoTimeZone ?? TimeZone.current.identifier
browserTimeZone: demoTimeZone ?? CompassDeviceTimeZone.currentIdentifier
)
if demoPresentation == nil {
viewStore.setTimeTravelTimeZone(CompassDevicePreferences.readTimeTravelTimeZone())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import Foundation

/// Device (browser) timezone at app bootstrap, overridable in tests without touching globals.
public enum CompassDeviceTimeZone {
private nonisolated(unsafe) static var identifierProvider: () -> String = {
TimeZone.current.identifier
}

public static var currentIdentifier: String {
identifierProvider()
}

public static func setIdentifierProviderForTests(_ provider: @escaping () -> String) {
identifierProvider = provider
}

public static func resetIdentifierProviderForTests() {
identifierProvider = { TimeZone.current.identifier }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,12 +36,46 @@ final class NativeCalendarRootModelAllDayTests: XCTestCase {
EffectiveTimeZone.calendar.date(from: DateComponents(year: 2026, month: 3, day: day))!
}

private func denverPrimaryCalendar() -> CompassCalendar {
CompassCalendar(
access: .owner,
accountEmail: "a@example.com",
backgroundColor: "#ffffff",
capabilities: .init(
canInviteAttendees: false,
canManage: false,
canReadAvailability: true,
canReadDetails: true,
canWatchEvents: false,
canWrite: true,
conferenceKinds: []
),
conference: nil,
createsGoogleMeet: false,
description: "",
foregroundColor: "#000000",
id: "denver-primary",
isActive: true,
isPrimary: true,
isVisible: true,
name: "Primary",
provider: "google",
timeZone: IANATimeZone(rawValue: "America/Denver")
)
}

private func model(startDay: Int, endDay: Int) throws -> NativeCalendarRootModel {
CompassDeviceTimeZone.setIdentifierProviderForTests { "GMT" }
let model = NativeCalendarRootModel(environment: try NativeCalendarEnvironment(inMemoryDatabase: true))
EffectiveTimeZone.identifier = "America/Denver"
model.applyCalendarsForPresentationTesting([denverPrimaryCalendar()])
model.draftStore.startGridDraft(activity: .keyboardPlace, draft: GridEventDraft(
clientId: .init(rawValue: "all-day"), schedule: .init(start: date(startDay), end: date(endDay), kind: .allDay),
calendarId: .init(rawValue: "cal-1")))
calendarId: .init(rawValue: "denver-primary")))
return model
}

override func tearDown() async throws {
CompassDeviceTimeZone.resetIdentifierProviderForTests()
try await super.tearDown()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,52 @@ import XCTest

@MainActor
final class NativeCalendarRootModelInitialWeekAnchorTests: XCTestCase {
override func tearDown() async throws {
CompassDeviceTimeZone.resetIdentifierProviderForTests()
_ = CompassDevicePreferences.writePinnedTimeZone(nil)
try await super.tearDown()
}

func testInitialWeekAnchorUsesDeviceZoneBeforeViewStore() throws {
let priorDefault = NSTimeZone.default
defer { NSTimeZone.default = priorDefault }
NSTimeZone.default = try XCTUnwrap(TimeZone(identifier: "America/Denver"))
CompassDeviceTimeZone.setIdentifierProviderForTests { "America/Denver" }
XCTAssertTrue(CompassDevicePreferences.writePinnedTimeZone(nil))

// Monday 06:00 UTC is Monday 23:00 MST; local day differs from GMT so week anchors diverge.
let referenceNow = try XCTUnwrap(
CompassDateParsing.parseGridDateTime(
"2026-11-12T01:30:00.000",
"2026-11-10T06:00:00.000",
timeZoneIdentifier: "UTC"
)
)
let environment = try NativeCalendarEnvironment(inMemoryDatabase: true)
let model = NativeCalendarRootModel(environment: environment, now: { referenceNow })

let expected = CalendarWindowMath.sundayWeekViewStart(containing: referenceNow)
XCTAssertEqual(model.viewStore.anchorDate, expected)
XCTAssertEqual(EffectiveTimeZone.identifier, "America/Denver")
let denverAnchor = CalendarWindowMath.sundayWeekViewStart(containing: referenceNow)
XCTAssertEqual(model.viewStore.anchorDate, denverAnchor)

EffectiveTimeZone.identifier = "GMT"
let gmtAnchor = CalendarWindowMath.sundayWeekViewStart(containing: referenceNow)
XCTAssertNotEqual(denverAnchor, gmtAnchor)
}

func testInitialWeekAnchorHonorsPinnedZoneOverDevice() throws {
CompassDeviceTimeZone.setIdentifierProviderForTests { "GMT" }
XCTAssertTrue(CompassDevicePreferences.writePinnedTimeZone("America/Chicago"))

let referenceNow = try XCTUnwrap(
CompassDateParsing.parseGridDateTime(
"2026-06-15T12:00:00.000",
timeZoneIdentifier: "UTC"
)
)
let environment = try NativeCalendarEnvironment(inMemoryDatabase: true)
let model = NativeCalendarRootModel(environment: environment, now: { referenceNow })

XCTAssertEqual(EffectiveTimeZone.identifier, "America/Chicago")
XCTAssertEqual(
model.viewStore.anchorDate,
CalendarWindowMath.sundayWeekViewStart(containing: referenceNow)
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public struct EventFormView: View {
model: model,
draft: draft
)
.id(EventFormField.recurrence)
.disabled(model.isScheduleLocked(for: draft))
}
}
Expand Down
Loading