Skip to content

Commit

Permalink
Merge pull request #47 from kittinunf/kv/add-roborazzi-into-project
Browse files Browse the repository at this point in the history
Add Test and implement basic foundation for Snapshot testing
  • Loading branch information
akexorcist authored Apr 3, 2024
2 parents 3bb3c55 + 81d6a8d commit 7f57f90
Show file tree
Hide file tree
Showing 15 changed files with 233 additions and 38 deletions.
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Gradle files
.gradle/
build/
build/*

# Local configuration file (sdk path, etc)
local.properties
Expand Down
3 changes: 2 additions & 1 deletion app/.gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
/build
build/*
!build/outputs/roborazzi/*
56 changes: 39 additions & 17 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ plugins {
alias(libs.plugins.jetbrainsKotlinAndroid)
alias(libs.plugins.devToolsKsp)
alias(libs.plugins.ossLicenses)
alias(libs.plugins.roborazzi)
}

android {
Expand All @@ -16,7 +17,7 @@ android {
versionCode = 2
versionName = "1.0.1"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
testInstrumentationRunner = "androidx.test.runner.InstrumentationTestRunner"
vectorDrawables {
useSupportLibrary = true
}
Expand All @@ -25,7 +26,10 @@ android {
buildTypes {
release {
isMinifyEnabled = true
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)

val signingKeyFile: String? = System.getenv("SIGNING_KEY_STORE_PATH")
if (signingKeyFile != null) {
Expand Down Expand Up @@ -59,35 +63,53 @@ android {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
}
}
testOptions {
unitTests {
isIncludeAndroidResources = true
}
}
}

dependencies {
implementation(libs.accompanist.drawable.painter)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.lifecycle.runtime.compose)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.material3)
implementation(libs.androidx.navigation.compose)
implementation(libs.androidx.room.runtime)
implementation(libs.androidx.splashscreen)
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.koin.androidx.compose)
implementation(libs.koin.androidx.compose.navigation)
implementation(libs.play.oss.licenses)
implementation(platform(libs.androidx.compose.bom))
implementation(platform(libs.koin.bom))

testImplementation(libs.androidx.espresso.core)
testImplementation(libs.androidx.ui.test.junit4)

testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
testImplementation(libs.koin.test)
testImplementation(libs.koin.test.junit4)
testImplementation(libs.robolectric)
testImplementation(libs.roborazzi)
testImplementation(libs.roborazzi.compose)
testImplementation(libs.roborazzi.rule)

androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
androidTestImplementation(platform(libs.androidx.compose.bom))

debugImplementation(libs.androidx.ui.test.manifest)
implementation(libs.androidx.navigation.compose)
implementation(platform(libs.koin.bom))
implementation(libs.koin.androidx.compose)
implementation(libs.koin.androidx.compose.navigation)
implementation(libs.accompanist.drawable.painter)
implementation(libs.androidx.splashscreen)
implementation(libs.androidx.room.runtime)
debugImplementation(libs.androidx.ui.tooling)

annotationProcessor(libs.androidx.room.compiler)
ksp(libs.androidx.room.compiler)
implementation(libs.play.oss.licenses)

coreLibraryDesugaring(libs.desugar)
}
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
14 changes: 8 additions & 6 deletions app/src/main/java/com/akexorcist/ruammij/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,17 @@
package com.akexorcist.ruammij

import android.content.pm.PackageManager
import android.hardware.display.DisplayManager
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import com.akexorcist.ruammij.data.InstalledApp
import com.akexorcist.ruammij.ui.RuamMijApp
import com.akexorcist.ruammij.ui.rememberAppState
import com.akexorcist.ruammij.ui.theme.RuamMijTheme
import com.akexorcist.ruammij.utility.getOwnerPackageName
import com.akexorcist.ruammij.utility.toInstalledApp
import org.koin.androidx.compose.KoinAndroidContext
import org.koin.androidx.viewmodel.ext.android.viewModel
import org.koin.core.annotation.KoinExperimentalAPI

class MainActivity : AppCompatActivity() {
private val sharedEventViewModel: SharedEventViewModel by viewModel()
Expand All @@ -21,13 +20,16 @@ class MainActivity : AppCompatActivity() {
getSystemService(DisplayManager::class.java)
}

@OptIn(KoinExperimentalAPI::class)
override fun onCreate(savedInstanceState: Bundle?) {
installSplashScreen()
super.onCreate(savedInstanceState)
setContent {
RuamMijTheme {
val appState = rememberAppState()
RuamMijApp(appState = appState)
KoinAndroidContext {
RuamMijTheme {
val appState = rememberAppState()
RuamMijApp(appState = appState)
}
}
}
displayManager.registerDisplayListener(displayListener, null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class RuamMijApplication : Application() {
startKoin {
androidLogger(Level.ERROR)
androidContext(applicationContext)
modules(AppModule.modules, AppModule.databaseModule)
modules(AppModule.allModules)
}
}
}
8 changes: 3 additions & 5 deletions app/src/main/java/com/akexorcist/ruammij/di/AppModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,14 @@ import org.koin.androidx.viewmodel.dsl.viewModelOf
import org.koin.dsl.module

object AppModule {
val modules = module {
private val modules = module {
factory<CoroutineDispatcherProvider> { DefaultCoroutineDispatcherProvider() }
single<DeviceRepository> { DefaultDeviceRepository(androidContext(), get(), get()) }
viewModelOf(::OverviewViewModel)
viewModelOf(::AccessibilityViewModel)
viewModelOf(::InstalledAppViewModel)
viewModelOf(::SharedEventViewModel)
}
val databaseModule = module {
single { provideDataBase(get()) }
single { provideSafeAppDao(get()) }
}

val allModules = modules + databaseModule
}
12 changes: 8 additions & 4 deletions app/src/main/java/com/akexorcist/ruammij/di/DatabaseModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,16 @@ import com.akexorcist.ruammij.data.database.RuamMijDatabase
import com.akexorcist.ruammij.data.database.SafeAppDao
import org.koin.dsl.module

fun provideDataBase(application: Application): RuamMijDatabase =
internal val databaseModule = module {
single { provideDataBase(get()) }
single { provideSafeAppDao(get()) }
}

private fun provideDataBase(application: Application): RuamMijDatabase =
Room.databaseBuilder(
application,
RuamMijDatabase::class.java,
"ruammij"
).
fallbackToDestructiveMigration().build()
).fallbackToDestructiveMigration().build()

fun provideSafeAppDao(database: RuamMijDatabase): SafeAppDao = database.getSafeAppDao()
private fun provideSafeAppDao(database: RuamMijDatabase): SafeAppDao = database.getSafeAppDao()
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ fun OverviewRoute(
) {
val activity = LocalContext.current as Activity
val uiState by viewModel.overviewUiState.collectAsStateWithLifecycle()
val mediaProjectionDetectionEvent by sharedEventViewModel.mediaProjectionEvent.collectAsStateWithLifecycle(initialValue = null)
val mediaProjectionDetectionEvent by sharedEventViewModel.mediaProjectionEvent.collectAsStateWithLifecycle(
initialValue = null
)

LaunchedEffect(Unit) { viewModel.checkDevicePrivacy() }

Expand Down Expand Up @@ -366,7 +368,13 @@ private fun UnverifiedInstalledAppItem(
if (packageName != null) {
BoldBodyText(text = packageName)
}
BodyText(text = pluralStringResource(R.plurals.installed_app_installer_amount, appCount, appCount))
BodyText(
text = pluralStringResource(
R.plurals.installed_app_installer_amount,
appCount,
appCount
)
)
}
Icon(
modifier = Modifier.size(24.dp),
Expand Down Expand Up @@ -915,4 +923,4 @@ private fun AppearOnTopSectionPreview() {
onOpenDrawOverOtherAppsClick = {}
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package com.akexorcist.ruammij

import android.app.Application
import android.content.Context
import androidx.test.runner.AndroidJUnitRunner
import com.akexorcist.ruammij.data.DeviceRepository
import com.akexorcist.ruammij.di.AppModule
import com.akexorcist.ruammij.fake.FakeDeviceRepository
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
import org.koin.core.logger.Level
import org.koin.dsl.module

class InstrumentationTestRunner : AndroidJUnitRunner() {
override fun newApplication(
classLoader: ClassLoader?,
className: String?,
context: Context?
): Application {
return super.newApplication(classLoader, TestRuamMijApplication::class.java.name, context)
}
}

private val modulesOverride = module {
single<DeviceRepository> {
FakeDeviceRepository()
}
}

class TestRuamMijApplication : Application() {
override fun onCreate() {
super.onCreate()
startKoin {
androidContext(this@TestRuamMijApplication)
modules(AppModule.allModules)
modules(modulesOverride)
}
}

override fun onTerminate() {
super.onTerminate()
stopKoin()
}
}
36 changes: 36 additions & 0 deletions app/src/test/java/com/akexorcist/ruammij/MainActivityTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.akexorcist.ruammij

import android.R
import androidx.test.core.app.ActivityScenario.launch
import androidx.test.espresso.Espresso.onView
import androidx.test.espresso.matcher.ViewMatchers.withId
import com.github.takahirom.roborazzi.captureRoboImage
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.test.KoinTest
import org.robolectric.RobolectricTestRunner
import org.robolectric.annotation.Config
import org.robolectric.annotation.GraphicsMode

@GraphicsMode(GraphicsMode.Mode.NATIVE)
@RunWith(RobolectricTestRunner::class)
class MainActivityTest : KoinTest {

@Test
@Config(qualifiers = "en-xlarge-long-hdpi")
fun overview_en() {
val activityScenario = launch(MainActivity::class.java)

onView(withId(R.id.content))
.captureRoboImage()
}

@Test
@Config(qualifiers = "th-xlarge-long-hdpi")
fun overview_th() {
val activityScenario = launch(MainActivity::class.java)

onView(withId(R.id.content))
.captureRoboImage()
}
}
19 changes: 19 additions & 0 deletions app/src/test/java/com/akexorcist/ruammij/ModuleCheckTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package com.akexorcist.ruammij

import com.akexorcist.ruammij.di.AppModule.allModules
import org.junit.Test
import org.junit.runner.RunWith
import org.koin.core.annotation.KoinExperimentalAPI
import org.koin.test.AutoCloseKoinTest
import org.koin.test.verify.verifyAll
import org.robolectric.RobolectricTestRunner

@RunWith(RobolectricTestRunner::class)
@OptIn(KoinExperimentalAPI::class)
class ModuleCheckTest : AutoCloseKoinTest() {

@Test
fun checkKoinModules() {
allModules.verifyAll()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.akexorcist.ruammij.fake

import com.akexorcist.ruammij.data.DeviceRepository
import com.akexorcist.ruammij.data.InstalledApp
import com.akexorcist.ruammij.data.database.SafeApp
import com.akexorcist.ruammij.ui.overview.MediaProjectionApp

class FakeDeviceRepository : DeviceRepository {
override suspend fun getInstalledApps(forceRefresh: Boolean): List<InstalledApp> {
return emptyList()
}

override suspend fun getSafeApps(forceRefresh: Boolean): List<SafeApp> {
return emptyList()
}

override suspend fun markAsSafe(packageName: String) {
}

override suspend fun getInstalledApp(
forceRefresh: Boolean,
packageName: String
): InstalledApp? {
return null
}

override suspend fun getEnabledAccessibilityApps(forceRefresh: Boolean): List<InstalledApp> {
return emptyList()
}

override suspend fun getAccessibilitySupportApps(forceRefresh: Boolean): List<InstalledApp> {
return emptyList()
}

override suspend fun getRunningMediaProjectionApps(forceRefresh: Boolean): List<MediaProjectionApp> {
return emptyList()
}

override suspend fun isUsbDebuggingEnabled(): Boolean {
return true
}

override suspend fun isWirelessDebuggingEnabled(): Boolean? {
return true
}

override suspend fun isDeveloperOptionsEnabled(): Boolean {
return true
}
}
Loading

0 comments on commit 7f57f90

Please sign in to comment.