Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Migrate and reenable sync realm integration test #6939

Merged
merged 20 commits into from
Sep 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,14 @@ package io.realm
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import io.realm.internal.network.LoggingInterceptor.LOGIN_FEATURE
import io.realm.mongodb.AppConfiguration
import io.realm.log.LogLevel
import io.realm.log.RealmLog
import io.realm.log.RealmLogger
import io.realm.mongodb.*
import io.realm.mongodb.log.obfuscator.HttpLogObfuscator
import io.realm.mongodb.sync.SyncSession
import io.realm.rule.BlockingLooperThread
import io.realm.util.assertFailsWithErrorCode
import org.bson.codecs.StringCodec
import org.bson.codecs.configuration.CodecRegistries
import org.junit.Assert.*
Expand All @@ -31,13 +36,22 @@ import org.junit.rules.TemporaryFolder
import org.junit.runner.RunWith
import java.io.File
import java.net.URL
import java.util.*
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import kotlin.collections.LinkedHashMap
import kotlin.test.assertFailsWith
import kotlin.test.assertNull

private const val CUSTOM_HEADER_NAME = "Foo"
private const val CUSTOM_HEADER_VALUE = "bar"
private const val AUTH_HEADER_NAME = "RealmAuth"

@RunWith(AndroidJUnit4::class)
class AppConfigurationTests {

val looperThread = BlockingLooperThread()

@get:Rule
val tempFolder = TemporaryFolder()

Expand Down Expand Up @@ -300,4 +314,48 @@ class AppConfigurationTests {
val defaultHttpLogObfuscator = HttpLogObfuscator(LOGIN_FEATURE, AppConfiguration.loginObfuscators)
assertEquals(defaultHttpLogObfuscator, config.httpLogObfuscator)
}
// Check that custom headers and auth header renames are correctly used for HTTP requests
// performed from Java.
@Test
fun javaRequestCustomHeaders() {
var app: App? = null
try {
looperThread.runBlocking {
app = TestApp(builder = { builder ->
builder.addCustomRequestHeader(CUSTOM_HEADER_NAME, CUSTOM_HEADER_VALUE)
builder.authorizationHeaderName(AUTH_HEADER_NAME)
})
runJavaRequestCustomHeadersTest(app!!)
}
} finally {
app?.close()
}
}

private fun runJavaRequestCustomHeadersTest(app: App) {
val username = UUID.randomUUID().toString()
val password = "password"
val headerSet = AtomicBoolean(false)

// Setup logger to inspect that we get a log message with the custom headers
val level = RealmLog.getLevel()
RealmLog.setLevel(LogLevel.ALL)
val logger = RealmLogger { level: Int, tag: String?, throwable: Throwable?, message: String? ->
if (level > LogLevel.TRACE && message!!.contains(CUSTOM_HEADER_NAME) && message.contains(CUSTOM_HEADER_VALUE)
&& message.contains("RealmAuth: ")) {
headerSet.set(true)
}
}
RealmLog.add(logger)
assertFailsWithErrorCode(ErrorCode.SERVICE_UNKNOWN) {
app.registerUserAndLogin(username, password)
}
RealmLog.remove(logger)
RealmLog.setLevel(level)

assertTrue(headerSet.get())
looperThread.testComplete()
}


}
Original file line number Diff line number Diff line change
Expand Up @@ -277,7 +277,7 @@ class AppTests {

// Setup an App instance with a random encryption key
Realm.init(context)
app = TestApp(customizeConfig = {
app = TestApp(builder = {
it.encryptionKey(TestHelper.getRandomKey())
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import io.realm.entities.DefaultSyncSchema
import io.realm.entities.SyncDog
import io.realm.entities.SyncStringOnly
import io.realm.internal.OsRealmConfig
import io.realm.kotlin.syncSession
import io.realm.kotlin.where
import io.realm.log.LogLevel
Expand All @@ -27,16 +29,21 @@ import io.realm.mongodb.User
import io.realm.mongodb.close
import io.realm.mongodb.registerUserAndLogin
import io.realm.mongodb.sync.*
import io.realm.rule.BlockingLooperThread
import org.bson.types.ObjectId
import org.junit.After
import org.junit.Assert.*
import org.junit.Before
import org.junit.Ignore
import org.junit.Test
import org.junit.runner.RunWith
import java.lang.IllegalStateException
import java.util.*
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
import java.util.concurrent.atomic.AtomicBoolean
import java.util.concurrent.atomic.AtomicInteger
import kotlin.test.assertFailsWith

@RunWith(AndroidJUnit4::class)
class ProgressListenerTests {
Expand All @@ -45,6 +52,9 @@ class ProgressListenerTests {
private const val TEST_SIZE: Long = 10
}

private val looperThread = BlockingLooperThread()
private val configurationFactory = TestSyncConfigurationFactory()

private lateinit var app: TestApp
private lateinit var partitionValue: String

Expand Down Expand Up @@ -290,6 +300,83 @@ class ProgressListenerTests {
}
}

@Test
@Ignore("FIXME: Flacky: Tracked by https://github.com/realm/realm-java/issues/6976")
fun progressListenersWorkWhenUsingWaitForInitialRemoteData() = looperThread.runBlocking {
val username = UUID.randomUUID().toString()
val password = "password"
var user: User = app.registerUserAndLogin(username, password)

// 1. Copy a valid Realm to the server (and pray it does it within 10 seconds)
val configOld: SyncConfiguration = configurationFactory.createSyncConfigurationBuilder(user, user.id)
.testSchema(SyncStringOnly::class.java)
.testSessionStopPolicy(OsRealmConfig.SyncSessionStopPolicy.IMMEDIATELY)
.build()
Realm.getInstance(configOld).use { realm ->
realm.executeTransaction { realm ->
for (i in 0..9) {
realm.createObject(SyncStringOnly::class.java, ObjectId()).chars = "Foo$i"
}
}
realm.syncSession.uploadAllLocalChanges()
rorbech marked this conversation as resolved.
Show resolved Hide resolved
}
user.logOut()

assertFailsWith<IllegalStateException> {
app.sync.getSession(configOld)
}

// 2. Local state should now be completely reset. Open the same sync Realm but different local name again with
// a new configuration which should download the uploaded changes (pray it managed to do so within the time frame).
// Use different user to trigger different path
val user2 = app.registerUserAndLogin(TestHelper.getRandomEmail(), password)
val config: SyncConfiguration = configurationFactory.createSyncConfigurationBuilder(user2, user.id)
.testSchema(SyncStringOnly::class.java)
.waitForInitialRemoteData()
.build()

assertFalse(config.testRealmExists())

val countDownLatch = CountDownLatch(2)

val indefiniteListenerComplete = AtomicBoolean(false)
val currentChangesListenerComplete = AtomicBoolean(false)
val task = Realm.getInstanceAsync(config, object : Realm.Callback() {
override fun onSuccess(realm: Realm) {
realm.syncSession.addDownloadProgressListener(ProgressMode.INDEFINITELY, object : ProgressListener {
override fun onChange(progress: Progress) {
if (progress.isTransferComplete()) {
indefiniteListenerComplete.set(true)
countDownLatch.countDown()
}
}
})
realm.syncSession.addDownloadProgressListener(ProgressMode.CURRENT_CHANGES, object : ProgressListener {
override fun onChange(progress: Progress) {
if (progress.isTransferComplete()) {
currentChangesListenerComplete.set(true)
countDownLatch.countDown()
}
}
})
countDownLatch.await(100, TimeUnit.SECONDS)
realm.close()
if (!indefiniteListenerComplete.get()) {
fail("Indefinite progress listener did not report complete.")
}
if (!currentChangesListenerComplete.get()) {
fail("Current changes progress listener did not report complete.")
}
looperThread.testComplete()
}

override fun onError(exception: Throwable) {
fail(exception.toString())
}
})
looperThread.keepStrongReference(task)
}

@Test
fun uploadListener_keepIncreasingInSize() {
val config = createSyncConfig()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright 2020 Realm Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.realm.entities

import io.realm.RealmObject
import io.realm.annotations.PrimaryKey
import io.realm.annotations.RealmField
import org.bson.types.ObjectId

open class SyncSchemeMigration : RealmObject() {

companion object {
const val CLASS_NAME = "SyncSchemeMigration"
}

@PrimaryKey
@RealmField(name = "_id")
var id = ObjectId()

}
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,7 @@ package io.realm.mongodb.sync
fun SyncSession.testClose() {
this.close()
}

fun SyncSession.testShutdownAndWait() {
this.shutdownAndWait()
}
Original file line number Diff line number Diff line change
Expand Up @@ -323,3 +323,14 @@ JNIEXPORT void JNICALL Java_io_realm_mongodb_sync_SyncSession_nativeStop(JNIEnv*
}
CATCH_STD()
}

JNIEXPORT void JNICALL Java_io_realm_mongodb_sync_SyncSession_nativeShutdownAndWait (JNIEnv* env, jclass, jstring j_local_realm_path) {
try {
JStringAccessor local_realm_path(env, j_local_realm_path);
auto session = SyncManager::shared().get_existing_session(local_realm_path);
if (session) {
session->shutdown_and_wait();
}
}
CATCH_STD()
}
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,7 @@ public String getPath() {
*
* @return {@code true} if the Realm file exists, {@code false} otherwise.
*/
boolean realmExists() {
protected boolean realmExists() {
return new File(canonicalPath).exists();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@
import javax.annotation.Nullable;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import io.realm.mongodb.log.obfuscator.HttpLogObfuscator;
import io.realm.internal.objectstore.OsJavaNetworkTransport;
import io.realm.mongodb.AppConfiguration;
import io.realm.mongodb.AppException;
import io.realm.mongodb.ErrorCode;
import io.realm.mongodb.log.obfuscator.HttpLogObfuscator;
Expand Down Expand Up @@ -38,6 +38,22 @@ public OkHttpNetworkTransport(@Nullable HttpLogObfuscator httpLogObfuscator) {

private okhttp3.Request makeRequest(String method, String url, Map<String, String> headers, String body){
okhttp3.Request.Builder builder = new okhttp3.Request.Builder().url(url);
// TODO Ensure that we have correct custom headers until OS handles it
// first of all add all custom headers
for (Map.Entry<String, String> entry : getCustomRequestHeaders().entrySet()) {
builder.addHeader(entry.getKey(), entry.getValue());
}
// and then replace default authorization header with custom one if present
String authorizationHeaderValue = headers.get(AppConfiguration.DEFAULT_AUTHORIZATION_HEADER_NAME);
String authorizationHeaderName = getAuthorizationHeaderName();
if (authorizationHeaderValue != null && !AppConfiguration.DEFAULT_AUTHORIZATION_HEADER_NAME.equals(authorizationHeaderName)) {
headers.remove(AppConfiguration.DEFAULT_AUTHORIZATION_HEADER_NAME);
headers.put(authorizationHeaderName, authorizationHeaderValue);
}

for (Map.Entry<String, String> entry : headers.entrySet()) {
builder.addHeader(entry.getKey(), entry.getValue());
}
switch (method) {
case "get":
builder.get();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,11 @@ public BsonValue getPartitionValue() {
return partitionValue;
}

@Override
protected boolean realmExists() {
return super.realmExists();
}

/**
* Builder used to construct instances of a SyncConfiguration in a fluent manner.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -635,6 +635,10 @@ private void checkTimeout(long timeout, TimeUnit unit) {
}
}

void shutdownAndWait() {
nativeShutdownAndWait(configuration.getPath());
}

/**
* Interface used to report any session errors.
*
Expand Down Expand Up @@ -758,4 +762,5 @@ public void throwExceptionIfNeeded() {
private static native byte nativeGetConnectionState(String localRealmPath);
private static native void nativeStart(String localRealmPath);
private static native void nativeStop(String localRealmPath);
private static native void nativeShutdownAndWait(String localRealmPath);
}
Loading