Skip to content

Commit bcec158

Browse files
committed
Add new smoke tests (Firestore).
This commit adds a new smoke test for Firestore and some infrastructure for the other smoke tests that be added soon.
1 parent e78fb3e commit bcec158

File tree

10 files changed

+485
-0
lines changed

10 files changed

+485
-0
lines changed

smoke-tests/README.md

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# Firebase Smoke Test Suite
2+
3+
This directory contains smoke tests for Firebase on Android. These tests are
4+
intended to verify the integrations between different components and versions of
5+
Firebase. The tests should not include additional overhead, such as user
6+
interfaces. However, the tests should strive to remain similar to real use
7+
cases. As such, these tests run on devices or emulators (no Robolectric). This
8+
is a work in progress, and the following list shows what is complete:
9+
10+
- [ ] Create first set of tests to replace old test apps.
11+
- [ ] Reliably run smoke tests on CI.
12+
- [ ] Support version matrices.
13+
- [ ] Extend to collect system health metrics.
14+
15+
# Project Structure
16+
17+
This Gradle project is split into flavors for each Firebase product. Test code
18+
lives in these flavors. Each flavor has a single activity with the test methods.
19+
Test methods are annotated with `SmokeTest`. The Android plugin adds additional,
20+
compound flavors, such as `androidTestDatabase`. These should only contain a
21+
test class that extends `SmokeTestBase`. The are no formal test methods in these
22+
classes.
23+
24+
# Test Workflow
25+
26+
Android instrumentation uses two APKs: one for the app and the other for tests.
27+
As we want to test building as a third-party consumer, the test code and
28+
Firebase dependencies all belong in the application APK. This can be optionally
29+
obfsucated like a real application.
30+
31+
To simplify the tests, all test code is one source tree. The test source tree is
32+
presently required for bootstrapping the testing procedure and does not house
33+
anything related to the test methods. The test class reflectively finds and
34+
executes the test methods from the application APK. This may be simplified in
35+
the future.

smoke-tests/build.gradle

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
buildscript {
16+
repositories {
17+
google()
18+
jcenter()
19+
}
20+
21+
dependencies {
22+
classpath "com.android.tools.build:gradle:3.3.2"
23+
classpath "com.google.gms:google-services:4.0.0"
24+
}
25+
}
26+
27+
apply plugin: "com.android.application"
28+
29+
android {
30+
compileSdkVersion 24
31+
32+
compileOptions {
33+
sourceCompatibility JavaVersion.VERSION_1_8
34+
targetCompatibility JavaVersion.VERSION_1_8
35+
}
36+
37+
defaultConfig {
38+
minSdkVersion 16
39+
multiDexEnabled true
40+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
41+
}
42+
43+
flavorDimensions "systemUnderTest"
44+
45+
productFlavors {
46+
firestore {
47+
dimension "systemUnderTest"
48+
applicationId "com.google.firebase.testing.firestore"
49+
}
50+
}
51+
}
52+
53+
repositories {
54+
google()
55+
jcenter()
56+
}
57+
58+
dependencies {
59+
// Common
60+
61+
implementation "com.google.firebase:firebase-common:16.0.7"
62+
63+
androidTestImplementation "androidx.test.espresso:espresso-core:3.1.0"
64+
androidTestImplementation "androidx.test.espresso:espresso-idling-resource:3.1.0"
65+
androidTestImplementation "androidx.test:runner:1.1.0"
66+
67+
// Firestore
68+
firestoreImplementation "com.google.android.gms:play-services-tasks:16.0.1"
69+
firestoreImplementation "com.google.firebase:firebase-auth:16.1.0"
70+
firestoreImplementation "com.google.firebase:firebase-firestore:18.1.0"
71+
firestoreImplementation "com.google.truth:truth:0.43"
72+
73+
androidTestFirestoreImplementation "androidx.test:rules:1.1.0"
74+
androidTestFirestoreImplementation "junit:junit:4.12"
75+
}
76+
77+
apply plugin: "com.google.gms.google-services"
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.testing.common;
16+
17+
import android.app.Activity;
18+
import android.util.Log;
19+
import java.lang.reflect.InvocationTargetException;
20+
import java.lang.reflect.Method;
21+
22+
/**
23+
* A base class for smoke tests.
24+
*
25+
* <p>All smoke tests should extend this class. It contains the implementation for finding tests in
26+
* the app APK and running them. Test methods must be annotated with {@link
27+
* com.google.firebase.testing.common.SmokeTest} and must be in the test's primary activity.
28+
*
29+
* <p>This design enables test code to be built, obfuscated, and run like any other application's
30+
* code. Test methods may be fully defined in one source tree, instead of splitting them across the
31+
* two APKs used during testing.
32+
*/
33+
public abstract class SmokeTestBase {
34+
35+
private static final String TAG = "SmokeTestBase";
36+
37+
/**
38+
* Runs all smoke tests in the provided activity.
39+
*
40+
* <p>This method reflectively obtains all methods defined in the activity with the {@link
41+
* com.google.firebase.testing.common.SmokeTest} annotation. These are executed sequentially. Any
42+
* exception thrown by the test methods is rethrown here without wrapping.
43+
*/
44+
protected void runSmokeTests(Activity instance) throws Exception {
45+
try {
46+
Method[] methods = instance.getClass().getMethods();
47+
Log.d(TAG, "Searching for SmokeTest methods.");
48+
for (Method method : methods) {
49+
if (method.getAnnotation(SmokeTest.class) == null) {
50+
Log.d(TAG, "Skipping non-SmokeTest method, " + method.getName() + ".");
51+
continue;
52+
}
53+
54+
Log.d(TAG, "Preparing to run method, " + method.getName() + ".");
55+
try {
56+
method.invoke(instance);
57+
Log.d(TAG, "Test method complete.");
58+
} catch (InvocationTargetException ex) {
59+
Throwable t = ex.getCause();
60+
if (t instanceof Exception) {
61+
throw (Exception) t;
62+
} else if (t instanceof Error) {
63+
throw (Error) t;
64+
}
65+
66+
throw new IllegalStateException("Test threw unexpected Throwable", t);
67+
}
68+
}
69+
} catch (Exception ex) {
70+
// TODO(allisonbm92): Catch more specific exceptions to provide better user experience.
71+
// An exception should only be caught here if there was a problem reflectively accessing the
72+
// test methods. This corresponds to a user configuration error.
73+
throw new IllegalStateException("Test framework error", ex);
74+
}
75+
76+
Log.d(TAG, "Finished test runs.");
77+
}
78+
}
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.testing.common;
16+
17+
import android.util.Log;
18+
import androidx.test.espresso.Espresso;
19+
import androidx.test.espresso.IdlingRegistry;
20+
import androidx.test.espresso.idling.CountingIdlingResource;
21+
import com.google.android.gms.tasks.RuntimeExecutionException;
22+
import com.google.android.gms.tasks.Task;
23+
24+
/**
25+
* Smoke test utilities in the test APK.
26+
*
27+
* <p>The purpose of this class is to avoid adding testing libraries to the application APK that
28+
* contains Firebase code and will be obfuscated. The {@code TestUtil} class in the application APK
29+
* calls the implementations in this class reflectively.
30+
*/
31+
public final class TestApkUtil {
32+
33+
private static final String TAG = "TestApkUtil";
34+
35+
private TestApkUtil() {}
36+
37+
/**
38+
* Waits for the task to complete successfully.
39+
*
40+
* <p>This method will block the current thread and return the result of the task. It will rethrow
41+
* any expection thrown by the task without wrapping.
42+
*/
43+
public static <T> T waitForSuccess(Task<T> task) throws Exception {
44+
Log.d(TAG, "Begin waiting for task, " + task + ".");
45+
CountingIdlingResource idler = new CountingIdlingResource("Task");
46+
47+
idler.increment();
48+
IdlingRegistry.getInstance().register(idler);
49+
task.addOnCompleteListener(
50+
t -> {
51+
Log.d(TAG, "Task completed. Success: " + t.isSuccessful() + ".");
52+
idler.decrement();
53+
});
54+
55+
Log.d(TAG, "Wait now.");
56+
Espresso.onIdle();
57+
Log.d(TAG, "End wait.");
58+
59+
IdlingRegistry.getInstance().unregister(idler);
60+
try {
61+
return task.getResult();
62+
} catch (RuntimeExecutionException ex) {
63+
Throwable t = ex.getCause();
64+
if (t instanceof Exception) {
65+
throw (Exception) t;
66+
} else if (t instanceof Error) {
67+
throw (Error) t;
68+
}
69+
70+
throw new IllegalStateException("Task threw unexpected Throwable", t);
71+
}
72+
}
73+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2018 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.firebase.testing.firestore;
16+
17+
import android.util.Log;
18+
import androidx.test.rule.ActivityTestRule;
19+
import androidx.test.runner.AndroidJUnit4;
20+
import com.google.firebase.testing.common.SmokeTestBase;
21+
import org.junit.Rule;
22+
import org.junit.Test;
23+
import org.junit.runner.RunWith;
24+
25+
@RunWith(AndroidJUnit4.class)
26+
public final class FirestoreTest extends SmokeTestBase {
27+
28+
private static final String TAG = "FirestoreTest";
29+
30+
@Rule
31+
public final ActivityTestRule<FirestoreActivity> atr =
32+
new ActivityTestRule<>(FirestoreActivity.class);
33+
34+
@Test
35+
public void runSmokeTests() throws Exception {
36+
Log.d(TAG, "Initializing activity.");
37+
FirestoreActivity instance = atr.getActivity();
38+
39+
// Delegate to base class.
40+
runSmokeTests(instance);
41+
}
42+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.google.firebase.testing">
3+
4+
<uses-permission android:name="android.permission.INTERNET" />
5+
6+
<application>
7+
<activity android:name="com.google.firebase.testing.firestore.FirestoreActivity">
8+
<intent-filter>
9+
<action android:name="android.intent.action.MAIN" />
10+
<category android:name="android.intent.category.LAUNCHER" />
11+
</intent-filter>
12+
</activity>
13+
</application>
14+
</manifest>

0 commit comments

Comments
 (0)