1+ package net.newpipe.newplayer
2+
3+ import android.app.Activity
4+ import android.app.Application
5+ import android.content.ComponentName
6+ import android.content.Intent
7+ import android.content.pm.PackageManager
8+ import android.content.pm.ResolveInfo
9+ import android.content.pm.ServiceInfo
10+ import android.os.Looper
11+ import android.text.TextUtils
12+ import android.util.Log
13+ import androidx.media3.common.MediaItem
14+ import androidx.media3.exoplayer.ExoPlayer
15+ import androidx.media3.session.MediaController
16+ import androidx.media3.session.SessionToken
17+ import com.google.common.util.concurrent.Futures
18+ import io.mockk.clearMocks
19+ import io.mockk.every
20+ import io.mockk.mockk
21+ import io.mockk.mockkConstructor
22+ import io.mockk.mockkStatic
23+ import io.mockk.verify
24+ import kotlinx.coroutines.Dispatchers
25+ import kotlinx.coroutines.ExperimentalCoroutinesApi
26+ import kotlinx.coroutines.test.UnconfinedTestDispatcher
27+ import kotlinx.coroutines.test.resetMain
28+ import kotlinx.coroutines.test.setMain
29+ import net.newpipe.newplayer.repository.DelayTestRepository
30+ import net.newpipe.newplayer.repository.MockMediaRepository
31+ import org.junit.Before
32+ import org.junit.BeforeClass
33+ import org.junit.Ignore
34+ import org.junit.Test
35+
36+ @OptIn(ExperimentalCoroutinesApi ::class )
37+ class NewPlayerImpltest {
38+ val mockExoPlayer = mockk<ExoPlayer >(relaxed = true )
39+ val mockMediaController = mockk<MediaController >(relaxed = true )
40+ val mockApp = mockk<Application >(relaxed = true )
41+ val playerActivityClass = Activity ::class .java
42+ val repository = DelayTestRepository (MockMediaRepository (), 100 )
43+ var player = NewPlayerImpl (mockApp, playerActivityClass, repository)
44+
45+ init {
46+ mockkStatic(Looper ::class )
47+ mockkStatic(TextUtils ::class )
48+ mockkStatic(Log ::class )
49+ mockkStatic(ExoPlayer ::class )
50+ mockkStatic(ExoPlayer .Builder ::class )
51+ mockkStatic(TextUtils ::class )
52+ mockkConstructor(ComponentName ::class )
53+ mockkConstructor(Intent ::class )
54+ mockkConstructor(SessionToken ::class )
55+ mockkConstructor(ExoPlayer .Builder ::class )
56+ mockkConstructor(MediaController .Builder ::class )
57+
58+ val mockLooper = mockk<Looper >(relaxed = true )
59+ val mockPackageManager = mockk<PackageManager >(relaxed = true )
60+ val mockResolveInfo = mockk<ResolveInfo >(relaxed = true )
61+ val mockServiceInfo = mockk<ServiceInfo >(relaxed = true )
62+ mockResolveInfo.serviceInfo = mockServiceInfo
63+ mockServiceInfo.name = " ComponentNameTest"
64+
65+ every { Looper .myLooper() } returns mockLooper
66+ every { TextUtils .isEmpty(any()) } returns true
67+ every { Log .i(any(), any()) } returns 1
68+ every { anyConstructed<ComponentName >().packageName } returns " net.newpipe.newplayer.test"
69+ every { anyConstructed<ComponentName >().className } returns " ComponentNameTest"
70+ every { anyConstructed<ComponentName >().hashCode() } returns 1
71+ every { anyConstructed<Intent >().setPackage(any()) } returns mockk<Intent >(relaxed = true )
72+ every { anyConstructed<ExoPlayer .Builder >().build() } returns mockExoPlayer
73+ every { TextUtils .equals(any(), any()) } returns true
74+ every { anyConstructed<MediaController .Builder >().buildAsync() } returns Futures .immediateFuture(mockMediaController)
75+ every { mockApp.packageManager } returns mockPackageManager
76+ every { mockPackageManager.queryIntentServices(any(), any<Int >()) } returns listOf (mockResolveInfo)
77+ }
78+
79+ companion object {
80+ @JvmStatic
81+ @BeforeClass
82+ fun init () {
83+ Dispatchers .setMain(UnconfinedTestDispatcher ())
84+ }
85+
86+ @JvmStatic
87+ @BeforeClass
88+ fun reset () {
89+ Dispatchers .resetMain()
90+ }
91+ }
92+
93+ @Before
94+ fun resetSpy () {
95+ clearMocks(mockExoPlayer)
96+ player = NewPlayerImpl (mockApp, playerActivityClass, repository)
97+ }
98+
99+ @Test
100+ fun onPlayBackError_pauseExoPlayer () {
101+ player.prepare()
102+ player.onPlayBackError(Exception (" test" ))
103+ verify (exactly = 1 ) { mockExoPlayer.pause() }
104+ }
105+
106+ @Test
107+ fun prepare_setupNewExoplayer () {
108+ player.prepare()
109+ verify (exactly = 1 ) { anyConstructed<ExoPlayer .Builder >().build() }
110+ }
111+
112+ @Test
113+ fun prepare_notSetupNewExoPlayerWhenAlreadySetUp () {
114+ // Setup new ExoPlayer
115+ player.prepare()
116+
117+ // Call prepare with an already set up ExoPlayer
118+ player.prepare()
119+
120+ // ExoPlayer should be built only one time
121+ verify (exactly = 1 ) { anyConstructed<ExoPlayer .Builder >().build() }
122+ }
123+
124+ @Test
125+ fun prepare_prepareExoPlayer () {
126+ player.prepare()
127+ verify (exactly = 1 ) { mockExoPlayer.prepare() }
128+ }
129+
130+ @Test
131+ fun prepare_setUpMediaController () {
132+ player.prepare()
133+ verify (exactly = 1 ) { anyConstructed<MediaController .Builder >().buildAsync() }
134+ }
135+
136+ @Test
137+ fun prepare_notSetUpMediaController () {
138+ // Setup media controller
139+ player.prepare()
140+
141+ // Call prepare with an already set up media controller
142+ player.prepare()
143+
144+ // Media controller should be built only one time
145+ verify (exactly = 1 ) { anyConstructed<MediaController .Builder >().buildAsync() }
146+ }
147+
148+ @Test
149+ fun play_playIfCurrentMediaItemIsNotNull () {
150+ player.prepare()
151+ player.play()
152+ verify (exactly = 1 ) { mockExoPlayer.play() }
153+ }
154+
155+ @Test
156+ fun play_notPlayIfCurrentMediaItemIsNull () {
157+ player.prepare()
158+ every { mockExoPlayer.currentMediaItem } returns null
159+ player.play()
160+ verify (exactly = 0 ) { mockExoPlayer.play() }
161+ }
162+
163+ @Test
164+ fun pause () {
165+ player.prepare()
166+ player.pause()
167+ verify (exactly = 1 ) { mockExoPlayer.pause() }
168+ }
169+
170+ @Test
171+ fun addToPlaylist_prepareExoPlayerIfNotPrepared () {
172+ player.addToPlaylist(" item" )
173+ verify (exactly = 1 ) { mockExoPlayer.prepare() }
174+ }
175+
176+ @Test
177+ fun addToPlaylist_notPrepareExoPlayerIfAlreadyPrepared () {
178+ player.prepare()
179+ clearMocks(mockExoPlayer)
180+ player.addToPlaylist(" item" )
181+ verify (exactly = 0 ) { mockExoPlayer.prepare() }
182+ }
183+
184+ @Ignore(" Find a way to test code inside launchJobAndCollectError" )
185+ @Test
186+ fun addToPlaylist_addMediaSource () {
187+ player.addToPlaylist(" item" )
188+ // try {
189+ // Dispatchers.Main.job.join()
190+ // } catch (e : Exception) { }
191+ // coVerify (exactly = 2) { mockExoPlayer.addMediaSource(any()) }
192+ }
193+
194+ @Test
195+ fun movePlaylistItem () {
196+ player.prepare()
197+ player.movePlaylistItem(0 , 1 )
198+ verify (exactly = 1 ){ mockExoPlayer.moveMediaItem(0 , 1 ) }
199+ }
200+
201+ @Test
202+ fun removePlaylistItem_removeItem () {
203+ player.prepare()
204+ val mediaItem = MediaItem .Builder ().setMediaId(" 123" ).build()
205+ every { mockExoPlayer.mediaItemCount } returns 1
206+ every { mockExoPlayer.getMediaItemAt(any()) } returns mediaItem
207+ player.removePlaylistItem(123 )
208+ verify (exactly = 1 ) { mockExoPlayer.removeMediaItem(0 ) }
209+ }
210+
211+ @Test
212+ fun removePlaylistItem_notRemoveItem () {
213+ player.prepare()
214+ val mediaItem = MediaItem .Builder ().setMediaId(" 123" ).build()
215+ every { mockExoPlayer.mediaItemCount } returns 1
216+ every { mockExoPlayer.getMediaItemAt(any()) } returns mediaItem
217+ player.removePlaylistItem(124 )
218+ verify (exactly = 0 ) { mockExoPlayer.removeMediaItem(0 ) }
219+ }
220+
221+ @Ignore(" Mock currentChapters.value and test the selection of a chapter" )
222+ @Test
223+ fun selectChapter () {
224+ player.selectChapter(0 )
225+ }
226+
227+ @Test(expected = IndexOutOfBoundsException ::class )
228+ fun selectChapter_throwsException () {
229+ player.selectChapter(3 )
230+ }
231+
232+ @Test
233+ fun release () {
234+ player.prepare()
235+ player.release()
236+ verify (exactly = 1 ) { mockMediaController.release() }
237+ verify (exactly = 1 ) { mockExoPlayer.release() }
238+ }
239+
240+ }
0 commit comments