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" 19#include "test/Builders.h" 20#include "test/Common.h" 21#include "test/Context.h" 22 23#include <gtest/gtest.h> 24 25namespace aapt { 26 27TEST(TableProtoSerializer, SerializeSinglePackage) { 28 std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); 29 std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() 30 .setPackageId(u"com.app.a", 0x7f) 31 .addFileReference(u"@com.app.a:layout/main", ResourceId(0x7f020000), 32 u"res/layout/main.xml") 33 .addReference(u"@com.app.a:layout/other", ResourceId(0x7f020001), 34 u"@com.app.a:layout/main") 35 .addString(u"@com.app.a:string/text", {}, u"hi") 36 .addValue(u"@com.app.a:id/foo", {}, util::make_unique<Id>()) 37 .build(); 38 39 Symbol publicSymbol; 40 publicSymbol.state = SymbolState::kPublic; 41 ASSERT_TRUE(table->setSymbolState(test::parseNameOrDie(u"@com.app.a:layout/main"), 42 ResourceId(0x7f020000), 43 publicSymbol, context->getDiagnostics())); 44 45 Id* id = test::getValue<Id>(table.get(), u"@com.app.a:id/foo"); 46 ASSERT_NE(nullptr, id); 47 48 // Make a plural. 49 std::unique_ptr<Plural> plural = util::make_unique<Plural>(); 50 plural->values[Plural::One] = util::make_unique<String>(table->stringPool.makeRef(u"one")); 51 ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:plurals/hey"), 52 ConfigDescription{}, std::string(), std::move(plural), 53 context->getDiagnostics())); 54 55 // Make a resource with different products. 56 ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:integer/one"), 57 test::parseConfigOrDie("land"), std::string(), 58 test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 123u), 59 context->getDiagnostics())); 60 ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:integer/one"), 61 test::parseConfigOrDie("land"), std::string("tablet"), 62 test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 321u), 63 context->getDiagnostics())); 64 65 // Make a reference with both resource name and resource ID. 66 // The reference should point to a resource outside of this table to test that both 67 // name and id get serialized. 68 Reference expectedRef; 69 expectedRef.name = test::parseNameOrDie(u"@android:layout/main"); 70 expectedRef.id = ResourceId(0x01020000); 71 ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:layout/abc"), 72 ConfigDescription::defaultConfig(), std::string(), 73 util::make_unique<Reference>(expectedRef), 74 context->getDiagnostics())); 75 76 std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get()); 77 ASSERT_NE(nullptr, pbTable); 78 79 std::unique_ptr<ResourceTable> newTable = deserializeTableFromPb(*pbTable, 80 Source{ "test" }, 81 context->getDiagnostics()); 82 ASSERT_NE(nullptr, newTable); 83 84 Id* newId = test::getValue<Id>(newTable.get(), u"@com.app.a:id/foo"); 85 ASSERT_NE(nullptr, newId); 86 EXPECT_EQ(id->isWeak(), newId->isWeak()); 87 88 Maybe<ResourceTable::SearchResult> result = newTable->findResource( 89 test::parseNameOrDie(u"@com.app.a:layout/main")); 90 AAPT_ASSERT_TRUE(result); 91 EXPECT_EQ(SymbolState::kPublic, result.value().type->symbolStatus.state); 92 EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state); 93 94 // Find the product-dependent values 95 BinaryPrimitive* prim = test::getValueForConfigAndProduct<BinaryPrimitive>( 96 newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), ""); 97 ASSERT_NE(nullptr, prim); 98 EXPECT_EQ(123u, prim->value.data); 99 100 prim = test::getValueForConfigAndProduct<BinaryPrimitive>( 101 newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet"); 102 ASSERT_NE(nullptr, prim); 103 EXPECT_EQ(321u, prim->value.data); 104 105 Reference* actualRef = test::getValue<Reference>(newTable.get(), u"@com.app.a:layout/abc"); 106 ASSERT_NE(nullptr, actualRef); 107 AAPT_ASSERT_TRUE(actualRef->name); 108 AAPT_ASSERT_TRUE(actualRef->id); 109 EXPECT_EQ(expectedRef.name.value(), actualRef->name.value()); 110 EXPECT_EQ(expectedRef.id.value(), actualRef->id.value()); 111} 112 113TEST(TableProtoSerializer, SerializeFileHeader) { 114 std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); 115 116 ResourceFile f; 117 f.config = test::parseConfigOrDie("hdpi-v9"); 118 f.name = test::parseNameOrDie(u"@com.app.a:layout/main"); 119 f.source.path = "res/layout-hdpi-v9/main.xml"; 120 f.exportedSymbols.push_back(SourcedResourceName{ test::parseNameOrDie(u"@+id/unchecked"), 23u }); 121 122 const std::string expectedData = "1234"; 123 124 std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f); 125 126 std::string outputStr; 127 { 128 google::protobuf::io::StringOutputStream outStream(&outputStr); 129 CompiledFileOutputStream outFileStream(&outStream, pbFile.get()); 130 131 ASSERT_TRUE(outFileStream.Write(expectedData.data(), expectedData.size())); 132 ASSERT_TRUE(outFileStream.Finish()); 133 } 134 135 CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size()); 136 const pb::CompiledFile* newPbFile = inFileStream.CompiledFile(); 137 ASSERT_NE(nullptr, newPbFile); 138 139 std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(*newPbFile, Source{ "test" }, 140 context->getDiagnostics()); 141 ASSERT_NE(nullptr, file); 142 143 std::string actualData((const char*)inFileStream.data(), inFileStream.size()); 144 EXPECT_EQ(expectedData, actualData); 145 EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(inFileStream.data()) & 0x03); 146 147 ASSERT_EQ(1u, file->exportedSymbols.size()); 148 EXPECT_EQ(test::parseNameOrDie(u"@+id/unchecked"), file->exportedSymbols[0].name); 149} 150 151TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) { 152 ResourceFile f; 153 std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f); 154 155 const std::string expectedData = "1234"; 156 157 std::string outputStr; 158 { 159 google::protobuf::io::StringOutputStream outStream(&outputStr); 160 CompiledFileOutputStream outFileStream(&outStream, pbFile.get()); 161 162 ASSERT_TRUE(outFileStream.Write(expectedData.data(), expectedData.size())); 163 ASSERT_TRUE(outFileStream.Finish()); 164 } 165 166 outputStr[0] = 0xff; 167 168 CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size()); 169 EXPECT_EQ(nullptr, inFileStream.CompiledFile()); 170 EXPECT_EQ(nullptr, inFileStream.data()); 171 EXPECT_EQ(0u, inFileStream.size()); 172} 173 174} // namespace aapt 175