Closed drizzd closed 3 years ago
I couldn't figure out how to label this issue, so I've labeled it for a human to triage. Hang tight.
Thanks for reporting this incompatibility, and sorry to hear about your troubles.
Excluding protobuf-lite
from Firestore will definitely break the SDK. The only workaround would be to try excluding protobuf
dependency from Robolectric -- this is what we do for our own tests. Note, however, that the two versions of the protobuf library are binary incompatible, and it's pretty much guaranteed that doing the exclusion will break something in Robolectric -- we've just been lucky that it didn't affect the way we use Robolectric. In other words, it might work, though it's certainly not a scenario supported by Robolectric and might end up broken for your use case. I'm sorry I don't have a better workaround for this.
That fixes the version conflict. However, the callbacks are not invoked in the following test. It passes after the 10 second sleep:
import android.content.Context
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.Test
import org.junit.runner.RunWith
@RunWith(AndroidJUnit4::class)
class FirebaseTest {
private val context = ApplicationProvider.getApplicationContext<Context>()
@Test
fun write() {
val settings = FirebaseFirestoreSettings.Builder()
.setHost("127.0.0.1:8080")
.setSslEnabled(false)
.setPersistenceEnabled(false)
.build()
FirebaseApp.initializeApp(context)
val firestore = FirebaseFirestore.getInstance()
firestore.firestoreSettings = settings
firestore.collection("ponys").document("foo").set(mapOf("key" to "bar"))
.addOnSuccessListener {
throw Exception("success")
}
.addOnFailureListener {
throw Exception("failure")
}
.addOnCanceledListener {
throw Exception("canceled")
}
Thread.sleep(10000)
}
Looking at the network traffic, it seems that no GRPC/Protobuf traffic happens:
As opposed to the Node.js version which works as expected:
const firebase = require('firebase/app');
require('firebase/firestore');
let firebaseApp;
async function main() {
firebaseInit();
try {
await run();
} finally {
firebaseDelete();
}
}
function firebaseInit() {
firebaseApp = firebase.initializeApp({
apiKey: "<...>",
authDomain: "<...>.firebaseapp.com",
databaseURL: "https://<...>.firebaseio.com",
projectId: "<...>",
storageBucket: "<...>.appspot.com",
messagingSenderId: "<...>",
appId: "<...>"
});
firebaseApp.firestore().settings({
host: 'localhost:8080',
ssl: false
});
}
function firebaseDelete() {
firebaseApp.delete();
}
async function run() {
let firestore = firebaseApp.firestore();
await firestore.collection('ponys').doc('foo').set({key: 'bar'});
}
main();
For reference, here is my build.gradle with protobuf-java excluded:
apply plugin: 'com.android.application'
apply plugin: 'com.google.gms.google-services'
apply plugin: 'com.getkeepsafe.dexcount'
apply plugin: 'com.google.firebase.firebase-perf'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'io.fabric'
android {
compileSdkVersion 28
defaultConfig {
applicationId "<...>"
minSdkVersion 16
targetSdkVersion 28
multiDexEnabled true
versionCode 2
versionName "1.1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
// Workaround for DuplicateRelativeFileException: More than one file was found with OS independent path 'META-INF/DEPENDENCIES'
packagingOptions {
exclude 'META-INF/DEPENDENCIES'
}
kotlinOptions {
jvmTarget = '1.8'
}
testOptions.unitTests {
includeAndroidResources = true
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'androidx.work:work-runtime-ktx:2.4.0-alpha01'
testImplementation 'junit:junit:4.12'
testImplementation 'androidx.test:core:1.2.0'
testImplementation 'androidx.test.ext:junit:1.1.1'
testImplementation 'androidx.test:runner:1.2.0'
testImplementation('org.robolectric:robolectric:4.2') {
exclude group: 'com.google.protobuf', module: 'protobuf-java'
}
testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2'
androidTestImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2'
androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:runner:1.2.0'
implementation 'org.jetbrains.anko:anko-commons:0.10.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.2'
implementation 'com.google.firebase:firebase-core:17.2.3'
implementation 'com.google.firebase:firebase-auth:19.2.0'
implementation 'com.google.firebase:firebase-firestore:21.4.1'
implementation 'com.google.firebase:firebase-ml-vision:24.0.1'
implementation 'com.google.firebase:firebase-perf:19.0.5'
implementation 'com.crashlytics.sdk.android:crashlytics:2.10.1'
implementation 'com.android.support:multidex:1.0.3'
testImplementation 'io.mockk:mockk:1.8.13.kotlin13'
testImplementation 'org.json:json:20171018'
// https://developers.google.com/identity/sign-in/android/start-integrating
implementation 'com.google.android.gms:play-services-auth:17.0.0'
// https://github.com/gsuitedevs/android-samples/blob/master/drive/deprecation/app/build.gradle
implementation('com.google.http-client:google-http-client-gson:1.26.0') {
exclude group: 'org.apache.httpcomponents'
}
implementation('com.google.api-client:google-api-client-android:1.26.0') {
exclude group: 'org.apache.httpcomponents'
}
implementation('com.google.apis:google-api-services-drive:v3-rev136-1.25.0') {
exclude group: 'org.apache.httpcomponents'
}
}
Here's a complete demo: https://gitlab.com/drizzd/firebaserobodemo
I believe the issue is that future.get
blocks the same thread that the callback is going to be invoked on; therefore, the callback never gets a chance to be invoked. Trying out your repro app (thanks for preparing it!) with logging enabled (FirebaseFirestore.setLoggingEnabled(true)
), I see that Firestore receives the response from the server, but the callback is never invoked. I'm not very familiar with how waiting for asynchronous events works in Robolectric, sorry; I don't think this is something specific to Firestore. It would appear that you would need to call either flushForegroundThreadScheduler
or flushBackgroundThreadScheduler
before you can poll the future.
Closing the issue as it hasn't been updated in a while. Feel free to reopen to follow up.
What feature would you like to see?
Demo: https://gitlab.com/drizzd/firebaserobodemo
I would like to test my app, including the Firestore code paths, with Robolectric. Unfortunately, Firestore and Robolectric depend on different versions of protobuf. I don't suppose it makes sense to just exclude one of the two?
There is also another issue with the SharedPreferences filename on Windows, for which I have submitted to Robolectric: https://github.com/robolectric/robolectric/issues/5529.
How would you use it?
I would like to use AndroidX Test unit tests which are based on Robolectric in order to efficiently test Android apps which use Firestore. Combined with the Firebase emulator suite it would make a very powerful testing environment.
Dependency Error
For reference, this is the runtime error: