Refactor and unify firmware update logic across platforms (#4966)

This commit is contained in:
James Rich 2026-04-01 07:14:26 -05:00 committed by GitHub
parent d8e295cafb
commit 89547afe6b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
102 changed files with 7206 additions and 3485 deletions

View file

@ -6,9 +6,10 @@ Architectural decision records and reviews. Each captures context, decision, and
|---|---|---|
| Architecture review (March 2026) | [`architecture-review-2026-03.md`](./architecture-review-2026-03.md) | Active |
| Navigation 3 parity strategy (Android + Desktop) | [`navigation3-parity-2026-03.md`](./navigation3-parity-2026-03.md) | Active |
| BLE KMP strategy (Nordic Hybrid) | [`ble-strategy.md`](./ble-strategy.md) | Decided |
| Navigation 3 API alignment audit | [`navigation3-api-alignment-2026-03.md`](./navigation3-api-alignment-2026-03.md) | Active |
| BLE KMP strategy (Kable) | [`ble-strategy.md`](./ble-strategy.md) | Decided |
| Hilt → Koin migration | [`koin-migration.md`](./koin-migration.md) | Complete |
| Testing consolidation (`core:testing`) | [`testing-consolidation-2026-03.md`](./testing-consolidation-2026-03.md) | Complete |
For the current KMP migration status, see [`docs/kmp-status.md`](../kmp-status.md).
For the forward-looking roadmap, see [`docs/roadmap.md`](../roadmap.md).

View file

@ -1,7 +1,7 @@
# Architecture Review — March 2026
> Status: **Active**
> Last updated: 2026-03-12
> Last updated: 2026-03-31
Re-evaluation of project modularity and architecture against modern KMP and Android best practices. Identifies gaps and actionable improvements across modularity, reusability, clean abstractions, DI, and testing.
@ -65,7 +65,6 @@ The core transport abstraction was previously locked in `app/repository/radio/`
**Recommended next steps:**
1. Move BLE transport to `core:ble/androidMain`
2. Move Serial/USB transport to `core:service/androidMain`
3. Retire Desktop's parallel `DesktopRadioInterfaceService` — use the shared `RadioTransport` + `TcpTransport`
### A3. No `feature:connections` module *(resolved 2026-03-12)*
@ -176,7 +175,7 @@ Android uses `@Module`-annotated classes (`CoreDataModule`, `CoreBleAndroidModul
### D2. No shared test fixtures *(resolved 2026-03-12)*
`core:testing` module established with shared fakes (`FakeNodeRepository`, `FakeServiceRepository`, `FakeRadioController`, `FakeRadioConfigRepository`, `FakePacketRepository`) and `TestDataFactory` builders. Used by all feature `commonTest` suites.
`core:testing` module established with shared fakes (`FakeNodeRepository`, `FakeServiceRepository`, `FakeRadioController`, `FakePacketRepository`) and `TestDataFactory` builders. Used by all feature `commonTest` suites.
### D3. Core module test gaps
@ -187,10 +186,9 @@ Android uses `@Module`-annotated classes (`CoreDataModule`, `CoreBleAndroidModul
- `core:ble` (connection state machine)
- `core:ui` (utility functions)
### D4. Desktop has 6 tests
### D4. Desktop has 2 tests
`desktop/src/test/` contains `DemoScenarioTest.kt` and `DesktopKoinTest.kt`. Still needs:
- `DesktopRadioInterfaceService` connection state tests
`desktop/src/test/` contains `DesktopKoinTest.kt` and `DesktopTopLevelDestinationParityTest.kt`. Still needs:
- Navigation graph coverage
---

View file

@ -17,7 +17,8 @@ However, as Desktop integration advanced, we found the need for a unified BLE tr
- We migrated all BLE transport logic across Android and Desktop to use Kable.
- The `commonMain` interfaces (`BleConnection`, `BleScanner`, `BleDevice`, `BluetoothRepository`, etc.) remain, but their core implementations (`KableBleConnection`, `KableBleScanner`) are now entirely shared in `commonMain`.
- The Android-specific Nordic dependencies (`no.nordicsemi.kotlin.ble:*`) and the Nordic DFU library were completely excised from the project.
- OTA Firmware updates on Android were successfully refactored to use the Kable-based `BleOtaTransport`.
- OTA Firmware updates were successfully refactored to use the Kable-based `BleOtaTransport`, shared across Android and Desktop in `commonMain`.
- Nordic Secure DFU was reimplemented as a pure KMP protocol stack (`SecureDfuTransport`, `SecureDfuProtocol`, `SecureDfuHandler`) using Kable, with no dependency on the Nordic DFU library.
## Consequences

View file

@ -8,7 +8,7 @@ Hilt (Dagger) was the strongest remaining barrier to KMP adoption — it require
## Decision
Migrated to **Koin 4.2.0-RC1** with the **K2 Compiler Plugin** (`io.insert-koin.compiler.plugin`) and later upgraded to **0.4.0**.
Migrated to **Koin 4.2.0-RC1** with the **K2 Compiler Plugin** (`io.insert-koin.compiler.plugin`) and later upgraded to **0.4.1**.
Key choices:
- `@KoinViewModel` replaces `@HiltViewModel`; `koinViewModel()` replaces `hiltViewModel()`
@ -16,7 +16,7 @@ Key choices:
- `@KoinWorker` replaces `@HiltWorker` for WorkManager
- `@InjectedParam` replaces `@Assisted` for factory patterns
- Root graph assembly centralized in `AppKoinModule`; shared modules expose annotated definitions
- **Koin 0.4.0 A1 Compile Safety Disabled:** Meshtastic heavily utilizes dependency inversion across KMP modules (e.g., interfaces defined in `core:repository` are implemented in `core:data`). Koin 0.4.0's per-module A1 validation strictly enforces that all dependencies must be explicitly provided or included locally, breaking this clean architecture. We have globally disabled A1 `compileSafety` in `KoinConventionPlugin` to properly rely on Koin's A3 full-graph validation at the composition root (`startKoin`).
- **Koin 0.4.1 A1 Compile Safety Disabled:** Meshtastic heavily utilizes dependency inversion across KMP modules (e.g., interfaces defined in `core:repository` are implemented in `core:data`). Koin 0.4.x's per-module A1 validation strictly enforces that all dependencies must be explicitly provided or included locally, breaking this clean architecture. We have globally disabled A1 `compileSafety` in `KoinConventionPlugin` to properly rely on Koin's A3 full-graph validation at the composition root (`startKoin`).
## Gotchas Discovered

View file

@ -30,36 +30,42 @@
### 1. NavDisplay — Scene Architecture (available since `1.1.0-alpha04`, stable in `beta01`)
**Remaining APIs we're NOT using broadly yet:**
**Available APIs we're NOT using:**
| API | Purpose | Status in project |
|---|---|---|
| `sceneStrategies: List<SceneStrategy<T>>` | Allows NavDisplay to render multi-pane Scenes | ⚠️ Partially used — `MeshtasticNavDisplay` applies dialog/list-detail/supporting/single strategies |
| `SceneStrategy<T>` interface | Custom scene calculation from backstack entries | ❌ Not used |
| `DialogSceneStrategy` | Renders `entry<T>(metadata = dialog())` entries as overlay Dialogs | ✅ Enabled in shared host wrapper |
| `sceneStrategies: List<SceneStrategy<T>>` | Allows NavDisplay to render multi-pane Scenes | ✅ Used — `DialogSceneStrategy`, `ListDetailSceneStrategy`, `SupportingPaneSceneStrategy`, `SinglePaneSceneStrategy` |
| `SceneStrategy<T>` interface | Custom scene calculation from backstack entries | ✅ Used via built-in strategies |
| `DialogSceneStrategy` | Renders `entry<T>(metadata = dialog())` entries as overlay Dialogs | ✅ Adopted |
| `SceneDecoratorStrategy<T>` | Wraps/decorates scenes with additional UI | ❌ Not used |
| `NavEntry.metadata` | Attaches typed metadata to entries (transitions, dialog hints, Scene classification) | ❌ Not used |
| `NavEntry.metadata` | Attaches typed metadata to entries (transitions, dialog hints, Scene classification) | ✅ Used — `ListDetailSceneStrategy.listPane()`, `.detailPane()`, `.extraPane()` |
| `NavDisplay.TransitionKey` / `PopTransitionKey` / `PredictivePopTransitionKey` | Per-entry custom transitions via metadata | ❌ Not used |
| `transitionSpec` / `popTransitionSpec` / `predictivePopTransitionSpec` params | Default transition animations for NavDisplay | ⚠️ Partially used — shared forward/pop crossfade adopted; predictive-pop custom spec not yet used |
| `transitionSpec` / `popTransitionSpec` / `predictivePopTransitionSpec` params | Default transition animations for NavDisplay | ✅ Used — 350 ms crossfade |
| `sharedTransitionScope: SharedTransitionScope?` | Shared element transitions between scenes | ❌ Not used |
| `entryDecorators: List<NavEntryDecorator<T>>` | Wraps entry content with additional behavior | ✅ Used via `MeshtasticNavDisplay` (`SaveableStateHolder` + `ViewModelStore`) |
| `entryDecorators: List<NavEntryDecorator<T>>` | Wraps entry content with additional behavior | ✅ Used `SaveableStateHolderNavEntryDecorator` + `ViewModelStoreNavEntryDecorator` |
**APIs we ARE using correctly:**
| API | Usage |
|---|---|
| `MeshtasticNavDisplay(...)` wrapper around `NavDisplay` | Both `app/Main.kt` and `desktop/DesktopMainScreen.kt` |
| `NavDisplay(backStack, entryProvider, modifier)` | Both `app/Main.kt` and `desktop/DesktopMainScreen.kt` |
| `rememberNavBackStack(SavedStateConfiguration, startKey)` | Backstack persistence |
| `entryProvider<NavKey> { entry<T> { ... } }` | All feature graph registrations |
| `NavigationBackHandler` from `navigationevent-compose` | Used in `AdaptiveListDetailScaffold` |
| `NavigationBackHandler` from `navigationevent-compose` | Used with `ListDetailSceneStrategy` |
### 2. ViewModel Scoping (`lifecycle-viewmodel-navigation3` `2.11.0-alpha02`)
**Current status:** Adopted. `MeshtasticNavDisplay` applies `rememberViewModelStoreNavEntryDecorator()` with `rememberSaveableStateHolderNavEntryDecorator()`, so `koinViewModel()` instances are entry-scoped and clear on pop.
**Key finding:** The `ViewModelStoreNavEntryDecorator` is available and provides automatic per-entry ViewModel scoping tied to backstack lifetime. The project passes it as an `entryDecorator` to `NavDisplay` via `MeshtasticNavDisplay` in `core:ui/commonMain`.
ViewModels obtained via `koinViewModel()` inside `entry<T>` blocks are scoped to the entry's backstack lifetime and automatically cleared when the entry is popped.
### 3. Material 3 Adaptive — Nav3 Scene Integration
**Current status:** Adopted for shared host-level strategies. `MeshtasticNavDisplay` uses adaptive Navigation 3 scene strategies (`rememberListDetailSceneStrategy`, `rememberSupportingPaneSceneStrategy`) with draggable pane expansion handles, while feature-level scaffold composition remains valid for route-specific layouts.
**Key finding:** The JetBrains `adaptive-navigation3` artifact at `1.3.0-alpha06` includes `ListDetailSceneStrategy` and `SupportingPaneSceneStrategy`. The project uses both via `rememberListDetailSceneStrategy` and `rememberSupportingPaneSceneStrategy` in `MeshtasticNavDisplay`, with draggable pane dividers via `VerticalDragHandle` + `paneExpansionDraggable`.
This means the project **successfully** uses the M3 Adaptive Scene bridge through `NavDisplay(sceneStrategies = ...)`. Feature entries annotate themselves with `ListDetailSceneStrategy.listPane()`, `.detailPane()`, or `.extraPane()` metadata.
**When to revisit:** Monitor the JetBrains adaptive fork for `MaterialListDetailSceneStrategy` inclusion. It will likely arrive when the JetBrains fork catches up to the AndroidX `1.3.0-alpha09+` feature set.
### 4. NavigationSuiteScaffold (`1.11.0-alpha05`)
@ -89,7 +95,7 @@
**Status:** ✅ Adopted (2026-03-26). A new `MeshtasticNavDisplay` composable in `core:ui/commonMain` encapsulates the standard `NavDisplay` configuration:
- Entry decorators: `rememberSaveableStateHolderNavEntryDecorator` + `rememberViewModelStoreNavEntryDecorator`
- Scene strategies: `DialogSceneStrategy` + adaptive list-detail/supporting pane strategies + `SinglePaneSceneStrategy`
- Scene strategies: `DialogSceneStrategy` + `SinglePaneSceneStrategy`
- Transition specs: 350 ms crossfade (forward + pop)
Both `app/Main.kt` and `desktop/DesktopMainScreen.kt` now call `MeshtasticNavDisplay` instead of configuring `NavDisplay` directly. The `lifecycle-viewmodel-navigation3` dependency was moved from host modules to `core:ui`.
@ -100,9 +106,9 @@ Individual entries can declare custom transitions via `entry<T>(metadata = NavDi
**Impact:** Polish improvement. Low priority until default transitions (P1) are established. Now unblocked by P1 adoption.
### Deferred: Scene-based multi-pane layout
### Deferred: Custom Scene strategies
Additional route-level Scene metadata adoption is deferred. The project now applies shared adaptive scene strategies in `MeshtasticNavDisplay`, and feature-level `AdaptiveListDetailScaffold` remains valid for route-specific layouts. Revisit custom per-route `SceneStrategy` policies when multi-pane route classification needs expand.
The `ListDetailSceneStrategy` and `SupportingPaneSceneStrategy` are adopted and working. Consider writing additional custom `SceneStrategy` implementations for specialized layouts (e.g., three-pane "Power User" scenes) as the Navigation 3 Scene API matures.
## Decision

View file

@ -15,142 +15,24 @@
- along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
# Testing Consolidation: `core:testing` Module
# Decision: Testing Consolidation — `core:testing` Module
**Date:** 2026-03-11
**Status:** Implemented
**Scope:** KMP test consolidation across all core and feature modules
## Overview
## Context
Created `core:testing` as a lightweight, reusable module for **shared test doubles, fakes, and utilities** across all Meshtastic-Android KMP modules. This consolidates testing dependencies and keeps the module dependency graph clean.
Each KMP module independently declared scattered test dependencies (`junit`, `mockk`, `coroutines-test`, `turbine`), leading to version drift and duplicated test doubles across modules.
## Design Principles
## Decision
### 1. Lightweight Dependencies Only
```
core:testing
├── depends on: core:model, core:repository
├── depends on: kotlin("test"), kotlinx.coroutines.test, turbine, junit
└── does NOT depend on: core:database, core:data, core:domain
```
Created `core:testing` as a lightweight shared module for test doubles, fakes, and utilities. It depends only on `core:model` and `core:repository` (no heavy deps like `core:database`). All modules declare `implementation(projects.core.testing)` in `commonTest` to get a unified test dependency set.
**Rationale:** `core:database` has KSP processor dependencies that can slow builds. Isolating `core:testing` with minimal deps ensures:
- Fast compilation of test infrastructure
- No circular dependency risk
- Modules depending on `core:testing` (via `commonTest`) don't drag heavy transitive deps
## Consequences
### 2. No Production Code Leakage
- `:core:testing` is declared **only in `commonTest` sourceSet**, never in `commonMain`
- Test code never appears in APKs or release JARs
- Strict separation between production and test concerns
### 3. Dependency Graph
```
┌─────────────────────┐
│ core:testing │
│ (light: model, │
│ repository) │
└──────────┬──────────┘
│ (commonTest only)
┌────┴─────────┬───────────────┐
↓ ↓ ↓
core:domain feature:messaging feature:node
core:data feature:settings etc.
```
Heavy modules (`core:domain`, `core:data`) depend on `:core:testing` in their test sources, **not** vice versa.
## Consolidation Strategy
### What Was Unified
**Before:**
```kotlin
// Each module's build.gradle.kts had scattered test deps
commonTest.dependencies {
implementation(libs.junit)
implementation(libs.mockk)
implementation(libs.kotlinx.coroutines.test)
implementation(libs.turbine)
}
```
**After:**
```kotlin
// All modules converge on single dependency
commonTest.dependencies {
implementation(projects.core.testing)
}
// core:testing re-exports all test libraries
```
### Modules Updated
- ✅ `core:domain` — test doubles for domain logic
- ✅ `feature:messaging` — commonTest bootstrap
- ✅ `feature:settings`, `feature:node`, `feature:intro`, `feature:map`, `feature:firmware`
## What's Included
### Test Doubles (Fakes)
- **`FakeRadioController`** — No-op `RadioController` with call tracking
- **`FakeNodeRepository`** — In-memory `NodeRepository` for isolated tests
- *(Extensible)* — Add new fakes as needed
### Test Builders & Factories
- **`TestDataFactory`** — Create domain objects (nodes, users) with sensible defaults
```kotlin
val node = TestDataFactory.createTestNode(num = 42)
val nodes = TestDataFactory.createTestNodes(count = 10)
```
### Test Utilities
- **Flow collection helper**`flow.toList()` for assertions
## Benefits
| Aspect | Before | After |
|--------|--------|-------|
| **Dependency Duplication** | Each module lists test libs separately | Single consolidated dependency |
| **Build Purity** | Test deps scattered across modules | One central, curated source |
| **Dependency Graph** | Risk of circular deps or conflicting versions | Clean, acyclic graph with minimal weights |
| **Reusability** | Fakes live in test sources of single module | Shared across all modules via `core:testing` |
| **Maintenance** | Updating test libs touches multiple files | Single `core:testing/build.gradle.kts` |
## Maintenance Guidelines
### Adding a New Test Double
1. Implement the interface from `core:model` or `core:repository`
2. Add call tracking for assertions (e.g., `sentPackets`, `callHistory`)
3. Provide test helpers (e.g., `setNodes()`, `clear()`)
4. Document with KDoc and example usage
### When Adding a New Module with Tests
- Add `implementation(projects.core.testing)` to its `commonTest.dependencies`
- Reuse existing fakes; create new ones only if genuinely reusable
### When Updating Repository Interfaces
- Update corresponding fakes in `:core:testing` to match new signatures
- Fakes remain no-op; don't replicate business logic
## Files & Documentation
- **`core/testing/build.gradle.kts`** — Minimal dependencies, KMP targets
- **`core/testing/README.md`** — Comprehensive usage guide with examples
- **`AGENTS.md`** — Updated with `:core:testing` description and testing rules
- **`feature/messaging/src/commonTest/`** — Bootstrap example test
## Next Steps
1. **Monitor compilation times** — Verify that isolating `core:testing` improves build speed
2. **Add more fakes as needed** — As feature modules add comprehensive tests, add fakes to `core:testing`
3. **Consider feature-specific extensions** — If a feature needs heavy, specialized test setup, keep it local; don't bloat `core:testing`
4. **Cross-module test sharing** — Enable tests across modules to reuse fakes (e.g., integration tests)
## Related Documentation
- `core/testing/README.md` — Detailed usage and API reference
- `AGENTS.md` § 3B — Testing rules and KMP purity
- `.github/copilot-instructions.md` — Build commands
- `docs/kmp-status.md` — KMP module status
- **Single source** for test fakes (`FakeRadioController`, `FakeNodeRepository`, `TestDataFactory`)
- **Clean dependency graph**`core:testing` is lightweight; heavy modules depend on it in test scope, not vice versa
- **No production leakage** — only declared in `commonTest`, never in release artifacts
- **Reduced maintenance** — updating test libraries touches one `build.gradle.kts`
See [`core/testing/README.md`](../../core/testing/README.md) for usage guide and API reference.

View file

@ -1,235 +0,0 @@
# Testing Consolidation in the KMP Migration Timeline
**Context:** This slice is part of the broader **Meshtastic-Android KMP Migration**.
## Position in KMP Migration Roadmap
```
KMP Migration Timeline
├─ Phase 1: Foundation (Completed)
│ ├─ Create core:model, core:repository, core:common
│ ├─ Set up KMP infrastructure
│ └─ Establish build patterns
├─ Phase 2: Core Business Logic (In Progress)
│ ├─ core:domain (usecases, business logic)
│ ├─ core:data (managers, orchestration)
│ └─ ✅ core:testing (TEST CONSOLIDATION ← YOU ARE HERE)
├─ Phase 3: Features (Next)
│ ├─ feature:messaging (+ tests)
│ ├─ feature:node (+ tests)
│ ├─ feature:settings (+ tests)
│ └─ feature:map, feature:firmware, etc. (+ tests)
├─ Phase 4: Non-Android Targets
│ ├─ desktop/ (Compose Desktop, first KMP target)
│ └─ iOS (future)
└─ Phase 5: Full KMP Realization
└─ All modules with 100% KMP coverage
```
## Why Testing Consolidation Matters Now
### Before KMP Testing Consolidation
```
Each module had scattered test dependencies:
feature:messaging → libs.junit, libs.turbine
feature:node → libs.junit, libs.turbine
core:domain → libs.junit, libs.turbine
Result: Duplication, inconsistency, hard to maintain
Problem: New developers don't know testing patterns
```
### After KMP Testing Consolidation
```
All modules share core:testing:
feature:messaging → projects.core.testing
feature:node → projects.core.testing
core:domain → projects.core.testing
Result: Single source of truth, consistent patterns
Benefit: Easier onboarding, faster development
```
## Integration Points
### 1. Core Domain Tests
`core:domain` now uses fakes from `core:testing` instead of local doubles:
```
Before:
core:domain/src/commonTest/FakeRadioController.kt (local)
↓ duplication
core:domain/src/commonTest/*Test.kt
After:
core:testing/src/commonMain/FakeRadioController.kt (shared)
↓ reused
core:domain/src/commonTest/*Test.kt
feature:messaging/src/commonTest/*Test.kt
feature:node/src/commonTest/*Test.kt
```
### 2. Feature Module Tests
All feature modules can now use unified test infrastructure:
```
feature:messaging, feature:node, feature:settings, feature:intro, etc.
└── commonTest.dependencies { implementation(projects.core.testing) }
└── Access to: FakeRadioController, FakeNodeRepository, TestDataFactory
```
### 3. Desktop Target Testing
`desktop/` module (first non-Android KMP target) benefits immediately:
```
desktop/src/commonTest/
├── Can use FakeNodeRepository (no Android deps!)
├── Can use TestDataFactory (KMP pure)
└── All tests run on JVM without special setup
```
## Dependency Graph Evolution
### Before (Scattered)
```
app
├── core:domain ← junit, mockk, turbine (in commonTest)
├── core:data ← junit, mockk, turbine (in commonTest)
├── feature:* ← junit, mockk, turbine (in commonTest)
└── (7+ modules with 5 scattered test deps each)
```
### After (Consolidated)
```
app
├── core:testing ← Single lightweight module
│ ├── core:domain (depends in commonTest)
│ ├── core:data (depends in commonTest)
│ ├── feature:* (depends in commonTest)
│ └── (All modules share same test infrastructure)
└── No circular dependencies ✅
```
## Downstream Benefits for Future Phases
### Phase 3: Feature Development
```
Adding feature:myfeature?
1. Add commonTest.dependencies { implementation(projects.core.testing) }
2. Use FakeNodeRepository, TestDataFactory immediately
3. Write tests using existing patterns
4. Done! No need to invent local test infrastructure
```
### Phase 4: Desktop Target
```
Implementing desktop/ (first non-Android KMP target)?
1. core:testing already has NO Android deps
2. All fakes work on JVM (no Android context needed)
3. Tests run on desktop instantly
4. No special handling needed ✅
```
### Phase 5: iOS Target (Future)
```
When iOS support arrives:
1. core:testing fakes will work on iOS (pure Kotlin)
2. All business logic tests already run on iOS
3. No test infrastructure changes needed
4. Massive time savings ✅
```
## Alignment with KMP Principles
### Platform Purity (AGENTS.md § 3B)
`core:testing` contains NO Android/Java imports
✅ All fakes use pure KMP types
✅ Works on all targets: JVM, Android, Desktop, iOS (future)
### Dependency Clarity (AGENTS.md § 3B)
✅ core:testing depends ONLY on core:model, core:repository
✅ No circular dependencies
✅ Clear separation: production vs. test
### Reusability (AGENTS.md § 3B)
✅ Test doubles shared across 7+ modules
✅ Factories and builders available everywhere
✅ Consistent testing patterns enforced
## Success Metrics
### Achieved This Slice ✅
| Metric | Target | Actual |
|--------|--------|--------|
| Dependency Consolidation | 70% | **80%** |
| Circular Dependencies | 0 | **0** |
| Documentation Completeness | 80% | **100%** |
| Bootstrap Tests | 3+ modules | **7 modules** |
| Build Verification | All targets | **JVM + Android** |
### Enabling Future Phases 🚀
| Future Phase | Blocker Removed | Benefit |
|-------------|-----------------|---------|
| Phase 3: Features | Test infrastructure | Can ship features faster |
| Phase 4: Desktop | KMP test support | Desktop tests work out-of-box |
| Phase 5: iOS | Multi-target testing | iOS tests use same fakes |
## Roadmap Alignment
```
Meshtastic-Android Roadmap (docs/roadmap.md)
├─ KMP Foundation Phase ← Phase 1-2
│ ├─ ✅ core:model
│ ├─ ✅ core:repository
│ ├─ ✅ core:domain
│ └─ ✅ core:testing (THIS SLICE)
├─ Feature Consolidation Phase ← Phase 3 (ready to start)
│ └─ All features with KMP + tests using core:testing
├─ Desktop Launch Phase ← Phase 4 (enabled by this slice)
│ └─ desktop/ module with full test support
└─ iOS & Multi-Platform Phase ← Phase 5
└─ iOS support using same test infrastructure
```
## Contributing to Migration Success
### Before This Slice
Developers had to:
1. Find where test dependencies were declared
2. Understand scattered patterns across modules
3. Create local test doubles for each feature
4. Worry about duplication
### After This Slice
Developers now:
1. Import from `core:testing` (single location)
2. Follow unified patterns
3. Reuse existing test doubles
4. Focus on business logic, not test infrastructure
---
## Related Documentation
- `docs/roadmap.md` — Overall KMP migration roadmap
- `docs/kmp-status.md` — Current KMP status by module
- `AGENTS.md` — KMP development guidelines
- `docs/decisions/architecture-review-2026-03.md` — Architecture review context
- `.github/copilot-instructions.md` — Build & test commands
---
**Testing consolidation is a foundational piece of the KMP migration that:**
1. Establishes patterns for all future feature work
2. Enables Desktop target testing (Phase 4)
3. Prepares for iOS support (Phase 5)
4. Improves developer velocity across all phases
This slice unblocks the next phases of the KMP migration. 🚀