From 9db2456118b0f028890185762bac374e1024d874 Mon Sep 17 00:00:00 2001 From: Maksim Moiseikin Date: Sun, 15 Jan 2017 19:37:16 +0100 Subject: [PATCH] Create Jackson Middleware (#60) * Create Jackson string parser * Create Jackson BufferedSource parser * Create Jackson Reader parser --- buildsystem/dependencies.gradle | 3 + middleware-jackson/.gitignore | 1 + middleware-jackson/build.gradle | 55 +++++++ middleware-jackson/gradle.properties | 3 + .../src/main/AndroidManifest.xml | 11 ++ .../jackson/JacksonParserFactory.java | 119 +++++++++++++++ .../jackson/JacksonReaderParser.java | 41 ++++++ .../jackson/JacksonSourceParser.java | 54 +++++++ .../jackson/JacksonStringParser.java | 42 ++++++ .../src/main/res/values/strings.xml | 3 + .../jackson/JacksonReaderParserStoreTest.java | 131 +++++++++++++++++ .../jackson/JacksonSourceParserStoreTest.java | 139 ++++++++++++++++++ .../jackson/JacksonStringParserStoreTest.java | 126 ++++++++++++++++ .../store/middleware/jackson/data/Bar.java | 12 ++ .../store/middleware/jackson/data/Foo.java | 18 +++ settings.gradle | 2 +- 16 files changed, 759 insertions(+), 1 deletion(-) create mode 100644 middleware-jackson/.gitignore create mode 100644 middleware-jackson/build.gradle create mode 100644 middleware-jackson/gradle.properties create mode 100644 middleware-jackson/src/main/AndroidManifest.xml create mode 100644 middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonParserFactory.java create mode 100644 middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonReaderParser.java create mode 100644 middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonSourceParser.java create mode 100644 middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonStringParser.java create mode 100644 middleware-jackson/src/main/res/values/strings.xml create mode 100644 middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonReaderParserStoreTest.java create mode 100644 middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonSourceParserStoreTest.java create mode 100644 middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonStringParserStoreTest.java create mode 100644 middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/data/Bar.java create mode 100644 middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/data/Foo.java diff --git a/buildsystem/dependencies.gradle b/buildsystem/dependencies.gradle index 74a46be..5c774b9 100644 --- a/buildsystem/dependencies.gradle +++ b/buildsystem/dependencies.gradle @@ -42,6 +42,7 @@ ext.versions = [ googlePlayServices : '9.6.1', gson : '2.6.2', moshi : '1.3.1', + jackson : '2.8.6', guava : '19.0', javapoet : '1.7.0', immutables : '2.2.1', @@ -99,6 +100,8 @@ ext.libraries = [ okio : "com.squareup.okio:okio:$versions.okio", gson : "com.google.code.gson:gson:$versions.gson", moshi : "com.squareup.moshi:moshi:$versions.moshi", + jacksonCore : "com.fasterxml.jackson.core:jackson-core:$versions.jackson", + jacksonDatabind : "com.fasterxml.jackson.core:jackson-databind:$versions.jackson", guava : "com.google.guava:guava:$versions.guava", googlePlayServices : "com.google.android.gms:play-services-base:$versions.googlePlayServices", googleMessagingCloud : "com.google.android.gms:play-services-gcm:$versions.googlePlayServices", diff --git a/middleware-jackson/.gitignore b/middleware-jackson/.gitignore new file mode 100644 index 0000000..796b96d --- /dev/null +++ b/middleware-jackson/.gitignore @@ -0,0 +1 @@ +/build diff --git a/middleware-jackson/build.gradle b/middleware-jackson/build.gradle new file mode 100644 index 0000000..dade140 --- /dev/null +++ b/middleware-jackson/build.gradle @@ -0,0 +1,55 @@ +apply plugin: 'com.android.library' +apply plugin: 'com.getkeepsafe.dexcount' + +group = GROUP +version = VERSION_NAME + +android { + compileSdkVersion versions.compileSdk + buildToolsVersion versions.buildTools + + defaultConfig { + minSdkVersion versions.minSdk + targetSdkVersion versions.targetSdk + } + + buildTypes { + debug { + minifyEnabled false + zipAlignEnabled false + } + } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_7 + targetCompatibility JavaVersion.VERSION_1_7 + } + + lintOptions { + abortOnError true + disable 'InvalidPackage' + } +} + +dependencies { + compile project(path: ':store') + compile libraries.jacksonCore + compile libraries.jacksonDatabind + compile libraries.rxJava + compile libraries.okio + compile libraries.dagger + compile libraries.supportAnnotations + testCompile libraries.mockito + testCompile libraries.junit +} + +task sourcesJar(type: Jar) { + from android.sourceSets.main.java.srcDirs + classifier = 'sources' +} + +artifacts { + archives sourcesJar +} +apply from: rootProject.file("gradle/maven-push.gradle") +apply from: rootProject.file("gradle/checkstyle.gradle") +apply from: rootProject.file("gradle/pmd.gradle") diff --git a/middleware-jackson/gradle.properties b/middleware-jackson/gradle.properties new file mode 100644 index 0000000..f80d5ec --- /dev/null +++ b/middleware-jackson/gradle.properties @@ -0,0 +1,3 @@ +POM_NAME=com.nytimes.android +POM_ARTIFACT_ID=middleware-jackson +POM_PACKAGING=aar diff --git a/middleware-jackson/src/main/AndroidManifest.xml b/middleware-jackson/src/main/AndroidManifest.xml new file mode 100644 index 0000000..f2de38a --- /dev/null +++ b/middleware-jackson/src/main/AndroidManifest.xml @@ -0,0 +1,11 @@ + + + + + + + diff --git a/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonParserFactory.java b/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonParserFactory.java new file mode 100644 index 0000000..7697ad6 --- /dev/null +++ b/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonParserFactory.java @@ -0,0 +1,119 @@ +package com.nytimes.android.external.store.middleware.jackson; + +import android.support.annotation.NonNull; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nytimes.android.external.cache.Preconditions; +import com.nytimes.android.external.store.base.Parser; + +import java.io.Reader; +import java.lang.reflect.Type; + +import okio.BufferedSource; + +/** + * Factory which returns various Jackson {@link Parser} implementations. + */ +public final class JacksonParserFactory { + + private JacksonParserFactory() { + } + + /** + * Returns a new Parser which parses from a String to the specified type, using + * the provided {@link JsonFactory} instance. + */ + @NonNull + public static Parser createStringParser(@NonNull JsonFactory jsonFactory, @NonNull Type type) { + Preconditions.checkNotNull(jsonFactory, "jsonFactory cannot be null."); + Preconditions.checkNotNull(type, "type cannot be null."); + return new JacksonStringParser<>(jsonFactory, type); + } + + /** + * Returns a new Parser which parses from a String to the specified type, using + * the provided {@link ObjectMapper} instance. + */ + @NonNull + public static Parser createStringParser(@NonNull ObjectMapper objectMapper, @NonNull Type type) { + Preconditions.checkNotNull(objectMapper, "objectMapper cannot be null."); + Preconditions.checkNotNull(type, "type cannot be null."); + return new JacksonStringParser<>(objectMapper, type); + } + + /** + * Returns a new Parser which parses from a String to the specified type, using + * a new default {@link ObjectMapper} instance. + */ + @NonNull + public static Parser createStringParser(@NonNull Class type) { + return createStringParser(new ObjectMapper(), type); + } + + /** + * Returns a new Parser which parses from {@link BufferedSource} to the specified type, using + * the provided {@link JsonFactory} instance. + */ + @NonNull + public static Parser createSourceParser(@NonNull JsonFactory jsonFactory, + @NonNull Type type) { + Preconditions.checkNotNull(jsonFactory, "jsonFactory cannot be null."); + Preconditions.checkNotNull(type, "type cannot be null."); + return new JacksonSourceParser(jsonFactory, type); + } + + /** + * Returns a new Parser which parses from {@link BufferedSource} to the specified type, using + * the provided {@link ObjectMapper} instance. + */ + @NonNull + public static Parser createSourceParser(@NonNull ObjectMapper objectMapper, + @NonNull Type type) { + Preconditions.checkNotNull(objectMapper, "objectMapper cannot be null."); + Preconditions.checkNotNull(type, "type cannot be null."); + return new JacksonSourceParser(objectMapper, type); + } + + /** + * Returns a new Parser which parses from {@link BufferedSource} to the specified type, using + * a new default configured {@link ObjectMapper} instance. + */ + @NonNull + public static Parser createSourceParser(@NonNull Type type) { + return createSourceParser(new ObjectMapper(), type); + } + + /** + * Returns a new Parser which parses from {@link Reader} to the specified type, using + * the provided {@link JsonFactory} instance. + */ + @NonNull + public static Parser createReaderParser(@NonNull JsonFactory jsonFactory, + @NonNull Type type) { + Preconditions.checkNotNull(jsonFactory, "jsonFactory cannot be null."); + Preconditions.checkNotNull(type, "type cannot be null."); + return new JacksonReaderParser<>(jsonFactory, type); + } + + /** + * Returns a new Parser which parses from {@link Reader} to the specified type, using + * the provided {@link ObjectMapper} instance. + */ + @NonNull + public static Parser createReaderParser(@NonNull ObjectMapper objectMapper, + @NonNull Type type) { + Preconditions.checkNotNull(objectMapper, "objectMapper cannot be null."); + Preconditions.checkNotNull(type, "type cannot be null."); + return new JacksonReaderParser(objectMapper, type); + } + + /** + * Returns a new Parser which parses from {@link Reader} to the specified type, using + * a new default configured {@link ObjectMapper} instance. + */ + @NonNull + public static Parser createReaderParser(@NonNull Type type) { + return createReaderParser(new ObjectMapper(), type); + } +} diff --git a/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonReaderParser.java b/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonReaderParser.java new file mode 100644 index 0000000..568f3a4 --- /dev/null +++ b/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonReaderParser.java @@ -0,0 +1,41 @@ +package com.nytimes.android.external.store.middleware.jackson; + +import android.support.annotation.NonNull; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nytimes.android.external.store.base.Parser; + +import java.io.IOException; +import java.io.Reader; +import java.lang.reflect.Type; + +import javax.inject.Inject; + +public class JacksonReaderParser implements Parser { + + private final ObjectMapper objectMapper; + private final JavaType parsedType; + + @Inject + public JacksonReaderParser(@NonNull JsonFactory jsonFactory, @NonNull Type type) { + objectMapper = new ObjectMapper(jsonFactory); + parsedType = objectMapper.constructType(type); + } + + @Inject + public JacksonReaderParser(@NonNull ObjectMapper objectMapper, @NonNull Type type) { + this.objectMapper = objectMapper; + parsedType = objectMapper.constructType(type); + } + + @Override + public Parsed call(@NonNull Reader reader) { + try { + return objectMapper.readValue(reader, parsedType); + } catch (IOException e) { + return null; + } + } +} diff --git a/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonSourceParser.java b/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonSourceParser.java new file mode 100644 index 0000000..eda9153 --- /dev/null +++ b/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonSourceParser.java @@ -0,0 +1,54 @@ +package com.nytimes.android.external.store.middleware.jackson; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nytimes.android.external.store.base.Parser; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Type; + +import javax.inject.Inject; + +import okio.BufferedSource; + +public class JacksonSourceParser implements Parser { + + private final ObjectMapper objectMapper; + private final JavaType parsedType; + + @Inject + public JacksonSourceParser(@NonNull JsonFactory jsonFactory, @NonNull Type type) { + objectMapper = new ObjectMapper(jsonFactory); + parsedType = objectMapper.constructType(type); + } + + @Inject + public JacksonSourceParser(@NonNull ObjectMapper objectMapper, @NonNull Type type) { + this.objectMapper = objectMapper; + parsedType = objectMapper.constructType(type); + } + + @Override + @Nullable + @SuppressWarnings("PMD.EmptyCatchBlock") + public Parsed call(@NonNull BufferedSource source) { + InputStream inputStream = source.inputStream(); + try { + return objectMapper.readValue(inputStream, parsedType); + } catch (IOException e) { + return null; + } finally { + try { + if (inputStream != null) { + inputStream.close(); + } + } catch (IOException e) { + } + } + } +} diff --git a/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonStringParser.java b/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonStringParser.java new file mode 100644 index 0000000..590a9d5 --- /dev/null +++ b/middleware-jackson/src/main/java/com/nytimes/android/external/store/middleware/jackson/JacksonStringParser.java @@ -0,0 +1,42 @@ +package com.nytimes.android.external.store.middleware.jackson; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nytimes.android.external.store.base.Parser; + +import java.io.IOException; +import java.lang.reflect.Type; + +import javax.inject.Inject; + +public class JacksonStringParser implements Parser { + + private final ObjectMapper objectMapper; + private final JavaType parsedType; + + @Inject + public JacksonStringParser(@NonNull JsonFactory jsonFactory, @NonNull Type type) { + objectMapper = new ObjectMapper(jsonFactory); + parsedType = objectMapper.constructType(type); + } + + @Inject + public JacksonStringParser(@NonNull ObjectMapper objectMapper, @NonNull Type type) { + this.objectMapper = objectMapper; + parsedType = objectMapper.constructType(type); + } + + @Override + @Nullable + public Parsed call(@NonNull String source) { + try { + return objectMapper.readValue(source, parsedType); + } catch (IOException e) { + return null; + } + } +} diff --git a/middleware-jackson/src/main/res/values/strings.xml b/middleware-jackson/src/main/res/values/strings.xml new file mode 100644 index 0000000..49aecc3 --- /dev/null +++ b/middleware-jackson/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + middleware-jackson + diff --git a/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonReaderParserStoreTest.java b/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonReaderParserStoreTest.java new file mode 100644 index 0000000..f2e2b0e --- /dev/null +++ b/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonReaderParserStoreTest.java @@ -0,0 +1,131 @@ +package com.nytimes.android.external.store.middleware.jackson; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nytimes.android.external.store.base.Fetcher; +import com.nytimes.android.external.store.base.Parser; +import com.nytimes.android.external.store.base.Persister; +import com.nytimes.android.external.store.base.Store; +import com.nytimes.android.external.store.base.impl.BarCode; +import com.nytimes.android.external.store.base.impl.ParsingStoreBuilder; +import com.nytimes.android.external.store.middleware.jackson.data.Foo; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.Reader; +import java.io.StringReader; + +import rx.Observable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class JacksonReaderParserStoreTest { + + private static final String KEY = "key"; + private static final String sourceString = + "{\"number\":123,\"string\":\"abc\",\"bars\":[{\"string\":\"def\"},{\"string\":\"ghi\"}]}"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Mock + Fetcher fetcher; + @Mock + Persister persister; + + private final BarCode barCode = new BarCode("value", KEY); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + Reader source = new StringReader(sourceString); + when(fetcher.fetch(barCode)) + .thenReturn(Observable.just(source)); + + when(persister.read(barCode)) + .thenReturn(Observable.empty()) + .thenReturn(Observable.just(source)); + + when(persister.write(barCode, source)) + .thenReturn(Observable.just(true)); + } + + @Test + public void testDefaultJacksonReaderParser() { + Parser parser = JacksonParserFactory.createReaderParser(Foo.class); + Store store = ParsingStoreBuilder.builder() + .persister(persister) + .fetcher(fetcher) + .parser(parser) + .open(); + + Foo result = store.get(barCode).toBlocking().first(); + + validateFoo(result); + + verify(fetcher, times(1)).fetch(barCode); + } + + @Test + public void testCustomJsonFactoryReaderParser() { + JsonFactory jsonFactory = new JsonFactory(); + + Parser parser = JacksonParserFactory.createReaderParser(jsonFactory, Foo.class); + + Store store = ParsingStoreBuilder.builder() + .persister(persister) + .fetcher(fetcher) + .parser(parser) + .open(); + + Foo result = store.get(barCode).toBlocking().first(); + + validateFoo(result); + + verify(fetcher, times(1)).fetch(barCode); + } + + private void validateFoo(Foo foo) { + assertNotNull(foo); + assertEquals(foo.number, 123); + assertEquals(foo.string, "abc"); + assertEquals(foo.bars.size(), 2); + assertEquals(foo.bars.get(0).string, "def"); + assertEquals(foo.bars.get(1).string, "ghi"); + } + + @Test + public void testNullJsonFactory() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createReaderParser((JsonFactory) null, Foo.class); + } + + @Test + public void testNullTypeWithValidJsonFactory() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createReaderParser(new JsonFactory(), null); + } + + @Test + public void testNullObjectMapper() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createReaderParser((ObjectMapper) null, Foo.class); + } + + @Test + public void testNullType() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createStringParser(null); + } + +} diff --git a/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonSourceParserStoreTest.java b/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonSourceParserStoreTest.java new file mode 100644 index 0000000..eeb6460 --- /dev/null +++ b/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonSourceParserStoreTest.java @@ -0,0 +1,139 @@ +package com.nytimes.android.external.store.middleware.jackson; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nytimes.android.external.store.base.Fetcher; +import com.nytimes.android.external.store.base.Parser; +import com.nytimes.android.external.store.base.Persister; +import com.nytimes.android.external.store.base.Store; +import com.nytimes.android.external.store.base.impl.BarCode; +import com.nytimes.android.external.store.base.impl.ParsingStoreBuilder; +import com.nytimes.android.external.store.middleware.jackson.data.Foo; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import java.io.ByteArrayInputStream; +import java.nio.charset.Charset; + +import okio.BufferedSource; +import okio.Okio; +import rx.Observable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class JacksonSourceParserStoreTest { + + private static final String KEY = "key"; + private static final String sourceString = + "{\"number\":123,\"string\":\"abc\",\"bars\":[{\"string\":\"def\"},{\"string\":\"ghi\"}]}"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Mock + Fetcher fetcher; + @Mock + Persister persister; + + private final BarCode barCode = new BarCode("value", KEY); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + BufferedSource bufferedSource = source(sourceString); + assertNotNull(bufferedSource); + + when(fetcher.fetch(barCode)) + .thenReturn(Observable.just(bufferedSource)); + + when(persister.read(barCode)) + .thenReturn(Observable.empty()) + .thenReturn(Observable.just(bufferedSource)); + + when(persister.write(barCode, bufferedSource)) + .thenReturn(Observable.just(true)); + } + + @Test + public void testDefaultJacksonSourceParser() { + Parser parser = JacksonParserFactory.createSourceParser(Foo.class); + Store store = ParsingStoreBuilder.builder() + .persister(persister) + .fetcher(fetcher) + .parser(parser) + .open(); + + Foo result = store.get(barCode).toBlocking().first(); + + validateFoo(result); + + verify(fetcher, times(1)).fetch(barCode); + } + + @Test + public void testCustomJsonFactorySourceParser() { + JsonFactory jsonFactory = new JsonFactory(); + + Parser parser = JacksonParserFactory.createSourceParser(jsonFactory, Foo.class); + + Store store = ParsingStoreBuilder.builder() + .persister(persister) + .fetcher(fetcher) + .parser(parser) + .open(); + + Foo result = store.get(barCode).toBlocking().first(); + + validateFoo(result); + + verify(fetcher, times(1)).fetch(barCode); + } + + private void validateFoo(Foo foo) { + assertNotNull(foo); + assertEquals(foo.number, 123); + assertEquals(foo.string, "abc"); + assertEquals(foo.bars.size(), 2); + assertEquals(foo.bars.get(0).string, "def"); + assertEquals(foo.bars.get(1).string, "ghi"); + } + + @Test + public void testNullJsonFactory() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createStringParser((JsonFactory) null, Foo.class); + } + + @Test + public void testNullTypeWithValidJsonFactory() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createStringParser(new JsonFactory(), null); + } + + @Test + public void testNullObjectMapper() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createStringParser((ObjectMapper) null, Foo.class); + } + + @Test + public void testNullType() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createStringParser(null); + } + + private static BufferedSource source(String data) { + return Okio.buffer(Okio.source(new ByteArrayInputStream(data.getBytes(Charset.defaultCharset())))); + } + +} diff --git a/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonStringParserStoreTest.java b/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonStringParserStoreTest.java new file mode 100644 index 0000000..a12f0b4 --- /dev/null +++ b/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/JacksonStringParserStoreTest.java @@ -0,0 +1,126 @@ +package com.nytimes.android.external.store.middleware.jackson; + +import com.fasterxml.jackson.core.JsonFactory; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.nytimes.android.external.store.base.Fetcher; +import com.nytimes.android.external.store.base.Parser; +import com.nytimes.android.external.store.base.Persister; +import com.nytimes.android.external.store.base.Store; +import com.nytimes.android.external.store.base.impl.BarCode; +import com.nytimes.android.external.store.base.impl.ParsingStoreBuilder; +import com.nytimes.android.external.store.middleware.jackson.data.Foo; + +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + +import rx.Observable; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +public class JacksonStringParserStoreTest { + + private static final String KEY = "key"; + private static final String source = + "{\"number\":123,\"string\":\"abc\",\"bars\":[{\"string\":\"def\"},{\"string\":\"ghi\"}]}"; + + @Rule + public ExpectedException expectedException = ExpectedException.none(); + + @Mock + Fetcher fetcher; + @Mock + Persister persister; + + private final BarCode barCode = new BarCode("value", KEY); + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + + when(fetcher.fetch(barCode)) + .thenReturn(Observable.just(source)); + + when(persister.read(barCode)) + .thenReturn(Observable.empty()) + .thenReturn(Observable.just(source)); + + when(persister.write(barCode, source)) + .thenReturn(Observable.just(true)); + } + + @Test + public void testDefaultJacksonStringParser() { + Store store = ParsingStoreBuilder.builder() + .persister(persister) + .fetcher(fetcher) + .parser(JacksonParserFactory.createStringParser(Foo.class)) + .open(); + + Foo result = store.get(barCode).toBlocking().first(); + + validateFoo(result); + + verify(fetcher, times(1)).fetch(barCode); + } + + @Test + public void testCustomJsonFactoryStringParser() { + JsonFactory jsonFactory = new JsonFactory(); + + Parser parser = JacksonParserFactory.createStringParser(jsonFactory, Foo.class); + + Store store = ParsingStoreBuilder.builder() + .persister(persister) + .fetcher(fetcher) + .parser(parser) + .open(); + + Foo result = store.get(barCode).toBlocking().first(); + + validateFoo(result); + + verify(fetcher, times(1)).fetch(barCode); + } + + private void validateFoo(Foo foo) { + assertNotNull(foo); + assertEquals(foo.number, 123); + assertEquals(foo.string, "abc"); + assertEquals(foo.bars.size(), 2); + assertEquals(foo.bars.get(0).string, "def"); + assertEquals(foo.bars.get(1).string, "ghi"); + } + + @Test + public void testNullJsonFactory() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createStringParser((JsonFactory) null, Foo.class); + } + + @Test + public void testNullTypeWithValidJsonFactory() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createStringParser(new JsonFactory(), null); + } + + @Test + public void testNullObjectMapper() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createStringParser((ObjectMapper) null, Foo.class); + } + + @Test + public void testNullType() { + expectedException.expect(NullPointerException.class); + JacksonParserFactory.createStringParser(null); + } + +} diff --git a/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/data/Bar.java b/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/data/Bar.java new file mode 100644 index 0000000..766161d --- /dev/null +++ b/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/data/Bar.java @@ -0,0 +1,12 @@ +package com.nytimes.android.external.store.middleware.jackson.data; + +public class Bar { + public String string; + + public Bar() { + } + + public Bar(String string) { + this.string = string; + } +} diff --git a/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/data/Foo.java b/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/data/Foo.java new file mode 100644 index 0000000..5bcb180 --- /dev/null +++ b/middleware-jackson/src/test/java/com/nytimes/android/external/store/middleware/jackson/data/Foo.java @@ -0,0 +1,18 @@ +package com.nytimes.android.external.store.middleware.jackson.data; + +import java.util.List; + +public class Foo { + public int number; + public String string; + public List bars; + + public Foo() { + } + + public Foo(int number, String string, List bars) { + this.number = number; + this.string = string; + this.bars = bars; + } +} diff --git a/settings.gradle b/settings.gradle index f78b4f2..46953f8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1 @@ -include ':app', ':store', ':middleware', ':cache', ':filesystem', ':middleware-moshi' +include ':app', ':store', ':middleware', ':cache', ':filesystem', ':middleware-moshi', ':middleware-jackson'