1 /*
2  * Copyright (C) 2016 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.verifierusbcompanion;
18 
19 import android.content.Context;
20 import android.os.Handler;
21 import android.os.Message;
22 import androidx.annotation.NonNull;
23 import android.util.Log;
24 
25 import java.util.Arrays;
26 
27 /**
28  * Framework for all companion tests of this app.
29  */
30 abstract class TestCompanion extends Thread {
31     private final @NonNull Context mContext;
32     private final @NonNull TestHandler mHandler;
33     private boolean mShouldAbort;
34 
35     /**
36      * Create a new test companion
37      *
38      * @param context Context to be used for the test companion
39      * @param observer Observer observing the test companion
40      */
TestCompanion(@onNull Context context, @NonNull TestObserver observer)41     TestCompanion(@NonNull Context context, @NonNull TestObserver observer) {
42         mContext = context;
43         mHandler = new TestHandler(observer);
44     }
45 
46     /**
47      * @return the context to be used by the test companion
48      */
getContext()49     protected @NonNull Context getContext() {
50         return mContext;
51     }
52 
requestAbort()53     void requestAbort() {
54         mShouldAbort = true;
55         interrupt();
56     }
57 
58     /**
59      * @return if the test companion should abort
60      */
shouldAbort()61     protected boolean shouldAbort() {
62         return mShouldAbort;
63     }
64 
65     /**
66      * Indicate that the test companion succeeded.
67      */
success()68     protected void success() {
69         mHandler.obtainMessage(TestHandler.SUCCESS).sendToTarget();
70     }
71 
72     /**
73      * Indicate that the test companion failed.
74      *
75      * @param error Description why the test failed
76      */
fail(@onNull CharSequence error)77     protected void fail(@NonNull CharSequence error) {
78         mHandler.obtainMessage(TestHandler.FAIL, error).sendToTarget();
79     }
80 
81     /**
82      * Indicate that the test companion was aborted.
83      */
abort()84     private void abort() {
85         mHandler.obtainMessage(TestHandler.ABORT).sendToTarget();
86     }
87 
88     /**
89      * Update the status of the test companion.
90      *
91      * @param status The new status message
92      */
updateStatus(@onNull CharSequence status)93     protected void updateStatus(@NonNull CharSequence status) {
94         Log.i(this.getClass().getSimpleName(), "Status: " + status);
95 
96         mHandler.obtainMessage(TestHandler.UPDATE_STATUS, status).sendToTarget();
97     }
98 
99     @Override
run()100     public final void run() {
101         try {
102             runTest();
103         } catch (Throwable e) {
104             if (e instanceof InterruptedException && shouldAbort()) {
105                 abort();
106             } else {
107                 fail(e + "\n" + Arrays.toString(e.getStackTrace()));
108             }
109 
110             return;
111         }
112 
113         success();
114     }
115 
116     /**
117      * The test companion code.
118      *
119      * @throws Throwable  If this returns without an exception, the test companion succeeded.
120      */
runTest()121     protected abstract void runTest() throws Throwable;
122 
123     /**
124      * Observe the state of this test companion
125      */
126     public interface TestObserver {
onStatusUpdate(@onNull CharSequence status)127         void onStatusUpdate(@NonNull CharSequence status);
onSuccess()128         void onSuccess();
onFail(@onNull CharSequence error)129         void onFail(@NonNull CharSequence error);
onAbort()130         void onAbort();
131     }
132 
133     /**
134      * Serialize callbacks to main thread.
135      */
136     public static class TestHandler extends Handler {
137         static final int SUCCESS = 0;
138         static final int FAIL = 1;
139         static final int ABORT = 2;
140         static final int UPDATE_STATUS = 3;
141 
142         private final @NonNull TestObserver mObserver;
143 
TestHandler(@onNull TestObserver observer)144         TestHandler(@NonNull TestObserver observer) {
145             mObserver = observer;
146         }
147 
148         @Override
handleMessage(Message msg)149         public void handleMessage(Message msg) {
150             switch (msg.what) {
151                 case SUCCESS:
152                     mObserver.onSuccess();
153                     break;
154                 case FAIL:
155                     mObserver.onFail((CharSequence)msg.obj);
156                     break;
157                 case ABORT:
158                     mObserver.onAbort();
159                     break;
160                 case UPDATE_STATUS:
161                     mObserver.onStatusUpdate((CharSequence)msg.obj);
162                     break;
163             }
164         }
165     }
166 }
167