Merge remote-tracking branch 'origin/feature/coroutines' into feature/coroutines

This commit is contained in:
Mike Nakhimovich 2019-02-12 11:41:01 -05:00
commit c072f1532b
16 changed files with 336 additions and 403 deletions

View file

@ -1,40 +1,26 @@
//package com.nytimes.android.external.store3.base.impl
//
//import com.nytimes.android.external.store3.util.KeyParser
//import com.nytimes.android.external.store3.util.ParserException
//
//import java.util.ArrayList
//
//import io.reactivex.annotations.NonNull
//
//import com.nytimes.android.external.cache3.Preconditions.checkArgument
//import com.nytimes.android.external.cache3.Preconditions.checkNotNull
//
//class MultiParser<Key, Raw, Parsed>(parsers: List<KeyParser<Key, Any, Any>>) : KeyParser<Key, Raw, Parsed> {
//
// private val parsers = ArrayList<KeyParser<*, *, *>>()
//
// init {
// this.parsers.addAll(parsers)
// }
//
// private fun createParserException(): ParserException {
// return ParserException("One of the provided parsers has a wrong typing. " + "Make sure that parsers are passed in a correct order and the fromTypes match each other.")
// }
//
// @NonNull
// @Throws(ParserException::class)
// override
// suspend fun apply(@NonNull key: Key, @NonNull raw: Raw): Parsed {
// var parsed: Any = raw!!
// for (parser in parsers) {
// try {
// parsed = parser.apply(key, parsed)!!
// } catch (exception: ClassCastException) {
// throw createParserException()
// }
//
// }
// return parsed as Parsed
// }
//}
package com.nytimes.android.external.store3.base.impl
import com.nytimes.android.external.store3.util.KeyParser
import com.nytimes.android.external.store3.util.ParserException
import io.reactivex.annotations.NonNull
class MultiParser<Key, Raw, Parsed>(private val parsers: List<KeyParser<Any?, Any?, Any?>>) : KeyParser<Key, Raw, Parsed> {
private fun createParserException(): ParserException {
return ParserException("One of the provided parsers has a wrong typing. " +
"Make sure that parsers are passed in a correct order and the fromTypes match each other.")
}
@NonNull
override suspend fun apply(@NonNull key: Key, @NonNull raw: Raw): Parsed {
var parsed: Any = raw!!
for (parser in parsers) {
try {
parsed = parser.apply(key, parsed)!!
} catch (exception: ClassCastException) {
throw createParserException()
}
}
return parsed as Parsed
}
}

View file

@ -10,9 +10,9 @@ import com.nytimes.android.external.store3.base.Parser
*/
class ParsingFetcher<Parsed, Raw, Key>
(private val rawFetcher: Fetcher<Raw, Key>,
private val parser: Parser<Raw, Parsed>) {
private val parser: Parser<Raw, Parsed>) : Fetcher<Parsed, Key> {
suspend fun fetch(key: Key): Parsed {
override suspend fun fetch(key: Key): Parsed {
return rawFetcher.fetch(key).let { parser.apply(it) }
}

View file

@ -9,8 +9,6 @@ import com.nytimes.android.external.store3.util.KeyParser
import com.nytimes.android.external.store3.util.NoKeyParser
import com.nytimes.android.external.store3.util.NoopParserFunc
import com.nytimes.android.external.store3.util.NoopPersister
import io.reactivex.Maybe
import io.reactivex.Observable
import io.reactivex.Single
import kotlinx.coroutines.channels.ReceiveChannel
@ -38,12 +36,12 @@ open class RealStore<Parsed, Key> : Store<Parsed, Key> {
StalePolicy.UNSPECIFIED)
}
constructor(fetcher: Fetcher<Any, Key>,
persister: Persister<Any, Key>,
parser: Parser<Any, Parsed>) {
internalStore = RealInternalStore(fetcher,
persister,
NoKeyParser(parser),
constructor(fetcher: Fetcher<*, Key>,
persister: Persister<*, Key>,
parser: Parser<*, Parsed>) {
internalStore = RealInternalStore(fetcher as Fetcher<Any, Key>,
persister as Persister<Any, Key>,
NoKeyParser(parser as Parser<Any, Parsed>),
StalePolicy.UNSPECIFIED)
}
@ -72,7 +70,7 @@ open class RealStore<Parsed, Key> : Store<Parsed, Key> {
}
fun getWithResult(key: Key): Single<Result<Parsed>> {
TODO("not implemented")
TODO("not implemented")
}
fun getRefreshing(key: Key): Observable<Parsed> {

View file

@ -6,13 +6,14 @@ import com.nytimes.android.external.store3.util.KeyParser
import com.nytimes.android.external.store3.util.NoKeyParser
import com.nytimes.android.external.store3.util.NoopParserFunc
import com.nytimes.android.external.store3.util.NoopPersister
import java.util.*
/**
* Builder where there parser is used.
*/
class RealStoreBuilder<Raw, Parsed, Key> {
private var parser: KeyParser<Key, Raw, Parsed>? = null
private val parsers = ArrayList<KeyParser<Any?, Any?, Any?>>()
private var persister: Persister<Raw, Key>? = null
private var fetcher: Fetcher<Raw, Key>? = null
private var memoryPolicy: MemoryPolicy? = null
@ -20,27 +21,24 @@ class RealStoreBuilder<Raw, Parsed, Key> {
private//remove when it is implemented...
var stalePolicy = StalePolicy.UNSPECIFIED
fun fetcher(fetcher: Fetcher<Raw, Key>): RealStoreBuilder<Raw, Parsed, Key> {
fun fetcher(fetcher: Fetcher<Raw, Key>): RealStoreBuilder<Raw, Parsed, Key> = apply {
this.fetcher = fetcher
return this
}
fun fetcher(fetcher: suspend (Key) -> Raw): RealStoreBuilder<Raw, Parsed, Key> {
fun fetcher(fetcher: suspend (Key) -> Raw): RealStoreBuilder<Raw, Parsed, Key> = apply {
this.fetcher = object : Fetcher<Raw, Key> {
override suspend fun fetch(key: Key): Raw {
return fetcher(key)
}
}
return this
}
fun persister(persister: Persister<Raw, Key>): RealStoreBuilder<Raw, Parsed, Key> {
fun persister(persister: Persister<Raw, Key>): RealStoreBuilder<Raw, Parsed, Key> = apply {
this.persister = persister
return this
}
fun persister(diskRead: DiskRead<Raw, Key>,
diskWrite: DiskWrite<Raw, Key>): RealStoreBuilder<Raw, Parsed, Key> {
diskWrite: DiskWrite<Raw, Key>): RealStoreBuilder<Raw, Parsed, Key> = apply {
persister = object : Persister<Raw, Key> {
override suspend fun read(key: Key): Raw? =
diskRead.read(key)
@ -48,46 +46,44 @@ class RealStoreBuilder<Raw, Parsed, Key> {
override suspend fun write(key: Key, raw: Raw): Boolean =
diskWrite.write(key, raw)
}
return this
}
fun parser(parser: Parser<Raw, Parsed>): RealStoreBuilder<Raw, Parsed, Key> {
this.parser = NoKeyParser(parser)
return this
fun parser(parser: Parser<Raw, Parsed>): RealStoreBuilder<Raw, Parsed, Key> = apply {
this.parsers.clear()
this.parsers.add(NoKeyParser(parser as Parser<Any?, Any?>))
}
fun parser(parser: KeyParser<Key, Raw, Parsed>): RealStoreBuilder<Raw, Parsed, Key> {
this.parser = parser
fun parser(parser: suspend (Raw) -> Parsed): RealStoreBuilder<Raw, Parsed, Key> =
parser(NoKeyParser(object : Parser<Raw, Parsed> {
override suspend fun apply(raw: Raw): Parsed {
return parser(raw)
}
}))
return this
fun parser(parser: KeyParser<Key, Raw, Parsed>): RealStoreBuilder<Raw, Parsed, Key> = apply {
this.parsers.clear()
this.parsers.add(parser as KeyParser<Any?, Any?, Any?>)
}
fun parsers(parsers: List<Parser<Raw, Parsed>>): RealStoreBuilder<Raw, Parsed, Key> {
TODO("not implemented")
// this.parsers.clear()
// for (parser in parsers) {
// this.parsers.add(NoKeyParser<Key,Raw, Parsed>(parser))
// }
// return this
fun parsers(parsers: List<Parser<*, *>>): RealStoreBuilder<Raw, Parsed, Key> = apply {
this.parsers.clear()
this.parsers.addAll(parsers.map { NoKeyParser<Any?, Any?, Any?>(it as Parser<Any?, Any?>) })
}
fun memoryPolicy(memoryPolicy: MemoryPolicy): RealStoreBuilder<Raw, Parsed, Key> {
fun memoryPolicy(memoryPolicy: MemoryPolicy): RealStoreBuilder<Raw, Parsed, Key> = apply {
this.memoryPolicy = memoryPolicy
return this
}
//Store will backfill the disk cache anytime a record is stale
//User will still get the stale record returned to them
fun refreshOnStale(): RealStoreBuilder<Raw, Parsed, Key> {
fun refreshOnStale(): RealStoreBuilder<Raw, Parsed, Key> = apply {
stalePolicy = StalePolicy.REFRESH_ON_STALE
return this
}
//Store will try to get network source when disk data is stale
//if network source throws error or is empty, stale disk data will be returned
fun networkBeforeStale(): RealStoreBuilder<Raw, Parsed, Key> {
fun networkBeforeStale(): RealStoreBuilder<Raw, Parsed, Key> = apply {
stalePolicy = StalePolicy.NETWORK_BEFORE_STALE
return this
}
fun open(): Store<Parsed, Key> {
@ -95,15 +91,15 @@ class RealStoreBuilder<Raw, Parsed, Key> {
persister = NoopPersister.create(memoryPolicy)
}
if (parser == null) {
if (parsers.isEmpty()) {
parser(NoopParserFunc())
}
// val multiParser = MultiParser<Key, Raw, Parsed>(parsers)
val multiParser = MultiParser<Key, Raw, Parsed>(parsers)
val realInternalStore: InternalStore<Parsed, Key> = RealInternalStore(fetcher!!, persister!!, parser!!, memoryPolicy, stalePolicy)
val realInternalStore: RealInternalStore<Raw, Parsed, Key> = RealInternalStore(fetcher!!, persister!!, multiParser, memoryPolicy, stalePolicy)
return RealStore(realInternalStore)
return RealStore<Parsed, Key>(realInternalStore)
}
companion object {

View file

@ -1,9 +1,8 @@
package com.nytimes.android.external.store3.util
import io.reactivex.annotations.NonNull
import io.reactivex.functions.BiFunction
interface KeyParser<Key, Raw, Parsed> {
interface KeyParser<in Key, in Raw, out Parsed> {
@Throws(ParserException::class)
suspend fun apply(@NonNull key: Key, @NonNull raw: Raw): Parsed

View file

@ -4,7 +4,7 @@ import com.nytimes.android.external.store3.base.Parser
import io.reactivex.annotations.NonNull
class NoKeyParser<Key, Raw, Parsed>(private val parser: Parser<Raw, Parsed>) : KeyParser<Key, Raw, Parsed> {
class NoKeyParser<in Key, in Raw, out Parsed>(private val parser: Parser<Raw, out Parsed>) : KeyParser<Key, Raw, Parsed> {
@Throws(ParserException::class)
override suspend fun apply(@NonNull key: Key, @NonNull raw: Raw): Parsed {

View file

@ -4,7 +4,7 @@ import com.nytimes.android.external.store3.base.Clearable
import com.nytimes.android.external.store3.base.Persister
import com.nytimes.android.external.store3.base.impl.BarCode
class ClearingPersister : Persister<Int, BarCode>, Clearable<BarCode> {
open class ClearingPersister : Persister<Int, BarCode>, Clearable<BarCode> {
override suspend fun read(key: BarCode): Int? {
throw RuntimeException()
}

View file

@ -36,7 +36,8 @@ class DontCacheErrorsTest {
try {
store.get(barcode)
fail()
} catch (e: Exception) {
} catch (e: RuntimeException) {
e.printStackTrace()
}
shouldThrow = false

View file

@ -1,71 +0,0 @@
package com.nytimes.android.external.store3;
import com.nytimes.android.external.store3.base.Fetcher;
import com.nytimes.android.external.store3.base.Persister;
import com.nytimes.android.external.store3.base.impl.BarCode;
import com.nytimes.android.external.store3.base.impl.Store;
import com.nytimes.android.external.store3.base.impl.StoreBuilder;
import org.junit.Test;
import java.util.Date;
import javax.annotation.Nonnull;
import io.reactivex.Maybe;
import io.reactivex.Single;
import static org.assertj.core.api.Assertions.assertThat;
public class StoreBuilderTest {
public static final Date DATE = new Date();
@Test
public void testBuildersBuildWithCorrectTypes() {
//test is checking whether types are correct in builders
Store<Date, Integer> store = StoreBuilder.<Integer, String, Date>parsedWithKey()
.fetcher(key -> Single.just(String.valueOf(key)))
.persister(new Persister<String, Integer>() {
@Nonnull
@Override
public Maybe<String> read(@Nonnull Integer key) {
return Maybe.just(String.valueOf(key));
}
@Nonnull
@Override
public Single<Boolean> write(@Nonnull Integer key, @Nonnull String s) {
return Single.just(true);
}
})
.parser(s -> DATE)
.open();
Store<Date, BarCode> barCodeStore = StoreBuilder.<Date>barcode().fetcher(new Fetcher<Date, BarCode>() {
@Nonnull
@Override
public Single<Date> fetch(@Nonnull BarCode barCode) {
return Single.just(DATE);
}
}).open();
Store<Date, Integer> keyStore = StoreBuilder.<Integer, Date>key()
.fetcher(new Fetcher<Date, Integer>() {
@Nonnull
@Override
public Single<Date> fetch(@Nonnull Integer key) {
return Single.just(DATE);
}
})
.open();
Date result = store.get(5).blockingGet();
result = barCodeStore.get(new BarCode("test", "5")).blockingGet();
result = keyStore.get(5).blockingGet();
assertThat(result).isNotNull();
}
}

View file

@ -0,0 +1,47 @@
package com.nytimes.android.external.store3
import com.nytimes.android.external.store3.base.Persister
import com.nytimes.android.external.store3.base.impl.BarCode
import com.nytimes.android.external.store3.base.impl.StoreBuilder
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import java.util.*
class StoreBuilderTest {
@Test
fun testBuildersBuildWithCorrectTypes() = runBlocking<Unit> {
//test is checking whether types are correct in builders
val store = StoreBuilder.parsedWithKey<Int, String, Date>()
.fetcher { key -> key.toString() }
.persister(object : Persister<String, Int> {
override suspend fun read(key: Int): String? {
return key.toString()
}
override suspend fun write(key: Int, raw: String) = true
})
.parser { DATE }
.open()
val barCodeStore = StoreBuilder.barcode<Date>().fetcher { DATE }.open()
val keyStore = StoreBuilder.key<Int, Date>()
.fetcher { DATE }
.open()
var result = store.get(5)
result = barCodeStore.get(BarCode("test", "5"))
result = keyStore.get(5)
assertThat(result).isNotNull()
}
companion object {
val DATE = Date()
}
}

View file

@ -1,13 +1,13 @@
package com.nytimes.android.external.store3
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.whenever
import com.nytimes.android.external.cache3.CacheBuilder
import com.nytimes.android.external.store3.base.Fetcher
import com.nytimes.android.external.store3.base.Persister
import com.nytimes.android.external.store3.base.impl.BarCode
import com.nytimes.android.external.store3.base.impl.StoreBuilder
import com.nytimes.android.external.store3.util.NoopPersister
import io.reactivex.Maybe
import io.reactivex.Single
import kotlinx.coroutines.async
import kotlinx.coroutines.runBlocking
@ -32,15 +32,15 @@ class StoreTest {
.open()
`when`<Any>(fetcher.fetch(barCode))
.thenReturn(Single.just(NETWORK))
whenever(fetcher.fetch(barCode))
.thenReturn(NETWORK)
`when`<Any>(persister.read(barCode))
.thenReturn(Maybe.empty<String>())
.thenReturn(Maybe.just(DISK))
whenever(persister.read(barCode))
.thenReturn(null)
.thenReturn(DISK)
`when`<Any>(persister.write(barCode, NETWORK))
.thenReturn(Single.just(true))
whenever(persister.write(barCode, NETWORK))
.thenReturn(true)
var value = simpleStore.get(barCode)
@ -59,15 +59,15 @@ class StoreTest {
// .open()
//
//
// `when`<Any>(fetcher.fetch(barCode))
// .thenReturn(Single.just(NETWORK))
// whenever(fetcher.fetch(barCode))
// .thenReturn(NETWORK)
//
// `when`<Any>(persister.read(barCode))
// .thenReturn(Maybe.empty<String>())
// .thenReturn(Maybe.just(DISK))
// whenever(persister.read(barCode))
// .thenReturn(null)
// .thenReturn(DISK)
//
// `when`<Any>(persister.write(barCode, NETWORK))
// .thenReturn(Single.just(true))
// whenever(persister.write(barCode, NETWORK))
// .thenReturn(true)
//
// var result = simpleStore.getWithResult(barCode)
//
@ -90,23 +90,24 @@ class StoreTest {
.open()
val networkSingle = Single.create<String> { emitter ->
if (counter.incrementAndGet() == 1) {
emitter.onSuccess(NETWORK)
} else {
emitter.onError(RuntimeException("Yo Dawg your inflight is broken"))
}
}
`when`<Any>(fetcher.fetch(barCode))
.thenReturn(networkSingle)
whenever(fetcher.fetch(barCode))
.thenAnswer {
if (counter.incrementAndGet() == 1) {
NETWORK
} else {
throw RuntimeException("Yo Dawg your inflight is broken")
}
}
`when`<Any>(persister.read(barCode))
.thenReturn(Maybe.empty<String>())
.thenReturn(Maybe.just(DISK))
whenever(persister.read(barCode))
.thenReturn(null)
.thenReturn(DISK)
`when`<Any>(persister.write(barCode, NETWORK))
.thenReturn(Single.just(true))
whenever(persister.write(barCode, NETWORK))
.thenReturn(true)
val deferred = async { simpleStore.get(barCode) }
@ -133,15 +134,15 @@ class StoreTest {
// }
//
//
// `when`<Any>(fetcher.fetch(barCode))
// whenever(fetcher.fetch(barCode))
// .thenReturn(networkSingle)
//
// `when`<Any>(persister.read(barCode))
// .thenReturn(Maybe.empty<String>())
// .thenReturn(Maybe.just(DISK))
// whenever(persister.read(barCode))
// .thenReturn(null)
// .thenReturn(DISK)
//
// `when`<Any>(persister.write(barCode, NETWORK))
// .thenReturn(Single.just(true))
// whenever(persister.write(barCode, NETWORK))
// .thenReturn(true)
//
//
// val response = simpleStore.getWithResult(barCode)
@ -160,13 +161,13 @@ class StoreTest {
val simpleStore = SampleStore(fetcher, persister)
simpleStore.clear()
`when`<Any>(fetcher.fetch(barCode))
.thenReturn(Single.just(NETWORK))
whenever(fetcher.fetch(barCode))
.thenReturn(NETWORK)
`when`<Any>(persister.read(barCode))
.thenReturn(Maybe.empty<String>())
.thenReturn(Maybe.just(DISK))
`when`<Any>(persister.write(barCode, NETWORK)).thenReturn(Single.just(true))
whenever(persister.read(barCode))
.thenReturn(null)
.thenReturn(DISK)
whenever(persister.write(barCode, NETWORK)).thenReturn(true)
var value = simpleStore.get(barCode)
assertThat(value).isEqualTo(DISK)
@ -181,13 +182,13 @@ class StoreTest {
// val simpleStore = SampleStore(fetcher, persister)
// simpleStore.clear()
//
// `when`<Any>(fetcher.fetch(barCode))
// .thenReturn(Single.just(NETWORK))
// whenever(fetcher.fetch(barCode))
// .thenReturn(NETWORK)
//
// `when`<Any>(persister.read(barCode))
// .thenReturn(Maybe.empty<String>())
// .thenReturn(Maybe.just(DISK))
// `when`<Any>(persister.write(barCode, NETWORK)).thenReturn(Single.just(true))
// whenever(persister.read(barCode))
// .thenReturn(null)
// .thenReturn(DISK)
// whenever(persister.write(barCode, NETWORK)).thenReturn(true)
//
// var result = simpleStore.getWithResult(barCode)
//
@ -207,8 +208,8 @@ class StoreTest {
val simpleStore = SampleStore(fetcher, persister)
`when`<Any>(fetcher.fetch(barCode))
.thenReturn(Single.just(NETWORK))
whenever(fetcher.fetch(barCode))
.thenReturn(NETWORK)
var value = simpleStore.get(barCode)
verify<Fetcher<String, BarCode>>(fetcher, times(1)).fetch(barCode)
@ -232,8 +233,8 @@ class StoreTest {
// val simpleStore = SampleStore(fetcher, persister)
//
//
// `when`<Any>(fetcher.fetch(barCode))
// .thenReturn(Single.just(NETWORK))
// whenever(fetcher.fetch(barCode))
// .thenReturn(NETWORK)
//
// var value = simpleStore.getWithResult(barCode)
// verify<Fetcher<String, BarCode>>(fetcher, times(1)).fetch(barCode)

View file

@ -1,6 +1,6 @@
package com.nytimes.android.external.store3
import com.nytimes.android.external.store.util.Result
import com.nhaarman.mockitokotlin2.mock
import com.nytimes.android.external.store3.base.Fetcher
import com.nytimes.android.external.store3.base.Parser
import com.nytimes.android.external.store3.base.Persister
@ -8,141 +8,128 @@ import com.nytimes.android.external.store3.base.impl.BarCode
import com.nytimes.android.external.store3.base.impl.ParsingStoreBuilder
import io.reactivex.Maybe
import io.reactivex.Single
import kotlinx.coroutines.runBlocking
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
class StoreWithParserTest {
@Mock
internal var fetcher: Fetcher<String, BarCode>? = null
@Mock
internal var persister: Persister<String, BarCode>? = null
@Mock
internal var parser: Parser<String, String>? = null
val fetcher: Fetcher<String, BarCode> = mock()
val persister: Persister<String, BarCode> = mock()
val parser: Parser<String, String> = mock()
private val barCode = BarCode("key", "value")
@Test
@Throws(Exception::class)
fun testSimple() {
MockitoAnnotations.initMocks(this)
fun testSimple() = runBlocking<Unit> {
val simpleStore = ParsingStoreBuilder.builder<String, String>()
.persister(persister!!)
.fetcher(fetcher!!)
.parser(parser!!)
.persister(persister)
.fetcher(fetcher)
.parser(parser)
.open()
`when`<Any>(fetcher!!.fetch(barCode))
`when`<Any>(fetcher.fetch(barCode))
.thenReturn(Single.just(NETWORK))
`when`<Any>(persister!!.read(barCode))
`when`<Any>(persister.read(barCode))
.thenReturn(Maybe.empty<String>())
.thenReturn(Maybe.just(DISK))
`when`<Any>(persister!!.write(barCode, NETWORK))
`when`<Any>(persister.write(barCode, NETWORK))
.thenReturn(Single.just(true))
`when`<Any>(parser!!.apply(DISK)).thenReturn(barCode.key)
`when`<Any>(parser.apply(DISK)).thenReturn(barCode.key)
var value = simpleStore.get(barCode).blockingGet()
var value = simpleStore.get(barCode)
assertThat(value).isEqualTo(barCode.key)
value = simpleStore.get(barCode).blockingGet()
value = simpleStore.get(barCode)
assertThat(value).isEqualTo(barCode.key)
verify<Fetcher<String, BarCode>>(fetcher, times(1)).fetch(barCode)
}
// @Test
// fun testSimpleWithResult() = runBlocking<Unit> {
// val simpleStore = ParsingStoreBuilder.builder<String, String>()
// .persister(persister)
// .fetcher(fetcher)
// .parser(parser)
// .open()
//
// `when`<Any>(fetcher.fetch(barCode))
// .thenReturn(Single.just(NETWORK))
//
// `when`<Any>(persister.read(barCode))
// .thenReturn(Maybe.empty<String>())
// .thenReturn(Maybe.just(DISK))
//
// `when`<Any>(persister.write(barCode, NETWORK))
// .thenReturn(Single.just(true))
//
// `when`<Any>(parser.apply(DISK)).thenReturn(barCode.key)
//
// var result = simpleStore.getWithResult(barCode)
// assertThat(result.source()).isEqualTo(Result.Source.NETWORK)
// assertThat(result.value()).isEqualTo(barCode.key)
//
// result = simpleStore.getWithResult(barCode)
// assertThat(result.source()).isEqualTo(Result.Source.CACHE)
// assertThat(result.value()).isEqualTo(barCode.key)
// verify<Fetcher<String, BarCode>>(fetcher, times(1)).fetch(barCode)
// }
@Test
@Throws(Exception::class)
fun testSimpleWithResult() {
fun testSubclass() = runBlocking<Unit> {
MockitoAnnotations.initMocks(this)
val simpleStore = SampleParsingStore(fetcher, persister, parser)
val simpleStore = ParsingStoreBuilder.builder<String, String>()
.persister(persister!!)
.fetcher(fetcher!!)
.parser(parser!!)
.open()
`when`<Any>(fetcher!!.fetch(barCode))
`when`<Any>(fetcher.fetch(barCode))
.thenReturn(Single.just(NETWORK))
`when`<Any>(persister!!.read(barCode))
`when`<Any>(persister.read(barCode))
.thenReturn(Maybe.empty<String>())
.thenReturn(Maybe.just(DISK))
`when`<Any>(persister!!.write(barCode, NETWORK))
`when`<Any>(persister.write(barCode, NETWORK))
.thenReturn(Single.just(true))
`when`<Any>(parser!!.apply(DISK)).thenReturn(barCode.key)
`when`<Any>(parser.apply(DISK)).thenReturn(barCode.key)
var result = simpleStore.getWithResult(barCode).blockingGet()
assertThat<Source>(result.source()).isEqualTo(Result.Source.NETWORK)
assertThat(result.value()).isEqualTo(barCode.key)
result = simpleStore.getWithResult(barCode).blockingGet()
assertThat<Source>(result.source()).isEqualTo(Result.Source.CACHE)
assertThat(result.value()).isEqualTo(barCode.key)
verify<Fetcher<String, BarCode>>(fetcher, times(1)).fetch(barCode)
}
@Test
@Throws(Exception::class)
fun testSubclass() {
MockitoAnnotations.initMocks(this)
val simpleStore = SampleParsingStore(fetcher!!, persister!!, parser!!)
`when`<Any>(fetcher!!.fetch(barCode))
.thenReturn(Single.just(NETWORK))
`when`<Any>(persister!!.read(barCode))
.thenReturn(Maybe.empty<String>())
.thenReturn(Maybe.just(DISK))
`when`<Any>(persister!!.write(barCode, NETWORK))
.thenReturn(Single.just(true))
`when`<Any>(parser!!.apply(DISK)).thenReturn(barCode.key)
var value = simpleStore.get(barCode).blockingGet()
var value = simpleStore.get(barCode)
assertThat(value).isEqualTo(barCode.key)
value = simpleStore.get(barCode).blockingGet()
value = simpleStore.get(barCode)
assertThat(value).isEqualTo(barCode.key)
verify<Fetcher<String, BarCode>>(fetcher, times(1)).fetch(barCode)
}
@Test
@Throws(Exception::class)
fun testSubclassWithResult() {
MockitoAnnotations.initMocks(this)
val simpleStore = SampleParsingStore(fetcher!!, persister!!, parser!!)
`when`<Any>(fetcher!!.fetch(barCode))
.thenReturn(Single.just(NETWORK))
`when`<Any>(persister!!.read(barCode))
.thenReturn(Maybe.empty<String>())
.thenReturn(Maybe.just(DISK))
`when`<Any>(persister!!.write(barCode, NETWORK))
.thenReturn(Single.just(true))
`when`<Any>(parser!!.apply(DISK)).thenReturn(barCode.key)
var result = simpleStore.getWithResult(barCode).blockingGet()
assertThat<Source>(result.source()).isEqualTo(Result.Source.NETWORK)
assertThat(result.value()).isEqualTo(barCode.key)
result = simpleStore.getWithResult(barCode).blockingGet()
assertThat<Source>(result.source()).isEqualTo(Result.Source.CACHE)
assertThat(result.value()).isEqualTo(barCode.key)
verify<Fetcher<String, BarCode>>(fetcher, times(1)).fetch(barCode)
}
// @Test
// fun testSubclassWithResult() = runBlocking<Unit> {
// MockitoAnnotations.initMocks(this)
//
// val simpleStore = SampleParsingStore(fetcher, persister, parser)
//
// `when`<Any>(fetcher.fetch(barCode))
// .thenReturn(Single.just(NETWORK))
//
// `when`<Any>(persister.read(barCode))
// .thenReturn(Maybe.empty<String>())
// .thenReturn(Maybe.just(DISK))
//
// `when`<Any>(persister.write(barCode, NETWORK))
// .thenReturn(Single.just(true))
//
// `when`<Any>(parser.apply(DISK)).thenReturn(barCode.key)
//
// var result = simpleStore.getWithResult(barCode)
// assertThat(result.source()).isEqualTo(Result.Source.NETWORK)
// assertThat(result.value()).isEqualTo(barCode.key)
//
// result = simpleStore.getWithResult(barCode)
// assertThat(result.source()).isEqualTo(Result.Source.CACHE)
// assertThat(result.value()).isEqualTo(barCode.key)
// verify<Fetcher<String, BarCode>>(fetcher, times(1)).fetch(barCode)
// }
companion object {

View file

@ -1,85 +0,0 @@
package com.nytimes.android.external.store3;
import com.nytimes.android.external.store3.base.Fetcher;
import com.nytimes.android.external.store3.base.Persister;
import com.nytimes.android.external.store3.base.impl.BarCode;
import com.nytimes.android.external.store3.base.impl.Store;
import com.nytimes.android.external.store3.base.impl.StoreBuilder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.runners.MockitoJUnitRunner;
import io.reactivex.Maybe;
import io.reactivex.Single;
import io.reactivex.observers.TestObserver;
import static org.mockito.Mockito.when;
@RunWith(MockitoJUnitRunner.class)
public class StreamOneKeyTest {
private static final String TEST_ITEM = "test";
private static final String TEST_ITEM2 = "test2";
@Mock
Fetcher<String, BarCode> fetcher;
@Mock
Persister<String, BarCode> persister;
private final BarCode barCode = new BarCode("key", "value");
private final BarCode barCode2 = new BarCode("key2", "value2");
private Store<String, BarCode> store;
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
store = StoreBuilder.<String>barcode()
.persister(persister)
.fetcher(fetcher)
.open();
when(fetcher.fetch(barCode))
.thenReturn(Single.just(TEST_ITEM))
.thenReturn(Single.just(TEST_ITEM2));
when(persister.read(barCode))
.thenReturn(Maybe.<String>empty())
.thenReturn(Maybe.just(TEST_ITEM))
.thenReturn(Maybe.just(TEST_ITEM2));
when(persister.write(barCode, TEST_ITEM))
.thenReturn(Single.just(true));
when(persister.write(barCode, TEST_ITEM2))
.thenReturn(Single.just(true));
}
@Test
public void testStream() {
TestObserver<String> streamObservable = store.stream(barCode).test();
//first time we subscribe to stream it will fail getting from memory & disk and instead
//fresh from network, write to disk and notifiy subscribers
streamObservable.assertValueCount(1);
store.clear();
//fresh should notify subscribers again
store.fresh(barCode).test().awaitCount(1);
streamObservable.assertValues(TEST_ITEM, TEST_ITEM2);
//get for another barcode should not trigger a stream for barcode1
when(fetcher.fetch(barCode2))
.thenReturn(Single.just(TEST_ITEM));
when(persister.read(barCode2))
.thenReturn(Maybe.empty())
.thenReturn(Maybe.just(TEST_ITEM));
when(persister.write(barCode2, TEST_ITEM))
.thenReturn(Single.just(true));
store.get(barCode2).test().awaitCount(1);
streamObservable.assertValueCount(2);
}
}

View file

@ -0,0 +1,75 @@
package com.nytimes.android.external.store3
import com.nhaarman.mockitokotlin2.mock
import com.nytimes.android.external.store3.base.Fetcher
import com.nytimes.android.external.store3.base.Persister
import com.nytimes.android.external.store3.base.impl.BarCode
import com.nytimes.android.external.store3.base.impl.Store
import com.nytimes.android.external.store3.base.impl.StoreBuilder
import io.reactivex.Maybe
import io.reactivex.Single
import kotlinx.coroutines.runBlocking
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.`when`
class StreamOneKeyTest {
val fetcher: Fetcher<String, BarCode> = mock()
val persister: Persister<String, BarCode> = mock()
private val barCode = BarCode("key", "value")
private val barCode2 = BarCode("key2", "value2")
private val store: Store<String, BarCode> = StoreBuilder.barcode<String>()
.persister(persister)
.fetcher(fetcher)
.open()
@Before
fun setUp() = runBlocking<Unit> {
`when`<Any>(fetcher.fetch(barCode))
.thenReturn(Single.just(TEST_ITEM))
.thenReturn(Single.just(TEST_ITEM2))
`when`<Any>(persister.read(barCode))
.thenReturn(Maybe.empty<String>())
.thenReturn(Maybe.just(TEST_ITEM))
.thenReturn(Maybe.just(TEST_ITEM2))
`when`<Any>(persister.write(barCode, TEST_ITEM))
.thenReturn(Single.just(true))
`when`<Any>(persister.write(barCode, TEST_ITEM2))
.thenReturn(Single.just(true))
}
@Test
fun testStream() = runBlocking<Unit> {
val streamObservable = store.stream(barCode).test()
//first time we subscribe to stream it will fail getting from memory & disk and instead
//fresh from network, write to disk and notifiy subscribers
streamObservable.assertValueCount(1)
store.clear()
//fresh should notify subscribers again
store.fresh(barCode)
streamObservable.assertValues(TEST_ITEM, TEST_ITEM2)
//get for another barcode should not trigger a stream for barcode1
`when`<Any>(fetcher.fetch(barCode2))
.thenReturn(Single.just(TEST_ITEM))
`when`<Any>(persister.read(barCode2))
.thenReturn(Maybe.empty<Any>())
.thenReturn(Maybe.just(TEST_ITEM))
`when`<Any>(persister.write(barCode2, TEST_ITEM))
.thenReturn(Single.just(true))
store.get(barCode2)
streamObservable.assertValueCount(2)
}
companion object {
private val TEST_ITEM = "test"
private val TEST_ITEM2 = "test2"
}
}

View file

@ -1,5 +1,6 @@
package com.nytimes.android.external.store3.room
import com.nhaarman.mockitokotlin2.mock
import com.nytimes.android.external.store3.base.Clearable
import com.nytimes.android.external.store3.base.impl.BarCode
import com.nytimes.android.external.store3.base.impl.StalePolicy
@ -8,16 +9,14 @@ import com.nytimes.android.external.store3.base.room.RoomPersister
import io.reactivex.Observable
import org.assertj.core.api.Assertions.assertThat
import org.junit.Test
import org.mockito.Mock
import org.mockito.Mockito.`when`
import org.mockito.Mockito.verify
import java.util.concurrent.atomic.AtomicInteger
class ClearStoreRoomTest {
@Mock
internal var persister: RoomClearingPersister? = null
private val persister: RoomClearingPersister = mock()
private val networkCalls: AtomicInteger = AtomicInteger(0)
private var store = StoreRoom.from({ Observable.fromCallable { networkCalls.incrementAndGet() } },
private val store = StoreRoom.from({ Observable.fromCallable { networkCalls.incrementAndGet() } },
persister,
StalePolicy.UNSPECIFIED)
@ -26,18 +25,18 @@ class ClearStoreRoomTest {
// one request should produce one call
val barcode = BarCode("type", "key")
`when`(persister!!.read(barcode))
`when`(persister.read(barcode))
.thenReturn(Observable.empty()) //read from disk on get
.thenReturn(Observable.just(1)) //read from disk after fetching from network
.thenReturn(Observable.empty()) //read from disk after clearing
.thenReturn(Observable.just(1)) //read from disk after making additional network call
store!!.get(barcode).test().awaitTerminalEvent()
store.get(barcode).test().awaitTerminalEvent()
assertThat(networkCalls.toInt()).isEqualTo(1)
// after clearing the memory another call should be made
store!!.clear(barcode)
store!!.get(barcode).test().awaitTerminalEvent()
store.clear(barcode)
store.get(barcode).test().awaitTerminalEvent()
verify<RoomClearingPersister>(persister).clear(barcode)
assertThat(networkCalls.toInt()).isEqualTo(2)
}
@ -47,13 +46,13 @@ class ClearStoreRoomTest {
val barcode1 = BarCode("type1", "key1")
val barcode2 = BarCode("type2", "key2")
`when`(persister!!.read(barcode1))
`when`(persister.read(barcode1))
.thenReturn(Observable.empty()) //read from disk
.thenReturn(Observable.just(1)) //read from disk after fetching from network
.thenReturn(Observable.empty()) //read from disk after clearing disk cache
.thenReturn(Observable.just(1)) //read from disk after making additional network call
`when`(persister!!.read(barcode2))
`when`(persister.read(barcode2))
.thenReturn(Observable.empty()) //read from disk
.thenReturn(Observable.just(1)) //read from disk after fetching from network
.thenReturn(Observable.empty()) //read from disk after clearing disk cache
@ -61,20 +60,20 @@ class ClearStoreRoomTest {
// each request should produce one call
store!!.get(barcode1).test().awaitTerminalEvent()
store!!.get(barcode2).test().awaitTerminalEvent()
store.get(barcode1).test().awaitTerminalEvent()
store.get(barcode2).test().awaitTerminalEvent()
assertThat(networkCalls.toInt()).isEqualTo(2)
store!!.clear()
store.clear()
// after everything is cleared each request should produce another 2 calls
store!!.get(barcode1).test().awaitTerminalEvent()
store!!.get(barcode2).test().awaitTerminalEvent()
store.get(barcode1).test().awaitTerminalEvent()
store.get(barcode2).test().awaitTerminalEvent()
assertThat(networkCalls.toInt()).isEqualTo(4)
}
//everything will be mocked
internal class RoomClearingPersister : RoomPersister<Int, Int, BarCode>, Clearable<BarCode> {
internal open class RoomClearingPersister : RoomPersister<Int, Int, BarCode>, Clearable<BarCode> {
override fun clear(key: BarCode) {
throw RuntimeException()
}

View file

@ -11,7 +11,7 @@ import java.util.concurrent.TimeUnit
class NoopPersisterTest {
@Rule
@get:Rule
var exception = ExpectedException.none()
@Test