blob: 8e0432d4d381931e53dcb38090f4cb588e379885 [file] [log] [blame]
/*
* Copyright (C) 2014 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.
*/
package com.android.manifmerger;
import com.android.SdkConstants;
import com.android.sdklib.mock.MockLog;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import com.google.common.collect.ImmutableMap;
import junit.framework.TestCase;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.w3c.dom.Element;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Map;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
/**
* Tests for the {@link com.android.manifmerger.ManifestMergerTest} class
*/
public class ManifestMerger2SmallTest extends TestCase {
@Mock
ActionRecorder mActionRecorder;
@Override
protected void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
}
public void testValidationFailure()
throws ParserConfigurationException, SAXException, IOException,
ManifestMerger2.MergeFailureException {
MockLog mockLog = new MockLog();
String input = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib3\">\n"
+ "\n"
+ " <application android:label=\"@string/lib_name\" />\n"
+ "\n"
+ " <activity android:name=\"activityOne\" "
+ " tools:replace=\"exported\"/>\n"
+ "\n"
+ "</manifest>";
File tmpFile = inputAsFile("ManifestMerger2Test_testValidationFailure", input);
assertTrue(tmpFile.exists());
try {
MergingReport mergingReport = ManifestMerger2.newMerger(tmpFile, mockLog,
ManifestMerger2.MergeType.APPLICATION).merge();
assertEquals(MergingReport.Result.ERROR, mergingReport.getResult());
// check the log complains about the incorrect "tools:replace"
assertStringPresenceInLogRecords(mergingReport, "tools:replace");
assertFalse(mergingReport.getMergedDocument().isPresent());
} finally {
assertTrue(tmpFile.delete());
}
}
public void testToolsAnnotationRemoval()
throws ParserConfigurationException, SAXException, IOException,
ManifestMerger2.MergeFailureException {
MockLog mockLog = new MockLog();
String input = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib3\">\n"
+ "\n"
+ " <application android:label=\"@string/lib_name\" "
+ " tools:replace=\"label\"/>\n"
+ "\n"
+ "</manifest>";
File tmpFile = inputAsFile("testToolsAnnotationRemoval", input);
assertTrue(tmpFile.exists());
try {
MergingReport mergingReport = ManifestMerger2.newMerger(tmpFile, mockLog,
ManifestMerger2.MergeType.APPLICATION)
.withFeatures(ManifestMerger2.Invoker.Feature.REMOVE_TOOLS_DECLARATIONS)
.merge();
assertEquals(MergingReport.Result.WARNING, mergingReport.getResult());
// ensure tools annotation removal.
XmlDocument mergedDocument = mergingReport.getMergedDocument().get();
Optional<XmlElement> applicationNode = mergedDocument
.getByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null);
assertTrue(applicationNode.isPresent());
String replaceAttribute = applicationNode.get().getXml().getAttributeNS(
SdkConstants.TOOLS_URI, "replace");
assertTrue(Strings.isNullOrEmpty(replaceAttribute));
System.out.println(mergedDocument.prettyPrint());
mergedDocument.prettyPrint();
} finally {
assertTrue(tmpFile.delete());
}
}
public void testToolsAnnotationPresence()
throws ParserConfigurationException, SAXException, IOException,
ManifestMerger2.MergeFailureException {
MockLog mockLog = new MockLog();
String input = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib3\">\n"
+ "\n"
+ " <application android:label=\"@string/lib_name\" "
+ " tools:replace=\"label\"/>\n"
+ "\n"
+ "</manifest>";
File tmpFile = inputAsFile("testToolsAnnotationRemoval", input);
assertTrue(tmpFile.exists());
try {
MergingReport mergingReport = ManifestMerger2.newMerger(tmpFile, mockLog,
ManifestMerger2.MergeType.LIBRARY)
.merge();
assertEquals(MergingReport.Result.WARNING, mergingReport.getResult());
// ensure tools annotation removal.
XmlDocument mergedDocument = mergingReport.getMergedDocument().get();
Optional<XmlElement> applicationNode = mergedDocument
.getByTypeAndKey(ManifestModel.NodeTypes.APPLICATION, null);
assertTrue(applicationNode.isPresent());
String replaceAttribute = applicationNode.get().getXml().getAttributeNS(
SdkConstants.TOOLS_URI, "replace");
assertEquals("label", replaceAttribute);
System.out.println(mergedDocument.prettyPrint());
} finally {
assertTrue(tmpFile.delete());
}
}
public void testPackageOverride()
throws ParserConfigurationException, SAXException, IOException {
String xml = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\""
+ " package=\"com.foo.old\" >\n"
+ " <activity android:name=\"activityOne\"/>\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testPackageOverride#xml"), xml);
ManifestMerger2.SystemProperty.PACKAGE.addTo(mActionRecorder, refDocument, "com.bar.new");
// verify the package value was overriden.
assertEquals("com.bar.new", refDocument.getRootNode().getXml().getAttribute("package"));
}
public void testMissingPackageOverride()
throws ParserConfigurationException, SAXException, IOException {
String xml = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"
+ " <activity android:name=\"activityOne\"/>\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testMissingPackageOverride#xml"), xml);
ManifestMerger2.SystemProperty.PACKAGE.addTo(mActionRecorder, refDocument, "com.bar.new");
// verify the package value was added.
assertEquals("com.bar.new", refDocument.getRootNode().getXml().getAttribute("package"));
}
public void testAddingSystemProperties()
throws ParserConfigurationException, SAXException, IOException {
String xml = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"
+ " <activity android:name=\"activityOne\"/>\n"
+ "</manifest>";
XmlDocument document = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(),
"testAddingSystemProperties#xml"), xml);
ManifestMerger2.SystemProperty.VERSION_CODE.addTo(mActionRecorder, document, "101");
assertEquals("101",
document.getXml().getDocumentElement().getAttribute("android:versionCode"));
ManifestMerger2.SystemProperty.VERSION_NAME.addTo(mActionRecorder, document, "1.0.1");
assertEquals("1.0.1",
document.getXml().getDocumentElement().getAttribute("android:versionName"));
ManifestMerger2.SystemProperty.MIN_SDK_VERSION.addTo(mActionRecorder, document, "10");
Element usesSdk = (Element) document.getXml().getElementsByTagName("uses-sdk").item(0);
assertNotNull(usesSdk);
assertEquals("10", usesSdk.getAttribute("android:minSdkVersion"));
ManifestMerger2.SystemProperty.TARGET_SDK_VERSION.addTo(mActionRecorder, document, "14");
usesSdk = (Element) document.getXml().getElementsByTagName("uses-sdk").item(0);
assertNotNull(usesSdk);
assertEquals("14", usesSdk.getAttribute("android:targetSdkVersion"));
ManifestMerger2.SystemProperty.MAX_SDK_VERSION.addTo(mActionRecorder, document, "16");
usesSdk = (Element) document.getXml().getElementsByTagName("uses-sdk").item(0);
assertNotNull(usesSdk);
assertEquals("16", usesSdk.getAttribute("android:maxSdkVersion"));
}
public void testAddingSystemProperties_withDifferentPrefix()
throws ParserConfigurationException, SAXException, IOException {
String xml = ""
+ "<manifest\n"
+ " xmlns:t=\"http://schemas.android.com/apk/res/android\">\n"
+ " <activity t:name=\"activityOne\"/>\n"
+ "</manifest>";
XmlDocument document = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(),
"testAddingSystemProperties#xml"), xml
);
ManifestMerger2.SystemProperty.VERSION_CODE.addTo(mActionRecorder, document, "101");
// using the non namespace aware API to make sure the prefix is the expected one.
assertEquals("101",
document.getXml().getDocumentElement().getAttribute("t:versionCode"));
}
public void testOverridingSystemProperties()
throws ParserConfigurationException, SAXException, IOException {
String xml = ""
+ "<manifest versionCode=\"34\" versionName=\"3.4\"\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"
+ " <uses-sdk minSdkVersion=\"9\" targetSdkVersion=\".9\"/>\n"
+ " <activity android:name=\"activityOne\"/>\n"
+ "</manifest>";
XmlDocument document = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(),
"testAddingSystemProperties#xml"), xml);
// check initial state.
assertEquals("34", document.getXml().getDocumentElement().getAttribute("versionCode"));
assertEquals("3.4", document.getXml().getDocumentElement().getAttribute("versionName"));
Element usesSdk = (Element) document.getXml().getElementsByTagName("uses-sdk").item(0);
assertNotNull(usesSdk);
assertEquals("9", usesSdk.getAttribute("minSdkVersion"));
assertEquals(".9", usesSdk.getAttribute("targetSdkVersion"));
ManifestMerger2.SystemProperty.VERSION_CODE.addTo(mActionRecorder, document, "101");
assertEquals("101",
document.getXml().getDocumentElement().getAttribute("android:versionCode"));
ManifestMerger2.SystemProperty.VERSION_NAME.addTo(mActionRecorder, document, "1.0.1");
assertEquals("1.0.1",
document.getXml().getDocumentElement().getAttribute("android:versionName"));
ManifestMerger2.SystemProperty.MIN_SDK_VERSION.addTo(mActionRecorder, document, "10");
usesSdk = (Element) document.getXml().getElementsByTagName("uses-sdk").item(0);
assertNotNull(usesSdk);
assertEquals("10", usesSdk.getAttribute("android:minSdkVersion"));
ManifestMerger2.SystemProperty.TARGET_SDK_VERSION.addTo(mActionRecorder, document, "14");
usesSdk = (Element) document.getXml().getElementsByTagName("uses-sdk").item(0);
assertNotNull(usesSdk);
assertEquals("14", usesSdk.getAttribute("android:targetSdkVersion"));
}
public void testPlaceholderSubstitution()
throws ParserConfigurationException, SAXException, IOException,
ManifestMerger2.MergeFailureException {
String xml = ""
+ "<manifest package=\"foo\" versionCode=\"34\" versionName=\"3.4\"\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"
+ " <activity android:name=\".activityOne\" android:label=\"${labelName}\"/>\n"
+ "</manifest>";
Map<String, String> placeholders = ImmutableMap.of("labelName", "injectedLabelName");
MockLog mockLog = new MockLog();
File inputFile = inputAsFile("testPlaceholderSubstitution", xml);
try {
MergingReport mergingReport = ManifestMerger2
.newMerger(inputFile, mockLog, ManifestMerger2.MergeType.APPLICATION)
.setPlaceHolderValues(placeholders)
.merge();
assertTrue(mergingReport.getResult().isSuccess());
assertTrue(mergingReport.getMergedDocument().isPresent());
XmlDocument xmlDocument = mergingReport.getMergedDocument().get();
Optional<XmlElement> activityOne = xmlDocument
.getByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY, "foo.activityOne");
assertTrue(activityOne.isPresent());
Optional<XmlAttribute> attribute = activityOne.get()
.getAttribute(XmlNode.fromXmlName("android:label"));
assertTrue(attribute.isPresent());
assertEquals("injectedLabelName", attribute.get().getValue());
} finally {
inputFile.delete();
}
}
public void testApplicationIdSubstitution()
throws ManifestMerger2.MergeFailureException, IOException {
String xml = ""
+ "<manifest package=\"foo\" versionCode=\"34\" versionName=\"3.4\"\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"
+ " <activity android:name=\"${applicationId}.activityOne\"/>\n"
+ "</manifest>";
MockLog mockLog = new MockLog();
File inputFile = inputAsFile("testPlaceholderSubstitution", xml);
try {
MergingReport mergingReport = ManifestMerger2
.newMerger(inputFile, mockLog, ManifestMerger2.MergeType.APPLICATION)
.setOverride(ManifestMerger2.SystemProperty.PACKAGE, "bar")
.merge();
assertTrue(mergingReport.getResult().isSuccess());
assertTrue(mergingReport.getMergedDocument().isPresent());
XmlDocument xmlDocument = mergingReport.getMergedDocument().get();
assertEquals("bar", xmlDocument.getPackageName());
Optional<XmlElement> activityOne = xmlDocument
.getByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY, "bar.activityOne");
assertTrue(activityOne.isPresent());
} finally {
inputFile.delete();
}
}
public void testNoApplicationIdValueProvided()
throws IOException, ManifestMerger2.MergeFailureException {
String xml = ""
+ "<manifest package=\"foo\" versionCode=\"34\" versionName=\"3.4\"\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\">\n"
+ " <activity android:name=\"${applicationId}.activityOne\"/>\n"
+ "</manifest>";
MockLog mockLog = new MockLog();
File inputFile = inputAsFile("testPlaceholderSubstitution", xml);
try {
MergingReport mergingReport = ManifestMerger2
.newMerger(inputFile, mockLog, ManifestMerger2.MergeType.APPLICATION)
.merge();
assertTrue(mergingReport.getResult().isSuccess());
assertTrue(mergingReport.getMergedDocument().isPresent());
XmlDocument xmlDocument = mergingReport.getMergedDocument().get();
assertEquals("foo", xmlDocument.getPackageName());
Optional<XmlElement> activityOne = xmlDocument
.getByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY, "foo.activityOne");
assertTrue(activityOne.isPresent());
} finally {
inputFile.delete();
}
}
public void testNoFqcnsExtraction()
throws ParserConfigurationException, SAXException, IOException,
ManifestMerger2.MergeFailureException {
String xml = ""
+ "<manifest\n"
+ " package=\"com.foo.example\""
+ " xmlns:t=\"http://schemas.android.com/apk/res/android\">\n"
+ " <activity t:name=\"activityOne\"/>\n"
+ " <activity t:name=\"com.foo.bar.example.activityTwo\"/>\n"
+ " <activity t:name=\"com.foo.example.activityThree\"/>\n"
+ " <application t:name=\".applicationOne\" "
+ " t:backupAgent=\"com.foo.example.myBackupAgent\"/>\n"
+ "</manifest>";
File inputFile = inputAsFile("testFcqnsExtraction", xml);
MockLog mockLog = new MockLog();
MergingReport mergingReport = ManifestMerger2
.newMerger(inputFile, mockLog, ManifestMerger2.MergeType.APPLICATION)
.merge();
assertTrue(mergingReport.getResult().isSuccess());
XmlDocument xmlDocument = mergingReport.getMergedDocument().get();
assertEquals("com.foo.example.activityOne",
xmlDocument.getXml().getElementsByTagName("activity").item(0).getAttributes()
.item(0).getNodeValue());
assertEquals("com.foo.bar.example.activityTwo",
xmlDocument.getXml().getElementsByTagName("activity").item(1).getAttributes()
.item(0).getNodeValue());
assertEquals("com.foo.example.activityThree",
xmlDocument.getXml().getElementsByTagName("activity").item(2).getAttributes()
.item(0).getNodeValue());
assertEquals("com.foo.example.applicationOne",
xmlDocument.getXml().getElementsByTagName("application").item(0).getAttributes()
.getNamedItemNS("http://schemas.android.com/apk/res/android", "name")
.getNodeValue());
assertEquals("com.foo.example.myBackupAgent",
xmlDocument.getXml().getElementsByTagName("application").item(0).getAttributes()
.getNamedItemNS("http://schemas.android.com/apk/res/android", "backupAgent")
.getNodeValue());
}
public void testFqcnsExtraction()
throws ParserConfigurationException, SAXException, IOException,
ManifestMerger2.MergeFailureException {
String xml = ""
+ "<manifest\n"
+ " package=\"com.foo.example\""
+ " xmlns:t=\"http://schemas.android.com/apk/res/android\">\n"
+ " <activity t:name=\"activityOne\"/>\n"
+ " <activity t:name=\"com.foo.bar.example.activityTwo\"/>\n"
+ " <activity t:name=\"com.foo.example.activityThree\"/>\n"
+ " <application t:name=\".applicationOne\" "
+ " t:backupAgent=\"com.foo.example.myBackupAgent\"/>\n"
+ "</manifest>";
File inputFile = inputAsFile("testFcqnsExtraction", xml);
MockLog mockLog = new MockLog();
MergingReport mergingReport = ManifestMerger2
.newMerger(inputFile, mockLog, ManifestMerger2.MergeType.APPLICATION)
.withFeatures(ManifestMerger2.Invoker.Feature.EXTRACT_FQCNS)
.merge();
assertTrue(mergingReport.getResult().isSuccess());
XmlDocument xmlDocument = mergingReport.getMergedDocument().get();
assertEquals(".activityOne",
xmlDocument.getXml().getElementsByTagName("activity").item(0).getAttributes()
.item(0).getNodeValue());
assertEquals("com.foo.bar.example.activityTwo",
xmlDocument.getXml().getElementsByTagName("activity").item(1).getAttributes()
.item(0).getNodeValue());
assertEquals(".activityThree",
xmlDocument.getXml().getElementsByTagName("activity").item(2).getAttributes()
.item(0).getNodeValue());
assertEquals(".applicationOne",
xmlDocument.getXml().getElementsByTagName("application").item(0).getAttributes()
.getNamedItemNS("http://schemas.android.com/apk/res/android", "name")
.getNodeValue());
assertEquals(".myBackupAgent",
xmlDocument.getXml().getElementsByTagName("application").item(0).getAttributes()
.getNamedItemNS("http://schemas.android.com/apk/res/android", "backupAgent")
.getNodeValue());
}
public void testNoPlaceholderReplacement()
throws IOException, ManifestMerger2.MergeFailureException {
String xml = ""
+ "<manifest\n"
+ " package=\"${applicationId}\""
+ " xmlns:t=\"http://schemas.android.com/apk/res/android\">\n"
+ " <activity t:name=\"activityOne\"/>\n"
+ " <application t:name=\".applicationOne\" "
+ " t:backupAgent=\"com.foo.example.myBackupAgent\"/>\n"
+ "</manifest>";
File inputFile = inputAsFile("testNoPlaceHolderReplacement", xml);
MockLog mockLog = new MockLog();
MergingReport mergingReport = ManifestMerger2
.newMerger(inputFile, mockLog, ManifestMerger2.MergeType.APPLICATION)
.withFeatures(ManifestMerger2.Invoker.Feature.NO_PLACEHOLDER_REPLACEMENT)
.merge();
assertTrue(mergingReport.getResult().isSuccess());
XmlDocument xmlDocument = mergingReport.getMergedDocument().get();
assertEquals("${applicationId}",
xmlDocument.getXml().getElementsByTagName("manifest")
.item(0).getAttributes().getNamedItem("package").getNodeValue());
}
/**
* Utility method to save a {@link String} XML into a file.
*/
private static File inputAsFile(String testName, String input) throws IOException {
File tmpFile = File.createTempFile(testName, ".xml");
FileWriter fw = null;
try {
fw = new FileWriter(tmpFile);
fw.append(input);
} finally {
if (fw != null) fw.close();
}
return tmpFile;
}
private static void assertStringPresenceInLogRecords(MergingReport mergingReport, String s) {
for (MergingReport.Record record : mergingReport.getLoggingRecords()) {
if (record.toString().contains(s)) {
return;
}
}
// failed, dump the records
for (MergingReport.Record record : mergingReport.getLoggingRecords()) {
Logger.getAnonymousLogger().info(record.toString());
}
fail("could not find " + s + " in logging records");
}
}