UNPKG

jessibuca

Version:
278 lines (216 loc) 9.52 kB
/** * VirtualTimeline 单元测试 * 测试时间轴管理、片段查找和事件触发等核心功能 */ import { EventBus, Events } from '../EventBus'; import { VirtualTimeline, MediaSegment, PlaylistInfo, BufferRange } from '../VirtualTimeline'; describe('VirtualTimeline 基础功能测试', () => { let eventBus: EventBus; let virtualTimeline: VirtualTimeline; let mockSegments: MediaSegment[]; let mockPlaylistInfo: PlaylistInfo; beforeEach(() => { // 创建模拟对象 eventBus = new EventBus(); // 监听相关事件 jest.spyOn(eventBus, 'emit'); jest.spyOn(eventBus, 'on'); jest.spyOn(eventBus, 'request'); // 创建模拟片段 mockSegments = Array.from({ length: 5 }, (_, i) => ({ index: i, url: `http://example.com/segment_${i}.mp4`, duration: 10, virtualStartTime: i * 10, virtualEndTime: (i + 1) * 10, physicalStartTime: i * 10 + 1000, // 物理时间与虚拟时间有偏移 physicalEndTime: (i + 1) * 10 + 1000, physicalTime: new Date(Date.now() + i * 10000), codec: 'avc1.64001E', isLoaded: false, isLoading: false, isBuffered: false })); // 创建模拟播放列表信息 mockPlaylistInfo = { segments: mockSegments, totalDuration: 50 // 5个片段,每个10秒 }; // 创建VirtualTimeline实例 virtualTimeline = new VirtualTimeline(eventBus); // 模拟计时器 jest.useFakeTimers(); }); afterEach(() => { jest.clearAllMocks(); jest.useRealTimers(); virtualTimeline.destroy(); }); test('loadPlaylist - 应该正确加载播放列表并触发事件', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); // 验证事件是否触发 expect(eventBus.emit).toHaveBeenCalledWith(Events.PLAYLIST_LOADED, mockPlaylistInfo); expect(eventBus.emit).toHaveBeenCalledWith(Events.TOTAL_DURATION_UPDATE, 50); // 验证内部状态 expect(virtualTimeline['segments']).toEqual(mockSegments); expect(virtualTimeline['virtualTotalDuration']).toBe(50); }); test('createPlaylistFromM3U8 - 应该正确解析M3U8内容', () => { const m3u8Content = `#EXTM3U #EXT-X-VERSION:3 #EXT-X-TARGETDURATION:10 #EXT-X-MEDIA-SEQUENCE:0 #EXTINF:10.0,2023-10-01T12:00:00Z,avc1.64001E segment_0.mp4 #EXTINF:10.0,2023-10-01T12:00:10Z,avc1.64001E segment_1.mp4 #EXTINF:10.0,2023-10-01T12:00:20Z,avc1.64001E segment_2.mp4 #EXT-X-ENDLIST`; const baseUrl = 'http://example.com/'; const playlist = virtualTimeline.createPlaylistFromM3U8(m3u8Content, baseUrl); expect(playlist.segments.length).toBe(3); expect(playlist.totalDuration).toBe(30); // 验证第一个片段 const firstSegment = playlist.segments[0]; expect(firstSegment.url).toBe('http://example.com/segment_0.mp4'); expect(firstSegment.duration).toBe(10); expect(firstSegment.codec).toBe('avc1.64001E'); expect(firstSegment.virtualStartTime).toBe(0); expect(firstSegment.virtualEndTime).toBe(10); }); test('findSegmentForVirtualTime - 应该找到对应时间的片段', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); // 查找中间时间点的片段 const result1 = virtualTimeline.findSegmentForVirtualTime(15); expect(result1.segment).toBe(mockSegments[1]); expect(result1.offsetInSegment).toBe(5); // 15 - 10 = 5 // 查找片段边界的时间点 const result2 = virtualTimeline.findSegmentForVirtualTime(20); expect(result2.segment).toBe(mockSegments[2]); expect(result2.offsetInSegment).toBe(0); // 查找超出范围的时间点 const result3 = virtualTimeline.findSegmentForVirtualTime(60); expect(result3.segment).toBeNull(); }); test('时间转换函数 - 应该正确转换虚拟时间和物理时间', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); // 测试虚拟时间到物理时间的转换 const physicalTime = virtualTimeline.virtualToPhysicalTime(15); expect(physicalTime).toBe(1015); // 15 + 1000 (偏移量) // 测试物理时间到虚拟时间的转换 const virtualTime = virtualTimeline.physicalToVirtualTime(1025); expect(virtualTime).toBe(25); // 1025 - 1000 (偏移量) }); test('getCurrentVirtualTime 和 getTotalDuration - 应该返回正确的时间信息', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); // 模拟当前时间 virtualTimeline['currentVirtualTime'] = 25; // 测试获取当前虚拟时间 expect(virtualTimeline.getCurrentVirtualTime()).toBe(25); // 测试获取总时长 expect(virtualTimeline.getTotalDuration()).toBe(50); }); test('getProgress - 应该返回正确的播放进度百分比', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); // 模拟不同的当前时间 virtualTimeline['currentVirtualTime'] = 0; expect(virtualTimeline.getProgress()).toBe(0); virtualTimeline['currentVirtualTime'] = 25; expect(virtualTimeline.getProgress()).toBe(50); // 25/50 * 100 = 50% virtualTimeline['currentVirtualTime'] = 50; expect(virtualTimeline.getProgress()).toBe(100); // 50/50 * 100 = 100% }); test('play 和 pause - 应该正确控制播放状态', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); // 测试播放 virtualTimeline.play(); expect(virtualTimeline['isPlaying']).toBe(true); expect(eventBus.emit).toHaveBeenCalledWith(Events.PLAY); // 推进定时器 jest.advanceTimersByTime(500); expect(virtualTimeline['currentVirtualTime']).toBeGreaterThan(0); // 测试暂停 virtualTimeline.pause(); expect(virtualTimeline['isPlaying']).toBe(false); expect(eventBus.emit).toHaveBeenCalledWith(Events.PAUSE); // 记录当前时间 const timeAtPause = virtualTimeline['currentVirtualTime']; // 推进定时器 jest.advanceTimersByTime(500); // 暂停后时间不应变化 expect(virtualTimeline['currentVirtualTime']).toBe(timeAtPause); }); test('seek - 应该正确跳转到指定时间', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); // 跳转到中间位置 virtualTimeline.seek(25); // 验证内部状态和事件 expect(virtualTimeline['currentVirtualTime']).toBe(25); expect(eventBus.emit).toHaveBeenCalledWith(Events.SEEK, 25); expect(eventBus.emit).toHaveBeenCalledWith(Events.TIME_UPDATE, 25); // 跳转到超出范围的位置应该被限制 virtualTimeline.seek(60); expect(virtualTimeline['currentVirtualTime']).toBeLessThanOrEqual(50); // 跳转到负时间应该被限制 virtualTimeline.seek(-10); expect(virtualTimeline['currentVirtualTime']).toBeGreaterThanOrEqual(0); }); test('setPlaybackRate - 应该正确设置播放速率', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); // 测试设置不同播放速率 virtualTimeline.setPlaybackRate(2); expect(virtualTimeline['playbackRate']).toBe(2); virtualTimeline.setPlaybackRate(0.5); expect(virtualTimeline['playbackRate']).toBe(0.5); // 测试无效值 virtualTimeline.setPlaybackRate(0); expect(virtualTimeline['playbackRate']).toBe(0.5); // 不应改变 virtualTimeline.setPlaybackRate(-1); expect(virtualTimeline['playbackRate']).toBe(0.5); // 不应改变 }); test('handleVideoTimeUpdate - 应该正确处理视频时间更新', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); // 模拟视频时间更新事件 const handleVideoTimeUpdate = virtualTimeline['handleVideoTimeUpdate']; handleVideoTimeUpdate(15); // 验证虚拟时间是否更新 expect(virtualTimeline['currentVirtualTime']).toBe(15); expect(eventBus.emit).toHaveBeenCalledWith(Events.TIME_UPDATE, 15); }); test('destroy - 应该正确清理资源', () => { // 监听off方法 jest.spyOn(eventBus, 'off'); virtualTimeline.loadPlaylist(mockPlaylistInfo); virtualTimeline.play(); // 启动定时器 // 销毁实例 virtualTimeline.destroy(); // 验证事件监听是否取消 expect(eventBus.off).toHaveBeenCalled(); // 验证内部状态是否清理 expect(virtualTimeline['segments']).toHaveLength(0); expect(virtualTimeline['isPlaying']).toBe(false); expect(virtualTimeline['playbackTimer']).toBeNull(); }); test('emitTimeUpdate - 应该触发时间更新事件', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); virtualTimeline['currentVirtualTime'] = 25; // 调用emitTimeUpdate const emitTimeUpdate = virtualTimeline['emitTimeUpdate']; emitTimeUpdate(); // 验证事件是否触发 expect(eventBus.emit).toHaveBeenCalledWith(Events.TIME_UPDATE, 25); }); test('处理响应请求 - 应该返回正确的时间信息', () => { virtualTimeline.loadPlaylist(mockPlaylistInfo); virtualTimeline['currentVirtualTime'] = 25; // 测试处理当前时间请求 const handleCurrentTimeRequest = virtualTimeline['handleCurrentTimeRequest']; const currentTime = handleCurrentTimeRequest(); expect(currentTime).toBe(25); // 测试处理总时长请求 const handleTotalDurationRequest = virtualTimeline['handleTotalDurationRequest']; const totalDuration = handleTotalDurationRequest(); expect(totalDuration).toBe(50); }); });