androidfw: Add tests for compact entries/short offsets
Bug: 237583012
This CL tests iteration through the resource entries with combinations of:
- 32-bit offsets + normal entry/value pairs
- 32-bit offsets + compact entries
- 16-bit offsets + normal entry/value pairs
- 16-bit offsets + compact entries
Change-Id: Ia348c4b05dde0bdc31e75671126b98809643f0f1
diff --git a/libs/androidfw/tests/TypeWrappers_test.cpp b/libs/androidfw/tests/TypeWrappers_test.cpp
index aba3ab3..ed30904 100644
--- a/libs/androidfw/tests/TypeWrappers_test.cpp
+++ b/libs/androidfw/tests/TypeWrappers_test.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <algorithm>
#include <androidfw/ResourceTypes.h>
#include <androidfw/TypeWrappers.h>
#include <utils/String8.h>
@@ -22,88 +23,123 @@
namespace android {
-void* createTypeData() {
- ResTable_type t;
- memset(&t, 0, sizeof(t));
+// create a ResTable_type in memory with a vector of Res_value*
+static ResTable_type* createTypeTable(std::vector<Res_value*>& values,
+ bool compact_entry = false,
+ bool short_offsets = false)
+{
+ ResTable_type t{};
t.header.type = RES_TABLE_TYPE_TYPE;
t.header.headerSize = sizeof(t);
+ t.header.size = sizeof(t);
t.id = 1;
- t.entryCount = 3;
+ t.flags = short_offsets ? ResTable_type::FLAG_OFFSET16 : 0;
- uint32_t offsets[3];
- t.entriesStart = t.header.headerSize + sizeof(offsets);
- t.header.size = t.entriesStart;
+ t.header.size += values.size() * (short_offsets ? sizeof(uint16_t) : sizeof(uint32_t));
+ t.entriesStart = t.header.size;
+ t.entryCount = values.size();
- offsets[0] = 0;
- ResTable_entry e1;
- memset(&e1, 0, sizeof(e1));
- e1.full.size = sizeof(e1);
- e1.full.key.index = 0;
- t.header.size += sizeof(e1);
+ size_t entry_size = compact_entry ? sizeof(ResTable_entry)
+ : sizeof(ResTable_entry) + sizeof(Res_value);
+ for (auto const v : values) {
+ t.header.size += v ? entry_size : 0;
+ }
- Res_value v1;
- memset(&v1, 0, sizeof(v1));
- t.header.size += sizeof(v1);
+ uint8_t* data = (uint8_t *)malloc(t.header.size);
+ uint8_t* p_header = data;
+ uint8_t* p_offsets = data + t.header.headerSize;
+ uint8_t* p_entries = data + t.entriesStart;
- offsets[1] = ResTable_type::NO_ENTRY;
+ memcpy(p_header, &t, sizeof(t));
- offsets[2] = sizeof(e1) + sizeof(v1);
- ResTable_entry e2;
- memset(&e2, 0, sizeof(e2));
- e2.full.size = sizeof(e2);
- e2.full.key.index = 1;
- t.header.size += sizeof(e2);
+ size_t i = 0, entry_offset = 0;
+ uint32_t k = 0;
+ for (auto const& v : values) {
+ if (short_offsets) {
+ uint16_t *p = reinterpret_cast<uint16_t *>(p_offsets) + i;
+ *p = v ? (entry_offset >> 2) & 0xffffu : 0xffffu;
+ } else {
+ uint32_t *p = reinterpret_cast<uint32_t *>(p_offsets) + i;
+ *p = v ? entry_offset : ResTable_type::NO_ENTRY;
+ }
- Res_value v2;
- memset(&v2, 0, sizeof(v2));
- t.header.size += sizeof(v2);
-
- uint8_t* data = (uint8_t*)malloc(t.header.size);
- uint8_t* p = data;
- memcpy(p, &t, sizeof(t));
- p += sizeof(t);
- memcpy(p, offsets, sizeof(offsets));
- p += sizeof(offsets);
- memcpy(p, &e1, sizeof(e1));
- p += sizeof(e1);
- memcpy(p, &v1, sizeof(v1));
- p += sizeof(v1);
- memcpy(p, &e2, sizeof(e2));
- p += sizeof(e2);
- memcpy(p, &v2, sizeof(v2));
- p += sizeof(v2);
- return data;
+ if (v) {
+ ResTable_entry entry{};
+ if (compact_entry) {
+ entry.compact.key = i;
+ entry.compact.flags = ResTable_entry::FLAG_COMPACT | (v->dataType << 8);
+ entry.compact.data = v->data;
+ memcpy(p_entries, &entry, sizeof(entry)); p_entries += sizeof(entry);
+ entry_offset += sizeof(entry);
+ } else {
+ Res_value value{};
+ entry.full.size = sizeof(entry);
+ entry.full.key.index = i;
+ value = *v;
+ memcpy(p_entries, &entry, sizeof(entry)); p_entries += sizeof(entry);
+ memcpy(p_entries, &value, sizeof(value)); p_entries += sizeof(value);
+ entry_offset += sizeof(entry) + sizeof(value);
+ }
+ }
+ i++;
+ }
+ return reinterpret_cast<ResTable_type*>(data);
}
TEST(TypeVariantIteratorTest, shouldIterateOverTypeWithoutErrors) {
- ResTable_type* data = (ResTable_type*) createTypeData();
+ std::vector<Res_value *> values;
- TypeVariant v(data);
+ Res_value *v1 = new Res_value{};
+ values.push_back(v1);
- TypeVariant::iterator iter = v.beginEntries();
- ASSERT_EQ(uint32_t(0), iter.index());
- ASSERT_TRUE(NULL != *iter);
- ASSERT_EQ(uint32_t(0), iter->full.key.index);
- ASSERT_NE(v.endEntries(), iter);
+ values.push_back(nullptr);
- iter++;
+ Res_value *v2 = new Res_value{};
+ values.push_back(v2);
- ASSERT_EQ(uint32_t(1), iter.index());
- ASSERT_TRUE(NULL == *iter);
- ASSERT_NE(v.endEntries(), iter);
+ Res_value *v3 = new Res_value{ sizeof(Res_value), 0, Res_value::TYPE_STRING, 0x12345678};
+ values.push_back(v3);
- iter++;
+ // test for combinations of compact_entry and short_offsets
+ for (size_t i = 0; i < 4; i++) {
+ bool compact_entry = i & 0x1, short_offsets = i & 0x2;
+ ResTable_type* data = createTypeTable(values, compact_entry, short_offsets);
+ TypeVariant v(data);
- ASSERT_EQ(uint32_t(2), iter.index());
- ASSERT_TRUE(NULL != *iter);
- ASSERT_EQ(uint32_t(1), iter->full.key.index);
- ASSERT_NE(v.endEntries(), iter);
+ TypeVariant::iterator iter = v.beginEntries();
+ ASSERT_EQ(uint32_t(0), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(uint32_t(0), iter->key());
+ ASSERT_NE(v.endEntries(), iter);
- iter++;
+ iter++;
- ASSERT_EQ(v.endEntries(), iter);
+ ASSERT_EQ(uint32_t(1), iter.index());
+ ASSERT_TRUE(NULL == *iter);
+ ASSERT_NE(v.endEntries(), iter);
- free(data);
+ iter++;
+
+ ASSERT_EQ(uint32_t(2), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(uint32_t(2), iter->key());
+ ASSERT_NE(v.endEntries(), iter);
+
+ iter++;
+
+ ASSERT_EQ(uint32_t(3), iter.index());
+ ASSERT_TRUE(NULL != *iter);
+ ASSERT_EQ(iter->is_compact(), compact_entry);
+ ASSERT_EQ(uint32_t(3), iter->key());
+ ASSERT_EQ(uint32_t(0x12345678), iter->value().data);
+ ASSERT_EQ(Res_value::TYPE_STRING, iter->value().dataType);
+
+ iter++;
+
+ ASSERT_EQ(v.endEntries(), iter);
+
+ free(data);
+ }
}
} // namespace android