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