1// Copyright (C) 2023 The Android Open Source Project 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15import {createStore, Track, TrackDescriptor} from '../public'; 16 17import {createEmptyState} from './empty_state'; 18import {TrackManager} from './track_cache'; 19 20function makeMockTrack(): Track { 21 return { 22 onCreate: jest.fn(), 23 onUpdate: jest.fn(), 24 onDestroy: jest.fn(), 25 26 render: jest.fn(), 27 onFullRedraw: jest.fn(), 28 getSliceRect: jest.fn(), 29 getHeight: jest.fn(), 30 getTrackShellButtons: jest.fn(), 31 onMouseMove: jest.fn(), 32 onMouseClick: jest.fn(), 33 onMouseOut: jest.fn(), 34 }; 35} 36 37async function settle() { 38 await new Promise((r) => setTimeout(r, 0)); 39} 40 41let track: Track; 42let td: TrackDescriptor; 43let trackCache: TrackManager; 44 45beforeEach(() => { 46 track = makeMockTrack(); 47 td = { 48 uri: 'test', 49 trackFactory: () => track, 50 }; 51 const store = createStore(createEmptyState()); 52 trackCache = new TrackManager(store); 53}); 54 55describe('TrackCache', () => { 56 it('calls track lifecycle hooks', async () => { 57 const entry = trackCache.resolveTrack('foo', td); 58 entry.update(); 59 await settle(); 60 expect(track.onUpdate).toHaveBeenCalledTimes(1); 61 }); 62 63 it('reuses tracks', async () => { 64 const first = trackCache.resolveTrack('foo', td); 65 trackCache.flushOldTracks(); 66 first.update(); 67 const second = trackCache.resolveTrack('foo', td); 68 trackCache.flushOldTracks(); 69 second.update(); 70 71 // Ensure onCreate only called once 72 expect(track.onCreate).toHaveBeenCalledTimes(1); 73 expect(first).toBe(second); 74 }); 75 76 it('destroys tracks', async () => { 77 const t = trackCache.resolveTrack('foo', td); 78 t.update(); 79 80 // Double flush should destroy all tracks 81 trackCache.flushOldTracks(); 82 trackCache.flushOldTracks(); 83 84 await settle(); 85 86 // Ensure onCreate only called once 87 expect(track.onDestroy).toHaveBeenCalledTimes(1); 88 }); 89}); 90 91describe('TrackCacheEntry', () => { 92 it('updates', async () => { 93 const entry = trackCache.resolveTrack('foo', td); 94 entry.update(); 95 await settle(); 96 expect(track.onUpdate).toHaveBeenCalledTimes(1); 97 }); 98 99 it('throws if updated when destroyed', async () => { 100 const entry = trackCache.resolveTrack('foo', td); 101 102 // Double flush should destroy all tracks 103 trackCache.flushOldTracks(); 104 trackCache.flushOldTracks(); 105 106 await settle(); 107 108 expect(() => entry.update()).toThrow(); 109 }); 110}); 111