diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3ddfc2f..c0e5fb2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,5 +1,7 @@ name: CI on: [pull_request] +permissions: + contents: read jobs: build-and-test: runs-on: macos-15 @@ -7,3 +9,18 @@ jobs: - uses: actions/checkout@v4 - run: swift build --build-tests - run: swift test --filter MetaWearTests # skip hardware tests in CI + # The app target's unit tests (coordinator E2E against the demo fleet, + # history grouping, foreign-log decisions, …) were invisible to CI — + # only the SPM suites ran. The app requires the iOS 26 SDK, hence the + # newer runner image. + app-tests: + runs-on: macos-26 + steps: + - uses: actions/checkout@v4 + - run: > + xcodebuild test + -project Apps/MetaWear/MetaWearApp.xcodeproj + -scheme MetaWearApp + -destination 'platform=iOS Simulator,name=iPhone 17' + -only-testing:MetaWearAppTests + CODE_SIGNING_ALLOWED=NO diff --git a/Apps/MetaWear/MetaWear/App/RootView.swift b/Apps/MetaWear/MetaWear/App/RootView.swift index 51e0315..0c289c5 100644 --- a/Apps/MetaWear/MetaWear/App/RootView.swift +++ b/Apps/MetaWear/MetaWear/App/RootView.swift @@ -145,8 +145,8 @@ private struct ErrorAndOrphanAlerts: ViewModifier { // so the user can decide what to do with it. The alert only // fires when LOG_LENGTH > 0 *and* we have no matching local // pending session — the in-app logging flow has its own UI - // for sessions it already knows about (LoggingPill, - // DownloadView). + // for sessions it already knows about (the Logging + // screen's status row, DownloadView). .alert( "Logging in progress", isPresented: Binding( diff --git a/Apps/MetaWear/MetaWear/Features/Logging/LogSessionView.swift b/Apps/MetaWear/MetaWear/Features/Logging/LogSessionView.swift index 5e8b896..51c8b9e 100644 --- a/Apps/MetaWear/MetaWear/Features/Logging/LogSessionView.swift +++ b/Apps/MetaWear/MetaWear/Features/Logging/LogSessionView.swift @@ -197,7 +197,7 @@ struct LogSessionView: View { Task { await viewModel?.start(selections) // Refresh the AppStore's pending-sessions cache so the - // global `LoggingPill` becomes visible immediately. + // pending-log badges appear immediately. appStore.refreshPendingLogSessions() } } diff --git a/Apps/MetaWear/MetaWear/Features/Logging/LoggingPill.swift b/Apps/MetaWear/MetaWear/Features/Logging/LoggingPill.swift deleted file mode 100644 index fab2999..0000000 --- a/Apps/MetaWear/MetaWear/Features/Logging/LoggingPill.swift +++ /dev/null @@ -1,53 +0,0 @@ -import SwiftUI - -/// Top-of-screen pill that surfaces an active logging session on the connected -/// device. Only shown while a session is *actually recording* (`status == .running`); -/// `.stopped` sessions (data captured, awaiting download) show up as the -/// remembered-device row badge instead, so the pill doesn't keep ticking after -/// the user has stopped the session. -struct LoggingPill: View { - @Environment(AppStore.self) private var appStore - @State private var now: Date = .now - - private var runningSessions: [LogSessionRecord] { - appStore.pendingLogSessions.filter { - $0.deviceID == appStore.activeDeviceID && $0.status == .running - } - } - - private var earliestStart: Date? { - runningSessions.map(\.startDate).min() - } - - var body: some View { - if let start = earliestStart { - HStack(spacing: 8) { - Image(systemName: "record.circle.fill") - .foregroundStyle(Palette.danger) - .symbolEffect(.pulse, options: .repeating) - .accessibilityHidden(true) - Text("Logging · \(elapsed(from: start))") - .font(.subheadline.weight(.medium)) - .monospacedDigit() - } - .glassPill(tint: Palette.danger.opacity(0.25)) - .task { - while !Task.isCancelled { - now = .now - try? await Task.sleep(for: .seconds(1)) - } - } - .accessibilityLabel("Logging in progress, \(elapsed(from: start)) elapsed") - } - } - - private func elapsed(from start: Date) -> String { - let elapsed = Int(now.timeIntervalSince(start)) - let m = elapsed / 60, s = elapsed % 60 - return "\(twoDigits(m)):\(twoDigits(s))" - } - - private func twoDigits(_ value: Int) -> String { - value < 10 ? "0\(value)" : "\(value)" - } -} diff --git a/Apps/MetaWear/MetaWear/ViewModels/LogSessionViewModel.swift b/Apps/MetaWear/MetaWear/ViewModels/LogSessionViewModel.swift index 59375c1..3be2cc9 100644 --- a/Apps/MetaWear/MetaWear/ViewModels/LogSessionViewModel.swift +++ b/Apps/MetaWear/MetaWear/ViewModels/LogSessionViewModel.swift @@ -167,7 +167,7 @@ final class LogSessionViewModel { // Per-record try/catch — a single failed `stopOne` (BLE hiccup, // unexpected board state) used to abort the loop, leaving the // remaining records' `status` stuck at `.running` and the global - // `LoggingPill` ticking forever. We always flip the status to + // pending-log badges stuck forever. We always flip the status to // `.stopped` even when the BLE command threw, because: // - the user explicitly asked the session to stop, and // - the captured data is still on the board for download.