• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  *
16  */
17 package com.google.android.exoplayer2.util;
18 
19 import static org.mockito.ArgumentMatchers.any;
20 import static org.mockito.ArgumentMatchers.anyInt;
21 import static org.mockito.Mockito.mock;
22 import static org.mockito.Mockito.never;
23 import static org.mockito.Mockito.verify;
24 import static org.mockito.Mockito.verifyNoMoreInteractions;
25 
26 import android.os.Handler;
27 import android.os.Looper;
28 import androidx.annotation.Nullable;
29 import androidx.test.ext.junit.runners.AndroidJUnit4;
30 import com.google.android.exoplayer2.drm.DrmSessionEventListener;
31 import com.google.android.exoplayer2.source.MediaSource;
32 import com.google.android.exoplayer2.source.MediaSource.MediaPeriodId;
33 import com.google.android.exoplayer2.source.MediaSourceEventListener;
34 import org.junit.Before;
35 import org.junit.Rule;
36 import org.junit.Test;
37 import org.junit.runner.RunWith;
38 import org.mockito.Mock;
39 import org.mockito.junit.MockitoJUnit;
40 import org.mockito.junit.MockitoRule;
41 
42 /** Tests for {@link MediaSourceEventDispatcher}. */
43 @RunWith(AndroidJUnit4.class)
44 public class MediaSourceEventDispatcherTest {
45 
46   private static final MediaSource.MediaPeriodId MEDIA_PERIOD_ID =
47       new MediaSource.MediaPeriodId("test uid");
48   private static final int WINDOW_INDEX = 200;
49   private static final int MEDIA_TIME_OFFSET_MS = 1_000;
50 
51   @Rule public final MockitoRule mockito = MockitoJUnit.rule();
52 
53   @Mock private MediaSourceEventListener mediaSourceEventListener;
54   @Mock private MediaAndDrmEventListener mediaAndDrmEventListener;
55 
56   private MediaSourceEventDispatcher eventDispatcher;
57 
58   @Before
setupEventDispatcher()59   public void setupEventDispatcher() {
60     eventDispatcher = new MediaSourceEventDispatcher();
61     eventDispatcher =
62         eventDispatcher.withParameters(WINDOW_INDEX, MEDIA_PERIOD_ID, MEDIA_TIME_OFFSET_MS);
63   }
64 
65   @Test
listenerReceivesEventPopulatedWithMediaPeriodInfo()66   public void listenerReceivesEventPopulatedWithMediaPeriodInfo() {
67     eventDispatcher.addEventListener(
68         Util.createHandler(), mediaSourceEventListener, MediaSourceEventListener.class);
69 
70     eventDispatcher.dispatch(
71         MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class);
72 
73     verify(mediaSourceEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID);
74   }
75 
76   @Test
sameListenerObjectRegisteredTwiceOnlyReceivesEventsOnce()77   public void sameListenerObjectRegisteredTwiceOnlyReceivesEventsOnce() {
78     eventDispatcher.addEventListener(
79         Util.createHandler(), mediaSourceEventListener, MediaSourceEventListener.class);
80     eventDispatcher.addEventListener(
81         Util.createHandler(), mediaSourceEventListener, MediaSourceEventListener.class);
82 
83     eventDispatcher.dispatch(
84         MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class);
85 
86     verify(mediaSourceEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID);
87   }
88 
89   @Test
sameListenerInstanceCanBeRegisteredWithTwoTypes()90   public void sameListenerInstanceCanBeRegisteredWithTwoTypes() {
91     eventDispatcher.addEventListener(
92         new Handler(Looper.getMainLooper()),
93         mediaAndDrmEventListener,
94         MediaSourceEventListener.class);
95     eventDispatcher.addEventListener(
96         new Handler(Looper.getMainLooper()),
97         mediaAndDrmEventListener,
98         DrmSessionEventListener.class);
99 
100     eventDispatcher.dispatch(
101         MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class);
102     eventDispatcher.dispatch(
103         (listener, windowIndex, mediaPeriodId) -> listener.onDrmKeysLoaded(),
104         DrmSessionEventListener.class);
105 
106     verify(mediaAndDrmEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID);
107     verify(mediaAndDrmEventListener).onDrmKeysLoaded();
108   }
109 
110   // If a listener is added that implements multiple types, it should only receive events for the
111   // type specified at registration time.
112   @Test
listenerOnlyReceivesEventsForRegisteredType()113   public void listenerOnlyReceivesEventsForRegisteredType() {
114     eventDispatcher.addEventListener(
115         new Handler(Looper.getMainLooper()),
116         mediaAndDrmEventListener,
117         MediaSourceEventListener.class);
118 
119     eventDispatcher.dispatch(
120         MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class);
121     eventDispatcher.dispatch(
122         (listener, windowIndex, mediaPeriodId) -> listener.onDrmKeysLoaded(),
123         DrmSessionEventListener.class);
124 
125     verify(mediaAndDrmEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID);
126     verify(mediaAndDrmEventListener, never()).onDrmKeysLoaded();
127   }
128 
129   @Test
listenerDoesntReceiveEventsDispatchedToSubclass()130   public void listenerDoesntReceiveEventsDispatchedToSubclass() {
131     SubclassListener subclassListener = mock(SubclassListener.class);
132     eventDispatcher.addEventListener(
133         new Handler(Looper.getMainLooper()), subclassListener, MediaSourceEventListener.class);
134 
135     eventDispatcher.dispatch(SubclassListener::subclassMethod, SubclassListener.class);
136 
137     // subclassListener can handle the call to subclassMethod, but it isn't called because
138     // it was registered 'as-a' MediaSourceEventListener, not SubclassListener.
139     verify(subclassListener, never()).subclassMethod(anyInt(), any());
140   }
141 
142   @Test
listenerDoesntReceiveEventsDispatchedToSuperclass()143   public void listenerDoesntReceiveEventsDispatchedToSuperclass() {
144     SubclassListener subclassListener = mock(SubclassListener.class);
145     eventDispatcher.addEventListener(
146         new Handler(Looper.getMainLooper()), subclassListener, SubclassListener.class);
147 
148     eventDispatcher.dispatch(
149         MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class);
150 
151     // subclassListener 'is-a' a MediaSourceEventListener, but it isn't called because the event
152     // is dispatched specifically to listeners registered as MediaSourceEventListener.
153     verify(subclassListener, never()).onMediaPeriodCreated(anyInt(), any());
154   }
155 
156   @Test
listenersAreCopiedToNewDispatcher()157   public void listenersAreCopiedToNewDispatcher() {
158     eventDispatcher.addEventListener(
159         Util.createHandler(), mediaSourceEventListener, MediaSourceEventListener.class);
160 
161     MediaSource.MediaPeriodId newPeriodId = new MediaSource.MediaPeriodId("different uid");
162     MediaSourceEventDispatcher newEventDispatcher =
163         this.eventDispatcher.withParameters(
164             /* windowIndex= */ 250, newPeriodId, /* mediaTimeOffsetMs= */ 500);
165 
166     newEventDispatcher.dispatch(
167         MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class);
168 
169     verify(mediaSourceEventListener).onMediaPeriodCreated(250, newPeriodId);
170   }
171 
172   @Test
removingListenerStopsEventDispatch()173   public void removingListenerStopsEventDispatch() {
174     eventDispatcher.addEventListener(
175         Util.createHandler(), mediaSourceEventListener, MediaSourceEventListener.class);
176     eventDispatcher.removeEventListener(mediaSourceEventListener, MediaSourceEventListener.class);
177 
178     eventDispatcher.dispatch(
179         MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class);
180 
181     verify(mediaSourceEventListener, never()).onMediaPeriodCreated(anyInt(), any());
182   }
183 
184   @Test
removingListenerWithDifferentTypeToRegistrationDoesntRemove()185   public void removingListenerWithDifferentTypeToRegistrationDoesntRemove() {
186     eventDispatcher.addEventListener(
187         Util.createHandler(), mediaAndDrmEventListener, MediaSourceEventListener.class);
188     eventDispatcher.removeEventListener(mediaAndDrmEventListener, DrmSessionEventListener.class);
189 
190     eventDispatcher.dispatch(
191         MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class);
192 
193     verify(mediaAndDrmEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID);
194   }
195 
196   @Test
listenersAreCountedBasedOnListenerAndType()197   public void listenersAreCountedBasedOnListenerAndType() {
198     // Add the listener twice and remove it once.
199     eventDispatcher.addEventListener(
200         Util.createHandler(), mediaSourceEventListener, MediaSourceEventListener.class);
201     eventDispatcher.addEventListener(
202         Util.createHandler(), mediaSourceEventListener, MediaSourceEventListener.class);
203     eventDispatcher.removeEventListener(mediaSourceEventListener, MediaSourceEventListener.class);
204 
205     eventDispatcher.dispatch(
206         MediaSourceEventListener::onMediaPeriodCreated, MediaSourceEventListener.class);
207 
208     verify(mediaSourceEventListener).onMediaPeriodCreated(WINDOW_INDEX, MEDIA_PERIOD_ID);
209 
210     // Remove it a second time and confirm the events stop being propagated.
211     eventDispatcher.removeEventListener(mediaSourceEventListener, MediaSourceEventListener.class);
212 
213     verifyNoMoreInteractions(mediaSourceEventListener);
214   }
215 
216   private interface MediaAndDrmEventListener
217       extends MediaSourceEventListener, DrmSessionEventListener {}
218 
219   private interface SubclassListener extends MediaSourceEventListener {
subclassMethod(int windowIndex, @Nullable MediaPeriodId mediaPeriodId)220     void subclassMethod(int windowIndex, @Nullable MediaPeriodId mediaPeriodId);
221   }
222 }
223