diff --git a/build.gradle b/build.gradle index 9e4257360..74b6711ac 100644 --- a/build.gradle +++ b/build.gradle @@ -58,6 +58,7 @@ ext.libraries = [ // Core libraries supportAnnotations : 'com.android.support:support-annotations:' + supportLibsVersion, + rxJava : 'io.reactivex:rxjava:1.2.1', rxJava2 : 'io.reactivex.rxjava2:rxjava:2.1.6', rxAndroid2 : 'io.reactivex.rxjava2:rxandroid:2.0.1', kotlinStdLib : "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version", @@ -73,6 +74,9 @@ ext.libraries = [ storIOContentResolverAnnotationsProcessor: project(':storio-content-resolver-annotations-processor'), storIOTestCommon : project(':storio-test-common'), + // StorIO old versions + storIOSQLite1 : 'com.pushtorefresh.storio:sqlite:1.13.0', + // Libraries for tests and sample app junit : 'junit:junit:4.12', assertJ : 'org.assertj:assertj-core:3.9.0', // http://joel-costigliola.github.io/assertj/assertj-core-news.html diff --git a/settings.gradle b/settings.gradle index e95c02190..b7f591eea 100644 --- a/settings.gradle +++ b/settings.gradle @@ -12,6 +12,8 @@ include ':storio-sqlite-annotations-processor-test' include ':storio-content-resolver-annotations-processor' include ':storio-content-resolver-annotations-processor-test' +include ':storio-sqlite-interop1to3' + include ':storio-test-without-rxjava' include ':storio-basic-sample-app' diff --git a/storio-sqlite-interop1to3/build.gradle b/storio-sqlite-interop1to3/build.gradle new file mode 100644 index 000000000..1b2f572c7 --- /dev/null +++ b/storio-sqlite-interop1to3/build.gradle @@ -0,0 +1,35 @@ +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.storIOSQLite1 + implementation libraries.storIOSQLite + + compileOnly libraries.rxJava + compileOnly libraries.rxJava2 + testImplementation libraries.rxJava + testImplementation 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-interop1to3/gradle.properties b/storio-sqlite-interop1to3/gradle.properties new file mode 100644 index 000000000..259c49c41 --- /dev/null +++ b/storio-sqlite-interop1to3/gradle.properties @@ -0,0 +1,3 @@ +POM_NAME=storio-sqlite-interop1to3 +POM_ARTIFACT_ID=storio-sqlite-interop1to3 +POM_PACKAGING=jar diff --git a/storio-sqlite-interop1to3/src/main/AndroidManifest.xml b/storio-sqlite-interop1to3/src/main/AndroidManifest.xml new file mode 100644 index 000000000..ed40771d8 --- /dev/null +++ b/storio-sqlite-interop1to3/src/main/AndroidManifest.xml @@ -0,0 +1,2 @@ + + diff --git a/storio-sqlite-interop1to3/src/main/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3.java b/storio-sqlite-interop1to3/src/main/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3.java new file mode 100644 index 000000000..3d5c18c52 --- /dev/null +++ b/storio-sqlite-interop1to3/src/main/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3.java @@ -0,0 +1,80 @@ +package com.pushtorefresh.storio3.sqlite.interop1to3; + +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 StorIOSQLite1To3 { + + private final Set forwardedChanges1 = Collections.newSetFromMap( + new ConcurrentHashMap() + ); + + private final Set forwardedChanges3 = Collections.newSetFromMap( + new ConcurrentHashMap() + ); + + public void forwardNotifications( + @NonNull final com.pushtorefresh.storio.sqlite.StorIOSQLite sqlite1, + @NonNull final com.pushtorefresh.storio3.sqlite.StorIOSQLite sqlite3 + ) { + com.pushtorefresh.storio.internal.Environment.throwExceptionIfRxJavaIsNotAvailable( + "forwardNotifications() requires rxJava2" + ); + com.pushtorefresh.storio3.internal.Environment.throwExceptionIfRxJava2IsNotAvailable( + "forwardNotifications() requires rxJava2" + ); + + sqlite1.observeChanges() + .subscribe(new Action1() { + @Override + public void call(@NonNull com.pushtorefresh.storio.sqlite.Changes changes1) { + if (!forwardedChanges1.remove(changes1)) { // Check to prevent cyclic forwarding. + final com.pushtorefresh.storio3.sqlite.Changes changes3ToForward + = convertToV3(changes1); + 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.storio.sqlite.Changes changes1ToForward + = convertToV1(changes3); + forwardedChanges1.add(changes1ToForward); + sqlite1.lowLevel().notifyAboutChanges(changes1ToForward); + } + } + }); + } + + @NonNull + private com.pushtorefresh.storio.sqlite.Changes convertToV1( + @NonNull com.pushtorefresh.storio3.sqlite.Changes changes3 + ) { + return com.pushtorefresh.storio.sqlite.Changes.newInstance( + changes3.affectedTables(), + changes3.affectedTags() + ); + } + + @NonNull + private com.pushtorefresh.storio3.sqlite.Changes convertToV3( + @NonNull com.pushtorefresh.storio.sqlite.Changes changes1 + ) { + return com.pushtorefresh.storio3.sqlite.Changes.newInstance( + changes1.affectedTables(), + changes1.affectedTags() + ); + } +} diff --git a/storio-sqlite-interop1to3/src/test/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3Test.kt b/storio-sqlite-interop1to3/src/test/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3Test.kt new file mode 100644 index 000000000..5cac98a3e --- /dev/null +++ b/storio-sqlite-interop1to3/src/test/java/com/pushtorefresh/storio3/sqlite/interop1to3/StorIOSQLite1To3Test.kt @@ -0,0 +1,114 @@ +package com.pushtorefresh.storio3.sqlite.interop1to3 + +import com.nhaarman.mockito_kotlin.mock +import io.reactivex.BackpressureStrategy +import org.junit.Before +import org.junit.Test +import com.pushtorefresh.storio.sqlite.Changes as Changes1 +import com.pushtorefresh.storio.sqlite.StorIOSQLite as StorIOSQLite1 +import com.pushtorefresh.storio.sqlite.impl.DefaultStorIOSQLite as DefaultStorIOSQLite1 +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 StorIOSQLite1To3Test { + + val observer1 = TestSubscriberRx1.create() + + val observer3 = TestSubscriberRx2.create() + + lateinit var storIOSQLite1: StorIOSQLite1 + + lateinit var storIOSQLite3: StorIOSQLite3 + + lateinit var changes1WithTags: Changes1 + + lateinit var changes1WithoutTags: Changes1 + + lateinit var changes3WithTags: Changes3 + + lateinit var changes3WithoutTags: Changes3 + + @Before + fun `before each test`() { + storIOSQLite1 = DefaultStorIOSQLite1.builder() + .sqliteOpenHelper(mock()) + .defaultScheduler(null) + .build() + storIOSQLite3 = DefaultStorIOSQLite3.builder() + .sqliteOpenHelper(mock()) + .defaultRxScheduler(null) + .build() + val interop = StorIOSQLite1To3() + interop.forwardNotifications(storIOSQLite1, storIOSQLite3) + + storIOSQLite1.observeChanges().subscribe(observer1) + storIOSQLite3.observeChanges(BackpressureStrategy.BUFFER).subscribe(observer3) + + val tables = HashSet() + tables.add("table1") + tables.add("table2") + + val tags = HashSet() + tags.add("tag1") + tags.add("tag2") + + changes1WithTags = Changes1.newInstance(tables, tags) + changes3WithTags = Changes3.newInstance(tables, tags) + + changes1WithoutTags = Changes1.newInstance(tables, null as Collection?) + changes3WithoutTags = Changes3.newInstance(tables, null as Collection?) + } + + @Test + fun `forwardNotifications should forward from 1 to 3 with tags`() { + storIOSQLite1.lowLevel().notifyAboutChanges(changes1WithTags) + observer1.assertValue(changes1WithTags) + observer3.assertValue(changes3WithTags) + } + + @Test + fun `forwardNotifications should forward from 1 to 3 without tags`() { + storIOSQLite1.lowLevel().notifyAboutChanges(changes1WithoutTags) + observer1.assertValue(changes1WithoutTags) + observer3.assertValue(changes3WithoutTags) + } + + @Test + fun `forwardNotifications should forward from 1 to 3 multiple`() { + storIOSQLite1.lowLevel().notifyAboutChanges(changes1WithTags) + observer1.assertValue(changes1WithTags) + observer3.assertValue(changes3WithTags) + + storIOSQLite1.lowLevel().notifyAboutChanges(changes1WithTags) + observer1.assertValues(changes1WithTags, changes1WithTags) + observer3.assertValues(changes3WithTags, changes3WithTags) + } + + @Test + fun `forwardNotifications should forward from 3 to 1 with tags`() { + storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithTags) + observer1.assertValue(changes1WithTags) + observer3.assertValue(changes3WithTags) + } + + @Test + fun `forwardNotifications should forward from 3 to 1 without tags`() { + storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithoutTags) + observer1.assertValue(changes1WithoutTags) + observer3.assertValue(changes3WithoutTags) + } + + @Test + fun `forwardNotifications should forward from 3 to 1 with multiple`() { + storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithTags) + observer1.assertValue(changes1WithTags) + observer3.assertValue(changes3WithTags) + + storIOSQLite3.lowLevel().notifyAboutChanges(changes3WithTags) + observer1.assertValues(changes1WithTags, changes1WithTags) + observer3.assertValues(changes3WithTags, changes3WithTags) + } +}