Home | History | Annotate | Download | only in verifier
      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 com.android.compatibility.common.util.ReportLog;
     20 
     21 import android.content.ContentResolver;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.database.ContentObserver;
     25 import android.database.Cursor;
     26 import android.os.AsyncTask;
     27 import android.os.Handler;
     28 import android.view.LayoutInflater;
     29 import android.view.View;
     30 import android.view.ViewGroup;
     31 import android.widget.BaseAdapter;
     32 import android.widget.ListView;
     33 import android.widget.TextView;
     34 
     35 import java.io.ByteArrayInputStream;
     36 import java.io.IOException;
     37 import java.io.ObjectInputStream;
     38 import java.util.ArrayList;
     39 import java.util.HashMap;
     40 import java.util.List;
     41 import java.util.Map;
     42 
     43 /**
     44  * {@link BaseAdapter} that handles loading, refreshing, and setting test
     45  * results. What tests are shown can be customized by overriding
     46  * {@link #getRows()}. See {@link ArrayTestListAdapter} and
     47  * {@link ManifestTestListAdapter} for examples.
     48  */
     49 public abstract class TestListAdapter extends BaseAdapter {
     50 
     51     /** Activities implementing {@link Intent#ACTION_MAIN} and this will appear in the list. */
     52     public static final String CATEGORY_MANUAL_TEST = "android.cts.intent.category.MANUAL_TEST";
     53 
     54     /** View type for a category of tests like "Sensors" or "Features" */
     55     private static final int CATEGORY_HEADER_VIEW_TYPE = 0;
     56 
     57     /** View type for an actual test like the Accelerometer test. */
     58     private static final int TEST_VIEW_TYPE = 1;
     59 
     60     /** Padding around the text views and icons. */
     61     private static final int PADDING = 10;
     62 
     63     private final Context mContext;
     64 
     65     /** Immutable data of tests like the test's title and launch intent. */
     66     private final List<TestListItem> mRows = new ArrayList<TestListItem>();
     67 
     68     /** Mutable test results that will change as each test activity finishes. */
     69     private final Map<String, Integer> mTestResults = new HashMap<String, Integer>();
     70 
     71     /** Map from test name to test details. */
     72     private final Map<String, String> mTestDetails = new HashMap<String, String>();
     73 
     74     /** Map from test name to {@link ReportLog}. */
     75     private final Map<String, ReportLog> mReportLogs = new HashMap<String, ReportLog>();
     76 
     77     private final LayoutInflater mLayoutInflater;
     78 
     79     /** {@link ListView} row that is either a test category header or a test. */
     80     public static class TestListItem {
     81 
     82         /** Title shown in the {@link ListView}. */
     83         final String title;
     84 
     85         /** Test name with class and test ID to uniquely identify the test. Null for categories. */
     86         final String testName;
     87 
     88         /** Intent used to launch the activity from the list. Null for categories. */
     89         final Intent intent;
     90 
     91         /** Features necessary to run this test. */
     92         final String[] requiredFeatures;
     93 
     94         /** Features such that, if any present, the test gets excluded from being shown. */
     95         final String[] excludedFeatures;
     96 
     97         /** If any of of the features are present the test is meaningful to run. */
     98         final String[] applicableFeatures;
     99 
    100         public static TestListItem newTest(Context context, int titleResId, String testName,
    101                 Intent intent, String[] requiredFeatures, String[] excludedFeatures,
    102                 String[] applicableFeatures) {
    103             return newTest(context.getString(titleResId), testName, intent, requiredFeatures,
    104                     excludedFeatures, applicableFeatures);
    105         }
    106 
    107         public static TestListItem newTest(Context context, int titleResId, String testName,
    108                 Intent intent, String[] requiredFeatures, String[] excludedFeatures) {
    109             return newTest(context.getString(titleResId), testName, intent, requiredFeatures,
    110                     excludedFeatures, null);
    111         }
    112 
    113         public static TestListItem newTest(Context context, int titleResId, String testName,
    114                 Intent intent, String[] requiredFeatures) {
    115             return newTest(context.getString(titleResId), testName, intent, requiredFeatures, null,
    116                     null);
    117         }
    118 
    119         public static TestListItem newTest(String title, String testName, Intent intent,
    120                 String[] requiredFeatures, String[] excludedFeatures, String[] applicableFeatures) {
    121             return new TestListItem(title, testName, intent, requiredFeatures, excludedFeatures,
    122                     applicableFeatures);
    123         }
    124 
    125         public static TestListItem newTest(String title, String testName, Intent intent,
    126                 String[] requiredFeatures, String[] excludedFeatures) {
    127             return new TestListItem(title, testName, intent, requiredFeatures, excludedFeatures,
    128                     null);
    129         }
    130 
    131         public static TestListItem newTest(String title, String testName, Intent intent,
    132                 String[] requiredFeatures) {
    133             return new TestListItem(title, testName, intent, requiredFeatures, null, null);
    134         }
    135 
    136         public static TestListItem newCategory(Context context, int titleResId) {
    137             return newCategory(context.getString(titleResId));
    138         }
    139 
    140         public static TestListItem newCategory(String title) {
    141             return new TestListItem(title, null, null, null, null, null);
    142         }
    143 
    144         protected TestListItem(String title, String testName, Intent intent,
    145                 String[] requiredFeatures, String[] excludedFeatures, String[] applicableFeatures) {
    146             this.title = title;
    147             this.testName = testName;
    148             this.intent = intent;
    149             this.requiredFeatures = requiredFeatures;
    150             this.excludedFeatures = excludedFeatures;
    151             this.applicableFeatures = applicableFeatures;
    152         }
    153 
    154         boolean isTest() {
    155             return intent != null;
    156         }
    157     }
    158 
    159     public TestListAdapter(Context context) {
    160         this.mContext = context;
    161         this.mLayoutInflater =
    162                 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    163 
    164         TestResultContentObserver observer = new TestResultContentObserver();
    165         ContentResolver resolver = context.getContentResolver();
    166         resolver.registerContentObserver(TestResultsProvider.getResultContentUri(context), true, observer);
    167     }
    168 
    169     public void loadTestResults() {
    170         new RefreshTestResultsTask().execute();
    171     }
    172 
    173     public void clearTestResults() {
    174         new ClearTestResultsTask().execute();
    175     }
    176 
    177     public void setTestResult(TestResult testResult) {
    178         new SetTestResultTask(testResult.getName(), testResult.getResult(),
    179                 testResult.getDetails(), testResult.getReportLog()).execute();
    180     }
    181 
    182     class RefreshTestResultsTask extends AsyncTask<Void, Void, RefreshResult> {
    183         @Override
    184         protected RefreshResult doInBackground(Void... params) {
    185             List<TestListItem> rows = getRows();
    186             return getRefreshResults(rows);
    187         }
    188 
    189         @Override
    190         protected void onPostExecute(RefreshResult result) {
    191             super.onPostExecute(result);
    192             mRows.clear();
    193             mRows.addAll(result.mItems);
    194             mTestResults.clear();
    195             mTestResults.putAll(result.mResults);
    196             mTestDetails.clear();
    197             mTestDetails.putAll(result.mDetails);
    198             mReportLogs.clear();
    199             mReportLogs.putAll(result.mReportLogs);
    200             notifyDataSetChanged();
    201         }
    202     }
    203 
    204     static class RefreshResult {
    205         List<TestListItem> mItems;
    206         Map<String, Integer> mResults;
    207         Map<String, String> mDetails;
    208         Map<String, ReportLog> mReportLogs;
    209 
    210         RefreshResult(
    211                 List<TestListItem> items,
    212                 Map<String, Integer> results,
    213                 Map<String, String> details,
    214                 Map<String, ReportLog> reportLogs) {
    215             mItems = items;
    216             mResults = results;
    217             mDetails = details;
    218             mReportLogs = reportLogs;
    219         }
    220     }
    221 
    222     protected abstract List<TestListItem> getRows();
    223 
    224     static final String[] REFRESH_PROJECTION = {
    225         TestResultsProvider._ID,
    226         TestResultsProvider.COLUMN_TEST_NAME,
    227         TestResultsProvider.COLUMN_TEST_RESULT,
    228         TestResultsProvider.COLUMN_TEST_DETAILS,
    229         TestResultsProvider.COLUMN_TEST_METRICS,
    230     };
    231 
    232     RefreshResult getRefreshResults(List<TestListItem> items) {
    233         Map<String, Integer> results = new HashMap<String, Integer>();
    234         Map<String, String> details = new HashMap<String, String>();
    235         Map<String, ReportLog> reportLogs = new HashMap<String, ReportLog>();
    236         ContentResolver resolver = mContext.getContentResolver();
    237         Cursor cursor = null;
    238         try {
    239             cursor = resolver.query(TestResultsProvider.getResultContentUri(mContext), REFRESH_PROJECTION,
    240                     null, null, null);
    241             if (cursor.moveToFirst()) {
    242                 do {
    243                     String testName = cursor.getString(1);
    244                     int testResult = cursor.getInt(2);
    245                     String testDetails = cursor.getString(3);
    246                     ReportLog reportLog = (ReportLog) deserialize(cursor.getBlob(4));
    247                     results.put(testName, testResult);
    248                     details.put(testName, testDetails);
    249                     reportLogs.put(testName, reportLog);
    250                 } while (cursor.moveToNext());
    251             }
    252         } finally {
    253             if (cursor != null) {
    254                 cursor.close();
    255             }
    256         }
    257         return new RefreshResult(items, results, details, reportLogs);
    258     }
    259 
    260     class ClearTestResultsTask extends AsyncTask<Void, Void, Void> {
    261 
    262         @Override
    263         protected Void doInBackground(Void... params) {
    264             ContentResolver resolver = mContext.getContentResolver();
    265             resolver.delete(TestResultsProvider.getResultContentUri(mContext), "1", null);
    266             return null;
    267         }
    268     }
    269 
    270     class SetTestResultTask extends AsyncTask<Void, Void, Void> {
    271 
    272         private final String mTestName;
    273 
    274         private final int mResult;
    275 
    276         private final String mDetails;
    277 
    278         private final ReportLog mReportLog;
    279 
    280         SetTestResultTask(
    281                 String testName,
    282                 int result,
    283                 String details,
    284                 ReportLog reportLog) {
    285             mTestName = testName;
    286             mResult = result;
    287             mDetails = details;
    288             mReportLog = reportLog;
    289         }
    290 
    291         @Override
    292         protected Void doInBackground(Void... params) {
    293             TestResultsProvider.setTestResult(mContext, mTestName, mResult, mDetails, mReportLog);
    294             return null;
    295         }
    296     }
    297 
    298     class TestResultContentObserver extends ContentObserver {
    299 
    300         public TestResultContentObserver() {
    301             super(new Handler());
    302         }
    303 
    304         @Override
    305         public void onChange(boolean selfChange) {
    306             super.onChange(selfChange);
    307             loadTestResults();
    308         }
    309     }
    310 
    311     @Override
    312     public boolean areAllItemsEnabled() {
    313         // Section headers for test categories are not clickable.
    314         return false;
    315     }
    316 
    317     @Override
    318     public boolean isEnabled(int position) {
    319         return getItem(position).isTest();
    320     }
    321 
    322     @Override
    323     public int getItemViewType(int position) {
    324         return getItem(position).isTest() ? TEST_VIEW_TYPE : CATEGORY_HEADER_VIEW_TYPE;
    325     }
    326 
    327     @Override
    328     public int getViewTypeCount() {
    329         return 2;
    330     }
    331 
    332     @Override
    333     public int getCount() {
    334         return mRows.size();
    335     }
    336 
    337     @Override
    338     public TestListItem getItem(int position) {
    339         return mRows.get(position);
    340     }
    341 
    342     @Override
    343     public long getItemId(int position) {
    344         return position;
    345     }
    346 
    347     public int getTestResult(int position) {
    348         TestListItem item = getItem(position);
    349         return mTestResults.containsKey(item.testName)
    350                 ? mTestResults.get(item.testName)
    351                 : TestResult.TEST_RESULT_NOT_EXECUTED;
    352     }
    353 
    354     public String getTestDetails(int position) {
    355         TestListItem item = getItem(position);
    356         return mTestDetails.containsKey(item.testName)
    357                 ? mTestDetails.get(item.testName)
    358                 : null;
    359     }
    360 
    361     public ReportLog getReportLog(int position) {
    362         TestListItem item = getItem(position);
    363         return mReportLogs.containsKey(item.testName)
    364                 ? mReportLogs.get(item.testName)
    365                 : null;
    366     }
    367 
    368     public boolean allTestsPassed() {
    369         for (TestListItem item : mRows) {
    370             if (item.isTest() && (!mTestResults.containsKey(item.testName)
    371                     || (mTestResults.get(item.testName) != TestResult.TEST_RESULT_PASSED))) {
    372                 return false;
    373             }
    374         }
    375         return true;
    376     }
    377 
    378     @Override
    379     public View getView(int position, View convertView, ViewGroup parent) {
    380         TextView textView;
    381         if (convertView == null) {
    382             int layout = getLayout(position);
    383             textView = (TextView) mLayoutInflater.inflate(layout, parent, false);
    384         } else {
    385             textView = (TextView) convertView;
    386         }
    387 
    388         TestListItem item = getItem(position);
    389         textView.setText(item.title);
    390         textView.setPadding(PADDING, 0, PADDING, 0);
    391         textView.setCompoundDrawablePadding(PADDING);
    392 
    393         if (item.isTest()) {
    394             int testResult = getTestResult(position);
    395             int backgroundResource = 0;
    396             int iconResource = 0;
    397 
    398             /** TODO: Remove fs_ prefix from feature icons since they are used here too. */
    399             switch (testResult) {
    400                 case TestResult.TEST_RESULT_PASSED:
    401                     backgroundResource = R.drawable.test_pass_gradient;
    402                     iconResource = R.drawable.fs_good;
    403                     break;
    404 
    405                 case TestResult.TEST_RESULT_FAILED:
    406                     backgroundResource = R.drawable.test_fail_gradient;
    407                     iconResource = R.drawable.fs_error;
    408                     break;
    409 
    410                 case TestResult.TEST_RESULT_NOT_EXECUTED:
    411                     break;
    412 
    413                 default:
    414                     throw new IllegalArgumentException("Unknown test result: " + testResult);
    415             }
    416 
    417             textView.setBackgroundResource(backgroundResource);
    418             textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, iconResource, 0);
    419         }
    420 
    421         return textView;
    422     }
    423 
    424     private int getLayout(int position) {
    425         int viewType = getItemViewType(position);
    426         switch (viewType) {
    427             case CATEGORY_HEADER_VIEW_TYPE:
    428                 return R.layout.test_category_row;
    429             case TEST_VIEW_TYPE:
    430                 return android.R.layout.simple_list_item_1;
    431             default:
    432                 throw new IllegalArgumentException("Illegal view type: " + viewType);
    433 
    434         }
    435     }
    436 
    437     private static Object deserialize(byte[] bytes) {
    438         if (bytes == null || bytes.length == 0) {
    439             return null;
    440         }
    441         ByteArrayInputStream byteStream = new ByteArrayInputStream(bytes);
    442         ObjectInputStream objectInput = null;
    443         try {
    444             objectInput = new ObjectInputStream(byteStream);
    445             return objectInput.readObject();
    446         } catch (IOException e) {
    447             return null;
    448         } catch (ClassNotFoundException e) {
    449             return null;
    450         } finally {
    451             try {
    452                 if (objectInput != null) {
    453                     objectInput.close();
    454                 }
    455                 byteStream.close();
    456             } catch (IOException e) {
    457                 // Ignore close exception.
    458             }
    459         }
    460     }
    461 }
    462