1 /*
2  * Copyright (C) 2022 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.server.wm;
18 
19 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
20 
21 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT;
22 import static com.android.server.wm.LetterboxConfiguration.LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP;
23 
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.content.Context;
27 import android.platform.test.annotations.Presubmit;
28 import android.util.AtomicFile;
29 
30 import androidx.test.filters.SmallTest;
31 
32 import com.android.internal.R;
33 import com.android.internal.annotations.VisibleForTesting;
34 
35 import junit.framework.Assert;
36 
37 import org.junit.After;
38 import org.junit.Before;
39 import org.junit.Test;
40 
41 import java.io.File;
42 import java.util.concurrent.atomic.AtomicInteger;
43 import java.util.function.Consumer;
44 import java.util.function.Supplier;
45 
46 /**
47  * Tests for the {@link LetterboxConfigurationPersister} class.
48  *
49  * Build/Install/Run:
50  *  atest WmTests:LetterboxConfigurationPersisterTest
51  */
52 @SmallTest
53 @Presubmit
54 public class LetterboxConfigurationPersisterTest {
55 
56     private static final long TIMEOUT = 2000L; // 2 secs
57 
58     private static final int DEFAULT_REACHABILITY_TEST = -1;
59     private static final Supplier<Integer> DEFAULT_REACHABILITY_SUPPLIER_TEST =
60             () -> DEFAULT_REACHABILITY_TEST;
61 
62     private static final String LETTERBOX_CONFIGURATION_TEST_FILENAME = "letterbox_config_test";
63 
64     private LetterboxConfigurationPersister mLetterboxConfigurationPersister;
65     private Context mContext;
66     private PersisterQueue mPersisterQueue;
67     private QueueState mQueueState;
68     private PersisterQueue.Listener mQueueListener;
69     private File mConfigFolder;
70 
71     @Before
setUp()72     public void setUp() throws Exception {
73         mContext = getInstrumentation().getTargetContext();
74         mConfigFolder = mContext.getFilesDir();
75         mPersisterQueue = new PersisterQueue();
76         mQueueState = new QueueState();
77         mLetterboxConfigurationPersister = new LetterboxConfigurationPersister(
78                 () -> mContext.getResources().getInteger(
79                         R.integer.config_letterboxDefaultPositionForHorizontalReachability),
80                 () -> mContext.getResources().getInteger(
81                         R.integer.config_letterboxDefaultPositionForVerticalReachability),
82                 () -> mContext.getResources().getInteger(
83                         R.integer.config_letterboxDefaultPositionForBookModeReachability),
84                 () -> mContext.getResources().getInteger(
85                         R.integer.config_letterboxDefaultPositionForTabletopModeReachability
86                 ),
87                 mConfigFolder, mPersisterQueue, mQueueState,
88                 LETTERBOX_CONFIGURATION_TEST_FILENAME);
89         mQueueListener = queueEmpty -> mQueueState.onItemAdded();
90         mPersisterQueue.addListener(mQueueListener);
91         mLetterboxConfigurationPersister.start();
92     }
93 
94     @After
tearDown()95     public void tearDown() throws InterruptedException {
96         deleteConfiguration(mLetterboxConfigurationPersister, mPersisterQueue);
97         waitForCompletion(mPersisterQueue);
98         mPersisterQueue.removeListener(mQueueListener);
99         stopPersisterSafe(mPersisterQueue);
100     }
101 
102     @Test
test_whenStoreIsCreated_valuesAreDefaults()103     public void test_whenStoreIsCreated_valuesAreDefaults() {
104         final int positionForHorizontalReachability =
105                 mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(
106                         false);
107         final int defaultPositionForHorizontalReachability =
108                 mContext.getResources().getInteger(
109                         R.integer.config_letterboxDefaultPositionForHorizontalReachability);
110         Assert.assertEquals(defaultPositionForHorizontalReachability,
111                 positionForHorizontalReachability);
112         final int positionForVerticalReachability =
113                 mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(false);
114         final int defaultPositionForVerticalReachability =
115                 mContext.getResources().getInteger(
116                         R.integer.config_letterboxDefaultPositionForVerticalReachability);
117         Assert.assertEquals(defaultPositionForVerticalReachability,
118                 positionForVerticalReachability);
119     }
120 
121     @Test
test_whenUpdatedWithNewValues_valuesAreWritten()122     public void test_whenUpdatedWithNewValues_valuesAreWritten() {
123         mLetterboxConfigurationPersister.setLetterboxPositionForHorizontalReachability(false,
124                 LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
125         mLetterboxConfigurationPersister.setLetterboxPositionForVerticalReachability(false,
126                 LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
127         waitForCompletion(mPersisterQueue);
128         final int newPositionForHorizontalReachability =
129                 mLetterboxConfigurationPersister.getLetterboxPositionForHorizontalReachability(
130                         false);
131         final int newPositionForVerticalReachability =
132                 mLetterboxConfigurationPersister.getLetterboxPositionForVerticalReachability(false);
133         Assert.assertEquals(LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT,
134                 newPositionForHorizontalReachability);
135         Assert.assertEquals(LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP,
136                 newPositionForVerticalReachability);
137     }
138 
139     @Test
test_whenUpdatedWithNewValues_valuesAreReadAfterRestart()140     public void test_whenUpdatedWithNewValues_valuesAreReadAfterRestart() {
141         final PersisterQueue firstPersisterQueue = new PersisterQueue();
142         final LetterboxConfigurationPersister firstPersister = new LetterboxConfigurationPersister(
143                 DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
144                 DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
145                 mContext.getFilesDir(), firstPersisterQueue, mQueueState,
146                 LETTERBOX_CONFIGURATION_TEST_FILENAME);
147         firstPersister.start();
148         firstPersister.setLetterboxPositionForHorizontalReachability(false,
149                 LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
150         firstPersister.setLetterboxPositionForVerticalReachability(false,
151                 LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
152         waitForCompletion(firstPersisterQueue);
153         stopPersisterSafe(firstPersisterQueue);
154         final PersisterQueue secondPersisterQueue = new PersisterQueue();
155         final LetterboxConfigurationPersister secondPersister = new LetterboxConfigurationPersister(
156                 DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
157                 DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
158                 mContext.getFilesDir(), secondPersisterQueue, mQueueState,
159                 LETTERBOX_CONFIGURATION_TEST_FILENAME);
160         secondPersister.start();
161         final int newPositionForHorizontalReachability =
162                 secondPersister.getLetterboxPositionForHorizontalReachability(false);
163         final int newPositionForVerticalReachability =
164                 secondPersister.getLetterboxPositionForVerticalReachability(false);
165         Assert.assertEquals(LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT,
166                 newPositionForHorizontalReachability);
167         Assert.assertEquals(LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP,
168                 newPositionForVerticalReachability);
169         deleteConfiguration(secondPersister, secondPersisterQueue);
170         waitForCompletion(secondPersisterQueue);
171         stopPersisterSafe(secondPersisterQueue);
172     }
173 
174     @Test
test_whenUpdatedWithNewValuesAndDeleted_valuesAreDefaults()175     public void test_whenUpdatedWithNewValuesAndDeleted_valuesAreDefaults() {
176         final PersisterQueue firstPersisterQueue = new PersisterQueue();
177         final LetterboxConfigurationPersister firstPersister = new LetterboxConfigurationPersister(
178                 DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
179                 DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
180                 mContext.getFilesDir(), firstPersisterQueue, mQueueState,
181                 LETTERBOX_CONFIGURATION_TEST_FILENAME);
182         firstPersister.start();
183         firstPersister.setLetterboxPositionForHorizontalReachability(false,
184                 LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT);
185         firstPersister.setLetterboxPositionForVerticalReachability(false,
186                 LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP);
187         waitForCompletion(firstPersisterQueue);
188         final int newPositionForHorizontalReachability =
189                 firstPersister.getLetterboxPositionForHorizontalReachability(false);
190         final int newPositionForVerticalReachability =
191                 firstPersister.getLetterboxPositionForVerticalReachability(false);
192         Assert.assertEquals(LETTERBOX_HORIZONTAL_REACHABILITY_POSITION_LEFT,
193                 newPositionForHorizontalReachability);
194         Assert.assertEquals(LETTERBOX_VERTICAL_REACHABILITY_POSITION_TOP,
195                 newPositionForVerticalReachability);
196         deleteConfiguration(firstPersister, firstPersisterQueue);
197         waitForCompletion(firstPersisterQueue);
198         stopPersisterSafe(firstPersisterQueue);
199 
200         final PersisterQueue secondPersisterQueue = new PersisterQueue();
201         final LetterboxConfigurationPersister secondPersister = new LetterboxConfigurationPersister(
202                 DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
203                 DEFAULT_REACHABILITY_SUPPLIER_TEST, DEFAULT_REACHABILITY_SUPPLIER_TEST,
204                 mContext.getFilesDir(), secondPersisterQueue, mQueueState,
205                 LETTERBOX_CONFIGURATION_TEST_FILENAME);
206         secondPersister.start();
207         final int positionForHorizontalReachability =
208                 secondPersister.getLetterboxPositionForHorizontalReachability(false);
209         final int positionForVerticalReachability =
210                 secondPersister.getLetterboxPositionForVerticalReachability(false);
211         Assert.assertEquals(DEFAULT_REACHABILITY_TEST, positionForHorizontalReachability);
212         Assert.assertEquals(DEFAULT_REACHABILITY_TEST, positionForVerticalReachability);
213         deleteConfiguration(secondPersister, secondPersisterQueue);
214         waitForCompletion(secondPersisterQueue);
215         stopPersisterSafe(secondPersisterQueue);
216     }
217 
stopPersisterSafe(PersisterQueue persisterQueue)218     private void stopPersisterSafe(PersisterQueue persisterQueue) {
219         try {
220             persisterQueue.stopPersisting();
221         } catch (InterruptedException e) {
222             e.printStackTrace();
223         }
224     }
225 
waitForCompletion(PersisterQueue persisterQueue)226     private void waitForCompletion(PersisterQueue persisterQueue) {
227         final long endTime = System.currentTimeMillis() + TIMEOUT;
228         // The queue could be empty but the last item still processing and not completed. For this
229         // reason the completion happens when there are not more items to process and the last one
230         // has completed.
231         while (System.currentTimeMillis() < endTime && (!isQueueEmpty(persisterQueue)
232                 || !hasLastItemCompleted())) {
233             try {
234                 Thread.sleep(100);
235             } catch (InterruptedException ie) { /* Nope */}
236         }
237     }
238 
isQueueEmpty(PersisterQueue persisterQueue)239     private boolean isQueueEmpty(PersisterQueue persisterQueue) {
240         return persisterQueue.findLastItem(
241                 writeQueueItem -> true, PersisterQueue.WriteQueueItem.class) != null;
242     }
243 
hasLastItemCompleted()244     private boolean hasLastItemCompleted() {
245         return mQueueState.isEmpty();
246     }
247 
deleteConfiguration(LetterboxConfigurationPersister persister, PersisterQueue persisterQueue)248     private void deleteConfiguration(LetterboxConfigurationPersister persister,
249             PersisterQueue persisterQueue) {
250         final AtomicFile fileToDelete = new AtomicFile(
251                 new File(mConfigFolder, LETTERBOX_CONFIGURATION_TEST_FILENAME));
252         persisterQueue.addItem(
253                 new DeleteFileCommand(fileToDelete, mQueueState.andThen(
254                         s -> persister.useDefaultValue())), true);
255     }
256 
257     private static class DeleteFileCommand implements
258             PersisterQueue.WriteQueueItem<DeleteFileCommand> {
259 
260         @NonNull
261         private final AtomicFile mFileToDelete;
262         @Nullable
263         private final Consumer<String> mOnComplete;
264 
DeleteFileCommand(@onNull AtomicFile fileToDelete, Consumer<String> onComplete)265         DeleteFileCommand(@NonNull AtomicFile fileToDelete, Consumer<String> onComplete) {
266             mFileToDelete = fileToDelete;
267             mOnComplete = onComplete;
268         }
269 
270         @Override
process()271         public void process() {
272             mFileToDelete.delete();
273             if (mOnComplete != null) {
274                 mOnComplete.accept("DeleteFileCommand");
275             }
276         }
277     }
278 
279     // Contains the current length of the persister queue
280     private static class QueueState implements Consumer<String> {
281 
282         // The current number of commands in the queue
283         @VisibleForTesting
284         private final AtomicInteger mCounter = new AtomicInteger(0);
285 
286         @Override
accept(String s)287         public void accept(String s) {
288             mCounter.decrementAndGet();
289         }
290 
onItemAdded()291         void onItemAdded() {
292             mCounter.incrementAndGet();
293         }
294 
isEmpty()295         boolean isEmpty() {
296             return mCounter.get() == 0;
297         }
298 
299     }
300 }
301