• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2023 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 {TamperedMessageType} from 'parsers/tampered_message_type';
18import root from 'protos/test/fake_proto/json';
19import {FakeProto, FakeProtoBuilder} from './fake_proto_builder';
20import {FakeProtoTransformer} from './fake_proto_transformer';
21
22interface Arg {
23  key: string;
24  value_type: string;
25  int_value?: bigint;
26  real_value?: number;
27  string_value?: string;
28}
29
30describe('FakeProtoBuilder', () => {
31  it('boolean', () => {
32    const args = [makeArg('a', false), makeArg('b', true)];
33    const proto = buildFakeProto(args);
34    expect(proto.a).toEqual(false);
35    expect(proto.b).toEqual(true);
36  });
37
38  it('number', () => {
39    const args = [makeArg('a', 1), makeArg('b', 10)];
40    const proto = buildFakeProto(args);
41    expect(proto.a).toEqual(1);
42    expect(proto.b).toEqual(10);
43  });
44
45  it('bigint', () => {
46    const args = [makeArg('a', 1n), makeArg('b', 10n)];
47    const proto = buildFakeProto(args);
48    expect(proto.a).toEqual(1n);
49    expect(proto.b).toEqual(10n);
50  });
51
52  it('string', () => {
53    const args = [makeArg('a', 'valuea'), makeArg('b', 'valueb')];
54    const proto = buildFakeProto(args);
55    expect(proto.a).toEqual('valuea');
56    expect(proto.b).toEqual('valueb');
57  });
58
59  it('array', () => {
60    const args = [
61      // Note: intentional random order + gap
62      makeArg('array[3]', 13),
63      makeArg('array[1]', 11),
64      makeArg('array[0]', 10),
65    ];
66    const proto = buildFakeProto(args);
67    expect(proto.array).toEqual([10, 11, undefined, 13]);
68  });
69
70  it('handles complex structure', () => {
71    const args = [
72      makeArg('a.b', false),
73      makeArg('a.numbers[0]', 10),
74      makeArg('a.numbers[1]', 11),
75      makeArg('a.objects[0].c.d', '20'),
76      makeArg('a.objects[0].c.e', '21'),
77      makeArg('a.objects[1].c', 21n),
78    ];
79    const proto = buildFakeProto(args);
80    expect(proto.a.b).toEqual(false);
81    expect(proto.a.numbers[0]).toEqual(10);
82    expect(proto.a.numbers[1]).toEqual(11);
83    expect(proto.a.objects[0].c.d).toEqual('20');
84    expect(proto.a.objects[0].c.e).toEqual('21');
85    expect(proto.a.objects[1].c).toEqual(21n);
86  });
87
88  it('converts snake_case to camelCase', () => {
89    const args = [
90      makeArg('_case_64bit', 10),
91      makeArg('case_64bit', 11),
92      makeArg('case_64bit_lsb', 12),
93      makeArg('case_64_bit', 13),
94      makeArg('case_64_bit_lsb', 14),
95    ];
96    const proto = buildFakeProto(args);
97
98    // Check it matches the snake_case to camelCase conversion performed by protobuf library (within the transformer)
99    const transformed = new FakeProtoTransformer(
100      TamperedMessageType.tamper(root.lookupType('Entry')),
101    ).transform(proto);
102
103    expect(transformed._case_64bit).toEqual(10n);
104    expect(transformed.case_64bit).toEqual(11n);
105    expect(transformed.case_64bitLsb).toEqual(12n);
106    expect(transformed.case_64Bit).toEqual(13n);
107    expect(transformed.case_64BitLsb).toEqual(14n);
108  });
109
110  const makeArg = (key: string, value: any): Arg => {
111    if (value === null) {
112      return {key, value_type: 'null'};
113    }
114
115    switch (typeof value) {
116      case 'boolean':
117        return {key, value_type: 'bool', int_value: BigInt(value)};
118      case 'bigint':
119        return {key, value_type: 'int', int_value: value};
120      case 'number':
121        return {key, value_type: 'real', real_value: value};
122      case 'string':
123        return {key, value_type: 'string', string_value: value};
124      default:
125        throw new Error(`Unexpected value type: ${typeof value}`);
126    }
127  };
128
129  const buildFakeProto = (args: Arg[]): FakeProto => {
130    const builder = new FakeProtoBuilder();
131    args.forEach((arg) => {
132      builder.addArg(
133        arg.key,
134        arg.value_type,
135        arg.int_value,
136        arg.real_value,
137        arg.string_value,
138      );
139    });
140    return builder.build();
141  };
142});
143