/* * Copyright (C) 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "ResourceTable.h" #include "proto/ProtoSerialize.h" #include "test/Builders.h" #include "test/Common.h" #include "test/Context.h" #include <gtest/gtest.h> namespace aapt { TEST(TableProtoSerializer, SerializeSinglePackage) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); std::unique_ptr<ResourceTable> table = test::ResourceTableBuilder() .setPackageId(u"com.app.a", 0x7f) .addFileReference(u"@com.app.a:layout/main", ResourceId(0x7f020000), u"res/layout/main.xml") .addReference(u"@com.app.a:layout/other", ResourceId(0x7f020001), u"@com.app.a:layout/main") .addString(u"@com.app.a:string/text", {}, u"hi") .addValue(u"@com.app.a:id/foo", {}, util::make_unique<Id>()) .build(); Symbol publicSymbol; publicSymbol.state = SymbolState::kPublic; ASSERT_TRUE(table->setSymbolState(test::parseNameOrDie(u"@com.app.a:layout/main"), ResourceId(0x7f020000), publicSymbol, context->getDiagnostics())); Id* id = test::getValue<Id>(table.get(), u"@com.app.a:id/foo"); ASSERT_NE(nullptr, id); // Make a plural. std::unique_ptr<Plural> plural = util::make_unique<Plural>(); plural->values[Plural::One] = util::make_unique<String>(table->stringPool.makeRef(u"one")); ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:plurals/hey"), ConfigDescription{}, std::string(), std::move(plural), context->getDiagnostics())); // Make a resource with different products. ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:integer/one"), test::parseConfigOrDie("land"), std::string(), test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 123u), context->getDiagnostics())); ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:integer/one"), test::parseConfigOrDie("land"), std::string("tablet"), test::buildPrimitive(android::Res_value::TYPE_INT_DEC, 321u), context->getDiagnostics())); // Make a reference with both resource name and resource ID. // The reference should point to a resource outside of this table to test that both // name and id get serialized. Reference expectedRef; expectedRef.name = test::parseNameOrDie(u"@android:layout/main"); expectedRef.id = ResourceId(0x01020000); ASSERT_TRUE(table->addResource(test::parseNameOrDie(u"@com.app.a:layout/abc"), ConfigDescription::defaultConfig(), std::string(), util::make_unique<Reference>(expectedRef), context->getDiagnostics())); std::unique_ptr<pb::ResourceTable> pbTable = serializeTableToPb(table.get()); ASSERT_NE(nullptr, pbTable); std::unique_ptr<ResourceTable> newTable = deserializeTableFromPb(*pbTable, Source{ "test" }, context->getDiagnostics()); ASSERT_NE(nullptr, newTable); Id* newId = test::getValue<Id>(newTable.get(), u"@com.app.a:id/foo"); ASSERT_NE(nullptr, newId); EXPECT_EQ(id->isWeak(), newId->isWeak()); Maybe<ResourceTable::SearchResult> result = newTable->findResource( test::parseNameOrDie(u"@com.app.a:layout/main")); AAPT_ASSERT_TRUE(result); EXPECT_EQ(SymbolState::kPublic, result.value().type->symbolStatus.state); EXPECT_EQ(SymbolState::kPublic, result.value().entry->symbolStatus.state); // Find the product-dependent values BinaryPrimitive* prim = test::getValueForConfigAndProduct<BinaryPrimitive>( newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), ""); ASSERT_NE(nullptr, prim); EXPECT_EQ(123u, prim->value.data); prim = test::getValueForConfigAndProduct<BinaryPrimitive>( newTable.get(), u"@com.app.a:integer/one", test::parseConfigOrDie("land"), "tablet"); ASSERT_NE(nullptr, prim); EXPECT_EQ(321u, prim->value.data); Reference* actualRef = test::getValue<Reference>(newTable.get(), u"@com.app.a:layout/abc"); ASSERT_NE(nullptr, actualRef); AAPT_ASSERT_TRUE(actualRef->name); AAPT_ASSERT_TRUE(actualRef->id); EXPECT_EQ(expectedRef.name.value(), actualRef->name.value()); EXPECT_EQ(expectedRef.id.value(), actualRef->id.value()); } TEST(TableProtoSerializer, SerializeFileHeader) { std::unique_ptr<IAaptContext> context = test::ContextBuilder().build(); ResourceFile f; f.config = test::parseConfigOrDie("hdpi-v9"); f.name = test::parseNameOrDie(u"@com.app.a:layout/main"); f.source.path = "res/layout-hdpi-v9/main.xml"; f.exportedSymbols.push_back(SourcedResourceName{ test::parseNameOrDie(u"@+id/unchecked"), 23u }); const std::string expectedData = "1234"; std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f); std::string outputStr; { google::protobuf::io::StringOutputStream outStream(&outputStr); CompiledFileOutputStream outFileStream(&outStream, pbFile.get()); ASSERT_TRUE(outFileStream.Write(expectedData.data(), expectedData.size())); ASSERT_TRUE(outFileStream.Finish()); } CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size()); const pb::CompiledFile* newPbFile = inFileStream.CompiledFile(); ASSERT_NE(nullptr, newPbFile); std::unique_ptr<ResourceFile> file = deserializeCompiledFileFromPb(*newPbFile, Source{ "test" }, context->getDiagnostics()); ASSERT_NE(nullptr, file); std::string actualData((const char*)inFileStream.data(), inFileStream.size()); EXPECT_EQ(expectedData, actualData); EXPECT_EQ(0u, reinterpret_cast<uintptr_t>(inFileStream.data()) & 0x03); ASSERT_EQ(1u, file->exportedSymbols.size()); EXPECT_EQ(test::parseNameOrDie(u"@+id/unchecked"), file->exportedSymbols[0].name); } TEST(TableProtoSerializer, DeserializeCorruptHeaderSafely) { ResourceFile f; std::unique_ptr<pb::CompiledFile> pbFile = serializeCompiledFileToPb(f); const std::string expectedData = "1234"; std::string outputStr; { google::protobuf::io::StringOutputStream outStream(&outputStr); CompiledFileOutputStream outFileStream(&outStream, pbFile.get()); ASSERT_TRUE(outFileStream.Write(expectedData.data(), expectedData.size())); ASSERT_TRUE(outFileStream.Finish()); } outputStr[0] = 0xff; CompiledFileInputStream inFileStream(outputStr.data(), outputStr.size()); EXPECT_EQ(nullptr, inFileStream.CompiledFile()); EXPECT_EQ(nullptr, inFileStream.data()); EXPECT_EQ(0u, inFileStream.size()); } } // namespace aapt