blob: d2fe4455f47bea05dac33b7fe251e8f247e14b10 [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 static com.android.manifmerger.Actions.NodeRecord;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.when;
import com.android.SdkConstants;
import com.android.utils.StdLogger;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import junit.framework.TestCase;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.util.List;
import java.util.logging.Logger;
import javax.xml.parsers.ParserConfigurationException;
/**
* Tests for the {@link XmlElement}
*/
public class XmlElementTest extends TestCase {
@Mock
MergingReport.Builder mergingReport;
@Override
protected void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
}
public void testToolsNodeInstructions()
throws ParserConfigurationException, SAXException, IOException {
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"
+ " <activity android:name=\"activityOne\" "
+ " tools:node=\"remove\"/>\n"
+ "\n"
+ " <activity android:name=\"activityTwo\" "
+ " tools:node=\"removeAll\"/>\n"
+ "\n"
+ " <activity android:name=\"activityThree\" "
+ " tools:node=\"mergeOnlyAttributes\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testToolsNodeInstructions()"), input);
Optional<XmlElement> activity = xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
assertTrue(activity.isPresent());
assertEquals(NodeOperationType.REMOVE,
activity.get().getOperationType());
activity = xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityTwo");
assertTrue(activity.isPresent());
assertEquals(NodeOperationType.REMOVE_ALL,
activity.get().getOperationType());
activity = xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityThree");
assertTrue(activity.isPresent());
assertEquals(NodeOperationType.MERGE_ONLY_ATTRIBUTES,
activity.get().getOperationType());
}
public void testInvalidNodeInstruction()
throws ParserConfigurationException, SAXException, IOException {
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"
+ " <activity android:name=\"activityOne\" "
+ " tools:node=\"funkyValue\"/>\n"
+ "\n"
+ "</manifest>";
try {
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testInvalidNodeInstruction()"), input);
xmlDocument.getRootNode();
fail("Exception not thrown");
} catch (IllegalArgumentException expected) {
// expected.
}
}
public void testAttributeInstructions()
throws ParserConfigurationException, SAXException, IOException {
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"
+ " <activity android:name=\"activityOne\" "
+ " tools:remove=\"android:theme\"/>\n"
+ "\n"
+ " <activity android:name=\"activityTwo\" "
+ " android:theme=\"@theme1\"\n"
+ " tools:replace=\"android:theme\"/>\n"
+ "\n"
+ " <activity android:name=\"activityThree\" "
+ " tools:strict=\"android:exported\"/>\n"
+ "\n"
+ " <activity android:name=\"activityFour\" "
+ " android:theme=\"@theme1\"\n"
+ " android:exported=\"true\"\n"
+ " android:windowSoftInputMode=\"stateUnchanged\"\n"
+ " tools:replace="
+ "\"android:theme, android:exported,android:windowSoftInputMode\"/>\n"
+ "\n"
+ " <activity android:name=\"activityFive\" "
+ " android:theme=\"@theme1\"\n"
+ " android:exported=\"true\"\n"
+ " android:windowSoftInputMode=\"stateUnchanged\"\n"
+ " tools:remove=\"android:exported\"\n"
+ " tools:replace=\"android:theme\"\n"
+ " tools:strict=\"android:windowSoftInputMode\"/>\n"
+ "\n"
+ "</manifest>";
// ActivityOne, remove operation.
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testAttributeInstructions()"), input);
Optional<XmlElement> activityOptional = xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
assertTrue(activityOptional.isPresent());
XmlElement activity = activityOptional.get();
assertEquals(1, activity.getAttributeOperations().size());
AttributeOperationType attributeOperationType =
activity.getAttributeOperationType(XmlNode.fromXmlName("android:theme"));
assertEquals(AttributeOperationType.REMOVE, attributeOperationType);
// ActivityTwo, replace operation.
activityOptional = xmlDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityTwo");
assertTrue(activityOptional.isPresent());
activity = activityOptional.get();
assertEquals(1, activity.getAttributeOperations().size());
attributeOperationType = activity.getAttributeOperationType(XmlNode.fromXmlName("android:theme"));
assertEquals(AttributeOperationType.REPLACE, attributeOperationType);
// ActivityThree, strict operation.
activityOptional = xmlDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityThree");
assertTrue(activityOptional.isPresent());
activity = activityOptional.get();
assertEquals(1, activity.getAttributeOperations().size());
attributeOperationType = activity.getAttributeOperationType(XmlNode.fromXmlName("android:theme"));
assertEquals(AttributeOperationType.STRICT, attributeOperationType);
// ActivityFour, multiple target fields.
activityOptional = xmlDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityFour");
assertTrue(activityOptional.isPresent());
activity = activityOptional.get();
assertEquals(3, activity.getAttributeOperations().size());
assertEquals(AttributeOperationType.REPLACE,
activity.getAttributeOperationType(XmlNode.fromXmlName("android:theme")));
assertEquals(AttributeOperationType.REPLACE,
activity.getAttributeOperationType(XmlNode.fromXmlName("android:theme")));
assertEquals(AttributeOperationType.REPLACE,
activity.getAttributeOperationType(XmlNode.fromXmlName("android:theme")));
// ActivityFive, multiple operations.
activityOptional = xmlDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityFive");
assertTrue(activityOptional.isPresent());
activity = activityOptional.get();
assertEquals(3, activity.getAttributeOperations().size());
assertEquals(AttributeOperationType.REMOVE,
activity.getAttributeOperationType(XmlNode.fromXmlName("android:exported")));
assertEquals(AttributeOperationType.REPLACE,
activity.getAttributeOperationType(XmlNode.fromXmlName("android:theme")));
assertEquals(AttributeOperationType.STRICT,
activity.getAttributeOperationType(XmlNode.fromXmlName("android:windowSoftInputMode")));
}
public void testNoNamespaceAwareAttributeInstructions()
throws ParserConfigurationException, SAXException, IOException {
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"
+ " <activity android:name=\"activityOne\" "
+ " tools:remove=\"theme\"/>\n"
+ "\n"
+ "</manifest>";
// ActivityOne, remove operation.
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testAttributeInstructions()"), input);
Optional<XmlElement> activityOptional = xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
assertTrue(activityOptional.isPresent());
XmlElement activity = activityOptional.get();
assertEquals(1, activity.getAttributeOperations().size());
AttributeOperationType attributeOperationType =
activity.getAttributeOperationType(XmlNode.fromXmlName("android:theme"));
assertEquals(AttributeOperationType.REMOVE, attributeOperationType);
}
public void testUnusualNamespacePrefixAttributeInstructions()
throws ParserConfigurationException, SAXException, IOException {
String input = ""
+ "<manifest\n"
+ " xmlns:z=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib3\">\n"
+ "\n"
+ " <activity z:name=\"activityOne\" tools:remove=\"theme\"/>\n"
+ "\n"
+ "</manifest>";
// ActivityOne, remove operation.
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testAttributeInstructions()"), input);
Optional<XmlElement> activityOptional = xmlDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
assertTrue(activityOptional.isPresent());
XmlElement activity = activityOptional.get();
assertEquals(1, activity.getAttributeOperations().size());
AttributeOperationType attributeOperationType =
activity.getAttributeOperationType(XmlNode.fromXmlName("z:theme"));
assertEquals(AttributeOperationType.REMOVE, attributeOperationType);
}
public void testInvalidAttributeInstruction()
throws ParserConfigurationException, SAXException, IOException {
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"
+ " <activity android:name=\"activityOne\" "
+ " tools:bad-name=\"android:theme\"/>\n"
+ "\n"
+ "</manifest>";
try {
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff6()"), input);
xmlDocument.getRootNode();
fail("Exception not thrown");
} catch (RuntimeException expected) {
// expected.
}
}
public void testOtherToolsInstruction()
throws ParserConfigurationException, SAXException, IOException {
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"
+ " <activity android:name=\"activityOne\" "
+ " tools:ignore=\"android:theme\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testOtherToolsInstruction"), input);
xmlDocument.getRootNode();
}
public void testDiff1()
throws Exception {
String reference = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\"/>\n"
+ "\n"
+ "</manifest>";
String other = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/apk/res/android/tools\"\n"
+ " package=\"com.example.lib3\">\n"
+ "\n"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff1()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff1()"), other);
assertFalse(refDocument.getRootNode().getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne").get()
.compareTo(otherDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne")
.get()).isPresent());
}
public void testDiff2()
throws Exception {
String reference = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\"/>\n"
+ "\n"
+ "</manifest>";
String other = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/apk/res/android/tools\"\n"
+ " package=\"com.example.lib3\">\n"
+ "\n"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"mcc\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff2()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff2()"), other);
assertTrue(refDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
.compareTo(
otherDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne")
.get()
).isPresent());
}
public void testDiff3()
throws Exception {
String reference = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\"/>\n"
+ "\n"
+ "</manifest>";
String other = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/apk/res/android/tools\"\n"
+ " package=\"com.example.lib3\">\n"
+ "\n"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\" android:exported=\"true\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff3()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff3()"), other);
assertTrue(refDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
.compareTo(
otherDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne")
.get()
).isPresent());
}
public void testDiff4()
throws Exception {
String reference = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\" android:exported=\"false\"/>\n"
+ "\n"
+ "</manifest>";
String other = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff4()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff4()"), other);
assertTrue(refDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
.compareTo(
otherDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne")
.get()
).isPresent());
}
public void testDiff5()
throws Exception {
String reference = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\">\n"
+ "\n"
+ " </activity>\n"
+ "</manifest>";
String other = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff5()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff5()"), other);
assertFalse(refDocument.compareTo(otherDocument).isPresent());
}
public void testDiff6()
throws Exception {
String reference = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\">\n"
+ "\n"
+ " <intent-filter android:label=\"@string/foo\"/>\n"
+ "\n"
+ " </activity>\n"
+ "</manifest>";
String other = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:configChanges=\"locale\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff6()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testDiff6()"), other);
assertTrue(
refDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne").get()
.compareTo(
otherDocument.getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne")
.get()
).isPresent()
);
}
/**
* test merging of same element types with no collision.
*/
public void testMerge_NoCollision() throws ParserConfigurationException, SAXException, IOException {
String reference = ""
+ "<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"
+ " <activity android:name=\"activityOne\">\n"
+ " <intent-filter android:label=\"@string/foo\"/>\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
String other = ""
+ "<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"
+ " <activity android:name=\"activityTwo\" "
+ " android:configChanges=\"locale\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testMerge()"), reference);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "testMerge()"), other);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
XmlDocument resultDocument = result.get();
Optional<XmlElement> activityOne = resultDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne");
assertTrue(activityOne.isPresent());
Optional<XmlElement> activityTwo = resultDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityTwo");
assertTrue(activityTwo.isPresent());
}
/**
* test merging of same element with no attribute collision.
*/
public void testAttributeMerging()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\">\n"
+ " <intent-filter android:label=\"@string/foo\"/>\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" \n"
+ " android:configChanges=\"locale\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
XmlDocument resultDocument = result.get();
Optional<XmlElement> activityOne = resultDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne");
assertTrue(activityOne.isPresent());
// verify that both attributes are in the resulting merged element.
List<XmlAttribute> attributes = activityOne.get().getAttributes();
assertEquals(2, attributes.size());
assertTrue(activityOne.get().getAttribute(
XmlNode.fromXmlName("android:configChanges")).isPresent());
assertTrue(activityOne.get().getAttribute(
XmlNode.fromXmlName("android:name")).isPresent());
}
/**
* test merging of same element with no attribute collision.
*/
public void testElementRemoval()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" tools:node=\"remove\"/>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" \n"
+ " android:configChanges=\"locale\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
// run the instruction cleaner to get rid of all unwanted attributes, nodes.
XmlDocument resultDocument = ToolsInstructionsCleaner.cleanToolsReferences(
result.get(), mergingReportBuilder.getLogger());
Optional<XmlElement> activityOne = resultDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne");
assertFalse(activityOne.isPresent());
}
/**
* test merging of same element with no attribute collision.
*/
public void testElementReplacement()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" tools:node=\"replace\""
+ " android:exported=\"true\"/>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\">\n"
+ " android:screenOrientation=\"landscape\">\n"
+ " <action android:label=\"@string/foo\"/>\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
XmlDocument resultDocument = result.get();
Optional<XmlElement> activityOne = resultDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne");
assertTrue(activityOne.isPresent());
Logger.getAnonymousLogger().info(resultDocument.prettyPrint());
assertFalse(refDocument.getRootNode().getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne").get().compareTo(activityOne.get()).isPresent());
}
/**
* test merging of same element type with STRICT enforcing and no difference between the high
* and low priority elements.
*/
public void testStrictElement_noDifference()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" tools:node=\"strict\""
+ " android:exported=\"true\">\n"
+ " <action android:label=\"@string/foo\"/>\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:exported=\"true\">\n"
+ " <action android:label=\"@string/foo\"/>\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
XmlDocument resultDocument = result.get();
Optional<XmlElement> activityOne = resultDocument.getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne");
assertTrue(activityOne.isPresent());
Logger.getAnonymousLogger().info(resultDocument.prettyPrint());
assertFalse(refDocument.getRootNode().getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne").get().compareTo(activityOne.get()).isPresent());
}
/**
* test merging of same element type with STRICT enforcing and no difference between the high
* and low priority elements.
*/
public void testStrictElement_withDifference()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" tools:node=\"strict\""
+ " android:exported=\"true\">\n"
+ " <action android:label=\"@string/foo\"/>\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\"\n"
+ " android:screenOrientation=\"landscape\">\n"
+ " <action android:label=\"@string/foo\"/>\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
refDocument.merge(otherDocument, mergingReportBuilder);
assertEquals(MergingReport.Result.ERROR, mergingReportBuilder.build().getResult());
ImmutableList<MergingReport.Record> loggingRecords = mergingReportBuilder.build()
.getLoggingRecords();
for (MergingReport.Record record : loggingRecords) {
Logger.getAnonymousLogger().info(record.toString());
}
assertEquals(1, loggingRecords.size());
}
/**
* test attributes merging of elements with no conflict between attributes.
*/
public void testMerging_noConflict()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:exported=\"true\">\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\"\n"
+ " android:screenOrientation=\"landscape\">\n"
+ " <meta-data android:name=\"zoo\" android:value=\"@string/kangaroo\" />\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
Optional<XmlElement> activityOptional = result.get().getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne");
assertTrue(activityOptional.isPresent());
XmlElement activityOne = activityOptional.get();
assertEquals(3, activityOne.getAttributes().size());
assertEquals(1, activityOne.getMergeableElements().size());
ImmutableList<MergingReport.Record> loggingRecords = mergingReportBuilder.build()
.getLoggingRecords();
for (MergingReport.Record record : loggingRecords) {
Logger.getAnonymousLogger().info(record.toString());
}
}
/**
* test attributes merging of elements with no conflict between attributes.
*/
public void testMerging_withConflict()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:exported=\"true\">\n"
+ " <action android:label=\"@string/foo\"/>\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\"\n"
+ " android:exported=\"false\">\n"
+ " <action android:label=\"@string/foo\"/>\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
refDocument.merge(otherDocument, mergingReportBuilder);
assertEquals(MergingReport.Result.ERROR, mergingReportBuilder.build().getResult());
ImmutableList<MergingReport.Record> loggingRecords = mergingReportBuilder.build()
.getLoggingRecords();
for (MergingReport.Record record : loggingRecords) {
Logger.getAnonymousLogger().info(record.toString());
}
}
/**
* test attributes merging of elements with no conflict between children.
*/
public void testMerging_childrenDoNotConflict()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:exported=\"true\">\n"
+ " <meta-data android:name=\"fish\" android:value=\"@string/cod\" />\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\"\n"
+ " android:screenOrientation=\"landscape\">\n"
+ " <meta-data android:name=\"bird\" android:value=\"@string/eagle\" />\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
Optional<XmlElement> activityOptional = result.get().getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne");
assertTrue(activityOptional.isPresent());
XmlElement activityOne = activityOptional.get();
assertEquals(3, activityOne.getAttributes().size());
// both metat-data should be present.
assertEquals(2, activityOne.getMergeableElements().size());
ImmutableList<MergingReport.Record> loggingRecords = mergingReportBuilder.build()
.getLoggingRecords();
assertTrue(loggingRecords.isEmpty());
}
/**
* test attributes merging of elements with some conflicts between children.
*/
public void testMerging_childrenConflict()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:exported=\"true\">\n"
+ " <meta-data android:name=\"bird\" android:value=\"@string/hawk\" />\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\"\n"
+ " android:screenOrientation=\"landscape\">\n"
+ " <meta-data android:name=\"bird\" android:value=\"@string/eagle\" />\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertFalse(result.isPresent());
ImmutableList<MergingReport.Record> loggingRecords = mergingReportBuilder.build()
.getLoggingRecords();
assertEquals(1, loggingRecords.size());
// check the error message complains about the right attribute.
assertTrue(loggingRecords.get(0).toString().contains("meta-data#bird@value"));
}
/**
* test attributes merging of elements with some conflicts between children.
*/
public void testMerging_differentChildrenTypes()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:exported=\"true\">\n"
+ " <meta-data android:name=\"bird\" android:value=\"@string/hawk\" />\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\"\n"
+ " android:screenOrientation=\"landscape\">\n"
+ " <intent-filter>\n"
+ " <action android:name=\"android.appwidget.action.APPWIDGET_CONFIGURE\"/>"
+ " </intent-filter>\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
Optional<XmlElement> activityOptional = result.get().getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY,
"com.example.lib3.activityOne");
assertTrue(activityOptional.isPresent());
XmlElement activityOne = activityOptional.get();
assertEquals(3, activityOne.getAttributes().size());
// both metat-data and intent-filter should be present.
assertEquals(2, activityOne.getMergeableElements().size());
ImmutableList<MergingReport.Record> loggingRecords = mergingReportBuilder.build()
.getLoggingRecords();
assertTrue(loggingRecords.isEmpty());
}
/**
* test attributes merging of elements with some conflicts between children.
*/
public void testHigherPriorityDefaultValue_NoOverride()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <permission android:name=\"permissionOne\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <permission android:name=\"permissionOne\""
+ " android:protectionLevel=\"dangerous\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
Optional<XmlElement> activityOptional = result.get().getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.PERMISSION, "permissionOne");
assertTrue(activityOptional.get()
.getAttribute(XmlNode.fromXmlName("android:protectionLevel")).isPresent());
}
/**
* test attributes merging of elements with some conflicts between children.
*/
public void testLowerPriorityDefaultValue_NoOverride()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <permission android:name=\"permissionOne\"\n"
+ " android:protectionLevel=\"dangerous\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <permission android:name=\"permissionOne\">"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
Optional<XmlElement> activityOptional = result.get().getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.PERMISSION, "permissionOne");
assertTrue(activityOptional.get()
.getAttribute(XmlNode.fromXmlName("android:protectionLevel")).isPresent());
}
/**
* test attributes merging of elements with some conflicts between children.
*/
public void testHigherPriorityDefaultValue_SameOverride()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <permission android:name=\"permissionOne\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <permission android:name=\"permissionOne\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
Optional<XmlElement> activityOptional = result.get().getRootNode()
.getNodeByTypeAndKey(ManifestModel.NodeTypes.PERMISSION, "permissionOne");
assertTrue(activityOptional.get()
.getAttribute(XmlNode.fromXmlName("android:protectionLevel")).isPresent());
}
/**
* test tools:node="removeAll" with several target elements to be removed.
*/
public void testRemoveAll_severalTargets()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <permission tools:node=\"removeAll\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <permission android:name=\"permissionOne\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ " <permission android:name=\"permissionTwo\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ " <permission android:name=\"permissionThree\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
ImmutableList<XmlElement> mergeableElements = result.get().getRootNode()
.getMergeableElements();
assertEquals(1, mergeableElements.size());
}
/**
* test tools:node="removeAll" with several target elements to be removed.
*/
public void testRemoveAll_oneTarget()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <permission tools:node=\"removeAll\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <permission android:name=\"permissionOne\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
ImmutableList<XmlElement> mergeableElements = result.get().getRootNode()
.getMergeableElements();
assertEquals(1, mergeableElements.size());
}
/**
* test tools:node="removeAll" with several target elements to be removed.
*/
public void testRemoveAll_noTarget()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <permission tools:node=\"removeAll\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
ImmutableList<XmlElement> mergeableElements = result.get().getRootNode()
.getMergeableElements();
assertEquals(1, mergeableElements.size());
}
/**
* test tools:node="removeAll" with several target elements to be removed.
*/
public void testMergeOnlyAttributes()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\" "
+ " android:exported=\"true\""
+ " tools:node=\"mergeOnlyAttributes\">\n"
+ " <meta-data android:name=\"bird\" android:value=\"@string/hawk\" />\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
String lowerPriority = ""
+ "<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"
+ " <activity android:name=\"activityOne\"\n"
+ " android:screenOrientation=\"landscape\">\n"
+ " <meta-data android:name=\"dog\" android:value=\"@string/dog\" />\n"
+ " </activity>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument otherDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "lowerPriority"), lowerPriority);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(otherDocument, mergingReportBuilder);
assertTrue(result.isPresent());
Optional<XmlElement> activityOne = result.get().getRootNode().getNodeByTypeAndKey(
ManifestModel.NodeTypes.ACTIVITY, "com.example.lib3.activityOne");
assertTrue(activityOne.isPresent());
assertEquals(1, activityOne.get().getMergeableElements().size());
assertEquals(4, activityOne.get().getAttributes().size());
// check that we kept the right child from the higher priority node.
XmlNode.NodeName nodeName = XmlNode.fromXmlName(
SdkConstants.ANDROID_NS_NAME_PREFIX + SdkConstants.ATTR_NAME);
assertEquals("bird", activityOne.get().getMergeableElements().get(0)
.getAttribute(nodeName).get().getValue());
// check the records.
Actions actionRecorder = mergingReportBuilder.getActionRecorder().build();
assertEquals(3, actionRecorder.getNodeKeys().size());
ImmutableList<NodeRecord> nodeRecords = actionRecorder.getNodeRecords(
new XmlNode.NodeKey("activity#com.example.lib3.activityOne"));
for (NodeRecord nodeRecord : nodeRecords) {
if ("meta-data#dog".equals(nodeRecord.getTargetId().toString())) {
assertEquals(Actions.ActionType.REJECTED,
nodeRecord.getActionType());
return;
}
}
fail("Did not find the meta-data rejection record");
}
/**
* test tools:node="removeAll" with several target elements to be removed.
*/
public void testRemove_withSelector()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <permission android:name=\"permissionOne\""
+ " tools:node=\"remove\""
+ " tools:selector=\"com.example.lib1\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriorityOne = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib1\">\n"
+ "\n"
+ " <permission android:name=\"permissionOne\""
+ " android:protectionLevel=\"signature\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriorityTwo = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib2\">\n"
+ "\n"
+ " <permission android:name=\"permissionOne\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ " <permission android:name=\"permissionTwo\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
KeyResolver<String> keyResolver = (KeyResolver<String>) Mockito.mock(KeyResolver.class);
when(keyResolver.resolve(any(String.class))).thenReturn("valid");
XmlDocument refDocument = TestUtils.xmlDocumentFromString(keyResolver,
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlDocumentFromString(keyResolver,
TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
XmlDocument secondLibrary = TestUtils.xmlDocumentFromString(keyResolver,
TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(firstLibrary, mergingReportBuilder);
assertTrue(result.isPresent());
result = result.get().merge(secondLibrary, mergingReportBuilder);
assertTrue(result.isPresent());
ImmutableList<XmlElement> mergeableElements = result.get().getRootNode()
.getMergeableElements();
assertEquals(2, mergeableElements.size());
}
/**
* test tools:node="removeAll" with several target elements to be removed.
*/
public void testRemoveAll_withSelector()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <permission"
+ " tools:node=\"removeAll\"\n"
+ " tools:selector=\"com.example.lib1\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriorityOne = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib1\">\n"
+ "\n"
+ " <permission android:name=\"permissionOne\""
+ " android:protectionLevel=\"signature\">\n"
+ " </permission>\n"
+ " <permission android:name=\"permissionTwo\""
+ " android:protectionLevel=\"signature\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriorityTwo = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib2\">\n"
+ "\n"
+ " <permission android:name=\"permissionThree\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ " <permission android:name=\"permissionFour\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
XmlDocument secondLibrary = TestUtils.xmlLibraryFromString(
TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(firstLibrary, mergingReportBuilder);
assertTrue(result.isPresent());
result = result.get().merge(secondLibrary, mergingReportBuilder);
assertTrue(result.isPresent());
ImmutableList<XmlElement> mergeableElements = result.get().getRootNode()
.getMergeableElements();
assertEquals(3, mergeableElements.size());
XmlNode.NodeName nodeName = XmlNode.fromXmlName("android:name");
assertEquals("permissionThree",
mergeableElements.get(1).getAttribute(nodeName).get().getValue());
assertEquals("permissionFour",
mergeableElements.get(2).getAttribute(nodeName).get().getValue());
}
/**
* test tools:node="removeAll" with several target elements to be removed.
*/
public void testInvalidSelector()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <permission android:name=\"permissionOne\""
+ " tools:node=\"remove\""
+ " tools:selector=\"com.example.libXYZ\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriorityOne = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib1\">\n"
+ "\n"
+ " <permission android:name=\"permissionOne\""
+ " android:protectionLevel=\"signature\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(firstLibrary, mergingReportBuilder);
assertFalse(result.isPresent());
ImmutableList<MergingReport.Record> loggingRecords = mergingReportBuilder.build()
.getLoggingRecords();
assertEquals(1, loggingRecords.size());
assertEquals(MergingReport.Record.Severity.ERROR, loggingRecords.get(0).getSeverity());
assertTrue(loggingRecords.get(0).getMessage().contains("tools:selector"));
}
/**
* test tools:node="removeAll" with several target elements to be removed.
*/
public void testRemoveAndRemoveAll_withReplace()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <permission"
+ " android:name=\"permissionOne\"\n"
+ " tools:node=\"remove\"\n"
+ " tools:selector=\"com.example.lib1\">\n"
+ " </permission>\n"
+ " <permission"
+ " tools:node=\"removeAll\"\n"
+ " tools:selector=\"com.example.lib3\">\n"
+ " </permission>\n"
+ " <permission android:name=\"permissionThree\""
+ " android:protectionLevel=\"signature\""
+ " tools:node=\"replace\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriorityOne = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib1\">\n"
+ "\n"
+ " <permission android:name=\"permissionOne\""
+ " android:protectionLevel=\"signature\">\n"
+ " </permission>\n"
+ " <permission android:name=\"permissionTwo\""
+ " android:protectionLevel=\"signature\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
String lowerPriorityTwo = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib2\">\n"
+ "\n"
+ " <permission android:name=\"permissionThree\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ " <permission android:name=\"permissionFour\""
+ " android:protectionLevel=\"normal\">\n"
+ " </permission>\n"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
XmlDocument secondLibrary = TestUtils.xmlLibraryFromString(
TestUtils.sourceFile(getClass(), "lowerPriorityTwo"), lowerPriorityTwo);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(firstLibrary, mergingReportBuilder);
assertTrue(result.isPresent());
result = result.get().merge(secondLibrary, mergingReportBuilder);
assertTrue(result.isPresent());
ImmutableList<XmlElement> mergeableElements = result.get().getRootNode()
.getMergeableElements();
// I should have permissionTwo, permissionThree (replaced), permissionFour
// + remove and removeAll not cleaned
assertEquals(5, mergeableElements.size());
XmlNode.NodeName nodeName = XmlNode.fromXmlName("android:name");
for (int i = 0; i < 5; i++) {
XmlElement xmlElement = mergeableElements.get(i);
Optional<XmlAttribute> optionalName = xmlElement.getAttribute(nodeName);
if (!optionalName.isPresent()) {
continue;
}
String elementName = optionalName.get().getValue();
if (elementName.equals("permissionThree")) {
assertEquals("signature", xmlElement.getAttribute(
XmlNode.fromXmlName("android:protectionLevel")).get().getValue());
} else {
if (!elementName.equals("permissionOne") && !elementName.equals("permissionTwo")
&& !elementName.equals("permissionFour")) {
fail("Unexepected permission " + elementName);
}
}
}
}
public void testCompatibleScreens()
throws ParserConfigurationException, SAXException, IOException {
String higherPriority = ""
+ "<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"
+ " <compatible-screens>\n"
+ " <!-- all small size screens -->\n"
+ " <screen android:screenSize=\"small\" android:screenDensity=\"ldpi\" />\n"
+ " <screen android:screenSize=\"small\" android:screenDensity=\"mdpi\" />\n"
+ " <screen android:screenSize=\"small\" android:screenDensity=\"xhdpi\" />\n"
+ " <!-- all normal size screens -->\n"
+ " <screen android:screenSize=\"normal\" android:screenDensity=\"ldpi\" />\n"
+ " <screen android:screenSize=\"normal\" android:screenDensity=\"hdpi\" />\n"
+ " <screen android:screenSize=\"normal\" android:screenDensity=\"xhdpi\" />\n"
+ " </compatible-screens>"
+ "\n"
+ "</manifest>";
String lowerPriorityOne = ""
+ "<manifest\n"
+ " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n"
+ " xmlns:tools=\"http://schemas.android.com/tools\"\n"
+ " package=\"com.example.lib1\">\n"
+ "\n"
+ " <compatible-screens>\n"
+ " <!-- all small size screens -->\n"
+ " <screen android:screenSize=\"small\" android:screenDensity=\"ldpi\" />\n"
+ " <screen android:screenSize=\"small\" android:screenDensity=\"mdpi\" />\n"
+ " <screen android:screenSize=\"small\" android:screenDensity=\"hdpi\" />\n"
+ " <!-- all normal size screens -->\n"
+ " <screen android:screenSize=\"normal\" android:screenDensity=\"mdpi\" />\n"
+ " <screen android:screenSize=\"normal\" android:screenDensity=\"hdpi\" />\n"
+ " <screen android:screenSize=\"normal\" android:screenDensity=\"xhdpi\" />\n"
+ " </compatible-screens>"
+ "\n"
+ "</manifest>";
XmlDocument refDocument = TestUtils.xmlDocumentFromString(
TestUtils.sourceFile(getClass(), "higherPriority"), higherPriority);
XmlDocument firstLibrary = TestUtils.xmlLibraryFromString(
TestUtils.sourceFile(getClass(), "lowerPriorityOne"), lowerPriorityOne);
MergingReport.Builder mergingReportBuilder = new MergingReport.Builder(
new StdLogger(StdLogger.Level.VERBOSE));
Optional<XmlDocument> result = refDocument.merge(firstLibrary, mergingReportBuilder);
assertTrue(result.isPresent());
ImmutableList<XmlElement> mergeableElements = result.get().getRootNode()
.getMergeableElements();
assertEquals(1, mergeableElements.size());
ImmutableList<XmlElement> mergedScreens = mergeableElements.get(0)
.getMergeableElements();
// we should have merged screens with no duplicated elements.
assertEquals(8, mergedScreens.size());
}
}