mirror of
https://github.com/meshtastic/Meshtastic-Android.git
synced 2026-04-20 22:23:37 +00:00
refactor: BLE transport and UI for Kotlin Multiplatform unification (#4911)
Some checks are pending
Dependency Submission / dependency-submission (push) Waiting to run
Main CI (Verify & Build) / validate-and-build (push) Waiting to run
Main Push Changelog / Generate main push changelog (push) Waiting to run
Some checks are pending
Dependency Submission / dependency-submission (push) Waiting to run
Main CI (Verify & Build) / validate-and-build (push) Waiting to run
Main Push Changelog / Generate main push changelog (push) Waiting to run
Signed-off-by: James Rich <2199651+jamesarich@users.noreply.github.com>
This commit is contained in:
parent
b0e91a390c
commit
6516287c62
42 changed files with 429 additions and 845 deletions
|
|
@ -15,180 +15,51 @@
|
|||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
# `:core:testing` — Shared Test Doubles and Utilities
|
||||
# `:core:testing`
|
||||
|
||||
## Purpose
|
||||
## Module dependency graph
|
||||
|
||||
The `:core:testing` module provides lightweight, reusable test doubles (fakes, builders, factories) and testing utilities for **all** KMP modules. This module **consolidates testing dependencies** into a single, well-controlled location to:
|
||||
<!--region graph-->
|
||||
```mermaid
|
||||
graph TB
|
||||
:core:testing[testing]:::kmp-library
|
||||
|
||||
- **Reduce duplication**: Shared fakes (e.g., `FakeNodeRepository`, `FakeRadioController`) used across multiple modules.
|
||||
- **Keep dependency graph clean**: All test doubles and libraries are defined once; modules depend on `:core:testing` instead of scattered test deps.
|
||||
- **Enable KMP-wide test patterns**: Every module (`commonTest`, `androidUnitTest`, JVM tests) can reuse the same fakes.
|
||||
- **Maintain purity**: Core business logic modules (e.g., `core:domain`, `core:data`) depend on `:core:testing` via `commonTest`, avoiding test-code leakage into production.
|
||||
|
||||
## Dependency Strategy
|
||||
classDef android-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef android-application-compose fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef compose-desktop-application fill:#CAFFBF,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef android-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef android-library fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef android-library-compose fill:#9BF6FF,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef android-test fill:#A0C4FF,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef jvm-library fill:#BDB2FF,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef kmp-feature fill:#FFD6A5,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef kmp-library-compose fill:#FFC1CC,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef kmp-library fill:#FFC1CC,stroke:#000,stroke-width:2px,color:#000;
|
||||
classDef unknown fill:#FFADAD,stroke:#000,stroke-width:2px,color:#000;
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────┐
|
||||
│ core:testing │
|
||||
│ (only deps: core:model, │
|
||||
│ core:repository, test libs) │
|
||||
└──────────────┬──────────────────────┘
|
||||
↑
|
||||
│ (commonTest dependency)
|
||||
┌──────┴─────────────┬────────────────────┐
|
||||
│ │ │
|
||||
core:domain feature:messaging feature:node
|
||||
core:data feature:settings feature:firmware
|
||||
(etc.) (etc.)
|
||||
```
|
||||
<!--endregion-->
|
||||
|
||||
### Target Compatibility Warning (March 2026 Audit)
|
||||
## Overview
|
||||
The `:core:testing` module is a dedicated **Kotlin Multiplatform (KMP)** library that provides shared test fakes, doubles, rules, and utilities. It is designed to be consumed by the `commonTest` source sets of all other KMP modules to ensure consistent and unified testing behavior across the codebase.
|
||||
|
||||
- **MockK Removal:** MockK has been removed from `commonMain` because it does not natively support Kotlin/Native (iOS).
|
||||
- **Future-Proofing:** The project is migrating to `dev.mokkery` for KMP-compatible mocking or favoring manual fakes.
|
||||
- **Recommendation:** Favor manual fakes (like `FakeNodeRepository`) in `commonMain` to maintain pure KMP portability.
|
||||
By centralizing fakes and mocking utilities here, we prevent duplication of test setups and enforce a standard approach to testing ViewModels, Repositories, and pure domain logic.
|
||||
|
||||
### Key Design Rules
|
||||
## Key Components
|
||||
|
||||
1. **`:core:testing` has NO dependencies on heavy modules**: It only depends on:
|
||||
- `core:model` — Domain types (Node, User, etc.)
|
||||
- `core:repository` — Interfaces (NodeRepository, etc.)
|
||||
- Test libraries (`kotlin("test")`, `kotlinx.coroutines.test`, `turbine`, `junit`)
|
||||
- **Test Doubles / Fakes**: Provides in-memory implementations of core repositories (e.g., `FakeNodeRepository`, `FakeMeshLogRepository`) to isolate components under test.
|
||||
- **Coroutines Testing**: Provides dispatchers and test rules that replace the main dispatcher with `TestDispatcher` to allow time-control and synchronous execution of coroutines in tests.
|
||||
- **Mokkery Support**: Integrated with the Mokkery compiler plugin to provide robust and unified mocking capabilities in `commonTest`.
|
||||
|
||||
2. **No circular dependencies**: Modules that depend on `:core:testing` (in `commonTest`) cannot be dependencies of `:core:testing` itself.
|
||||
|
||||
3. **`:core:testing` is NOT part of the app bundle**: It's declared in `commonTest` sourceSet only, so it never appears in release APKs or final JARs.
|
||||
|
||||
## What's Included
|
||||
|
||||
### Test Doubles (Fakes)
|
||||
|
||||
#### `FakeRadioController`
|
||||
A no-op implementation of `RadioController` for unit tests. Tracks method calls and state changes.
|
||||
## Usage
|
||||
Add this module to your `commonTest` source set dependencies in your KMP module's `build.gradle.kts`:
|
||||
|
||||
```kotlin
|
||||
val radioController = FakeRadioController()
|
||||
radioController.setConnectionState(ConnectionState.Connected)
|
||||
assertEquals(1, radioController.sentPackets.size)
|
||||
```
|
||||
|
||||
#### `FakeNodeRepository`
|
||||
An in-memory implementation of `NodeRepository` for isolated testing.
|
||||
|
||||
```kotlin
|
||||
val nodeRepo = FakeNodeRepository()
|
||||
nodeRepo.setNodes(TestDataFactory.createTestNodes(5))
|
||||
assertEquals(5, nodeRepo.nodeDBbyNum.value.size)
|
||||
```
|
||||
|
||||
### Test Builders & Factories
|
||||
|
||||
#### `TestDataFactory`
|
||||
Factory methods for creating domain objects with sensible defaults.
|
||||
|
||||
```kotlin
|
||||
val node = TestDataFactory.createTestNode(num = 42, longName = "Alice")
|
||||
val nodes = TestDataFactory.createTestNodes(10)
|
||||
```
|
||||
|
||||
### Test Utilities
|
||||
|
||||
#### Flow collection helper
|
||||
```kotlin
|
||||
val emissions = flow { emit(1); emit(2) }.toList()
|
||||
assertEquals(listOf(1, 2), emissions)
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Testing a ViewModel (in `feature:messaging/src/commonTest`)
|
||||
|
||||
```kotlin
|
||||
class MessageViewModelTest {
|
||||
private val nodeRepository = FakeNodeRepository()
|
||||
|
||||
@Test
|
||||
fun testLoadsNodesCorrectly() = runTest {
|
||||
nodeRepository.setNodes(TestDataFactory.createTestNodes(3))
|
||||
val viewModel = createViewModel(nodeRepository)
|
||||
assertEquals(3, viewModel.nodeCount.value)
|
||||
kotlin {
|
||||
sourceSets {
|
||||
commonTest.dependencies {
|
||||
implementation(projects.core.testing)
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Testing a UseCase (in `core:domain/src/commonTest`)
|
||||
|
||||
```kotlin
|
||||
class SendMessageUseCaseTest {
|
||||
private val radioController = FakeRadioController()
|
||||
|
||||
@Test
|
||||
fun testSendsPacket() = runTest {
|
||||
val useCase = SendMessageUseCase(radioController)
|
||||
useCase.sendMessage(testPacket)
|
||||
assertEquals(1, radioController.sentPackets.size)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Adding New Test Doubles
|
||||
|
||||
When adding a new fake to `:core:testing`:
|
||||
|
||||
1. **Implement the interface** from `core:model` or `core:repository`.
|
||||
2. **Track side effects** (e.g., `sentPackets`, `calledMethods`) for test assertions.
|
||||
3. **Provide test helpers** (e.g., `setNodes()`, `clear()`) to manipulate state.
|
||||
4. **Document with examples** in the class KDoc.
|
||||
|
||||
Example:
|
||||
|
||||
```kotlin
|
||||
/**
|
||||
* A test double for [SomeRepository].
|
||||
*/
|
||||
class FakeSomeRepository : SomeRepository {
|
||||
val callHistory = mutableListOf<String>()
|
||||
|
||||
override suspend fun doSomething(value: String) {
|
||||
callHistory.add(value)
|
||||
}
|
||||
|
||||
// Test helpers
|
||||
fun getCallCount() = callHistory.size
|
||||
fun clear() = callHistory.clear()
|
||||
}
|
||||
```
|
||||
|
||||
## Dependency Maintenance
|
||||
|
||||
### When adding a new module:
|
||||
- If it has `commonTest` tests, add `implementation(projects.core.testing)` to its `commonTest.dependencies`.
|
||||
- Do NOT add heavy modules (e.g., `core:database`) to `:core:testing`'s dependencies.
|
||||
|
||||
### When a test needs a mock:
|
||||
- Check `:core:testing` first for an existing fake.
|
||||
- If none exists, consider adding it there (if it's reusable) vs. using `mockk()` inline.
|
||||
|
||||
### When updating interfaces:
|
||||
- Update corresponding fakes in `:core:testing` to match new method signatures.
|
||||
- Keep fakes no-op; don't replicate business logic.
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
core/testing/
|
||||
├── build.gradle.kts # Lightweight, minimal dependencies
|
||||
├── README.md # This file
|
||||
└── src/commonMain/kotlin/org/meshtastic/core/testing/
|
||||
├── FakeRadioController.kt # RadioController test double
|
||||
├── FakeNodeRepository.kt # NodeRepository test double
|
||||
└── TestDataFactory.kt # Builders and factories
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- `AGENTS.md` §3B: KMP platform purity guidelines (relevant for test code).
|
||||
- `docs/kmp-status.md`: KMP module status and targets.
|
||||
- `.github/copilot-instructions.md`: Build and test commands.
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue