How would Android UiAutomator behave if it encounters "Force Close or ANR"

2.2k views Asked by At

being new to Android UiAutomator, i am able to validate some tests by script development, wanted to know 1) what would happen Or how UIautomator would handle / behave when an Application being tested gets an "Force close" Or "ANR" 2) please let me know if we should do anything specific to handle this ? will the remaining scripts continue to get executed if Force close or ANR occurs ?

Thanks in Advance

2

There are 2 answers

0
Allen Hair On

Crash dialogs can pose a problem when they prevent your test from finding the UI elements it expects. If you can dismiss the dialog however, your test can continue running (as long as your test is running in a different process to the one that was killed).

The difficult part is that these dialogs can appear at any point during your test execution. The UiWatcher class created for this reason. For each watcher registered using UiDevice.registerWatcher(..), UiAutomator will call the checkForCondition() method when it can't find an object. This gives you a chance to detect the crash dialog and dismiss it without it interfereing with your test.

0
Eaway Lu On

Here has a good reference for ANR/CRASH UiWatchers implementation.

/*
 * Copyright (C) 2013 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.uiautomator.common;

import android.util.Log;
import com.android.uiautomator.core.*;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class UiWatchers {
  private static final String LOG_TAG = UiWatchers.class.getSimpleName();
  private final List<String>  mErrors = new ArrayList<String>();

  /**
   * We can use the UiDevice registerWatcher to register a small script to be
   * executed when the framework is waiting for a control to appear. Waiting may
   * be the cause of an unexpected dialog on the screen and it is the time when
   * the framework runs the registered watchers. This is a sample watcher
   * looking for ANR and crashes. it closes it and moves on. You should create
   * your own watchers and handle error logging properly for your type of tests.
   */
  public void registerAnrAndCrashWatchers() {

    UiDevice.getInstance().registerWatcher("ANR", new UiWatcher() {
      @Override
      public boolean checkForCondition() {
        UiObject window = new UiObject(new UiSelector()
            .className("com.android.server.am.AppNotRespondingDialog"));
        String errorText = null;
        if (window.exists()) {
          try {
            errorText = window.getText();
          } catch (UiObjectNotFoundException e) {
            Log.e(LOG_TAG, "dialog gone?", e);
          }
          onAnrDetected(errorText);
          postHandler();
          return true; // triggered
        }
        return false; // no trigger
      }
    });

    // class names may have changed
    UiDevice.getInstance().registerWatcher("ANR2", new UiWatcher() {
      @Override
      public boolean checkForCondition() {
        UiObject window = new UiObject(new UiSelector().packageName("android")
            .textContains("isn't responding."));
        if (window.exists()) {
          String errorText = null;
          try {
            errorText = window.getText();
          } catch (UiObjectNotFoundException e) {
            Log.e(LOG_TAG, "dialog gone?", e);
          }
          onAnrDetected(errorText);
          postHandler();
          return true; // triggered
        }
        return false; // no trigger
      }
    });

    UiDevice.getInstance().registerWatcher("CRASH", new UiWatcher() {
      @Override
      public boolean checkForCondition() {
        UiObject window = new UiObject(new UiSelector()
            .className("com.android.server.am.AppErrorDialog"));
        if (window.exists()) {
          String errorText = null;
          try {
            errorText = window.getText();
          } catch (UiObjectNotFoundException e) {
            Log.e(LOG_TAG, "dialog gone?", e);
          }
          onCrashDetected(errorText);
          postHandler();
          return true; // triggered
        }
        return false; // no trigger
      }
    });

    UiDevice.getInstance().registerWatcher("CRASH2", new UiWatcher() {
      @Override
      public boolean checkForCondition() {
        UiObject window = new UiObject(new UiSelector().packageName("android")
            .textContains("has stopped"));
        if (window.exists()) {
          String errorText = null;
          try {
            errorText = window.getText();
          } catch (UiObjectNotFoundException e) {
            Log.e(LOG_TAG, "dialog gone?", e);
          }
          onCrashDetected(errorText);
          postHandler();
          return true; // triggered
        }
        return false; // no trigger
      }
    });

    Log.i(LOG_TAG, "Registed GUI Exception watchers");
  }

  public void registerAcceptSSLCertWatcher() {
    UiDevice.getInstance().registerWatcher("SSLCERTERROR", new UiWatcher() {
      @Override
      public boolean checkForCondition() {
        UiObject continueButton = new UiObject(new UiSelector()
          .className("android.widget.Button").packageName("com.android.browser").text("Continue"));
        if (continueButton.exists()) {
          try {
            continueButton.click();
            return true; // triggered
          } catch (UiObjectNotFoundException e) {
            Log.e(LOG_TAG, "Exception", e);
          }
        }
        return false; // no trigger
      }
    });

    Log.i(LOG_TAG, "Registered SSL Certificate Error Watchers");
  }

  public void onAnrDetected(String errorText) {
    mErrors.add(errorText);
  }

  public void onCrashDetected(String errorText) {
    mErrors.add(errorText);
  }

  public void reset() {
    mErrors.clear();
  }

  public List<String> getErrors() {
    return Collections.unmodifiableList(mErrors);
  }

  /**
   * Current implementation ignores the exception and continues.
   */
  public void postHandler() {
    // TODO: Add custom error logging here

    String formatedOutput = String.format("UI Exception Message: %-20s\n",
        UiDevice.getInstance().getCurrentPackageName());
    Log.e(LOG_TAG, formatedOutput);

    UiObject buttonOK = new UiObject(new UiSelector().text("OK").enabled(true));
    // sometimes it takes a while for the OK button to become enabled
    buttonOK.waitForExists(5000);
    try {
      buttonOK.click();
    } catch (UiObjectNotFoundException e) {
      Log.e(LOG_TAG, "Exception", e);
    }
  }
}

And base on the same concept, you are able to handle other common system/3rd-party popups.

// Common buttons that we can handle.
public static final String[] COMMON_BUTTONS = {
        "OK", "Cancel", "Yes", "No", "Dismiss"
};

public static boolean handleCommonDialog() {
    UiDevice device = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
    UiObject button = null;
    for (String keyword : Config.COMMON_BUTTONS) {
        button = device.findObject(new UiSelector().text(keyword).enabled(true));
        if (button != null && button.exists()) {
            break;
        }
    }
    try {
        // sometimes it takes a while for the OK button to become enabled
        if (button != null && button.exists()) {
            button.waitForExists(5000);
            button.click();
            return true; // triggered
        }
    } catch (UiObjectNotFoundException e) {
    }
    return false; // no trigger
}