xref: /aosp_15_r20/development/tools/winscope/src/viewers/common/abstract_log_viewer_presenter_test.ts (revision 90c8c64db3049935a07c6143d7fd006e26f8ecca)
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 {TracePositionUpdate} from 'messaging/winscope_event';
19import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils';
20import {
21  AbstractLogViewerPresenter,
22  NotifyLogViewCallbackType,
23} from './abstract_log_viewer_presenter';
24import {LogSelectFilter, LogTextFilter} from './log_filters';
25import {LogHeader, UiDataLog} from './ui_data_log';
26
27export abstract class AbstractLogViewerPresenterTest<UiData extends UiDataLog> {
28  execute() {
29    describe('Common tests', () => {
30      let uiData: UiData;
31      let presenter: AbstractLogViewerPresenter<UiData, object>;
32
33      beforeAll(async () => {
34        await this.setUpTestEnvironment();
35      });
36
37      beforeEach(async () => {
38        jasmine.addCustomEqualityTester(filterEqualityTester);
39        presenter = await this.createPresenter((newData) => {
40          uiData = newData;
41        });
42        if (this.resetTestEnvironment) {
43          this.resetTestEnvironment();
44        }
45      });
46
47      it('is robust to empty trace', async () => {
48        const presenter = await this.createPresenterWithEmptyTrace(
49          (newData: UiData) => (uiData = newData),
50        );
51        await presenter.onAppEvent(
52          TracePositionUpdate.fromTimestamp(
53            TimestampConverterUtils.makeRealTimestamp(0n),
54          ),
55        );
56        for (const [index, expectedHeader] of this.expectedHeaders.entries()) {
57          const header = uiData.headers[index];
58          expect(header.spec).toEqual(expectedHeader.header.spec);
59          if (expectedHeader.options) {
60            expect((header.filter as LogSelectFilter)?.options).toEqual([]);
61          }
62        }
63        if (this.executePropertiesChecksForEmptyTrace) {
64          this.executePropertiesChecksForEmptyTrace(uiData);
65        }
66      });
67
68      it('processes trace position updates', async () => {
69        await assertDefined(presenter).onAppEvent(
70          assertDefined(this.getPositionUpdate()),
71        );
72        for (const [index, expectedHeader] of this.expectedHeaders.entries()) {
73          const header = uiData.headers[index];
74          expect(header).toEqual(expectedHeader.header);
75          if (expectedHeader.options) {
76            expect((header.filter as LogSelectFilter).options).toEqual(
77              expectedHeader.options,
78            );
79          }
80        }
81        if (this.executePropertiesChecksAfterPositionUpdate) {
82          this.executePropertiesChecksAfterPositionUpdate(uiData);
83        }
84      });
85    });
86
87    function filterEqualityTester(
88      first: any,
89      second: any,
90    ): boolean | undefined {
91      if (first instanceof LogTextFilter && second instanceof LogTextFilter) {
92        return (
93          first.textFilter.filterString === second.textFilter.filterString &&
94          first.textFilter.flags.length === second.textFilter.flags.length &&
95          first.textFilter.flags.every(
96            (flag, index) => flag === second.textFilter.flags[index],
97          )
98        );
99      }
100      if (
101        first instanceof LogSelectFilter &&
102        second instanceof LogSelectFilter
103      ) {
104        return (
105          first.options.length === second.options.length &&
106          first.shouldFilterBySubstring === second.shouldFilterBySubstring
107        );
108      }
109      return undefined;
110    }
111
112    if (this.executeSpecializedTests) {
113      this.executeSpecializedTests();
114    }
115  }
116
117  abstract readonly expectedHeaders: Array<{
118    header: LogHeader;
119    options?: string[];
120  }>;
121
122  abstract setUpTestEnvironment(): Promise<void>;
123  abstract createPresenter(
124    callback: NotifyLogViewCallbackType<UiData>,
125  ): Promise<AbstractLogViewerPresenter<UiData, object>>;
126  abstract createPresenterWithEmptyTrace(
127    callback: NotifyLogViewCallbackType<UiData>,
128  ): Promise<AbstractLogViewerPresenter<UiData, object>>;
129  abstract getPositionUpdate(): TracePositionUpdate;
130
131  resetTestEnvironment?(): void;
132  executePropertiesChecksForEmptyTrace?(uiData: UiDataLog): void;
133  executePropertiesChecksAfterPositionUpdate?(uiData: UiDataLog): void;
134  executeSpecializedTests?(): void;
135}
136