From 38ac427289ca3442f7facdcceca463f06449601c Mon Sep 17 00:00:00 2001 From: Serchinastico <54cymru@gmail.com> Date: Wed, 6 Feb 2019 19:27:29 +0100 Subject: [PATCH] Create thanos work Add tests --- .../superheroes/ui/view/FragmentTest.kt | 18 +--- .../superheroes/worker/ThanosWorkerTest.kt | 84 +++++++++++++++++++ .../superheroes/SuperHeroesApplication.kt | 23 ++++- .../repository/LocalSuperHeroDataSource.kt | 6 +- .../repository/RemoteSuperHeroDataSource.kt | 4 +- .../data/repository/room/SuperHeroDao.kt | 23 +++++ .../superheroes/worker/ThanosWorker.kt | 23 +++++ 7 files changed, 161 insertions(+), 20 deletions(-) create mode 100644 app/src/androidTest/java/com/karumi/jetpack/superheroes/worker/ThanosWorkerTest.kt create mode 100644 app/src/main/java/com/karumi/jetpack/superheroes/worker/ThanosWorker.kt diff --git a/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/FragmentTest.kt b/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/FragmentTest.kt index a2cd13d..fa6d28f 100644 --- a/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/FragmentTest.kt +++ b/app/src/androidTest/java/com/karumi/jetpack/superheroes/ui/view/FragmentTest.kt @@ -9,16 +9,13 @@ import androidx.test.filters.LargeTest import androidx.test.platform.app.InstrumentationRegistry import com.karumi.jetpack.superheroes.R import com.karumi.jetpack.superheroes.SuperHeroesApplication -import com.nhaarman.mockitokotlin2.any -import com.nhaarman.mockitokotlin2.mock import org.junit.Before import org.junit.runner.RunWith import org.kodein.di.Kodein import org.kodein.di.erased.bind import org.kodein.di.erased.instance import org.mockito.MockitoAnnotations -import java.util.concurrent.ExecutorService -import java.util.concurrent.FutureTask +import java.util.concurrent.Executor @LargeTest @RunWith(AndroidJUnit4::class) @@ -26,22 +23,15 @@ abstract class FragmentTest : ScreenshotTest { abstract val fragmentBlock: () -> F - private val executorServiceOnUiThread = mock { - on(it.execute(any())).thenAnswer { invocation -> - InstrumentationRegistry.getInstrumentation().runOnMainSync { - (invocation.getArgument(0) as Runnable).run() - } - FutureTask { null } - } - } - @Before fun setup() { MockitoAnnotations.initMocks(this) val app = ApplicationProvider.getApplicationContext() app.override(Kodein.Module("Base test dependencies", allowSilentOverride = true) { import(testDependencies, allowOverride = true) - bind() with instance(executorServiceOnUiThread) + bind() with instance(Executor { + InstrumentationRegistry.getInstrumentation().runOnMainSync { it.run() } + }) }) } diff --git a/app/src/androidTest/java/com/karumi/jetpack/superheroes/worker/ThanosWorkerTest.kt b/app/src/androidTest/java/com/karumi/jetpack/superheroes/worker/ThanosWorkerTest.kt new file mode 100644 index 0000000..65d0b69 --- /dev/null +++ b/app/src/androidTest/java/com/karumi/jetpack/superheroes/worker/ThanosWorkerTest.kt @@ -0,0 +1,84 @@ +package com.karumi.jetpack.superheroes.worker + +import androidx.room.Room +import androidx.test.core.app.ApplicationProvider +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import androidx.work.OneTimeWorkRequest +import androidx.work.WorkManager +import androidx.work.testing.WorkManagerTestInitHelper +import com.karumi.jetpack.superheroes.SuperHeroesApplication +import com.karumi.jetpack.superheroes.common.SuperHeroesDatabase +import com.karumi.jetpack.superheroes.data.repository.room.SuperHeroEntity +import com.karumi.jetpack.superheroes.domain.model.SuperHero +import org.junit.Assert.assertEquals +import org.junit.Before +import org.junit.Test +import org.kodein.di.Kodein +import org.kodein.di.erased.bind +import org.kodein.di.erased.instance +import java.util.concurrent.Executor + +class ThanosWorkerTest { + + private lateinit var database: SuperHeroesDatabase + + @Before + fun setUp() { + val app = ApplicationProvider.getApplicationContext() + WorkManagerTestInitHelper.initializeTestWorkManager(app) + database = Room.inMemoryDatabaseBuilder(app, SuperHeroesDatabase::class.java) + .allowMainThreadQueries() + .build() + app.override(Kodein.Module("Test dependencies", allowSilentOverride = true) { + bind() with instance(database) + bind() with instance(Executor { + getInstrumentation().runOnMainSync { it.run() } + }) + }) + database.superHeroesDao().deleteAll() + } + + @Test + fun thanosWorkerDoNotDeleteAnyoneIfSuperHeroesAreLessThanTwo() { + val superHeroes = insertSuperHeroes(1) + + enqueueWorker() + + assertEquals(superHeroes, getInsertedSuperHeroes()) + } + + @Test + fun thanosWorkerDeletesHalfTheSuperHeroesWhenThereAreMoreThanTwo() { + insertSuperHeroes(10) + + enqueueWorker() + + assertEquals(5, getInsertedSuperHeroes().size) + } + + @Test + fun thanosWorkerDeletesHalfTheSuperHeroesRoundingDownWhenThereIsAnOddNumberOfSuperHeroes() { + insertSuperHeroes(11) + + enqueueWorker() + + assertEquals(6, getInsertedSuperHeroes().size) + } + + private fun enqueueWorker() = getInstrumentation().runOnMainSync { + val work = OneTimeWorkRequest.Builder(ThanosWorker::class.java).build() + WorkManager.getInstance().enqueue(work) + } + + private fun insertSuperHeroes(numberOfSuperHeroes: Int): List { + val superHeroes = (1..numberOfSuperHeroes) + .map { SuperHero("#$it", "SH $it", null, false, "") } + + database.superHeroesDao().insertAll(superHeroes.map { SuperHeroEntity(it) }) + + return superHeroes + } + + private fun getInsertedSuperHeroes(): List = + database.superHeroesDao().getAllSuperHeroes().map { it.superHero } +} \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/SuperHeroesApplication.kt b/app/src/main/java/com/karumi/jetpack/superheroes/SuperHeroesApplication.kt index 8b26bf2..2b52683 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/SuperHeroesApplication.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/SuperHeroesApplication.kt @@ -1,6 +1,9 @@ package com.karumi.jetpack.superheroes import android.app.Application +import androidx.work.PeriodicWorkRequest +import androidx.work.PeriodicWorkRequest.MIN_PERIODIC_INTERVAL_MILLIS +import androidx.work.WorkManager import com.karumi.jetpack.superheroes.common.SuperHeroesDatabase import com.karumi.jetpack.superheroes.common.module import com.karumi.jetpack.superheroes.data.repository.LocalSuperHeroDataSource @@ -8,6 +11,7 @@ import com.karumi.jetpack.superheroes.data.repository.RemoteSuperHeroDataSource import com.karumi.jetpack.superheroes.data.repository.SuperHeroRepository import com.karumi.jetpack.superheroes.data.repository.SuperHeroesBoundaryCallback import com.karumi.jetpack.superheroes.data.repository.room.SuperHeroDao +import com.karumi.jetpack.superheroes.worker.ThanosWorker import org.kodein.di.DKodein import org.kodein.di.Kodein import org.kodein.di.KodeinAware @@ -16,8 +20,9 @@ import org.kodein.di.erased.bind import org.kodein.di.erased.instance import org.kodein.di.erased.provider import org.kodein.di.erased.singleton -import java.util.concurrent.ExecutorService +import java.util.concurrent.Executor import java.util.concurrent.Executors +import java.util.concurrent.TimeUnit class SuperHeroesApplication : Application(), KodeinAware { override var kodein = Kodein.lazy { @@ -25,6 +30,20 @@ class SuperHeroesApplication : Application(), KodeinAware { import(androidModule(this@SuperHeroesApplication)) } + override fun onCreate() { + super.onCreate() + startThanosWork() + } + + private fun startThanosWork() { + val work = PeriodicWorkRequest.Builder( + ThanosWorker::class.java, + MIN_PERIODIC_INTERVAL_MILLIS, + TimeUnit.MILLISECONDS + ).build() + WorkManager.getInstance().enqueue(work) + } + fun override(overrideModule: Kodein.Module) { kodein = Kodein.lazy { import(appDependencies()) @@ -53,7 +72,7 @@ class SuperHeroesApplication : Application(), KodeinAware { bind() with provider { RemoteSuperHeroDataSource(instance()) } - bind() with provider { + bind() with provider { Executors.newCachedThreadPool() } bind() with provider { this } diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/LocalSuperHeroDataSource.kt b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/LocalSuperHeroDataSource.kt index f905541..acc307f 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/LocalSuperHeroDataSource.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/LocalSuperHeroDataSource.kt @@ -7,11 +7,11 @@ import androidx.paging.PagedList import com.karumi.jetpack.superheroes.data.repository.room.SuperHeroDao import com.karumi.jetpack.superheroes.data.repository.room.SuperHeroEntity import com.karumi.jetpack.superheroes.domain.model.SuperHero -import java.util.concurrent.ExecutorService +import java.util.concurrent.Executor class LocalSuperHeroDataSource( private val dao: SuperHeroDao, - private val executor: ExecutorService + private val executor: Executor ) { fun getAllSuperHeroes( pageSize: Int, @@ -35,6 +35,8 @@ class LocalSuperHeroDataSource( return superHero } + fun deleteRandomHalf() = dao.deleteHalf() + private fun SuperHeroEntity.toSuperHero(): SuperHero = superHero private fun SuperHero.toEntity(): SuperHeroEntity = SuperHeroEntity(this) } \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/RemoteSuperHeroDataSource.kt b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/RemoteSuperHeroDataSource.kt index 196a4d6..32a3d98 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/RemoteSuperHeroDataSource.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/RemoteSuperHeroDataSource.kt @@ -3,10 +3,10 @@ package com.karumi.jetpack.superheroes.data.repository import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import com.karumi.jetpack.superheroes.domain.model.SuperHero -import java.util.concurrent.ExecutorService +import java.util.concurrent.Executor class RemoteSuperHeroDataSource( - private val executor: ExecutorService + private val executor: Executor ) { companion object { private const val BIT_TIME = 1500L diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/room/SuperHeroDao.kt b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/room/SuperHeroDao.kt index 2415ee9..0601628 100644 --- a/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/room/SuperHeroDao.kt +++ b/app/src/main/java/com/karumi/jetpack/superheroes/data/repository/room/SuperHeroDao.kt @@ -6,10 +6,14 @@ import androidx.room.Dao import androidx.room.Insert import androidx.room.OnConflictStrategy import androidx.room.Query +import androidx.room.Transaction import androidx.room.Update @Dao interface SuperHeroDao { + @Query("SELECT * FROM superheroes") + fun getAllSuperHeroes(): List + @Query("SELECT * FROM superheroes ORDER BY superhero_id ASC") fun getAll(): DataSource.Factory @@ -24,4 +28,23 @@ interface SuperHeroDao { @Query("DELETE FROM superheroes") fun deleteAll() + + @Query("DELETE FROM superheroes WHERE superhero_id IN (:ids)") + fun deleteAll(ids: List) + + @Transaction + fun deleteHalf() { + val superHeroes = getAllSuperHeroes() + + if (superHeroes.size < 2) { + return + } + + val randomSuperHeroIds = superHeroes + .shuffled() + .take(superHeroes.size / 2) + .map { it.superHero.id } + + deleteAll(randomSuperHeroIds) + } } \ No newline at end of file diff --git a/app/src/main/java/com/karumi/jetpack/superheroes/worker/ThanosWorker.kt b/app/src/main/java/com/karumi/jetpack/superheroes/worker/ThanosWorker.kt new file mode 100644 index 0000000..0103546 --- /dev/null +++ b/app/src/main/java/com/karumi/jetpack/superheroes/worker/ThanosWorker.kt @@ -0,0 +1,23 @@ +package com.karumi.jetpack.superheroes.worker + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.karumi.jetpack.superheroes.data.repository.LocalSuperHeroDataSource +import org.kodein.di.KodeinAware +import org.kodein.di.android.closestKodein +import org.kodein.di.erased.instance + +class ThanosWorker( + context: Context, + params: WorkerParameters +) : Worker(context, params), KodeinAware { + + override val kodein by closestKodein(context) + private val localSuperHeroesDataSource: LocalSuperHeroDataSource by instance() + + override fun doWork(): Result { + localSuperHeroesDataSource.deleteRandomHalf() + return Result.success() + } +} \ No newline at end of file