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  TamperedMessageType,
20  TamperedProtoField,
21} from 'parsers/tampered_message_type';
22import root from 'protos/test/fake_proto/json';
23import {PropertyTreeBuilder} from 'test/unit/property_tree_builder';
24import {
25  PropertySource,
26  PropertyTreeNode,
27} from 'trace/tree_node/property_tree_node';
28import {AddDefaults} from './add_defaults';
29
30describe('AddDefaults', () => {
31  let propertyRoot: PropertyTreeNode;
32  let operation: AddDefaults;
33  let rootField: TamperedProtoField;
34
35  beforeEach(() => {
36    rootField = TamperedMessageType.tamper(root.lookupType('RootMessage'))
37      .fields['entry'];
38    propertyRoot = new PropertyTreeBuilder()
39      .setIsRoot(true)
40      .setRootId('test')
41      .setName('node')
42      .build();
43  });
44
45  it('adds only defaults from allowlist', () => {
46    operation = new AddDefaults(rootField, ['number_32bit']);
47    operation.apply(propertyRoot);
48    expect(propertyRoot.getAllChildren().length).toEqual(1);
49    const defaultNode = assertDefined(
50      propertyRoot.getChildByName('number_32bit'),
51    );
52    expect(defaultNode.getValue()).toEqual(0);
53    checkAllNodesAreDefault(propertyRoot);
54  });
55
56  it('adds all defaults from prototype definition in absence of allowlist', () => {
57    operation = new AddDefaults(rootField);
58    operation.apply(propertyRoot);
59    expect(propertyRoot.getAllChildren().length).toEqual(11);
60    checkAllNodesAreDefault(propertyRoot);
61    expect(
62      assertDefined(propertyRoot.getChildByName('array')).getValue(),
63    ).toEqual([]);
64    expect(
65      assertDefined(propertyRoot.getChildByName('number_32bit')).getValue(),
66    ).toEqual(0);
67    expect(
68      assertDefined(propertyRoot.getChildByName('number_64bit')).getValue(),
69    ).toEqual(0n);
70    expect(
71      assertDefined(propertyRoot.getChildByName('boolValue')).getValue(),
72    ).toBeFalse();
73  });
74
75  it('does not add defaults in denylist', () => {
76    operation = new AddDefaults(rootField, undefined, [
77      'number_32bit',
78      'number_64bit',
79    ]);
80    operation.apply(propertyRoot);
81
82    expect(propertyRoot.getAllChildren().length).toEqual(9);
83    checkAllNodesAreDefault(propertyRoot);
84    expect(propertyRoot.getChildByName('number_32bit')).toBeUndefined();
85    expect(propertyRoot.getChildByName('number_64bit')).toBeUndefined();
86  });
87
88  it('replaces undefined proto node with default node', () => {
89    operation = new AddDefaults(rootField, ['number_32bit']);
90    propertyRoot = new PropertyTreeBuilder()
91      .setIsRoot(true)
92      .setRootId('test')
93      .setName('node')
94      .setChildren([{name: 'number_32bit', value: undefined}])
95      .build();
96    operation.apply(propertyRoot);
97
98    expect(propertyRoot.getAllChildren().length).toEqual(1);
99    const defaultNode = assertDefined(
100      propertyRoot.getChildByName('number_32bit'),
101    );
102    expect(defaultNode.getValue()).toEqual(0);
103    checkAllNodesAreDefault(propertyRoot);
104  });
105
106  function checkAllNodesAreDefault(root: PropertyTreeNode) {
107    root.getAllChildren().forEach((child) => {
108      expect(child.source).toEqual(PropertySource.DEFAULT);
109    });
110  }
111});
112