AAPT2: Fix overlay support
Supports the <add-resource> tag and mimics old AAPT behavior of
not allowing new resources defined unless <add-resource> was used
or --auto-add-overlay was specified.
Change-Id: I9b461137357617ade37fd7045b418b8e6450b9c4
diff --git a/tools/aapt2/link/TableMerger_test.cpp b/tools/aapt2/link/TableMerger_test.cpp
index b7ffba7..fa4afd3 100644
--- a/tools/aapt2/link/TableMerger_test.cpp
+++ b/tools/aapt2/link/TableMerger_test.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
+#include "io/FileSystem.h"
#include "link/TableMerger.h"
-
#include "test/Builders.h"
#include "test/Context.h"
@@ -57,10 +57,11 @@
.build();
ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable);
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+ io::FileCollection collection;
- ASSERT_TRUE(merger.merge({}, tableA.get(), false));
- ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get(), &collection));
EXPECT_TRUE(merger.getMergedPackages().count(u"com.app.b") != 0);
@@ -76,6 +77,60 @@
AAPT_EXPECT_TRUE(finalTable.findResource(test::parseNameOrDie(u"@com.app.a:id/com.app.b$foo")));
}
+TEST_F(TableMergerTest, MergeFile) {
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, options);
+
+ ResourceFile fileDesc;
+ fileDesc.config = test::parseConfigOrDie("hdpi-v4");
+ fileDesc.name = test::parseNameOrDie(u"@layout/main");
+ fileDesc.source = Source("res/layout-hdpi/main.xml");
+ test::TestFile testFile("path/to/res/layout-hdpi/main.xml.flat");
+
+ ASSERT_TRUE(merger.mergeFile(fileDesc, &testFile));
+
+ FileReference* file = test::getValueForConfig<FileReference>(&finalTable,
+ u"@com.app.a:layout/main",
+ test::parseConfigOrDie("hdpi-v4"));
+ ASSERT_NE(nullptr, file);
+ EXPECT_EQ(std::u16string(u"res/layout-hdpi-v4/main.xml"), *file->path);
+
+ ResourceName name = test::parseNameOrDie(u"@com.app.a:layout/main");
+ ResourceKeyRef key = { name, test::parseConfigOrDie("hdpi-v4") };
+
+ auto iter = merger.getFilesToMerge().find(key);
+ ASSERT_NE(merger.getFilesToMerge().end(), iter);
+
+ const FileToMerge& actualFileToMerge = iter->second;
+ EXPECT_EQ(&testFile, actualFileToMerge.file);
+ EXPECT_EQ(std::string("res/layout-hdpi-v4/main.xml"), actualFileToMerge.dstPath);
+}
+
+TEST_F(TableMergerTest, MergeFileOverlay) {
+ ResourceTable finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+
+ ResourceFile fileDesc;
+ fileDesc.name = test::parseNameOrDie(u"@xml/foo");
+ test::TestFile fileA("path/to/fileA.xml.flat");
+ test::TestFile fileB("path/to/fileB.xml.flat");
+
+ ASSERT_TRUE(merger.mergeFile(fileDesc, &fileA));
+ ASSERT_TRUE(merger.mergeFileOverlay(fileDesc, &fileB));
+
+ ResourceName name = test::parseNameOrDie(u"@com.app.a:xml/foo");
+ ResourceKeyRef key = { name, ConfigDescription{} };
+ auto iter = merger.getFilesToMerge().find(key);
+ ASSERT_NE(merger.getFilesToMerge().end(), iter);
+
+ const FileToMerge& actualFileToMerge = iter->second;
+ EXPECT_EQ(&fileB, actualFileToMerge.file);
+}
+
TEST_F(TableMergerTest, MergeFileReferences) {
std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
.setPackageId(u"com.app.a", 0x7f)
@@ -87,10 +142,12 @@
.build();
ResourceTable finalTable;
- TableMerger merger(mContext.get(), &finalTable);
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+ io::FileCollection collection;
+ collection.insertFile("res/xml/file.xml");
- ASSERT_TRUE(merger.merge({}, tableA.get(), false));
- ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get()));
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeAndMangle({}, u"com.app.b", tableB.get(), &collection));
FileReference* f = test::getValue<FileReference>(&finalTable, u"@com.app.a:xml/file");
ASSERT_NE(f, nullptr);
@@ -100,13 +157,90 @@
ASSERT_NE(f, nullptr);
EXPECT_EQ(std::u16string(u"res/xml/com.app.b$file.xml"), *f->path);
- std::queue<FileToMerge>* filesToMerge = merger.getFileMergeQueue();
- ASSERT_FALSE(filesToMerge->empty());
+ ResourceName name = test::parseNameOrDie(u"@com.app.a:xml/com.app.b$file");
+ ResourceKeyRef key = { name, ConfigDescription{} };
+ auto iter = merger.getFilesToMerge().find(key);
+ ASSERT_NE(merger.getFilesToMerge().end(), iter);
- FileToMerge& fileToMerge = filesToMerge->front();
- EXPECT_EQ(fileToMerge.srcTable, tableB.get());
- EXPECT_EQ(fileToMerge.srcPath, u"res/xml/file.xml");
- EXPECT_EQ(fileToMerge.dstPath, u"res/xml/com.app.b$file.xml");
+ const FileToMerge& actualFileToMerge = iter->second;
+ EXPECT_EQ(Source("res/xml/file.xml"), actualFileToMerge.file->getSource());
+ EXPECT_EQ(std::string("res/xml/com.app.b$file.xml"), actualFileToMerge.dstPath);
+}
+
+TEST_F(TableMergerTest, OverrideResourceWithOverlay) {
+ std::unique_ptr<ResourceTable> base = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x00)
+ .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+ .build();
+ std::unique_ptr<ResourceTable> overlay = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x00)
+ .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"false"))
+ .build();
+
+ ResourceTable finalTable;
+ TableMergerOptions tableMergerOptions;
+ tableMergerOptions.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, tableMergerOptions);
+
+ ASSERT_TRUE(merger.merge({}, base.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, overlay.get()));
+
+ BinaryPrimitive* foo = test::getValue<BinaryPrimitive>(&finalTable, u"@com.app.a:bool/foo");
+ ASSERT_NE(nullptr, foo);
+ EXPECT_EQ(0x0u, foo->value.data);
+}
+
+TEST_F(TableMergerTest, MergeAddResourceFromOverlay) {
+ std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .setSymbolState(u"@bool/foo", {}, SymbolState::kUndefined)
+ .build();
+ std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+ .build();
+
+ ResourceTable finalTable;
+ TableMerger merger(mContext.get(), &finalTable, TableMergerOptions{});
+
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+}
+
+TEST_F(TableMergerTest, MergeAddResourceFromOverlayWithAutoAddOverlay) {
+ std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .build();
+ std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+ .build();
+
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = true;
+ TableMerger merger(mContext.get(), &finalTable, options);
+
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_TRUE(merger.mergeOverlay({}, tableB.get()));
+}
+
+TEST_F(TableMergerTest, FailToMergeNewResourceWithoutAutoAddOverlay) {
+ std::unique_ptr<ResourceTable> tableA = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .build();
+ std::unique_ptr<ResourceTable> tableB = test::ResourceTableBuilder()
+ .setPackageId(u"", 0x7f)
+ .addValue(u"@bool/foo", ResourceUtils::tryParseBool(u"true"))
+ .build();
+
+ ResourceTable finalTable;
+ TableMergerOptions options;
+ options.autoAddOverlay = false;
+ TableMerger merger(mContext.get(), &finalTable, options);
+
+ ASSERT_TRUE(merger.merge({}, tableA.get()));
+ ASSERT_FALSE(merger.mergeOverlay({}, tableB.get()));
}
} // namespace aapt