• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.cts.verifier.vr;
17 
18 import android.content.ComponentName;
19 import android.content.Intent;
20 import android.content.SharedPreferences;
21 import android.os.Bundle;
22 import android.os.Handler;
23 import android.os.HandlerThread;
24 import android.provider.Settings;
25 import android.util.Log;
26 import android.view.LayoutInflater;
27 import android.view.View;
28 import android.view.ViewGroup;
29 import android.widget.Button;
30 import android.widget.ImageView;
31 import android.widget.TextView;
32 
33 
34 import com.android.cts.verifier.PassFailButtons;
35 import com.android.cts.verifier.R;
36 
37 import java.util.Arrays;
38 import java.util.Objects;
39 import java.util.concurrent.ArrayBlockingQueue;
40 import java.util.concurrent.Executors;
41 import java.util.concurrent.ScheduledExecutorService;
42 import java.util.concurrent.TimeUnit;
43 
44 public class VrListenerVerifierActivity extends PassFailButtons.Activity {
45 
46     private static final String TAG = "VrListenerActivity";
47     public static final String ENABLED_VR_LISTENERS = "enabled_vr_listeners";
48     private static final String STATE = "state";
49     private static final int POLL_DELAY_MS = 2000;
50     static final String EXTRA_LAUNCH_SECOND_INTENT = "do2intents";
51 
52     private LayoutInflater mInflater;
53     private InteractiveTestCase[] mTests;
54     private ViewGroup mTestViews;
55     private int mCurrentIdx;
56     private Handler mMainHandler;
57     private Handler mTestHandler;
58     private HandlerThread mTestThread;
59     private PersistentTestStatusHandler persistentTestStatusHandler;
60 
61     public enum Status {
62         SETUP,
63         RUNNING,
64         PASS,
65         FAIL,
66         WAIT_FOR_USER;
67     }
68 
69     @Override
onCreate(Bundle savedState)70     protected void onCreate(Bundle savedState) {
71         super.onCreate(savedState);
72         mCurrentIdx = (savedState == null) ? 0 : savedState.getInt(STATE, 0);
73 
74         persistentTestStatusHandler = new PersistentTestStatusHandler();
75         mTestThread = new HandlerThread("VrTestThread");
76         mTestThread.start();
77         mTestHandler = new Handler(mTestThread.getLooper());
78         mInflater = getLayoutInflater();
79         View v = mInflater.inflate(R.layout.vr_main, null);
80         setContentView(v);
81         setPassFailButtonClickListeners();
82         getPassButton().setEnabled(false);
83         setInfoResources(R.string.vr_test_title, R.string.vr_info, -1);
84 
85         mTestViews = (ViewGroup) v.findViewById(R.id.vr_test_items);
86         mTests = new InteractiveTestCase[] {
87                 new IsDefaultDisabledTest(),
88                 new UserEnableTest(),
89                 new VrModeSwitchTest(),
90                 new VrModeMultiSwitchTest(),
91                 new UserDisableTest(),
92         };
93 
94         for (InteractiveTestCase test : mTests) {
95             test.setStatus((savedState == null) ? Status.SETUP :
96                     Status.values()[savedState.getInt(test.getClass().getSimpleName(), 0)]);
97             mTestViews.addView(test.getView(mTestViews));
98         }
99 
100         updateUiState();
101 
102         mMainHandler = new Handler();
103     }
104 
105     @Override
onDestroy()106     public void onDestroy() {
107         super.onDestroy();
108         if (mTestThread != null) {
109             mTestThread.quit();
110         }
111     }
112 
113     @Override
onSaveInstanceState(Bundle outState)114     protected void onSaveInstanceState(Bundle outState) {
115         outState.putInt(STATE, mCurrentIdx);
116         for (InteractiveTestCase i : mTests) {
117             outState.putInt(i.getClass().getSimpleName(), i.getStatus().ordinal());
118         }
119         super.onSaveInstanceState(outState);
120     }
121 
122     @Override
onResume()123     protected void onResume() {
124         super.onResume();
125 
126         final InteractiveTestCase current = mTests[mCurrentIdx];
127         final Status currentTestStatus = current.getStatus();
128         if (currentTestStatus == Status.RUNNING) {
129           // The previous instance of this class was interurpted while a test
130           // was running most likely due to a configuration change
131           // We must wait for the previous test results to be written to the
132           // shared configuration file before continuing with the next test
133           waitForPreviousRunningTest();
134         } else if (currentTestStatus == Status.PASS) {
135           // The previous instance of this class was interrupted after a test
136           // has finished running most likely due to a configuration change
137           selectNext();
138         } else {
139           runNext();
140         }
141     }
142 
waitForPreviousRunningTest()143     private void waitForPreviousRunningTest() {
144       final InteractiveTestCase current = mTests[mCurrentIdx];
145       ScheduledExecutorService s = Executors.newSingleThreadScheduledExecutor();
146 
147       s.scheduleAtFixedRate(new Runnable() {
148         private void handleTestFinished() {
149           selectNext();
150           s.shutdown();
151         }
152 
153         @Override
154         public void run() {
155           Status status = persistentTestStatusHandler.getStatusOfTest(mCurrentIdx);
156 
157           if (status == Status.PASS) {
158             current.markPassed();
159             handleTestFinished();
160           } else if (status == Status.FAIL) {
161             current.markFailed();
162             handleTestFinished();
163           }
164         }
165       }, 0, POLL_DELAY_MS, TimeUnit.MILLISECONDS);
166     }
167 
updateUiState()168     private void updateUiState() {
169         boolean allPassed = true;
170         for (InteractiveTestCase t : mTests) {
171             t.updateViews();
172             if (t.getStatus() != Status.PASS) {
173                 allPassed = false;
174             }
175         }
176 
177         if (allPassed) {
178             getPassButton().setEnabled(true);
179         }
180     }
181 
logWithStack(String message)182     protected void logWithStack(String message) {
183         logWithStack(message, null);
184     }
185 
logWithStack(String message, Throwable stackTrace)186     protected void logWithStack(String message, Throwable stackTrace) {
187         if (stackTrace == null) {
188             stackTrace = new Throwable();
189             stackTrace.fillInStackTrace();
190         }
191         Log.e(TAG, message, stackTrace);
192     }
193 
selectNext()194     private void selectNext() {
195         mCurrentIdx++;
196         if (mCurrentIdx >= mTests.length) {
197             done();
198             return;
199         }
200         final InteractiveTestCase current = mTests[mCurrentIdx];
201         current.markWaiting();
202     }
203 
runNext()204     private void runNext() {
205         if (mCurrentIdx >= mTests.length) {
206             done();
207             return;
208         }
209         final InteractiveTestCase current = mTests[mCurrentIdx];
210         mTestHandler.post(new Runnable() {
211             @Override
212             public void run() {
213                 Log.i(TAG, "Starting test: " + current.getClass().getSimpleName());
214                 persistentTestStatusHandler.setStatusOfTest(mCurrentIdx, Status.RUNNING);
215                 boolean passed = true;
216                 try {
217                     current.setUp();
218                     current.test();
219                 } catch (Throwable e) {
220                     logWithStack("Failed " + current.getClass().getSimpleName() + " with: ", e);
221                     setFailed(current);
222                     passed = false;
223                 } finally {
224                     try {
225                         current.tearDown();
226                     } catch (Throwable e) {
227                         logWithStack("Failed tearDown of " + current.getClass().getSimpleName() +
228                                 " with: ", e);
229                         setFailed(current);
230                         passed = false;
231                     }
232                 }
233 
234                 // Write to persistent store in the event that the activity was
235                 // destroyed while this test was running
236                 persistentTestStatusHandler.setStatusOfTest(mCurrentIdx, passed ? Status.PASS : Status.FAIL);
237 
238                 if (passed) {
239                     current.markPassed();
240                     mMainHandler.post(new Runnable() {
241                         @Override
242                         public void run() {
243                             selectNext();
244                         }
245                     });
246                 }
247                 Log.i(TAG, "Done test: " + current.getClass().getSimpleName());
248             }
249         });
250     }
251 
done()252     private void done() {
253         updateUiState();
254         persistentTestStatusHandler.clear();
255         Log.i(TAG, "Completed run!");
256     }
257 
258 
setFailed(final InteractiveTestCase current)259     private void setFailed(final InteractiveTestCase current) {
260         mMainHandler.post(new Runnable() {
261             @Override
262             public void run() {
263                 getPassButton().setEnabled(false);
264                 current.markFailed();
265             }
266         });
267     }
268 
createUserInteractionTestView(ViewGroup parent, int stringId, int messageId)269     protected View createUserInteractionTestView(ViewGroup parent, int stringId, int messageId) {
270         View v = mInflater.inflate(R.layout.vr_item, parent, false);
271         TextView instructions = (TextView) v.findViewById(R.id.vr_instructions);
272         instructions.setText(getString(messageId));
273         Button b = (Button) v.findViewById(R.id.vr_action_button);
274         b.setText(stringId);
275         b.setTag(stringId);
276         return v;
277     }
278 
createAutoTestView(ViewGroup parent, int messageId)279     protected View createAutoTestView(ViewGroup parent, int messageId) {
280         View v = mInflater.inflate(R.layout.vr_item, parent, false);
281         TextView instructions = (TextView) v.findViewById(R.id.vr_instructions);
282         instructions.setText(getString(messageId));
283         Button b = (Button) v.findViewById(R.id.vr_action_button);
284         b.setVisibility(View.GONE);
285         return v;
286     }
287 
288     protected abstract class InteractiveTestCase {
289         protected static final String TAG = "InteractiveTest";
290         private Status status;
291         private View view;
292 
inflate(ViewGroup parent)293         abstract View inflate(ViewGroup parent);
294 
getView(ViewGroup parent)295         View getView(ViewGroup parent) {
296             if (view == null) {
297                 view = inflate(parent);
298             }
299             return view;
300         }
301 
test()302         abstract void test() throws Throwable;
303 
setUp()304         void setUp() throws Throwable {
305             // Noop
306         }
307 
tearDown()308         void tearDown() throws Throwable {
309             // Noop
310         }
311 
getStatus()312         Status getStatus() {
313             return status;
314         }
315 
setStatus(Status s)316         void setStatus(Status s) {
317             status = s;
318         }
319 
markFailed()320         void markFailed() {
321             Log.i(TAG, "FAILED test: " + this.getClass().getSimpleName());
322             mMainHandler.post(new Runnable() {
323                 @Override
324                 public void run() {
325                     InteractiveTestCase.this.setStatus(Status.FAIL);
326                     updateViews();
327                 }
328             });
329         }
330 
markPassed()331         void markPassed() {
332             Log.i(TAG, "PASSED test: " + this.getClass().getSimpleName());
333             mMainHandler.post(new Runnable() {
334                 @Override
335                 public void run() {
336                     InteractiveTestCase.this.setStatus(Status.PASS);
337                     updateViews();
338                 }
339             });
340         }
341 
markFocused()342         void markFocused() {
343             mMainHandler.post(new Runnable() {
344                 @Override
345                 public void run() {
346                     InteractiveTestCase.this.setStatus(Status.SETUP);
347                     updateViews();
348                 }
349             });
350         }
351 
markWaiting()352         void markWaiting() {
353             mMainHandler.post(new Runnable() {
354                 @Override
355                 public void run() {
356                     InteractiveTestCase.this.setStatus(Status.WAIT_FOR_USER);
357                     updateViews();
358                 }
359             });
360         }
361 
markRunning()362         void markRunning() {
363             mMainHandler.post(new Runnable() {
364                 @Override
365                 public void run() {
366                     InteractiveTestCase.this.setStatus(Status.RUNNING);
367                     updateViews();
368                 }
369             });
370         }
371 
updateViews()372         private void updateViews() {
373             View item = view;
374             ImageView statusView = (ImageView) item.findViewById(R.id.vr_status);
375             View button = item.findViewById(R.id.vr_action_button);
376             switch (status) {
377                 case WAIT_FOR_USER:
378                     statusView.setImageResource(R.drawable.fs_warning);
379                     button.setEnabled(true);
380                     break;
381                 case SETUP:
382                     statusView.setImageResource(R.drawable.fs_indeterminate);
383                     button.setEnabled(false);
384                     break;
385                 case RUNNING:
386                     statusView.setImageResource(R.drawable.fs_clock);
387                     break;
388                 case FAIL:
389                     statusView.setImageResource(R.drawable.fs_error);
390                     break;
391                 case PASS:
392                     statusView.setImageResource(R.drawable.fs_good);
393                     button.setClickable(false);
394                     button.setEnabled(false);
395                     break;
396             }
397             statusView.invalidate();
398         }
399     }
400 
assertTrue(String message, boolean b)401     private static void assertTrue(String message, boolean b) {
402         if (!b) {
403             throw new IllegalStateException(message);
404         }
405     }
406 
assertIn(String message, E elem, E[] c)407     private static <E> void assertIn(String message, E elem, E[] c) {
408         if (!Arrays.asList(c).contains(elem)) {
409             throw new IllegalStateException(message);
410         }
411     }
412 
assertEventIn(String message, MockVrListenerService.Event elem, MockVrListenerService.EventType[] c)413     private static void assertEventIn(String message, MockVrListenerService.Event elem,
414                                       MockVrListenerService.EventType[] c) {
415         if (!Arrays.asList(c).contains(elem.type)) {
416             throw new IllegalStateException(message);
417         }
418     }
419 
launchVrListenerSettings()420     protected void launchVrListenerSettings() {
421         VrListenerVerifierActivity.this.startActivity(
422                 new Intent(Settings.ACTION_VR_LISTENER_SETTINGS));
423     }
424 
launchVrActivity()425     protected void launchVrActivity() {
426         VrListenerVerifierActivity.this.startActivity(
427                 new Intent(VrListenerVerifierActivity.this, MockVrActivity.class));
428     }
429 
launchDoubleVrActivity()430     protected void launchDoubleVrActivity() {
431         VrListenerVerifierActivity.this.startActivity(
432                 new Intent(VrListenerVerifierActivity.this, MockVrActivity.class).
433                         putExtra(EXTRA_LAUNCH_SECOND_INTENT, true));
434     }
435 
actionPressed(View v)436     public void actionPressed(View v) {
437         Object tag = v.getTag();
438         if (tag instanceof Integer) {
439             int id = ((Integer) tag).intValue();
440             if (id == R.string.vr_start_settings) {
441                 launchVrListenerSettings();
442             } else if (id == R.string.vr_start_vr_activity) {
443                 launchVrActivity();
444             } else if (id == R.string.vr_start_double_vr_activity) {
445                 launchDoubleVrActivity();
446             }
447         }
448     }
449 
450     private class IsDefaultDisabledTest extends InteractiveTestCase {
451 
452         @Override
inflate(ViewGroup parent)453         View inflate(ViewGroup parent) {
454             return createAutoTestView(parent, R.string.vr_check_disabled);
455         }
456 
457         @Override
setUp()458         void setUp() {
459             markFocused();
460         }
461 
462         @Override
test()463         void test() {
464             assertTrue("VR listeners should not be bound by default.",
465                     MockVrListenerService.getNumBoundMockVrListeners() == 0);
466         }
467     }
468 
469     private class UserEnableTest extends InteractiveTestCase {
470 
471         @Override
inflate(ViewGroup parent)472         View inflate(ViewGroup parent) {
473             return createUserInteractionTestView(parent, R.string.vr_start_settings,
474                     R.string.vr_enable_service);
475         }
476 
477         @Override
setUp()478         void setUp() {
479             markWaiting();
480         }
481 
482         @Override
test()483         void test() {
484             String helpers = Settings.Secure.getString(getContentResolver(), ENABLED_VR_LISTENERS);
485             ComponentName c = new ComponentName(VrListenerVerifierActivity.this,
486                     MockVrListenerService.class);
487             if (MockVrListenerService.getPendingEvents().size() > 0) {
488                 MockVrListenerService.getPendingEvents().clear();
489                 throw new IllegalStateException("VrListenerService bound before entering VR mode!");
490             }
491             assertTrue("Settings must now contain " + c.flattenToString(),
492                     helpers != null && helpers.contains(c.flattenToString()));
493         }
494     }
495 
496     private class VrModeSwitchTest extends InteractiveTestCase {
497 
498         @Override
inflate(ViewGroup parent)499         View inflate(ViewGroup parent) {
500             return createUserInteractionTestView(parent, R.string.vr_start_vr_activity,
501                     R.string.vr_start_vr_activity_desc);
502         }
503 
504         @Override
setUp()505         void setUp() {
506             markWaiting();
507         }
508 
509         @Override
test()510         void test() throws Throwable {
511             markRunning();
512 
513             ArrayBlockingQueue<MockVrListenerService.Event> q =
514                     MockVrListenerService.getPendingEvents();
515             MockVrListenerService.Event e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
516             assertTrue("Timed out before receive onCreate or onBind event from VrListenerService.",
517                     e != null);
518             assertEventIn("First listener service event must be onCreate or onBind, but was " +
519                     e.type, e, new MockVrListenerService.EventType[]{
520                     MockVrListenerService.EventType.ONCREATE,
521                     MockVrListenerService.EventType.ONBIND
522             });
523             if (e.type == MockVrListenerService.EventType.ONCREATE) {
524                 e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
525                 assertTrue("Timed out before receive onBind event from VrListenerService.",
526                         e != null);
527                 assertEventIn("Second listener service event must be onBind, but was " +
528                         e.type, e, new MockVrListenerService.EventType[]{
529                         MockVrListenerService.EventType.ONBIND
530                 });
531             }
532 
533             e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
534             assertTrue("Timed out before receive onCurrentVrModeActivityChanged event " +
535                     "from VrListenerService.", e != null);
536             assertTrue("Listener service must receive onCurrentVrModeActivityChanged, but was " +
537                     e.type,
538                     e.type == MockVrListenerService.EventType.ONCURRENTVRMODEACTIVITYCHANGED);
539             ComponentName expected = new ComponentName(VrListenerVerifierActivity.this,
540                     MockVrActivity.class);
541             assertTrue("Activity component must be " + expected + ", but was: " + e.arg1,
542                     Objects.equals(expected, e.arg1));
543 
544             e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
545             assertTrue("Timed out before receive unbind event from VrListenerService.", e != null);
546             assertEventIn("Listener service must receive onUnbind, but was " +
547                     e.type, e, new MockVrListenerService.EventType[]{
548                     MockVrListenerService.EventType.ONUNBIND
549             });
550 
551             // Consume onDestroy
552             e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
553             assertTrue("Timed out before receive onDestroy event from VrListenerService.",
554                     e != null);
555             assertEventIn("Listener service must receive onDestroy, but was " +
556                     e.type, e, new MockVrListenerService.EventType[]{
557                     MockVrListenerService.EventType.ONDESTROY
558             });
559 
560             e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
561             if (e != null) {
562                 throw new IllegalStateException("Spurious event received after onDestroy: "
563                         + e.type);
564             }
565         }
566     }
567 
568     private class VrModeMultiSwitchTest extends InteractiveTestCase {
569 
570         @Override
inflate(ViewGroup parent)571         View inflate(ViewGroup parent) {
572             return createUserInteractionTestView(parent, R.string.vr_start_double_vr_activity,
573                     R.string.vr_start_vr_double_activity_desc);
574         }
575 
576         @Override
setUp()577         void setUp() {
578             markWaiting();
579         }
580 
581         @Override
test()582         void test() throws Throwable {
583             markRunning();
584 
585             ArrayBlockingQueue<MockVrListenerService.Event> q =
586                     MockVrListenerService.getPendingEvents();
587             MockVrListenerService.Event e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
588             assertTrue("Timed out before receive event from VrListenerService.", e != null);
589             assertEventIn("First listener service event must be onCreate or onBind, but was " +
590                     e.type, e, new MockVrListenerService.EventType[]{
591                     MockVrListenerService.EventType.ONCREATE,
592                     MockVrListenerService.EventType.ONBIND
593             });
594             if (e.type == MockVrListenerService.EventType.ONCREATE) {
595                 e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
596                 assertTrue("Timed out before receive event from VrListenerService.", e != null);
597                 assertEventIn("Second listener service event must be onBind, but was " +
598                         e.type, e, new MockVrListenerService.EventType[]{
599                         MockVrListenerService.EventType.ONBIND
600                 });
601             }
602 
603             e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
604             assertTrue("Timed out before receive event from VrListenerService.", e != null);
605             assertTrue("Listener service must receive onCurrentVrModeActivityChanged, but received "
606                     + e.type, e.type ==
607                     MockVrListenerService.EventType.ONCURRENTVRMODEACTIVITYCHANGED);
608             ComponentName expected = new ComponentName(VrListenerVerifierActivity.this,
609                     MockVrActivity.class);
610             assertTrue("Activity component must be " + expected + ", but was: " + e.arg1,
611                     Objects.equals(expected, e.arg1));
612 
613             e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
614             assertTrue("Timed out before receive event from VrListenerService.", e != null);
615             assertTrue("Listener service must receive onCurrentVrModeActivityChanged, but received "
616                     + e.type, e.type ==
617                     MockVrListenerService.EventType.ONCURRENTVRMODEACTIVITYCHANGED);
618             ComponentName expected2 = new ComponentName(VrListenerVerifierActivity.this,
619                     MockVrActivity2.class);
620             assertTrue("Activity component must be " + expected2 + ", but was: " + e.arg1,
621                     Objects.equals(expected2, e.arg1));
622 
623             e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
624             assertTrue("Timed out before receive event from VrListenerService.", e != null);
625             assertTrue("Listener service must receive onCurrentVrModeActivityChanged, but received "
626                     + e.type, e.type ==
627                     MockVrListenerService.EventType.ONCURRENTVRMODEACTIVITYCHANGED);
628             assertTrue("Activity component must be " + expected + ", but was: " + e.arg1,
629                     Objects.equals(expected, e.arg1));
630 
631             e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
632             assertTrue("Timed out before receive event from VrListenerService.", e != null);
633             assertEventIn("Listener service must receive onUnbind, but was " +
634                     e.type, e, new MockVrListenerService.EventType[]{
635                     MockVrListenerService.EventType.ONUNBIND
636             });
637 
638             // Consume onDestroy
639             e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
640             assertTrue("Timed out before receive onDestroy event from VrListenerService.",
641                     e != null);
642             assertEventIn("Listener service must receive onDestroy, but was " +
643                     e.type, e, new MockVrListenerService.EventType[]{
644                     MockVrListenerService.EventType.ONDESTROY
645             });
646 
647             e = q.poll(POLL_DELAY_MS, TimeUnit.MILLISECONDS);
648             if (e != null) {
649                 throw new IllegalStateException("Spurious event received after onDestroy: "
650                         + e.type);
651             }
652         }
653     }
654 
655     private class UserDisableTest extends InteractiveTestCase {
656 
657         @Override
inflate(ViewGroup parent)658         View inflate(ViewGroup parent) {
659             return createUserInteractionTestView(parent, R.string.vr_start_settings,
660                     R.string.vr_disable_service);
661         }
662 
663         @Override
setUp()664         void setUp() {
665             markWaiting();
666         }
667 
668         @Override
test()669         void test() {
670             String helpers = Settings.Secure.getString(getContentResolver(), ENABLED_VR_LISTENERS);
671             ComponentName c = new ComponentName(VrListenerVerifierActivity.this,
672                     MockVrListenerService.class);
673             assertTrue("Settings must no longer contain " + c.flattenToString(),
674                     helpers == null || !(helpers.contains(c.flattenToString())));
675         }
676     }
677 
678     private class PersistentTestStatusHandler {
679 
680       private final String PREFERENCE_FILE_NAME = "VrListenerVerifierActivityTestStatus";
681       private final SharedPreferences sharedPref = getSharedPreferences(PREFERENCE_FILE_NAME, 0);
682 
getStatusOfTest(int testIndex)683       Status getStatusOfTest(int testIndex) {
684         String key = Integer.toString(testIndex);
685         String stringStatus = sharedPref.getString(key, Status.WAIT_FOR_USER.name());
686 
687         return Status.valueOf(stringStatus);
688       }
689 
setStatusOfTest(int testIndex, Status status)690       void setStatusOfTest(int testIndex, Status status) {
691         String key = Integer.toString(testIndex);
692         String stringStatus = status.name();
693 
694         SharedPreferences.Editor editor = sharedPref.edit();
695         editor.putString(key, stringStatus);
696         editor.apply();
697       }
698 
clear()699       void clear() {
700         SharedPreferences.Editor editor = sharedPref.edit();
701         editor.clear();
702         editor.apply();
703       }
704     }
705 }
706