| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 "ResourceTable.h" |
| 18 | #include "proto/ProtoSerialize.h" |
| Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 19 | #include "test/Test.h" |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 20 | |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 21 | using namespace google::protobuf::io; |
| 22 | |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 23 | namespace aapt { |
| 24 | |
| 25 | TEST(TableProtoSerializer, SerializeSinglePackage) { |
| 26 | std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); |
| 27 | std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() |
| Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 28 | .setPackageId("com.app.a", 0x7f) |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 29 | .addFileReference("com.app.a:layout/main", ResourceId(0x7f020000), |
| Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 30 | "res/layout/main.xml") |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 31 | .addReference("com.app.a:layout/other", ResourceId(0x7f020001), |
| 32 | "com.app.a:layout/main") |
| 33 | .addString("com.app.a:string/text", {}, "hi") |
| 34 | .addValue("com.app.a:id/foo", {}, util::make_unique<Id>()) |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 35 | .build(); |
| 36 | |
| 37 | Symbol publicSymbol; |
| 38 | publicSymbol.state = SymbolState::kPublic; |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 39 | ASSERT_TRUE(table->setSymbolState(test::parseNameOrDie("com.app.a:layout/main"), |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 40 | ResourceId(0x7f020000), |
| 41 | publicSymbol, context->getDiagnostics())); |
| 42 | |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 43 | Id* id = test::getValue<Id>(table.get(), "com.app.a:id/foo"); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 44 | ASSERT_NE(nullptr, id); |
| 45 | |
| 46 | // Make a plural. |
| 47 | std::unique_ptr<Plural> plural = util::make_unique<Plural>(); |
| Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 48 | plural->values[Plural::One] = util::make_unique<String>(table->stringPool.makeRef("one")); |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 49 | ASSERT_TRUE(table->addResource(test::parseNameOrDie("com.app.a:plurals/hey"), |
| Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 50 | ConfigDescription{}, {}, std::move(plural), |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 51 | context->getDiagnostics())); |
| 52 | |
| Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 53 | // Make a resource with different products. |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 54 | ASSERT_TRUE(table->addResource(test::parseNameOrDie("com.app.a:integer/one"), |
| Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 55 | test::parseConfigOrDie("land"), {}, |
| Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 56 | test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 123u), |
| 57 | context->getDiagnostics())); |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 58 | ASSERT_TRUE(table->addResource(test::parseNameOrDie("com.app.a:integer/one"), |
| Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 59 | test::parseConfigOrDie("land"), "tablet", |
| Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 60 | test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 321u), |
| 61 | context->getDiagnostics())); |
| 62 | |
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 63 | // Make a reference with both resource name and resource ID. |
| 64 | // The reference should point to a resource outside of this table to test that both |
| 65 | // name and id get serialized. |
| 66 | Reference expectedRef; |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 67 | expectedRef.name = test::parseNameOrDie("android:layout/main"); |
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 68 | expectedRef.id = ResourceId(0x01020000); |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 69 | ASSERT_TRUE(table->addResource(test::parseNameOrDie("com.app.a:layout/abc"), |
| Adam Lesinski | d0f116b | 2016-07-08 15:00:32 -0700 | [diff] [blame] | 70 | ConfigDescription::defaultConfig(), {}, |
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 71 | util::make_unique<Reference>(expectedRef), |
| 72 | context->getDiagnostics())); |
| 73 | |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 74 | std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get()); |
| 75 | ASSERT_NE(nullptr, pbTable); |
| 76 | |
| 77 | std::unique_ptr<ResourceTable> newTable = deserializeTableFromPb(*pbTable, |
| 78 | Source{ "test" }, |
| 79 | context->getDiagnostics()); |
| 80 | ASSERT_NE(nullptr, newTable); |
| 81 | |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 82 | Id* newId = test::getValue<Id>(newTable.get(), "com.app.a:id/foo"); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 83 | ASSERT_NE(nullptr, newId); |
| 84 | EXPECT_EQ(id->isWeak(), newId->isWeak()); |
| 85 | |
| 86 | Maybe<ResourceTable::SearchResult> result = newTable->findResource( |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 87 | test::parseNameOrDie("com.app.a:layout/main")); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 88 | AAPT_ASSERT_TRUE(result); |
| 89 | EXPECT_EQ(SymbolState::kPublic, result.value().type->symbolStatus.state); |
| 90 | EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state); |
| Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 91 | |
| 92 | // Find the product-dependent values |
| 93 | BinaryPrimitive* prim = test::getValueForConfigAndProduct<BinaryPrimitive>( |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 94 | newTable.get(), "com.app.a:integer/one", test::parseConfigOrDie("land"), ""); |
| Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 95 | ASSERT_NE(nullptr, prim); |
| 96 | EXPECT_EQ(123u, prim->value.data); |
| 97 | |
| 98 | prim = test::getValueForConfigAndProduct<BinaryPrimitive>( |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 99 | newTable.get(), "com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet"); |
| Adam Lesinski | e4bb9eb | 2016-02-12 22:18:51 -0800 | [diff] [blame] | 100 | ASSERT_NE(nullptr, prim); |
| 101 | EXPECT_EQ(321u, prim->value.data); |
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 102 | |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 103 | Reference* actualRef = test::getValue<Reference>(newTable.get(), "com.app.a:layout/abc"); |
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 104 | ASSERT_NE(nullptr, actualRef); |
| 105 | AAPT_ASSERT_TRUE(actualRef->name); |
| 106 | AAPT_ASSERT_TRUE(actualRef->id); |
| 107 | EXPECT_EQ(expectedRef.name.value(), actualRef->name.value()); |
| 108 | EXPECT_EQ(expectedRef.id.value(), actualRef->id.value()); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 109 | } |
| 110 | |
| 111 | TEST(TableProtoSerializer, SerializeFileHeader) { |
| 112 | std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); |
| 113 | |
| 114 | ResourceFile f; |
| 115 | f.config = test::parseConfigOrDie("hdpi-v9"); |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 116 | f.name = test::parseNameOrDie("com.app.a:layout/main"); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 117 | f.source.path = "res/layout-hdpi-v9/main.xml"; |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 118 | f.exportedSymbols.push_back(SourcedResourceName{ test::parseNameOrDie("id/unchecked"), 23u }); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 119 | |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 120 | const std::string expectedData1 = "123"; |
| 121 | const std::string expectedData2 = "1234"; |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 122 | |
| 123 | std::string outputStr; |
| 124 | { |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 125 | std::unique_ptr<pb::CompiledFile> pbFile1 = serializeCompiledFileToPb(f); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 126 | |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 127 | f.name.entry = "__" + f.name.entry + "$0"; |
| 128 | std::unique_ptr<pb::CompiledFile> pbFile2 = serializeCompiledFileToPb(f); |
| 129 | |
| 130 | StringOutputStream outStream(&outputStr); |
| 131 | CompiledFileOutputStream outFileStream(&outStream); |
| 132 | outFileStream.WriteLittleEndian32(2); |
| 133 | outFileStream.WriteCompiledFile(pbFile1.get()); |
| 134 | outFileStream.WriteData(expectedData1.data(), expectedData1.size()); |
| 135 | outFileStream.WriteCompiledFile(pbFile2.get()); |
| 136 | outFileStream.WriteData(expectedData2.data(), expectedData2.size()); |
| 137 | ASSERT_FALSE(outFileStream.HadError()); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size()); |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 141 | uint32_t numFiles = 0; |
| 142 | ASSERT_TRUE(inFileStream.ReadLittleEndian32(&numFiles)); |
| 143 | ASSERT_EQ(2u, numFiles); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 144 | |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 145 | // Read the first compiled file. |
| 146 | |
| 147 | pb::CompiledFile newPbFile; |
| 148 | ASSERT_TRUE(inFileStream.ReadCompiledFile(&newPbFile)); |
| 149 | |
| 150 | std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(newPbFile, Source("test"), |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 151 | context->getDiagnostics()); |
| 152 | ASSERT_NE(nullptr, file); |
| 153 | |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 154 | uint64_t offset, len; |
| 155 | ASSERT_TRUE(inFileStream.ReadDataMetaData(&offset, &len)); |
| 156 | |
| 157 | std::string actualData(outputStr.data() + offset, len); |
| 158 | EXPECT_EQ(expectedData1, actualData); |
| 159 | |
| 160 | // Expect the data to be aligned. |
| 161 | EXPECT_EQ(0u, offset & 0x03); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 162 | |
| 163 | ASSERT_EQ(1u, file->exportedSymbols.size()); |
| Adam Lesinski | 58a20a6 | 2016-07-25 17:56:58 -0700 | [diff] [blame] | 164 | EXPECT_EQ(test::parseNameOrDie("id/unchecked"), file->exportedSymbols[0].name); |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 165 | |
| 166 | // Read the second compiled file. |
| 167 | |
| 168 | ASSERT_TRUE(inFileStream.ReadCompiledFile(&newPbFile)); |
| 169 | |
| 170 | file = deserializeCompiledFileFromPb(newPbFile, Source("test"), context->getDiagnostics()); |
| 171 | ASSERT_NE(nullptr, file); |
| 172 | |
| 173 | ASSERT_TRUE(inFileStream.ReadDataMetaData(&offset, &len)); |
| 174 | |
| 175 | actualData = std::string(outputStr.data() + offset, len); |
| 176 | EXPECT_EQ(expectedData2, actualData); |
| 177 | |
| 178 | // Expect the data to be aligned. |
| 179 | EXPECT_EQ(0u, offset & 0x03); |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 180 | } |
| 181 | |
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 182 | TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) { |
| 183 | ResourceFile f; |
| 184 | std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f); |
| 185 | |
| 186 | const std::string expectedData = "1234"; |
| 187 | |
| 188 | std::string outputStr; |
| 189 | { |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 190 | StringOutputStream outStream(&outputStr); |
| 191 | CompiledFileOutputStream outFileStream(&outStream); |
| 192 | outFileStream.WriteLittleEndian32(1); |
| 193 | outFileStream.WriteCompiledFile(pbFile.get()); |
| 194 | outFileStream.WriteData(expectedData.data(), expectedData.size()); |
| 195 | ASSERT_FALSE(outFileStream.HadError()); |
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 196 | } |
| 197 | |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 198 | outputStr[4] = 0xff; |
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 199 | |
| 200 | CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size()); |
| Adam Lesinski | 5eeaadd | 2016-08-25 12:26:56 -0700 | [diff] [blame^] | 201 | |
| 202 | uint32_t numFiles = 0; |
| 203 | EXPECT_TRUE(inFileStream.ReadLittleEndian32(&numFiles)); |
| 204 | EXPECT_EQ(1u, numFiles); |
| 205 | |
| 206 | pb::CompiledFile newPbFile; |
| 207 | EXPECT_FALSE(inFileStream.ReadCompiledFile(&newPbFile)); |
| 208 | |
| 209 | uint64_t offset, len; |
| 210 | EXPECT_FALSE(inFileStream.ReadDataMetaData(&offset, &len)); |
| Adam Lesinski | 64587af | 2016-02-18 18:33:06 -0800 | [diff] [blame] | 211 | } |
| 212 | |
| Adam Lesinski | 59e04c6 | 2016-02-04 15:59:23 -0800 | [diff] [blame] | 213 | } // namespace aapt |