mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
Refactor and unify firmware update logic across platforms (#4966)
This commit is contained in:
parent
d8e295cafb
commit
89547afe6b
102 changed files with 7206 additions and 3485 deletions
|
|
@ -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).
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
---
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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. 🚀
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue