Skip to content

Commit

Permalink
Minor tidy ups (#935)
Browse files Browse the repository at this point in the history
* Add suspend to upsertAll

* withContext only wraps mapping functions

* Remove main coroutine rule from TaskDaoTest

* Rename tasksRepository to taskRepository

* Replace MainCoroutineRule with testScope and testDispatcher

* Fix spotless

* All tests run with testScope.runTest

* Add mutex to TaskNetworkDataSource

* Fix spotless

* Recreate database before each test
  • Loading branch information
dturner authored Apr 3, 2023
1 parent 4aff247 commit d7ba290
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 107 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,11 @@ import androidx.room.Room
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.example.android.architecture.blueprints.todoapp.MainCoroutineRule
import junit.framework.TestCase.assertEquals
import junit.framework.TestCase.assertNotNull
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

Expand All @@ -37,19 +35,16 @@ class TaskDaoTest {

// using an in-memory database because the information stored here disappears when the
// process is killed
private val database = Room.inMemoryDatabaseBuilder(
getApplicationContext(),
ToDoDatabase::class.java
).allowMainThreadQueries().build()
private lateinit var database: ToDoDatabase

// Set the main coroutines dispatcher for unit testing.
@get:Rule
val mainCoroutineRule = MainCoroutineRule()

// Ensure that we use an empty database for each test.
// Ensure that we use a new database for each test.
@Before
fun initDb() = database.clearAllTables()

fun initDb() {
database = Room.inMemoryDatabaseBuilder(
getApplicationContext(),
ToDoDatabase::class.java
).allowMainThreadQueries().build()
}
@Test
fun insertTaskAndGetById() = runTest {
// GIVEN - insert a task
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,10 @@ class DefaultTaskRepository @Inject constructor(
scope.launch {
try {
val localTasks = localDataSource.getAll()
networkDataSource.saveTasks(localTasks.toNetwork())
val networkTasks = withContext(dispatcher) {
localTasks.toNetwork()
}
networkDataSource.saveTasks(networkTasks)
} catch (e: Exception) {
// In a real app you'd handle the exception e.g. by exposing a `networkStatus` flow
// to an app level UI state holder which could then display a Toast message.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ interface TaskDao {
* @param tasks the tasks to be inserted or updated.
*/
@Upsert
fun upsertAll(tasks: List<LocalTask>)
suspend fun upsertAll(tasks: List<LocalTask>)

/**
* Update the complete status of a task
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,46 +18,35 @@ package com.example.android.architecture.blueprints.todoapp.data.source.network

import javax.inject.Inject
import kotlinx.coroutines.delay
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock

/**
* Implementation of the data source that adds a latency simulating network.
*
*/
class TaskNetworkDataSource @Inject constructor() : NetworkDataSource {

private val SERVICE_LATENCY_IN_MILLIS = 2000L

private var TASK_SERVICE_DATA = LinkedHashMap<String, NetworkTask>(2)

init {
addTask(
// A mutex is used to ensure that reads and writes are thread-safe.
private val accessMutex = Mutex()
private var tasks = listOf(
NetworkTask(
id = "PISA",
title = "Build tower in Pisa",
shortDescription = "Ground looks good, no foundation work required."
)
addTask(
),
NetworkTask(
id = "TACOMA",
title = "Finish bridge in Tacoma",
shortDescription = "Found awesome girders at half the cost!"
)
}
)

override suspend fun loadTasks(): List<NetworkTask> {
// Simulate network by delaying the execution.
val tasks = TASK_SERVICE_DATA.values.toList()
override suspend fun loadTasks(): List<NetworkTask> = accessMutex.withLock {
delay(SERVICE_LATENCY_IN_MILLIS)
return tasks
}

override suspend fun saveTasks(tasks: List<NetworkTask>) {
// Simulate network by delaying the execution.
override suspend fun saveTasks(newTasks: List<NetworkTask>) = accessMutex.withLock {
delay(SERVICE_LATENCY_IN_MILLIS)
TASK_SERVICE_DATA.clear()
TASK_SERVICE_DATA.putAll(tasks.associateBy(NetworkTask::id))
}

private fun addTask(id: String, title: String, shortDescription: String) {
val newTask = NetworkTask(id = id, title = title, shortDescription = shortDescription)
TASK_SERVICE_DATA[newTask.id] = newTask
tasks = newTasks
}
}

private const val SERVICE_LATENCY_IN_MILLIS = 2000L
Loading

0 comments on commit d7ba290

Please sign in to comment.