blob: e861d78c13817af812fd9e59f631fa29e57c28a6 [file] [log] [blame]
// 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.chromecast.shell;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.PatternMatcher;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLooper;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chromecast.base.Consumer;
import org.chromium.chromecast.base.Observer;
import org.chromium.chromecast.base.Scope;
import org.chromium.chromecast.shell.CastWebContentsSurfaceHelper.MediaSessionGetter;
import org.chromium.chromecast.shell.CastWebContentsSurfaceHelper.StartParams;
import org.chromium.content.browser.MediaSessionImpl;
import org.chromium.content_public.browser.WebContents;
import java.util.ArrayList;
import java.util.List;
/**
* Tests for CastWebContentsSurfaceHelper.
*/
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class CastWebContentsSurfaceHelperTest {
private @Mock Observer<WebContents> mWebContentsView;
private @Mock Consumer<Uri> mFinishCallback;
private CastWebContentsSurfaceHelper mSurfaceHelper;
private @Mock MediaSessionGetter mMediaSessionGetter;
private @Mock MediaSessionImpl mMediaSessionImpl;
private static class StartParamsBuilder {
private String mId = "0";
private WebContents mWebContents = mock(WebContents.class);
private boolean mIsRemoteControlMode;
private boolean mIsTouchInputEnabled;
public StartParamsBuilder withId(String id) {
mId = id;
return this;
}
public StartParamsBuilder withWebContents(WebContents webContents) {
mWebContents = webContents;
return this;
}
public StartParamsBuilder withIsRemoteControlMode(boolean isRemoteControlMode) {
mIsRemoteControlMode = isRemoteControlMode;
return this;
}
public StartParamsBuilder enableTouchInput(boolean enableTouchInput) {
mIsTouchInputEnabled = enableTouchInput;
return this;
}
public StartParams build() {
return new StartParams(CastWebContentsIntentUtils.getInstanceUri(mId), mWebContents,
mIsRemoteControlMode, mIsTouchInputEnabled);
}
}
private static class BroadcastAsserter {
private final Intent mExpectedIntent;
private final List<Intent> mReceivedIntents = new ArrayList<>();
private final LocalBroadcastReceiverScope mReceiver;
public BroadcastAsserter(Intent looksLike) {
mExpectedIntent = looksLike;
IntentFilter filter = new IntentFilter();
Uri instanceUri = looksLike.getData();
filter.addDataScheme(instanceUri.getScheme());
filter.addDataAuthority(instanceUri.getAuthority(), null);
filter.addDataPath(instanceUri.getPath(), PatternMatcher.PATTERN_LITERAL);
filter.addAction(looksLike.getAction());
mReceiver = new LocalBroadcastReceiverScope(filter, mReceivedIntents::add);
}
public void verify() {
assertEquals(1, mReceivedIntents.size());
Intent receivedIntent = mReceivedIntents.get(0);
assertEquals(mExpectedIntent.getAction(), receivedIntent.getAction());
assertEquals(mExpectedIntent.getData(), receivedIntent.getData());
mReceiver.close();
}
}
private void sendBroadcastSync(Intent intent) {
CastWebContentsIntentUtils.getLocalBroadcastManager().sendBroadcastSync(intent);
}
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mMediaSessionGetter.get(any())).thenReturn(mMediaSessionImpl);
when(mWebContentsView.open(any())).thenReturn(mock(Scope.class));
mSurfaceHelper = new CastWebContentsSurfaceHelper(mWebContentsView, mFinishCallback);
mSurfaceHelper.setMediaSessionGetterForTesting(mMediaSessionGetter);
}
@Test
public void testActivatesWebContentsViewOnNewStartParams() {
WebContents webContents = mock(WebContents.class);
StartParams params = new StartParamsBuilder().withWebContents(webContents).build();
mSurfaceHelper.onNewStartParams(params);
verify(mWebContentsView).open(webContents);
}
@Test
public void testRequestsAudioFocusOnNewStartParams() {
WebContents webContents = mock(WebContents.class);
StartParams params = new StartParamsBuilder().withWebContents(webContents).build();
mSurfaceHelper.onNewStartParams(params);
verify(mMediaSessionImpl).requestSystemAudioFocus();
}
@Test
public void testDoesNotTakeAudioFocusInRemoteControlMode() {
WebContents webContents = mock(WebContents.class);
StartParams params = new StartParamsBuilder()
.withId("3")
.withWebContents(webContents)
.withIsRemoteControlMode(true)
.build();
mSurfaceHelper.onNewStartParams(params);
verify(mMediaSessionImpl, never()).requestSystemAudioFocus();
}
@Test
public void testDeactivatesOldWebContentsViewOnNewStartParams() {
WebContents webContents1 = mock(WebContents.class);
StartParams params1 =
new StartParamsBuilder().withId("1").withWebContents(webContents1).build();
WebContents webContents2 = mock(WebContents.class);
StartParams params2 =
new StartParamsBuilder().withId("2").withWebContents(webContents2).build();
Scope scope1 = mock(Scope.class);
Scope scope2 = mock(Scope.class);
when(mWebContentsView.open(webContents1)).thenReturn(scope1);
when(mWebContentsView.open(webContents2)).thenReturn(scope2);
mSurfaceHelper.onNewStartParams(params1);
verify(mWebContentsView).open(webContents1);
mSurfaceHelper.onNewStartParams(params2);
verify(scope1).close();
verify(mWebContentsView).open(webContents2);
}
@Test
public void testDoesNotRestartWebContentsIfNewStartParamsHasSameWebContents() {
WebContents webContents = mock(WebContents.class);
// Create two StartParams that have the same WebContents but different values.
StartParams params1 = new StartParamsBuilder()
.withId("1")
.withWebContents(webContents)
.enableTouchInput(false)
.build();
StartParams params2 = new StartParamsBuilder()
.withId("1")
.withWebContents(webContents)
.enableTouchInput(true)
.build();
Scope scope = mock(Scope.class);
when(mWebContentsView.open(webContents)).thenReturn(scope);
mSurfaceHelper.onNewStartParams(params1);
// The second StartParams has the same WebContents, so we shouldn't open the scopes again.
mSurfaceHelper.onNewStartParams(params2);
verify(mWebContentsView, times(1)).open(webContents);
}
@Test
public void testIsTouchInputEnabled() {
assertFalse(mSurfaceHelper.isTouchInputEnabled());
StartParams params1 = new StartParamsBuilder().enableTouchInput(true).build();
mSurfaceHelper.onNewStartParams(params1);
assertTrue(mSurfaceHelper.isTouchInputEnabled());
StartParams params2 = new StartParamsBuilder().enableTouchInput(false).build();
mSurfaceHelper.onNewStartParams(params2);
assertFalse(mSurfaceHelper.isTouchInputEnabled());
}
@Test
public void testSessionId() {
assertNull(mSurfaceHelper.getSessionId());
StartParams params1 = new StartParamsBuilder().withId("/abc123").build();
mSurfaceHelper.onNewStartParams(params1);
assertEquals("/abc123", mSurfaceHelper.getSessionId());
StartParams params2 = new StartParamsBuilder().withId("/123-abc").build();
mSurfaceHelper.onNewStartParams(params2);
assertEquals("/123-abc", mSurfaceHelper.getSessionId());
}
@Test
public void testScreenOffResetsWebContentsView() {
WebContents webContents = mock(WebContents.class);
StartParams params = new StartParamsBuilder().withWebContents(webContents).build();
Scope scope = mock(Scope.class);
when(mWebContentsView.open(webContents)).thenReturn(scope);
mSurfaceHelper.onNewStartParams(params);
// Send SCREEN_OFF broadcast.
sendBroadcastSync(new Intent(CastIntents.ACTION_SCREEN_OFF));
verify(scope).close();
}
@Test
public void testStopWebContentsIntentResetsWebContentsView() {
WebContents webContents = mock(WebContents.class);
StartParams params =
new StartParamsBuilder().withId("3").withWebContents(webContents).build();
Scope scope = mock(Scope.class);
when(mWebContentsView.open(webContents)).thenReturn(scope);
mSurfaceHelper.onNewStartParams(params);
// Send notification to stop web content
sendBroadcastSync(CastWebContentsIntentUtils.requestStopWebContents("3"));
verify(scope).close();
}
@Test
public void testStopWebContentsIntentWithWrongIdIsIgnored() {
WebContents webContents = mock(WebContents.class);
StartParams params =
new StartParamsBuilder().withId("2").withWebContents(webContents).build();
Scope scope = mock(Scope.class);
when(mWebContentsView.open(webContents)).thenReturn(scope);
mSurfaceHelper.onNewStartParams(params);
// Send notification to stop web content with different ID.
sendBroadcastSync(CastWebContentsIntentUtils.requestStopWebContents("4"));
verify(scope, never()).close();
}
@Test
public void testEnableTouchInputIntentMutatesIsTouchInputEnabled() {
WebContents webContents = mock(WebContents.class);
StartParams params = new StartParamsBuilder().withId("1").enableTouchInput(false).build();
mSurfaceHelper.onNewStartParams(params);
assertFalse(mSurfaceHelper.isTouchInputEnabled());
// Send broadcast to enable touch input.
sendBroadcastSync(CastWebContentsIntentUtils.enableTouchInput("1", true));
assertTrue(mSurfaceHelper.isTouchInputEnabled());
}
@Test
public void testEnableTouchInputIntentWithWrongIdIsIgnored() {
WebContents webContents = mock(WebContents.class);
StartParams params = new StartParamsBuilder().withId("1").enableTouchInput(false).build();
mSurfaceHelper.onNewStartParams(params);
assertFalse(mSurfaceHelper.isTouchInputEnabled());
// Send broadcast to enable touch input with different ID.
sendBroadcastSync(CastWebContentsIntentUtils.enableTouchInput("2", true));
assertFalse(mSurfaceHelper.isTouchInputEnabled());
}
@Test
public void testDisableTouchInputIntent() {
WebContents webContents = mock(WebContents.class);
StartParams params = new StartParamsBuilder().withId("1").enableTouchInput(true).build();
mSurfaceHelper.onNewStartParams(params);
assertTrue(mSurfaceHelper.isTouchInputEnabled());
// Send broadcast to enable touch input.
sendBroadcastSync(CastWebContentsIntentUtils.enableTouchInput("1", false));
assertFalse(mSurfaceHelper.isTouchInputEnabled());
}
@Test
public void testSendsComponentClosedBroadcastWhenWebContentsViewIsClosedAlt() {
StartParams params1 = new StartParamsBuilder().withId("1").build();
StartParams params2 = new StartParamsBuilder().withId("2").build();
mSurfaceHelper.onNewStartParams(params1);
// Listen for notification from surface helper that web contents closed.
BroadcastAsserter intentWasSent =
new BroadcastAsserter(CastWebContentsIntentUtils.onActivityStopped("1"));
mSurfaceHelper.onNewStartParams(params2);
intentWasSent.verify();
}
@Test
public void testFinishLaterCallbackIsRunAfterStopWebContents() {
StartParams params = new StartParamsBuilder().withId("0").build();
mSurfaceHelper.onNewStartParams(params);
sendBroadcastSync(CastWebContentsIntentUtils.requestStopWebContents("0"));
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mFinishCallback).accept(CastWebContentsIntentUtils.getInstanceUri("0"));
}
@Test
public void testFinishLaterCallbackIsRunAfterScreenOff() {
StartParams params = new StartParamsBuilder().withId("0").build();
mSurfaceHelper.onNewStartParams(params);
sendBroadcastSync(new Intent(CastIntents.ACTION_SCREEN_OFF));
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mFinishCallback).accept(CastWebContentsIntentUtils.getInstanceUri("0"));
}
@Test
public void testFinishLaterCallbackIsNotRunIfNewWebContentsIsReceived() {
StartParams params1 = new StartParamsBuilder().withId("1").build();
StartParams params2 = new StartParamsBuilder().withId("2").build();
mSurfaceHelper.onNewStartParams(params1);
sendBroadcastSync(CastWebContentsIntentUtils.requestStopWebContents("1"));
mSurfaceHelper.onNewStartParams(params2);
ShadowLooper.runUiThreadTasksIncludingDelayedTasks();
verify(mFinishCallback, never()).accept(any());
}
@Test
public void testOnDestroyClosesWebContentsView() {
WebContents webContents = mock(WebContents.class);
Scope scope = mock(Scope.class);
StartParams params = new StartParamsBuilder().withWebContents(webContents).build();
when(mWebContentsView.open(webContents)).thenReturn(scope);
mSurfaceHelper.onNewStartParams(params);
mSurfaceHelper.onDestroy();
verify(scope).close();
}
@Test
public void testOnDestroyNotifiesComponent() {
StartParams params = new StartParamsBuilder().withId("2").build();
mSurfaceHelper.onNewStartParams(params);
BroadcastAsserter intentWasSent =
new BroadcastAsserter(CastWebContentsIntentUtils.onActivityStopped("2"));
mSurfaceHelper.onDestroy();
intentWasSent.verify();
}
}