1 /*
2  * Copyright (C) 2014 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 
17 #include <algorithm>
18 #include <androidfw/ResourceTypes.h>
19 #include <androidfw/TypeWrappers.h>
20 #include <utils/String8.h>
21 
22 #include <gtest/gtest.h>
23 
24 namespace android {
25 
26 // create a ResTable_type in memory with a vector of Res_value*
createTypeTable(std::vector<Res_value * > & values,bool compact_entry=false,bool short_offsets=false)27 static ResTable_type* createTypeTable(std::vector<Res_value*>& values,
28                              bool compact_entry = false,
29                              bool short_offsets = false)
30 {
31     ResTable_type t{};
32     t.header.type = RES_TABLE_TYPE_TYPE;
33     t.header.headerSize = sizeof(t);
34     t.header.size = sizeof(t);
35     t.id = 1;
36     t.flags = short_offsets ? ResTable_type::FLAG_OFFSET16 : 0;
37 
38     t.header.size += values.size() * (short_offsets ? sizeof(uint16_t) : sizeof(uint32_t));
39     t.entriesStart = t.header.size;
40     t.entryCount = values.size();
41 
42     size_t entry_size = compact_entry ? sizeof(ResTable_entry)
43                                       : sizeof(ResTable_entry) + sizeof(Res_value);
44     for (auto const v : values) {
45         t.header.size += v ? entry_size : 0;
46     }
47 
48     uint8_t* data = (uint8_t *)malloc(t.header.size);
49     uint8_t* p_header = data;
50     uint8_t* p_offsets = data + t.header.headerSize;
51     uint8_t* p_entries = data + t.entriesStart;
52 
53     memcpy(p_header, &t, sizeof(t));
54 
55     size_t i = 0, entry_offset = 0;
56     uint32_t k = 0;
57     for (auto const& v : values) {
58         if (short_offsets) {
59             uint16_t *p = reinterpret_cast<uint16_t *>(p_offsets) + i;
60             *p = v ? (entry_offset >> 2) & 0xffffu : 0xffffu;
61         } else {
62             uint32_t *p = reinterpret_cast<uint32_t *>(p_offsets) + i;
63             *p = v ? entry_offset : ResTable_type::NO_ENTRY;
64         }
65 
66         if (v) {
67             ResTable_entry entry{};
68             if (compact_entry) {
69                 entry.compact.key = i;
70                 entry.compact.flags = ResTable_entry::FLAG_COMPACT | (v->dataType << 8);
71                 entry.compact.data = v->data;
72                 memcpy(p_entries, &entry, sizeof(entry)); p_entries += sizeof(entry);
73                 entry_offset += sizeof(entry);
74             } else {
75                 Res_value value{};
76                 entry.full.size = sizeof(entry);
77                 entry.full.key.index = i;
78                 value = *v;
79                 memcpy(p_entries, &entry, sizeof(entry)); p_entries += sizeof(entry);
80                 memcpy(p_entries, &value, sizeof(value)); p_entries += sizeof(value);
81                 entry_offset += sizeof(entry) + sizeof(value);
82             }
83         }
84         i++;
85     }
86     return reinterpret_cast<ResTable_type*>(data);
87 }
88 
TEST(TypeVariantIteratorTest,shouldIterateOverTypeWithoutErrors)89 TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
90     std::vector<Res_value *> values;
91 
92     Res_value *v1 = new Res_value{};
93     values.push_back(v1);
94 
95     values.push_back(nullptr);
96 
97     Res_value *v2 = new Res_value{};
98     values.push_back(v2);
99 
100     Res_value *v3 = new Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x12345678};
101     values.push_back(v3);
102 
103     // test for combinations of compact_entry and short_offsets
104     for (size_t i = 0; i < 4; i++) {
105         bool compact_entry = i & 0x1, short_offsets = i & 0x2;
106         ResTable_type* data = createTypeTable(values, compact_entry, short_offsets);
107         TypeVariant v(data);
108 
109         TypeVariant::iterator iter = v.beginEntries();
110         ASSERT_EQ(uint32_t(0), iter.index());
111         ASSERT_TRUE(NULL != *iter);
112         ASSERT_EQ(uint32_t(0), iter->key());
113         ASSERT_NE(v.endEntries(), iter);
114 
115         iter++;
116 
117         ASSERT_EQ(uint32_t(1), iter.index());
118         ASSERT_TRUE(NULL == *iter);
119         ASSERT_NE(v.endEntries(), iter);
120 
121         iter++;
122 
123         ASSERT_EQ(uint32_t(2), iter.index());
124         ASSERT_TRUE(NULL != *iter);
125         ASSERT_EQ(uint32_t(2), iter->key());
126         ASSERT_NE(v.endEntries(), iter);
127 
128         iter++;
129 
130         ASSERT_EQ(uint32_t(3), iter.index());
131         ASSERT_TRUE(NULL != *iter);
132         ASSERT_EQ(iter->is_compact(), compact_entry);
133         ASSERT_EQ(uint32_t(3), iter->key());
134         ASSERT_EQ(uint32_t(0x12345678), iter->value().data);
135         ASSERT_EQ(Res_value::TYPE_STRING, iter->value().dataType);
136 
137         iter++;
138 
139         ASSERT_EQ(v.endEntries(), iter);
140 
141         free(data);
142     }
143 }
144 
145 } // namespace android
146