forked from GitHub-Mirror/riotX-android
Start introducing tests
This commit is contained in:
parent
683305030a
commit
0266380485
@ -20,6 +20,7 @@ repositories {
|
|||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 28
|
||||||
|
testOptions.unitTests.includeAndroidResources = true
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 21
|
minSdkVersion 21
|
||||||
@ -45,6 +46,7 @@ dependencies {
|
|||||||
def support_version = '28.0.0'
|
def support_version = '28.0.0'
|
||||||
def moshi_version = '1.8.0'
|
def moshi_version = '1.8.0'
|
||||||
def lifecycle_version = "1.1.1"
|
def lifecycle_version = "1.1.1"
|
||||||
|
def powermock_version = "2.0.0-RC.4"
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
@ -94,7 +96,14 @@ dependencies {
|
|||||||
|
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.12'
|
testImplementation 'junit:junit:4.12'
|
||||||
|
testImplementation 'org.robolectric:robolectric:4.0.2'
|
||||||
|
testImplementation 'org.robolectric:shadows-support-v4:3.0'
|
||||||
|
testImplementation "io.mockk:mockk:1.8.13.kotlin13"
|
||||||
|
testImplementation 'org.amshove.kluent:kluent-android:1.44'
|
||||||
|
|
||||||
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
androidTestImplementation 'com.android.support.test:runner:1.0.2'
|
||||||
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
|
||||||
|
androidTestImplementation 'org.amshove.kluent:kluent-android:1.44'
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
package im.vector.matrix.android;
|
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.support.test.InstrumentationRegistry;
|
|
||||||
import android.support.test.runner.AndroidJUnit4;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
import org.junit.runner.RunWith;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instrumented test, which will execute on an Android device.
|
|
||||||
*
|
|
||||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
|
||||||
*/
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
|
||||||
public class ExampleInstrumentedTest {
|
|
||||||
@Test
|
|
||||||
public void useAppContext() {
|
|
||||||
// Context of the app under test.
|
|
||||||
Context appContext = InstrumentationRegistry.getTargetContext();
|
|
||||||
|
|
||||||
assertEquals("im.vector.matrix.android.test", appContext.getPackageName());
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,15 @@
|
|||||||
|
package im.vector.matrix.android
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.support.test.InstrumentationRegistry
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
abstract class InstrumentedTest {
|
||||||
|
fun context(): Context {
|
||||||
|
return InstrumentationRegistry.getTargetContext()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cacheDir(): File {
|
||||||
|
return context().cacheDir
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,147 @@
|
|||||||
|
package im.vector.matrix.android.session.room.timeline
|
||||||
|
|
||||||
|
import com.zhuinden.monarchy.Monarchy
|
||||||
|
import im.vector.matrix.android.InstrumentedTest
|
||||||
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
|
import im.vector.matrix.android.internal.database.helper.add
|
||||||
|
import im.vector.matrix.android.internal.database.helper.addAll
|
||||||
|
import im.vector.matrix.android.internal.database.helper.isUnlinked
|
||||||
|
import im.vector.matrix.android.internal.database.helper.lastStateIndex
|
||||||
|
import im.vector.matrix.android.internal.database.helper.merge
|
||||||
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
|
import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
import io.realm.kotlin.createObject
|
||||||
|
import org.amshove.kluent.shouldEqual
|
||||||
|
import org.junit.Before
|
||||||
|
import org.junit.Test
|
||||||
|
import kotlin.random.Random
|
||||||
|
|
||||||
|
|
||||||
|
internal class ChunkEntityTest : InstrumentedTest() {
|
||||||
|
|
||||||
|
private lateinit var monarchy: Monarchy
|
||||||
|
|
||||||
|
@Before
|
||||||
|
fun setup() {
|
||||||
|
Realm.init(context())
|
||||||
|
val testConfig = RealmConfiguration.Builder().inMemory().name("test-realm").build()
|
||||||
|
monarchy = Monarchy.Builder().setRealmConfiguration(testConfig).build()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun add_shouldAdd_whenNotAlreadyIncluded() {
|
||||||
|
monarchy.runTransactionSync { realm ->
|
||||||
|
val chunk: ChunkEntity = realm.createObject()
|
||||||
|
val fakeEvent = createFakeEvent(false)
|
||||||
|
chunk.add(fakeEvent, PaginationDirection.FORWARDS)
|
||||||
|
chunk.events.size shouldEqual 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun add_shouldNotAdd_whenAlreadyIncluded() {
|
||||||
|
monarchy.runTransactionSync { realm ->
|
||||||
|
val chunk: ChunkEntity = realm.createObject()
|
||||||
|
val fakeEvent = createFakeEvent(false)
|
||||||
|
chunk.add(fakeEvent, PaginationDirection.FORWARDS)
|
||||||
|
chunk.add(fakeEvent, PaginationDirection.FORWARDS)
|
||||||
|
chunk.events.size shouldEqual 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun add_shouldStateIndexIncremented_whenStateEventIsAddedForward() {
|
||||||
|
monarchy.runTransactionSync { realm ->
|
||||||
|
val chunk: ChunkEntity = realm.createObject()
|
||||||
|
val fakeEvent = createFakeEvent(true)
|
||||||
|
chunk.add(fakeEvent, PaginationDirection.FORWARDS)
|
||||||
|
chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun add_shouldStateIndexNotIncremented_whenNoStateEventIsAdded() {
|
||||||
|
monarchy.runTransactionSync { realm ->
|
||||||
|
val chunk: ChunkEntity = realm.createObject()
|
||||||
|
val fakeEvent = createFakeEvent(false)
|
||||||
|
chunk.add(fakeEvent, PaginationDirection.FORWARDS)
|
||||||
|
chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addAll_shouldStateIndexIncremented_whenStateEventsAreAddedForward() {
|
||||||
|
monarchy.runTransactionSync { realm ->
|
||||||
|
val chunk: ChunkEntity = realm.createObject()
|
||||||
|
val fakeEvents = createFakeListOfEvents(30)
|
||||||
|
val numberOfStateEvents = fakeEvents.filter { it.isStateEvent() }.size
|
||||||
|
chunk.addAll(fakeEvents, PaginationDirection.FORWARDS)
|
||||||
|
chunk.lastStateIndex(PaginationDirection.FORWARDS) shouldEqual numberOfStateEvents
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun addAll_shouldStateIndexDecremented_whenStateEventsAreAddedBackward() {
|
||||||
|
monarchy.runTransactionSync { realm ->
|
||||||
|
val chunk: ChunkEntity = realm.createObject()
|
||||||
|
val fakeEvents = createFakeListOfEvents(30)
|
||||||
|
val numberOfStateEvents = fakeEvents.filter { it.isStateEvent() }.size
|
||||||
|
val lastIsState = fakeEvents.last().isStateEvent()
|
||||||
|
val expectedStateIndex = if (lastIsState) -numberOfStateEvents + 1 else -numberOfStateEvents
|
||||||
|
chunk.addAll(fakeEvents, PaginationDirection.BACKWARDS)
|
||||||
|
chunk.lastStateIndex(PaginationDirection.BACKWARDS) shouldEqual expectedStateIndex
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun merge_shouldAddEvents_whenMergingBackward() {
|
||||||
|
monarchy.runTransactionSync { realm ->
|
||||||
|
val chunk1: ChunkEntity = realm.createObject()
|
||||||
|
val chunk2: ChunkEntity = realm.createObject()
|
||||||
|
chunk1.addAll(createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
||||||
|
chunk2.addAll(createFakeListOfEvents(30), PaginationDirection.BACKWARDS)
|
||||||
|
chunk1.merge(chunk2, PaginationDirection.BACKWARDS)
|
||||||
|
chunk1.events.size shouldEqual 60
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun merge_shouldEventsBeLinked_whenMergingLinkedWithUnlinked() {
|
||||||
|
monarchy.runTransactionSync { realm ->
|
||||||
|
val chunk1: ChunkEntity = realm.createObject()
|
||||||
|
val chunk2: ChunkEntity = realm.createObject()
|
||||||
|
chunk1.addAll(createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
||||||
|
chunk2.addAll(createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = false)
|
||||||
|
chunk1.merge(chunk2, PaginationDirection.BACKWARDS)
|
||||||
|
chunk1.isUnlinked() shouldEqual false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun merge_shouldEventsBeUnlinked_whenMergingUnlinkedWithUnlinked() {
|
||||||
|
monarchy.runTransactionSync { realm ->
|
||||||
|
val chunk1: ChunkEntity = realm.createObject()
|
||||||
|
val chunk2: ChunkEntity = realm.createObject()
|
||||||
|
chunk1.addAll(createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
||||||
|
chunk2.addAll(createFakeListOfEvents(30), PaginationDirection.BACKWARDS, isUnlinked = true)
|
||||||
|
chunk1.merge(chunk2, PaginationDirection.BACKWARDS)
|
||||||
|
chunk1.isUnlinked() shouldEqual true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun createFakeListOfEvents(size: Int = 10): List<Event> {
|
||||||
|
return (0 until size).map { createFakeEvent(Random.nextBoolean()) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createFakeEvent(asStateEvent: Boolean = false): Event {
|
||||||
|
val eventId = Random.nextLong(System.currentTimeMillis()).toString()
|
||||||
|
val type = if (asStateEvent) EventType.STATE_ROOM_NAME else EventType.MESSAGE
|
||||||
|
return Event(type, eventId)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,7 +2,6 @@ package im.vector.matrix.android.internal.database.helper
|
|||||||
|
|
||||||
import im.vector.matrix.android.api.session.events.model.Event
|
import im.vector.matrix.android.api.session.events.model.Event
|
||||||
import im.vector.matrix.android.api.session.events.model.EventType
|
import im.vector.matrix.android.api.session.events.model.EventType
|
||||||
import im.vector.matrix.android.internal.database.mapper.asDomain
|
|
||||||
import im.vector.matrix.android.internal.database.mapper.asEntity
|
import im.vector.matrix.android.internal.database.mapper.asEntity
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
@ -41,7 +40,7 @@ internal fun ChunkEntity.merge(chunkToMerge: ChunkEntity,
|
|||||||
eventsToMerge = chunkToMerge.events
|
eventsToMerge = chunkToMerge.events
|
||||||
}
|
}
|
||||||
eventsToMerge.forEach {
|
eventsToMerge.forEach {
|
||||||
add(it.asDomain(), direction, isUnlinked = isUnlinked)
|
add(it, direction, isUnlinked = isUnlinked)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -63,16 +62,23 @@ internal fun ChunkEntity.add(event: Event,
|
|||||||
direction: PaginationDirection,
|
direction: PaginationDirection,
|
||||||
stateIndexOffset: Int = 0,
|
stateIndexOffset: Int = 0,
|
||||||
isUnlinked: Boolean = false) {
|
isUnlinked: Boolean = false) {
|
||||||
|
add(event.asEntity(), direction, stateIndexOffset, isUnlinked)
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun ChunkEntity.add(eventEntity: EventEntity,
|
||||||
|
direction: PaginationDirection,
|
||||||
|
stateIndexOffset: Int = 0,
|
||||||
|
isUnlinked: Boolean = false) {
|
||||||
if (!isManaged) {
|
if (!isManaged) {
|
||||||
throw IllegalStateException("Chunk entity should be managed to use fast contains")
|
throw IllegalStateException("Chunk entity should be managed to use fast contains")
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event.eventId == null || events.fastContains(event.eventId)) {
|
if (eventEntity.eventId.isEmpty() || events.fastContains(eventEntity.eventId)) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var currentStateIndex = lastStateIndex(direction, defaultValue = stateIndexOffset)
|
var currentStateIndex = lastStateIndex(direction, defaultValue = stateIndexOffset)
|
||||||
if (direction == PaginationDirection.FORWARDS && event.isStateEvent()) {
|
if (direction == PaginationDirection.FORWARDS && EventType.isStateEvent(eventEntity.type)) {
|
||||||
currentStateIndex += 1
|
currentStateIndex += 1
|
||||||
} else if (direction == PaginationDirection.BACKWARDS && events.isNotEmpty()) {
|
} else if (direction == PaginationDirection.BACKWARDS && events.isNotEmpty()) {
|
||||||
val lastEventType = events.last()?.type ?: ""
|
val lastEventType = events.last()?.type ?: ""
|
||||||
@ -81,7 +87,6 @@ internal fun ChunkEntity.add(event: Event,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val eventEntity = event.asEntity()
|
|
||||||
eventEntity.stateIndex = currentStateIndex
|
eventEntity.stateIndex = currentStateIndex
|
||||||
eventEntity.isUnlinked = isUnlinked
|
eventEntity.isUnlinked = isUnlinked
|
||||||
val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size
|
val position = if (direction == PaginationDirection.FORWARDS) 0 else this.events.size
|
||||||
@ -90,7 +95,7 @@ internal fun ChunkEntity.add(event: Event,
|
|||||||
|
|
||||||
internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
|
internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int {
|
||||||
return when (direction) {
|
return when (direction) {
|
||||||
PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex
|
PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex
|
||||||
PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex
|
PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex
|
||||||
} ?: defaultValue
|
} ?: defaultValue
|
||||||
}
|
}
|
@ -69,6 +69,7 @@ internal fun RealmList<EventEntity>.find(eventId: String): EventEntity? {
|
|||||||
return this.where().equalTo(EventEntityFields.EVENT_ID, eventId).findFirst()
|
return this.where().equalTo(EventEntityFields.EVENT_ID, eventId).findFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun RealmList<EventEntity>.fastContains(eventId: String): Boolean {
|
internal fun RealmList<EventEntity>.
|
||||||
|
fastContains(eventId: String): Boolean {
|
||||||
return this.find(eventId) != null
|
return this.find(eventId) != null
|
||||||
}
|
}
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
package im.vector.matrix.android;
|
|
||||||
|
|
||||||
import org.junit.Test;
|
|
||||||
|
|
||||||
import static org.junit.Assert.*;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Example local unit test, which will execute on the development machine (host).
|
|
||||||
*
|
|
||||||
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
|
||||||
*/
|
|
||||||
public class ExampleUnitTest {
|
|
||||||
@Test
|
|
||||||
public void addition_isCorrect() {
|
|
||||||
assertEquals(4, 2 + 2);
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user