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 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());
  }