text: /*
   * Copyright (C) 2015 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 "link/ReferenceLinker.h"
  
  #include "test/Test.h"
  
  using ::android::ResTable_map;
  using ::testing::Eq;
  using ::testing::IsNull;
  using ::testing::NotNull;
  
  namespace aapt {
  
  TEST(ReferenceLinkerTest, LinkSimpleReferences) {
    std::unique_ptr<ResourceTable> table =
        test::ResourceTableBuilder()
            .SetPackageId("com.app.test", 0x7f)
            .AddReference("com.app.test:string/foo", ResourceId(0x7f020000),
                          "com.app.test:string/bar")
  
            // Test use of local reference (w/o package name).
            .AddReference("com.app.test:string/bar", ResourceId(0x7f020001),
                          "string/baz")
  
            .AddReference("com.app.test:string/baz", ResourceId(0x7f020002),
                          "android:string/ok")
            .Build();
  
    std::unique_ptr<IAaptContext> context =
        test::ContextBuilder()
            .SetCompilationPackage("com.app.test")
            .SetPackageId(0x7f)
            .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
            .AddSymbolSource(
                util::make_unique<ResourceTableSymbolSource>(table.get()))
            .AddSymbolSource(
                test::StaticSymbolSourceBuilder()
                    .AddPublicSymbol("android:string/ok", ResourceId(0x01040034))
                    .Build())
            .Build();
  
    ReferenceLinker linker;
    ASSERT_TRUE(linker.Consume(context.get(), table.get()));
  
    Reference* ref = test::GetValue<Reference>(table.get(), "com.app.test:string/foo");
    ASSERT_THAT(ref, NotNull());
    ASSERT_TRUE(ref->id);
    EXPECT_EQ(ResourceId(0x7f020001), ref->id.value());
  
    ref = test::GetValue<Reference>(table.get(), "com.app.test:string/bar");
    ASSERT_THAT(ref, NotNull());
    ASSERT_TRUE(ref->id);
    EXPECT_EQ(ResourceId(0x7f020002), ref->id.value());
  
    ref = test::GetValue<Reference>(table.get(), "com.app.test:string/baz");
    ASSERT_THAT(ref, NotNull());
    ASSERT_TRUE(ref->id);
    EXPECT_EQ(ResourceId(0x01040034), ref->id.value());
  }
  
  TEST(ReferenceLinkerTest, LinkStyleAttributes) {
    std::unique_ptr<ResourceTable> table =
        test::ResourceTableBuilder()
            .SetPackageId("com.app.test", 0x7f)
            .AddValue("com.app.test:style/Theme",
                      test::StyleBuilder()
                          .SetParent("android:style/Theme.Material")
                          .AddItem("android:attr/foo",
                                   ResourceUtils::TryParseColor("#ff00ff"))
                          .AddItem("android:attr/bar", {} /* placeholder */)
                          .Build())
            .Build();
  
    {
      // We need to fill in the value for the attribute android:attr/bar after we
      // build the table, because we need access to the string pool.
      Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
      ASSERT_THAT(style, NotNull());
      style->entries.back().value =
          util::make_unique<RawString>(table->string_pool.MakeRef("one|two"));
    }
  
    std::unique_ptr<IAaptContext> context =
        test::ContextBuilder()
            .SetCompilationPackage("com.app.test")
            .SetPackageId(0x7f)
            .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
            .AddSymbolSource(
                test::StaticSymbolSourceBuilder()
                    .AddPublicSymbol("android:style/Theme.Material",
                                     ResourceId(0x01060000))
                    .AddPublicSymbol("android:attr/foo", ResourceId(0x01010001),
                                     test::AttributeBuilder()
                                         .SetTypeMask(ResTable_map::TYPE_COLOR)
                                         .Build())
                    .AddPublicSymbol("android:attr/bar", ResourceId(0x01010002),
                                     test::AttributeBuilder()
                                         .SetTypeMask(ResTable_map::TYPE_FLAGS)
                                         .AddItem("one", 0x01)
                                         .AddItem("two", 0x02)
                                         .Build())
                    .Build())
            .Build();
  
    ReferenceLinker linker;
    ASSERT_TRUE(linker.Consume(context.get(), table.get()));
  
    Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
    ASSERT_THAT(style, NotNull());
    ASSERT_TRUE(style->parent);
    ASSERT_TRUE(style->parent.value().id);
    EXPECT_EQ(ResourceId(0x01060000), style->parent.value().id.value());
  
    ASSERT_EQ(2u, style->entries.size());
  
    ASSERT_TRUE(style->entries[0].key.id);
    EXPECT_EQ(ResourceId(0x01010001), style->entries[0].key.id.value());
    ASSERT_THAT(ValueCast<BinaryPrimitive>(style->entries[0].value.get()), NotNull());
  
    ASSERT_TRUE(style->entries[1].key.id);
    EXPECT_EQ(ResourceId(0x01010002), style->entries[1].key.id.value());
    ASSERT_THAT(ValueCast<BinaryPrimitive>(style->entries[1].value.get()), NotNull());
  }
  
  TEST(ReferenceLinkerTest, LinkMangledReferencesAndAttributes) {
    std::unique_ptr<IAaptContext> context =
        test::ContextBuilder()
            .SetCompilationPackage("com.app.test")
            .SetPackageId(0x7f)
            .SetNameManglerPolicy(
                NameManglerPolicy{"com.app.test", {"com.android.support"}})
            .AddSymbolSource(
                test::StaticSymbolSourceBuilder()
                    .AddPublicSymbol("com.app.test:attr/com.android.support$foo",
                                     ResourceId(0x7f010000),
                                     test::AttributeBuilder()
                                         .SetTypeMask(ResTable_map::TYPE_COLOR)
                                         .Build())
                    .Build())
            .Build();
  
    std::unique_ptr<ResourceTable> table =
        test::ResourceTableBuilder()
            .SetPackageId("com.app.test", 0x7f)
            .AddValue("com.app.test:style/Theme", ResourceId(0x7f020000),
                      test::StyleBuilder()
                          .AddItem("com.android.support:attr/foo",
                                   ResourceUtils::TryParseColor("#ff0000"))
                          .Build())
            .Build();
  
    ReferenceLinker linker;
    ASSERT_TRUE(linker.Consume(context.get(), table.get()));
  
    Style* style = test::GetValue<Style>(table.get(), "com.app.test:style/Theme");
    ASSERT_THAT(style, NotNull());
    ASSERT_EQ(1u, style->entries.size());
    ASSERT_TRUE(style->entries.front().key.id);
    EXPECT_EQ(ResourceId(0x7f010000), style->entries.front().key.id.value());
  }
  
  TEST(ReferenceLinkerTest, FailToLinkPrivateSymbols) {
    std::unique_ptr<ResourceTable> table =
        test::ResourceTableBuilder()
            .SetPackageId("com.app.test", 0x7f)
            .AddReference("com.app.test:string/foo", ResourceId(0x7f020000),
                          "android:string/hidden")
            .Build();
  
    std::unique_ptr<IAaptContext> context =
        test::ContextBuilder()
            .SetCompilationPackage("com.app.test")
            .SetPackageId(0x7f)
            .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
            .AddSymbolSource(
                util::make_unique<ResourceTableSymbolSource>(table.get()))
            .AddSymbolSource(
                test::StaticSymbolSourceBuilder()
                    .AddSymbol("android:string/hidden", ResourceId(0x01040034))
                    .Build())
            .Build();
  
    ReferenceLinker linker;
    ASSERT_FALSE(linker.Consume(context.get(), table.get()));
  }
  
  TEST(ReferenceLinkerTest, FailToLinkPrivateMangledSymbols) {
    std::unique_ptr<ResourceTable> table =
        test::ResourceTableBuilder()
            .SetPackageId("com.app.test", 0x7f)
            .AddReference("com.app.test:string/foo", ResourceId(0x7f020000),
                          "com.app.lib:string/hidden")
            .Build();
  
    std::unique_ptr<IAaptContext> context =
        test::ContextBuilder()
            .SetCompilationPackage("com.app.test")
            .SetPackageId(0x7f)
            .SetNameManglerPolicy(
                NameManglerPolicy{"com.app.test", {"com.app.lib"}})
            .AddSymbolSource(
                util::make_unique<ResourceTableSymbolSource>(table.get()))
            .AddSymbolSource(
                test::StaticSymbolSourceBuilder()
                    .AddSymbol("com.app.test:string/com.app.lib$hidden",
                               ResourceId(0x7f040034))
                    .Build())
  
            .Build();
  
    ReferenceLinker linker;
    ASSERT_FALSE(linker.Consume(context.get(), table.get()));
  }
  
  TEST(ReferenceLinkerTest, FailToLinkPrivateStyleAttributes) {
    std::unique_ptr<ResourceTable> table =
        test::ResourceTableBuilder()
            .SetPackageId("com.app.test", 0x7f)
            .AddValue("com.app.test:style/Theme",
                      test::StyleBuilder()
                          .AddItem("android:attr/hidden",
                                   ResourceUtils::TryParseColor("#ff00ff"))
                          .Build())
            .Build();
  
    std::unique_ptr<IAaptContext> context =
        test::ContextBuilder()
            .SetCompilationPackage("com.app.test")
            .SetPackageId(0x7f)
            .SetNameManglerPolicy(NameManglerPolicy{"com.app.test"})
            .AddSymbolSource(
                util::make_unique<ResourceTableSymbolSource>(table.get()))
            .AddSymbolSource(
                test::StaticSymbolSourceBuilder()
                    .AddSymbol("android:attr/hidden", ResourceId(0x01010001),
                               test::AttributeBuilder()
                                   .SetTypeMask(android::ResTable_map::TYPE_COLOR)
                                   .Build())
                    .Build())
            .Build();
  
    ReferenceLinker linker;
    ASSERT_FALSE(linker.Consume(context.get(), table.get()));
  }
  
  TEST(ReferenceLinkerTest, AppsWithSamePackageButDifferentIdAreVisibleNonPublic) {
    NameMangler mangler(NameManglerPolicy{"com.app.test"});
    SymbolTable table(&mangler);
    table.AppendSource(test::StaticSymbolSourceBuilder()
                           .AddSymbol("com.app.test:string/foo", ResourceId(0x7f010000))
                           .Build());
  
    std::string error;
    const CallSite call_site{"com.app.test"};
    const SymbolTable::Symbol* symbol = ReferenceLinker::ResolveSymbolCheckVisibility(
        *test::BuildReference("com.app.test:string/foo"), call_site, &table, &error);
    ASSERT_THAT(symbol, NotNull());
    EXPECT_TRUE(error.empty());
  }
  
  TEST(ReferenceLinkerTest, AppsWithDifferentPackageCanNotUseEachOthersAttribute) {
    NameMangler mangler(NameManglerPolicy{"com.app.ext"});
    SymbolTable table(&mangler);
    table.AppendSource(test::StaticSymbolSourceBuilder()
                           .AddSymbol("com.app.test:attr/foo", ResourceId(0x7f010000),
                                      test::AttributeBuilder().Build())
                           .AddPublicSymbol("com.app.test:attr/public_foo", ResourceId(0x7f010001),
                                            test::AttributeBuilder().Build())
                           .Build());
  
    std::string error;
    const CallSite call_site{"com.app.ext"};
  
    EXPECT_FALSE(ReferenceLinker::CompileXmlAttribute(
        *test::BuildReference("com.app.test:attr/foo"), call_site, &table, &error));
    EXPECT_FALSE(error.empty());
  
    error = "";
    ASSERT_TRUE(ReferenceLinker::CompileXmlAttribute(
        *test::BuildReference("com.app.test:attr/public_foo"), call_site, &table, &error));
    EXPECT_TRUE(error.empty());
  }
  
  TEST(ReferenceLinkerTest, ReferenceWithNoPackageUsesCallSitePackage) {
    NameMangler mangler(NameManglerPolicy{"com.app.test"});
    SymbolTable table(&mangler);
    table.AppendSource(test::StaticSymbolSourceBuilder()
                           .AddSymbol("com.app.test:string/foo", ResourceId(0x7f010000))
                           .AddSymbol("com.app.lib:string/foo", ResourceId(0x7f010001))
                           .Build());
  
    const SymbolTable::Symbol* s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
                                                                  CallSite{"com.app.test"}, &table);
    ASSERT_THAT(s, NotNull());
    EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010000)));
  
    s = ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"), CallSite{"com.app.lib"},
                                       &table);
    ASSERT_THAT(s, NotNull());
    EXPECT_THAT(s->id, Eq(make_value<ResourceId>(0x7f010001)));
  
    EXPECT_THAT(ReferenceLinker::ResolveSymbol(*test::BuildReference("string/foo"),
                                               CallSite{"com.app.bad"}, &table),
                IsNull());
  }
  
  }  // namespace aapt
  