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