1/* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17import {assertDefined} from 'common/assert_utils'; 18import {TimestampConverterUtils} from 'common/time/test_utils'; 19import {TimeUtils} from 'common/time/time_utils'; 20import {TracePositionUpdate} from 'messaging/winscope_event'; 21import { 22 AbstractLogViewerPresenter, 23 NotifyLogViewCallbackType, 24} from './abstract_log_viewer_presenter'; 25import {LogSelectFilter, LogTextFilter} from './log_filters'; 26import {LogHeader, UiDataLog} from './ui_data_log'; 27 28export abstract class AbstractLogViewerPresenterTest<UiData extends UiDataLog> { 29 execute() { 30 describe('Common tests', () => { 31 let uiData: UiData; 32 let presenter: AbstractLogViewerPresenter<UiData, object>; 33 34 beforeAll(async () => { 35 await this.setUpTestEnvironment(); 36 }); 37 38 beforeEach(async () => { 39 jasmine.addCustomEqualityTester(filterEqualityTester); 40 presenter = await this.createPresenter((newData) => { 41 uiData = newData; 42 }); 43 await TimeUtils.wait(() => !uiData.isFetchingData); 44 if (this.resetTestEnvironment) { 45 this.resetTestEnvironment(); 46 } 47 }); 48 49 it('is robust to empty trace', async () => { 50 const presenter = await this.createPresenterWithEmptyTrace( 51 (newData: UiData) => (uiData = newData), 52 ); 53 await presenter.onAppEvent( 54 TracePositionUpdate.fromTimestamp( 55 TimestampConverterUtils.makeRealTimestamp(0n), 56 ), 57 ); 58 await TimeUtils.wait(() => !uiData.isFetchingData); 59 for (const [index, expectedHeader] of this.expectedHeaders.entries()) { 60 const header = uiData.headers[index]; 61 expect(header.spec).toEqual(expectedHeader.header.spec); 62 if (expectedHeader.options) { 63 expect((header.filter as LogSelectFilter)?.options).toEqual([]); 64 } 65 } 66 if (this.executePropertiesChecksForEmptyTrace) { 67 this.executePropertiesChecksForEmptyTrace(uiData); 68 } 69 }); 70 71 it('processes trace position updates', async () => { 72 await assertDefined(presenter).onAppEvent( 73 assertDefined(this.getPositionUpdate()), 74 ); 75 await TimeUtils.wait(() => !uiData.isFetchingData); 76 for (const [index, expectedHeader] of this.expectedHeaders.entries()) { 77 const header = uiData.headers[index]; 78 expect(header).toEqual(expectedHeader.header); 79 if (expectedHeader.options) { 80 expect((header.filter as LogSelectFilter).options).toEqual( 81 expectedHeader.options, 82 ); 83 } 84 } 85 if (this.executePropertiesChecksAfterPositionUpdate) { 86 this.executePropertiesChecksAfterPositionUpdate(uiData); 87 } 88 }); 89 }); 90 91 function filterEqualityTester( 92 first: unknown, 93 second: unknown, 94 ): boolean | undefined { 95 if (first instanceof LogTextFilter && second instanceof LogTextFilter) { 96 return ( 97 first.textFilter.filterString === second.textFilter.filterString && 98 first.textFilter.flags.length === second.textFilter.flags.length && 99 first.textFilter.flags.every( 100 (flag, index) => flag === second.textFilter.flags[index], 101 ) 102 ); 103 } 104 if ( 105 first instanceof LogSelectFilter && 106 second instanceof LogSelectFilter 107 ) { 108 return ( 109 first.options.length === second.options.length && 110 first.shouldFilterBySubstring === second.shouldFilterBySubstring 111 ); 112 } 113 return undefined; 114 } 115 116 if (this.executeSpecializedTests) { 117 this.executeSpecializedTests(); 118 } 119 } 120 121 abstract readonly expectedHeaders: Array<{ 122 header: LogHeader; 123 options?: string[]; 124 }>; 125 126 abstract setUpTestEnvironment(): Promise<void>; 127 abstract createPresenter( 128 callback: NotifyLogViewCallbackType<UiData>, 129 ): Promise<AbstractLogViewerPresenter<UiData, object>>; 130 abstract createPresenterWithEmptyTrace( 131 callback: NotifyLogViewCallbackType<UiData>, 132 ): Promise<AbstractLogViewerPresenter<UiData, object>>; 133 abstract getPositionUpdate(): TracePositionUpdate; 134 135 resetTestEnvironment?(): void; 136 executePropertiesChecksForEmptyTrace?(uiData: UiDataLog): void; 137 executePropertiesChecksAfterPositionUpdate?(uiData: UiDataLog): void; 138 executeSpecializedTests?(): void; 139} 140