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