Skip to content

Commit d2d34e6

Browse files
authored
chore: update gradle (#32)
* chore: update gradle * chore: change project-level build.gradle file to build.gradle.kts * chore: use gradle version catalog in project level build.gradle.kts file * chore: update gradle android plugin to 8.10.1 * refactor: move dependency definitions from app level build.gradle.kts to gradle catalog toml file * nitpick: update instrumented test cluster; no changes * nitpick: update comments * nitpick: make the version catalog file more readable * deps: add missing coroutines.test dependency implementation for instrumented tests * nitpick: rename MainActivityTest.kt to MainActivityAndroidTest.kt * tests: make sure clicking on the settings button opens the settings sheet * tests: make sure action items are accessible to allow testing on headless emulator e.g. on the CI * tests: separate checking action menu items in another method * tests (nitpick): rename unused cached error to _ * ci: record a video of emulator screen while running instrumented tests and store them in the artifacts folder * chore: change settings.gradle to settings.gradle.kts
1 parent 64d880b commit d2d34e6

File tree

8 files changed

+267
-116
lines changed

8 files changed

+267
-116
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ commands:
99
- restore_cache:
1010
key: v1-gradle-wrapper-{{ arch }}-{{ checksum "gradle/wrapper/gradle-wrapper.properties" }}
1111
- restore_cache:
12-
key: v1-gradle-cache-{{ arch }}-{{ checksum "build.gradle" }}-{{ checksum "settings.gradle" }}-{{ checksum "gradle.properties" }}-{{ checksum "app/build.gradle.kts" }}
12+
key: v1-gradle-cache-{{ arch }}-{{ checksum "build.gradle.kts" }}-{{ checksum "settings.gradle.kts" }}-{{ checksum "gradle.properties" }}-{{ checksum "app/build.gradle.kts" }}
1313
restore_bundler_cache:
1414
steps:
1515
- restore_cache:
@@ -24,7 +24,7 @@ commands:
2424
- save_cache:
2525
paths:
2626
- ~/.gradle/caches
27-
key: v1-gradle-cache-{{ arch }}-{{ checksum "build.gradle" }}-{{ checksum "settings.gradle" }}-{{ checksum "gradle.properties" }}-{{ checksum "app/build.gradle.kts" }}
27+
key: v1-gradle-cache-{{ arch }}-{{ checksum "build.gradle.kts" }}-{{ checksum "settings.gradle.kts" }}-{{ checksum "gradle.properties" }}-{{ checksum "app/build.gradle.kts" }}
2828
save_bundler_cache:
2929
steps:
3030
- save_cache:

.github/workflows/android.yml

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ jobs:
2828
uses: actions/cache@v3
2929
with:
3030
path: ~/.gradle/caches
31-
key: ${{ runner.OS }}-gradle-caches-cache-${{ hashFiles('build.gradle') }}
31+
key: ${{ runner.OS }}-gradle-caches-cache-${{ hashFiles('build.gradle.kts') }}
3232
restore-keys: |
3333
${{ runner.OS }}-gradle-caches-cache-
3434
- name: generate ksProp file
@@ -91,11 +91,21 @@ jobs:
9191
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
9292
sudo udevadm control --reload-rules
9393
sudo udevadm trigger --name-match=kvm
94-
- name: run tests
94+
- name: run tests with screen record
9595
uses: reactivecircus/android-emulator-runner@v2
9696
with:
9797
api-level: 29
98-
script: ./gradlew connectedCheck
98+
script: |
99+
adb shell screenrecord /sdcard/ui-test.mp4 &
100+
SCREENRECORD_PID=$!
101+
./gradlew connectedCheck || true
102+
kill $SCREENRECORD_PID || true
103+
adb pull /sdcard/ui-test.mp4 ./ui-test.mp4 || true
104+
- name: Upload UI test video
105+
uses: actions/upload-artifact@v4
106+
with:
107+
name: ui-test-video
108+
path: ./ui-test.mp4
99109

100110
notify-slack:
101111
needs: unit-test

app/build.gradle.kts

Lines changed: 50 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,8 @@ android {
6262
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
6363
}
6464
named("debug") {
65-
isTestCoverageEnabled = true
65+
enableUnitTestCoverage = true
66+
enableAndroidTestCoverage = true
6667
}
6768
}
6869

@@ -94,8 +95,7 @@ android {
9495
}
9596

9697
jacoco {
97-
val jacoco_version: String by project
98-
toolVersion = jacoco_version
98+
toolVersion = libs.versions.jacoco.get()
9999
reportsDirectory.set(layout.buildDirectory.dir("mergedReportDir"))
100100
}
101101

@@ -216,82 +216,70 @@ fun loadKeyStore(name: String): Properties? {
216216
}
217217
}
218218

219-
val firebase_bom_version: String by project
220-
val hilt_version: String by project
221-
val coroutines_version: String by project
222-
val material_version: String by project
223-
val mockk_version: String by project
224219
dependencies {
225220

226-
implementation("androidx.appcompat:appcompat:1.7.0")
227-
implementation("androidx.core:core-ktx:1.16.0")
228-
implementation("androidx.constraintlayout:constraintlayout:2.2.1")
221+
// AndroidX
222+
implementation(libs.appcompat)
223+
implementation(libs.core.ktx)
224+
implementation(libs.constraintlayout)
229225

230226
// Firebase
231-
implementation(platform("com.google.firebase:firebase-bom:$firebase_bom_version"))
232-
implementation("com.google.firebase:firebase-analytics-ktx")
233-
implementation("com.google.firebase:firebase-crashlytics-ktx")
227+
implementation(platform(libs.firebase.bom))
228+
implementation(libs.firebase.analytics.ktx)
229+
implementation(libs.firebase.crashlytics.ktx)
234230

235231
// Dependency Injection
236-
implementation("com.google.dagger:hilt-android:$hilt_version")
237-
kapt("com.google.dagger:hilt-compiler:$hilt_version")
232+
implementation(libs.hilt.android)
233+
kapt(libs.hilt.compiler)
238234

239235
// Coroutines
240-
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
241-
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.7")
242-
implementation("androidx.lifecycle:lifecycle-extensions:2.2.0")
243-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
244-
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
236+
implementation(libs.lifecycle.runtime.ktx)
237+
implementation(libs.lifecycle.viewmodel.ktx)
238+
implementation(libs.lifecycle.extensions)
239+
implementation(libs.coroutines.core)
240+
implementation(libs.coroutines.android)
245241

246242
// Compose Bom
247-
val composeBom = platform("androidx.compose:compose-bom:2023.06.01")
243+
val composeBom = platform(libs.compose.bom)
248244
implementation(composeBom)
249-
androidTestImplementation(composeBom)
250-
implementation("androidx.compose.foundation:foundation")
251-
implementation("androidx.compose.material3:material3")
245+
implementation(libs.compose.foundation)
246+
implementation(libs.compose.material3)
252247
// Compose - Android Studio Preview support
253-
implementation("androidx.compose.ui:ui-tooling-preview")
254-
debugImplementation("androidx.compose.ui:ui-tooling")
255-
implementation("androidx.activity:activity-compose:1.10.1")
248+
implementation(libs.compose.ui.tooling.preview)
249+
debugImplementation(libs.compose.ui.tooling)
250+
implementation(libs.activity.compose)
256251

257252
// Other UI Libraries
258-
implementation("com.google.android.material:material:$material_version")
259-
260-
// data
261-
implementation("androidx.datastore:datastore-preferences:1.1.4")
262-
263-
// unit test libs
264-
testImplementation("junit:junit:4.13.2")
265-
266-
// instrumented test libs
267-
androidTestImplementation("androidx.test:core:1.6.1")
268-
androidTestImplementation("androidx.test.ext:junit:1.2.1")
269-
androidTestImplementation("androidx.test.ext:junit-ktx:1.2.1")
270-
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
253+
implementation(libs.material)
271254

272-
// Hamcrest for view matching
273-
androidTestImplementation("org.hamcrest:hamcrest-library:2.2")
274-
androidTestImplementation("androidx.test:runner:1.6.2")
275-
androidTestImplementation("androidx.test:rules:1.6.1")
255+
// Data
256+
implementation(libs.datastore.preferences)
276257

277-
// coroutine testing
278-
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version")
279-
androidTestImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:$coroutines_version")
258+
// Unit Test Libraries
259+
testImplementation(libs.junit)
260+
testImplementation(libs.coroutines.test)
261+
testImplementation(libs.truth)
262+
testImplementation(libs.mockk.android)
263+
testImplementation(libs.mockk.agent)
280264

281-
// google truth for assertions
282-
testImplementation("com.google.truth:truth:1.1.3")
283-
androidTestImplementation("androidx.test.ext:truth:1.6.0")
284-
285-
// mockk
286-
testImplementation("io.mockk:mockk-android:$mockk_version")
287-
testImplementation("io.mockk:mockk-agent:$mockk_version")
288-
androidTestImplementation("io.mockk:mockk-android:$mockk_version")
289-
androidTestImplementation("io.mockk:mockk-agent:$mockk_version")
290-
291-
// hilt testing - https://developer.android.com/training/dependency-injection/hilt-testing
292-
androidTestImplementation("com.google.dagger:hilt-android-testing:$hilt_version")
293-
kaptAndroidTest("com.google.dagger:hilt-android-compiler:$hilt_version")
265+
// Instrumented Test Libraries
266+
androidTestImplementation(composeBom)
267+
androidTestImplementation(libs.coroutines.test)
268+
androidTestImplementation(libs.androidx.test.core)
269+
androidTestImplementation(libs.androidx.test.ext.junit)
270+
androidTestImplementation(libs.androidx.test.ext.junit.ktx)
271+
androidTestImplementation(libs.espresso.core)
272+
androidTestImplementation(libs.hamcrest)
273+
androidTestImplementation(libs.androidx.test.runner)
274+
androidTestImplementation(libs.androidx.test.rules)
275+
androidTestImplementation(libs.androidx.test.truth)
276+
androidTestImplementation(libs.mockk.android)
277+
androidTestImplementation(libs.mockk.agent)
278+
279+
// Hilt Testing
280+
androidTestImplementation(libs.hilt.android.testing)
281+
kaptAndroidTest(libs.hilt.android.compiler)
294282

295283
// Android Serial Controller
296-
implementation("com.github.superus8r:UsbSerial:6.1.1")
284+
implementation(libs.usb.serial)
297285
}
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package org.kabiri.android.usbterminal
2+
3+
import androidx.test.espresso.Espresso.onView
4+
import androidx.test.espresso.Espresso.openActionBarOverflowOrOptionsMenu
5+
import androidx.test.espresso.NoMatchingViewException
6+
import androidx.test.espresso.assertion.ViewAssertions.matches
7+
import androidx.test.espresso.action.ViewActions.click
8+
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
9+
import androidx.test.espresso.matcher.ViewMatchers.withId
10+
import androidx.test.ext.junit.rules.activityScenarioRule
11+
import androidx.test.ext.junit.runners.AndroidJUnit4
12+
import androidx.test.platform.app.InstrumentationRegistry
13+
import org.junit.Rule
14+
import org.junit.Test
15+
import org.junit.runner.RunWith
16+
17+
18+
@RunWith(AndroidJUnit4::class)
19+
internal class MainActivityAndroidTest {
20+
21+
@get:Rule
22+
var rule = activityScenarioRule<MainActivity>()
23+
24+
private fun ensureMenuIsAccessible(menuItemId: Int) {
25+
try {
26+
// Try to find the menu item first
27+
onView(withId(menuItemId)).check(matches(isDisplayed()))
28+
} catch (_: NoMatchingViewException) {
29+
// If not found then open the overflow menu
30+
openActionBarOverflowOrOptionsMenu(
31+
InstrumentationRegistry.getInstrumentation().targetContext
32+
)
33+
}
34+
}
35+
36+
@Test
37+
fun checkUiViewsAreDisplayed() {
38+
// arrange
39+
// act
40+
// assert
41+
onView(withId(R.id.tvOutput)).check(matches(isDisplayed()))
42+
onView(withId(R.id.btEnter)).check(matches(isDisplayed()))
43+
onView(withId(R.id.etInput)).check(matches(isDisplayed()))
44+
}
45+
46+
@Test
47+
fun checkActionMenuItemsAreDisplayed() {
48+
// arrange
49+
// act
50+
// Ensure action items are accessible, either in the toolbar or via overflow
51+
ensureMenuIsAccessible(R.id.actionSettings)
52+
ensureMenuIsAccessible(R.id.actionConnect)
53+
ensureMenuIsAccessible(R.id.actionDisconnect)
54+
55+
// assert
56+
// Check menu items are displayed
57+
onView(withId(R.id.actionSettings)).check(matches(isDisplayed()))
58+
onView(withId(R.id.actionConnect)).check(matches(isDisplayed()))
59+
onView(withId(R.id.actionDisconnect)).check(matches(isDisplayed()))
60+
}
61+
62+
@Test
63+
fun clickingSettingsOpensSettingsBottomSheet() {
64+
// arrange
65+
// Ensure the action item is accisble, either in the toolbar or via overflow
66+
ensureMenuIsAccessible(R.id.actionSettings)
67+
68+
// act
69+
onView(withId(R.id.actionSettings)).perform(click())
70+
71+
// assert
72+
onView(withId(R.id.composeViewSettingContent)).check(matches(isDisplayed()))
73+
}
74+
}

app/src/androidTest/java/org/kabiri/android/usbterminal/MainActivityTest.kt

Lines changed: 0 additions & 27 deletions
This file was deleted.

build.gradle renamed to build.gradle.kts

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,20 @@
11
buildscript {
2-
ext {
3-
coroutines_version = "1.6.4"
4-
firebase_bom_version = "32.8.0"
5-
hilt_version = "2.56.2"
6-
jacoco_version = "0.8.8"
7-
kotlin_version = "2.1.20"
8-
material_version = "1.12.0"
9-
mockk_version = "1.14.2"
10-
}
112
dependencies {
12-
classpath "com.google.dagger:hilt-android-gradle-plugin:$hilt_version"
13-
classpath "com.google.gms:google-services:4.4.1"
14-
classpath "com.google.firebase:firebase-crashlytics-gradle:2.9.9"
3+
classpath(libs.hilt.android.gradle.plugin)
4+
classpath(libs.google.services)
5+
classpath(libs.firebase.crashlytics.gradle)
156
}
167
}
178

189
plugins {
19-
id("com.android.application") version '8.10.0' apply false
20-
id("org.jetbrains.kotlin.android") version "2.1.20" apply false
21-
id("org.jetbrains.kotlin.plugin.compose") version "2.1.20"
22-
id("org.sonarqube") version "3.5.0.2730"
10+
alias(libs.plugins.android.application) apply false
11+
alias(libs.plugins.kotlin.android) apply false
12+
alias(libs.plugins.kotlin.compose)
13+
alias(libs.plugins.sonarqube)
2314
}
2415

25-
task clean(type: Delete) {
26-
delete layout.buildDirectory
16+
tasks.register<Delete>("clean") {
17+
delete(layout.buildDirectory)
2718
}
2819

2920
sonarqube {
@@ -35,7 +26,6 @@ sonarqube {
3526
property("sonar.host.url", "https://sonarcloud.io")
3627

3728
property("sonar.binaries", project(":app").layout.buildDirectory.dir("tmp/kotlin-classes/debug").get().asFile.absolutePath)
38-
// sonar requires absolute path for lint and jacoco reports!
3929
property("sonar.androidLint.reportPaths", project(":app").layout.buildDirectory.dir("reports/lint-results-debug.xml").get().asFile.absolutePath)
4030
property("sonar.coverage.jacoco.xmlReportPaths", project(":app").layout.buildDirectory.dir("mergedReportDir/jacocoTestReport/jacocoTestReport.xml").get().asFile.absolutePath)
4131
}

0 commit comments

Comments
 (0)