blob: 83bc78de86a6b0373236b7547c3121611f3fafa6 [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 org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import com.android.SdkConstants;
import com.android.utils.ILogger;
import com.android.xml.AndroidManifest;
import com.google.common.collect.ImmutableList;
import junit.framework.TestCase;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.w3c.dom.Attr;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import java.io.IOException;
import java.util.List;
import javax.xml.parsers.ParserConfigurationException;
/**
* Tests for the {@link ElementsTrimmer} class
*/
public class ElementsTrimmerTest extends TestCase {
@Mock
ILogger mILogger;
@Override
protected void setUp() throws Exception {
super.setUp();
MockitoAnnotations.initMocks(this);
}
public void testNoUseFeaturesDeclaration()
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"
+ " <permission android:name=\"permissionOne\" "
+ " permissionGroup=\"permissionGroupOne\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
new TestUtils.TestSourceLocation(
getClass(), "testNoUseFeaturesDeclaration"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
assertFalse(mergingReport.hasErrors());
Mockito.verifyZeroInteractions(mILogger);
assertEquals(0, mergingReport.getActionRecorder().build().getNodeKeys().size());
}
public void testNothingToTrim()
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"
+ " <uses-feature"
+ " android:required=\"false\""
+ " android:glEsVersion=\"0x0002000\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
new TestUtils.TestSourceLocation(
getClass(), "testNothingToTrim"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
assertFalse(mergingReport.hasErrors());
Mockito.verifyZeroInteractions(mILogger);
assertEquals(0, mergingReport.getActionRecorder().build().getNodeKeys().size());
}
public void testMultipleAboveTwoResults()
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"
+ " <uses-feature"
+ " android:required=\"true\""
+ " android:glEsVersion=\"0x00020000\"/>\n"
+ " <uses-feature"
+ " android:required=\"false\""
+ " android:glEsVersion=\"0x00021000\"/>\n"
+ " <uses-feature"
+ " android:glEsVersion=\"0x00022000\"/>\n"
+ " <uses-feature"
+ " android:required=\"false\""
+ " android:glEsVersion=\"0x00030000\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
new TestUtils.TestSourceLocation(
getClass(), "testMultipleAboveTwoResults"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
assertFalse(mergingReport.hasErrors());
Mockito.verifyZeroInteractions(mILogger);
// check action recording.
checkActionsRecording(mergingReport, 2);
// check results.
checkResults(xmlDocument, ImmutableList.of("0x00030000", "0x00022000"));
}
public void testSingleAboveTwoResults()
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"
+ " <uses-feature android:glEsVersion=\"0x00020000\"/>\n"
+ " <uses-feature android:glEsVersion=\"0x00021000\"/>\n"
+ " <uses-feature android:glEsVersion=\"0x00030000\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
new TestUtils.TestSourceLocation(
getClass(), "testSingleAboveTwoResults"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
assertFalse(mergingReport.hasErrors());
Mockito.verifyZeroInteractions(mILogger);
// check action recording.
checkActionsRecording(mergingReport, 2);
// check results.
checkResults(xmlDocument, ImmutableList.of("0x00030000"));
}
public void testMultipleBelowTwoResults()
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"
+ " <uses-feature"
+ " android:required=\"true\""
+ " android:glEsVersion=\"0x00010000\"/>\n"
+ " <uses-feature"
+ " android:glEsVersion=\"0x00011000\"/>\n"
+ " <uses-feature"
+ " android:required=\"false\""
+ " android:glEsVersion=\"0x00012000\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
new TestUtils.TestSourceLocation(
getClass(), "testMultipleBelowTwoResults"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
assertFalse(mergingReport.hasErrors());
Mockito.verifyZeroInteractions(mILogger);
// check action recording.
checkActionsRecording(mergingReport, 1);
// check results.
checkResults(xmlDocument, ImmutableList.of("0x00011000", "0x00012000"));
}
public void testSingleBelowTwoResults()
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"
+ " <uses-feature android:glEsVersion=\"0x00010000\"/>\n"
+ " <uses-feature android:glEsVersion=\"0x00011000\"/>\n"
+ " <uses-feature android:glEsVersion=\"0x00012000\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
new TestUtils.TestSourceLocation(
getClass(), "testSingleBelowTwoResults"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
assertFalse(mergingReport.hasErrors());
Mockito.verifyZeroInteractions(mILogger);
// check action recording.
checkActionsRecording(mergingReport, 2);
// check results.
checkResults(xmlDocument, ImmutableList.of("0x00012000"));
}
public void testMultipleAboveAndBelowTwoResults()
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"
+ " <uses-feature"
+ " android:required=\"true\""
+ " android:glEsVersion=\"0x00010000\"/>\n"
+ " <uses-feature"
+ " android:glEsVersion=\"0x00011000\"/>\n"
+ " <uses-feature"
+ " android:required=\"false\""
+ " android:glEsVersion=\"0x00012000\"/>\n"
+ " <uses-feature"
+ " android:required=\"true\""
+ " android:glEsVersion=\"0x00020000\"/>\n"
+ " <uses-feature"
+ " android:required=\"false\""
+ " android:glEsVersion=\"0x00021000\"/>\n"
+ " <uses-feature"
+ " android:glEsVersion=\"0x00022000\"/>\n"
+ " <uses-feature"
+ " android:required=\"false\""
+ " android:glEsVersion=\"0x00030000\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
new TestUtils.TestSourceLocation(
getClass(), "testMultipleAboveAndBelowTwoResults"), input);
MergingReport.Builder mergingReport = new MergingReport.Builder(mILogger);
ElementsTrimmer.trim(xmlDocument, mergingReport);
assertFalse(mergingReport.hasErrors());
Mockito.verifyZeroInteractions(mILogger);
// check action recording.
checkActionsRecording(mergingReport, 3);
// check results.
checkResults(xmlDocument,
ImmutableList.of("0x00011000", "0x00012000", "0x00022000", "0x00030000"));
}
public void testUsesFeatureSplit_attributeDeleted()
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"
+ " <uses-feature android:name=\"@string/lib_name\""
+ " android:required=\"false\""
+ " android:glEsVersion=\"0x00020000\"/>\n"
+ " <uses-feature"
+ " android:required=\"false\""
+ " android:glEsVersion=\"0x00030000\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
new TestUtils.TestSourceLocation(
getClass(), "testUsesFeatureSplit"), input);
ActionRecorder mockActionRecorder = Mockito.mock(ActionRecorder.class);
MergingReport.Builder mockReport = Mockito.mock(MergingReport.Builder.class);
when(mockReport.getActionRecorder()).thenReturn(mockActionRecorder);
ElementsTrimmer.trim(xmlDocument, mockReport);
// check that we have now 2 uses-feature with separated keys.
NodeList elementsByTagName = xmlDocument.getRootNode().getXml()
.getElementsByTagName("uses-feature");
assertEquals(2, elementsByTagName.getLength());
// verify the action was recorded.
verify(mockActionRecorder).recordAttributeAction(any(XmlAttribute.class),
eq(Actions.ActionType.REJECTED), (AttributeOperationType) eq(null));
for (int i = 0; i < elementsByTagName.getLength(); i++) {
NamedNodeMap attributes = elementsByTagName.item(i).getAttributes();
assertEquals(2, attributes.getLength());
ensureOnlyOneKey(attributes, ManifestModel.NodeTypes.USES_FEATURE);
}
}
public void testUsesFeatureSplit_elementDeleted()
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"
+ " <uses-feature android:required=\"false\""
+ " android:glEsVersion=\"0x00020000\"/>\n"
+ " <uses-feature"
+ " android:required=\"false\""
+ " android:glEsVersion=\"0x00030000\"/>\n"
+ "\n"
+ "</manifest>";
XmlDocument xmlDocument = TestUtils.xmlDocumentFromString(
new TestUtils.TestSourceLocation(
getClass(), "testUsesFeatureSplit"), input);
ActionRecorder mockActionRecorder = Mockito.mock(ActionRecorder.class);
MergingReport.Builder mockReport = Mockito.mock(MergingReport.Builder.class);
when(mockReport.getActionRecorder()).thenReturn(mockActionRecorder);
ElementsTrimmer.trim(xmlDocument, mockReport);
// check that we have now 2 uses-feature with separated keys.
NodeList elementsByTagName = xmlDocument.getRootNode().getXml()
.getElementsByTagName("uses-feature");
assertEquals(1, elementsByTagName.getLength());
// verify the action was recorded.
verify(mockActionRecorder).recordNodeAction(any(XmlElement.class),
eq(Actions.ActionType.REJECTED));
for (int i = 0; i < elementsByTagName.getLength(); i++) {
NamedNodeMap attributes = elementsByTagName.item(i).getAttributes();
assertEquals(2, attributes.getLength());
ensureOnlyOneKey(attributes, ManifestModel.NodeTypes.USES_FEATURE);
}
}
private void ensureOnlyOneKey(NamedNodeMap namedNodeMap, ManifestModel.NodeTypes nodeType) {
String firstKey = null;
ImmutableList<String> keyAttributesNames =
nodeType.getNodeKeyResolver().getKeyAttributesNames();
for (String keyAttributesName : keyAttributesNames) {
if (namedNodeMap.getNamedItemNS(SdkConstants. ANDROID_URI, keyAttributesName) != null) {
if (firstKey != null) {
fail("Found 2 keys : " + firstKey + " and " + keyAttributesName);
}
firstKey = keyAttributesName;
}
}
}
private static void checkActionsRecording(
MergingReport.Builder mergingReport,
int expectedActionsNumber) {
Actions actions = mergingReport.build().getActions();
assertEquals(expectedActionsNumber, actions.getNodeKeys().size());
for (XmlNode.NodeKey nodeKey : actions.getNodeKeys()) {
assertEquals(1, actions.getNodeRecords(nodeKey).size());
assertEquals(Actions.ActionType.REJECTED,
actions.getNodeRecords(nodeKey).get(0).getActionType());
}
}
private static void checkResults(XmlDocument xmlDocument, List<String> expectedVersions) {
NodeList elementsByTagName = xmlDocument.getRootNode().getXml()
.getElementsByTagName("uses-feature");
assertEquals(expectedVersions.size(), elementsByTagName.getLength());
for (int i = 0; i < elementsByTagName.getLength(); i++) {
Element item = (Element) elementsByTagName.item(i);
Attr glEsVersion = item.getAttributeNodeNS(SdkConstants.ANDROID_URI,
AndroidManifest.ATTRIBUTE_GLESVERSION);
assertNotNull(glEsVersion);
assertTrue(expectedVersions.contains(glEsVersion.getValue()));
}
}
}