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