PlaybackCoordinator.test.ets 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import { describe, expect, it } from '@ohos/hypium'
  2. import { PlaybackCoordinator, PlaybackRuntime } from '../../../main/ets/controller/PlaybackCoordinator'
  3. import { PlaybackStateBridge } from '../../../main/ets/controller/PlaybackStateBridge'
  4. import { VideoItem } from '../../../main/ets/viewmodel/VideoItem'
  5. export default function playbackCoordinatorTest() {
  6. describe('PlaybackCoordinatorTest', () => {
  7. it('playQueueCallsRuntimeAndUpdatesState', 0, async () => {
  8. const bridge = PlaybackStateBridge.getInstance()
  9. const coordinator = PlaybackCoordinator.getInstance()
  10. const calls: string[] = []
  11. const songs: VideoItem[] = [
  12. new VideoItem('Song A', '1', '/music/a.flac', 0, 0, '2026-04-02'),
  13. new VideoItem('Song B', '2', '/music/b.flac', 0, 0, '2026-04-02')
  14. ]
  15. const runtime: PlaybackRuntime = {
  16. playQueue: async (_queue, startIndex, source) => {
  17. calls.push(`playQueue:${startIndex}:${source}`)
  18. },
  19. playOrPause: async () => {
  20. calls.push('playOrPause')
  21. },
  22. playNext: async () => {
  23. calls.push('playNext')
  24. },
  25. playPrevious: async () => {
  26. calls.push('playPrevious')
  27. },
  28. seekTo: async (value, source) => {
  29. calls.push(`seekTo:${value}:${source ?? ''}`)
  30. }
  31. }
  32. bridge.reset()
  33. coordinator.clearRuntime()
  34. coordinator.setRuntime(runtime)
  35. await coordinator.playQueue(songs, 1, 'charts-count')
  36. const snapshot = bridge.getSnapshot()
  37. expect(snapshot.currentIndex).assertEqual(1)
  38. expect(snapshot.currentSong?.filePath ?? '').assertEqual('/music/b.flac')
  39. expect(calls.join(',')).assertEqual('playQueue:1:charts-count')
  40. coordinator.clearRuntime()
  41. })
  42. it('playSongPropagatesStartIndexAndSourceAndUpdatesBridge', 0, async () => {
  43. const coordinator = PlaybackCoordinator.getInstance()
  44. const bridge = PlaybackStateBridge.getInstance()
  45. const song = new VideoItem('Song C', '3', '/music/c.flac', 0, 0, '2026-04-02')
  46. const runtimeCalls: string[] = []
  47. bridge.reset()
  48. coordinator.clearRuntime()
  49. coordinator.setRuntime({
  50. playQueue: async (_queue, startIndex, source) => {
  51. runtimeCalls.push(`${startIndex}:${source}`)
  52. },
  53. playOrPause: async () => {},
  54. playNext: async () => {},
  55. playPrevious: async () => {},
  56. seekTo: async () => {}
  57. })
  58. await coordinator.playSong(song, 'playlist-detail')
  59. const snapshot = bridge.getSnapshot()
  60. expect(runtimeCalls.join(',')).assertEqual('0:playlist-detail')
  61. expect(snapshot.currentIndex).assertEqual(0)
  62. expect(snapshot.currentSong?.filePath ?? '').assertEqual('/music/c.flac')
  63. coordinator.clearRuntime()
  64. })
  65. it('clearRuntimeOnlyRemovesMatchingInstance', 0, async () => {
  66. const coordinator = PlaybackCoordinator.getInstance()
  67. let playOrPauseCount = 0
  68. const runtimeA: PlaybackRuntime = {
  69. playQueue: async () => {},
  70. playOrPause: async () => {
  71. playOrPauseCount++
  72. },
  73. playNext: async () => {},
  74. playPrevious: async () => {},
  75. seekTo: async () => {}
  76. }
  77. const runtimeB: PlaybackRuntime = {
  78. playQueue: async () => {},
  79. playOrPause: async () => {},
  80. playNext: async () => {},
  81. playPrevious: async () => {},
  82. seekTo: async () => {}
  83. }
  84. coordinator.clearRuntime()
  85. coordinator.setRuntime(runtimeA)
  86. coordinator.clearRuntime(runtimeB)
  87. await coordinator.playOrPause()
  88. expect(playOrPauseCount).assertEqual(1)
  89. coordinator.clearRuntime(runtimeA)
  90. await coordinator.playOrPause()
  91. expect(playOrPauseCount).assertEqual(1)
  92. })
  93. it('coordinatorTransportControlsUseRegisteredRuntimeOnly', 0, async () => {
  94. const coordinator = PlaybackCoordinator.getInstance()
  95. const calls: string[] = []
  96. coordinator.clearRuntime()
  97. coordinator.setRuntime({
  98. playQueue: async () => {},
  99. playOrPause: async () => {
  100. calls.push('playOrPause')
  101. },
  102. playNext: async () => {
  103. calls.push('playNext')
  104. },
  105. playPrevious: async () => {
  106. calls.push('playPrevious')
  107. },
  108. seekTo: async (value, source) => {
  109. calls.push(`seekTo:${value}:${source ?? ''}`)
  110. }
  111. })
  112. await coordinator.playOrPause()
  113. await coordinator.playNext()
  114. await coordinator.playPrevious()
  115. await coordinator.seekTo('1200', 'slider')
  116. expect(calls.join(',')).assertEqual('playOrPause,playNext,playPrevious,seekTo:1200:slider')
  117. coordinator.clearRuntime()
  118. })
  119. it('playQueueWithoutRuntimeKeepsBridgeState', 0, async () => {
  120. const coordinator = PlaybackCoordinator.getInstance()
  121. const bridge = PlaybackStateBridge.getInstance()
  122. const initialSong = new VideoItem('Initial', 'init', '/music/initial.flac', 0, 0, '2026-04-02')
  123. bridge.reset()
  124. bridge.replaceQueue([initialSong], 0)
  125. const before = bridge.getSnapshot()
  126. coordinator.clearRuntime()
  127. await coordinator.playQueue(
  128. [new VideoItem('Song D', '4', '/music/d.flac', 0, 0, '2026-04-02')],
  129. 0,
  130. 'charts'
  131. )
  132. const after = bridge.getSnapshot()
  133. expect(after.currentIndex).assertEqual(before.currentIndex)
  134. expect(after.currentSong?.filePath ?? '').assertEqual(before.currentSong?.filePath ?? '')
  135. expect(after.queue.length).assertEqual(before.queue.length)
  136. })
  137. it('playQueueRollsBackOnRuntimeError', 0, async () => {
  138. const coordinator = PlaybackCoordinator.getInstance()
  139. const bridge = PlaybackStateBridge.getInstance()
  140. const previousSong = new VideoItem('Previous', 'prev', '/music/prev.flac', 0, 0, '2026-04-02')
  141. bridge.reset()
  142. bridge.replaceQueue([previousSong], 0)
  143. const snapshotBefore = bridge.getSnapshot()
  144. let threwError = false
  145. coordinator.clearRuntime()
  146. coordinator.setRuntime({
  147. playQueue: async () => {
  148. throw new Error('boom')
  149. },
  150. playOrPause: async () => {},
  151. playNext: async () => {},
  152. playPrevious: async () => {},
  153. seekTo: async () => {}
  154. })
  155. try {
  156. await coordinator.playQueue(
  157. [
  158. new VideoItem('Song E', '5', '/music/e.flac', 0, 0, '2026-04-02'),
  159. new VideoItem('Song F', '6', '/music/f.flac', 0, 0, '2026-04-02')
  160. ],
  161. 0,
  162. 'playlist'
  163. )
  164. } catch (error) {
  165. threwError = true
  166. }
  167. expect(threwError).assertEqual(true)
  168. const snapshotAfter = bridge.getSnapshot()
  169. expect(snapshotAfter.queue.length).assertEqual(snapshotBefore.queue.length)
  170. expect(snapshotAfter.currentIndex).assertEqual(snapshotBefore.currentIndex)
  171. expect(snapshotAfter.currentSong?.filePath ?? '').assertEqual(snapshotBefore.currentSong?.filePath ?? '')
  172. coordinator.clearRuntime()
  173. })
  174. it('playSongWrapsSingleSongQueue', 0, async () => {
  175. const coordinator = PlaybackCoordinator.getInstance()
  176. let queueLength = 0
  177. coordinator.clearRuntime()
  178. coordinator.setRuntime({
  179. playQueue: async (queue) => {
  180. queueLength = queue.length
  181. },
  182. playOrPause: async () => {},
  183. playNext: async () => {},
  184. playPrevious: async () => {},
  185. seekTo: async () => {}
  186. })
  187. await coordinator.playSong(new VideoItem('Song A', '1', '/music/a.flac', 0, 0, '2026-04-02'), 'playlist-detail')
  188. expect(queueLength).assertEqual(1)
  189. coordinator.clearRuntime()
  190. })
  191. })
  192. }