Skip to content

Commit

Permalink
Limit traversal depth and maximum length of xml values to avoid unexp…
Browse files Browse the repository at this point in the history
…ected OOM errors (#341)
  • Loading branch information
mykola-mokhnach authored Dec 11, 2018
1 parent 25dc0a6 commit 84ea805
Show file tree
Hide file tree
Showing 2 changed files with 20 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,7 @@
public class StringHelpers {

public static String abbreviate(@Nullable String str, int len) {
if (str != null && str.length() > len) {
return str.substring(0, len) + "...";
}
return str;
return str != null && str.length() > len ? str.substring(0, len) + "\u2026" : str;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@

import static androidx.test.espresso.util.TreeIterables.breadthFirstViewTraversal;
import static io.appium.espressoserver.lib.helpers.AndroidLogger.logger;
import static io.appium.espressoserver.lib.helpers.StringHelpers.abbreviate;
import static io.appium.espressoserver.lib.helpers.XMLHelpers.toNodeName;
import static io.appium.espressoserver.lib.helpers.XMLHelpers.toSafeString;

Expand All @@ -57,6 +58,8 @@ public class SourceDocument {
private static final String VIEW_INDEX = "viewIndex";
private static final String NAMESPACE = "";
private final static String DEFAULT_VIEW_CLASS_NAME = "android.view.View";
private final static int MAX_TRAVERSE_DEPTH = 70;
private final static int MAX_XML_VALUE_LENGTH = 64 * 1024;

private XmlSerializer serializer;
@Nullable
Expand All @@ -76,8 +79,10 @@ private SourceDocument(@Nullable View root, @Nullable SparseArray<View> viewMap)
private void setAttribute(ViewAttributesEnum attrName, @Nullable Object attrValue) throws IOException {
// Do not write attributes, whose values equal to null
if (attrValue != null) {
serializer.attribute(NAMESPACE, attrName.toString(),
toSafeString(String.valueOf(attrValue), NON_XML_CHAR_REPLACEMENT));
// Cut off longer strings to avoid OOM errors
String xmlValue = abbreviate(toSafeString(String.valueOf(attrValue), NON_XML_CHAR_REPLACEMENT),
MAX_XML_VALUE_LENGTH);
serializer.attribute(NAMESPACE, attrName.toString(), xmlValue);
}
}

Expand Down Expand Up @@ -130,8 +135,9 @@ private static String toXmlNodeName(@Nullable String className) {
* Recursively visit all of the views and map them to XML elements
*
* @param view The root view
* @param depth The current traversal depth
*/
private void serializeView(View view) throws IOException {
private void serializeView(View view, final int depth) throws IOException {
if (view == null) {
return;
}
Expand Down Expand Up @@ -173,11 +179,16 @@ private void serializeView(View view) throws IOException {
viewMap.put(viewMap.size(), view);
}

// Visit the children and build them too
for (View childView : breadthFirstViewTraversal(view)) {
if (!view.equals(childView)) {
serializeView(childView);
if (depth < MAX_TRAVERSE_DEPTH) {
// Visit the children and build them too
for (View childView : breadthFirstViewTraversal(view)) {
if (!view.equals(childView)) {
serializeView(childView, depth + 1);
}
}
} else {
logger.warn(String.format("Skipping traversal of %s's children, since the current depth " +
"has reached its maximum allowed value of %s", view.getClass().getName(), depth));
}

serializer.endTag(NAMESPACE, tagName);
Expand All @@ -194,7 +205,7 @@ public synchronized String toXMLString() throws AppiumException {
serializer.startDocument("UTF-8", true);
serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
final long startTime = SystemClock.uptimeMillis();
serializeView(root == null ? new ViewGetter().getRootView() : root);
serializeView(root == null ? new ViewGetter().getRootView() : root, 0);
serializer.endDocument();
logger.info(String.format("The source XML tree has been fetched in %sms", SystemClock.uptimeMillis() - startTime));
return writer.toString();
Expand Down

0 comments on commit 84ea805

Please sign in to comment.