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 {TraceSearchQueryFailed} from 'messaging/user_warnings'; 19import {ParserSurfaceFlinger} from 'parsers/surface_flinger/perfetto/parser_surface_flinger'; 20import {TimestampConverterUtils} from 'test/unit/timestamp_converter_utils'; 21import {UserNotifierChecker} from 'test/unit/user_notifier_checker'; 22import {UnitTestUtils} from 'test/unit/utils'; 23import {CoarseVersion} from 'trace/coarse_version'; 24import {TraceType} from 'trace/trace_type'; 25import {ParserSearch} from './parser_search'; 26 27describe('ParserSearch', () => { 28 let userNotifierChecker: UserNotifierChecker; 29 let parser: ParserSearch; 30 31 beforeAll(() => { 32 userNotifierChecker = new UserNotifierChecker(); 33 jasmine.addCustomEqualityTester(UnitTestUtils.timestampEqualityTester); 34 }); 35 36 afterEach(() => { 37 userNotifierChecker.reset(); 38 }); 39 40 describe('valid query with timestamps', () => { 41 beforeAll(async () => { 42 parser = await createParser( 43 'SELECT * FROM surfaceflinger_layers_snapshot', 44 ); 45 }); 46 47 afterEach(() => { 48 userNotifierChecker.expectNone(); 49 }); 50 51 it('has expected trace type', () => { 52 expect(parser.getTraceType()).toEqual(TraceType.SEARCH); 53 }); 54 55 it('has expected coarse version', () => { 56 expect(parser.getCoarseVersion()).toEqual(CoarseVersion.LATEST); 57 }); 58 59 it('has length entries equal to number of rows', () => { 60 expect(parser.getLengthEntries()).toEqual(21); 61 }); 62 63 it('provides timestamps', () => { 64 const expected = [ 65 TimestampConverterUtils.makeElapsedTimestamp(14500282843n), 66 TimestampConverterUtils.makeElapsedTimestamp(14631249355n), 67 TimestampConverterUtils.makeElapsedTimestamp(15403446377n), 68 ]; 69 const actual = assertDefined(parser.getTimestamps()).slice(0, 3); 70 expect(actual).toEqual(expected); 71 userNotifierChecker.expectNone(); 72 }); 73 74 it('provides query result', async () => { 75 const entry = await parser.getEntry(0); 76 expect(entry.numRows()).toEqual(21); 77 const firstRow = entry.iter({}); 78 expect(firstRow.get('id')).toEqual(0n); 79 expect(firstRow.get('ts')).toEqual(14500282843n); 80 expect(firstRow.get('type')).toEqual('surfaceflinger_layers_snapshot'); 81 expect(firstRow.get('arg_set_id')).toEqual(176n); 82 }); 83 }); 84 85 describe('valid query without timestamps', () => { 86 beforeAll(async () => { 87 parser = await createParser('SELECT * FROM surfaceflinger_layer'); 88 userNotifierChecker.expectNone(); 89 }); 90 91 afterEach(() => { 92 userNotifierChecker.expectNone(); 93 }); 94 95 it('has length entries equal to 1 so query result can be accessed', () => { 96 expect(parser.getLengthEntries()).toEqual(1); 97 }); 98 99 it('provides one invalid timestamp so query result can be accessed', () => { 100 expect(parser.getTimestamps()).toEqual([ 101 TimestampConverterUtils.makeZeroTimestamp(), 102 ]); 103 }); 104 105 it('provides query result', async () => { 106 const entry = await parser.getEntry(0); 107 expect(entry.numRows()).toEqual(1815); 108 const firstRow = entry.iter({}); 109 expect(firstRow.get('id')).toEqual(0n); 110 expect(firstRow.get('type')).toEqual('surfaceflinger_layer'); 111 expect(firstRow.get('arg_set_id')).toEqual(1n); 112 expect(firstRow.get('snapshot_id')).toEqual(0n); 113 }); 114 }); 115 116 describe('valid query without rows', () => { 117 beforeAll(async () => { 118 parser = await createParser('SELECT * FROM surfaceflinger_transactions'); 119 }); 120 121 afterEach(() => { 122 userNotifierChecker.expectNone(); 123 }); 124 125 it('has length entries equal to 1 so query result can be accessed', () => { 126 expect(parser.getLengthEntries()).toEqual(1); 127 }); 128 129 it('provides one invalid timestamp so query result can be accessed', () => { 130 expect(parser.getTimestamps()).toEqual([ 131 TimestampConverterUtils.makeZeroTimestamp(), 132 ]); 133 }); 134 135 it('provides query result', async () => { 136 const entry = await parser.getEntry(0); 137 expect(entry.columns()).toEqual([ 138 'id', 139 'type', 140 'ts', 141 'arg_set_id', 142 'base64_proto', 143 'base64_proto_id', 144 ]); 145 expect(entry.numRows()).toEqual(0); 146 }); 147 }); 148 149 it('notifies user of parsing error before throwing error', async () => { 150 const createFailingParser = () => createParser('SELECT * FROM fake_table'); 151 await expectAsync(createFailingParser()).toBeRejected(); 152 userNotifierChecker.reset(); 153 try { 154 parser = await createFailingParser(); 155 } catch (e) { 156 userNotifierChecker.expectNotified([ 157 new TraceSearchQueryFailed((e as Error).message), 158 ]); 159 } 160 }); 161 162 async function createParser(query: string): Promise<ParserSearch> { 163 await ( 164 (await UnitTestUtils.getPerfettoParser( 165 TraceType.SURFACE_FLINGER, 166 'traces/perfetto/layers_trace.perfetto-trace', 167 )) as ParserSurfaceFlinger 168 ).parse(); 169 parser = new ParserSearch(query, UnitTestUtils.getTimestampConverter()); 170 await parser.parse(); 171 return parser; 172 } 173}); 174