blob: da7f410b8b0860e95b7a72ec857e68874f3e917e [file] [log] [blame]
/*
* 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/ManifestFixer.h"
#include "test/Test.h"
using ::android::StringPiece;
using ::testing::NotNull;
namespace aapt {
struct ManifestFixerTest : public ::testing::Test {
std::unique_ptr<IAaptContext> mContext;
void SetUp() override {
mContext =
test::ContextBuilder()
.SetCompilationPackage("android")
.SetPackageId(0x01)
.SetNameManglerPolicy(NameManglerPolicy{"android"})
.AddSymbolSource(
test::StaticSymbolSourceBuilder()
.AddSymbol(
"android:attr/package", ResourceId(0x01010000),
test::AttributeBuilder()
.SetTypeMask(android::ResTable_map::TYPE_STRING)
.Build())
.AddSymbol(
"android:attr/minSdkVersion", ResourceId(0x01010001),
test::AttributeBuilder()
.SetTypeMask(android::ResTable_map::TYPE_STRING |
android::ResTable_map::TYPE_INTEGER)
.Build())
.AddSymbol(
"android:attr/targetSdkVersion", ResourceId(0x01010002),
test::AttributeBuilder()
.SetTypeMask(android::ResTable_map::TYPE_STRING |
android::ResTable_map::TYPE_INTEGER)
.Build())
.AddSymbol("android:string/str", ResourceId(0x01060000))
.Build())
.Build();
}
std::unique_ptr<xml::XmlResource> Verify(const StringPiece& str) {
return VerifyWithOptions(str, {});
}
std::unique_ptr<xml::XmlResource> VerifyWithOptions(
const StringPiece& str, const ManifestFixerOptions& options) {
std::unique_ptr<xml::XmlResource> doc = test::BuildXmlDom(str);
ManifestFixer fixer(options);
if (fixer.Consume(mContext.get(), doc.get())) {
return doc;
}
return {};
}
};
TEST_F(ManifestFixerTest, EnsureManifestIsRootTag) {
EXPECT_EQ(nullptr, Verify("<other-tag />"));
EXPECT_EQ(nullptr, Verify("<ns:manifest xmlns:ns=\"com\" />"));
EXPECT_NE(nullptr, Verify("<manifest package=\"android\"></manifest>"));
}
TEST_F(ManifestFixerTest, EnsureManifestHasPackage) {
EXPECT_NE(nullptr, Verify("<manifest package=\"android\" />"));
EXPECT_NE(nullptr, Verify("<manifest package=\"com.android\" />"));
EXPECT_NE(nullptr, Verify("<manifest package=\"com.android.google\" />"));
EXPECT_EQ(nullptr,
Verify("<manifest package=\"com.android.google.Class$1\" />"));
EXPECT_EQ(nullptr, Verify("<manifest "
"xmlns:android=\"http://schemas.android.com/apk/"
"res/android\" "
"android:package=\"com.android\" />"));
EXPECT_EQ(nullptr, Verify("<manifest package=\"@string/str\" />"));
}
TEST_F(ManifestFixerTest, AllowMetaData) {
auto doc = Verify(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<meta-data />
<application>
<meta-data />
<activity android:name=".Hi"><meta-data /></activity>
<activity-alias android:name=".Ho"><meta-data /></activity-alias>
<receiver android:name=".OffTo"><meta-data /></receiver>
<provider android:name=".Work"><meta-data /></provider>
<service android:name=".We"><meta-data /></service>
</application>
<instrumentation android:name=".Go"><meta-data /></instrumentation>
</manifest>)EOF");
ASSERT_NE(nullptr, doc);
}
TEST_F(ManifestFixerTest, UseDefaultSdkVersionsIfNonePresent) {
ManifestFixerOptions options = {std::string("8"), std::string("22")};
std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-sdk android:minSdkVersion="7" android:targetSdkVersion="21" />
</manifest>)EOF",
options);
ASSERT_NE(nullptr, doc);
xml::Element* el;
xml::Attribute* attr;
el = doc->root.get();
ASSERT_NE(nullptr, el);
el = el->FindChild({}, "uses-sdk");
ASSERT_NE(nullptr, el);
attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
ASSERT_NE(nullptr, attr);
EXPECT_EQ("7", attr->value);
attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
ASSERT_NE(nullptr, attr);
EXPECT_EQ("21", attr->value);
doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-sdk android:targetSdkVersion="21" />
</manifest>)EOF",
options);
ASSERT_NE(nullptr, doc);
el = doc->root.get();
ASSERT_NE(nullptr, el);
el = el->FindChild({}, "uses-sdk");
ASSERT_NE(nullptr, el);
attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
ASSERT_NE(nullptr, attr);
EXPECT_EQ("8", attr->value);
attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
ASSERT_NE(nullptr, attr);
EXPECT_EQ("21", attr->value);
doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-sdk />
</manifest>)EOF",
options);
ASSERT_NE(nullptr, doc);
el = doc->root.get();
ASSERT_NE(nullptr, el);
el = el->FindChild({}, "uses-sdk");
ASSERT_NE(nullptr, el);
attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
ASSERT_NE(nullptr, attr);
EXPECT_EQ("8", attr->value);
attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
ASSERT_NE(nullptr, attr);
EXPECT_EQ("22", attr->value);
doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android" />)EOF",
options);
ASSERT_NE(nullptr, doc);
el = doc->root.get();
ASSERT_NE(nullptr, el);
el = el->FindChild({}, "uses-sdk");
ASSERT_NE(nullptr, el);
attr = el->FindAttribute(xml::kSchemaAndroid, "minSdkVersion");
ASSERT_NE(nullptr, attr);
EXPECT_EQ("8", attr->value);
attr = el->FindAttribute(xml::kSchemaAndroid, "targetSdkVersion");
ASSERT_NE(nullptr, attr);
EXPECT_EQ("22", attr->value);
}
TEST_F(ManifestFixerTest, UsesSdkMustComeBeforeApplication) {
ManifestFixerOptions options = {std::string("8"), std::string("22")};
std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<application android:name=".MainApplication" />
</manifest>)EOF",
options);
ASSERT_NE(nullptr, doc);
xml::Element* manifest_el = doc->root.get();
ASSERT_NE(nullptr, manifest_el);
ASSERT_EQ("manifest", manifest_el->name);
xml::Element* application_el = manifest_el->FindChild("", "application");
ASSERT_NE(nullptr, application_el);
xml::Element* uses_sdk_el = manifest_el->FindChild("", "uses-sdk");
ASSERT_NE(nullptr, uses_sdk_el);
// Check that the uses_sdk_el comes before application_el in the children
// vector.
// Since there are no namespaces here, these children are direct descendants
// of manifest.
auto uses_sdk_iter =
std::find_if(manifest_el->children.begin(), manifest_el->children.end(),
[&](const std::unique_ptr<xml::Node>& child) {
return child.get() == uses_sdk_el;
});
auto application_iter =
std::find_if(manifest_el->children.begin(), manifest_el->children.end(),
[&](const std::unique_ptr<xml::Node>& child) {
return child.get() == application_el;
});
ASSERT_NE(manifest_el->children.end(), uses_sdk_iter);
ASSERT_NE(manifest_el->children.end(), application_iter);
// The distance should be positive, meaning uses_sdk_iter comes before
// application_iter.
EXPECT_GT(std::distance(uses_sdk_iter, application_iter), 0);
}
TEST_F(ManifestFixerTest, RenameManifestPackageAndFullyQualifyClasses) {
ManifestFixerOptions options;
options.rename_manifest_package = std::string("com.android");
std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<application android:name=".MainApplication" text="hello">
<activity android:name=".activity.Start" />
<receiver android:name="com.google.android.Receiver" />
</application>
</manifest>)EOF",
options);
ASSERT_NE(nullptr, doc);
xml::Element* manifestEl = doc->root.get();
ASSERT_NE(nullptr, manifestEl);
xml::Attribute* attr = nullptr;
attr = manifestEl->FindAttribute({}, "package");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(std::string("com.android"), attr->value);
xml::Element* applicationEl = manifestEl->FindChild({}, "application");
ASSERT_NE(nullptr, applicationEl);
attr = applicationEl->FindAttribute(xml::kSchemaAndroid, "name");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(std::string("android.MainApplication"), attr->value);
attr = applicationEl->FindAttribute({}, "text");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(std::string("hello"), attr->value);
xml::Element* el;
el = applicationEl->FindChild({}, "activity");
ASSERT_NE(nullptr, el);
attr = el->FindAttribute(xml::kSchemaAndroid, "name");
ASSERT_NE(nullptr, el);
EXPECT_EQ(std::string("android.activity.Start"), attr->value);
el = applicationEl->FindChild({}, "receiver");
ASSERT_NE(nullptr, el);
attr = el->FindAttribute(xml::kSchemaAndroid, "name");
ASSERT_NE(nullptr, el);
EXPECT_EQ(std::string("com.google.android.Receiver"), attr->value);
}
TEST_F(ManifestFixerTest,
RenameManifestInstrumentationPackageAndFullyQualifyTarget) {
ManifestFixerOptions options;
options.rename_instrumentation_target_package = std::string("com.android");
std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<instrumentation android:name=".TestRunner" android:targetPackage="android" />
</manifest>)EOF",
options);
ASSERT_NE(nullptr, doc);
xml::Element* manifest_el = doc->root.get();
ASSERT_NE(nullptr, manifest_el);
xml::Element* instrumentation_el =
manifest_el->FindChild({}, "instrumentation");
ASSERT_NE(nullptr, instrumentation_el);
xml::Attribute* attr =
instrumentation_el->FindAttribute(xml::kSchemaAndroid, "targetPackage");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(std::string("com.android"), attr->value);
}
TEST_F(ManifestFixerTest, UseDefaultVersionNameAndCode) {
ManifestFixerOptions options;
options.version_name_default = std::string("Beta");
options.version_code_default = std::string("0x10000000");
std::unique_ptr<xml::XmlResource> doc = VerifyWithOptions(R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android" />)EOF",
options);
ASSERT_NE(nullptr, doc);
xml::Element* manifest_el = doc->root.get();
ASSERT_NE(nullptr, manifest_el);
xml::Attribute* attr =
manifest_el->FindAttribute(xml::kSchemaAndroid, "versionName");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(std::string("Beta"), attr->value);
attr = manifest_el->FindAttribute(xml::kSchemaAndroid, "versionCode");
ASSERT_NE(nullptr, attr);
EXPECT_EQ(std::string("0x10000000"), attr->value);
}
TEST_F(ManifestFixerTest, EnsureManifestAttributesAreTyped) {
EXPECT_EQ(nullptr,
Verify("<manifest package=\"android\" coreApp=\"hello\" />"));
EXPECT_EQ(nullptr,
Verify("<manifest package=\"android\" coreApp=\"1dp\" />"));
std::unique_ptr<xml::XmlResource> doc =
Verify("<manifest package=\"android\" coreApp=\"true\" />");
ASSERT_NE(nullptr, doc);
xml::Element* el = doc->root.get();
ASSERT_NE(nullptr, el);
EXPECT_EQ("manifest", el->name);
xml::Attribute* attr = el->FindAttribute("", "coreApp");
ASSERT_NE(nullptr, attr);
EXPECT_NE(nullptr, attr->compiled_value);
EXPECT_NE(nullptr, ValueCast<BinaryPrimitive>(attr->compiled_value.get()));
}
TEST_F(ManifestFixerTest, UsesFeatureMustHaveNameOrGlEsVersion) {
std::string input = R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-feature android:name="feature" />
<uses-feature android:glEsVersion="1" />
<feature-group />
<feature-group>
<uses-feature android:name="feature_in_group" />
<uses-feature android:glEsVersion="2" />
</feature-group>
</manifest>)EOF";
EXPECT_NE(nullptr, Verify(input));
input = R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-feature android:name="feature" android:glEsVersion="1" />
</manifest>)EOF";
EXPECT_EQ(nullptr, Verify(input));
input = R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<uses-feature />
</manifest>)EOF";
EXPECT_EQ(nullptr, Verify(input));
input = R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<feature-group>
<uses-feature android:name="feature" android:glEsVersion="1" />
</feature-group>
</manifest>)EOF";
EXPECT_EQ(nullptr, Verify(input));
input = R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<feature-group>
<uses-feature />
</feature-group>
</manifest>)EOF";
EXPECT_EQ(nullptr, Verify(input));
}
TEST_F(ManifestFixerTest, IgnoreNamespacedElements) {
std::string input = R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<special:tag whoo="true" xmlns:special="http://google.com" />
</manifest>)EOF";
EXPECT_NE(nullptr, Verify(input));
}
TEST_F(ManifestFixerTest, DoNotIgnoreNonNamespacedElements) {
std::string input = R"EOF(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<tag whoo="true" />
</manifest>)EOF";
EXPECT_EQ(nullptr, Verify(input));
}
TEST_F(ManifestFixerTest, SupportKeySets) {
std::string input = R"(
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="android">
<key-sets>
<key-set android:name="old-set">
<public-key android:name="old-key" android:value="some+old+key" />
</key-set>
<key-set android:name="new-set">
<public-key android:name="new-key" android:value="some+new+key" />
</key-set>
<upgrade-key-set android:name="old-set" />
<upgrade-key-set android:name="new-set" />
</key-sets>
</manifest>)";
EXPECT_THAT(Verify(input), NotNull());
}
} // namespace aapt