1 // Copyright 2018 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.chrome.browser.vr.util;
6 
7 import android.content.Intent;
8 import android.os.SystemClock;
9 import android.support.test.InstrumentationRegistry;
10 import android.support.test.uiautomator.UiDevice;
11 
12 import org.junit.Assert;
13 import org.junit.rules.RuleChain;
14 import org.junit.rules.TestRule;
15 import org.junit.runner.Description;
16 import org.junit.runners.model.Statement;
17 
18 import org.chromium.base.BundleUtils;
19 import org.chromium.base.test.BundleTestRule;
20 import org.chromium.base.test.params.ParameterSet;
21 import org.chromium.chrome.browser.vr.TestVrShellDelegate;
22 import org.chromium.chrome.browser.vr.VrFeedbackStatus;
23 import org.chromium.chrome.browser.vr.VrIntentDelegate;
24 import org.chromium.chrome.browser.vr.rules.ChromeTabbedActivityVrTestRule;
25 import org.chromium.chrome.browser.vr.rules.CustomTabActivityVrTestRule;
26 import org.chromium.chrome.browser.vr.rules.VrActivityRestrictionRule;
27 import org.chromium.chrome.browser.vr.rules.VrTestRule;
28 import org.chromium.chrome.browser.vr.rules.WebappActivityVrTestRule;
29 
30 import java.util.ArrayList;
31 import java.util.concurrent.Callable;
32 
33 /**
34  * Utility class for interacting with VR-specific Rules, i.e. ChromeActivityTestRules that implement
35  * the VrTestRule interface.
36  */
37 public class VrTestRuleUtils extends XrTestRuleUtils {
38     // VrCore waits this amount of time after exiting VR before actually unregistering a registered
39     // Daydream intent, meaning that it still thinks VR is active until this amount of time has
40     // passed.
41     private static final int VRCORE_UNREGISTER_DELAY_MS = 500;
42 
43     /**
44      * Helper method to apply a VrTestRule/ChromeActivityTestRule combination. The only difference
45      * between various classes that implement VrTestRule is how they start their activity, so the
46      * common boilerplate code can be kept here so each VrTestRule only has to provide a way to
47      * launch Chrome.
48      *
49      * @param base The Statement passed to the calling ChromeActivityTestRule's apply() method.
50      * @param desc The Description passed to the calling ChromeActivityTestRule's apply() method.
51      * @param rule The calling VrTestRule.
52      * @param launcher A ChromeLaunchMethod whose launch() contains the code snippet to start Chrome
53      *        in the calling ChromeActivityTestRule's activity type.
54      */
evaluateVrTestRuleImpl(final Statement base, final Description desc, final VrTestRule rule, final ChromeLaunchMethod launcher)55     public static void evaluateVrTestRuleImpl(final Statement base, final Description desc,
56             final VrTestRule rule, final ChromeLaunchMethod launcher) throws Throwable {
57         TestVrShellDelegate.setDescription(desc);
58 
59         VrTestRuleUtils.ensureNoVrActivitiesDisplayed();
60         launcher.launch();
61 
62         // Reset the VR feedback shared preferences if they're not currently the default because
63         // otherwise we can run into issues with VrFeedbackInfoBarTest#* erroneously failing due to
64         // tests being non-hermetic. Only set if not the default instead of unconditionally in order
65         // to avoid unnecessary disk writes.
66         if (VrFeedbackStatus.getFeedbackOptOut()) {
67             VrFeedbackStatus.setFeedbackOptOut(false);
68         }
69         if (VrFeedbackStatus.getUserExitedAndEntered2DCount() != 0) {
70             VrFeedbackStatus.setUserExitedAndEntered2DCount(0);
71         }
72 
73         base.evaluate();
74     }
75 
76     /**
77      * Creates the list of VrTestRules that are currently supported for use in test
78      * parameterization.
79      *
80      * @return An ArrayList of ParameterSets, with each ParameterSet containing a callable to create
81      *         a VrTestRule for a supported ChromeActivity.
82      */
generateDefaultTestRuleParameters()83     public static ArrayList<ParameterSet> generateDefaultTestRuleParameters() {
84         ArrayList<ParameterSet> parameters = new ArrayList<ParameterSet>();
85         parameters.add(new ParameterSet()
86                                .value(new Callable<ChromeTabbedActivityVrTestRule>() {
87                                    @Override
88                                    public ChromeTabbedActivityVrTestRule call() {
89                                        return new ChromeTabbedActivityVrTestRule();
90                                    }
91                                })
92                                .name("ChromeTabbedActivity"));
93 
94         parameters.add(new ParameterSet()
95                                .value(new Callable<CustomTabActivityVrTestRule>() {
96                                    @Override
97                                    public CustomTabActivityVrTestRule call() {
98                                        return new CustomTabActivityVrTestRule();
99                                    }
100                                })
101                                .name("CustomTabActivity"));
102 
103         parameters.add(new ParameterSet()
104                                .value(new Callable<WebappActivityVrTestRule>() {
105                                    @Override
106                                    public WebappActivityVrTestRule call() {
107                                        return new WebappActivityVrTestRule();
108                                    }
109                                })
110                                .name("WebappActivity"));
111 
112         return parameters;
113     }
114 
115     /**
116      * Creates a RuleChain that applies the XrActivityRestrictionRule and VrActivityRestrictionRule
117      * before the given VrTestRule.
118      *
119      * Also enforces that {@link BundleUtils#isBundle()} returns true for vr to be initialized.
120      *
121      * @param rule The TestRule to wrap in an XrActivityRestrictionRule and
122      *        VrActivityRestrictionRule.
123      * @return A RuleChain that ensures an XrActivityRestrictionRule and VrActivityRestrictionRule
124      *         is applied before the provided TestRule.
125      */
wrapRuleInActivityRestrictionRule(TestRule rule)126     public static RuleChain wrapRuleInActivityRestrictionRule(TestRule rule) {
127         Assert.assertTrue("Given rule is not an VrTestRule", rule instanceof VrTestRule);
128         return RuleChain
129                 .outerRule(new VrActivityRestrictionRule(((VrTestRule) rule).getRestriction()))
130                 .around(new BundleTestRule())
131                 .around(XrTestRuleUtils.wrapRuleInActivityRestrictionRule(rule));
132     }
133 
134     /**
135      * Ensures that no VR-related activity is currently being displayed. This is meant to be used
136      * by TestRules before starting any activities. Having a VR activity in the foreground (e.g.
137      * Daydream Home) has the potential to affect test results, as it often means that we are in VR
138      * at the beginning of the test, which we don't want. This is most commonly caused by VrCore
139      * automatically launching Daydream Home when Chrome gets closed after a test, but can happen
140      * for other reasons as well.
141      */
ensureNoVrActivitiesDisplayed()142     public static void ensureNoVrActivitiesDisplayed() {
143         // This will always be hit on standalones, but we're expected to be in VR in that case.
144         if (TestVrShellDelegate.isOnStandalone()) return;
145         UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
146         String currentPackageName = uiDevice.getCurrentPackageName();
147         if (currentPackageName != null && currentPackageName.contains("vr")) {
148             uiDevice.pressHome();
149             // Chrome startup would likely be slow enough that this sleep is unnecessary, but sleep
150             // to be sure since this will be hit relatively infrequently.
151             SystemClock.sleep(VRCORE_UNREGISTER_DELAY_MS);
152         }
153     }
154 
155     /**
156      * Helper method to add additional data to a Chrome startup intent that makes it usable on a
157      * standalone VR device.
158      */
maybeAddStandaloneIntentData(Intent intent)159     public static Intent maybeAddStandaloneIntentData(Intent intent) {
160         if (TestVrShellDelegate.isOnStandalone()) {
161             // Tell VrShellDelegate that it should create a TestVrShellDelegate on startup
162             TestVrShellDelegate.enableTestVrShellDelegateOnStartupForTesting();
163             intent.addCategory(VrIntentDelegate.DAYDREAM_CATEGORY);
164             intent.putExtra("android.intent.extra.VR_LAUNCH", true);
165         }
166         return intent;
167     }
168 }
169