• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.verifier;
18 
19 import static com.android.cts.verifier.ReportExporter.LOGS_DIRECTORY;
20 import static com.android.cts.verifier.TestListActivity.sCurrentDisplayMode;
21 import static com.android.cts.verifier.TestListActivity.sInitialLaunch;
22 
23 import android.content.ContentResolver;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.database.ContentObserver;
27 import android.database.Cursor;
28 import android.os.AsyncTask;
29 import android.os.Environment;
30 import android.os.Handler;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.widget.BaseAdapter;
35 import android.widget.ListView;
36 import android.widget.TextView;
37 
38 import com.android.compatibility.common.util.ReportLog;
39 import com.android.compatibility.common.util.TestScreenshotsMetadata;
40 import com.android.cts.verifier.TestListActivity.DisplayMode;
41 
42 import java.io.ByteArrayInputStream;
43 import java.io.File;
44 import java.io.IOException;
45 import java.io.ObjectInputStream;
46 import java.util.ArrayList;
47 import java.util.HashMap;
48 import java.util.List;
49 import java.util.Map;
50 import java.util.concurrent.atomic.AtomicBoolean;
51 
52 /**
53  * {@link BaseAdapter} that handles loading, refreshing, and setting test
54  * results. What tests are shown can be customized by overriding
55  * {@link #getRows()}. See {@link ArrayTestListAdapter} and
56  * {@link ManifestTestListAdapter} for examples.
57  */
58 public abstract class TestListAdapter extends BaseAdapter {
59 
60     /** Activities implementing {@link Intent#ACTION_MAIN} and this will appear in the list. */
61     public static final String CATEGORY_MANUAL_TEST = "android.cts.intent.category.MANUAL_TEST";
62 
63     /** View type for a category of tests like "Sensors" or "Features" */
64     private static final int CATEGORY_HEADER_VIEW_TYPE = 0;
65 
66     /** View type for an actual test like the Accelerometer test. */
67     private static final int TEST_VIEW_TYPE = 1;
68 
69     /** Padding around the text views and icons. */
70     private static final int PADDING = 10;
71 
72     private final Context mContext;
73 
74     /** Immutable data of tests like the test's title and launch intent. */
75     private final List<TestListItem> mRows = new ArrayList<TestListItem>();
76 
77     /** Mutable test results that will change as each test activity finishes. */
78     private final Map<String, Integer> mTestResults = new HashMap<String, Integer>();
79 
80     /** Map from test name to test details. */
81     private final Map<String, String> mTestDetails = new HashMap<String, String>();
82 
83     /** Map from test name to {@link ReportLog}. */
84     private final Map<String, ReportLog> mReportLogs = new HashMap<String, ReportLog>();
85 
86     /** Map from test name to {@link TestResultHistoryCollection}. */
87     private final Map<String, TestResultHistoryCollection> mHistories = new HashMap<>();
88 
89     /** Map from test name to {@link TestScreenshotsMetadata}. */
90     private final Map<String, TestScreenshotsMetadata> mScreenshotsMetadata = new HashMap<>();
91 
92     /** Flag to identify whether the mHistories has been loaded. */
93     private final AtomicBoolean mHasLoadedResultHistory = new AtomicBoolean(false);
94 
95     private final LayoutInflater mLayoutInflater;
96 
97     /** Map from display mode to the list of {@link TestListItem}.
98      *  Records the TestListItem from main view only, including unfolded mode and folded mode
99      *  respectively. */
100     protected Map<String, List<TestListItem>> mDisplayModesTests = new HashMap<>();
101 
102     /** {@link ListView} row that is either a test category header or a test. */
103     public static class TestListItem {
104 
105         /** Title shown in the {@link ListView}. */
106         final String title;
107 
108         /** Test name with class and test ID to uniquely identify the test. Null for categories. */
109         String testName;
110 
111         /** Intent used to launch the activity from the list. Null for categories. */
112         final Intent intent;
113 
114         /** Features necessary to run this test. */
115         final String[] requiredFeatures;
116 
117         /** Configs necessary to run this test. */
118         final String[] requiredConfigs;
119 
120         /** Intent actions necessary to run this test. */
121         final String[] requiredActions;
122 
123         /** Features such that, if any present, the test gets excluded from being shown. */
124         final String[] excludedFeatures;
125 
126         /** If any of of the features are present the test is meaningful to run. */
127         final String[] applicableFeatures;
128 
129         /** Configs display mode to run this test. */
130         final String displayMode;
131 
132         // TODO: refactor to use a Builder approach instead
133 
newTest(Context context, int titleResId, String testName, Intent intent, String[] requiredFeatures, String[] excludedFeatures, String[] applicableFeatures)134         public static TestListItem newTest(Context context, int titleResId, String testName,
135             Intent intent, String[] requiredFeatures, String[] excludedFeatures,
136             String[] applicableFeatures) {
137             return newTest(context.getString(titleResId), testName, intent, requiredFeatures,
138                 excludedFeatures, applicableFeatures);
139         }
140 
newTest(Context context, int titleResId, String testName, Intent intent, String[] requiredFeatures, String[] excludedFeatures)141         public static TestListItem newTest(Context context, int titleResId, String testName,
142                 Intent intent, String[] requiredFeatures, String[] excludedFeatures) {
143             return newTest(context.getString(titleResId), testName, intent, requiredFeatures,
144                     excludedFeatures, /* applicableFeatures= */ null);
145         }
146 
newTest(Context context, int titleResId, String testName, Intent intent, String[] requiredFeatures)147         public static TestListItem newTest(Context context, int titleResId, String testName,
148                 Intent intent, String[] requiredFeatures) {
149             return newTest(context.getString(titleResId), testName, intent, requiredFeatures,
150                     /* excludedFeatures= */ null, /* applicableFeatures= */ null);
151         }
152 
newTest(String title, String testName, Intent intent, String[] requiredFeatures, String[] requiredConfigs, String[] requiredActions, String[] excludedFeatures, String[] applicableFeatures, String displayMode)153         public static TestListItem newTest(String title, String testName, Intent intent,
154                 String[] requiredFeatures, String[] requiredConfigs, String[] requiredActions,
155                 String[] excludedFeatures, String[] applicableFeatures, String displayMode) {
156             return new TestListItem(title, testName, intent, requiredFeatures, requiredConfigs,
157                     requiredActions, excludedFeatures, applicableFeatures, displayMode);
158         }
159 
newTest(String title, String testName, Intent intent, String[] requiredFeatures, String[] requiredConfigs, String[] excludedFeatures, String[] applicableFeatures)160         public static TestListItem newTest(String title, String testName, Intent intent,
161             String[] requiredFeatures, String[] requiredConfigs, String[] excludedFeatures,
162             String[] applicableFeatures) {
163             return new TestListItem(title, testName, intent, requiredFeatures, requiredConfigs,
164                     /* requiredActions = */ null, excludedFeatures, applicableFeatures,
165                     /* displayMode= */ null);
166         }
167 
newTest(String title, String testName, Intent intent, String[] requiredFeatures, String[] excludedFeatures, String[] applicableFeatures)168         public static TestListItem newTest(String title, String testName, Intent intent,
169                 String[] requiredFeatures, String[] excludedFeatures, String[] applicableFeatures) {
170             return new TestListItem(title, testName, intent, requiredFeatures,
171                     /* requiredConfigs= */ null, /* requiredActions = */ null, excludedFeatures,
172                     applicableFeatures, /* displayMode= */ null);
173         }
174 
newTest(String title, String testName, Intent intent, String[] requiredFeatures, String[] excludedFeatures)175         public static TestListItem newTest(String title, String testName, Intent intent,
176                 String[] requiredFeatures, String[] excludedFeatures) {
177             return new TestListItem(title, testName, intent, requiredFeatures,
178                     /* requiredConfigs= */ null, /* requiredActions = */ null, excludedFeatures,
179                     /* applicableFeatures= */ null, /* displayMode= */ null);
180         }
181 
newTest(String title, String testName, Intent intent, String[] requiredFeatures)182         public static TestListItem newTest(String title, String testName, Intent intent,
183                 String[] requiredFeatures) {
184             return new TestListItem(title, testName, intent, requiredFeatures,
185                     /* requiredConfigs= */ null, /* requiredActions = */ null,
186                     /* excludedFeatures= */ null, /* applicableFeatures= */ null,
187                     /* displayMode= */ null);
188         }
189 
newCategory(Context context, int titleResId)190         public static TestListItem newCategory(Context context, int titleResId) {
191             return newCategory(context.getString(titleResId));
192         }
193 
newCategory(String title)194         public static TestListItem newCategory(String title) {
195             return new TestListItem(title, /* testName= */ null, /* intent= */ null,
196                     /* requiredFeatures= */ null,  /* requiredConfigs= */ null,
197                     /* requiredActions = */ null, /* excludedFeatures= */ null,
198                     /* applicableFeatures= */ null, /* displayMode= */ null);
199         }
200 
TestListItem(String title, String testName, Intent intent, String[] requiredFeatures, String[] excludedFeatures, String[] applicableFeatures)201         protected TestListItem(String title, String testName, Intent intent,
202                 String[] requiredFeatures, String[] excludedFeatures, String[] applicableFeatures) {
203             this(title, testName, intent, requiredFeatures, /* requiredConfigs= */ null,
204                     /* requiredActions = */ null, excludedFeatures, applicableFeatures,
205                     /* displayMode= */ null);
206         }
207 
TestListItem(String title, String testName, Intent intent, String[] requiredFeatures, String[] requiredConfigs, String[] requiredActions, String[] excludedFeatures, String[] applicableFeatures, String displayMode)208         protected TestListItem(String title, String testName, Intent intent,
209                 String[] requiredFeatures, String[] requiredConfigs, String[] requiredActions,
210                 String[] excludedFeatures, String[] applicableFeatures, String displayMode) {
211             this.title = title;
212             if (!sInitialLaunch) {
213                 testName = setTestNameSuffix(sCurrentDisplayMode, testName);
214             }
215             this.testName = testName;
216             this.intent = intent;
217             this.requiredActions = requiredActions;
218             this.requiredFeatures = requiredFeatures;
219             this.requiredConfigs = requiredConfigs;
220             this.excludedFeatures = excludedFeatures;
221             this.applicableFeatures = applicableFeatures;
222             this.displayMode = displayMode;
223         }
224 
isTest()225         boolean isTest() {
226             return intent != null;
227         }
228     }
229 
TestListAdapter(Context context)230     public TestListAdapter(Context context) {
231         this.mContext = context;
232         this.mLayoutInflater =
233                 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
234 
235         TestResultContentObserver observer = new TestResultContentObserver();
236         ContentResolver resolver = context.getContentResolver();
237         resolver.registerContentObserver(TestResultsProvider.getResultContentUri(context), true, observer);
238     }
239 
loadTestResults()240     public void loadTestResults() {
241         new RefreshTestResultsTask(false).execute();
242     }
243 
clearTestResults()244     public void clearTestResults() {
245         new ClearTestResultsTask().execute();
246     }
247 
setTestResult(TestResult testResult)248     public void setTestResult(TestResult testResult) {
249         String name = testResult.getName();
250 
251         // Append existing history
252         TestResultHistoryCollection histories = testResult.getHistoryCollection();
253         histories.merge(null, mHistories.get(name));
254 
255         new SetTestResultTask(name, testResult.getResult(),
256                 testResult.getDetails(), testResult.getReportLog(), histories,
257                 mScreenshotsMetadata.get(name)).execute();
258     }
259 
260     class RefreshTestResultsTask extends AsyncTask<Void, Void, RefreshResult> {
261 
262         private boolean mIsFromMainView;
263 
RefreshTestResultsTask(boolean isFromMainView)264         RefreshTestResultsTask(boolean isFromMainView) {
265             mIsFromMainView = isFromMainView;
266         }
267 
268         @Override
doInBackground(Void... params)269         protected RefreshResult doInBackground(Void... params) {
270             List<TestListItem> rows = getRows();
271             // When initial launch, needs to fetch tests in the unfolded/folded mode
272             // to be stored in mDisplayModesTests as the basis for the future switch.
273             if (sInitialLaunch) {
274                 sInitialLaunch = false;
275             }
276 
277             if (mIsFromMainView) {
278                 rows = mDisplayModesTests.get(sCurrentDisplayMode);
279             }
280 
281             return getRefreshResults(rows);
282         }
283 
284         @Override
onPostExecute(RefreshResult result)285         protected void onPostExecute(RefreshResult result) {
286             super.onPostExecute(result);
287             mRows.clear();
288             mRows.addAll(result.mItems);
289             mTestResults.clear();
290             mTestResults.putAll(result.mResults);
291             mTestDetails.clear();
292             mTestDetails.putAll(result.mDetails);
293             mReportLogs.clear();
294             mReportLogs.putAll(result.mReportLogs);
295             mHistories.clear();
296             mHistories.putAll(result.mHistories);
297             mScreenshotsMetadata.clear();
298             mScreenshotsMetadata.putAll(result.mScreenshotsMetadata);
299             mHasLoadedResultHistory.set(true);
300             notifyDataSetChanged();
301         }
302     }
303 
304     static class RefreshResult {
305         List<TestListItem> mItems;
306         Map<String, Integer> mResults;
307         Map<String, String> mDetails;
308         Map<String, ReportLog> mReportLogs;
309         Map<String, TestResultHistoryCollection> mHistories;
310         Map<String, TestScreenshotsMetadata> mScreenshotsMetadata;
311 
RefreshResult( List<TestListItem> items, Map<String, Integer> results, Map<String, String> details, Map<String, ReportLog> reportLogs, Map<String, TestResultHistoryCollection> histories, Map<String, TestScreenshotsMetadata> screenshotsMetadata)312         RefreshResult(
313                 List<TestListItem> items,
314                 Map<String, Integer> results,
315                 Map<String, String> details,
316                 Map<String, ReportLog> reportLogs,
317                 Map<String, TestResultHistoryCollection> histories,
318                 Map<String, TestScreenshotsMetadata> screenshotsMetadata) {
319             mItems = items;
320             mResults = results;
321             mDetails = details;
322             mReportLogs = reportLogs;
323             mHistories = histories;
324             mScreenshotsMetadata = screenshotsMetadata;
325         }
326     }
327 
getRows()328     protected abstract List<TestListItem> getRows();
329 
330     static final String[] REFRESH_PROJECTION = {
331         TestResultsProvider._ID,
332         TestResultsProvider.COLUMN_TEST_NAME,
333         TestResultsProvider.COLUMN_TEST_RESULT,
334         TestResultsProvider.COLUMN_TEST_DETAILS,
335         TestResultsProvider.COLUMN_TEST_METRICS,
336         TestResultsProvider.COLUMN_TEST_RESULT_HISTORY,
337         TestResultsProvider.COLUMN_TEST_SCREENSHOTS_METADATA,
338     };
339 
getRefreshResults(List<TestListItem> items)340     RefreshResult getRefreshResults(List<TestListItem> items) {
341         Map<String, Integer> results = new HashMap<String, Integer>();
342         Map<String, String> details = new HashMap<String, String>();
343         Map<String, ReportLog> reportLogs = new HashMap<String, ReportLog>();
344         Map<String, TestResultHistoryCollection> histories = new HashMap<>();
345         Map<String, TestScreenshotsMetadata> screenshotsMetadata = new HashMap<>();
346         ContentResolver resolver = mContext.getContentResolver();
347         Cursor cursor = null;
348         try {
349             cursor = resolver.query(TestResultsProvider.getResultContentUri(mContext), REFRESH_PROJECTION,
350                     null, null, null);
351             if (cursor.moveToFirst()) {
352                 do {
353                     String testName = cursor.getString(1);
354                     int testResult = cursor.getInt(2);
355                     String testDetails = cursor.getString(3);
356                     ReportLog reportLog = (ReportLog) deserialize(cursor.getBlob(4));
357                     TestResultHistoryCollection historyCollection =
358                         (TestResultHistoryCollection) deserialize(cursor.getBlob(5));
359                     TestScreenshotsMetadata screenshots =
360                             (TestScreenshotsMetadata) deserialize(cursor.getBlob(6));
361                     results.put(testName, testResult);
362                     details.put(testName, testDetails);
363                     reportLogs.put(testName, reportLog);
364                     histories.put(testName, historyCollection);
365                     screenshotsMetadata.put(testName, screenshots);
366                 } while (cursor.moveToNext());
367             }
368         } finally {
369             if (cursor != null) {
370                 cursor.close();
371             }
372         }
373         return new RefreshResult(
374                 items, results, details, reportLogs, histories, screenshotsMetadata);
375     }
376 
377     class ClearTestResultsTask extends AsyncTask<Void, Void, Void> {
378 
deleteDirectory(File file)379         private void deleteDirectory(File file) {
380             for (File subfile : file.listFiles()) {
381                 if (subfile.isDirectory()) {
382                     deleteDirectory(subfile);
383                 }
384                 subfile.delete();
385             }
386         }
387 
388         @Override
doInBackground(Void... params)389         protected Void doInBackground(Void... params) {
390             ContentResolver resolver = mContext.getContentResolver();
391             resolver.delete(TestResultsProvider.getResultContentUri(mContext), "1", null);
392 
393             // Apart from deleting metadata from content resolver database, need to delete
394             // files generated in LOGS_DIRECTORY. For example screenshots.
395             File resFolder = new File(
396                     Environment.getExternalStorageDirectory().getAbsolutePath()
397                             + File.separator + LOGS_DIRECTORY);
398             deleteDirectory(resFolder);
399 
400             return null;
401         }
402     }
403 
404     class SetTestResultTask extends AsyncTask<Void, Void, Void> {
405 
406         private final String mTestName;
407         private final int mResult;
408         private final String mDetails;
409         private final ReportLog mReportLog;
410         private final TestResultHistoryCollection mHistoryCollection;
411         private final TestScreenshotsMetadata mScreenshotsMetadata;
412 
SetTestResultTask( String testName, int result, String details, ReportLog reportLog, TestResultHistoryCollection historyCollection, TestScreenshotsMetadata screenshotsMetadata)413         SetTestResultTask(
414                 String testName,
415                 int result,
416                 String details,
417                 ReportLog reportLog,
418                 TestResultHistoryCollection historyCollection,
419                 TestScreenshotsMetadata screenshotsMetadata) {
420             mTestName = testName;
421             mResult = result;
422             mDetails = details;
423             mReportLog = reportLog;
424             mHistoryCollection = historyCollection;
425             mScreenshotsMetadata = screenshotsMetadata;
426         }
427 
428         @Override
doInBackground(Void... params)429         protected Void doInBackground(Void... params) {
430             if (mHasLoadedResultHistory.get()) {
431                 mHistoryCollection.merge(null, mHistories.get(mTestName));
432             } else {
433                 // Loads history from ContentProvider directly if it has not been loaded yet.
434                 ContentResolver resolver = mContext.getContentResolver();
435 
436                 try (Cursor cursor = resolver.query(
437                         TestResultsProvider.getTestNameUri(mContext, mTestName),
438                         new String[] {TestResultsProvider.COLUMN_TEST_RESULT_HISTORY},
439                         null,
440                         null,
441                         null)) {
442                     if (cursor.moveToFirst()) {
443                         do {
444                             TestResultHistoryCollection historyCollection =
445                                     (TestResultHistoryCollection) deserialize(cursor.getBlob(0));
446                             mHistoryCollection.merge(null, historyCollection);
447                         } while (cursor.moveToNext());
448                     }
449                 }
450             }
451             TestResultsProvider.setTestResult(
452                     mContext, mTestName, mResult, mDetails, mReportLog, mHistoryCollection,
453                     mScreenshotsMetadata);
454             return null;
455         }
456     }
457 
458     class TestResultContentObserver extends ContentObserver {
459 
TestResultContentObserver()460         public TestResultContentObserver() {
461             super(new Handler());
462         }
463 
464         @Override
onChange(boolean selfChange)465         public void onChange(boolean selfChange) {
466             super.onChange(selfChange);
467             loadTestResults();
468         }
469     }
470 
471     @Override
areAllItemsEnabled()472     public boolean areAllItemsEnabled() {
473         // Section headers for test categories are not clickable.
474         return false;
475     }
476 
477     @Override
isEnabled(int position)478     public boolean isEnabled(int position) {
479         if (getItem(position) == null) {
480             return false;
481         }
482         return getItem(position).isTest();
483     }
484 
485     @Override
getItemViewType(int position)486     public int getItemViewType(int position) {
487         return getItem(position).isTest() ? TEST_VIEW_TYPE : CATEGORY_HEADER_VIEW_TYPE;
488     }
489 
490     @Override
getViewTypeCount()491     public int getViewTypeCount() {
492         return 2;
493     }
494 
495     @Override
getCount()496     public int getCount() {
497         return mRows.size();
498     }
499 
500     @Override
getItem(int position)501     public TestListItem getItem(int position) {
502         return mRows.get(position);
503     }
504 
505     @Override
getItemId(int position)506     public long getItemId(int position) {
507         return position;
508     }
509 
getTestResult(int position)510     public int getTestResult(int position) {
511         TestListItem item = getItem(position);
512         return mTestResults.containsKey(item.testName)
513                 ? mTestResults.get(item.testName)
514                 : TestResult.TEST_RESULT_NOT_EXECUTED;
515     }
516 
getTestDetails(int position)517     public String getTestDetails(int position) {
518         TestListItem item = getItem(position);
519         return mTestDetails.containsKey(item.testName)
520                 ? mTestDetails.get(item.testName)
521                 : null;
522     }
523 
getReportLog(int position)524     public ReportLog getReportLog(int position) {
525         TestListItem item = getItem(position);
526         return mReportLogs.containsKey(item.testName)
527                 ? mReportLogs.get(item.testName)
528                 : null;
529     }
530 
531     /**
532      * Get test result histories.
533      *
534      * @param position The position of test.
535      * @return A {@link TestResultHistoryCollection} object containing test result histories of tests.
536      */
getHistoryCollection(int position)537     public TestResultHistoryCollection getHistoryCollection(int position) {
538         TestListItem item = getItem(position);
539         if (item == null) {
540             return null;
541         }
542         return mHistories.containsKey(item.testName) ? mHistories.get(item.testName) : null;
543     }
544 
545     /**
546      * Get test screenshots metadata
547      *
548      * @param position The position of test
549      * @return A {@link TestScreenshotsMetadata} object containing test screenshots metadata.
550      */
getScreenshotsMetadata(String mode, int position)551     public TestScreenshotsMetadata getScreenshotsMetadata(String mode, int position) {
552         TestListItem item = getItem(mode, position);
553         return mScreenshotsMetadata.containsKey(item.testName)
554                 ? mScreenshotsMetadata.get(item.testName)
555                 : null;
556     }
557 
558     /**
559      * Get test item by the given display mode and position.
560      *
561      * @param mode The display mode.
562      * @param position The position of test.
563      * @return A {@link TestListItem} object containing the test item.
564      */
getItem(String mode, int position)565     public TestListItem getItem(String mode, int position) {
566         return mDisplayModesTests.get(mode).get(position);
567     }
568 
569     /**
570      * Get test item count by the given display mode.
571      *
572      * @param mode The display mode.
573      * @return A count of test items.
574      */
getCount(String mode)575     public int getCount(String mode){
576         return mDisplayModesTests.getOrDefault(mode, new ArrayList<>()).size();
577     }
578 
579     /**
580      * Get test result by the given display mode and position.
581      *
582      * @param mode The display mode.
583      * @param position The position of test.
584      * @return The test item result.
585      */
getTestResult(String mode, int position)586     public int getTestResult(String mode, int position) {
587         TestListItem item = mDisplayModesTests.get(mode).get(position);
588         return mTestResults.containsKey(item.testName)
589             ? mTestResults.get(item.testName)
590             : TestResult.TEST_RESULT_NOT_EXECUTED;
591     }
592 
593     /**
594      * Get test details by the given display mode and position.
595      *
596      * @param mode The display mode.
597      * @param position The position of test.
598      * @return A string containing the test details.
599      */
getTestDetails(String mode, int position)600     public String getTestDetails(String mode, int position) {
601         TestListItem item = mDisplayModesTests.get(mode).get(position);
602         return mTestDetails.containsKey(item.testName)
603             ? mTestDetails.get(item.testName)
604             : null;
605     }
606 
607     /**
608      * Get test report log by the given display mode and position.
609      *
610      * @param mode The display mode.
611      * @param position The position of test.
612      * @return A {@link ReportLog} object containing the test report log of the test item.
613      */
getReportLog(String mode, int position)614     public ReportLog getReportLog(String mode, int position) {
615         TestListItem item = mDisplayModesTests.get(mode).get(position);
616         return mReportLogs.containsKey(item.testName)
617             ? mReportLogs.get(item.testName)
618             : null;
619     }
620 
621     /**
622      * Get test result histories by the given display mode and position.
623      *
624      * @param mode The display mode.
625      * @param position The position of test.
626      * @return A {@link TestResultHistoryCollection} object containing the test result histories of
627      *         the test item.
628      */
getHistoryCollection(String mode, int position)629     public TestResultHistoryCollection getHistoryCollection(String mode, int position) {
630         TestListItem item = mDisplayModesTests.get(mode).get(position);
631         return mHistories.containsKey(item.testName)
632             ? mHistories.get(item.testName)
633             : null;
634     }
635 
allTestsPassed()636     public boolean allTestsPassed() {
637         for (TestListItem item : mRows) {
638             if (item != null && item.isTest()
639                     && (!mTestResults.containsKey(item.testName)
640                             || (mTestResults.get(item.testName)
641                                     != TestResult.TEST_RESULT_PASSED))) {
642                 return false;
643             }
644         }
645         return true;
646     }
647 
648     @Override
getView(int position, View convertView, ViewGroup parent)649     public View getView(int position, View convertView, ViewGroup parent) {
650         TextView textView;
651         if (convertView == null) {
652             int layout = getLayout(position);
653             textView = (TextView) mLayoutInflater.inflate(layout, parent, false);
654         } else {
655             textView = (TextView) convertView;
656         }
657 
658         TestListItem item = getItem(position);
659         textView.setText(item.title);
660         textView.setPadding(PADDING, 0, PADDING, 0);
661         textView.setCompoundDrawablePadding(PADDING);
662 
663         if (item.isTest()) {
664             int testResult = getTestResult(position);
665             int backgroundResource = 0;
666             int iconResource = 0;
667 
668             /** TODO: Remove fs_ prefix from feature icons since they are used here too. */
669             switch (testResult) {
670                 case TestResult.TEST_RESULT_PASSED:
671                     backgroundResource = R.drawable.test_pass_gradient;
672                     iconResource = R.drawable.fs_good;
673                     break;
674 
675                 case TestResult.TEST_RESULT_FAILED:
676                     backgroundResource = R.drawable.test_fail_gradient;
677                     iconResource = R.drawable.fs_error;
678                     break;
679 
680                 case TestResult.TEST_RESULT_NOT_EXECUTED:
681                     break;
682 
683                 default:
684                     throw new IllegalArgumentException("Unknown test result: " + testResult);
685             }
686 
687             textView.setBackgroundResource(backgroundResource);
688             textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, iconResource, 0);
689         }
690 
691         return textView;
692     }
693 
getLayout(int position)694     private int getLayout(int position) {
695         int viewType = getItemViewType(position);
696         switch (viewType) {
697             case CATEGORY_HEADER_VIEW_TYPE:
698                 return R.layout.test_category_row;
699             case TEST_VIEW_TYPE:
700                 return android.R.layout.simple_list_item_1;
701             default:
702                 throw new IllegalArgumentException("Illegal view type: " + viewType);
703 
704         }
705     }
706 
deserialize(byte[] bytes)707     public static Object deserialize(byte[] bytes) {
708         if (bytes == null || bytes.length == 0) {
709             return null;
710         }
711         ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
712         ObjectInputStream objectInput = null;
713         try {
714             objectInput = new ObjectInputStream(byteStream);
715             return objectInput.readObject();
716         } catch (IOException e) {
717             return null;
718         } catch (ClassNotFoundException e) {
719             return null;
720         } finally {
721             try {
722                 if (objectInput != null) {
723                     objectInput.close();
724                 }
725                 byteStream.close();
726             } catch (IOException e) {
727                 // Ignore close exception.
728             }
729         }
730     }
731 
732     /**
733      * Sets test name suffix. In the folded mode, the suffix is [folded]; otherwise, it is empty
734      * string.
735      *
736      * @param mode A string of current display mode.
737      * @param name A string of test name.
738      * @return A string of test name with suffix, [folded], in the folded mode.
739      *         A string of input test name in the unfolded mode.
740      */
setTestNameSuffix(String mode, String name)741     public static String setTestNameSuffix(String mode, String name) {
742         if (name != null && mode.equals(DisplayMode.FOLDED.toString())
743             && !name.endsWith(DisplayMode.FOLDED.asSuffix())){
744             return name + DisplayMode.FOLDED.asSuffix();
745         }
746         return name;
747     }
748 }
749