blob: 6d278c1b4e7c7815c1d6f8377ceb1802da30500c [file]
// Copyright (C) 2026 The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import {parsePinIntents, PinIntentKind} from './pinIntent';
describe('parsePinIntents', () => {
describe('Dictionary inputs', () => {
it('parses wildcard CUJ dictionary "[{ cuj: \'*\' }]"', () => {
expect(parsePinIntents([{cuj: '*'}])).toEqual([
{kind: PinIntentKind.Cuj, cujName: '*'},
]);
});
it('parses single CUJ dictionary record', () => {
expect(parsePinIntents([{cuj: 'RECENTS_SCROLLING'}])).toEqual([
{kind: PinIntentKind.Cuj, cujName: 'RECENTS_SCROLLING'},
]);
});
it('parses allJankCujs and allLatencyCujs flags', () => {
expect(parsePinIntents([{allJankCujs: 'true'}])).toEqual([
{kind: PinIntentKind.Cuj, cujName: '*'},
]);
expect(parsePinIntents([{allLatencyCujs: 'true'}])).toEqual([
{kind: PinIntentKind.Cuj, cujName: '*'},
]);
});
it('parses missed frames during CUJ', () => {
const input = [
{
process: 'systemui',
cuj: 'RECENTS_SCROLLING',
jankType: 'sf_frames',
isWeighted: 'true',
},
];
expect(parsePinIntents(input)).toEqual([
{
kind: PinIntentKind.CujScopedJank,
process: 'com.android.systemui',
cujName: 'RECENTS_SCROLLING',
jankType: 'sf_frames',
isWeighted: true,
},
]);
});
it('parses blocking calls', () => {
const cujBlocking = [
{
process: 'com.android.systemui',
cuj: 'SHADE_EXPAND',
blockingCall: 'input',
aggregation: 'mean_dur_per_frame_ns-max',
},
];
expect(parsePinIntents(cujBlocking)).toEqual([
{
kind: PinIntentKind.CujBlockingCall,
process: 'com.android.systemui',
cujName: 'SHADE_EXPAND',
blockingCallName: 'input',
aggregation: 'mean_dur_per_frame_ns-max',
},
]);
});
it('parses full trace jank dictionaries', () => {
expect(
parsePinIntents([
{
process: 'systemui',
fullTrace: 'true',
jankType: 'app_frames',
},
]),
).toEqual([
{
kind: PinIntentKind.FullTraceJank,
process: 'com.android.systemui',
jankType: 'app_frames',
isWeighted: false,
},
]);
});
it('parses multiple requests in an array', () => {
const input = [{cuj: 'CUJ_A'}, {cuj: 'CUJ_B'}];
expect(parsePinIntents(input)).toEqual([
{kind: PinIntentKind.Cuj, cujName: 'CUJ_A'},
{kind: PinIntentKind.Cuj, cujName: 'CUJ_B'},
]);
});
it('parses JSON-encoded string dictionaries from URL startupCommands', () => {
const input = [
'{"cuj":"SHADE_EXPAND","process":"systemui","jankType":"sf_frames"}',
];
expect(parsePinIntents(input)).toEqual([
{
kind: PinIntentKind.CujScopedJank,
process: 'com.android.systemui',
cujName: 'SHADE_EXPAND',
jankType: 'sf_frames',
isWeighted: false,
},
]);
});
});
describe('Validation and error handling', () => {
it('returns empty array for valid empty array or array with empty dictionary', () => {
expect(parsePinIntents([])).toEqual([]);
expect(parsePinIntents([{}])).toEqual([]);
});
it('fails fast with ZodError when non-array or invalid input is provided', () => {
expect(() => parsePinIntents(undefined)).toThrow();
expect(() => parsePinIntents(null)).toThrow();
expect(() => parsePinIntents({})).toThrow();
expect(() => parsePinIntents(123)).toThrow();
expect(() => parsePinIntents(['invalid-json'])).toThrow();
});
});
});