1// Copyright 2014 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#include <string>
6#include <vector>
7
8#include "base/json/json_writer.h"
9#include "base/prefs/pref_service.h"
10#include "base/strings/stringprintf.h"
11#include "base/values.h"
12#include "chrome/browser/extensions/extension_apitest.h"
13#include "chrome/common/pref_names.h"
14#include "extensions/test/result_catcher.h"
15
16// API tests for chrome.accessibilityFeatures API.
17// Note that the API is implemented using preference API infrastructure.
18// See preference_api.cc for the list of accessibility features exposed by the
19// API and the related preferences.
20
21namespace extensions {
22
23namespace {
24
25// Keys for data in the test config argument that will be set for the test app
26// to use.
27// The test that the app should run.
28const char kTestNameKey[] = "testName";
29// Key for list of features enabled when the test is initialized.
30const char kEnabledFeaturesKey[] = "enabled";
31// Key for list fo features disabled when the test is initialized.
32const char kDisabledFeaturesKey[] = "disabled";
33
34// A test extension path. The extension has only |accessibilityFeatures.read|
35// permission.
36const char kTestExtensionPathReadPermission[] =
37    "accessibility_features/read_permission/";
38// A test extension path. The extension has only |accessibilityFeatures.modify|
39// permission.
40const char kTestExtensionPathMofifyPermission[] =
41    "accessibility_features/modify_permission/";
42
43// Accessibility features API test.
44// Tests are parameterized by whether the test extension is write-only (the
45// parameter value is true) or read-only (the parameter value is false).
46class AccessibilityFeaturesApiTest : public ExtensionApiTest,
47                                     public testing::WithParamInterface<bool> {
48 public:
49  AccessibilityFeaturesApiTest() {}
50  virtual ~AccessibilityFeaturesApiTest() {}
51
52 protected:
53  // Returns pref service to be used to initialize and later verify
54  // accessibility preference values.
55  PrefService* GetPrefs() { return browser()->profile()->GetPrefs(); }
56
57  // Returns the path of the extension that should be used in a parameterized
58  // test.
59  std::string GetTestExtensionPath() const {
60    if (GetParam())
61      return kTestExtensionPathMofifyPermission;
62    return kTestExtensionPathReadPermission;
63  }
64
65  // Whether a parameterized test should have been able to modify accessibility
66  // preferences (i.e. whether the test extension had modify permission).
67  bool ShouldModifyingFeatureSucceed() const { return GetParam(); }
68
69  // Returns preference path for accessibility features as defined by the API.
70  const char* const GetPrefForFeature(const std::string& feature) {
71    if (feature == "spokenFeedback")
72      return prefs::kAccessibilitySpokenFeedbackEnabled;
73    if (feature == "largeCursor")
74      return prefs::kAccessibilityLargeCursorEnabled;
75    if (feature == "stickyKeys")
76      return prefs::kAccessibilityStickyKeysEnabled;
77    if (feature == "highContrast")
78      return prefs::kAccessibilityHighContrastEnabled;
79    if (feature == "screenMagnifier")
80      return prefs::kAccessibilityScreenMagnifierEnabled;
81    if (feature == "autoclick")
82      return prefs::kAccessibilityAutoclickEnabled;
83    if (feature == "virtualKeyboard")
84      return prefs::kAccessibilityVirtualKeyboardEnabled;
85    return NULL;
86  }
87
88  // Initializes preferences before running the test extension.
89  // |prefs| Pref service which should be initializzed.
90  // |enabled_features| List of boolean preference whose value should be set to
91  //     true.
92  // |disabled_features| List of boolean preferences whose value should be set
93  //     to false.
94  bool InitPrefServiceForTest(
95      PrefService* prefs,
96      const std::vector<std::string>& enabled_features,
97      const std::vector<std::string>& disabled_features) {
98    for (size_t i = 0; i < enabled_features.size(); ++i) {
99      const char* const pref_name = GetPrefForFeature(enabled_features[i]);
100      EXPECT_TRUE(pref_name) << "Invalid feature " << enabled_features[i];
101      if (!pref_name)
102        return false;
103      prefs->SetBoolean(pref_name, true);
104    }
105
106    for (size_t i = 0; i < disabled_features.size(); ++i) {
107      const char* const pref_name = GetPrefForFeature(disabled_features[i]);
108      EXPECT_TRUE(pref_name) << "Invalid feature " << disabled_features[i];
109      if (!pref_name)
110        return false;
111      prefs->SetBoolean(pref_name, false);
112    }
113    return true;
114  }
115
116  // Verifies that preferences have the expected value.
117  // |prefs| The pref service to be verified.
118  // |enabled_features| The list of boolean preferences whose value should be
119  //     true.
120  // |disabled_features| The list of boolean preferences whose value should be
121  //     false.
122  void VerifyPrefServiceState(
123      PrefService* prefs,
124      const std::vector<std::string>& enabled_features,
125      const std::vector<std::string>& disabled_features) {
126    for (size_t i = 0; i < enabled_features.size(); ++i) {
127      const char* const pref_name = GetPrefForFeature(enabled_features[i]);
128      ASSERT_TRUE(pref_name) << "Invalid feature " << enabled_features[i];
129      ASSERT_TRUE(prefs->GetBoolean(pref_name));
130    }
131
132    for (size_t i = 0; i < disabled_features.size(); ++i) {
133      const char* const pref_name = GetPrefForFeature(disabled_features[i]);
134      ASSERT_TRUE(pref_name) << "Invalid feature " << disabled_features[i];
135      ASSERT_FALSE(prefs->GetBoolean(pref_name));
136    }
137  }
138
139  // Given the test name and list of enabled and disabled features, generates
140  // and sets the JSON string that should be given to the test extension as
141  // test configuration.
142  // The result is saved to |result|. The return value is whether the test
143  // argument was successfully generated.
144  bool GenerateTestArg(const std::string& test_name,
145                       const std::vector<std::string>& enabled_features,
146                       const std::vector<std::string>& disabled_features,
147                       std::string* result) {
148    base::DictionaryValue test_arg;
149    test_arg.SetString(kTestNameKey, test_name);
150
151    scoped_ptr<base::ListValue> enabled_list(new base::ListValue);
152    for (size_t i = 0; i < enabled_features.size(); ++i)
153      enabled_list->AppendString(enabled_features[i]);
154    test_arg.Set(kEnabledFeaturesKey, enabled_list.release());
155
156    scoped_ptr<base::ListValue> disabled_list(new base::ListValue);
157    for (size_t i = 0; i < disabled_features.size(); ++i)
158      disabled_list->AppendString(disabled_features[i]);
159    test_arg.Set(kDisabledFeaturesKey, disabled_list.release());
160
161    return base::JSONWriter::Write(&test_arg, result);
162  }
163};
164
165INSTANTIATE_TEST_CASE_P(AccessibilityFeatureaApiTestInstantiatePermission,
166                        AccessibilityFeaturesApiTest,
167                        testing::Bool());
168
169// Tests that an extension with read permission can read accessibility features
170// state, while an extension that doesn't have the permission cannot.
171IN_PROC_BROWSER_TEST_P(AccessibilityFeaturesApiTest, Get) {
172  // WARNING: Make sure that spoken feedback is not among enabled_features
173  // (see |Set| test for the reason).
174  std::vector<std::string> enabled_features;
175  enabled_features.push_back("largeCursor");
176  enabled_features.push_back("stickyKeys");
177  enabled_features.push_back("highContrast");
178
179  std::vector<std::string> disabled_features;
180  disabled_features.push_back("spokenFeedback");
181  disabled_features.push_back("screenMagnifier");
182  disabled_features.push_back("autoclick");
183  disabled_features.push_back("virtualKeyboard");
184
185  ASSERT_TRUE(
186      InitPrefServiceForTest(GetPrefs(), enabled_features, disabled_features));
187
188  std::string test_arg;
189  ASSERT_TRUE(GenerateTestArg(
190      "getterTest", enabled_features, disabled_features, &test_arg));
191  EXPECT_TRUE(
192      RunPlatformAppTestWithArg(GetTestExtensionPath(), test_arg.c_str()))
193      << message_;
194}
195
196// Tests that an extension with modify permission can modify accessibility
197// features, while an extension that doesn't have the permission can't.
198IN_PROC_BROWSER_TEST_P(AccessibilityFeaturesApiTest, Set) {
199  // WARNING: Make sure that spoken feedback does not get enabled at this point
200  // (before the test app is loaded), as that may break the test:
201  // |RunPlatformAppTestWithArg| waits for the test extension to load by
202  // waiting for EXTENSION_LOADED notification to be observed. It also assumes
203  // that there is only one extension being loaded during this time (it finishes
204  // when the first notification is seen). Enabling spoken feedback here would
205  // break this assumption as it would induce loading of ChromeVox extension.
206  std::vector<std::string> enabled_features;
207  enabled_features.push_back("stickyKeys");
208  enabled_features.push_back("autoclick");
209  enabled_features.push_back("virtualKeyboard");
210
211  std::vector<std::string> disabled_features;
212  disabled_features.push_back("spokenFeedback");
213  disabled_features.push_back("largeCursor");
214  disabled_features.push_back("highContrast");
215  disabled_features.push_back("screenMagnifier");
216
217  ASSERT_TRUE(
218      InitPrefServiceForTest(GetPrefs(), enabled_features, disabled_features));
219
220  std::string test_arg;
221  ASSERT_TRUE(GenerateTestArg(
222      "setterTest", enabled_features, disabled_features, &test_arg));
223
224  // The test extension attempts to flip all feature values.
225  ASSERT_TRUE(
226      RunPlatformAppTestWithArg(GetTestExtensionPath(), test_arg.c_str()))
227      << message_;
228
229  // The test tries to flip the feature states.
230  if (ShouldModifyingFeatureSucceed()) {
231    VerifyPrefServiceState(GetPrefs(), disabled_features, enabled_features);
232  } else {
233    VerifyPrefServiceState(GetPrefs(), enabled_features, disabled_features);
234  }
235}
236
237// Tests that an extension with read permission is notified when accessibility
238// features change.
239IN_PROC_BROWSER_TEST_F(AccessibilityFeaturesApiTest, ObserveFeatures) {
240  // WARNING: Make sure that spoken feedback is not among enabled_features
241  // (see |Set| test for the reason).
242  std::vector<std::string> enabled_features;
243  enabled_features.push_back("largeCursor");
244  enabled_features.push_back("stickyKeys");
245  enabled_features.push_back("highContrast");
246
247  std::vector<std::string> disabled_features;
248  disabled_features.push_back("screenMagnifier");
249
250  ASSERT_TRUE(
251      InitPrefServiceForTest(GetPrefs(), enabled_features, disabled_features));
252
253  std::string test_arg;
254  ASSERT_TRUE(GenerateTestArg(
255      "observerTest", enabled_features, disabled_features, &test_arg));
256
257  // The test extension is supposed to report result twice when runnign this
258  // test. First time when in initializes it's feature listeners, and second
259  // time, when gets all expected events. This is done so the extension is
260  // running when the accessibility features are flipped; oterwise, the
261  // extension may not see events.
262  ASSERT_TRUE(RunPlatformAppTestWithArg(kTestExtensionPathReadPermission,
263                                        test_arg.c_str()))
264      << message_;
265
266  // This should flip all features.
267  ASSERT_TRUE(
268      InitPrefServiceForTest(GetPrefs(), disabled_features, enabled_features));
269
270  // Catch the second result notification sent by the test extension.
271  ResultCatcher result_catcher;
272  ASSERT_TRUE(result_catcher.GetNextResult()) << result_catcher.message();
273}
274
275}  // namespace
276
277}  // namespace extensions
278