diff --git a/.github/workflows/build-release.yml b/.github/workflows/build-release.yml index 68b89a56..5736cf87 100644 --- a/.github/workflows/build-release.yml +++ b/.github/workflows/build-release.yml @@ -13,8 +13,8 @@ permissions: jobs: test: - name: Unit tests (PR only) - if: github.event_name == 'pull_request' + name: Unit tests & coverage + if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main') runs-on: ubuntu-latest steps: @@ -44,19 +44,32 @@ jobs: working-directory: ft8cn run: ./gradlew testDebugUnitTest --stacktrace + - name: Generate coverage report + working-directory: ft8cn + run: ./gradlew jacocoTestReport --stacktrace + + - name: Upload coverage to Codecov + continue-on-error: true + id: codecov + uses: codecov/codecov-action@v6 + with: + token: ${{ secrets.CODECOV_TOKEN }} + files: ft8cn/app/build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml + - name: Upload test reports on failure - if: failure() - uses: actions/upload-artifact@v4 + if: steps.codecov.outcome == 'failure' + uses: actions/upload-artifact@v7 with: name: unit-test-reports path: | ft8cn/app/build/reports/tests/ ft8cn/app/build/test-results/ + ft8cn/app/build/reports/jacoco/ build: name: Build APK needs: test - # Run when tests pass (PR), or when tests were skipped (push to main/dev/tags). + # Run when tests pass (PR/main push), or when tests were skipped (push to main/dev/tags). # Block only on actual test failure or cancellation. if: ${{ always() && needs.test.result != 'failure' && needs.test.result != 'cancelled' }} runs-on: ubuntu-latest @@ -185,14 +198,14 @@ jobs: mv "$src" "FT8CN-${{ steps.tag.outputs.release_tag }}.apk" - name: Upload APK artifact - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ft8cn-release path: ft8cn/app/build/outputs/apk/release/*.apk - name: Upload AAB artifact if: steps.tag.outputs.should_release == 'true' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: ft8cn-release-aab path: ft8cn/app/build/outputs/bundle/release/*.aab diff --git a/ft8cn/app/build.gradle b/ft8cn/app/build.gradle index af9c80af..d6d1f736 100644 --- a/ft8cn/app/build.gradle +++ b/ft8cn/app/build.gradle @@ -4,6 +4,7 @@ import java.text.SimpleDateFormat plugins { id 'com.android.application' id 'org.jetbrains.kotlin.android' + id 'jacoco' } def currentTime = getCurrentTime() @@ -197,3 +198,42 @@ dependencies { androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' androidTestImplementation 'androidx.compose.ui:ui-test-junit4' } + +jacoco { + toolVersion = '0.8.12' +} + +tasks.register('jacocoTestReport', JacocoReport) { + dependsOn 'testDebugUnitTest' + + reports { + xml.required = true + html.required = true + csv.required = false + } + + def excludes = [ + '**/R.class', + '**/R$*.class', + '**/BuildConfig.*', + '**/Manifest*.*', + '**/*Test*.*', + '**/*$ViewBinder*.*', + '**/*$ViewInjector*.*', + '**/*Binding.*', + '**/*BindingImpl.*', + '**/DataBinderMapperImpl.*', + '**/BR.*', + '**/*ComposableSingletons*.*' + ] + + classDirectories.setFrom(files( + fileTree(dir: "$buildDir/intermediates/javac/debug/classes", excludes: excludes), + fileTree(dir: "$buildDir/tmp/kotlin-classes/debug", excludes: excludes) + )) + sourceDirectories.setFrom(files('src/main/java', 'src/main/kotlin')) + executionData.setFrom(fileTree(dir: "$buildDir", includes: [ + 'jacoco/testDebugUnitTest.exec', + 'outputs/unit_test_code_coverage/debugUnitTest/testDebugUnitTest.exec' + ])) +}