1 /*
2  * Copyright (C) 2020 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 package com.android.server.timezonedetector.location;
17 
18 import static org.junit.Assert.assertEquals;
19 import static org.junit.Assert.assertFalse;
20 import static org.junit.Assert.assertTrue;
21 
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 
25 import java.time.Duration;
26 import java.util.ArrayList;
27 import java.util.Comparator;
28 import java.util.Objects;
29 import java.util.concurrent.Callable;
30 
31 /**
32  * A ThreadingDomain that simulates idealized post() semantics. Execution takes place in zero time,
33  * exactly when scheduled, when the test code explicitly requests it. Execution takes place on the
34  * test's main thread.
35  */
36 class TestThreadingDomain extends ThreadingDomain {
37 
38     static class QueuedRunnable {
39 
40         static final Comparator<? super QueuedRunnable> COMPARATOR =
41                 (o1, o2) -> (int) (o1.executionTimeMillis - o2.executionTimeMillis);
42 
43         @NonNull public final Runnable runnable;
44         @Nullable public final Object token;
45         public final long executionTimeMillis;
46 
QueuedRunnable(@onNull Runnable runnable, @Nullable Object token, long executionTimeMillis)47         QueuedRunnable(@NonNull Runnable runnable, @Nullable Object token,
48                 long executionTimeMillis) {
49             this.runnable = Objects.requireNonNull(runnable);
50             this.token = token;
51             this.executionTimeMillis = executionTimeMillis;
52         }
53 
54         @Override
toString()55         public String toString() {
56             return "QueuedRunnable{"
57                     + "runnable=" + runnable
58                     + ", token=" + token
59                     + ", executionTimeMillis=" + executionTimeMillis
60                     + '}';
61         }
62     }
63 
64     private long mCurrentTimeMillis;
65     private ArrayList<QueuedRunnable> mQueue = new ArrayList<>();
66 
TestThreadingDomain()67     TestThreadingDomain() {
68         // Pick an arbitrary time.
69         mCurrentTimeMillis = 123456L;
70     }
71 
72     @Override
getThread()73     Thread getThread() {
74         return Thread.currentThread();
75     }
76 
77     @Override
post(Runnable r)78     void post(Runnable r) {
79         postDelayed(r, null, 0);
80     }
81 
82     @Override
postAndWait(Callable<V> callable, long durationMillis)83     <V> V postAndWait(Callable<V> callable, long durationMillis) {
84         throw new UnsupportedOperationException("Not implemented");
85     }
86 
87     @Override
postDelayed(Runnable r, long delayMillis)88     void postDelayed(Runnable r, long delayMillis) {
89         postDelayed(r, null, delayMillis);
90     }
91 
92     @Override
postDelayed(Runnable r, Object token, long delayMillis)93     void postDelayed(Runnable r, Object token, long delayMillis) {
94         mQueue.add(new QueuedRunnable(r, token, mCurrentTimeMillis + delayMillis));
95         mQueue.sort(QueuedRunnable.COMPARATOR);
96     }
97 
98     @Override
removeQueuedRunnables(Object token)99     void removeQueuedRunnables(Object token) {
100         mQueue.removeIf(runnable -> runnable.token != null && runnable.token == token);
101     }
102 
removeAllQueuedRunnables()103     void removeAllQueuedRunnables() {
104         mQueue.clear();
105     }
106 
assertSingleDelayedQueueItem(Duration expectedDelay)107     void assertSingleDelayedQueueItem(Duration expectedDelay) {
108         assertQueueLength(1);
109         assertNextQueueItemIsDelayed(expectedDelay);
110     }
111 
assertSingleImmediateQueueItem()112     void assertSingleImmediateQueueItem() {
113         assertQueueLength(1);
114         assertNextQueueItemIsImmediate();
115     }
116 
assertQueueLength(int expectedLength)117     void assertQueueLength(int expectedLength) {
118         assertEquals(expectedLength, mQueue.size());
119     }
120 
assertNextQueueItemIsImmediate()121     void assertNextQueueItemIsImmediate() {
122         assertTrue(getNextQueueItemDelayMillis() == 0);
123     }
124 
assertNextQueueItemIsDelayed(Duration expectedDelay)125     private void assertNextQueueItemIsDelayed(Duration expectedDelay) {
126         assertEquals(getNextQueueItemDelayMillis(), expectedDelay.toMillis());
127     }
128 
assertQueueEmpty()129     void assertQueueEmpty() {
130         assertTrue(mQueue.isEmpty());
131     }
132 
getNextQueueItemDelayMillis()133     long getNextQueueItemDelayMillis() {
134         assertFalse(mQueue.isEmpty());
135         return mQueue.get(0).executionTimeMillis - mCurrentTimeMillis;
136     }
137 
executeNext()138     void executeNext() {
139         assertFalse(mQueue.isEmpty());
140         QueuedRunnable queued = mQueue.remove(0);
141 
142         mCurrentTimeMillis = queued.executionTimeMillis;
143         queued.runnable.run();
144     }
145 }
146