Home | History | Annotate | Download | only in util
      1 package com.android.compatibility.common.util;
      2 
      3 import static junit.framework.TestCase.fail;
      4 
      5 import com.google.common.base.Joiner;
      6 import com.google.common.io.Closeables;
      7 
      8 import java.io.BufferedReader;
      9 import java.io.IOException;
     10 import java.io.InputStream;
     11 import java.io.InputStreamReader;
     12 import java.util.UUID;
     13 import java.util.concurrent.TimeUnit;
     14 
     15 /**
     16  * Inherit this class and implement {@link #executeShellCommand(String)} to be able to assert that
     17  * logcat contains what you want.
     18  */
     19 public abstract class LogcatInspector {
     20     private static final int SMALL_LOGCAT_DELAY = 1000;
     21 
     22     /**
     23      * Should execute adb shell {@param command} and return an {@link InputStream} with the result.
     24      */
     25     protected abstract InputStream executeShellCommand(String command) throws IOException;
     26 
     27     /**
     28      * Logs an unique string using tag {@param tag} and wait until it appears to continue execution.
     29      *
     30      * @return a unique separator string.
     31      * @throws IOException if error while executing command.
     32      */
     33     public String mark(String tag) throws IOException {
     34         String uniqueString = ":::" + UUID.randomUUID().toString();
     35         executeShellCommand("log -t " + tag + " " + uniqueString);
     36         // This is to guarantee that we only return after the string has been logged, otherwise
     37         // in practice the case where calling Log.?(<message1>) right after clearAndMark() resulted
     38         // in <message1> appearing before the unique identifier. It's not guaranteed per the docs
     39         // that log command will have written when returning, so better be safe. 5s should be fine.
     40         assertLogcatContainsInOrder(tag + ":* *:S", 5, uniqueString);
     41         return uniqueString;
     42     }
     43 
     44     /**
     45      * Wait for up to {@param maxTimeoutInSeconds} for the given {@param logcatStrings} strings to
     46      * appear in logcat in the given order. By passing the separator returned by {@link
     47      * #mark(String)} as the first string you can ensure that only logs emitted after that
     48      * call to mark() are found. Repeated strings are not supported.
     49      *
     50      * @throws AssertionError if the strings are not found in the given time.
     51      * @throws IOException if error while reading.
     52      */
     53     public void assertLogcatContainsInOrder(
     54             String filterSpec, int maxTimeoutInSeconds, String... logcatStrings)
     55             throws AssertionError, IOException {
     56         try {
     57             int nextStringIndex =
     58                     numberOfLogcatStringsFound(filterSpec, maxTimeoutInSeconds, logcatStrings);
     59             if (nextStringIndex < logcatStrings.length) {
     60                 fail(
     61                         "Couldn't find "
     62                                 + logcatStrings[nextStringIndex]
     63                                 + (nextStringIndex > 0
     64                                         ? " after " + logcatStrings[nextStringIndex - 1]
     65                                         : "")
     66                                 + " within "
     67                                 + maxTimeoutInSeconds
     68                                 + " seconds ");
     69             }
     70         } catch (InterruptedException e) {
     71             fail("Thread interrupted unexpectedly: " + e.getMessage());
     72         }
     73     }
     74 
     75     /**
     76      * Wait for up to {@param timeInSeconds}, if all the strings {@param logcatStrings} are found in
     77      * order then the assertion fails, otherwise it succeeds.
     78      *
     79      * @throws AssertionError if all the strings are found in order in the given time.
     80      * @throws IOException if error while reading.
     81      */
     82     public void assertLogcatDoesNotContainInOrder(int timeInSeconds, String... logcatStrings)
     83             throws IOException {
     84         try {
     85             int stringsFound = numberOfLogcatStringsFound("", timeInSeconds, logcatStrings);
     86             if (stringsFound == logcatStrings.length) {
     87                 fail("Found " + Joiner.on(", ").join(logcatStrings) + " that weren't expected");
     88             }
     89         } catch (InterruptedException e) {
     90             fail("Thread interrupted unexpectedly: " + e.getMessage());
     91         }
     92     }
     93 
     94     private int numberOfLogcatStringsFound(
     95             String filterSpec, int timeInSeconds, String... logcatStrings)
     96             throws InterruptedException, IOException {
     97         long timeout = System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(timeInSeconds);
     98         int stringIndex = 0;
     99         while (timeout >= System.currentTimeMillis()) {
    100             InputStream logcatStream = executeShellCommand("logcat -v brief -d " + filterSpec);
    101             BufferedReader logcat = new BufferedReader(new InputStreamReader(logcatStream));
    102             String line;
    103             stringIndex = 0;
    104             while ((line = logcat.readLine()) != null) {
    105                 if (line.contains(logcatStrings[stringIndex])) {
    106                     stringIndex++;
    107                     if (stringIndex >= logcatStrings.length) {
    108                         drainAndClose(logcat);
    109                         return stringIndex;
    110                     }
    111                 }
    112             }
    113             Closeables.closeQuietly(logcat);
    114             // In case the key has not been found, wait for the log to update before
    115             // performing the next search.
    116             Thread.sleep(SMALL_LOGCAT_DELAY);
    117         }
    118         return stringIndex;
    119     }
    120 
    121     private static void drainAndClose(BufferedReader reader) {
    122         try {
    123             while (reader.read() >= 0) {
    124                 // do nothing.
    125             }
    126         } catch (IOException ignored) {
    127         }
    128         Closeables.closeQuietly(reader);
    129     }
    130 }
    131