Skip to content

Commit

Permalink
Added tests for remaining viewmodels in main source set
Browse files Browse the repository at this point in the history
  • Loading branch information
maltaisn committed Apr 26, 2020
1 parent c7f3a00 commit 0313638
Show file tree
Hide file tree
Showing 13 changed files with 694 additions and 34 deletions.
2 changes: 2 additions & 0 deletions app/src/main/kotlin/com/maltaisn/notes/model/PrefsManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import android.content.Context
import android.content.SharedPreferences
import androidx.core.content.edit
import androidx.preference.PreferenceManager
import com.maltaisn.notes.OpenForTesting
import com.maltaisn.notes.R
import com.maltaisn.notes.ui.AppTheme
import com.maltaisn.notes.ui.note.adapter.NoteListLayoutMode
Expand All @@ -32,6 +33,7 @@ import kotlin.time.days
*
* Flavors provide their own extension of this manager.
*/
@OpenForTesting
open class PrefsManager(protected val prefs: SharedPreferences) {

val theme: AppTheme
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.yield


class HomeViewModel @AssistedInject constructor(
Expand Down Expand Up @@ -97,6 +98,7 @@ class HomeViewModel @AssistedInject constructor(
noteListJob = viewModelScope.launch {
notesRepository.getNotesByStatus(status).collect { notes ->
createListItems(status, notes)
yield()
}
}
}
Expand Down Expand Up @@ -168,7 +170,7 @@ class HomeViewModel @AssistedInject constructor(
PrefsManager.TRASH_REMINDER_DELAY.toLongMilliseconds()) {
this += MessageItem(TRASH_REMINDER_ITEM_ID,
R.string.trash_reminder_message,
PrefsManager.TRASH_AUTO_DELETE_DELAY.inDays.toInt())
listOf(PrefsManager.TRASH_AUTO_DELETE_DELAY.inDays.toInt()))
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ abstract class NoteFragment : Fragment(), ActionMode.Callback, ConfirmDialog.Cal
when (item.itemId) {
R.id.item_move -> viewModel.moveSelectedNotes()
R.id.item_select_all -> viewModel.selectAll()
R.id.item_share -> viewModel.shareNote()
R.id.item_share -> viewModel.shareSelectedNote()
R.id.item_copy -> viewModel.copySelectedNote(
getString(R.string.edit_copy_untitled_name), getString(R.string.edit_copy_suffix))
R.id.item_delete -> viewModel.deleteSelectedNotesPre()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,7 +170,7 @@ abstract class NoteViewModel(
}
}

fun shareNote() {
fun shareSelectedNote() {
if (selectedNotes.isEmpty()) {
return
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,13 @@ data class NoteItem(override val id: Long, val note: Note, val checked: Boolean,

}

class HeaderItem(id: Long, @StringRes val title: Int) : NoteListItem(id) {
data class HeaderItem(override val id: Long, @StringRes val title: Int) : NoteListItem(id) {
override val type: Int
get() = NoteAdapter.VIEW_TYPE_HEADER
}

class MessageItem(id: Long, @StringRes @PluralsRes val message: Int,
vararg val args: Any) : NoteListItem(id) {
data class MessageItem(override val id: Long, @StringRes @PluralsRes val message: Int,
val args: List<Any>) : NoteListItem(id) {
override val type: Int
get() = NoteAdapter.VIEW_TYPE_MESSAGE
}
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ class MessageViewHolder(private val binding: ItemMessageBinding) :
RecyclerView.ViewHolder(binding.root) {

fun bind(item: MessageItem, adapter: NoteAdapter) {
binding.messageTxv.text = adapter.context.getString(item.message, *item.args)
binding.messageTxv.text = adapter.context.getString(item.message, *item.args.toTypedArray())
binding.closeImv.setOnClickListener {
adapter.callback.onMessageItemDismissed(item, adapterPosition)
adapter.notifyItemRemoved(adapterPosition)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ package com.maltaisn.notes.model

import com.maltaisn.notes.model.entity.Note
import com.maltaisn.notes.model.entity.NoteStatus
import kotlinx.coroutines.channels.ConflatedBroadcastChannel
import kotlinx.coroutines.channels.sendBlocking
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.JsonConfiguration
Expand All @@ -26,6 +30,9 @@ import kotlinx.serialization.json.JsonObject

/**
* Implementation of the notes repository that stores data itself instead of relying on DAOs.
*
* This implementation should work almost exactly like [DefaultNotesRepository].
* Returned flows will also emit a new value on every change.
*/
class MockNotesRepository : NotesRepository {

Expand All @@ -42,6 +49,8 @@ class MockNotesRepository : NotesRepository {
var lastAddedNote: Note? = null
private set

private val changeChannel = ConflatedBroadcastChannel(Unit)


fun addNote(note: Note): Long {
val id = if (note.id != Note.NO_ID) {
Expand All @@ -56,6 +65,7 @@ class MockNotesRepository : NotesRepository {
lastId
}
lastAddedNote = notes[id]
changeChannel.sendBlocking(Unit)
return id
}

Expand All @@ -75,6 +85,7 @@ class MockNotesRepository : NotesRepository {

override suspend fun deleteNote(note: Note) {
notes.remove(note.id)
changeChannel.sendBlocking(Unit)
}

override suspend fun deleteNotes(notes: List<Note>) {
Expand All @@ -86,21 +97,39 @@ class MockNotesRepository : NotesRepository {
override suspend fun getById(id: Long) = notes[id]

override fun getNotesByStatus(status: NoteStatus) = flow {
emit(notes.mapNotNull { (_, note) ->
note.takeIf { note.status == status }
})
changeChannel.asFlow().collect {
// Sort by last modified, then by ID.
val sorted = notes.values.sortedWith(
compareByDescending<Note> { it.lastModifiedDate }.thenBy { it.id })
emit(sorted.mapNotNull { note ->
note.takeIf { note.status == status }
})
}
}

override fun searchNotes(query: String) = flow {
emit(notes.mapNotNull { (_, note) ->
note.takeIf { query in note.title || query in note.content }
})
override fun searchNotes(query: String) = flow<List<Note>> {
val queryNoFtsSyntax = query.replace("[*\"-]".toRegex(), "")
if (queryNoFtsSyntax.isEmpty()) {
emit(emptyList())
} else {
changeChannel.asFlow().collect {
val found = notes.mapNotNullTo(ArrayList()) { (_, note) ->
note.takeIf {
note.status != NoteStatus.TRASHED &&
(queryNoFtsSyntax in note.title || queryNoFtsSyntax in note.content)
}
}
found.sortWith(compareBy<Note> { it.status }.thenByDescending { it.lastModifiedDate })
emit(found)
}
}
}

override suspend fun emptyTrash() {
notes.entries.removeIf { (_, note) ->
note.status == NoteStatus.TRASHED
}
changeChannel.sendBlocking(Unit)
}

override suspend fun deleteOldNotesInTrash() {
Expand All @@ -109,6 +138,7 @@ class MockNotesRepository : NotesRepository {
(System.currentTimeMillis() - note.lastModifiedDate.time) >
PrefsManager.TRASH_AUTO_DELETE_DELAY.toLongMilliseconds()
}
changeChannel.sendBlocking(Unit)
}

override suspend fun getJsonData(): String {
Expand All @@ -121,6 +151,13 @@ class MockNotesRepository : NotesRepository {
override suspend fun clearAllData() {
notes.clear()
lastId = 0
changeChannel.sendBlocking(Unit)
}

fun getAll() = flow {
changeChannel.asFlow().collect {
emit(notes.values)
}
}

}
18 changes: 18 additions & 0 deletions app/src/sharedTest/kotlin/com/maltaisn/notes/noteUtils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package com.maltaisn.notes

import com.maltaisn.notes.model.entity.*
import org.junit.Assert.assertEquals
import org.junit.Assert.assertTrue
import java.util.*


Expand Down Expand Up @@ -44,3 +46,19 @@ fun listNote(
synced: Boolean = true
) = Note(id, uuid, NoteType.LIST, title, items.joinToString("\n") { it.content },
ListNoteMetadata(items.map { it.checked }), added, modified, status, synced)


fun assertNoteEquals(expected: Note, actual: Note,
dateEpsilon: Long = 1000,
ignoreId: Boolean = true,
ignoreUuid: Boolean = true) {
assertTrue("Notes have different added dates.",
(expected.addedDate.time - actual.addedDate.time) < dateEpsilon)
assertTrue("Notes have different last modified dates.",
(expected.lastModifiedDate.time - actual.lastModifiedDate.time) < dateEpsilon)
assertEquals(expected, actual.copy(
id = if (ignoreId) expected.id else actual.id,
uuid = if (ignoreUuid) expected.uuid else actual.uuid,
addedDate = expected.addedDate,
lastModifiedDate = expected.lastModifiedDate))
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,9 @@ import com.maltaisn.notes.model.LoginRepository
import com.maltaisn.notes.ui.Event
import com.maltaisn.notes.ui.send
import com.maltaisn.notes.ui.sync.SyncPage
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.asFlow
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import javax.inject.Inject


Expand Down Expand Up @@ -63,9 +61,7 @@ class SyncMainViewModel @Inject constructor(
// Check if user has verified their email first. If not send verification.
loginRepository.reloadUser()
if (!loginRepository.isUserEmailVerified) {
withContext(Dispatchers.IO) {
loginRepository.sendVerificationEmail()
}
loginRepository.sendVerificationEmail()
_messageEvent.send(R.string.sync_verification_success_message)
}
} catch (e: FirebaseException) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ package com.maltaisn.notes.ui.edit

import androidx.arch.core.executor.testing.InstantTaskExecutorRule
import com.maltaisn.notes.MainCoroutineRule
import com.maltaisn.notes.assertNoteEquals
import com.maltaisn.notes.listNote
import com.maltaisn.notes.model.MockNotesRepository
import com.maltaisn.notes.model.converter.DateTimeConverter
Expand Down Expand Up @@ -586,19 +587,4 @@ class EditViewModelTest {
))
}

private fun assertNoteEquals(expected: Note, actual: Note,
dateEpsilon: Long = 1000,
ignoreId: Boolean = true,
ignoreUuid: Boolean = true) {
assertTrue("Notes have different added dates.",
(expected.addedDate.time - actual.addedDate.time) < dateEpsilon)
assertTrue("Notes have different last modified dates.",
(expected.lastModifiedDate.time - actual.lastModifiedDate.time) < dateEpsilon)
assertEquals(expected, actual.copy(
id = if (ignoreId) expected.id else actual.id,
uuid = if (ignoreUuid) expected.uuid else actual.uuid,
addedDate = expected.addedDate,
lastModifiedDate = expected.lastModifiedDate))
}

}
Loading

0 comments on commit 0313638

Please sign in to comment.