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
|
|
@ -1,97 +0,0 @@
|
|||
# Build Convention: Test Dependencies for KMP Modules
|
||||
|
||||
## Summary
|
||||
|
||||
We've centralized test dependency configuration for Kotlin Multiplatform (KMP) modules by creating a new build convention plugin function. This eliminates code duplication across all feature and core modules.
|
||||
|
||||
## Changes Made
|
||||
|
||||
### 1. **New Convention Function** (`build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt`)
|
||||
|
||||
Added `configureKmpTestDependencies()` function that automatically configures test dependencies for all KMP modules:
|
||||
|
||||
```kotlin
|
||||
internal fun Project.configureKmpTestDependencies() {
|
||||
extensions.configure<KotlinMultiplatformExtension> {
|
||||
sourceSets.apply {
|
||||
val commonTest = findByName("commonTest") ?: return@apply
|
||||
commonTest.dependencies {
|
||||
implementation(kotlin("test"))
|
||||
}
|
||||
|
||||
// Configure androidHostTest if it exists
|
||||
val androidHostTest = findByName("androidHostTest")
|
||||
androidHostTest?.dependencies {
|
||||
implementation(kotlin("test"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Benefits:**
|
||||
- Single source of truth for test framework dependencies
|
||||
- Automatically applied to all KMP modules using `meshtastic.kmp.library`
|
||||
- Reduces build.gradle.kts boilerplate across 7+ feature modules
|
||||
|
||||
### 2. **Plugin Integration** (`build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt`)
|
||||
|
||||
Updated `KmpLibraryConventionPlugin` to call the new function:
|
||||
|
||||
```kotlin
|
||||
configureKotlinMultiplatform()
|
||||
configureKmpTestDependencies() // NEW
|
||||
configureAndroidMarketplaceFallback()
|
||||
```
|
||||
|
||||
### 3. **Removed Duplicate Dependencies**
|
||||
|
||||
Removed manual `implementation(kotlin("test"))` declarations from:
|
||||
- `feature/messaging/build.gradle.kts`
|
||||
- `feature/firmware/build.gradle.kts`
|
||||
- `feature/intro/build.gradle.kts`
|
||||
- `feature/map/build.gradle.kts`
|
||||
- `feature/node/build.gradle.kts`
|
||||
- `feature/settings/build.gradle.kts`
|
||||
- `feature/connections/build.gradle.kts`
|
||||
|
||||
Each module now only declares project-specific test dependencies:
|
||||
```kotlin
|
||||
commonTest.dependencies {
|
||||
implementation(projects.core.testing)
|
||||
// kotlin("test") is now added by convention!
|
||||
}
|
||||
```
|
||||
|
||||
## Impact
|
||||
|
||||
### Before
|
||||
- 7+ feature modules each manually adding `implementation(kotlin("test"))` to `commonTest.dependencies`
|
||||
- 7+ feature modules each manually adding `implementation(kotlin("test"))` to `androidHostTest` source sets
|
||||
- High risk of inconsistency or missing dependencies in new modules
|
||||
|
||||
### After
|
||||
- Single configuration in `build-logic/` applies to all KMP modules
|
||||
- Guaranteed consistency across all feature modules
|
||||
- Future modules automatically benefit from this convention
|
||||
- Build.gradle.kts files are cleaner and more focused on module-specific dependencies
|
||||
|
||||
## Testing
|
||||
|
||||
Verified with:
|
||||
```bash
|
||||
./gradlew :feature:node:testAndroidHostTest :feature:settings:testAndroidHostTest
|
||||
# BUILD SUCCESSFUL
|
||||
```
|
||||
|
||||
The convention plugin automatically provides `kotlin("test")` to all commonTest and androidHostTest source sets in KMP modules.
|
||||
|
||||
## Future Considerations
|
||||
|
||||
If additional test framework dependencies are needed across all KMP modules (e.g., new assertion libraries, mocking frameworks), they can be added to `configureKmpTestDependencies()` in one place, automatically benefiting all KMP modules.
|
||||
|
||||
This follows the established pattern in the project for convention plugins, as seen with:
|
||||
- `configureComposeCompiler()` - centralizes Compose compiler configuration
|
||||
- `configureKotlinAndroid()` - centralizes Kotlin/Android base configuration
|
||||
- Koin, Detekt, Spotless conventions - all follow this pattern
|
||||
|
||||
|
|
@ -286,8 +286,5 @@ tasks.withType<Test>().configureEach {
|
|||
## Related Files
|
||||
|
||||
- `AGENTS.md` - Development guidelines (Section 3.B testing, Section 4.A build protocol)
|
||||
- `docs/BUILD_LOGIC_INDEX.md` - Current build-logic doc entry point (with links to active references)
|
||||
|
||||
- `build-logic/convention/build.gradle.kts` - Convention plugin build config
|
||||
- `.github/copilot-instructions.md` - Build & test commands
|
||||
|
||||
|
|
|
|||
|
|
@ -1,41 +0,0 @@
|
|||
# Build-Logic Documentation Index
|
||||
|
||||
Quick navigation guide for build-logic conventions in this repository.
|
||||
|
||||
## Start Here
|
||||
|
||||
- New to build-logic? -> `docs/BUILD_LOGIC_CONVENTIONS_GUIDE.md`
|
||||
- Need test-dependency specifics? -> `docs/BUILD_CONVENTION_TEST_DEPS.md`
|
||||
- Need implementation code? -> `build-logic/convention/src/main/kotlin/`
|
||||
|
||||
## Primary Docs (Current)
|
||||
|
||||
| Document | Purpose |
|
||||
| :--- | :--- |
|
||||
| `docs/BUILD_LOGIC_CONVENTIONS_GUIDE.md` | Canonical conventions, duplication heuristics, verification commands, common pitfalls |
|
||||
| `docs/BUILD_CONVENTION_TEST_DEPS.md` | Rationale and behavior for centralized KMP test dependencies |
|
||||
|
||||
## Key Conventions to Follow
|
||||
|
||||
- Prefer lazy Gradle APIs in convention plugins: `configureEach`, `withPlugin`, provider APIs.
|
||||
- Avoid `afterEvaluate` in `build-logic/convention` unless there is no viable lazy alternative.
|
||||
- Keep convention plugins single-purpose and compose them (e.g., `meshtastic.kmp.feature` composes KMP + Compose + Koin conventions).
|
||||
- Use version-catalog aliases from `gradle/libs.versions.toml` consistently.
|
||||
|
||||
## Verification Commands
|
||||
|
||||
```bash
|
||||
./gradlew :build-logic:convention:compileKotlin
|
||||
./gradlew :build-logic:convention:validatePlugins
|
||||
./gradlew spotlessCheck
|
||||
./gradlew detekt
|
||||
```
|
||||
|
||||
## Related Files
|
||||
|
||||
- `build-logic/convention/build.gradle.kts`
|
||||
- `build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/KotlinAndroid.kt`
|
||||
- `build-logic/convention/src/main/kotlin/org/meshtastic/buildlogic/FlavorResolution.kt`
|
||||
- `AGENTS.md`
|
||||
- `.github/copilot-instructions.md`
|
||||
- `GEMINI.md`
|
||||
|
|
@ -9,7 +9,7 @@ Use `AGENTS.md` as the source of truth for architecture boundaries and required
|
|||
When checking upstream docs/examples, match these repository-pinned versions from `gradle/libs.versions.toml`:
|
||||
|
||||
- Kotlin: `2.3.20`
|
||||
- Koin: `4.2.0` (`koin-annotations` `4.2.0`, compiler plugin `0.4.1`)
|
||||
- Koin: `4.2.0` (`koin-annotations` `4.2.0` — uses same version as `koin-core`; compiler plugin `0.4.1`)
|
||||
- JetBrains Navigation 3: `1.1.0-beta01` (`org.jetbrains.androidx.navigation3`)
|
||||
- JetBrains Lifecycle (multiplatform): `2.11.0-alpha02` (`org.jetbrains.androidx.lifecycle`)
|
||||
- AndroidX Lifecycle (Android-only): `2.10.0` (`androidx.lifecycle`)
|
||||
|
|
@ -26,16 +26,13 @@ Version catalog aliases split cleanly by fork provenance. **Use the right prefix
|
|||
| Alias prefix | Coordinates | Use in |
|
||||
|---|---|---|
|
||||
| `jetbrains-lifecycle-*` | `org.jetbrains.androidx.lifecycle:*` | `commonMain`, `androidMain` |
|
||||
| `jetbrains-navigation3-*` | `org.jetbrains.androidx.navigation3:*` | `commonMain`, `androidMain` |
|
||||
| `jetbrains-navigation3-ui` | `org.jetbrains.androidx.navigation3:navigation3-ui` | `commonMain`, `androidMain` |
|
||||
| `jetbrains-navigationevent-*` | `org.jetbrains.androidx.navigationevent:*` | `commonMain`, `androidMain` |
|
||||
| `jetbrains-compose-material3-adaptive-*` | `org.jetbrains.compose.material3.adaptive:*` | `commonMain`, `androidMain` |
|
||||
| `androidx-lifecycle-process` | `androidx.lifecycle:lifecycle-process` | `androidMain` only — `ProcessLifecycleOwner` |
|
||||
| `androidx-lifecycle-runtime-ktx` | `androidx.lifecycle:lifecycle-runtime-ktx` | `androidMain` only |
|
||||
| `androidx-lifecycle-viewmodel-ktx` | `androidx.lifecycle:lifecycle-viewmodel-ktx` | `androidMain` only |
|
||||
| `androidx-lifecycle-testing` | `androidx.lifecycle:lifecycle-runtime-testing` | `androidUnitTest` only |
|
||||
| `androidx-navigation-common` | `androidx.navigation:navigation-common` | `androidMain` only |
|
||||
|
||||
> `jetbrains-navigation3-runtime` and `jetbrains-navigation3-ui` resolve to the same `navigation3-ui` artifact — JetBrains does not publish a separate runtime artifact yet.
|
||||
> **Note:** JetBrains does not publish a separate `navigation3-runtime` artifact — `navigation3-ui` is the only artifact. The version catalog only defines `jetbrains-navigation3-ui`. The `lifecycle-runtime-ktx` and `lifecycle-viewmodel-ktx` KTX aliases were removed (extensions merged into base artifacts since Lifecycle 2.8.0).
|
||||
|
||||
Quick references:
|
||||
|
||||
|
|
@ -46,12 +43,10 @@ Quick references:
|
|||
|
||||
## Playbooks
|
||||
|
||||
- `docs/agent-playbooks/common-practices.md` - architecture and coding patterns to mirror.
|
||||
- `docs/agent-playbooks/di-navigation3-anti-patterns-playbook.md` - DI and Navigation 3 mistakes to avoid.
|
||||
- `docs/agent-playbooks/kmp-source-set-bridging-playbook.md` - when to use `expect`/`actual` vs interfaces + app wiring.
|
||||
- `docs/agent-playbooks/task-playbooks.md` - step-by-step recipes for common implementation tasks.
|
||||
- `docs/agent-playbooks/task-playbooks.md` - step-by-step recipes for common implementation tasks, plus code anchor quick reference.
|
||||
- `docs/agent-playbooks/testing-and-ci-playbook.md` - which Gradle tasks to run based on change type, plus CI parity.
|
||||
- `docs/agent-playbooks/testing-quick-ref.md` - Quick reference for using the new testing infrastructure.
|
||||
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,54 +0,0 @@
|
|||
# Common Practices Playbook
|
||||
|
||||
This document captures discoverable patterns that are already used in the repository.
|
||||
|
||||
## 1) Module and layering boundaries
|
||||
|
||||
- Keep domain logic in KMP modules (`commonMain`) and keep Android framework wiring in `app` or `androidMain`.
|
||||
- Use `core:*` for shared logic, `feature:*` for user-facing flows, and `app` for Android entrypoints and integration wiring.
|
||||
- Note: Former passthrough Android ViewModel wrappers have been eliminated. ViewModels are now shared KMP components. Platform-specific dependencies (file I/O, permissions) are isolated behind injected `core:repository` interfaces.
|
||||
|
||||
## 2) Dependency injection conventions (Koin)
|
||||
|
||||
- Use Koin annotations (`@Module`, `@ComponentScan`, `@KoinViewModel`, `@KoinWorker`) and keep DI wiring discoverable from `app`.
|
||||
- Example app scan module: `app/src/main/kotlin/org/meshtastic/app/MainKoinModule.kt`.
|
||||
- Example app startup and module registration: `app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt`.
|
||||
- Ensure feature/core modules are included in the app root module: `app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt`.
|
||||
- Prefer DI-agnostic shared logic in `commonMain`; inject from Android wrappers.
|
||||
|
||||
## 3) Navigation conventions (Navigation 3)
|
||||
|
||||
- Use Navigation 3 types (`NavKey`, `NavBackStack`, entry providers) instead of legacy controller-first patterns.
|
||||
- Example graph using `EntryProviderScope<NavKey>` and `backStack.add/removeLastOrNull`: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`.
|
||||
- Hosts should render navigation via `MeshtasticNavDisplay` from `core:ui/commonMain` (not raw `NavDisplay`) so entry decorators, scene strategies, and transitions stay consistent.
|
||||
- Host examples: `app/src/main/kotlin/org/meshtastic/app/ui/Main.kt`, `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`.
|
||||
|
||||
## 4) UI and resources
|
||||
|
||||
- Keep shared dialogs/components in `core:ui` where possible.
|
||||
- Put localizable UI strings in Compose Multiplatform resources: `core/resources/src/commonMain/composeResources/values/strings.xml`.
|
||||
- Use `stringResource(Res.string.key)` from shared resources in feature screens.
|
||||
- When retrieving strings in non-composable Coroutines, Managers, or ViewModels, use `getStringSuspend()`. Never use the blocking `getString()` inside a coroutine as it will crash iOS and freeze the UI thread.
|
||||
- Example usage: `feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt`.
|
||||
|
||||
## 5) Platform abstraction in shared UI
|
||||
|
||||
- Use `CompositionLocal` providers in `app` to inject Android/flavor-specific UI behavior into shared modules.
|
||||
- Example provider wiring in `MainActivity`: `app/src/main/kotlin/org/meshtastic/app/MainActivity.kt`.
|
||||
- Example abstraction contract: `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.kt`.
|
||||
|
||||
## 6) I/O and concurrency in shared code
|
||||
|
||||
- In `commonMain`, use Okio streams (`BufferedSource`/`BufferedSink`) and coroutines/Flow.
|
||||
- For ViewModel state exposure, prefer `stateInWhileSubscribed(...)` in shared ViewModels and collect in UI with `collectAsStateWithLifecycle()`.
|
||||
- Example shared extension: `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/ViewModelExtensions.kt`.
|
||||
- Example Okio usage in shared domain code:
|
||||
- `core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt`
|
||||
- `core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ExportDataUseCase.kt`
|
||||
|
||||
## 7) Namespace and compatibility
|
||||
|
||||
- New code should use `org.meshtastic.*`.
|
||||
- Keep compatibility constraints where required (notably legacy app ID and intent signatures for external integration).
|
||||
|
||||
|
||||
|
|
@ -23,7 +23,7 @@ Version note: align guidance with repository-pinned versions in `gradle/libs.ver
|
|||
- App-level module scanning: `app/src/main/kotlin/org/meshtastic/app/MainKoinModule.kt`
|
||||
- App startup + Koin init: `app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt`
|
||||
- Shared ViewModel base: `feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt`
|
||||
- Shared base UI ViewModel: `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/BaseUIViewModel.kt`
|
||||
- Shared base UI ViewModel: `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/UIViewModel.kt`
|
||||
|
||||
## Navigation 3 anti-patterns
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,23 @@
|
|||
|
||||
Use these as practical recipes. Keep edits minimal and aligned with existing module boundaries.
|
||||
|
||||
For architecture rules and coding standards, see [`AGENTS.md`](../../AGENTS.md).
|
||||
|
||||
## Code Anchor Quick Reference
|
||||
|
||||
Key files for discovering established patterns:
|
||||
|
||||
| Pattern | Reference File |
|
||||
|---|---|
|
||||
| App DI wiring | `app/src/main/kotlin/org/meshtastic/app/di/AppKoinModule.kt` |
|
||||
| App startup / Koin bootstrap | `app/src/main/kotlin/org/meshtastic/app/MeshUtilApplication.kt` |
|
||||
| Shared ViewModel | `feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt` |
|
||||
| `CompositionLocal` platform injection | `app/src/main/kotlin/org/meshtastic/app/MainActivity.kt` |
|
||||
| Platform abstraction contract | `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/util/MapViewProvider.kt` |
|
||||
| Shared strings resource | `core/resources/src/commonMain/composeResources/values/strings.xml` |
|
||||
| Okio shared I/O | `core/domain/src/commonMain/kotlin/org/meshtastic/core/domain/usecase/settings/ImportProfileUseCase.kt` |
|
||||
| `stateInWhileSubscribed` | `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/ViewModelExtensions.kt` |
|
||||
|
||||
## Playbook A: Add or update a user-visible string
|
||||
|
||||
1. Add/update key in `core/resources/src/commonMain/composeResources/values/strings.xml`.
|
||||
|
|
@ -11,7 +28,7 @@ Use these as practical recipes. Keep edits minimal and aligned with existing mod
|
|||
5. Verify no hardcoded user-facing strings were introduced.
|
||||
|
||||
Reference examples:
|
||||
- `feature/node/src/androidMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt`
|
||||
- `feature/node/src/commonMain/kotlin/org/meshtastic/feature/node/list/NodeListScreen.kt`
|
||||
- `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/component/AlertDialogs.kt`
|
||||
|
||||
## Playbook B: Add shared ViewModel logic in a feature module
|
||||
|
|
@ -19,13 +36,13 @@ Reference examples:
|
|||
1. Implement or extend base ViewModel logic in `feature/<name>/src/commonMain/...`.
|
||||
2. Keep shared class free of Android framework dependencies.
|
||||
3. Keep Android framework dependencies out of shared logic; if the module already uses Koin annotations in `commonMain`, keep patterns consistent and ensure app root inclusion.
|
||||
4. Update shared navigation entry points in `feature/*/src/commonMain/kotlin/org/meshtastic/feature/*/navigation/...` to resolve ViewModels with `koinViewModel()`.
|
||||
4. Update navigation entry points in `feature/*/src/androidMain/kotlin/org/meshtastic/feature/*/navigation/...` to resolve ViewModels with `koinViewModel()`.
|
||||
|
||||
Reference examples:
|
||||
- Shared base: `feature/messaging/src/commonMain/kotlin/org/meshtastic/feature/messaging/MessageViewModel.kt`
|
||||
- Shared base UI ViewModel: `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/BaseUIViewModel.kt`
|
||||
- Shared base UI ViewModel: `core/ui/src/commonMain/kotlin/org/meshtastic/core/ui/viewmodel/UIViewModel.kt`
|
||||
- Navigation usage: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
|
||||
- Desktop navigation usage: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopSettingsNavigation.kt`
|
||||
- Desktop navigation usage: `desktop/src/main/kotlin/org/meshtastic/desktop/navigation/DesktopNavigation.kt`
|
||||
|
||||
## Playbook C: Add a new dependency or service binding
|
||||
|
||||
|
|
@ -50,8 +67,7 @@ Reference examples:
|
|||
|
||||
Reference examples:
|
||||
- Shared graph wiring: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
|
||||
- Shared graph content: `feature/settings/src/commonMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsNavigation.kt`
|
||||
- Android-specific content actual: `feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsMainScreen.kt`
|
||||
- Android specific content: `feature/settings/src/androidMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsMainScreen.kt`
|
||||
- Desktop specific content: `feature/settings/src/jvmMain/kotlin/org/meshtastic/feature/settings/navigation/SettingsMainScreen.kt`
|
||||
- Feature intro graph pattern: `feature/intro/src/androidMain/kotlin/org/meshtastic/feature/intro/IntroNavGraph.kt`
|
||||
- Desktop nav shell: `desktop/src/main/kotlin/org/meshtastic/desktop/ui/DesktopMainScreen.kt`
|
||||
|
|
@ -78,7 +94,7 @@ Reference examples:
|
|||
4. Add `kotlinx-coroutines-swing` (JVM/Desktop) or the equivalent platform coroutines dispatcher module. Without it, `Dispatchers.Main` is unavailable and any code using `lifecycle.coroutineScope` will crash at runtime.
|
||||
5. Progressively replace stubs with real implementations (e.g., serial transport for desktop, CoreBluetooth for iOS).
|
||||
6. Add `<platform>()` target to feature modules as needed (all `core:*` modules already declare `jvm()`).
|
||||
7. Ensure the new module applies the expected KMP convention plugin so root `kmpSmokeCompile` auto-discovers and validates it in CI.
|
||||
7. Update CI JVM smoke compile step in `.github/workflows/reusable-check.yml` to include new modules.
|
||||
8. If `commonMain` code fails to compile for the new target, it's a KMP migration debt — fix the shared code, not the target.
|
||||
|
||||
Reference examples:
|
||||
|
|
|
|||
|
|
@ -1,147 +0,0 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# Copyright (c) 2025 Meshtastic LLC
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
# Testing Consolidation: Quick Reference Card
|
||||
|
||||
## Use core:testing in Your Module Tests
|
||||
|
||||
### 1. Add Dependency (in build.gradle.kts)
|
||||
```kotlin
|
||||
commonTest.dependencies {
|
||||
implementation(projects.core.testing)
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Import and Use Fakes
|
||||
```kotlin
|
||||
// In your src/commonTest/kotlin/...Test.kt files
|
||||
import org.meshtastic.core.testing.FakeNodeRepository
|
||||
import org.meshtastic.core.testing.FakeRadioController
|
||||
import org.meshtastic.core.testing.TestDataFactory
|
||||
|
||||
@Test
|
||||
fun myTest() = runTest {
|
||||
val nodeRepo = FakeNodeRepository()
|
||||
val nodes = TestDataFactory.createTestNodes(5)
|
||||
nodeRepo.setNodes(nodes)
|
||||
// Test away!
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Common Patterns
|
||||
|
||||
#### Testing with Fake Node Repository
|
||||
```kotlin
|
||||
val nodeRepo = FakeNodeRepository()
|
||||
nodeRepo.setNodes(TestDataFactory.createTestNodes(3))
|
||||
assertEquals(3, nodeRepo.nodeDBbyNum.value.size)
|
||||
```
|
||||
|
||||
#### Testing with Fake Radio Controller
|
||||
```kotlin
|
||||
val radio = FakeRadioController()
|
||||
radio.setConnectionState(ConnectionState.Connected)
|
||||
// Test your code that uses RadioController
|
||||
assertEquals(1, radio.sentPackets.size)
|
||||
```
|
||||
|
||||
#### Creating Custom Test Data
|
||||
```kotlin
|
||||
val customNode = TestDataFactory.createTestNode(
|
||||
num = 42,
|
||||
userId = "!mytest",
|
||||
longName = "Alice",
|
||||
shortName = "A"
|
||||
)
|
||||
```
|
||||
|
||||
## Module Dependencies (Consolidated)
|
||||
|
||||
### Before Testing Consolidation
|
||||
```
|
||||
feature:messaging/build.gradle.kts
|
||||
├── commonTest
|
||||
│ ├── libs.junit
|
||||
│ ├── libs.kotlinx.coroutines.test
|
||||
│ ├── libs.turbine
|
||||
│ └── [duplicated in 7+ other modules...]
|
||||
```
|
||||
|
||||
### After Testing Consolidation
|
||||
```
|
||||
feature:messaging/build.gradle.kts
|
||||
├── commonTest
|
||||
│ └── projects.core.testing ✅ (single source of truth)
|
||||
│
|
||||
└── core:testing provides: junit, mockk, coroutines.test, turbine
|
||||
```
|
||||
|
||||
## Files Reference
|
||||
|
||||
| File | Purpose | Location |
|
||||
|------|---------|----------|
|
||||
| FakeRadioController | RadioController test double | `core/testing/src/commonMain/kotlin/...` |
|
||||
| FakeNodeRepository | NodeRepository test double | `core/testing/src/commonMain/kotlin/...` |
|
||||
| TestDataFactory | Domain object builders | `core/testing/src/commonMain/kotlin/...` |
|
||||
| MessageViewModelTest | Example test pattern | `feature/messaging/src/commonTest/kotlin/...` |
|
||||
|
||||
## Documentation
|
||||
|
||||
- **Full API:** `core/testing/README.md`
|
||||
- **Decision Record:** `docs/decisions/testing-consolidation-2026-03.md`
|
||||
- **Slice Summary:** `docs/agent-playbooks/kmp-testing-consolidation-slice.md`
|
||||
- **Build Rules:** `AGENTS.md` § 3B and § 5
|
||||
|
||||
## Verification Commands
|
||||
|
||||
```bash
|
||||
# Build core:testing
|
||||
./gradlew :core:testing:compileKotlinJvm
|
||||
|
||||
# Verify a feature module with core:testing
|
||||
./gradlew :feature:messaging:compileKotlinJvm
|
||||
|
||||
# Run all tests (when domain tests are fixed)
|
||||
./gradlew allTests
|
||||
|
||||
# Check dependency tree
|
||||
./gradlew :feature:messaging:dependencies
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Cannot find projects.core.testing"
|
||||
- Did you add `:core:testing` to `settings.gradle.kts`? ✅ Already done
|
||||
- Did you run `./gradlew clean`? Try that
|
||||
|
||||
### Compilation error: "Unresolved reference 'Test'" or similar
|
||||
- This is a pre-existing issue in `core:domain` tests (missing Kotlin test annotations)
|
||||
- Not related to consolidation; will be fixed separately
|
||||
- Your new tests should work fine with `kotlin("test")`
|
||||
|
||||
### My fake isn't working
|
||||
- Check `core:testing/README.md` for API
|
||||
- Verify you're using the test-only version (not production code)
|
||||
- Fakes are intentionally no-op; add tracking/state as needed
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** 2026-03-11
|
||||
**Author:** Testing Consolidation Slice
|
||||
**Status:** ✅ Implemented & Verified
|
||||
|
||||
|
|
@ -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. 🚀
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# KMP Migration Status
|
||||
|
||||
> Last updated: 2026-03-21
|
||||
> Last updated: 2026-03-31
|
||||
|
||||
Single source of truth for Kotlin Multiplatform migration progress. For the forward-looking roadmap, see [`roadmap.md`](./roadmap.md). For completed decision records, see [`decisions/`](./decisions/).
|
||||
|
||||
|
|
@ -39,7 +39,7 @@ Modules that share JVM-specific code between Android and desktop now standardize
|
|||
|
||||
**18/20** core modules are KMP with JVM targets. The 2 Android-only modules are intentionally platform-specific, with shared contracts already abstracted into `core:ui/commonMain`.
|
||||
|
||||
### Feature Modules (8 total — 7 KMP with JVM)
|
||||
### Feature Modules (8 total — 8 KMP with JVM, 1 Android-only widget)
|
||||
|
||||
| Module | UI in commonMain? | Desktop wired? |
|
||||
|---|:---:|:---:|
|
||||
|
|
@ -47,9 +47,9 @@ Modules that share JVM-specific code between Android and desktop now standardize
|
|||
| `feature:node` | ✅ | ✅ Adaptive list-detail; fully shared `nodesGraph`, `PositionLogScreen`, and `NodeContextMenu` |
|
||||
| `feature:messaging` | ✅ | ✅ Adaptive contacts + messages; fully shared `contactsGraph`, `MessageScreen`, `ContactsScreen`, and `MessageListPaged` |
|
||||
| `feature:connections` | ✅ | ✅ Shared `ConnectionsScreen` with dynamic transport detection |
|
||||
| `feature:intro` | ✅ | — |
|
||||
| `feature:map` | ✅ | Placeholder; shared `NodeMapViewModel` |
|
||||
| `feature:firmware` | — | Placeholder; DFU is Android-only |
|
||||
| `feature:intro` | — | — | Screens remain in `androidMain`; shared ViewModel only |
|
||||
| `feature:map` | — | Placeholder; shared `NodeMapViewModel` and `BaseMapViewModel` only |
|
||||
| `feature:firmware` | ✅ | ✅ Fully KMP: Unified OTA, native Secure DFU, USB/UF2, FirmwareRetriever |
|
||||
| `feature:widget` | ❌ | — | Android-only (Glance appwidgets). Intentional. |
|
||||
|
||||
### Desktop Module
|
||||
|
|
@ -72,7 +72,7 @@ Working Compose Desktop application with:
|
|||
| Area | Score | Notes |
|
||||
|---|---|---|
|
||||
| Shared business/data logic | **9/10** | All core layers shared; RadioTransport interface unified |
|
||||
| Shared feature/UI logic | **9.5/10** | All 7 KMP; feature:connections unified; Navigation 3 Stable Scene-based architecture adopted; cross-platform deduplication complete |
|
||||
| Shared feature/UI logic | **9/10** | 8 KMP feature modules; firmware fully migrated; `feature:intro` and `feature:map` share ViewModels but UI remains in `androidMain` |
|
||||
| Android decoupling | **9/10** | No known `java.*` calls in `commonMain`; app module extraction in progress (navigation, connections, background services, and widgets extracted) |
|
||||
| Multi-target readiness | **9/10** | Full JVM; release-ready desktop; iOS simulator builds compiling successfully |
|
||||
| CI confidence | **9/10** | 25 modules validated (including feature:connections); native release installers automated |
|
||||
|
|
@ -87,7 +87,7 @@ Working Compose Desktop application with:
|
|||
|---|---:|
|
||||
| Android-first structural KMP | ~100% |
|
||||
| Shared business logic | ~98% |
|
||||
| Shared feature/UI | ~97% |
|
||||
| Shared feature/UI | ~92% |
|
||||
| True multi-target readiness | ~85% |
|
||||
| "Add iOS without surprises" | ~100% |
|
||||
|
||||
|
|
@ -96,17 +96,17 @@ Working Compose Desktop application with:
|
|||
Based on the latest codebase investigation, the following steps are proposed to complete the multi-target and iOS-readiness migrations:
|
||||
|
||||
1. **Wire Desktop Features:** Complete desktop UI wiring for `feature:intro` and implement a shared fallback for `feature:map` (which is currently a placeholder on desktop).
|
||||
2. **Decouple Firmware DFU:** `feature:firmware` relies on Android-only DFU libraries. Evaluate wrapping this in a shared KMP interface or extracting it into a separate plugin to allow the core `feature:firmware` module to be fully utilized on desktop/iOS.
|
||||
3. **Flesh out iOS Actuals:** Complete the actual implementations for iOS UI stubs (e.g., `AboutLibrariesLoader`, `rememberOpenMap`, `SettingsMainScreen`) that were recently added to unblock iOS compilation.
|
||||
4. **Boot iOS Target:** Set up an initial skeleton Xcode project to start running the now-compiling `iosSimulatorArm64` / `iosArm64` binaries on a real simulator/device.
|
||||
2. **Flesh out iOS Actuals:** Complete the actual implementations for iOS UI stubs (e.g., `AboutLibrariesLoader`, `rememberOpenMap`, `SettingsMainScreen`) that were recently added to unblock iOS compilation.
|
||||
3. **Boot iOS Target:** Set up an initial skeleton Xcode project to start running the now-compiling `iosSimulatorArm64` / `iosArm64` binaries on a real simulator/device.
|
||||
|
||||
## Key Architecture Decisions
|
||||
|
||||
| Decision | Status | Details |
|
||||
|---|---|---|
|
||||
| Navigation 3 parity model (shared `TopLevelDestination` + platform adapters) | ✅ Done | Both shells use shared enum + parity tests. See [`decisions/navigation3-parity-2026-03.md`](./decisions/navigation3-parity-2026-03.md) |
|
||||
| Navigation 3 parity model (shared `TopLevelDestination` + platform adapters) | ✅ Done | See [`decisions/navigation3-parity-2026-03.md`](./decisions/navigation3-parity-2026-03.md) |
|
||||
| Hilt → Koin | ✅ Done | See [`decisions/koin-migration.md`](./decisions/koin-migration.md) |
|
||||
| BLE abstraction (Kable) | ✅ Done | See [`decisions/ble-strategy.md`](./decisions/ble-strategy.md) |
|
||||
| Firmware KMP migration (pure Secure DFU) | ✅ Done | Native Nordic Secure DFU protocol reimplemented in pure KMP using Kable; desktop is first-class target |
|
||||
| Material 3 Adaptive (JetBrains) | ✅ Done | Version `1.3.0-alpha06` aligned with CMP `1.11.0-beta01`; supports Large (1200dp) and Extra-large (1600dp) breakpoints |
|
||||
| JetBrains lifecycle/nav3 alias alignment | ✅ Done | All forked deps use `jetbrains-*` prefix in version catalog; `core:data` commonMain uses JetBrains lifecycle runtime |
|
||||
| Expect/actual consolidation | ✅ Done | 7 pairs eliminated; 15+ genuinely platform-specific retained |
|
||||
|
|
@ -114,12 +114,12 @@ Based on the latest codebase investigation, the following steps are proposed to
|
|||
| **Transport Lifecycle Unification** | ✅ Done | `SharedRadioInterfaceService` orchestrates auto-reconnect, connection state, and heartbeat uniformly across Android and Desktop. |
|
||||
| **Database Parity** | ✅ Done | `DatabaseManager` is pure KMP, giving iOS and Desktop support for multiple connected nodes with LRU caching. |
|
||||
| Emoji picker unification | ✅ Done | Single commonMain implementation replacing 3 platform variants |
|
||||
| Cross-platform deduplication pass | ✅ Done | Extracted shared `AlertHost`, `SharedDialogs`, `PlaceholderScreen`, `ThemePickerDialog`, `AdaptiveListDetailScaffold`, `formatLogsTo()`, `handleNodeAction()`, `findNodeByNameSuffix()`, `MeshtasticAppShell`, `BleRadioInterface`, and `BaseRadioTransportFactory` to `commonMain`; eliminated ~1,200 lines of duplicated Compose UI code across Android/desktop |
|
||||
| Cross-platform deduplication pass | ✅ Done | Extracted shared `AlertHost`, `SharedDialogs`, `PlaceholderScreen`, `ThemePickerDialog`, `MeshtasticNavDisplay`, `formatLogsTo()`, `handleNodeAction()`, `findNodeByNameSuffix()`, `MeshtasticAppShell`, `BleRadioInterface`, and `BaseRadioTransportFactory` to `commonMain`; eliminated ~1,200 lines of duplicated Compose UI code across Android/desktop |
|
||||
|
||||
## Navigation Parity Note
|
||||
|
||||
- Desktop and Android both use the shared `TopLevelDestination` enum from `core:navigation/commonMain` — no separate `DesktopDestination` remains.
|
||||
- Both shells utilize the stable **Navigation 3 Scene-based architecture**, allowing for multi-pane layouts (e.g., three-pane on Large/XL displays) using shared routes.
|
||||
- Both shells utilize the **Navigation 3 Scene-based architecture**, allowing for multi-pane layouts (e.g., three-pane on Large/XL displays) using shared routes.
|
||||
- Both shells iterate `TopLevelDestination.entries` with shared icon mapping from `core:ui` (`TopLevelDestinationExt.icon`).
|
||||
- Desktop locale changes now trigger a full subtree recomposition from `Main.kt` without resetting the shared Navigation 3 backstack, so translated labels update in place.
|
||||
- Firmware remains available as an in-flow route instead of a top-level destination, matching Android information architecture.
|
||||
|
|
@ -131,7 +131,7 @@ Based on the latest codebase investigation, the following steps are proposed to
|
|||
|
||||
All major ViewModels have now been extracted to `commonMain` and no longer rely on Android-specific subclasses. Platform-specific dependencies (like `android.net.Uri` or Location permissions) have been successfully isolated behind injected `core:repository` interfaces (e.g., `FileService`, `LocationService`).
|
||||
|
||||
**The extraction of all feature-specific navigation graphs, background services, and widgets out of `:app` is complete.** The `:app` module now only serves as the root DI assembler and shared Navigation 3 host shell (`MeshtasticNavDisplay`) container.
|
||||
**The extraction of all feature-specific navigation graphs, background services, and widgets out of `:app` is complete.** The `:app` module now only serves as the root DI assembler and NavHost container.
|
||||
|
||||
Extracted to shared `commonMain` (no longer app-only):
|
||||
- `SettingsViewModel` → `feature:settings/commonMain`
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# Roadmap
|
||||
|
||||
> Last updated: 2026-03-23
|
||||
> Last updated: 2026-03-31
|
||||
|
||||
Forward-looking priorities for the Meshtastic KMP multi-target effort. For current state, see [`kmp-status.md`](./kmp-status.md). For the full gap analysis, see [`decisions/architecture-review-2026-03.md`](./decisions/architecture-review-2026-03.md).
|
||||
|
||||
|
|
@ -31,7 +31,7 @@ These items address structural gaps identified in the March 2026 architecture re
|
|||
- ✅ **Messaging:** Adaptive contacts with message view + send
|
||||
- ✅ **Connections:** Dynamic discovery of platform-supported transports (TCP, Serial/USB, BLE)
|
||||
- ❌ **Map:** Placeholder only, needs MapLibre or alternative
|
||||
- ⚠️ **Firmware:** Placeholder wired into nav graph; native DFU not applicable to desktop
|
||||
- ⚠️ **Firmware:** Fully KMP (Unified OTA + native Secure DFU + USB/UF2); desktop is first-class target
|
||||
- ⚠️ **Intro:** Onboarding flow (may not apply to desktop)
|
||||
|
||||
**Implementation Steps:**
|
||||
|
|
@ -92,8 +92,6 @@ These items address structural gaps identified in the March 2026 architecture re
|
|||
1. **iOS proof target** — ✅ **Done (Stubbing):** Stubbed iOS target implementations (`NoopStubs.kt` equivalent) to successfully pass compile-time checks. **Next:** Setup an Xcode skeleton project and launch the iOS app.
|
||||
2. **Migrate to Navigation 3 Scene-based architecture** — leverage the first stable release of Nav 3 to support multi-pane layouts. **Investigate 3-pane "Power User" scenes** (e.g., Node List + Detail + Map/Charts) on Large (1200dp) and Extra-large (1600dp) displays (Android 16 QPR3).
|
||||
3. **`core:api` contract split** — separate transport-neutral service contracts from the Android AIDL packaging to support iOS/Desktop service layers.
|
||||
4. **Decouple Firmware DFU** — `feature:firmware` relies on Android-only DFU libraries. Evaluate wrapping this in a shared KMP interface or extracting it to allow the core `feature:firmware` module to be utilized on desktop/iOS.
|
||||
5. ✅ **Adopt `WindowSizeClass.BREAKPOINTS_V2`** — Done: Updated `AdaptiveTwoPane.kt` and `Main.kt` components to call `currentWindowAdaptiveInfo(supportLargeAndXLargeWidth = true)`.
|
||||
|
||||
## Longer-Term (90+ days)
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue