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;
18 
19 import static org.mockito.Mockito.spy;
20 
21 import android.os.Handler;
22 import android.os.Looper;
23 import android.util.Log;
24 
25 import com.android.internal.telephony.PhoneConfigurationManager;
26 
27 import org.junit.After;
28 import org.junit.Before;
29 import org.junit.Rule;
30 import org.mockito.junit.MockitoJUnit;
31 import org.mockito.junit.MockitoRule;
32 
33 import java.lang.reflect.Field;
34 import java.util.HashMap;
35 import java.util.Iterator;
36 import java.util.LinkedList;
37 import java.util.concurrent.CountDownLatch;
38 import java.util.concurrent.Executor;
39 import java.util.concurrent.TimeUnit;
40 
41 /**
42  * Helper class to load Mockito Resources into a test.
43  */
44 public class TelephonyTestBase {
45     @Rule public final MockitoRule mocks = MockitoJUnit.rule();
46 
47     protected TestContext mContext;
48 
49     private final HashMap<InstanceKey, Object> mOldInstances = new HashMap<>();
50     private final LinkedList<InstanceKey> mInstanceKeys = new LinkedList<>();
51 
52     @Before
setUp()53     public void setUp() throws Exception {
54         mContext = spy(new TestContext());
55         // Set up the looper if it does not exist on the test thread.
56         if (Looper.myLooper() == null) {
57             Looper.prepare();
58             // Wait until the looper is not null anymore
59             for(int i = 0; i < 5; i++) {
60                 if (Looper.myLooper() != null) {
61                     break;
62                 }
63                 Looper.prepare();
64                 Thread.sleep(100);
65             }
66         }
67     }
68 
69     @After
tearDown()70     public void tearDown() throws Exception {
71         // Ensure there are no static references to handlers after test completes.
72         PhoneConfigurationManager.unregisterAllMultiSimConfigChangeRegistrants();
73         restoreInstances();
74     }
75 
waitForExecutorAction(Executor executor, long timeoutMillis)76     protected final boolean waitForExecutorAction(Executor executor, long timeoutMillis) {
77         final CountDownLatch lock = new CountDownLatch(1);
78         executor.execute(() -> {
79             lock.countDown();
80         });
81         while (lock.getCount() > 0) {
82             try {
83                 return lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
84             } catch (InterruptedException e) {
85                 // do nothing
86             }
87         }
88         return true;
89     }
90 
waitForHandlerAction(Handler h, long timeoutMillis)91     protected final void waitForHandlerAction(Handler h, long timeoutMillis) {
92         final CountDownLatch lock = new CountDownLatch(1);
93         h.post(lock::countDown);
94         while (lock.getCount() > 0) {
95             try {
96                 lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
97             } catch (InterruptedException e) {
98                 // do nothing
99             }
100         }
101     }
102 
waitForHandlerActionDelayed(Handler h, long timeoutMillis, long delayMs)103     protected final void waitForHandlerActionDelayed(Handler h, long timeoutMillis, long delayMs) {
104         final CountDownLatch lock = new CountDownLatch(1);
105         h.postDelayed(lock::countDown, delayMs);
106         while (lock.getCount() > 0) {
107             try {
108                 lock.await(timeoutMillis, TimeUnit.MILLISECONDS);
109             } catch (InterruptedException e) {
110                 // do nothing
111             }
112         }
113     }
114 
waitForMs(long ms)115     protected void waitForMs(long ms) {
116         try {
117             Thread.sleep(ms);
118         } catch (InterruptedException e) {
119             Log.e("TelephonyTestBase", "InterruptedException while waiting: " + e);
120         }
121     }
122 
replaceInstance(final Class c, final String instanceName, final Object obj, final Object newValue)123     protected synchronized void replaceInstance(final Class c, final String instanceName,
124             final Object obj, final Object newValue)
125             throws Exception {
126         Field field = c.getDeclaredField(instanceName);
127         field.setAccessible(true);
128 
129         InstanceKey key = new InstanceKey(c, instanceName, obj);
130         if (!mOldInstances.containsKey(key)) {
131             mOldInstances.put(key, field.get(obj));
132             mInstanceKeys.add(key);
133         }
134         field.set(obj, newValue);
135     }
136 
restoreInstances()137     private synchronized void restoreInstances() throws Exception {
138         Iterator<InstanceKey> it = mInstanceKeys.descendingIterator();
139 
140         while (it.hasNext()) {
141             InstanceKey key = it.next();
142             Field field = key.mClass.getDeclaredField(key.mInstName);
143             field.setAccessible(true);
144             field.set(key.mObj, mOldInstances.get(key));
145         }
146 
147         mInstanceKeys.clear();
148         mOldInstances.clear();
149     }
150 
151     private static class InstanceKey {
152         public final Class mClass;
153         public final String mInstName;
154         public final Object mObj;
InstanceKey(final Class c, final String instName, final Object obj)155         InstanceKey(final Class c, final String instName, final Object obj) {
156             mClass = c;
157             mInstName = instName;
158             mObj = obj;
159         }
160 
161         @Override
hashCode()162         public int hashCode() {
163             return (mClass.getName().hashCode() * 31 + mInstName.hashCode()) * 31;
164         }
165 
166         @Override
equals(Object obj)167         public boolean equals(Object obj) {
168             if (obj == null || obj.getClass() != getClass()) {
169                 return false;
170             }
171 
172             InstanceKey other = (InstanceKey) obj;
173             return (other.mClass == mClass && other.mInstName.equals(mInstName)
174                     && other.mObj == mObj);
175         }
176     }
177 
getTestContext()178     protected TestContext getTestContext() {
179         return mContext;
180     }
181 }
182