Skip to content

Commit b8fb2a7

Browse files
committed
Test
1 parent 5e255d6 commit b8fb2a7

File tree

3 files changed

+251
-38
lines changed

3 files changed

+251
-38
lines changed

.github/workflows/sonarqube.yml

Lines changed: 137 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -158,39 +158,146 @@ jobs:
158158
echo "\nChecking all available class directories:"
159159
find build -path "*/build/*" -name "*.class" | head -n 5 || echo "No class files found"
160160
161-
# Commented out Android instrumented test part to focus on unit test flow
162-
# - name: AVD cache
163-
# uses: actions/cache@v4
164-
# id: avd-cache
165-
# with:
166-
# path: |
167-
# ~/.android/avd/*
168-
# ~/.android/adb*
169-
# key: avd-28
170-
171-
# - name: Create AVD and generate snapshot for caching
172-
# if: steps.avd-cache.outputs.cache-hit != 'true'
173-
# uses: reactivecircus/android-emulator-runner@v2
174-
# with:
175-
# api-level: 28
176-
# force-avd-creation: false
177-
# emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
178-
# disable-animations: true
179-
# script: echo "Generated AVD snapshot for caching."
161+
# Android instrumented test section for comprehensive coverage
162+
- name: AVD cache
163+
uses: actions/cache@v4
164+
id: avd-cache
165+
with:
166+
path: |
167+
~/.android/avd/*
168+
~/.android/adb*
169+
key: avd-28
180170

181-
# - name: Assemble debug AndroidTest
182-
# run: ./gradlew assembleDebugAndroidTest
171+
- name: Create AVD and generate snapshot for caching
172+
if: steps.avd-cache.outputs.cache-hit != 'true'
173+
uses: reactivecircus/android-emulator-runner@v2
174+
with:
175+
api-level: 28
176+
force-avd-creation: false
177+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
178+
disable-animations: true
179+
script: echo "Generated AVD snapshot for caching."
180+
181+
- name: Assemble debug AndroidTest
182+
run: ./gradlew assembleDebugAndroidTest
183183

184-
# - name: Run instrumented tests with coverage (continue on error)
185-
# uses: reactivecircus/android-emulator-runner@v2
186-
# with:
187-
# api-level: 28
188-
# profile: Galaxy Nexus
189-
# script: ./gradlew connectedDebugAndroidTest jacocoAndroidTestReport --continue
190-
# continue-on-error: true
184+
- name: Run instrumented tests with coverage
185+
uses: reactivecircus/android-emulator-runner@v2
186+
with:
187+
api-level: 28
188+
profile: Galaxy Nexus
189+
script: |
190+
# Run instrumented tests with coverage - allow test failures
191+
# Use a more direct approach to ensure coverage is enabled
192+
./gradlew assembleDebugAndroidTest
193+
194+
# Run only a single test class with coverage explicitly enabled
195+
adb shell am instrument -w -e coverage true -e class tests.database.DatabaseInitializationTest io.split.android.android_client.test/androidx.test.runner.AndroidJUnitRunner || {
196+
echo "Some instrumented tests failed, but continuing to check for coverage data..."
197+
}
198+
199+
# Create directory for coverage files
200+
mkdir -p build/outputs/code_coverage/debugAndroidTest/connected/
201+
202+
# Try to find and pull coverage files from various possible locations
203+
echo "Searching for coverage files..."
204+
205+
# Check app-specific data directory
206+
adb shell run-as io.split.android.android_client find /data/data/io.split.android.android_client -name "*.ec" | while read -r file; do
207+
echo "Found coverage file: $file"
208+
filename=$(basename "$file")
209+
adb shell run-as io.split.android.android_client cat "$file" > "build/outputs/code_coverage/debugAndroidTest/connected/$filename"
210+
echo "Pulled coverage file to build/outputs/code_coverage/debugAndroidTest/connected/$filename"
211+
done
212+
213+
# Also check sdcard location
214+
adb pull /sdcard/coverage.ec build/outputs/code_coverage/debugAndroidTest/connected/ || echo "No coverage file at /sdcard/coverage.ec"
215+
216+
# List all coverage files to verify
217+
find build -name "*.ec" -o -name "*.exec"
218+
219+
# Run the JaCoCo report task
220+
./gradlew jacocoAndroidTestReport || {
221+
echo "Failed to generate Android test coverage report, but continuing..."
222+
}
223+
224+
# Check if the Android test report was generated with content
225+
if [ -f build/reports/jacoco/jacocoAndroidTestReport/jacocoAndroidTestReport.xml ]; then
226+
# Use stat command compatible with both Linux and macOS
227+
if [[ "$(uname)" == "Darwin" ]]; then
228+
# macOS syntax
229+
REPORT_SIZE=$(stat -f%z build/reports/jacoco/jacocoAndroidTestReport/jacocoAndroidTestReport.xml)
230+
else
231+
# Linux syntax
232+
REPORT_SIZE=$(stat -c%s build/reports/jacoco/jacocoAndroidTestReport/jacocoAndroidTestReport.xml)
233+
fi
234+
235+
echo "Android test JaCoCo report size: $REPORT_SIZE bytes"
236+
237+
if [ "$REPORT_SIZE" -lt 1000 ]; then
238+
echo "WARNING: Android test JaCoCo report is too small, likely empty"
239+
else
240+
echo "Android test JaCoCo report generated successfully"
241+
fi
242+
else
243+
echo "WARNING: Android test JaCoCo report file not found"
244+
fi
245+
continue-on-error: true
191246

192-
# - name: Generate combined coverage report
193-
# run: ./gradlew jacocoCombinedReport
247+
- name: Generate combined coverage report
248+
run: |
249+
# Generate combined report - allow failures
250+
./gradlew jacocoCombinedReport || {
251+
echo "Failed to generate combined report, but continuing..."
252+
}
253+
254+
# Check if the combined report was generated with content
255+
if [ -f build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml ]; then
256+
# Use stat command compatible with both Linux and macOS
257+
if [[ "$(uname)" == "Darwin" ]]; then
258+
# macOS syntax
259+
REPORT_SIZE=$(stat -f%z build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml)
260+
else
261+
# Linux syntax
262+
REPORT_SIZE=$(stat -c%s build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml)
263+
fi
264+
265+
echo "Combined JaCoCo report size: $REPORT_SIZE bytes"
266+
267+
if [ "$REPORT_SIZE" -lt 1000 ]; then
268+
echo "WARNING: Combined JaCoCo report is too small, likely empty"
269+
# Try to manually combine the reports
270+
mkdir -p build/reports/jacoco/jacocoCombinedReport/
271+
cp build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml
272+
else
273+
echo "Combined JaCoCo report generated successfully"
274+
fi
275+
else
276+
echo "WARNING: Combined JaCoCo report file not found, using unit test report as fallback"
277+
mkdir -p build/reports/jacoco/jacocoCombinedReport/
278+
cp build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml
279+
fi
280+
continue-on-error: true
281+
282+
- name: Check combined coverage report
283+
run: |
284+
echo "Checking for all coverage data files:"
285+
find build -name "*.exec" -o -name "*.ec" | sort
286+
287+
echo "\nChecking combined JaCoCo report content:"
288+
if [ -f build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml ]; then
289+
echo "Report file size: $(wc -c < build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml) bytes"
290+
echo "First 500 chars of report:"
291+
head -c 500 build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml
292+
echo "\n\nCounting coverage elements:"
293+
grep -c "<package" build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml || echo "No packages found"
294+
grep -c "<class" build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml || echo "No classes found"
295+
grep -c "<method" build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml || echo "No methods found"
296+
grep -c "<line" build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml || echo "No lines found"
297+
grep -c 'ci="1"' build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml || echo "No covered lines found"
298+
else
299+
echo "Combined JaCoCo report file not found"
300+
fi
194301
195302
- name: SonarCloud Scan
196303
uses: SonarSource/sonarqube-scan-action@v5

build.gradle

Lines changed: 112 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,25 @@ apply plugin: 'kotlin-android'
1717
apply plugin: 'jacoco'
1818
apply from: 'spec.gradle'
1919

20+
jacoco {
21+
toolVersion = '0.8.8'
22+
}
23+
2024
ext {
2125
splitVersion = '5.3.0'
2226
jacocoVersion = '0.8.8'
2327
}
2428

29+
// Define exclusions for JaCoCo coverage
30+
def coverageExclusions = [
31+
'**/R.class',
32+
'**/R$*.class',
33+
'**/BuildConfig.*',
34+
'**/Manifest*.*',
35+
'**/*Test*.*',
36+
'android/**/*.*'
37+
]
38+
2539
android {
2640
compileSdk 33
2741
targetCompatibility = '1.8'
@@ -46,6 +60,8 @@ android {
4660

4761
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
4862
testInstrumentationRunnerArguments clearPackageData: 'true'
63+
// Enable coverage for instrumented tests by default
64+
testInstrumentationRunnerArguments coverage: 'true'
4965

5066
javaCompileOptions {
5167
annotationProcessorOptions {
@@ -55,6 +71,13 @@ android {
5571

5672
testOptions {
5773
execution 'ANDROIDX_TEST_ORCHESTRATOR'
74+
// Enable JaCoCo coverage for instrumented tests
75+
unitTests.all {
76+
jacoco {
77+
includeNoLocationClasses = true
78+
excludes = ['jdk.internal.*']
79+
}
80+
}
5881
}
5982
}
6083

@@ -65,6 +88,14 @@ android {
6588

6689
testOptions {
6790
unitTests.returnDefaultValues = true
91+
92+
// Configure JaCoCo for all test tasks
93+
unitTests.all {
94+
jacoco {
95+
includeNoLocationClasses = true
96+
excludes = ['jdk.internal.*']
97+
}
98+
}
6899
}
69100

70101
sourceSets {
@@ -82,7 +113,9 @@ android {
82113
debug {
83114
buildConfigField("String", "SPLIT_VERSION_NAME", "\"${splitVersion}\"")
84115
buildConfigField("String", "FLAGS_SPEC", "\"${flagsSpec}\"")
85-
testCoverageEnabled true
116+
117+
enableAndroidTestCoverage = true
118+
enableUnitTestCoverage = true
86119
}
87120
release {
88121
buildConfigField("String", "SPLIT_VERSION_NAME", "\"${splitVersion}\"")
@@ -451,7 +484,77 @@ tasks.register('jacocoTestReport', JacocoReport) {
451484
}
452485
}
453486

454-
// Android instrumented test coverage report task
487+
// Custom task for running filtered Android instrumented tests
488+
tasks.register('connectedFilteredAndroidTest') {
489+
doLast {
490+
def testClass = project.hasProperty('testClass') ? project.getProperty('testClass') : 'tests.database.DatabaseInitializationTest'
491+
exec {
492+
commandLine android.getAdbExecutable().toString(), 'shell', 'am', 'instrument', '-w', '-e', 'class', testClass,
493+
'-e', 'coverage', 'true', "${android.defaultConfig.testInstrumentationRunner}/androidx.test.runner.AndroidJUnitRunner"
494+
}
495+
496+
// Create directory for coverage files
497+
def coverageDir = new File("${buildDir}/outputs/code_coverage/debugAndroidTest/connected/")
498+
coverageDir.mkdirs()
499+
500+
// Pull coverage file from device
501+
exec {
502+
commandLine android.getAdbExecutable().toString(), 'pull', '/sdcard/coverage.ec',
503+
"${buildDir}/outputs/code_coverage/debugAndroidTest/connected/"
504+
ignoreExitValue true
505+
}
506+
}
507+
}
508+
509+
// Comprehensive JaCoCo coverage report task
510+
tasks.register('jacocoCodeCoverageReport', JacocoReport) {
511+
group = 'Reporting'
512+
description = 'Generate Jacoco coverage report for both unit and instrumented tests'
513+
514+
// Depend on both unit and instrumented tests
515+
dependsOn 'testDebugUnitTest', 'connectedDebugAndroidTest'
516+
517+
reports {
518+
xml.required = true
519+
html.required = true
520+
csv.required = false
521+
}
522+
523+
// Use the defined exclusions
524+
def debugTree = fileTree(dir: "${buildDir}/intermediates/runtime_library_classes_dir/debug", excludes: coverageExclusions)
525+
def kotlinTree = fileTree(dir: "${buildDir}/tmp/kotlin-classes/debug", excludes: coverageExclusions)
526+
527+
sourceDirectories.from(files(['src/main/java', 'src/main/kotlin'].findAll { new File(it).exists() }))
528+
classDirectories.from(files([debugTree, kotlinTree]))
529+
530+
// Include all execution data files
531+
executionData.from(fileTree(dir: "$buildDir", includes: [
532+
'jacoco/testDebugUnitTest.exec',
533+
'outputs/code_coverage/debugAndroidTest/connected/*coverage.ec',
534+
'**/*.exec',
535+
'**/*.ec'
536+
]))
537+
538+
// Ensure this task always runs and doesn't get skipped
539+
outputs.upToDateWhen { false }
540+
541+
doFirst {
542+
// Log a warning if no execution data files exist
543+
def execFiles = executionData.files
544+
executionData = executionData.filter { it.exists() }
545+
if (execFiles.isEmpty() || !execFiles.any { it.exists() }) {
546+
logger.warn("JaCoCo will not generate coverage report because no execution data files were found")
547+
} else {
548+
execFiles.each { file ->
549+
if (file.exists()) {
550+
logger.lifecycle("Found JaCoCo execution data file: $file")
551+
}
552+
}
553+
}
554+
}
555+
}
556+
557+
// Keep the original tasks for backward compatibility
455558
tasks.register('jacocoAndroidTestReport', JacocoReport) {
456559
dependsOn 'connectedDebugAndroidTest'
457560

@@ -461,13 +564,14 @@ tasks.register('jacocoAndroidTestReport', JacocoReport) {
461564
csv.required = false
462565
}
463566

464-
def fileFilter = ['**/R.class', '**/R$*.class', '**/BuildConfig.*', '**/Manifest*.*', '**/*Test*.*', 'android/**/*.*']
465-
def debugTree = fileTree(dir: "${buildDir}/intermediates/runtime_library_classes_dir/debug", excludes: fileFilter)
567+
def debugTree = fileTree(dir: "${buildDir}/intermediates/runtime_library_classes_dir/debug", excludes: coverageExclusions)
466568

467569
sourceDirectories.from(files(['src/main/java']))
468570
classDirectories.from(files([debugTree]))
469571
executionData.from(fileTree(dir: "$buildDir", includes: [
470-
'outputs/code_coverage/debugAndroidTest/connected/*coverage.ec'
572+
'outputs/code_coverage/debugAndroidTest/connected/*coverage.ec',
573+
'**/*.exec',
574+
'**/*.ec'
471575
]))
472576

473577
// Ensure this task always runs and doesn't get skipped
@@ -505,7 +609,9 @@ tasks.register('jacocoCombinedReport', JacocoReport) {
505609
classDirectories.from(files([debugTree]))
506610
executionData.from(fileTree(dir: "$buildDir", includes: [
507611
'jacoco/testDebugUnitTest.exec',
508-
'outputs/code_coverage/debugAndroidTest/connected/*coverage.ec'
612+
'outputs/code_coverage/debugAndroidTest/connected/*coverage.ec',
613+
'**/*.exec',
614+
'**/*.ec'
509615
]))
510616

511617
// Ensure this task always runs and doesn't get skipped

sonar-project.properties

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ sonar.tests=src/test/java,src/androidTest/java,src/sharedTest/java
1414
# Encoding of the source code
1515
sonar.sourceEncoding=UTF-8
1616

17-
# Include test coverage reports
18-
sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml,build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml,build/reports/jacoco/test/jacocoTestReport.xml,target/site/jacoco/jacoco.xml
17+
# Include test coverage reports - prioritize combined report
18+
sonar.coverage.jacoco.xmlReportPaths=build/reports/jacoco/jacocoCombinedReport/jacocoCombinedReport.xml,build/reports/jacoco/jacocoTestReport/jacocoTestReport.xml,build/reports/jacoco/jacocoAndroidTestReport/jacocoAndroidTestReport.xml,build/reports/jacoco/test/jacocoTestReport.xml,target/site/jacoco/jacoco.xml
1919

2020
# Exclusions
2121
sonar.exclusions=**/R.class,**/R$*.class,**/BuildConfig.*,**/Manifest*.*,**/*Test*.*,android/**/*.*

0 commit comments

Comments
 (0)