mirror of
https://github.com/element-hq/element-android
synced 2024-11-27 03:48:12 +03:00
Start introducing tests
This commit is contained in:
parent
683305030a
commit
0266380485
7 changed files with 186 additions and 52 deletions
|
@ -20,6 +20,7 @@ repositories {
|
|||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
testOptions.unitTests.includeAndroidResources = true
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
|
@ -45,6 +46,7 @@ dependencies {
|
|||
def support_version = '28.0.0'
|
||||
def moshi_version = '1.8.0'
|
||||
def lifecycle_version = "1.1.1"
|
||||
def powermock_version = "2.0.0-RC.4"
|
||||
|
||||
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
@ -94,7 +96,14 @@ dependencies {
|
|||
|
||||
|
||||
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.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.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.model.ChunkEntity
|
||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||
|
@ -41,7 +40,7 @@ internal fun ChunkEntity.merge(chunkToMerge: ChunkEntity,
|
|||
eventsToMerge = chunkToMerge.events
|
||||
}
|
||||
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,
|
||||
stateIndexOffset: Int = 0,
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
var currentStateIndex = lastStateIndex(direction, defaultValue = stateIndexOffset)
|
||||
if (direction == PaginationDirection.FORWARDS && event.isStateEvent()) {
|
||||
if (direction == PaginationDirection.FORWARDS && EventType.isStateEvent(eventEntity.type)) {
|
||||
currentStateIndex += 1
|
||||
} else if (direction == PaginationDirection.BACKWARDS && events.isNotEmpty()) {
|
||||
val lastEventType = events.last()?.type ?: ""
|
||||
|
@ -81,7 +87,6 @@ internal fun ChunkEntity.add(event: Event,
|
|||
}
|
||||
}
|
||||
|
||||
val eventEntity = event.asEntity()
|
||||
eventEntity.stateIndex = currentStateIndex
|
||||
eventEntity.isUnlinked = isUnlinked
|
||||
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 {
|
||||
return when (direction) {
|
||||
PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex
|
||||
PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex
|
||||
} ?: defaultValue
|
||||
PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex
|
||||
PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex
|
||||
} ?: defaultValue
|
||||
}
|
|
@ -69,6 +69,7 @@ internal fun RealmList<EventEntity>.find(eventId: String): EventEntity? {
|
|||
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
|
||||
}
|
||||
|
|
|
@ -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 a new issue