Skip to content

[Fragment Performance] e2e test app fragments #3210

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

Merged
merged 11 commits into from
Dec 15, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
15 changes: 12 additions & 3 deletions firebase-perf/e2e-app/e2e-app.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ firebaseTestLab {
}

android {
compileSdkVersion 28
compileSdkVersion 31

// Specifies the build type that the Android plugin should use to run the instrumentation tests.
// TL;DR Configure what build types should Firebase Test Lab (FTL) runs your tests on.
Expand All @@ -52,7 +52,7 @@ android {
// https://developer.android.com/studio/build/application-id.html
applicationId "com.google.firebase.testing.fireperf"

minSdkVersion 16
minSdkVersion 18
targetSdkVersion 28
versionCode 3
versionName "3.0"
Expand Down Expand Up @@ -87,9 +87,16 @@ dependencies {
implementation "com.google.android.gms:play-services-tasks:17.2.0"
implementation "com.google.guava:guava:29.0-android"
implementation 'androidx.annotation:annotation:1.1.0'
implementation "androidx.appcompat:appcompat:1.2.0"
implementation 'androidx.multidex:multidex:2.0.1'
implementation "androidx.recyclerview:recyclerview:1.1.0"
implementation 'androidx.appcompat:appcompat:1.4.0'
implementation 'com.google.android.material:material:1.4.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.2'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.4.0'
implementation 'androidx.navigation:navigation-fragment:2.3.5'
implementation 'androidx.navigation:navigation-ui:2.3.5'
implementation 'com.google.android.material:material:1.4.0'

// 3rd party Deps
implementation 'com.squareup.okhttp3:okhttp:4.9.0'
Expand All @@ -102,6 +109,8 @@ dependencies {
androidTestImplementation 'androidx.test:runner:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
androidTestImplementation "androidx.test.espresso:espresso-contrib:3.3.0"
androidTestImplementation 'androidx.test.uiautomator:uiautomator:2.2.0'
androidTestImplementation "com.google.truth:truth:$googleTruthVersion"
}

// This allows the app to connect to Firebase on the CI.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
// Copyright 2021 Google LLC
//
// 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 com.google.firebase.testing.fireperf;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static com.google.common.truth.Truth.assertThat;

import android.app.Activity;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle.State;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.test.core.app.ActivityScenario;
import androidx.test.ext.junit.rules.ActivityScenarioRule;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import com.google.firebase.testing.fireperf.ui.fast.FastFragment;
import com.google.firebase.testing.fireperf.ui.home.HomeFragment;
import com.google.firebase.testing.fireperf.ui.slow.SlowFragment;
import java.util.Arrays;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

/**
* Scrolls a slow RecyclerView all the way to the end, which should generate slow and frozen frame
* data.
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
public class FirebasePerformanceFragmentScreenTracesTest {

@Rule
public ActivityScenarioRule<FragmentActivity> activityRule =
new ActivityScenarioRule<>(FragmentActivity.class);

@Test
public void scrollAndCycleThroughAllFragments() throws InterruptedException {
activityRule
.getScenario()
.onActivity(
activity -> {
((AppCompatActivity) activity)
.getSupportFragmentManager()
.registerFragmentLifecycleCallbacks(
new FragmentManager.FragmentLifecycleCallbacks() {
@Override
public void onFragmentResumed(
@NonNull FragmentManager fm, @NonNull Fragment f) {
super.onFragmentResumed(fm, f);
notifyNavigationLock();
}
},
true);
});
scrollRecyclerViewToEnd(HomeFragment.NUM_LIST_ITEMS, R.id.rv_numbers_home);
activityRule.getScenario().onActivity(new NavigateAction(R.id.navigation_fast));
blockUntilNavigationDone();
scrollRecyclerViewToEnd(FastFragment.NUM_LIST_ITEMS, R.id.rv_numbers_fast);
activityRule.getScenario().onActivity(new NavigateAction(R.id.navigation_slow));
blockUntilNavigationDone();
scrollRecyclerViewToEnd(SlowFragment.NUM_LIST_ITEMS, R.id.rv_numbers_slow);
assertThat(activityRule.getScenario().getState())
.isIn(Arrays.asList(State.CREATED, State.RESUMED));
activityRule.getScenario().moveToState(State.CREATED);
}

private void scrollRecyclerViewToEnd(int itemCount, int viewId) {
int currItemCount = 0;

while (currItemCount < itemCount) {
onView(withId(viewId)).perform(scrollToPosition(currItemCount));
currItemCount += 5;
}
}

private synchronized void blockUntilNavigationDone() throws InterruptedException {
wait();
}

private synchronized void notifyNavigationLock() {
notify();
}

static class NavigateAction implements ActivityScenario.ActivityAction {
private final int destinationId;

public NavigateAction(int destinationId) {
this.destinationId = destinationId;
}

@Override
public void perform(Activity activity) {
NavController navController =
Navigation.findNavController(activity, R.id.nav_host_fragment_activity_fragment);
navController.navigate(destinationId);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,17 @@
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.contrib.RecyclerViewActions.scrollToPosition;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static com.google.common.truth.Truth.assertThat;

import androidx.recyclerview.widget.RecyclerView;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.rule.ActivityTestRule;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
import org.junit.After;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand All @@ -33,6 +39,7 @@
@RunWith(AndroidJUnit4.class)
@MediumTest
public class FirebasePerformanceScreenTracesTest {
private static final int LAUNCH_TIMEOUT = 5000;

@Rule
public ActivityTestRule<FirebasePerfScreenTracesActivity> activityRule =
Expand All @@ -41,6 +48,16 @@ public class FirebasePerformanceScreenTracesTest {
/* initialTouchMode= */ false,
/* launchActivity= */ true);

@After
public void pressHome_toTriggerSendScreenTrace() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are we triggering an event sent for every app backgrounding? I thought we were dispatching events scheduled every 30 seconds managed by Firelog service. @jeremyjiang-dev

UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
boolean success = device.pressHome();
// Wait for launcher
final String launcherPackage = device.getLauncherPackageName();
assertThat(launcherPackage).isNotNull();
device.wait(Until.hasObject(By.pkg(launcherPackage).depth(0)), LAUNCH_TIMEOUT);
}

@Test
public void scrollRecyclerViewToEnd() {
RecyclerView recyclerView = activityRule.getActivity().findViewById(R.id.rv_numbers);
Expand Down
73 changes: 36 additions & 37 deletions firebase-perf/e2e-app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,39 +1,38 @@
<?xml version="1.0" encoding="utf-8"?> <!-- Copyright 2020 Google Inc. All Rights Reserved. -->

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.google.firebase.testing.fireperf">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:debuggable="true"
android:label="FirePerf E2E Test App"
android:usesCleartextTraffic="true"
tools:ignore="HardcodedDebugMode,UnusedAttribute">

<activity
android:label="FirebasePerfActivity"
android:name=".FirebasePerfActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<activity
android:label="FirebasePerfScreenTracesActivity"
android:name=".FirebasePerfScreenTracesActivity" />

<meta-data
android:name="firebase_performance_logcat_enabled"
android:value="true" />

<meta-data
android:name="sessions_sampling_percentage"
android:value="100.0" />

</application>

</manifest>
xmlns:tools="http://schemas.android.com/tools"
package="com.google.firebase.testing.fireperf">

<uses-permission android:name="android.permission.INTERNET" />

<application
android:allowBackup="true"
android:debuggable="true"
android:label="FirePerf E2E Test App"
android:usesCleartextTraffic="true"
tools:ignore="HardcodedDebugMode,UnusedAttribute">
<activity
android:name=".FragmentActivity"
android:label="@string/title_activity_fragment"
android:theme="@style/Theme.MaterialComponents.DayNight.DarkActionBar" />
<activity
android:name=".FirebasePerfActivity"
android:label="FirebasePerfActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".FirebasePerfScreenTracesActivity"
android:label="FirebasePerfScreenTracesActivity" />

<meta-data
android:name="firebase_performance_logcat_enabled"
android:value="true" />
<meta-data
android:name="sessions_sampling_percentage"
android:value="100.0" />
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ protected void onCreate(Bundle savedInstanceState) {

RecyclerView numbersList = findViewById(R.id.rv_numbers);
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
ListAdapter listAdapter = new ListAdapter(NUM_LIST_ITEMS);
ListAdapter listAdapter = new SlowListAdapter(NUM_LIST_ITEMS);

numbersList.setLayoutManager(layoutManager);
numbersList.setHasFixedSize(true);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
// Copyright 2021 Google LLC
//
// 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 com.google.firebase.testing.fireperf;

import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import androidx.navigation.NavController;
import androidx.navigation.Navigation;
import androidx.navigation.ui.AppBarConfiguration;
import androidx.navigation.ui.NavigationUI;
import com.google.android.material.bottomnavigation.BottomNavigationView;

public class FragmentActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment);
BottomNavigationView navView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
AppBarConfiguration appBarConfiguration =
new AppBarConfiguration.Builder(
R.id.navigation_home, R.id.navigation_fast, R.id.navigation_slow)
.build();
NavController navController =
Navigation.findNavController(this, R.id.nav_host_fragment_activity_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, appBarConfiguration);
NavigationUI.setupWithNavController(navView, navController);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@

package com.google.firebase.testing.fireperf;

import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
Expand All @@ -25,15 +24,14 @@
/** The Adapter for the ScreenTraces test ListView. */
public class ListAdapter extends RecyclerView.Adapter<ListAdapter.NumberViewHolder> {

private static final String LOG_TAG = ListAdapter.class.getSimpleName();
private final int numberOfItems;

/**
* Constructor for ListAdapter that accepts a number of items to display.
*
* @param numberOfItems Number of items to display in list
*/
ListAdapter(int numberOfItems) {
public ListAdapter(int numberOfItems) {
this.numberOfItems = numberOfItems;
}

Expand All @@ -47,16 +45,6 @@ public NumberViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {

@Override
public void onBindViewHolder(@NotNull NumberViewHolder holder, int position) {
try {
if (position % 15 == 0) {
Thread.sleep(900);
} else if (position % 5 == 0) {
Thread.sleep(50);
}
} catch (InterruptedException e) {
Log.e(LOG_TAG, e.getMessage(), e);
}

holder.bind(position);
}

Expand Down
Loading