/* * 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.PlaceholderHandler.KeyBasedValueResolver; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import com.android.annotations.NonNull; import com.android.ide.common.blame.SourceFilePosition; import com.android.ide.common.blame.SourcePosition; import com.android.sdklib.mock.MockLog; import com.google.common.base.Optional; import junit.framework.TestCase; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.xml.sax.SAXException; import java.io.IOException; import javax.xml.parsers.ParserConfigurationException; /** * Tests for the {@link com.android.manifmerger.PlaceholderHandler} */ public class PlaceholderHandlerTest extends TestCase { @Mock ActionRecorder mActionRecorder; @Mock MergingReport.Builder mBuilder; MockLog mMockLog = new MockLog(); KeyBasedValueResolver<String> nullResolver = new KeyBasedValueResolver<String>() { @Override public String getValue(@NonNull String key) { // not provided a placeholder value should generate an error. return null; } }; @Override protected void setUp() throws Exception { super.setUp(); MockitoAnnotations.initMocks(this); when(mBuilder.getLogger()).thenReturn(mMockLog); when(mBuilder.getActionRecorder()).thenReturn(mActionRecorder); } public void testPlaceholders() throws ParserConfigurationException, SAXException, IOException { String xml = "" + "<manifest\n" + " xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" + " <activity android:name=\"activityOne\"\n" + " android:attr1=\"${landscapePH}\"\n" + " android:attr2=\"prefix.${landscapePH}\"\n" + " android:attr3=\"${landscapePH}.suffix\"\n" + " android:attr4=\"prefix${landscapePH}suffix\">\n" + " </activity>\n" + "</manifest>"; XmlDocument refDocument = TestUtils.xmlDocumentFromString( TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml); PlaceholderHandler handler = new PlaceholderHandler(); handler.visit( ManifestMerger2.MergeType.APPLICATION, refDocument, new KeyBasedValueResolver<String>() { @Override public String getValue(@NonNull String key) { return "newValue"; } }, mBuilder); Optional<XmlElement> activityOne = refDocument.getRootNode() .getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY, ".activityOne"); assertTrue(activityOne.isPresent()); assertEquals(5, activityOne.get().getAttributes().size()); // check substitution. assertEquals("newValue", activityOne.get().getAttribute( XmlNode.fromXmlName("android:attr1")).get().getValue()); assertEquals("prefix.newValue", activityOne.get().getAttribute( XmlNode.fromXmlName("android:attr2")).get().getValue()); assertEquals("newValue.suffix", activityOne.get().getAttribute( XmlNode.fromXmlName("android:attr3")).get().getValue()); assertEquals("prefixnewValuesuffix", activityOne.get().getAttribute( XmlNode.fromXmlName("android:attr4")).get().getValue()); for (XmlAttribute xmlAttribute : activityOne.get().getAttributes()) { // any attribute other than android:name should have been injected. if (!xmlAttribute.getName().toString().contains("name")) { verify(mActionRecorder).recordAttributeAction( xmlAttribute, SourcePosition.UNKNOWN, Actions.ActionType.INJECTED, null); } } } public void testSeveralPlaceholders() throws ParserConfigurationException, SAXException, IOException { String xml = "" + "<manifest\n" + " xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" + " <activity android:name=\"activityOne\"\n" + " android:attr1=\"prefix${first}${second}\"\n" + " android:attr2=\"${first}${second}suffix\"\n" + " android:attr3=\"prefix${first}.${second}suffix\"\n" + " android:attr4=\"${first}.${second}\"/>\n" + "</manifest>"; XmlDocument refDocument = TestUtils.xmlDocumentFromString( TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml); PlaceholderHandler handler = new PlaceholderHandler(); handler.visit( ManifestMerger2.MergeType.APPLICATION, refDocument, new KeyBasedValueResolver<String>() { @Override public String getValue(@NonNull String key) { if (key.equals("first")) { return "firstValue"; } else { return "secondValue"; } } }, mBuilder); Optional<XmlElement> activityOne = refDocument.getRootNode() .getNodeByTypeAndKey(ManifestModel.NodeTypes.ACTIVITY, ".activityOne"); assertTrue(activityOne.isPresent()); assertEquals(5, activityOne.get().getAttributes().size()); // check substitution. assertEquals("prefixfirstValuesecondValue", activityOne.get().getAttribute( XmlNode.fromXmlName("android:attr1")).get().getValue()); assertEquals("firstValuesecondValuesuffix", activityOne.get().getAttribute( XmlNode.fromXmlName("android:attr2")).get().getValue()); assertEquals("prefixfirstValue.secondValuesuffix", activityOne.get().getAttribute( XmlNode.fromXmlName("android:attr3")).get().getValue()); assertEquals("firstValue.secondValue", activityOne.get().getAttribute( XmlNode.fromXmlName("android:attr4")).get().getValue()); for (XmlAttribute xmlAttribute : activityOne.get().getAttributes()) { // any attribute other than android:name should have been injected. if (!xmlAttribute.getName().toString().contains("name")) { verify(mActionRecorder, times(2)).recordAttributeAction( xmlAttribute, SourcePosition.UNKNOWN, Actions.ActionType.INJECTED, null); } } } public void testPlaceHolder_notProvided() throws ParserConfigurationException, SAXException, IOException { String xml = "" + "<manifest\n" + " xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" + " <activity android:name=\"activityOne\"\n" + " android:attr1=\"${landscapePH}\"/>\n" + "</manifest>"; XmlDocument refDocument = TestUtils.xmlDocumentFromString( TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml); PlaceholderHandler handler = new PlaceholderHandler(); handler.visit(ManifestMerger2.MergeType.APPLICATION, refDocument, nullResolver, mBuilder); // verify the error was recorded. verify(mBuilder).addMessage( any(SourceFilePosition.class), eq(MergingReport.Record.Severity.ERROR), anyString()); } public void testPlaceHolder_notProvided_inLibrary() throws ParserConfigurationException, SAXException, IOException { String xml = "" + "<manifest\n" + " xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" + " <activity android:name=\"activityOne\"\n" + " android:attr1=\"${landscapePH}\"/>\n" + "</manifest>"; XmlDocument refDocument = TestUtils.xmlDocumentFromString( TestUtils.sourceFile(getClass(), "testPlaceholders#xml"), xml); PlaceholderHandler handler = new PlaceholderHandler(); handler.visit(ManifestMerger2.MergeType.LIBRARY, refDocument, nullResolver, mBuilder); // verify the error was recorded. verify(mBuilder).addMessage( any(SourceFilePosition.class), eq(MergingReport.Record.Severity.INFO), anyString()); } }