diff --git a/build.gradle b/build.gradle index bea88a9cc..d473b7261 100644 --- a/build.gradle +++ b/build.gradle @@ -76,6 +76,7 @@ ext.libraries = [ // StorIO old versions storIOSQLite1 : 'com.pushtorefresh.storio:sqlite:1.13.0', + storIOSQLite2 : 'com.pushtorefresh.storio2:sqlite:2.1.0', // Libraries for tests and sample app junit : 'junit:junit:4.12', diff --git a/settings.gradle b/settings.gradle index b7f591eea..91b5a66c8 100644 --- a/settings.gradle +++ b/settings.gradle @@ -13,6 +13,7 @@ include ':storio-content-resolver-annotations-processor' include ':storio-content-resolver-annotations-processor-test' include ':storio-sqlite-interop1to3' +include ':storio-sqlite-interop2to3' include ':storio-test-without-rxjava' diff --git a/storio-sqlite-interop2to3/build.gradle b/storio-sqlite-interop2to3/build.gradle new file mode 100644 index 000000000..32a9661ad --- /dev/null +++ b/storio-sqlite-interop2to3/build.gradle @@ -0,0 +1,34 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' + +android { + compileSdkVersion rootProject.ext.compileSdkVersion + + defaultConfig { + versionName VERSION_NAME + versionCode Integer.parseInt(VERSION_CODE) + minSdkVersion rootProject.ext.minSdkVersion + } + + packagingOptions { + exclude 'LICENSE.txt' // multiple libs have this file -> cause build error + } +} + +dependencies { + implementation libraries.storIOSQLite2 + implementation libraries.storIOSQLite + + // Do not use compileOnly because without rxJava and rxJava2 using interoperability makes sense. + implementation libraries.rxJava + implementation libraries.rxJava2 + + testImplementation libraries.junit + testImplementation libraries.kotlinStdLib + testImplementation libraries.mockitoKotlin + testImplementation libraries.assertJ +} + +apply from: '../gradle/checkstyle.gradle' +apply from: '../gradle/jacoco-android.gradle' +apply from: '../gradle/publish-android-lib.gradle' diff --git a/storio-sqlite-interop2to3/gradle.properties b/storio-sqlite-interop2to3/gradle.properties new file mode 100644 index 000000000..8ee3f42f6 --- /dev/null +++ b/storio-sqlite-interop2to3/gradle.properties @@ -0,0 +1,3 @@ +POM_NAME=storio-sqlite-interop2to3 +POM_ARTIFACT_ID=storio-sqlite-interop2to3 +POM_PACKAGING=jar diff --git a/storio-sqlite-interop2to3/src/main/AndroidManifest.xml b/storio-sqlite-interop2to3/src/main/AndroidManifest.xml new file mode 100644 index 000000000..3aa51a5bf --- /dev/null +++ b/storio-sqlite-interop2to3/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/storio-sqlite-interop2to3/src/main/java/com/pushtorefresh/storio3/sqlite/interop2to3/StorIOSQLite2To3.java b/storio-sqlite-interop2to3/src/main/java/com/pushtorefresh/storio3/sqlite/interop2to3/StorIOSQLite2To3.java new file mode 100644 index 000000000..08ba5f044 --- /dev/null +++ b/storio-sqlite-interop2to3/src/main/java/com/pushtorefresh/storio3/sqlite/interop2to3/StorIOSQLite2To3.java @@ -0,0 +1,73 @@ +package com.pushtorefresh.storio3.sqlite.interop2to3; + +import android.support.annotation.NonNull; + +import java.util.Collections; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import io.reactivex.BackpressureStrategy; +import io.reactivex.functions.Consumer; +import rx.functions.Action1; + +public class StorIOSQLite2To3 { + + private final Set forwardedChanges2 = Collections.newSetFromMap( + new ConcurrentHashMap() + ); + + private final Set forwardedChanges3 = Collections.newSetFromMap( + new ConcurrentHashMap() + ); + + public void forwardNotifications( + @NonNull final com.pushtorefresh.storio2.sqlite.StorIOSQLite sqlite2, + @NonNull final com.pushtorefresh.storio3.sqlite.StorIOSQLite sqlite3 + ) { + sqlite2.observeChanges() + .subscribe(new Action1() { + @Override + public void call(@NonNull com.pushtorefresh.storio2.sqlite.Changes changes2) { + if (!forwardedChanges2.remove(changes2)) { // Check to prevent cyclic forwarding. + final com.pushtorefresh.storio3.sqlite.Changes changes3ToForward + = convertToV3(changes2); + forwardedChanges3.add(changes3ToForward); + sqlite3.lowLevel().notifyAboutChanges(changes3ToForward); + } + } + }); + + sqlite3.observeChanges(BackpressureStrategy.BUFFER) + .subscribe(new Consumer() { + @Override + public void accept(com.pushtorefresh.storio3.sqlite.Changes changes3) throws Exception { + if (!forwardedChanges3.remove(changes3)) { // Check to prevent cyclic forwarding. + final com.pushtorefresh.storio2.sqlite.Changes changes2ToForward + = convertToV2(changes3); + forwardedChanges2.add(changes2ToForward); + sqlite2.lowLevel().notifyAboutChanges(changes2ToForward); + } + } + }); + } + + @NonNull + private com.pushtorefresh.storio2.sqlite.Changes convertToV2( + @NonNull com.pushtorefresh.storio3.sqlite.Changes changes3 + ) { + return com.pushtorefresh.storio2.sqlite.Changes.newInstance( + changes3.affectedTables(), + changes3.affectedTags() + ); + } + + @NonNull + private com.pushtorefresh.storio3.sqlite.Changes convertToV3( + @NonNull com.pushtorefresh.storio2.sqlite.Changes changes2 + ) { + return com.pushtorefresh.storio3.sqlite.Changes.newInstance( + changes2.affectedTables(), + changes2.affectedTags() + ); + } +} diff --git a/storio-sqlite-interop2to3/src/test/java/com/pushtorefresh/storio3/sqlite/interop2to3/StorIOSQLite2To3Test.kt b/storio-sqlite-interop2to3/src/test/java/com/pushtorefresh/storio3/sqlite/interop2to3/StorIOSQLite2To3Test.kt new file mode 100644 index 000000000..2a3963471 --- /dev/null +++ b/storio-sqlite-interop2to3/src/test/java/com/pushtorefresh/storio3/sqlite/interop2to3/StorIOSQLite2To3Test.kt @@ -0,0 +1,114 @@ +package com.pushtorefresh.storio3.sqlite.interop2to3 + +import com.nhaarman.mockito_kotlin.mock +import io.reactivex.BackpressureStrategy +import org.junit.Before +import org.junit.Test +import com.pushtorefresh.storio2.sqlite.Changes as Changes2 +import com.pushtorefresh.storio2.sqlite.StorIOSQLite as StorIOSQLite2 +import com.pushtorefresh.storio2.sqlite.impl.DefaultStorIOSQLite as DefaultStorIOSQLite2 +import com.pushtorefresh.storio3.sqlite.Changes as Changes3 +import com.pushtorefresh.storio3.sqlite.StorIOSQLite as StorIOSQLite3 +import com.pushtorefresh.storio3.sqlite.impl.DefaultStorIOSQLite as DefaultStorIOSQLite3 +import io.reactivex.subscribers.TestSubscriber as TestSubscriberRx2 +import rx.observers.TestSubscriber as TestSubscriberRx1 + +class StorIOSQLite2To3Test { + + val observer2 = TestSubscriberRx1.create() + + val observer3 = TestSubscriberRx2.create() + + lateinit var storIOSQLite2: StorIOSQLite2 + + lateinit var storIOSQLite3: StorIOSQLite3 + + lateinit var changes2WithTags: Changes2 + + lateinit var changes2WithoutTags: Changes2 + + lateinit var changes3WithTags: Changes3 + + lateinit var changes3WithoutTags: Changes3 + + @Before + fun `before each test`() { + storIOSQLite2 = DefaultStorIOSQLite2.builder() + .sqliteOpenHelper(mock()) + .defaultScheduler(null) + .build() + storIOSQLite3 = DefaultStorIOSQLite3.builder() + .sqliteOpenHelper(mock()) + .defaultRxScheduler(null) + .build() + val interop = StorIOSQLite2To3() + interop.forwardNotifications(storIOSQLite2, storIOSQLite3) + + storIOSQLite2.observeChanges().subscribe(observer2) + storIOSQLite3.observeChanges(BackpressureStrategy.BUFFER).subscribe(observer3) + + val tables = HashSet() + tables.add("table1") + tables.add("table2") + + val tags = HashSet() + tags.add("tag1") + tags.add("tag2") + + changes2WithTags = Changes2.newInstance(tables, tags) + changes3WithTags = Changes3.newInstance(tables, tags) + + changes2WithoutTags = Changes2.newInstance(tables, null as Collection?) + changes3WithoutTags = Changes3.newInstance(tables, null as Collection?) + } + + @Test + fun `forwardNotifications should forward from 2 to 3 with tags`() { + storIOSQLite2.lowLevel().notifyAboutChanges(changes2WithTags) + observer2.assertValue(changes2WithTags) + observer3.assertValue(changes3WithTags) + } + + @Test + fun `forwardNotifications should forward from 2 to 3 without tags`() { + storIOSQLite2.lowLevel().notifyAboutChanges(changes2WithoutTags) + observer2.assertValue(changes2WithoutTags) + observer3.assertValue(changes3WithoutTags) + } + + @Test + fun `forwardNotifications should forward from 2 to 3 multiple`() { + storIOSQLite2.lowLevel().notifyAboutChanges(changes2WithTags) + observer2.assertValue(changes2WithTags) + observer3.assertValue(changes3WithTags) + + storIOSQLite2.lowLevel().notifyAboutChanges(changes2WithTags) + observer2.assertValues(changes2WithTags, changes2WithTags) + observer3.assertValues(changes3WithTags, changes3WithTags) + } + + @Test + fun `forwardNotifications should forward from 3 to 2 with tags`() { + storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithTags) + observer2.assertValue(changes2WithTags) + observer3.assertValue(changes3WithTags) + } + + @Test + fun `forwardNotifications should forward from 3 to 2 without tags`() { + storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithoutTags) + observer2.assertValue(changes2WithoutTags) + observer3.assertValue(changes3WithoutTags) + } + + @Test + fun `forwardNotifications should forward from 3 to 2 with multiple`() { + storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithTags) + observer2.assertValue(changes2WithTags) + observer3.assertValue(changes3WithTags) + + storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithTags) + observer2.assertValues(changes2WithTags, changes2WithTags) + observer3.assertValues(changes3WithTags, changes3WithTags) + } +}