Skip to content

Commit 8757b15

Browse files
Add tests for FragmentStateMonitor
1 parent 707d28e commit 8757b15

File tree

4 files changed

+125
-22
lines changed

4 files changed

+125
-22
lines changed

firebase-perf/src/main/java/com/google/firebase/perf/application/FragmentStateMonitor.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,7 @@
3131
public class FragmentStateMonitor extends FragmentManager.FragmentLifecycleCallbacks {
3232
private static final AndroidLogger logger = AndroidLogger.getInstance();
3333
private final WeakHashMap<Fragment, Trace> fragmentToTraceMap = new WeakHashMap<>();
34-
private final WeakHashMap<Fragment, FrameMetricsCalculator.FrameMetrics> fragmentToMetricsMap =
35-
new WeakHashMap<>();
34+
private final WeakHashMap<Fragment, FrameMetrics> fragmentToMetricsMap = new WeakHashMap<>();
3635
private final Clock clock;
3736
private final TransportManager transportManager;
3837
private final AppStateMonitor appStateMonitor;
@@ -90,8 +89,7 @@ public void onFragmentPaused(@NonNull FragmentManager fm, @NonNull Fragment f) {
9089
// Stop Fragment screen trace
9190
logger.debug("FragmentMonitor %s.onFragmentPaused ", f.getClass().getSimpleName());
9291
if (!fragmentToTraceMap.containsKey(f)) {
93-
logger.warn(
94-
"FragmentMonitor: missed a fragment trace from %s", f.getClass().getSimpleName());
92+
logger.warn("FragmentMonitor: missed a fragment trace from %s", f.getClass().getSimpleName());
9593
return;
9694
}
9795

@@ -129,4 +127,9 @@ public void onFragmentPaused(@NonNull FragmentManager fm, @NonNull Fragment f) {
129127
WeakHashMap<Fragment, Trace> getFragmentToTraceMap() {
130128
return fragmentToTraceMap;
131129
}
130+
131+
@VisibleForTesting
132+
WeakHashMap<Fragment, FrameMetrics> getFragmentToMetricsMap() {
133+
return fragmentToMetricsMap;
134+
}
132135
}

firebase-perf/src/main/java/com/google/firebase/perf/metrics/FrameMetricsCalculator.java

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,17 @@
1+
// Copyright 2022 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+
115
package com.google.firebase.perf.metrics;
216

317
import android.util.SparseIntArray;
@@ -6,13 +20,19 @@
620
import androidx.core.app.FrameMetricsAggregator;
721
import com.google.firebase.perf.util.Constants;
822

23+
/**
24+
* FrameMetricsCalculator helps calculate total frames, slow frames, and frozen frames from {@link
25+
* FrameMetricsAggregator}
26+
*
27+
* @hide
28+
*/
929
public class FrameMetricsCalculator {
1030
public static class FrameMetrics {
1131
int totalFrames = 0;
1232
int slowFrames = 0;
1333
int frozenFrames = 0;
1434

15-
FrameMetrics(int totalFrames, int slowFrames, int frozenFrames) {
35+
public FrameMetrics(int totalFrames, int slowFrames, int frozenFrames) {
1636
this.totalFrames = totalFrames;
1737
this.slowFrames = slowFrames;
1838
this.frozenFrames = frozenFrames;
@@ -32,7 +52,9 @@ public int getTotalFrames() {
3252
}
3353

3454
/**
35-
* Calculate total frames, slow frames, and frozen frames from arr.
55+
* Calculate total frames, slow frames, and frozen frames from SparseIntArray[] recorded by {@link
56+
* FrameMetricsAggregator}. For a given non-null SparseIntArray, the results stored are the number
57+
* of samples at each millisecond value (rounded).
3658
*
3759
* @param arr the metrics data collected by {@link FrameMetricsAggregator}
3860
* @return the frame metrics

firebase-perf/src/test/java/com/google/firebase/perf/application/FragmentStateMonitorTest.java

Lines changed: 83 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,20 +21,25 @@
2121
import static org.mockito.Mockito.spy;
2222
import static org.mockito.Mockito.times;
2323
import static org.mockito.Mockito.verify;
24+
import static org.mockito.Mockito.when;
2425
import static org.mockito.MockitoAnnotations.initMocks;
2526

2627
import android.app.Activity;
28+
import android.util.SparseIntArray;
2729
import androidx.appcompat.app.AppCompatActivity;
2830
import androidx.core.app.FrameMetricsAggregator;
2931
import androidx.fragment.app.Fragment;
3032
import androidx.fragment.app.FragmentManager;
3133
import com.google.firebase.perf.FirebasePerformanceTestBase;
3234
import com.google.firebase.perf.config.ConfigResolver;
3335
import com.google.firebase.perf.config.DeviceCacheManager;
36+
import com.google.firebase.perf.metrics.FrameMetricsCalculator;
3437
import com.google.firebase.perf.metrics.Trace;
3538
import com.google.firebase.perf.transport.TransportManager;
3639
import com.google.firebase.perf.util.Clock;
40+
import com.google.firebase.perf.util.Constants;
3741
import com.google.firebase.perf.util.Timer;
42+
import com.google.firebase.perf.v1.ApplicationProcessState;
3843
import com.google.firebase.perf.v1.TraceMetric;
3944
import com.google.testing.timing.FakeDirectExecutorService;
4045
import java.util.WeakHashMap;
@@ -66,10 +71,16 @@ public class FragmentStateMonitorTest extends FirebasePerformanceTestBase {
6671
private long currentTime = 0;
6772
private static final String longFragmentName =
6873
"_st_NeverGonnaGiveYouUpNeverGonnaLetYouDownNeverGonnaRunAroundAndDesertYouNeverGonnaMakeYouCryNeverGonnaSayGoodbyeNeverGonnaTellALieAndHurtYou";
69-
70-
private Activity activity1;
7174
private ConfigResolver configResolver;
7275

76+
/**
77+
* Array of SparseIntArray to mock the return value from {@link
78+
* FrameMetricsAggregator#getMetrics()}
79+
*/
80+
private SparseIntArray[] fmaMetrics1 = new SparseIntArray[1];
81+
82+
private SparseIntArray[] fmaMetrics2 = new SparseIntArray[1];
83+
7384
@Before
7485
public void setUp() {
7586
currentTime = 0;
@@ -84,27 +95,85 @@ public void setUp() {
8495
ConfigResolver spyConfigResolver = spy(configResolver);
8596
doReturn(true).when(spyConfigResolver).isPerformanceMonitoringEnabled();
8697
this.configResolver = spyConfigResolver;
98+
99+
// fmaMetrics1 should have 1+3+1=5 total frames, 3+1=4 slow frames, and 1 frozen frames.
100+
SparseIntArray sparseIntArray = new SparseIntArray();
101+
sparseIntArray.append(1, 1);
102+
sparseIntArray.append(17, 3);
103+
sparseIntArray.append(800, 1);
104+
fmaMetrics1[FrameMetricsAggregator.TOTAL_INDEX] = sparseIntArray;
105+
106+
// fmaMetrics2 should have 5+5+4=14 total frames, 5+4=9 slow frames, and 4 frozen frames.
107+
sparseIntArray = new SparseIntArray();
108+
sparseIntArray.append(1, 5);
109+
sparseIntArray.append(18, 5);
110+
sparseIntArray.append(800, 4);
111+
fmaMetrics2[FrameMetricsAggregator.TOTAL_INDEX] = sparseIntArray;
87112
}
88113

89114
/************ Trace Creation Tests ****************/
90115

91116
@Test
92-
public void lifecycleCallbacks_logFragmentScreenTrace() {
117+
public void lifecycleCallbacks_differentFrameMetricsCapturedByFma_logFragmentScreenTrace() {
93118
FragmentStateMonitor monitor =
94119
new FragmentStateMonitor(clock, mockTransportManager, appStateMonitor, fma);
120+
when(fma.getMetrics()).thenReturn(fmaMetrics1);
95121
monitor.onFragmentResumed(mockFragmentManager, mockFragment);
96122
verify(mockTransportManager, times(0)).log(any(TraceMetric.class), any());
97123

124+
when(fma.getMetrics()).thenReturn(fmaMetrics2);
98125
monitor.onFragmentPaused(mockFragmentManager, mockFragment);
99126
verify(mockTransportManager, times(1)).log(any(TraceMetric.class), any());
100127

128+
when(fma.getMetrics()).thenReturn(fmaMetrics1);
101129
monitor.onFragmentResumed(mockFragmentManager, mockFragment);
102130
verify(mockTransportManager, times(1)).log(any(TraceMetric.class), any());
103131

132+
when(fma.getMetrics()).thenReturn(fmaMetrics2);
104133
monitor.onFragmentPaused(mockFragmentManager, mockFragment);
105134
verify(mockTransportManager, times(2)).log(any(TraceMetric.class), any());
106135
}
107136

137+
@Test
138+
public void lifecycleCallbacks_sameFrameMetricsCapturedByFma_dropFragmentScreenTrace() {
139+
FragmentStateMonitor monitor =
140+
new FragmentStateMonitor(clock, mockTransportManager, appStateMonitor, fma);
141+
when(fma.getMetrics()).thenReturn(fmaMetrics1);
142+
monitor.onFragmentResumed(mockFragmentManager, mockFragment);
143+
verify(mockTransportManager, times(0)).log(any(TraceMetric.class), any());
144+
145+
when(fma.getMetrics()).thenReturn(fmaMetrics1);
146+
monitor.onFragmentPaused(mockFragmentManager, mockFragment);
147+
verify(mockTransportManager, times(0)).log(any(TraceMetric.class), any());
148+
}
149+
150+
@Test
151+
public void
152+
lifecycleCallbacks_differentFrameMetricsCapturedByFma_logFragmentScreenTraceWithCorrectFrames() {
153+
FragmentStateMonitor monitor =
154+
new FragmentStateMonitor(clock, mockTransportManager, appStateMonitor, fma);
155+
when(fma.getMetrics()).thenReturn(fmaMetrics1);
156+
monitor.onFragmentResumed(mockFragmentManager, mockFragment);
157+
verify(mockTransportManager, times(0)).log(any(TraceMetric.class), any());
158+
159+
when(fma.getMetrics()).thenReturn(fmaMetrics2);
160+
monitor.onFragmentPaused(mockFragmentManager, mockFragment);
161+
162+
// fmaMetrics1 has 1+3+1=5 total frames, 3+1=4 slow frames, and 1 frozen frames
163+
// fmaMetrics2 has 5+5+4=14 total frames, 5+4=9 slow frames, and 4 frozen frames
164+
// we expect the trace to have 14-5=9 total frames, 9-4=5 slow frames, and 4-1=3 frozen
165+
// frames.
166+
verify(mockTransportManager, times(1))
167+
.log(argTraceMetric.capture(), nullable(ApplicationProcessState.class));
168+
TraceMetric metric = argTraceMetric.getValue();
169+
Assert.assertEquals(
170+
9, (long) metric.getCountersMap().get(Constants.CounterNames.FRAMES_TOTAL.toString()));
171+
Assert.assertEquals(
172+
5, (long) metric.getCountersMap().get(Constants.CounterNames.FRAMES_SLOW.toString()));
173+
Assert.assertEquals(
174+
3, (long) metric.getCountersMap().get(Constants.CounterNames.FRAMES_FROZEN.toString()));
175+
}
176+
108177
@Test
109178
public void lifecycleCallbacks_cleansUpMap_duringActivityTransitions() {
110179
// Simulate call order of activity + fragment lifecycle events
@@ -113,23 +182,29 @@ public void lifecycleCallbacks_cleansUpMap_duringActivityTransitions() {
113182
FragmentStateMonitor fragmentMonitor =
114183
new FragmentStateMonitor(clock, mockTransportManager, appStateMonitor, fma);
115184
doReturn(true).when(appStateMonitor).isScreenTraceSupported();
116-
WeakHashMap<Fragment, Trace> map = fragmentMonitor.getFragmentToTraceMap();
185+
WeakHashMap<Fragment, Trace> fragmentToTraceMap = fragmentMonitor.getFragmentToTraceMap();
186+
WeakHashMap<Fragment, FrameMetricsCalculator.FrameMetrics> fragmentToMetricsMap =
187+
fragmentMonitor.getFragmentToMetricsMap();
117188
// Activity_A onCreate registers FragmentStateMonitor, then:
118189
appStateMonitor.onActivityStarted(mockActivity);
119-
Assert.assertEquals(0, map.size());
190+
Assert.assertEquals(0, fragmentToTraceMap.size());
191+
Assert.assertEquals(0, fragmentToMetricsMap.size());
120192
appStateMonitor.onActivityResumed(mockActivity);
121193
fragmentMonitor.onFragmentResumed(mockFragmentManager, mockFragment);
122-
Assert.assertEquals(1, map.size());
194+
Assert.assertEquals(1, fragmentToTraceMap.size());
195+
Assert.assertEquals(1, fragmentToMetricsMap.size());
123196
appStateMonitor.onActivityPaused(mockActivity);
124197
fragmentMonitor.onFragmentPaused(mockFragmentManager, mockFragment);
125-
Assert.assertEquals(0, map.size());
198+
Assert.assertEquals(0, fragmentToTraceMap.size());
199+
Assert.assertEquals(0, fragmentToMetricsMap.size());
126200
appStateMonitor.onActivityPostPaused(mockActivity);
127201
// Activity_B onCreate registers FragmentStateMonitor, then:
128202
appStateMonitor.onActivityStarted(mockActivityB);
129203
appStateMonitor.onActivityResumed(mockActivityB);
130204
fragmentMonitor.onFragmentResumed(mockFragmentManager, mockFragment);
131205
appStateMonitor.onActivityStopped(mockActivity);
132-
Assert.assertEquals(1, map.size());
206+
Assert.assertEquals(1, fragmentToTraceMap.size());
207+
Assert.assertEquals(1, fragmentToMetricsMap.size());
133208
}
134209

135210
@Test

firebase-perf/src/test/java/com/google/firebase/perf/metrics/FrameMetricsCalculatorTest.java

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33
import static com.google.common.truth.Truth.assertThat;
44

55
import android.util.SparseIntArray;
6-
76
import androidx.core.app.FrameMetricsAggregator;
8-
97
import org.junit.Test;
108
import org.junit.runner.RunWith;
119
import org.robolectric.RobolectricTestRunner;
@@ -14,7 +12,7 @@
1412
@RunWith(RobolectricTestRunner.class)
1513
public class FrameMetricsCalculatorTest {
1614
@Test
17-
public void calculateFrameMetrics_inputIsNull_returnsFrameMetricsWithAllZeros() {
15+
public void calculateFrameMetrics_sparseIntArrayIsNull_returnsFrameMetricsWithAllZeros() {
1816
SparseIntArray[] arr = new SparseIntArray[1];
1917
arr[FrameMetricsAggregator.TOTAL_INDEX] = null;
2018
FrameMetricsCalculator.FrameMetrics metrics = FrameMetricsCalculator.calculateFrameMetrics(arr);
@@ -25,7 +23,8 @@ public void calculateFrameMetrics_inputIsNull_returnsFrameMetricsWithAllZeros()
2523

2624
@Test
2725
public void calculateFrameMetrics_validSparseIntArray_returnsCorrectFrameMetrics() {
28-
// For a given non-null SparseIntArray, the results stored are the number of samples at each millisecond value (rounded).
26+
// For a given non-null SparseIntArray, the results stored are the number of samples at each
27+
// millisecond value (rounded).
2928
// Slow frames are duration greater than 16ms and frozen frames are duration greater than 700ms.
3029
SparseIntArray sparseIntArray = new SparseIntArray();
3130
sparseIntArray.append(5, 3);
@@ -41,8 +40,10 @@ public void calculateFrameMetrics_validSparseIntArray_returnsCorrectFrameMetrics
4140
}
4241

4342
@Test
44-
public void calculateFrameMetrics_validSparseIntArrayWithoutFrozenFrames_returnsCorrectFrameMetrics() {
45-
// For a given non-null SparseIntArray, the results stored are the number of samples at each millisecond value (rounded).
43+
public void
44+
calculateFrameMetrics_validSparseIntArrayWithoutFrozenFrames_returnsCorrectFrameMetrics() {
45+
// For a given non-null SparseIntArray, the results stored are the number of samples at each
46+
// millisecond value (rounded).
4647
// Slow frames are duration greater than 16ms and frozen frames are duration greater than 700ms.
4748
SparseIntArray sparseIntArray = new SparseIntArray();
4849
sparseIntArray.append(5, 3);
@@ -57,8 +58,10 @@ public void calculateFrameMetrics_validSparseIntArrayWithoutFrozenFrames_returns
5758
}
5859

5960
@Test
60-
public void calculateFrameMetrics_validSparseIntArrayWithoutSlowFrames_returnsCorrectFrameMetrics() {
61-
// For a given non-null SparseIntArray, the results stored are the number of samples at each millisecond value (rounded).
61+
public void
62+
calculateFrameMetrics_validSparseIntArrayWithoutSlowFrames_returnsCorrectFrameMetrics() {
63+
// For a given non-null SparseIntArray, the results stored are the number of samples at each
64+
// millisecond value (rounded).
6265
// Slow frames are duration greater than 16ms and frozen frames are duration greater than 700ms.
6366
SparseIntArray sparseIntArray = new SparseIntArray();
6467
sparseIntArray.append(5, 3);

0 commit comments

Comments
 (0)