| Index: chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java
|
| diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..4966dc33fbcba2a6b0e347acf6f968bf57e5aa87
|
| --- /dev/null
|
| +++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java
|
| @@ -0,0 +1,483 @@
|
| +// Copyright 2014 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.media.remote;
|
| +
|
| +import static org.junit.Assert.assertFalse;
|
| +import static org.junit.Assert.assertNotNull;
|
| +import static org.junit.Assert.assertTrue;
|
| +import static org.mockito.Matchers.any;
|
| +import static org.mockito.Matchers.anyBoolean;
|
| +import static org.mockito.Matchers.anyInt;
|
| +import static org.mockito.Matchers.eq;
|
| +import static org.mockito.Mockito.doReturn;
|
| +import static org.mockito.Mockito.mock;
|
| +import static org.mockito.Mockito.never;
|
| +import static org.mockito.Mockito.spy;
|
| +import static org.mockito.Mockito.times;
|
| +import static org.mockito.Mockito.verify;
|
| +import static org.mockito.Mockito.verifyNoMoreInteractions;
|
| +import static org.mockito.Mockito.when;
|
| +
|
| +import android.content.Context;
|
| +import android.net.Uri;
|
| +import android.support.v7.media.MediaItemStatus;
|
| +import android.support.v7.media.MediaRouteSelector;
|
| +import android.support.v7.media.MediaRouter;
|
| +import android.support.v7.media.MediaRouter.Callback;
|
| +import android.support.v7.media.MediaRouter.ProviderInfo;
|
| +import android.support.v7.media.MediaRouter.RouteInfo;
|
| +
|
| +import org.chromium.base.CommandLine;
|
| +import org.chromium.base.test.util.Feature;
|
| +import org.chromium.chrome.browser.media.remote.MediaRouteController.MediaStateListener;
|
| +import org.chromium.chrome.browser.media.remote.MediaRouteController.UiListener;
|
| +import org.chromium.testing.local.LocalRobolectricTestRunner;
|
| +import org.junit.Before;
|
| +import org.junit.Test;
|
| +import org.junit.runner.RunWith;
|
| +import org.mockito.ArgumentCaptor;
|
| +import org.mockito.Mockito;
|
| +import org.mockito.invocation.InvocationOnMock;
|
| +import org.mockito.stubbing.Answer;
|
| +import org.robolectric.annotation.Config;
|
| +import org.robolectric.annotation.Implementation;
|
| +import org.robolectric.annotation.Implements;
|
| +import org.robolectric.internal.ReflectionHelpers;
|
| +
|
| +/** Tests for {@link AbstractMediaRouteController}. */
|
| +@RunWith(LocalRobolectricTestRunner.class)
|
| +@Config(manifest = Config.NONE, shadows = AbstractMediaRouteControllerTest.ShadowMediaRouter.class)
|
| +public class AbstractMediaRouteControllerTest {
|
| + /** Reset the environment before each test. */
|
| + @Before
|
| + public void beforeTest() {
|
| + // TODO(dgn): Remove when command line flags are not used anymore to detect debug
|
| + // see http://crbug.com/469649
|
| + CommandLine.init(new String[] {});
|
| +
|
| + ShadowMediaRouter.sMediaRouter = null;
|
| + ShadowMediaRouter.sCallback = null;
|
| + DummyMediaRouteController.sMediaRouteSelector = mock(MediaRouteSelector.class);
|
| + }
|
| +
|
| + /**
|
| + * Test method for {@link AbstractMediaRouteController#isPlaying()}.
|
| + *
|
| + * Checks that it returns the correct value for all possible playback states.
|
| + */
|
| + @Test
|
| + @Feature({"MediaRemote"})
|
| + public void testIsPlaying() {
|
| + // Using a spy here to override some methods.
|
| + AbstractMediaRouteController mediaRouteCtrl = spy(new DummyMediaRouteController());
|
| +
|
| + // Default
|
| + assertFalse(mediaRouteCtrl.isPlaying());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_BUFFERING);
|
| + assertTrue(mediaRouteCtrl.isPlaying());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_CANCELED);
|
| + assertFalse(mediaRouteCtrl.isPlaying());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_ERROR);
|
| + assertFalse(mediaRouteCtrl.isPlaying());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_FINISHED);
|
| + assertFalse(mediaRouteCtrl.isPlaying());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_INVALIDATED);
|
| + assertFalse(mediaRouteCtrl.isPlaying());
|
| +
|
| + doReturn(0).when(mediaRouteCtrl).getPosition();
|
| + doReturn(5000).when(mediaRouteCtrl).getDuration();
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PAUSED);
|
| + assertFalse(mediaRouteCtrl.isPlaying());
|
| +
|
| + doReturn(5000).when(mediaRouteCtrl).getPosition();
|
| + doReturn(5000).when(mediaRouteCtrl).getDuration();
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PAUSED);
|
| + assertFalse(mediaRouteCtrl.isPlaying());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PENDING);
|
| + assertFalse(mediaRouteCtrl.isPlaying());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PLAYING);
|
| + assertTrue(mediaRouteCtrl.isPlaying());
|
| + }
|
| +
|
| + /**
|
| + * Test method for {@link AbstractMediaRouteController#isBeingCast()}.
|
| + *
|
| + * Checks that it returns the correct value for all possible playback states.
|
| + */
|
| + @Test
|
| + @Feature({"MediaRemote"})
|
| + public void testIsBeingCast() {
|
| + // Using a spy here to override some methods.
|
| + AbstractMediaRouteController mediaRouteCtrl = spy(new DummyMediaRouteController());
|
| +
|
| + // Default
|
| + assertFalse(mediaRouteCtrl.isBeingCast());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_BUFFERING);
|
| + assertTrue(mediaRouteCtrl.isBeingCast());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_CANCELED);
|
| + assertFalse(mediaRouteCtrl.isBeingCast());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_ERROR);
|
| + assertFalse(mediaRouteCtrl.isBeingCast());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_FINISHED);
|
| + assertFalse(mediaRouteCtrl.isBeingCast());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_INVALIDATED);
|
| + assertFalse(mediaRouteCtrl.isBeingCast());
|
| +
|
| + doReturn(0).when(mediaRouteCtrl).getPosition();
|
| + doReturn(5000).when(mediaRouteCtrl).getDuration();
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PAUSED);
|
| + assertTrue(mediaRouteCtrl.isBeingCast());
|
| +
|
| + doReturn(5000).when(mediaRouteCtrl).getPosition();
|
| + doReturn(5000).when(mediaRouteCtrl).getDuration();
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PAUSED);
|
| + assertFalse(mediaRouteCtrl.isBeingCast());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PENDING);
|
| + assertTrue(mediaRouteCtrl.isBeingCast());
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PLAYING);
|
| + assertTrue(mediaRouteCtrl.isBeingCast());
|
| + }
|
| +
|
| + /** Test method for {@link AbstractMediaRouteController#isRemotePlaybackAvailable()}.*/
|
| + @Test
|
| + @Feature({"MediaRemote"})
|
| + public void testIsRemotePlaybackAvailable() {
|
| + MediaRouter mediaRouter = mock(MediaRouter.class);
|
| + AbstractMediaRouteController mediaRouteCtrl = new DummyMediaRouteController();
|
| + when(mediaRouter.getSelectedRoute())
|
| + .thenReturn(createRouteInfo(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE));
|
| + when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt()))
|
| + .thenReturn(true);
|
| +
|
| + // Default
|
| + assertFalse(mediaRouteCtrl.isRemotePlaybackAvailable());
|
| +
|
| + ShadowMediaRouter.sMediaRouter = mediaRouter;
|
| + mediaRouteCtrl = spy(new DummyMediaRouteController());
|
| + assertTrue(mediaRouteCtrl.isRemotePlaybackAvailable());
|
| +
|
| + when(mediaRouter.getSelectedRoute())
|
| + .thenReturn(createRouteInfo(MediaRouter.RouteInfo.PLAYBACK_TYPE_LOCAL));
|
| + assertTrue(mediaRouteCtrl.isRemotePlaybackAvailable());
|
| +
|
| + when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt()))
|
| + .thenReturn(false);
|
| + assertFalse(mediaRouteCtrl.isRemotePlaybackAvailable());
|
| +
|
| + when(mediaRouter.getSelectedRoute())
|
| + .thenReturn(createRouteInfo(MediaRouter.RouteInfo.PLAYBACK_TYPE_REMOTE));
|
| + assertTrue(mediaRouteCtrl.isRemotePlaybackAvailable());
|
| + }
|
| +
|
| + /**
|
| + * Test method for
|
| + * {@link AbstractMediaRouteController#addMediaStateListener(MediaStateListener)} and
|
| + * {@link AbstractMediaRouteController#removeMediaStateListener(MediaStateListener)}
|
| + *
|
| + * Makes sure that listeners gets notified when they are added and don't get notified
|
| + * when removed.
|
| + */
|
| + @Test
|
| + @Feature({"MediaRemote"})
|
| + public void testAddRemoveMediaStateListener() {
|
| + MediaStateListener listener = mock(MediaStateListener.class);
|
| + MediaStateListener otherListener = mock(MediaStateListener.class);
|
| + MediaRouter mediaRouter = ShadowMediaRouter.createCapturingMock();
|
| + when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt()))
|
| + .thenReturn(true);
|
| + ShadowMediaRouter.sMediaRouter = mediaRouter;
|
| +
|
| + // Creating the MediaRouteController
|
| + AbstractMediaRouteController mediaRouteController = new DummyMediaRouteController();
|
| + assertNotNull(mediaRouteController.getMediaRouter());
|
| +
|
| + // 1. #addMediaStateListener()
|
| + mediaRouteController.addMediaStateListener(listener);
|
| +
|
| + // The route selector should get notified of new states.
|
| + verify(mediaRouter)
|
| + .addCallback(any(MediaRouteSelector.class), any(Callback.class), anyInt());
|
| + verify(listener).onRouteAvailabilityChanged(true);
|
| +
|
| + // Check the behavior difference between the first and subsequent additions.
|
| + mediaRouteController.addMediaStateListener(otherListener);
|
| + verify(otherListener).onRouteAvailabilityChanged(true);
|
| + // The call count should not change.
|
| + verify(mediaRouter)
|
| + .addCallback(any(MediaRouteSelector.class), any(Callback.class), anyInt());
|
| + verify(listener).onRouteAvailabilityChanged(true);
|
| +
|
| + // 2. #removeMediaStateListener()
|
| + mediaRouteController.removeMediaStateListener(otherListener);
|
| +
|
| + // The removed listener should not be notified of changes anymore.
|
| + when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt()))
|
| + .thenReturn(false);
|
| + ShadowMediaRouter.sCallback.onRouteRemoved(mediaRouter, null);
|
| + verifyNoMoreInteractions(otherListener);
|
| + verify(listener).onRouteAvailabilityChanged(false);
|
| + verify(mediaRouter, times(0)).removeCallback(any(Callback.class));
|
| +
|
| + mediaRouteController.removeMediaStateListener(listener);
|
| +
|
| + when(mediaRouter.isRouteAvailable(any(MediaRouteSelector.class), anyInt()))
|
| + .thenReturn(true);
|
| + ShadowMediaRouter.sCallback.onRouteAdded(mediaRouter, null);
|
| + verifyNoMoreInteractions(otherListener);
|
| + verifyNoMoreInteractions(listener);
|
| + verify(mediaRouter).removeCallback(any(Callback.class));
|
| + }
|
| +
|
| + /**
|
| + * Test method for
|
| + * {@link AbstractMediaRouteController#addMediaStateListener(MediaStateListener)}
|
| + *
|
| + * Tests that listeners are not used (state not initialized) when the media router
|
| + * is not initialized.
|
| + */
|
| + @Test
|
| + @Feature({"MediaRemote"})
|
| + public void testAddMediaStateListenerInitFailed() {
|
| + MediaStateListener listener = mock(MediaStateListener.class);
|
| +
|
| + AbstractMediaRouteController mediaRouteController = new DummyMediaRouteController();
|
| + mediaRouteController.addMediaStateListener(listener);
|
| +
|
| + verify(listener, never()).onRouteAvailabilityChanged(anyBoolean());
|
| + }
|
| +
|
| + /** Test method for {@link AbstractMediaRouteController#prepareMediaRoute()}.*/
|
| + @Test
|
| + @Feature({"MediaRemote"})
|
| + public void testPrepareMediaRoute() {
|
| + // Check when no media router, check that not done twice
|
| +
|
| + ShadowMediaRouter.sMediaRouter = mock(MediaRouter.class);
|
| + AbstractMediaRouteController mediaRouteCtrl = new DummyMediaRouteController();
|
| +
|
| + verify(ShadowMediaRouter.sMediaRouter, times(0))
|
| + .addCallback(any(MediaRouteSelector.class), any(Callback.class), anyInt());
|
| +
|
| + mediaRouteCtrl.prepareMediaRoute();
|
| + verify(ShadowMediaRouter.sMediaRouter, times(1))
|
| + .addCallback(eq(DummyMediaRouteController.sMediaRouteSelector), any(Callback.class),
|
| + eq(MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY));
|
| +
|
| + mediaRouteCtrl.prepareMediaRoute();
|
| + verify(ShadowMediaRouter.sMediaRouter, times(1))
|
| + .addCallback(any(MediaRouteSelector.class), any(Callback.class), anyInt());
|
| + }
|
| +
|
| + /**
|
| + * Test method for {@link AbstractMediaRouteController#shouldResetState(MediaStateListener)}.
|
| + */
|
| + @Test
|
| + @Feature({"MediaRemote"})
|
| + public void testShouldResetState() {
|
| + AbstractMediaRouteController mediaRouteCtrl = new DummyMediaRouteController();
|
| + MediaStateListener listener = mock(MediaStateListener.class);
|
| +
|
| + mediaRouteCtrl.setMediaStateListener(null);
|
| +
|
| + assertTrue(mediaRouteCtrl.shouldResetState(null));
|
| + assertTrue(mediaRouteCtrl.shouldResetState(mock(MediaStateListener.class)));
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PLAYING);
|
| +
|
| + assertFalse(mediaRouteCtrl.shouldResetState(null));
|
| + assertTrue(mediaRouteCtrl.shouldResetState(mock(MediaStateListener.class)));
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_FINISHED);
|
| + mediaRouteCtrl.setMediaStateListener(listener);
|
| +
|
| + assertTrue(mediaRouteCtrl.shouldResetState(null));
|
| + assertTrue(mediaRouteCtrl.shouldResetState(mock(MediaStateListener.class)));
|
| + assertTrue(mediaRouteCtrl.shouldResetState(listener));
|
| +
|
| + mediaRouteCtrl.setPlayerStateForMediaItemState(MediaItemStatus.PLAYBACK_STATE_PLAYING);
|
| +
|
| + assertTrue(mediaRouteCtrl.shouldResetState(null));
|
| + assertTrue(mediaRouteCtrl.shouldResetState(mock(MediaStateListener.class)));
|
| + assertFalse(mediaRouteCtrl.shouldResetState(listener));
|
| + }
|
| +
|
| + /**
|
| + * Test method for {@link AbstractMediaRouteController#addUiListener(UiListener)} and
|
| + * {@link AbstractMediaRouteController#removeMediaStateListener(MediaStateListener)}.
|
| + */
|
| + @Test
|
| + @Feature({"MediaRemote"})
|
| + public void testAddRemoveUiListener() {
|
| + DummyMediaRouteController mediaRouteCtrl = new DummyMediaRouteController();
|
| + UiListener listener = mock(UiListener.class);
|
| +
|
| + mediaRouteCtrl.addUiListener(listener);
|
| + mediaRouteCtrl.verifyListenerActivation(1, listener);
|
| +
|
| + // Should not be added twice.
|
| + mediaRouteCtrl.addUiListener(listener);
|
| + mediaRouteCtrl.verifyListenerActivation(2, listener);
|
| +
|
| + mediaRouteCtrl.removeUiListener(listener);
|
| + mediaRouteCtrl.verifyListenerActivation(2, listener);
|
| + }
|
| +
|
| + private static RouteInfo createRouteInfo(int playbackType) {
|
| + Class<?>[] paramClasses = new Class[] {ProviderInfo.class, String.class, String.class};
|
| + Object[] paramValues = new Object[] {null, "", ""};
|
| + RouteInfo routeInfo = ReflectionHelpers.callConstructorReflectively(RouteInfo.class,
|
| + ReflectionHelpers.ClassParameter.fromComponentLists(paramClasses, paramValues));
|
| + ReflectionHelpers.setFieldReflectively(routeInfo, "mPlaybackType", playbackType);
|
| + return routeInfo;
|
| + }
|
| +
|
| + /** Shadow needed because getInstance() can't be mocked */
|
| + @Implements(MediaRouter.class)
|
| + public static class ShadowMediaRouter {
|
| + public static MediaRouter sMediaRouter = null;
|
| + public static Callback sCallback;
|
| +
|
| + @Implementation
|
| + public static MediaRouter getInstance(Context context) {
|
| + return sMediaRouter;
|
| + }
|
| +
|
| + /**
|
| + * Creates a {@link MediaRouter} mock that will capture the callback argument
|
| + * when {@link MediaRouter#addCallback(MediaRouteSelector, Callback, int)} is called
|
| + * and sets it to {@link #sCallback}.
|
| + */
|
| + public static MediaRouter createCapturingMock() {
|
| + MediaRouter mediaRouter = mock(MediaRouter.class);
|
| +
|
| + final ArgumentCaptor<Callback> callbackArg = ArgumentCaptor.forClass(Callback.class);
|
| + final Answer<?> addCallbackAnswer = new Answer<Object>() {
|
| + @Override
|
| + public Object answer(InvocationOnMock invocation) {
|
| + sCallback = callbackArg.getValue();
|
| + return null;
|
| + }
|
| + };
|
| +
|
| + Mockito.doAnswer(addCallbackAnswer)
|
| + .when(mediaRouter)
|
| + .addCallback(any(MediaRouteSelector.class), callbackArg.capture(), anyInt());
|
| + return mediaRouter;
|
| + }
|
| + }
|
| +
|
| + /**
|
| + * A dummy class used here to be able to instantiate the abstract class while calling its
|
| + * constructor. {@link AbstractMediaRouteController#buildMediaRouteSelector()} needs to be
|
| + * implemented before the object's construction. Mockito's mocks don't call constructors.
|
| + */
|
| + private static class DummyMediaRouteController extends AbstractMediaRouteController {
|
| + /**
|
| + * MediaRouteSelector required during MediaRouteController's constructor call via
|
| + * {@link #buildMediaRouteSelector()}
|
| + */
|
| + public static MediaRouteSelector sMediaRouteSelector;
|
| +
|
| + @Override
|
| + public boolean initialize() {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public boolean canPlayMedia(String sourceUrl, String frameUrl) {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public MediaRouteSelector buildMediaRouteSelector() {
|
| + return sMediaRouteSelector;
|
| + }
|
| +
|
| + @Override
|
| + public boolean reconnectAnyExistingRoute() {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public void setDataSource(Uri uri, String cookies) {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public void prepareAsync(String frameUrl, long startPositionMillis) {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public void setRemoteVolume(int delta) {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public void resume() {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public void pause() {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public int getPosition() {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public int getDuration() {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public void seekTo(int msec) {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + public void release() {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + protected void onRouteAddedEvent(MediaRouter router, RouteInfo route) {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + protected void onRouteSelectedEvent(MediaRouter router, RouteInfo route) {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + @Override
|
| + protected void onRouteUnselectedEvent(MediaRouter router, RouteInfo route) {
|
| + throw new UnsupportedOperationException();
|
| + }
|
| +
|
| + public void verifyListenerActivation(int times, UiListener listener) {
|
| + String testTitle = "foo";
|
| + updateTitle(testTitle); // protected methods, needs to be called within the class.
|
| + verify(listener, times(times)).onTitleChanged(testTitle);
|
| + }
|
| + }
|
| +}
|
|
|