Commit 62214d92 authored by Becky Zhou's avatar Becky Zhou Committed by Commit Bot

[Feed] Add unit tests for StreamLifecycleManager

Bug: 851977
Change-Id: I99de0abe80ea536a23ecdca2401dd3fd6b816955
Reviewed-on: https://chromium-review.googlesource.com/1101699
Commit-Queue: Becky Zhou <huayinz@chromium.org>
Reviewed-by: default avatarTheresa <twellington@chromium.org>
Reviewed-by: default avatarFilip Gorski <fgorski@chromium.org>
Cr-Commit-Position: refs/heads/master@{#568481}
parent 645cc6a8
...@@ -11,6 +11,7 @@ import com.google.android.libraries.feed.api.stream.Stream; ...@@ -11,6 +11,7 @@ import com.google.android.libraries.feed.api.stream.Stream;
import org.chromium.base.ActivityState; import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus; import org.chromium.base.ApplicationStatus;
import org.chromium.base.VisibleForTesting;
import org.chromium.chrome.browser.tab.EmptyTabObserver; import org.chromium.chrome.browser.tab.EmptyTabObserver;
import org.chromium.chrome.browser.tab.Tab; import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabObserver; import org.chromium.chrome.browser.tab.TabObserver;
...@@ -160,7 +161,7 @@ class StreamLifecycleManager implements ApplicationStatus.ActivityStateListener ...@@ -160,7 +161,7 @@ class StreamLifecycleManager implements ApplicationStatus.ActivityStateListener
/** Calls {@link Stream#onHide()}. */ /** Calls {@link Stream#onHide()}. */
private void hide() { private void hide() {
if (mStreamState == HIDDEN || mStreamState == DESTROYED) return; if (mStreamState == HIDDEN || mStreamState == CREATED || mStreamState == DESTROYED) return;
deactivate(); deactivate();
mStreamState = HIDDEN; mStreamState = HIDDEN;
...@@ -180,4 +181,9 @@ class StreamLifecycleManager implements ApplicationStatus.ActivityStateListener ...@@ -180,4 +181,9 @@ class StreamLifecycleManager implements ApplicationStatus.ActivityStateListener
ApplicationStatus.unregisterActivityStateListener(this); ApplicationStatus.unregisterActivityStateListener(this);
mStream.onDestroy(); mStream.onDestroy();
} }
@VisibleForTesting
TabObserver getTabObserverForTesting() {
return mTabObserver;
}
} }
...@@ -2201,8 +2201,10 @@ if (enable_offline_pages_harness) { ...@@ -2201,8 +2201,10 @@ if (enable_offline_pages_harness) {
} }
if (enable_feed_in_chrome) { if (enable_feed_in_chrome) {
chrome_junit_test_java_sources += chrome_junit_test_java_sources += [
[ "junit/src/org/chromium/chrome/browser/feed/FeedImageLoaderTest.java" ] "junit/src/org/chromium/chrome/browser/feed/FeedImageLoaderTest.java",
"junit/src/org/chromium/chrome/browser/feed/StreamLifecycleManagerTest.java",
]
chrome_test_java_sources += feed_conformance_test_sources chrome_test_java_sources += feed_conformance_test_sources
} }
......
# For tests that apply to parts of feature implemented in components with a thin Java layer (e.g. host APIs).
file://components/feed/OWNERS file://components/feed/OWNERS
# For tests affecting Android specific parts (e.g. Stream).
file://chrome/android/feed/OWNERS
// Copyright 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.chrome.browser.feed;
import static org.mockito.AdditionalMatchers.or;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.Activity;
import android.os.Bundle;
import android.support.test.filters.SmallTest;
import com.google.android.libraries.feed.api.stream.Stream;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.InOrder;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.chromium.base.ActivityState;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.tab.Tab;
/**
* Unit tests for {@link StreamLifecycleManager}.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class StreamLifecycleManagerTest {
@Mock
private Activity mActivity;
@Mock
private Tab mTab;
@Mock
private Stream mStream;
private StreamLifecycleManager mStreamLifecycleManager;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.CREATED);
mStreamLifecycleManager = new StreamLifecycleManager(mStream, mActivity, mTab);
verify(mStream, times(1)).onCreate(or(any(Bundle.class), isNull()));
}
@Test
@SmallTest
public void testShow() {
// Verify that onShow is not called before activity started.
when(mTab.isHidden()).thenReturn(false);
when(mTab.isUserInteractable()).thenReturn(true);
mStreamLifecycleManager.getTabObserverForTesting().onShown(mTab);
verify(mStream, times(0)).onShow();
// Verify that onShow is not called when Tab is hidden.
when(mTab.isHidden()).thenReturn(true);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
verify(mStream, times(0)).onShow();
// Verify that onShow is called when Tab is shown and activity is started.
when(mTab.isHidden()).thenReturn(false);
mStreamLifecycleManager.getTabObserverForTesting().onShown(mTab);
verify(mStream, times(1)).onShow();
// When the Stream is shown, it won't call Stream#onShow() again.
mStreamLifecycleManager.getTabObserverForTesting().onShown(mTab);
verify(mStream, times(1)).onShow();
}
@Test
@SmallTest
public void testActivate() {
// Verify that stream is not active before activity resumed.
when(mTab.isHidden()).thenReturn(false);
when(mTab.isUserInteractable()).thenReturn(true);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
verify(mStream, times(1)).onShow();
verify(mStream, times(0)).onActive();
// Verify that stream is not active before tab is user interactable.
when(mTab.isUserInteractable()).thenReturn(false);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
verify(mStream, times(1)).onShow();
verify(mStream, times(0)).onActive();
// Verify that stream is active when tab is user interactable and activity is resumed.
when(mTab.isUserInteractable()).thenReturn(true);
mStreamLifecycleManager.getTabObserverForTesting().onInteractabilityChanged(true);
verify(mStream, times(1)).onShow();
verify(mStream, times(1)).onActive();
// When the Stream is active, it won't call Stream#onShow() again.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
verify(mStream, times(1)).onShow();
// When the Stream is active, it won't call Stream#onActive() again.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
verify(mStream, times(1)).onActive();
}
@Test
@SmallTest
public void testActivateAfterCreateAndHideAfterActivate() {
// Activate the stream from created state.
InOrder inOrder = Mockito.inOrder(mStream);
when(mTab.isHidden()).thenReturn(false);
when(mTab.isUserInteractable()).thenReturn(true);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
inOrder.verify(mStream).onShow();
inOrder.verify(mStream).onActive();
verify(mStream, times(1)).onShow();
verify(mStream, times(1)).onActive();
// Verify that the stream is deactivated before hidden.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STOPPED);
inOrder.verify(mStream).onInactive();
inOrder.verify(mStream).onHide();
verify(mStream, times(1)).onShow();
verify(mStream, times(1)).onActive();
verify(mStream, times(1)).onInactive();
verify(mStream, times(1)).onHide();
}
@Test
@SmallTest
public void testDeactivate() {
// Verify that the Stream cannot be set inactive from created.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
verify(mStream, times(0)).onInactive();
// Show the stream.
when(mTab.isHidden()).thenReturn(false);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
verify(mStream, times(1)).onShow();
// Verify that the Stream cannot be set inactive from shown.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
verify(mStream, times(0)).onInactive();
// Activate the stream.
when(mTab.isUserInteractable()).thenReturn(true);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
verify(mStream, times(1)).onActive();
// Verify that the Stream can be set inactive from active on activity paused.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
verify(mStream, times(1)).onInactive();
// When the Stream is inactive, it won't call Stream#onInactive() again.
mStreamLifecycleManager.getTabObserverForTesting().onInteractabilityChanged(false);
verify(mStream, times(1)).onInactive();
// Verify that the Stream cannot be set shown from inactive.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
verify(mStream, times(1)).onShow();
// Verify that the Stream can be set active from inactive.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
verify(mStream, times(2)).onActive();
// Verify that the Stream can be set inactive from active on tab interactability changed.
mStreamLifecycleManager.getTabObserverForTesting().onInteractabilityChanged(false);
verify(mStream, times(2)).onInactive();
}
@Test
@SmallTest
public void testHideFromActivityStopped() {
// Activate the Stream.
when(mTab.isHidden()).thenReturn(false);
when(mTab.isUserInteractable()).thenReturn(true);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
verify(mStream, times(1)).onShow();
verify(mStream, times(1)).onActive();
// Deactivate the Stream.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
verify(mStream, times(1)).onInactive();
// Verify that the Stream can be set hidden from inactive.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STOPPED);
verify(mStream, times(1)).onHide();
// Verify that the Stream cannot be set inactive from hidden.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
verify(mStream, times(1)).onInactive();
// When the Stream is hidden, it won't call Stream#onHide() again.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STOPPED);
verify(mStream, times(1)).onHide();
}
@Test
@SmallTest
public void testHideFromTabHiddenAfterShow() {
// Show the stream.
when(mTab.isHidden()).thenReturn(false);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
verify(mStream, times(1)).onShow();
// Verify that onActive and onInactive are skipped when Stream is set hidden from shown.
mStreamLifecycleManager.getTabObserverForTesting().onHidden(mTab);
verify(mStream, times(1)).onShow();
verify(mStream, times(0)).onActive();
verify(mStream, times(0)).onInactive();
verify(mStream, times(1)).onHide();
}
@Test
@SmallTest
public void testDestroy() {
// Verify that Stream#onDestroy is called on activity destroyed.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.DESTROYED);
verify(mStream, times(1)).onDestroy();
}
@Test
@SmallTest
public void testDestroyAfterCreate() {
// After the Stream is destroyed, lifecycle methods should never be called. Directly calling
// destroy here to simulate destroy() being called on FeedNewTabPage destroyed.
mStreamLifecycleManager.destroy();
verify(mStream, times(1)).onDestroy();
// Verify that lifecycle methods are not called after destroy.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STOPPED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.DESTROYED);
verify(mStream, times(0)).onShow();
verify(mStream, times(0)).onActive();
verify(mStream, times(0)).onInactive();
verify(mStream, times(0)).onHide();
verify(mStream, times(1)).onDestroy();
}
@Test
@SmallTest
public void testDestroyAfterActivate() {
InOrder inOrder = Mockito.inOrder(mStream);
when(mTab.isHidden()).thenReturn(false);
when(mTab.isUserInteractable()).thenReturn(true);
// Activate the Stream.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
inOrder.verify(mStream).onShow();
inOrder.verify(mStream).onActive();
verify(mStream, times(1)).onShow();
verify(mStream, times(1)).onActive();
// Verify that onInactive and onHide is called before onDestroy.
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.DESTROYED);
inOrder.verify(mStream).onInactive();
inOrder.verify(mStream).onHide();
inOrder.verify(mStream).onDestroy();
verify(mStream, times(1)).onInactive();
verify(mStream, times(1)).onHide();
verify(mStream, times(1)).onDestroy();
}
@Test
@SmallTest
public void testFullActivityLifecycle() {
InOrder inOrder = Mockito.inOrder(mStream);
when(mTab.isHidden()).thenReturn(false);
when(mTab.isUserInteractable()).thenReturn(true);
// On activity start and resume (simulates app become foreground).
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
inOrder.verify(mStream).onShow();
inOrder.verify(mStream).onActive();
verify(mStream, times(1)).onShow();
verify(mStream, times(1)).onActive();
// On activity pause and then resume (simulates multi-window mode).
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
inOrder.verify(mStream).onInactive();
inOrder.verify(mStream).onActive();
verify(mStream, times(1)).onInactive();
verify(mStream, times(2)).onActive();
// On activity stop (simulates app switched to background).
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STOPPED);
inOrder.verify(mStream).onInactive();
inOrder.verify(mStream).onHide();
verify(mStream, times(2)).onInactive();
verify(mStream, times(1)).onHide();
// On activity start (simulates app switched back to foreground).
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
inOrder.verify(mStream).onShow();
inOrder.verify(mStream).onActive();
verify(mStream, times(2)).onShow();
verify(mStream, times(3)).onActive();
// On activity pause, stop, and destroy (simulates app removed from Android recents).
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.PAUSED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STOPPED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.DESTROYED);
inOrder.verify(mStream).onInactive();
inOrder.verify(mStream).onHide();
inOrder.verify(mStream).onDestroy();
verify(mStream, times(3)).onInactive();
verify(mStream, times(2)).onHide();
verify(mStream, times(1)).onDestroy();
}
@Test
@SmallTest
public void testFullTabLifecycle() {
InOrder inOrder = Mockito.inOrder(mStream);
// On new tab page created.
when(mTab.isHidden()).thenReturn(true);
when(mTab.isUserInteractable()).thenReturn(false);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.STARTED);
ApplicationStatus.onStateChangeForTesting(mActivity, ActivityState.RESUMED);
verify(mStream, times(0)).onShow();
// On tab shown.
when(mTab.isHidden()).thenReturn(false);
mStreamLifecycleManager.getTabObserverForTesting().onShown(mTab);
inOrder.verify(mStream).onShow();
verify(mStream, times(1)).onShow();
// On tab interactable.
when(mTab.isUserInteractable()).thenReturn(true);
mStreamLifecycleManager.getTabObserverForTesting().onInteractabilityChanged(true);
inOrder.verify(mStream).onActive();
verify(mStream, times(1)).onActive();
// On tab un-interactable (simulates user enter the tab switcher).
when(mTab.isUserInteractable()).thenReturn(false);
mStreamLifecycleManager.getTabObserverForTesting().onInteractabilityChanged(false);
inOrder.verify(mStream).onInactive();
verify(mStream, times(1)).onInactive();
// On tab interactable (simulates user exit the tab switcher).
when(mTab.isUserInteractable()).thenReturn(true);
mStreamLifecycleManager.getTabObserverForTesting().onInteractabilityChanged(true);
inOrder.verify(mStream).onActive();
verify(mStream, times(2)).onActive();
// On tab un-interactable and hidden (simulates user switch to another tab).
when(mTab.isHidden()).thenReturn(true);
when(mTab.isUserInteractable()).thenReturn(false);
mStreamLifecycleManager.getTabObserverForTesting().onInteractabilityChanged(false);
mStreamLifecycleManager.getTabObserverForTesting().onHidden(mTab);
inOrder.verify(mStream).onInactive();
inOrder.verify(mStream).onHide();
verify(mStream, times(2)).onInactive();
verify(mStream, times(1)).onHide();
// On tab shown (simulates user switch back to this tab).
when(mTab.isHidden()).thenReturn(false);
mStreamLifecycleManager.getTabObserverForTesting().onShown(mTab);
inOrder.verify(mStream).onShow();
verify(mStream, times(2)).onShow();
// On tab destroy (simulates user close the tab or navigate to another URL).
mStreamLifecycleManager.destroy();
inOrder.verify(mStream).onHide();
inOrder.verify(mStream).onDestroy();
verify(mStream, times(2)).onHide();
verify(mStream, times(1)).onDestroy();
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment