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