• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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