1 package com.mobileer.oboetester; 2 3 import android.content.Context; 4 import android.content.Intent; 5 import android.os.Build; 6 import android.util.AttributeSet; 7 import android.util.Log; 8 import android.view.LayoutInflater; 9 import android.view.View; 10 import android.view.WindowManager; 11 import android.widget.Button; 12 import android.widget.LinearLayout; 13 import android.widget.ScrollView; 14 import android.widget.TextView; 15 16 import java.text.DateFormat; 17 import java.text.SimpleDateFormat; 18 import java.util.Calendar; 19 import java.util.Date; 20 21 /** 22 * Run an automated test from a UI, gather logs, 23 * and display a summary. 24 */ 25 public class AutomatedTestRunner extends LinearLayout implements Runnable { 26 27 private Button mStartButton; 28 private Button mStopButton; 29 private Button mShareButton; 30 private TextView mAutoTextView; 31 private ScrollView mAutoTextScroller; 32 private TextView mSingleTestIndex; 33 private StringBuffer mFailedSummary; 34 private StringBuffer mSummary; 35 private StringBuffer mFullSummary; 36 private int mTestCount; 37 private int mPassCount; 38 private int mFailCount; 39 private TestAudioActivity mActivity; 40 41 private Thread mAutoThread; 42 private volatile boolean mThreadEnabled; 43 private CachedTextViewLog mCachedTextView; 44 AutomatedTestRunner(Context context)45 public AutomatedTestRunner(Context context) { 46 super(context); 47 initializeViews(context); 48 } 49 AutomatedTestRunner(Context context, AttributeSet attrs)50 public AutomatedTestRunner(Context context, AttributeSet attrs) { 51 super(context, attrs); 52 initializeViews(context); 53 } 54 AutomatedTestRunner(Context context, AttributeSet attrs, int defStyle)55 public AutomatedTestRunner(Context context, 56 AttributeSet attrs, 57 int defStyle) { 58 super(context, attrs, defStyle); 59 initializeViews(context); 60 } 61 getActivity()62 public TestAudioActivity getActivity() { 63 return mActivity; 64 } 65 setActivity(TestAudioActivity activity)66 public void setActivity(TestAudioActivity activity) { 67 this.mActivity = activity; 68 mCachedTextView = new CachedTextViewLog(activity, mAutoTextView); 69 } 70 71 /** 72 * Inflates the views in the layout. 73 * 74 * @param context 75 * the current context for the view. 76 */ initializeViews(Context context)77 private void initializeViews(Context context) { 78 LayoutInflater inflater = (LayoutInflater) context 79 .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 80 inflater.inflate(R.layout.auto_test_runner, this); 81 82 mStartButton = (Button) findViewById(R.id.button_start); 83 mStartButton.setOnClickListener( new OnClickListener() { 84 @Override 85 public void onClick(View v) { 86 startTest(); 87 } 88 }); 89 90 mStopButton = (Button) findViewById(R.id.button_stop); 91 mStopButton.setOnClickListener( new OnClickListener() { 92 @Override 93 public void onClick(View v) { 94 stopTest(); 95 } 96 }); 97 98 mShareButton = (Button) findViewById(R.id.button_share); 99 mShareButton.setOnClickListener( new OnClickListener() { 100 @Override 101 public void onClick(View v) { 102 shareResult(); 103 mShareButton.setEnabled(true); 104 } 105 }); 106 mShareButton.setEnabled(false); 107 108 mSingleTestIndex = (TextView) findViewById(R.id.single_test_index); 109 110 mAutoTextScroller = (ScrollView) findViewById(R.id.text_log_auto_scroller); 111 mAutoTextView = (TextView) findViewById(R.id.text_log_auto); 112 113 mFailedSummary = new StringBuffer(); 114 mSummary = new StringBuffer(); 115 mFullSummary = new StringBuffer(); 116 } 117 updateStartStopButtons(boolean running)118 private void updateStartStopButtons(boolean running) { 119 mStartButton.setEnabled(!running); 120 mStopButton.setEnabled(running); 121 } 122 getTestCount()123 public int getTestCount() { 124 return mTestCount; 125 } 126 isThreadEnabled()127 public boolean isThreadEnabled() { 128 return mThreadEnabled; 129 } 130 appendFailedSummary(String text)131 public void appendFailedSummary(String text) { 132 mFailedSummary.append(text); 133 } 134 appendSummary(String text)135 public void appendSummary(String text) { 136 mSummary.append(text); 137 } 138 incrementFailCount()139 public void incrementFailCount() { 140 mFailCount++; 141 } incrementPassCount()142 public void incrementPassCount() { 143 mPassCount++; 144 } incrementTestCount()145 public void incrementTestCount() { 146 mTestCount++; 147 } 148 149 // Write to scrollable TextView log(final String text)150 public void log(final String text) { 151 if (text == null) return; 152 Log.d(TestAudioActivity.TAG, "LOG - " + text); 153 mCachedTextView.append(text + "\n"); 154 mFullSummary.append(text + "\n"); 155 scrollToBottom(); 156 } 157 scrollToBottom()158 public void scrollToBottom() { 159 mAutoTextScroller.fullScroll(View.FOCUS_DOWN); 160 } 161 162 // Flush any logs that are stuck in the cache. flushLog()163 public void flushLog() { 164 mCachedTextView.flush(); 165 scrollToBottom(); 166 } 167 logClear()168 private void logClear() { 169 mCachedTextView.clear(); 170 mFullSummary.delete(0, mFullSummary.length()); 171 mSummary.delete(0, mSummary.length()); 172 mFailedSummary.delete(0, mFailedSummary.length()); 173 } 174 getFullLogs()175 protected String getFullLogs() { 176 return mFullSummary.toString(); 177 } 178 startAutoThread()179 private void startAutoThread() { 180 mThreadEnabled = true; 181 mAutoThread = new Thread(this); 182 mAutoThread.start(); 183 } 184 stopAutoThread()185 private void stopAutoThread() { 186 try { 187 if (mAutoThread != null) { 188 Log.d(TestAudioActivity.TAG, 189 "Who called stopAutoThread()?", 190 new RuntimeException("Just for debugging.")); 191 mThreadEnabled = false; 192 mAutoThread.interrupt(); 193 mAutoThread.join(100); 194 mAutoThread = null; 195 } 196 } catch (InterruptedException e) { 197 e.printStackTrace(); 198 } 199 } 200 setTestIndexText(int newTestIndex)201 protected void setTestIndexText(int newTestIndex) { 202 if (newTestIndex >= 0) { 203 mSingleTestIndex.setText(String.valueOf(newTestIndex)); 204 } else { 205 mSingleTestIndex.setText(""); 206 } 207 } 208 updateTestIndex()209 private void updateTestIndex() { 210 CharSequence chars = mSingleTestIndex.getText(); 211 String text = chars.toString(); 212 int testIndex = -1; 213 String trimmed = chars.toString().trim(); 214 if (trimmed.length() > 0) { 215 try { 216 testIndex = Integer.parseInt(text); 217 } catch (NumberFormatException e) { 218 mActivity.showErrorToast("Badly formated callback size: " + text); 219 mSingleTestIndex.setText(""); 220 } 221 } 222 mActivity.setSingleTestIndex(testIndex); 223 } 224 startTest()225 protected void startTest() { 226 updateTestIndex(); 227 updateStartStopButtons(true); 228 startAutoThread(); 229 } 230 stopTest()231 public void stopTest() { 232 stopAutoThread(); 233 } 234 235 // Only call from UI thread. onTestStarted()236 public void onTestStarted() { 237 mActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 238 } 239 240 // Only call from UI thread. onTestFinished()241 public void onTestFinished() { 242 mActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 243 updateStartStopButtons(false); 244 mShareButton.setEnabled(true); 245 } 246 getTimestampString()247 public static String getTimestampString() { 248 DateFormat df = new SimpleDateFormat("yyyyMMdd-HHmmss"); 249 Date now = Calendar.getInstance().getTime(); 250 return df.format(now); 251 } 252 253 // Share text from log via GMail, Drive or other method. shareResult()254 public void shareResult() { 255 Intent sharingIntent = new Intent(android.content.Intent.ACTION_SEND); 256 sharingIntent.setType("text/plain"); 257 258 String subjectText = "OboeTester-" + mActivity.getTestName() 259 + "-" + Build.MANUFACTURER 260 + "-" + Build.MODEL 261 + "-" + getTimestampString(); 262 subjectText = subjectText.replace(' ', '-'); 263 sharingIntent.putExtra(android.content.Intent.EXTRA_SUBJECT, subjectText); 264 265 String shareBody = mAutoTextView.getText().toString(); 266 sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, shareBody); 267 268 mActivity.startActivity(Intent.createChooser(sharingIntent, "Share using:")); 269 } 270 271 @Override run()272 public void run() { 273 mActivity.runOnUiThread(new Runnable() { 274 @Override 275 public void run() { 276 onTestStarted(); 277 } 278 }); 279 logClear(); 280 log("=== STARTED at " + new Date()); 281 log(mActivity.getTestName()); 282 log(MainActivity.getVersionText()); 283 log(Build.MANUFACTURER + ", " + Build.MODEL + ", " + Build.PRODUCT); 284 log(Build.DISPLAY); 285 appendFailedSummary("Summary\n"); 286 mTestCount = 0; 287 mPassCount = 0; 288 mFailCount = 0; 289 try { 290 mActivity.runTest(); 291 log("Tests finished."); 292 } catch(Exception e) { 293 log("EXCEPTION: " + e.getMessage()); 294 } finally { 295 mActivity.stopTest(); 296 if (!mThreadEnabled) { 297 log("== TEST STOPPED =="); 298 } 299 log("\n==== SUMMARY ========"); 300 log(mSummary.toString()); 301 if (mFailCount > 0) { 302 log("These tests FAILED:"); 303 log(mFailedSummary.toString()); 304 log("------------"); 305 } else if (mPassCount > 0) { 306 log("All " + mPassCount + " tests PASSED."); 307 } else { 308 log("No tests were run!"); 309 } 310 int skipped = mTestCount - (mPassCount + mFailCount); 311 log(mPassCount + " passed. " 312 + mFailCount + " failed. " 313 + skipped + " skipped. "); 314 log("== FINISHED at " + new Date()); 315 316 flushLog(); 317 318 mActivity.saveIntentLog(); 319 320 mActivity.runOnUiThread(new Runnable() { 321 @Override 322 public void run() { 323 onTestFinished(); 324 flushLog(); 325 } 326 }); 327 } 328 } 329 330 } 331