@rntp/player 5.0.0-beta.4 → 5.0.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/android/build.gradle +7 -0
- package/android/src/main/java/com/doublesymmetry/trackplayer/SleepTimerController.kt +128 -0
- package/android/src/main/java/com/doublesymmetry/trackplayer/TrackPlayerModule.kt +40 -0
- package/android/src/main/java/com/doublesymmetry/trackplayer/TrackPlayerPlaybackService.kt +99 -87
- package/android/src/main/java/com/doublesymmetry/trackplayer/models/PlayerConfig.kt +12 -1
- package/android/src/test/java/com/doublesymmetry/trackplayer/ExoPlayerIntegrationTest.kt +319 -0
- package/android/src/test/java/com/doublesymmetry/trackplayer/SleepTimerIntegrationTest.kt +473 -0
- package/android/src/test/java/com/doublesymmetry/trackplayer/SleepTimerStateTest.kt +58 -0
- package/android/src/test/java/com/doublesymmetry/trackplayer/models/BrowseNavigationTest.kt +215 -0
- package/android/src/test/java/com/doublesymmetry/trackplayer/models/BrowseTreeTest.kt +166 -0
- package/android/src/test/java/com/doublesymmetry/trackplayer/models/EmitEventTest.kt +68 -0
- package/android/src/test/java/com/doublesymmetry/trackplayer/models/PlayerConfigTest.kt +400 -0
- package/android/src/test/java/com/doublesymmetry/trackplayer/models/TrackPlayerMediaItemTest.kt +380 -0
- package/android/src/test/resources/robolectric.properties +1 -0
- package/ios/TrackPlayer.swift +47 -101
- package/ios/TrackPlayerBridge.mm +2 -0
- package/ios/player/AVPlayerEngine.swift +47 -35
- package/ios/player/AudioCache.swift +34 -0
- package/ios/player/AudioPlayer.swift +70 -22
- package/ios/player/CacheProxyServer.swift +429 -0
- package/ios/player/DownloadCoordinator.swift +242 -0
- package/ios/player/Preloader.swift +21 -90
- package/ios/player/SleepTimerController.swift +147 -0
- package/ios/tests/AVPlayerEngineIntegrationTests.swift +230 -0
- package/ios/tests/AudioPlayerTests.swift +6 -0
- package/ios/tests/CacheProxyServerTests.swift +403 -0
- package/ios/tests/DownloadCoordinatorTests.swift +197 -0
- package/ios/tests/LocalAudioServer.swift +171 -0
- package/ios/tests/MockPlayerEngine.swift +1 -0
- package/ios/tests/QueueManagerTests.swift +6 -0
- package/ios/tests/SleepTimerIntegrationTests.swift +408 -0
- package/ios/tests/SleepTimerTests.swift +70 -0
- package/lib/commonjs/NativeTrackPlayer.js.map +1 -1
- package/lib/commonjs/audio.js +19 -0
- package/lib/commonjs/audio.js.map +1 -1
- package/lib/commonjs/interfaces/PlayerConfig.js +1 -1
- package/lib/commonjs/interfaces/PlayerConfig.js.map +1 -1
- package/lib/module/NativeTrackPlayer.js.map +1 -1
- package/lib/module/audio.js +17 -0
- package/lib/module/audio.js.map +1 -1
- package/lib/module/interfaces/PlayerConfig.js +1 -1
- package/lib/module/interfaces/PlayerConfig.js.map +1 -1
- package/lib/typescript/src/NativeTrackPlayer.d.ts +2 -0
- package/lib/typescript/src/NativeTrackPlayer.d.ts.map +1 -1
- package/lib/typescript/src/audio.d.ts +12 -1
- package/lib/typescript/src/audio.d.ts.map +1 -1
- package/lib/typescript/src/interfaces/MediaItem.d.ts +4 -1
- package/lib/typescript/src/interfaces/MediaItem.d.ts.map +1 -1
- package/lib/typescript/src/interfaces/PlayerConfig.d.ts +21 -2
- package/lib/typescript/src/interfaces/PlayerConfig.d.ts.map +1 -1
- package/package.json +4 -1
- package/src/NativeTrackPlayer.ts +4 -0
- package/src/audio.ts +18 -0
- package/src/interfaces/MediaItem.ts +4 -1
- package/src/interfaces/PlayerConfig.ts +24 -3
- package/ios/player/CachingResourceLoader.swift +0 -273
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
package com.doublesymmetry.trackplayer.models
|
|
2
|
+
|
|
3
|
+
import org.junit.Test
|
|
4
|
+
import org.junit.Assert.*
|
|
5
|
+
import org.junit.runner.RunWith
|
|
6
|
+
import org.robolectric.RobolectricTestRunner
|
|
7
|
+
|
|
8
|
+
@RunWith(RobolectricTestRunner::class)
|
|
9
|
+
class BrowseNavigationTest {
|
|
10
|
+
|
|
11
|
+
// Helper to build a tree with two categories, each having different item types
|
|
12
|
+
private fun buildTree(): BrowseTree {
|
|
13
|
+
return BrowseTree(listOf(
|
|
14
|
+
BrowseCategory(
|
|
15
|
+
mediaId = "cat-music",
|
|
16
|
+
title = "Music",
|
|
17
|
+
items = listOf(
|
|
18
|
+
BrowseMediaItem(
|
|
19
|
+
mediaId = "track-1",
|
|
20
|
+
url = "https://example.com/track1.mp3",
|
|
21
|
+
title = "Track 1",
|
|
22
|
+
artist = "Artist A",
|
|
23
|
+
artworkUrl = "https://example.com/art.jpg",
|
|
24
|
+
),
|
|
25
|
+
BrowseMediaItem(
|
|
26
|
+
mediaId = "album-1",
|
|
27
|
+
title = "Album",
|
|
28
|
+
children = listOf(
|
|
29
|
+
BrowseMediaItem(
|
|
30
|
+
mediaId = "track-2",
|
|
31
|
+
url = "https://example.com/track2.mp3",
|
|
32
|
+
title = "Track 2",
|
|
33
|
+
artist = "Artist B",
|
|
34
|
+
)
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
),
|
|
38
|
+
),
|
|
39
|
+
BrowseCategory(
|
|
40
|
+
mediaId = "cat-podcasts",
|
|
41
|
+
title = "Podcasts",
|
|
42
|
+
items = listOf(
|
|
43
|
+
BrowseMediaItem(
|
|
44
|
+
mediaId = "episode-1",
|
|
45
|
+
url = "https://example.com/ep1.mp3",
|
|
46
|
+
title = "Episode 1",
|
|
47
|
+
)
|
|
48
|
+
),
|
|
49
|
+
),
|
|
50
|
+
))
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// --- Simulating onGetChildren root logic ---
|
|
54
|
+
// Root: return categories mapped to browsable MediaItems
|
|
55
|
+
|
|
56
|
+
@Test
|
|
57
|
+
fun `root returns all categories as browsable items`() {
|
|
58
|
+
val tree = buildTree()
|
|
59
|
+
|
|
60
|
+
// Simulate what onGetChildren("root") does: map categories to MediaItems
|
|
61
|
+
val items = tree.categories.map { cat ->
|
|
62
|
+
BrowseMediaItem(
|
|
63
|
+
mediaId = cat.mediaId,
|
|
64
|
+
title = cat.title,
|
|
65
|
+
children = cat.items, // presence of children makes it browsable
|
|
66
|
+
).toMediaItem()
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
assertEquals(2, items.size)
|
|
70
|
+
items.forEach { item ->
|
|
71
|
+
assertEquals(true, item.mediaMetadata.isBrowsable)
|
|
72
|
+
assertEquals(false, item.mediaMetadata.isPlayable)
|
|
73
|
+
}
|
|
74
|
+
assertEquals("cat-music", items[0].mediaId)
|
|
75
|
+
assertEquals("cat-podcasts", items[1].mediaId)
|
|
76
|
+
assertEquals("Music", items[0].mediaMetadata.title.toString())
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// --- Simulating onGetChildren for a category parentId ---
|
|
80
|
+
|
|
81
|
+
@Test
|
|
82
|
+
fun `category returns its direct children`() {
|
|
83
|
+
val tree = buildTree()
|
|
84
|
+
|
|
85
|
+
val category = tree.findCategory("cat-music")
|
|
86
|
+
assertNotNull(category)
|
|
87
|
+
|
|
88
|
+
val items = category!!.items.map { it.toMediaItem() }
|
|
89
|
+
assertEquals(2, items.size)
|
|
90
|
+
|
|
91
|
+
// First child is a playable track
|
|
92
|
+
val track = items[0]
|
|
93
|
+
assertEquals("track-1", track.mediaId)
|
|
94
|
+
assertEquals(true, track.mediaMetadata.isPlayable)
|
|
95
|
+
assertEquals(false, track.mediaMetadata.isBrowsable)
|
|
96
|
+
|
|
97
|
+
// Second child is a browsable album
|
|
98
|
+
val album = items[1]
|
|
99
|
+
assertEquals("album-1", album.mediaId)
|
|
100
|
+
assertEquals(false, album.mediaMetadata.isPlayable)
|
|
101
|
+
assertEquals(true, album.mediaMetadata.isBrowsable)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// --- Simulating onGetChildren for a browsable item parentId ---
|
|
105
|
+
|
|
106
|
+
@Test
|
|
107
|
+
fun `browsable item returns its children`() {
|
|
108
|
+
val tree = buildTree()
|
|
109
|
+
|
|
110
|
+
val item = tree.findItem("album-1")
|
|
111
|
+
assertNotNull(item)
|
|
112
|
+
assertNotNull(item!!.children)
|
|
113
|
+
|
|
114
|
+
val children = item.children!!.map { it.toMediaItem() }
|
|
115
|
+
assertEquals(1, children.size)
|
|
116
|
+
assertEquals("track-2", children[0].mediaId)
|
|
117
|
+
assertEquals(true, children[0].mediaMetadata.isPlayable)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
@Test
|
|
121
|
+
fun `unknown parentId returns null for both lookups`() {
|
|
122
|
+
val tree = buildTree()
|
|
123
|
+
|
|
124
|
+
val category = tree.findCategory("nonexistent")
|
|
125
|
+
val item = tree.findItem("nonexistent")
|
|
126
|
+
|
|
127
|
+
assertNull(category)
|
|
128
|
+
assertNull(item)
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
@Test
|
|
132
|
+
fun `playable items in category have correct metadata`() {
|
|
133
|
+
val tree = buildTree()
|
|
134
|
+
|
|
135
|
+
val category = tree.findCategory("cat-music")!!
|
|
136
|
+
val playableItem = category.items.first { it.isPlayable }
|
|
137
|
+
val mediaItem = playableItem.toMediaItem()
|
|
138
|
+
|
|
139
|
+
assertEquals("track-1", mediaItem.mediaId)
|
|
140
|
+
assertEquals("Track 1", mediaItem.mediaMetadata.title.toString())
|
|
141
|
+
assertEquals("Artist A", mediaItem.mediaMetadata.artist.toString())
|
|
142
|
+
assertNotNull(mediaItem.localConfiguration?.uri)
|
|
143
|
+
assertEquals("https://example.com/track1.mp3", mediaItem.localConfiguration!!.uri.toString())
|
|
144
|
+
assertEquals(true, mediaItem.mediaMetadata.isPlayable)
|
|
145
|
+
assertEquals(false, mediaItem.mediaMetadata.isBrowsable)
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
@Test
|
|
149
|
+
fun `browsable items have no URI and isBrowsable is true`() {
|
|
150
|
+
val tree = buildTree()
|
|
151
|
+
|
|
152
|
+
val category = tree.findCategory("cat-music")!!
|
|
153
|
+
val browsableItem = category.items.first { it.isBrowsable }
|
|
154
|
+
val mediaItem = browsableItem.toMediaItem()
|
|
155
|
+
|
|
156
|
+
assertEquals("album-1", mediaItem.mediaId)
|
|
157
|
+
assertEquals(true, mediaItem.mediaMetadata.isBrowsable)
|
|
158
|
+
assertEquals(false, mediaItem.mediaMetadata.isPlayable)
|
|
159
|
+
// Browsable items have no local configuration (no URI set)
|
|
160
|
+
assertNull(mediaItem.localConfiguration)
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
@Test
|
|
164
|
+
fun `category lookup works across multiple categories`() {
|
|
165
|
+
val tree = buildTree()
|
|
166
|
+
|
|
167
|
+
val podcasts = tree.findCategory("cat-podcasts")
|
|
168
|
+
assertNotNull(podcasts)
|
|
169
|
+
assertEquals("Podcasts", podcasts!!.title)
|
|
170
|
+
assertEquals(1, podcasts.items.size)
|
|
171
|
+
assertEquals("episode-1", podcasts.items[0].mediaId)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
@Test
|
|
175
|
+
fun `toMediaItem sets correct artwork URI for playable items`() {
|
|
176
|
+
val item = BrowseMediaItem(
|
|
177
|
+
mediaId = "t1",
|
|
178
|
+
url = "https://example.com/t.mp3",
|
|
179
|
+
artworkUrl = "https://example.com/art.jpg",
|
|
180
|
+
)
|
|
181
|
+
val mediaItem = item.toMediaItem()
|
|
182
|
+
|
|
183
|
+
assertNotNull(mediaItem.mediaMetadata.artworkUri)
|
|
184
|
+
assertEquals("https://example.com/art.jpg", mediaItem.mediaMetadata.artworkUri.toString())
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@Test
|
|
188
|
+
fun `toMediaItem stores duration and isLive in extras`() {
|
|
189
|
+
val item = BrowseMediaItem(
|
|
190
|
+
mediaId = "live-1",
|
|
191
|
+
url = "https://example.com/live.mp3",
|
|
192
|
+
duration = 3600.0,
|
|
193
|
+
isLive = true,
|
|
194
|
+
)
|
|
195
|
+
val mediaItem = item.toMediaItem()
|
|
196
|
+
|
|
197
|
+
val extras = mediaItem.mediaMetadata.extras
|
|
198
|
+
assertNotNull(extras)
|
|
199
|
+
assertEquals(3600.0, extras!!.getDouble("duration"), 0.001)
|
|
200
|
+
assertTrue(extras.getBoolean("isLive"))
|
|
201
|
+
// Live items get a LiveConfiguration
|
|
202
|
+
assertNotNull(mediaItem.liveConfiguration)
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
@Test
|
|
206
|
+
fun `empty category returns empty children list`() {
|
|
207
|
+
val tree = BrowseTree(listOf(
|
|
208
|
+
BrowseCategory(mediaId = "empty-cat", title = "Empty", items = emptyList())
|
|
209
|
+
))
|
|
210
|
+
|
|
211
|
+
val category = tree.findCategory("empty-cat")
|
|
212
|
+
assertNotNull(category)
|
|
213
|
+
assertTrue(category!!.items.isEmpty())
|
|
214
|
+
}
|
|
215
|
+
}
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
package com.doublesymmetry.trackplayer.models
|
|
2
|
+
|
|
3
|
+
import org.junit.Test
|
|
4
|
+
import org.junit.Assert.*
|
|
5
|
+
import org.junit.runner.RunWith
|
|
6
|
+
import org.robolectric.RobolectricTestRunner
|
|
7
|
+
import org.robolectric.RuntimeEnvironment
|
|
8
|
+
|
|
9
|
+
@RunWith(RobolectricTestRunner::class)
|
|
10
|
+
class BrowseTreeTest {
|
|
11
|
+
|
|
12
|
+
@Test
|
|
13
|
+
fun `store and load round-trip preserves data`() {
|
|
14
|
+
val tree = BrowseTree(listOf(
|
|
15
|
+
BrowseCategory("cat-1", "Music", listOf(
|
|
16
|
+
BrowseMediaItem(mediaId = "t1", url = "https://example.com/t1.mp3", title = "Track 1")
|
|
17
|
+
))
|
|
18
|
+
))
|
|
19
|
+
val context = RuntimeEnvironment.getApplication()
|
|
20
|
+
tree.store(context)
|
|
21
|
+
val loaded = BrowseTree.load(context)
|
|
22
|
+
|
|
23
|
+
assertEquals(1, loaded.categories.size)
|
|
24
|
+
assertEquals("Music", loaded.categories[0].title)
|
|
25
|
+
assertEquals("t1", loaded.categories[0].items[0].mediaId)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@Test
|
|
29
|
+
fun `load with corrupt JSON returns empty tree`() {
|
|
30
|
+
val context = RuntimeEnvironment.getApplication()
|
|
31
|
+
val prefs = context.getSharedPreferences("TrackPlayerPrefs", 0)
|
|
32
|
+
prefs.edit().putString("browse_tree", "not valid json{{{").apply()
|
|
33
|
+
|
|
34
|
+
val tree = BrowseTree.load(context)
|
|
35
|
+
assertTrue(tree.categories.isEmpty())
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
@Test
|
|
39
|
+
fun `load with no saved data returns empty tree`() {
|
|
40
|
+
val context = RuntimeEnvironment.getApplication()
|
|
41
|
+
val tree = BrowseTree.load(context)
|
|
42
|
+
assertTrue(tree.categories.isEmpty())
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
@Test
|
|
46
|
+
fun `findCategory returns matching category`() {
|
|
47
|
+
val tree = BrowseTree(listOf(
|
|
48
|
+
BrowseCategory("cat-1", "Music", emptyList()),
|
|
49
|
+
BrowseCategory("cat-2", "Podcasts", emptyList()),
|
|
50
|
+
))
|
|
51
|
+
assertEquals("Podcasts", tree.findCategory("cat-2")?.title)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
@Test
|
|
55
|
+
fun `findCategory returns null for unknown id`() {
|
|
56
|
+
val tree = BrowseTree(listOf(BrowseCategory("cat-1", "Music", emptyList())))
|
|
57
|
+
assertNull(tree.findCategory("unknown"))
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
@Test
|
|
61
|
+
fun `findItem finds top-level item`() {
|
|
62
|
+
val tree = BrowseTree(listOf(
|
|
63
|
+
BrowseCategory("cat-1", "Music", listOf(
|
|
64
|
+
BrowseMediaItem(mediaId = "t1", url = "a.mp3", title = "Track 1"),
|
|
65
|
+
BrowseMediaItem(mediaId = "t2", url = "b.mp3", title = "Track 2"),
|
|
66
|
+
))
|
|
67
|
+
))
|
|
68
|
+
assertEquals("Track 2", tree.findItem("t2")?.title)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
@Test
|
|
72
|
+
fun `findItem finds deeply nested item`() {
|
|
73
|
+
val tree = BrowseTree(listOf(
|
|
74
|
+
BrowseCategory("cat-1", "Music", listOf(
|
|
75
|
+
BrowseMediaItem(mediaId = "album-1", title = "Album", children = listOf(
|
|
76
|
+
BrowseMediaItem(mediaId = "disc-1", title = "Disc 1", children = listOf(
|
|
77
|
+
BrowseMediaItem(mediaId = "deep-track", url = "deep.mp3", title = "Deep Track")
|
|
78
|
+
))
|
|
79
|
+
))
|
|
80
|
+
))
|
|
81
|
+
))
|
|
82
|
+
val found = tree.findItem("deep-track")
|
|
83
|
+
assertEquals("Deep Track", found?.title)
|
|
84
|
+
assertEquals("deep.mp3", found?.url)
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
@Test
|
|
88
|
+
fun `findItem returns null for unknown id`() {
|
|
89
|
+
val tree = BrowseTree(listOf(
|
|
90
|
+
BrowseCategory("cat-1", "Music", listOf(
|
|
91
|
+
BrowseMediaItem(mediaId = "t1", url = "a.mp3")
|
|
92
|
+
))
|
|
93
|
+
))
|
|
94
|
+
assertNull(tree.findItem("nonexistent"))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
@Test
|
|
98
|
+
fun `isPlayable is true when url is present`() {
|
|
99
|
+
val item = BrowseMediaItem(mediaId = "t1", url = "a.mp3")
|
|
100
|
+
assertTrue(item.isPlayable)
|
|
101
|
+
assertFalse(item.isBrowsable)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
@Test
|
|
105
|
+
fun `isBrowsable is true when children present and no url`() {
|
|
106
|
+
val item = BrowseMediaItem(mediaId = "folder", children = listOf(
|
|
107
|
+
BrowseMediaItem(mediaId = "t1", url = "a.mp3")
|
|
108
|
+
))
|
|
109
|
+
assertTrue(item.isBrowsable)
|
|
110
|
+
assertFalse(item.isPlayable)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@Test
|
|
114
|
+
fun `url takes precedence over children for playability`() {
|
|
115
|
+
val item = BrowseMediaItem(mediaId = "t1", url = "a.mp3", children = listOf(
|
|
116
|
+
BrowseMediaItem(mediaId = "t2", url = "b.mp3")
|
|
117
|
+
))
|
|
118
|
+
assertTrue(item.isPlayable)
|
|
119
|
+
assertFalse(item.isBrowsable)
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
@Test
|
|
123
|
+
fun `toMediaItem sets playable metadata for track`() {
|
|
124
|
+
val item = BrowseMediaItem(
|
|
125
|
+
mediaId = "t1", url = "https://example.com/t1.mp3",
|
|
126
|
+
title = "Song", artist = "Artist",
|
|
127
|
+
)
|
|
128
|
+
val mediaItem = item.toMediaItem()
|
|
129
|
+
assertEquals("t1", mediaItem.mediaId)
|
|
130
|
+
assertEquals("Song", mediaItem.mediaMetadata.title.toString())
|
|
131
|
+
assertEquals(true, mediaItem.mediaMetadata.isPlayable)
|
|
132
|
+
assertEquals(false, mediaItem.mediaMetadata.isBrowsable)
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
@Test
|
|
136
|
+
fun `toMediaItem sets browsable metadata for folder`() {
|
|
137
|
+
val item = BrowseMediaItem(
|
|
138
|
+
mediaId = "album-1", title = "Album",
|
|
139
|
+
children = listOf(BrowseMediaItem(mediaId = "t1", url = "a.mp3")),
|
|
140
|
+
)
|
|
141
|
+
val mediaItem = item.toMediaItem()
|
|
142
|
+
assertEquals(false, mediaItem.mediaMetadata.isPlayable)
|
|
143
|
+
assertEquals(true, mediaItem.mediaMetadata.isBrowsable)
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
@Test
|
|
147
|
+
fun `store and load preserves nested children`() {
|
|
148
|
+
val tree = BrowseTree(listOf(
|
|
149
|
+
BrowseCategory("cat-1", "Music", listOf(
|
|
150
|
+
BrowseMediaItem(mediaId = "album", title = "Album", children = listOf(
|
|
151
|
+
BrowseMediaItem(mediaId = "t1", url = "a.mp3", title = "Track 1"),
|
|
152
|
+
BrowseMediaItem(mediaId = "t2", url = "b.mp3", title = "Track 2"),
|
|
153
|
+
))
|
|
154
|
+
))
|
|
155
|
+
))
|
|
156
|
+
val context = RuntimeEnvironment.getApplication()
|
|
157
|
+
tree.store(context)
|
|
158
|
+
val loaded = BrowseTree.load(context)
|
|
159
|
+
|
|
160
|
+
val album = loaded.categories[0].items[0]
|
|
161
|
+
assertEquals("album", album.mediaId)
|
|
162
|
+
assertNotNull(album.children)
|
|
163
|
+
assertEquals(2, album.children!!.size)
|
|
164
|
+
assertEquals("t1", album.children!![0].mediaId)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
package com.doublesymmetry.trackplayer.models
|
|
2
|
+
|
|
3
|
+
import org.junit.Test
|
|
4
|
+
import org.junit.Assert.*
|
|
5
|
+
|
|
6
|
+
class EmitEventTest {
|
|
7
|
+
|
|
8
|
+
@Test
|
|
9
|
+
fun `PlaybackStateChangedEvent produces correct pairs`() {
|
|
10
|
+
val event = PlaybackStateChangedEvent("ready")
|
|
11
|
+
assertEquals(EmitEventType.PLAYBACK_STATE_CHANGED, event.type)
|
|
12
|
+
assertEquals(1, event.pairs().size)
|
|
13
|
+
assertEquals("state" to "ready", event.pairs()[0])
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@Test
|
|
17
|
+
fun `IsPlayingChangedEvent produces correct pairs`() {
|
|
18
|
+
val event = IsPlayingChangedEvent(true)
|
|
19
|
+
assertEquals("playing" to true, event.pairs()[0])
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
@Test
|
|
23
|
+
fun `MediaItemTransitionEvent with null item omits item`() {
|
|
24
|
+
val event = MediaItemTransitionEvent(null, 3)
|
|
25
|
+
assertEquals(1, event.pairs().size)
|
|
26
|
+
assertEquals("index" to 3, event.pairs()[0])
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@Test
|
|
30
|
+
fun `MediaItemTransitionEvent with item includes both`() {
|
|
31
|
+
val event = MediaItemTransitionEvent("item-data", 0)
|
|
32
|
+
assertEquals(2, event.pairs().size)
|
|
33
|
+
assertTrue(event.pairs().any { it.first == "index" && it.second == 0 })
|
|
34
|
+
assertTrue(event.pairs().any { it.first == "item" && it.second == "item-data" })
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
@Test
|
|
38
|
+
fun `SleepTimerTriggeredEvent produces correct pairs`() {
|
|
39
|
+
val event = SleepTimerTriggeredEvent("time")
|
|
40
|
+
assertEquals(EmitEventType.SLEEP_TIMER_TRIGGERED, event.type)
|
|
41
|
+
assertEquals("type" to "time", event.pairs()[0])
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@Test
|
|
45
|
+
fun `PlaybackErrorEvent produces code and message`() {
|
|
46
|
+
val event = PlaybackErrorEvent("network", "Connection failed")
|
|
47
|
+
assertTrue(event.pairs().any { it.first == "code" && it.second == "network" })
|
|
48
|
+
assertTrue(event.pairs().any { it.first == "message" && it.second == "Connection failed" })
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@Test
|
|
52
|
+
fun `MetadataReceivedEvent with partial fields only includes non-null`() {
|
|
53
|
+
val event = MetadataReceivedEvent(title = "Song", artist = null)
|
|
54
|
+
assertEquals(1, event.pairs().size)
|
|
55
|
+
assertEquals("title" to "Song", event.pairs()[0])
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
@Test
|
|
59
|
+
fun `QueueChangedEvent has empty pairs`() {
|
|
60
|
+
assertEquals(0, QueueChangedEvent().pairs().size)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@Test
|
|
64
|
+
fun `all EmitEventType values have unique string values`() {
|
|
65
|
+
val values = EmitEventType.entries.map { it.value }
|
|
66
|
+
assertEquals(values.size, values.toSet().size)
|
|
67
|
+
}
|
|
68
|
+
}
|