/* * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership. * 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 io.appium.android.bootstrap.utils; import junit.framework.TestCase; import org.w3c.dom.Document; import org.w3c.dom.Node; import org.xml.sax.InputSource; import javax.xml.transform.Transformer; import javax.xml.transform.TransformerFactory; import javax.xml.transform.dom.DOMSource; import javax.xml.transform.stream.StreamResult; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import java.io.StringReader; import java.io.StringWriter; import java.util.ArrayList; public class XMLHierarchyTest extends TestCase { private static XPath xpath = XPathFactory.newInstance().newXPath(); public void testGetClassInstancePairs() throws Exception { String xmlString = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><hierarchy rotation=\"0\"><android.widget.FrameLayout index=\"0\" text=\"\" resource-id=\"\" class=\"android.widget.FrameLayout\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[0,0][480,800]\" instance=\"0\"><android.view.View index=\"0\" text=\"\" resource-id=\"android:id/action_bar\" class=\"android.view.View\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[0,38][480,110]\" instance=\"0\"><android.widget.TextView index=\"0\" text=\"TestApp\" resource-id=\"android:id/action_bar_title\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[73,55][173,92]\" instance=\"0\"/><android.widget.ImageButton index=\"1\" text=\"\" resource-id=\"\" class=\"android.widget.ImageButton\" package=\"com.droidzilla.testapp\" content-desc=\"More options\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"true\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[396,38][480,110]\" instance=\"0\"/></android.view.View><android.widget.GridView index=\"1\" text=\"\" resource-id=\"com.droidzilla.testapp:id/cm_course_books_grid\" class=\"android.widget.GridView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"true\" focused=\"true\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[3,113][477,797]\" instance=\"0\"><android.widget.RelativeLayout index=\"0\" text=\"\" resource-id=\"\" class=\"android.widget.RelativeLayout\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[3,113][238,413]\" instance=\"0\"><android.widget.TextView index=\"0\" text=\"Image 0\" resource-id=\"com.droidzilla.testapp:id/bookStatus\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[82,136][158,165]\" instance=\"1\"/><android.widget.TextView index=\"1\" text=\"John\" resource-id=\"com.droidzilla.testapp:id/bookTitle\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[96,361][144,390]\" instance=\"2\"/></android.widget.RelativeLayout><android.widget.RelativeLayout index=\"1\" text=\"\" resource-id=\"\" class=\"android.widget.RelativeLayout\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[241,113][476,413]\" instance=\"1\"><android.widget.TextView index=\"0\" text=\"Image 1\" resource-id=\"com.droidzilla.testapp:id/bookStatus\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[320,136][396,165]\" instance=\"3\"/><android.widget.TextView index=\"1\" text=\"Dan\" resource-id=\"com.droidzilla.testapp:id/bookTitle\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[339,361][377,390]\" instance=\"4\"/></android.widget.RelativeLayout><android.widget.RelativeLayout index=\"2\" text=\"\" resource-id=\"\" class=\"android.widget.RelativeLayout\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[3,416][238,716]\" instance=\"2\"><android.widget.TextView index=\"0\" text=\"Image 2\" resource-id=\"com.droidzilla.testapp:id/bookStatus\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[82,439][158,468]\" instance=\"5\"/><android.widget.TextView index=\"1\" text=\"Mary\" resource-id=\"com.droidzilla.testapp:id/bookTitle\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[96,664][145,693]\" instance=\"6\"/></android.widget.RelativeLayout><android.widget.RelativeLayout index=\"3\" text=\"\" resource-id=\"\" class=\"android.widget.RelativeLayout\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[241,416][476,716]\" instance=\"3\"><android.widget.TextView index=\"0\" text=\"Image 3\" resource-id=\"com.droidzilla.testapp:id/bookStatus\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[320,439][396,468]\" instance=\"7\"/><android.widget.TextView index=\"1\" text=\"Diana\" resource-id=\"com.droidzilla.testapp:id/bookTitle\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[331,664][386,693]\" instance=\"8\"/></android.widget.RelativeLayout></android.widget.GridView></android.widget.FrameLayout></hierarchy>\n"; InputSource testInput = new InputSource(new StringReader(xmlString)); Node root = (Node) xpath.evaluate("/", testInput, XPathConstants.NODE); XPathExpression expression = xpath.compile("//android.widget.GridView/android.widget.RelativeLayout"); ArrayList<ClassInstancePair> pairs = XMLHierarchy.getClassInstancePairs(expression, root); assertEquals(4, pairs.size()); assertEquals("android.widget.RelativeLayout", pairs.get(0).getAndroidClass()); assertEquals("0", pairs.get(0).getInstance()); assertEquals("2", pairs.get(2).getInstance()); } public void testFormatXMLInput() throws Exception { String xmlString = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><hierarchy><foo0 class='class0'><foo1 class='class1'></foo1><foo0 class='class0'></foo0><foo1 class='class1'></foo1></foo0><foo1 class='class1'></foo1></hierarchy>"; InputSource testInput = new InputSource(new StringReader(xmlString)); Node formatted = XMLHierarchy.formatXMLInput(testInput); Node childNode = formatted.getFirstChild().getFirstChild(); assertEquals("class0", childNode.getNodeName()); assertEquals("0", childNode.getAttributes().getNamedItem("instance").getNodeValue()); childNode = formatted.getFirstChild().getLastChild(); assertEquals("class1", childNode.getNodeName()); assertEquals("2", childNode.getAttributes().getNamedItem("instance").getNodeValue()); } public void testCleaningTags() throws Exception { String[] samples = {"foo $ bar", "foo$bar", "foo.bar", "foo. bar"}; String expected = "foo.bar"; for (String sample : samples) { String test = "<tag class=\"" + sample + "\">teeeeeext</tag>"; InputSource testInput = new InputSource(new StringReader(test)); Node formatted = XMLHierarchy.formatXMLInput(testInput); assertEquals(expected, formatted.getFirstChild().getNodeName()); } } public void testOutput() throws Exception { String xmlString = "<?xml version='1.0' encoding='UTF-8' standalone='yes'?><hierarchy rotation=\"0\"><android.widget.FrameLayout index=\"0\" text=\"\" resource-id=\"\" class=\"android.widget.FrameLayout\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[0,0][480,800]\" instance=\"0\"><android.view.View index=\"0\" text=\"\" resource-id=\"android:id/action_bar\" class=\"android.view.View\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[0,38][480,110]\" instance=\"0\"><android.widget.TextView index=\"0\" text=\"TestApp\" resource-id=\"android:id/action_bar_title\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[73,55][173,92]\" instance=\"0\"/><android.widget.ImageButton index=\"1\" text=\"\" resource-id=\"\" class=\"android.widget.ImageButton\" package=\"com.droidzilla.testapp\" content-desc=\"More options\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"true\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[396,38][480,110]\" instance=\"0\"/></android.view.View><android.widget.GridView index=\"1\" text=\"\" resource-id=\"com.droidzilla.testapp:id/cm_course_books_grid\" class=\"android.widget.GridView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"true\" focused=\"true\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[3,113][477,797]\" instance=\"0\"><android.widget.RelativeLayout index=\"0\" text=\"\" resource-id=\"\" class=\"android.widget.RelativeLayout\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[3,113][238,413]\" instance=\"0\"><android.widget.TextView index=\"0\" text=\"Image 0\" resource-id=\"com.droidzilla.testapp:id/bookStatus\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[82,136][158,165]\" instance=\"1\"/><android.widget.TextView index=\"1\" text=\"John\" resource-id=\"com.droidzilla.testapp:id/bookTitle\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[96,361][144,390]\" instance=\"2\"/></android.widget.RelativeLayout><android.widget.RelativeLayout index=\"1\" text=\"\" resource-id=\"\" class=\"android.widget.RelativeLayout\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[241,113][476,413]\" instance=\"1\"><android.widget.TextView index=\"0\" text=\"Image 1\" resource-id=\"com.droidzilla.testapp:id/bookStatus\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[320,136][396,165]\" instance=\"3\"/><android.widget.TextView index=\"1\" text=\"Dan\" resource-id=\"com.droidzilla.testapp:id/bookTitle\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[339,361][377,390]\" instance=\"4\"/></android.widget.RelativeLayout><android.widget.RelativeLayout index=\"2\" text=\"\" resource-id=\"\" class=\"android.widget.RelativeLayout\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[3,416][238,716]\" instance=\"2\"><android.widget.TextView index=\"0\" text=\"Image 2\" resource-id=\"com.droidzilla.testapp:id/bookStatus\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[82,439][158,468]\" instance=\"5\"/><android.widget.TextView index=\"1\" text=\"Mary\" resource-id=\"com.droidzilla.testapp:id/bookTitle\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[96,664][145,693]\" instance=\"6\"/></android.widget.RelativeLayout><android.widget.RelativeLayout index=\"3\" text=\"\" resource-id=\"\" class=\"android.widget.RelativeLayout\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"true\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[241,416][476,716]\" instance=\"3\"><android.widget.TextView index=\"0\" text=\"Image 3\" resource-id=\"com.droidzilla.testapp:id/bookStatus\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[320,439][396,468]\" instance=\"7\"/><android.widget.TextView index=\"1\" text=\"Diana\" resource-id=\"com.droidzilla.testapp:id/bookTitle\" class=\"android.widget.TextView\" package=\"com.droidzilla.testapp\" content-desc=\"\" checkable=\"false\" checked=\"false\" clickable=\"false\" enabled=\"true\" focusable=\"false\" focused=\"false\" scrollable=\"false\" long-clickable=\"false\" password=\"false\" selected=\"false\" bounds=\"[331,664][386,693]\" instance=\"8\"/></android.widget.RelativeLayout></android.widget.GridView></android.widget.FrameLayout></hierarchy>\n"; InputSource testInput = new InputSource(new StringReader(xmlString)); Node node = XMLHierarchy.formatXMLInput(testInput); Document doc = (Document) node; TransformerFactory tf = TransformerFactory.newInstance(); StringWriter writer = new StringWriter(); Transformer transformer; String out; transformer = tf.newTransformer(); transformer.transform(new DOMSource(doc), new StreamResult(writer)); out = writer.getBuffer().toString(); assertEquals(xmlString, out); //close enough } }