/* * 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.DecisionTreeRecord; import static com.android.manifmerger.XmlNode.NodeKey; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import com.android.SdkConstants; import com.android.ide.common.blame.SourceFile; import com.android.ide.common.blame.SourceFilePosition; import com.android.ide.common.blame.SourcePosition; import com.android.sdklib.mock.MockLog; import com.android.utils.ILogger; import com.android.utils.StdLogger; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import junit.framework.TestCase; import org.w3c.dom.Element; import org.xml.sax.SAXException; import java.io.IOException; import java.util.List; import javax.xml.parsers.ParserConfigurationException; /** * Tests for {@link Actions} class */ public class ActionsTest extends TestCase { public void testGetNodeKeys() { Element xmlElement = mock(Element.class); when(xmlElement.getNodeName()).thenReturn("activity"); when(xmlElement.getAttributeNS(SdkConstants.ANDROID_URI, "name")).thenReturn("keyOne"); NodeKey nodeKey = NodeKey.fromXml(xmlElement); assertNotNull(nodeKey); ImmutableMap.Builder<NodeKey, DecisionTreeRecord> records = ImmutableMap.builder(); DecisionTreeRecord activityDecisionTree = new DecisionTreeRecord(); records.put(nodeKey, activityDecisionTree); Actions actions = new Actions(records.build()); assertEquals(1, actions.getNodeKeys().size()); assertTrue(actions.getNodeKeys().contains(nodeKey)); } public void testGetNodeRecords() { Element xmlElement = mock(Element.class); when(xmlElement.getNodeName()).thenReturn("activity"); when(xmlElement.getAttributeNS(SdkConstants.ANDROID_URI, "name")).thenReturn("keyOne"); NodeKey nodeKey = NodeKey.fromXml(xmlElement); assertNotNull(nodeKey); ImmutableMap.Builder<NodeKey, DecisionTreeRecord> records = ImmutableMap.builder(); DecisionTreeRecord activityDecisionTree = new DecisionTreeRecord(); activityDecisionTree.addNodeRecord(new Actions.NodeRecord( Actions.ActionType.ADDED, new SourceFilePosition(new SourceFile("file"), new SourcePosition(1, 2, -1)), new NodeKey("nodeKey"), null, /* reason */ NodeOperationType.MERGE)); records.put(nodeKey, activityDecisionTree); Actions actions = new Actions(records.build()); // lookup using key ImmutableList<Actions.NodeRecord> nodeRecords = actions.getNodeRecords(nodeKey); assertNotNull(nodeRecords); assertEquals(1, nodeRecords.size()); } public void testGetAttributesRecords() { Element xmlElement = mock(Element.class); when(xmlElement.getNodeName()).thenReturn("activity"); when(xmlElement.getAttributeNS(SdkConstants.ANDROID_URI, "name")).thenReturn("keyOne"); NodeKey nodeKey = NodeKey.fromXml(xmlElement); assertNotNull(nodeKey); ImmutableMap.Builder<NodeKey, DecisionTreeRecord> records = ImmutableMap.builder(); DecisionTreeRecord activityDecisionTree = new DecisionTreeRecord(); activityDecisionTree.addNodeRecord(new Actions.NodeRecord( Actions.ActionType.ADDED, new SourceFilePosition(new SourceFile("file"), new SourcePosition(1, 2, -1)), new NodeKey("nodeKey"), null, /* reason */ NodeOperationType.MERGE)); XmlNode.NodeName attributeName = XmlNode.fromXmlName("android:name"); activityDecisionTree.mAttributeRecords.put(attributeName, ImmutableList.of( new Actions.AttributeRecord(Actions.ActionType.INJECTED, new SourceFilePosition( new SourceFile("file"), new SourcePosition(1, 2, -1)), new NodeKey("nodeKey"), null, /* reason */ AttributeOperationType.STRICT))); records.put(nodeKey, activityDecisionTree); Actions actions = new Actions(records.build()); ImmutableList<XmlNode.NodeName> recordedAttributeNames = actions .getRecordedAttributeNames(nodeKey); assertTrue(recordedAttributeNames.contains(attributeName)); ImmutableList<Actions.AttributeRecord> attributeRecords = actions .getAttributeRecords(nodeKey, attributeName); assertEquals(1, attributeRecords.size()); } /** * test tools:node="removeAll" with several target elements to be removed. */ public void testActionsPersistenceAndLoading() throws ParserConfigurationException, SAXException, IOException { String higherPriority = "" + "<manifest\n" // 1 + " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" // 2 + " xmlns:tools=\"http://schemas.android.com/tools\"\n" // 3 + " package=\"com.example.lib3\">\n" // 4 + "\n" // 5 + " <permission\n" // 6 + " android:name=\"permissionOne\"\n" // 7 + " tools:node=\"remove\">\n" // 8 + " </permission>\n" // 10 + " <permission \n" // 11 + " tools:node=\"removeAll\"\n" // 12 + " tools:selector=\"com.example.lib3\">\n" // 13 + " </permission>\n" // 14 + " <permission\n" // 15 + " android:name=\"permissionThree\"\n" // 16 + " android:protectionLevel=\"signature\"\n" // 17 + " tools:node=\"replace\">\n" // 18 + " </permission>\n" // 19 + "\n" // 20 + "</manifest>"; // 21 String lowerPriorityOne = "" + "<manifest\n" // 1 + " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" // 2 + " xmlns:tools=\"http://schemas.android.com/tools\"\n" // 3 + " package=\"com.example.lib1\">\n" // 4 + "\n" // 5 + " <permission android:name=\"permissionOne\"\n" // 6 + " android:protectionLevel=\"signature\">\n" // 7 + " </permission>\n" // 8 + " <permission android:name=\"permissionTwo\"\n" // 9 + " android:protectionLevel=\"signature\">\n" // 10 + " </permission>\n" // 11 + "\n" // 12 + "</manifest>"; // 13 String lowerPriorityTwo = "" + "<manifest\n" // 1 + " xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" // 2 + " xmlns:tools=\"http://schemas.android.com/tools\"\n" // 3 + " package=\"com.example.lib2\">\n" // 4 + "\n" // 5 + " <permission android:name=\"permissionThree\"\n" // 6 + " android:protectionLevel=\"normal\">\n" // 7 + " </permission>\n" // 8 + " <permission android:name=\"permissionFour\"\n" // 9 + " android:protectionLevel=\"normal\">\n" // 10 + " </permission>\n" // 11 + "\n" // 12 + "</manifest>"; // 13 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()); ILogger logger = new MockLog(); XmlDocument cleanedDocument = ToolsInstructionsCleaner.cleanToolsReferences(result.get(), logger); assertNotNull(cleanedDocument); Actions actions = mergingReportBuilder.getActionRecorder().build(); String expectedMappings = "1<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" + "2<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" + "3 package=\"com.example.lib3\" >\n" + "4\n" + "5 <permission\n" + "5-->ActionsTest#higherPriority:14:5-18:18\n" + "6 android:name=\"permissionThree\"\n" + "6-->ActionsTest#higherPriority:15:14-44\n" + "7 android:protectionLevel=\"signature\" >\n" + "7-->ActionsTest#higherPriority:16:14-49\n" + "8 </permission>\n" + "9 <permission\n" + "9-->ActionsTest#lowerPriorityOne:9:5-11:18\n" + "10 android:name=\"permissionTwo\"\n" + "10-->ActionsTest#lowerPriorityOne:9:17-45\n" + "11 android:protectionLevel=\"signature\" >\n" + "11-->ActionsTest#lowerPriorityOne:10:14-49\n" + "12 </permission>\n" + "13 <permission\n" + "13-->ActionsTest#lowerPriorityTwo:9:5-11:18\n" + "14 android:name=\"permissionFour\"\n" + "14-->ActionsTest#lowerPriorityTwo:9:17-46\n" + "15 android:protectionLevel=\"normal\" >\n" + "15-->ActionsTest#lowerPriorityTwo:10:14-46\n" + "16 </permission>\n" + "17\n" + "18</manifest>\n"; assertEquals(expectedMappings, actions.blame(cleanedDocument)); // persist the records String persistedMappings = actions.persist(); System.out.println(persistedMappings); // and reload them from the persisted media. Actions newActions = Actions.load(persistedMappings); assertNotNull(newActions); // check equality. for (NodeKey nodeKey : actions.getNodeKeys()) { ImmutableList<Actions.NodeRecord> expectedNodeRecords = actions.getNodeRecords(nodeKey); assertNotNull(expectedNodeRecords); assertEquals(expectedNodeRecords.size(), newActions.getNodeRecords(nodeKey).size()); for (Actions.NodeRecord nodeRecord : newActions.getNodeRecords(nodeKey)) { assertTrue("Cannot find node=" + nodeKey + "record=" + nodeRecord, findNodeRecordInList(nodeRecord, expectedNodeRecords)); } for (XmlNode.NodeName nodeName : actions .getRecordedAttributeNames(nodeKey)) { ImmutableList<Actions.AttributeRecord> expectedAttributeRecords = actions.getAttributeRecords(nodeKey, nodeName); ImmutableList<Actions.AttributeRecord> actualAttributeRecords = newActions.getAttributeRecords(nodeKey, nodeName); assertEquals(expectedAttributeRecords.size(), actualAttributeRecords.size()); for (Actions.AttributeRecord expectedAttributeRecord : expectedAttributeRecords) { assertTrue("Cannot find attribute=" + nodeName + " record=" + expectedAttributeRecord, findAttributeRecordInList( expectedAttributeRecord, actualAttributeRecords) ); } } } } public static boolean findNodeRecordInList( Actions.NodeRecord nodeRecord, List<Actions.NodeRecord> nodeRecordList) { for (Actions.NodeRecord record : nodeRecordList) { if (record.getActionLocation().equals(nodeRecord.getActionLocation()) && record.getActionType() == nodeRecord.getActionType()) { return true; } } return false; } private static boolean findAttributeRecordInList( Actions.AttributeRecord attributeRecord, List<Actions.AttributeRecord> attributeRecordList) { for (Actions.AttributeRecord record : attributeRecordList) { if (record.getOperationType() == attributeRecord.getOperationType() && record.getActionType() == attributeRecord.getActionType() && record.getActionLocation().equals(attributeRecord.getActionLocation())) { return true; } } return false; } }