From d8cd7d2078cb7b2ef6d2c5e39fa390706972345a Mon Sep 17 00:00:00 2001 From: ankek Date: Wed, 30 Sep 2026 22:09:47 +0300 Subject: [PATCH 1/2] fix1 --- .github/workflows/ci.yml | 4 ++-- .github/workflows/release.yml | 17 +++++++++-------- .../hho/android/ui/items/ViewModelTeardown.kt | 8 ++++++-- .../ui/receiving/ReceivingViewModelTest.kt | 3 ++- .../hho/android/ui/scan/ScanViewModelTest.kt | 8 +++++++- 5 files changed, 26 insertions(+), 14 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 791c54c..3b590c0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -39,14 +39,14 @@ jobs: - name: Upload test reports if: failure() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: test-reports path: android-app/app/build/reports/tests/ retention-days: 7 - name: Upload debug APKs - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: debug-apks path: android-app/app/build/outputs/apk/*/debug/*.apk diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ed20a94..a2a82b6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -77,11 +77,12 @@ jobs: run: rm -f "$RUNNER_TEMP/release.jks" - name: Publish GitHub release - uses: softprops/action-gh-release@v2 - with: - name: ${{ steps.version.outputs.name }} - generate_release_notes: true - fail_on_unmatched_files: true - files: | - android-app/dist/*.apk - android-app/dist/SHA256SUMS + env: + GH_TOKEN: ${{ github.token }} + VERSION: ${{ steps.version.outputs.name }} + run: | + gh release create "$GITHUB_REF_NAME" \ + --title "$VERSION" \ + --generate-notes \ + --verify-tag \ + dist/*.apk dist/SHA256SUMS diff --git a/android-app/app/src/test/kotlin/dev/hho/android/ui/items/ViewModelTeardown.kt b/android-app/app/src/test/kotlin/dev/hho/android/ui/items/ViewModelTeardown.kt index ea58464..cc0a85e 100644 --- a/android-app/app/src/test/kotlin/dev/hho/android/ui/items/ViewModelTeardown.kt +++ b/android-app/app/src/test/kotlin/dev/hho/android/ui/items/ViewModelTeardown.kt @@ -2,7 +2,9 @@ package dev.hho.android.ui.items import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.cancel +import kotlinx.coroutines.job +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.withTimeout import org.robolectric.shadows.ShadowLooper internal class ViewModelTracker { @@ -11,7 +13,9 @@ internal class ViewModelTracker { fun track(viewModel: T): T = viewModel.also { tracked += it } fun cancelAll() { - tracked.forEach { it.viewModelScope.cancel() } + val jobs = tracked.map { it.viewModelScope.coroutineContext.job } + jobs.forEach { it.cancel() } + runBlocking { withTimeout(10_000) { jobs.forEach { it.join() } } } tracked.clear() ShadowLooper.idleMainLooper() } diff --git a/android-app/app/src/test/kotlin/dev/hho/android/ui/receiving/ReceivingViewModelTest.kt b/android-app/app/src/test/kotlin/dev/hho/android/ui/receiving/ReceivingViewModelTest.kt index 72d5bb2..4e75083 100644 --- a/android-app/app/src/test/kotlin/dev/hho/android/ui/receiving/ReceivingViewModelTest.kt +++ b/android-app/app/src/test/kotlin/dev/hho/android/ui/receiving/ReceivingViewModelTest.kt @@ -215,7 +215,7 @@ class ReceivingViewModelTest { vm.dismissUnexpected() await { sessionState()!!.pendingUnexpected == null } assertTrue(sessionState()!!.lines.isEmpty()) - assertFalse(vm.paused) + await("analysis resumes") { !vm.paused } } @Test fun noMatchShowsMessageAndWritesNothing() { @@ -246,6 +246,7 @@ class ReceivingViewModelTest { vm.setScanning(true) scan("111") await { line("drill")?.discrepancy?.receivedQty == 1 } + await("first resolve finishes") { !vm.paused } clock += 1_000 vm.onBarcodes(code("111")) assertFalse("a repeat in the suppression window starts no resolve", vm.paused) diff --git a/android-app/app/src/test/kotlin/dev/hho/android/ui/scan/ScanViewModelTest.kt b/android-app/app/src/test/kotlin/dev/hho/android/ui/scan/ScanViewModelTest.kt index 498e87f..a2bdf0d 100644 --- a/android-app/app/src/test/kotlin/dev/hho/android/ui/scan/ScanViewModelTest.kt +++ b/android-app/app/src/test/kotlin/dev/hho/android/ui/scan/ScanViewModelTest.kt @@ -1,5 +1,6 @@ package dev.hho.android.ui.scan +import androidx.lifecycle.viewModelScope import androidx.test.ext.junit.runners.AndroidJUnit4 import dev.hho.android.data.items.ItemSearch import dev.hho.android.data.room.HhoDatabase @@ -17,6 +18,7 @@ import dev.hho.android.ui.items.ViewModelTracker import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.first +import kotlinx.coroutines.job import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.resetMain import kotlinx.coroutines.test.setMain @@ -40,7 +42,11 @@ class ScanViewModelTest { private val viewModels = ViewModelTracker() private fun awaitSheet(): ScanUiState.Sheet = kotlinx.coroutines.runBlocking { - kotlinx.coroutines.withTimeout(10_000) { vm.uiState.first { it is ScanUiState.Sheet } as ScanUiState.Sheet } + kotlinx.coroutines.withTimeout(10_000) { + val sheet = vm.uiState.first { it is ScanUiState.Sheet } as ScanUiState.Sheet + vm.viewModelScope.coroutineContext.job.children.forEach { it.join() } + sheet + } } private fun code(v: String) = listOf(DecodedBarcode(v, BarcodeFormat.QR_CODE)) From c02a3bcc644e82fab3a591ecfa50e698f7cf4205 Mon Sep 17 00:00:00 2001 From: ankek Date: Wed, 30 Sep 2026 23:55:36 +0300 Subject: [PATCH 2/2] 2 --- .../kotlin/dev/hho/android/ui/items/ViewModelTeardown.kt | 8 ++------ .../hho/android/ui/syncstatus/SyncStatusViewModelTest.kt | 4 ++++ 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/android-app/app/src/test/kotlin/dev/hho/android/ui/items/ViewModelTeardown.kt b/android-app/app/src/test/kotlin/dev/hho/android/ui/items/ViewModelTeardown.kt index cc0a85e..ea58464 100644 --- a/android-app/app/src/test/kotlin/dev/hho/android/ui/items/ViewModelTeardown.kt +++ b/android-app/app/src/test/kotlin/dev/hho/android/ui/items/ViewModelTeardown.kt @@ -2,9 +2,7 @@ package dev.hho.android.ui.items import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import kotlinx.coroutines.job -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.cancel import org.robolectric.shadows.ShadowLooper internal class ViewModelTracker { @@ -13,9 +11,7 @@ internal class ViewModelTracker { fun track(viewModel: T): T = viewModel.also { tracked += it } fun cancelAll() { - val jobs = tracked.map { it.viewModelScope.coroutineContext.job } - jobs.forEach { it.cancel() } - runBlocking { withTimeout(10_000) { jobs.forEach { it.join() } } } + tracked.forEach { it.viewModelScope.cancel() } tracked.clear() ShadowLooper.idleMainLooper() } diff --git a/android-app/app/src/test/kotlin/dev/hho/android/ui/syncstatus/SyncStatusViewModelTest.kt b/android-app/app/src/test/kotlin/dev/hho/android/ui/syncstatus/SyncStatusViewModelTest.kt index f40182b..bfe2faa 100644 --- a/android-app/app/src/test/kotlin/dev/hho/android/ui/syncstatus/SyncStatusViewModelTest.kt +++ b/android-app/app/src/test/kotlin/dev/hho/android/ui/syncstatus/SyncStatusViewModelTest.kt @@ -1,5 +1,6 @@ package dev.hho.android.ui.syncstatus +import androidx.lifecycle.viewModelScope import androidx.test.ext.junit.runners.AndroidJUnit4 import dev.hho.android.data.room.HhoDatabase import dev.hho.android.data.room.ItemEntity @@ -16,6 +17,7 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.first +import kotlinx.coroutines.job import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher @@ -65,7 +67,9 @@ class SyncStatusViewModelTest { } @After fun tearDown() { + val job = vm.viewModelScope.coroutineContext.job viewModels.cancelAll() + runBlocking { withTimeout(10_000) { job.join() } } db.close() Dispatchers.resetMain() }