Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
*.iml
/.idea/*
.gradle
/local.properties
/.idea/caches/build_file_checksums.ser
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
.DS_Store
/build
/captures
.externalNativeBuild
1 change: 1 addition & 0 deletions app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
49 changes: 49 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 28
defaultConfig {
applicationId "laurens.hearthstoneassessment"
minSdkVersion 15
targetSdkVersion 28
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
def room_version = "1.1.1"
implementation "android.arch.persistence.room:runtime:$room_version"
implementation 'com.squareup.picasso:picasso:2.71828'
kapt "android.arch.persistence.room:compiler:$room_version"
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation 'com.android.support:design:28.0.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.1"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.0.1"
implementation "org.jetbrains.anko:anko-commons:0.10.4"
implementation 'android.arch.paging:runtime:1.0.0'
implementation "android.arch.lifecycle:extensions:1.1.0"
implementation "android.arch.lifecycle:viewmodel:1.1.0"
implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.8"
implementation 'com.google.dagger:dagger:2.21'
kapt 'com.google.dagger:dagger-compiler:2.21'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'com.android.support.test:runner:1.0.2'
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
21 changes: 21 additions & 0 deletions app/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html

# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable

# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package laurens.hearthstoneassessment.dataaccess

import kotlinx.coroutines.runBlocking
import laurens.hearthstoneassessment.dataaccess.room.RoomCardDao
import laurens.hearthstoneassessment.dataaccess.room.RoomTest
import laurens.hearthstoneassessment.model.CardModel
import laurens.hearthstoneassessment.model.CardStatus
import org.junit.Assert.*
import org.junit.Before
import org.junit.Test
import java.lang.AssertionError
import javax.security.auth.Subject

class CardRepositoryTest : RoomTest() {

private lateinit var subject: CardRepository
private lateinit var cards: List<CardModel>

@Before
fun setup() {
subject = CardRepository(RoomCardDao(db.cardDao()), db.cardStatusDao())
runBlocking {
cards = subject.cards().awaitFirstData()
}
}

@Test
fun shouldSetFavoriteStatus() {
subject.selectItem(2)
subject.setCurrentFavoriteStatus(true)
runBlocking {
val cardStatus = cards.first { it.card.cardId == "EX1_100" }
.status.await()
assertEquals(CardStatus("EX1_100", true), cardStatus)
}
}

@Test
fun shouldTraverse() {
subject.selectItem(2)
val traverser = subject.traverser ?: throw AssertionError("Traverser should not be null")
assertEquals("EX1_100", traverser.current()?.card?.cardId)
assertEquals("EX1_012", traverser.previous()?.card?.cardId)
assertEquals("EX1_100", traverser.next()?.card?.cardId)
assertEquals("EX1_556", traverser.next()?.card?.cardId)
}


}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package laurens.hearthstoneassessment.dataaccess

import android.arch.lifecycle.LiveData
import android.arch.lifecycle.Observer
import android.arch.paging.DataSource
import android.arch.paging.LivePagedListBuilder
import android.arch.paging.PagedList
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.resume

suspend fun <T> LiveData<PagedList<T>>.awaitFirstData(): List<T> {
return suspendCancellableCoroutine { continuation ->
observeForever { nullable ->
nullable?.also {
continuation.resume(it.snapshot())
}
}
}
}

suspend fun <T> DataSource.Factory<Int, T>.awaitFirstData(): List<T> {
val pagedList = LivePagedListBuilder(this, 10).build()
return pagedList.awaitFirstData()
}

suspend fun <T> LiveData<T>.await(): T? {
return suspendCancellableCoroutine { continuation ->
this.observeForever(object : Observer<T> {
override fun onChanged(t: T?) {
this@await.removeObserver(this)
continuation.resume(t)
}
})

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package laurens.hearthstoneassessment.dataaccess.room

import kotlinx.coroutines.runBlocking
import laurens.hearthstoneassessment.dataaccess.await
import laurens.hearthstoneassessment.dataaccess.dao.CardStatusDao
import laurens.hearthstoneassessment.model.CardStatus
import org.junit.Assert
import org.junit.Before
import org.junit.Test

class CardStatusDaoTest : RoomTest() {

private lateinit var subject: CardStatusDao

@Before
fun setup() {
subject = db.cardStatusDao()
}

@Test
fun shouldToggleStatus() {
runBlocking {
Assert.assertEquals(null, subject.getCardStatusLive("card").await()?.favorite)
subject.updateOrInsert(CardStatus("non_existent", true))
Assert.assertEquals(true, subject.getCardStatusLive("non_existent").await()?.favorite)
subject.updateOrInsert(CardStatus("non_existent", false))
Assert.assertEquals(false, subject.getCardStatusLive("non_existent").await()?.favorite)

}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package laurens.hearthstoneassessment.dataaccess.room

import android.arch.paging.LivePagedListBuilder
import junit.framework.Assert.assertEquals
import kotlinx.coroutines.runBlocking
import laurens.hearthstoneassessment.dataaccess.SortDirection
import laurens.hearthstoneassessment.dataaccess.awaitFirstData
import laurens.hearthstoneassessment.model.Mechanic
import org.hamcrest.Matchers.containsInAnyOrder
import org.jetbrains.anko.AnkoLogger
import org.junit.Assert
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4

@RunWith(JUnit4::class)
class RoomCardDaoTest : RoomTest(), AnkoLogger {
private lateinit var subject: RoomCardDao

@Before
fun setup() {
subject = RoomCardDao(db.cardDao())
}


@Test
fun shouldLoadAllTestCards() {
runBlocking {
val cards = subject.loadCards().awaitFirstData()
assertEquals(5, cards.size)
}
}

@Test
fun shouldLoadClassesAndMechanicsInCard() {
runBlocking {
val card = subject.loadCards().awaitFirstData().first { it.cardId == "CFM_321" }
Assert.assertThat(
card.classes, containsInAnyOrder(
"Hunter",
"Paladin",
"Warrior"
)
)
Assert.assertThat(
card.mechanics, containsInAnyOrder(
Mechanic("Discover"),
Mechanic("Battlecry")
)
)
}
}

@Test
fun shouldSortCardsAscending() {
runBlocking {
val cards = subject.loadCards(sortDirection = SortDirection.ASCENDING).awaitFirstData()
val cardNames = cards.map { it.name }
assertEquals(cards.map { it.name }.sorted(), cardNames)
}
}

@Test
fun shouldSortCardsDescending() {
runBlocking {
val cards = subject.loadCards(sortDirection = SortDirection.DESCENDING).awaitFirstData()
val cardNames = cards.map { it.name }
assertEquals(cards.map { it.name }.sortedDescending(), cardNames)
}
}

@Test
fun shouldLoadLegendaryCards() {
runBlocking {
val cards = subject.loadCards(rarity = "Legendary").awaitFirstData()
Assert.assertThat(
cards.map { it.cardId }, containsInAnyOrder(
"EX1_012",
"EX1_100"
)
)
}
}

@Test
fun shouldLoadDeathrattleCards() {
runBlocking {
val cards = subject.loadCards(mechanic = "Deathrattle").awaitFirstData()
Assert.assertThat(
cards.map { it.cardId }, containsInAnyOrder(
"EX1_012",
"EX1_556"
)
)
}
}

@Test
fun shouldLoadClasses() {
runBlocking {
val cards = subject.loadCards(cardClass = "Hunter").awaitFirstData()
Assert.assertThat(
cards.map { it.cardId }, containsInAnyOrder(
"CFM_321"
)
)
}
}

@Test
fun shouldLoadType() {
runBlocking {
val cards = subject.loadCards(type = "Minion").awaitFirstData()
Assert.assertThat(
cards.map { it.cardId }, containsInAnyOrder(
"CFM_321",
"EX1_012",
"EX1_100",
"EX1_556"
)
)
}
}

@Test
fun shouldLoadLegendaryDeathrattleCard() {
runBlocking {
val cards = subject.loadCards(mechanic = "Deathrattle", rarity = "Legendary").awaitFirstData()
Assert.assertThat(
cards.map { it.cardId }, containsInAnyOrder(
"EX1_012"
)
)
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package laurens.hearthstoneassessment.dataaccess.room

import android.arch.persistence.room.Room
import android.support.test.InstrumentationRegistry
import kotlinx.coroutines.runBlocking
import laurens.hearthstoneassessment.test.R
import org.junit.After
import org.junit.Before
import java.io.IOException


open class RoomTest {
lateinit var db: LocalCardDatabase
private set


@After
@Throws(IOException::class)
fun closeDb() {
db.close()
}

@Before
fun createDb() {
val context = InstrumentationRegistry.getContext()
val populateRoomCallback = PopulateRoomCallback(context.resources.openRawResource(R.raw.test_cards))
db = Room.inMemoryDatabaseBuilder(
context, LocalCardDatabase::class.java
).build()
populateRoomCallback.database = db
runBlocking {
populateRoomCallback.insertCards()
}
}
}
Loading