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.
5 #include "base/message_loop/message_loop.h"
6 #include "base/prefs/pref_service.h"
7 #include "base/strings/stringprintf.h"
8 #include "base/task_runner.h"
9 #include "chrome/browser/browser_process.h"
10 #include "chrome/browser/chrome_notification_types.h"
11 #include "chrome/browser/chromeos/customization_document.h"
12 #include "chrome/browser/chromeos/input_method/input_method_util.h"
13 #include "chrome/browser/chromeos/login/login_display_host_impl.h"
14 #include "chrome/browser/chromeos/login/login_wizard.h"
15 #include "chrome/browser/chromeos/login/test/js_checker.h"
16 #include "chrome/common/pref_names.h"
17 #include "chrome/test/base/in_process_browser_test.h"
18 #include "chromeos/ime/extension_ime_util.h"
19 #include "chromeos/ime/input_method_manager.h"
20 #include "chromeos/ime/input_method_whitelist.h"
21 #include "chromeos/system/statistics_provider.h"
22 #include "content/public/browser/notification_service.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/test/browser_test_utils.h"
25 #include "content/public/test/test_utils.h"
36 const char* kLocaleSelect
= "language-select";
37 const char* kKeyboardSelect
= "keyboard-select";
39 const char* kUSLayout
= "xkb:us::eng";
45 // Custom StatisticsProvider that will return each set of region settings.
46 class FakeStatisticsProvider
: public StatisticsProvider
{
48 virtual ~FakeStatisticsProvider() {}
50 void set_locale(const std::string
& locale
) {
51 initial_locale_
= locale
;
54 void set_keyboard_layout(const std::string
& keyboard_layout
) {
55 keyboard_layout_
= keyboard_layout
;
59 // StatisticsProvider overrides.
60 virtual void StartLoadingMachineStatistics(
61 const scoped_refptr
<base::TaskRunner
>& file_task_runner
,
62 bool load_oem_manifest
) OVERRIDE
{
65 // Populates the named machine statistic for initial_locale and
66 // keyboard_layout only.
67 virtual bool GetMachineStatistic(const std::string
& name
,
68 std::string
* result
) OVERRIDE
{
69 if (name
== "initial_locale")
70 *result
= initial_locale_
;
71 else if (name
== "keyboard_layout")
72 *result
= keyboard_layout_
;
79 virtual bool GetMachineFlag(const std::string
& name
, bool* result
) OVERRIDE
{
83 virtual void Shutdown() OVERRIDE
{
86 std::string initial_locale_
;
87 std::string keyboard_layout_
;
92 class OobeLocalizationTest
: public InProcessBrowserTest
{
94 OobeLocalizationTest();
95 virtual ~OobeLocalizationTest();
97 // Verifies that the comma-separated |values| corresponds with the first
98 // values in |select_id|, optionally checking for an options group label after
99 // the first set of options.
100 bool VerifyInitialOptions(const char* select_id
,
102 bool check_separator
);
104 // Verifies that |value| exists in |select_id|.
105 bool VerifyOptionExists(const char* select_id
, const char* value
);
107 // Dumps OOBE select control (language or keyboard) to string.
108 std::string
DumpOptions(const char* select_id
);
111 // Runs the test for the given locale and keyboard layout.
112 void RunLocalizationTest(const std::string
& initial_locale
,
113 const std::string
& keyboard_layout
,
114 const std::string
& expected_locale
,
115 const std::string
& expected_keyboard_layout
,
116 const std::string
& expected_keyboard_select_control
);
119 scoped_ptr
<system::FakeStatisticsProvider
> statistics_provider_
;
120 test::JSChecker checker
;
122 DISALLOW_COPY_AND_ASSIGN(OobeLocalizationTest
);
125 OobeLocalizationTest::OobeLocalizationTest() {
126 statistics_provider_
.reset(new system::FakeStatisticsProvider());
127 // Set the instance returned by GetInstance() for testing.
128 system::StatisticsProvider::SetTestProvider(statistics_provider_
.get());
131 OobeLocalizationTest::~OobeLocalizationTest() {
132 system::StatisticsProvider::SetTestProvider(NULL
);
135 bool OobeLocalizationTest::VerifyInitialOptions(const char* select_id
,
137 bool check_separator
) {
138 const std::string expression
= base::StringPrintf(
140 " var select = document.querySelector('#%s');\n"
143 " var values = '%s'.split(',');\n"
144 " var correct = select.selectedIndex == 0;\n"
145 " for (var i = 0; i < values.length && correct; i++) {\n"
146 " if (select.options[i].value != values[i])\n"
147 " correct = false;\n"
149 " if (%d && correct)\n"
150 " correct = select.children[values.length].tagName === 'OPTGROUP';\n"
152 "})()", select_id
, values
, check_separator
);
153 const bool execute_status
= checker
.GetBool(expression
);
154 EXPECT_TRUE(execute_status
) << expression
;
155 return execute_status
;
158 bool OobeLocalizationTest::VerifyOptionExists(const char* select_id
,
160 const std::string expression
= base::StringPrintf(
162 " var select = document.querySelector('#%s');\n"
165 " for (var i = 0; i < select.options.length; i++) {\n"
166 " if (select.options[i].value == '%s')\n"
170 "})()", select_id
, value
);
171 const bool execute_status
= checker
.GetBool(expression
);
172 EXPECT_TRUE(execute_status
) << expression
;
173 return execute_status
;
176 std::string
OobeLocalizationTest::DumpOptions(const char* select_id
) {
177 const std::string expression
= base::StringPrintf(
180 " var selector = '#%s';\n"
181 " var divider = ',';\n"
182 " var select = document.querySelector(selector);\n"
184 " return 'document.querySelector(' + selector + ') failed.';\n"
185 " var dumpOptgroup = function(group) {\n"
186 " var result = '';\n"
187 " for (var i = 0; i < group.children.length; i++) {\n"
189 " result += divider;\n"
191 " if (group.children[i].value) {\n"
192 " result += group.children[i].value;\n"
194 " result += '__NO_VALUE__';\n"
199 " var result = '';\n"
200 " if (select.selectedIndex != 0) {\n"
201 " result += '(selectedIndex=' + select.selectedIndex + \n"
202 " ', selected \"' + select.options[select.selectedIndex].value +\n"
205 " var children = select.children;\n"
206 " for (var i = 0; i < children.length; i++) {\n"
208 " result += divider;\n"
210 " if (children[i].value) {\n"
211 " result += children[i].value;\n"
212 " } else if (children[i].tagName === 'OPTGROUP') {\n"
213 " result += '[' + dumpOptgroup(children[i]) + ']';\n"
215 " result += '__NO_VALUE__';\n"
221 return checker
.GetString(expression
);
224 std::string
TranslateXKB2Extension(const std::string
& src
) {
225 std::string
result(src
);
226 if (!extension_ime_util::UseWrappedExtensionKeyboardLayouts())
228 // Modifies the expected keyboard select control options for the new
229 // extension based xkb id.
231 std::string repl_old
= "xkb:";
232 std::string repl_new
=
233 extension_ime_util::GetInputMethodIDByKeyboardLayout("xkb:");
234 while ((pos
= result
.find(repl_old
, pos
)) != std::string::npos
) {
235 result
.replace(pos
, repl_old
.length(), repl_new
);
236 pos
+= repl_new
.length();
241 void OobeLocalizationTest::RunLocalizationTest(
242 const std::string
& initial_locale
,
243 const std::string
& keyboard_layout
,
244 const std::string
& expected_locale
,
245 const std::string
& expected_keyboard_layout
,
246 const std::string
& expected_keyboard_select_control
) {
247 statistics_provider_
->set_locale(initial_locale
);
248 statistics_provider_
->set_keyboard_layout(keyboard_layout
);
250 // Initialize StartupCustomizationDocument with fake statistics provider.
251 StartupCustomizationDocument::GetInstance()->Init(
252 statistics_provider_
.get());
254 g_browser_process
->local_state()->SetString(
255 prefs::kHardwareKeyboardLayout
, keyboard_layout
);
257 input_method::InputMethodManager::Get()
258 ->GetInputMethodUtil()
259 ->InitXkbInputMethodsForTesting();
261 const std::string expected_keyboard_select
=
262 TranslateXKB2Extension(expected_keyboard_select_control
);
264 // Bring up the OOBE network screen.
265 chromeos::ShowLoginWizard(chromeos::WizardController::kNetworkScreenName
);
266 content::WindowedNotificationObserver(
267 chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE
,
268 content::NotificationService::AllSources()).Wait();
270 checker
.set_web_contents(static_cast<chromeos::LoginDisplayHostImpl
*>(
271 chromeos::LoginDisplayHostImpl::default_host())->
272 GetOobeUI()->web_ui()->GetWebContents());
274 if (!VerifyInitialOptions(kLocaleSelect
, expected_locale
.c_str(), true)) {
275 LOG(ERROR
) << "Actual value of " << kLocaleSelect
<< ":\n"
276 << DumpOptions(kLocaleSelect
);
278 if (!VerifyInitialOptions(
280 TranslateXKB2Extension(expected_keyboard_layout
).c_str(),
282 LOG(ERROR
) << "Actual value of " << kKeyboardSelect
<< ":\n"
283 << DumpOptions(kKeyboardSelect
);
286 // Make sure we have a fallback keyboard.
287 if (!VerifyOptionExists(kKeyboardSelect
,
288 extension_ime_util::GetInputMethodIDByKeyboardLayout(
289 kUSLayout
).c_str())) {
290 LOG(ERROR
) << "Actual value of " << kKeyboardSelect
<< ":\n"
291 << DumpOptions(kKeyboardSelect
);
294 // Note, that sort order is locale-specific, but is unlikely to change.
295 // Especially for keyboard layouts.
296 EXPECT_EQ(expected_keyboard_select
, DumpOptions(kKeyboardSelect
));
298 // Shut down the display host.
299 chromeos::LoginDisplayHostImpl::default_host()->Finalize();
300 base::MessageLoopForUI::current()->RunUntilIdle();
302 // Clear the locale pref so the statistics provider is pinged next time.
303 g_browser_process
->local_state()->SetString(prefs::kApplicationLocale
,
307 IN_PROC_BROWSER_TEST_F(OobeLocalizationTest
, NetworkScreenNonLatin
) {
308 // For a non-Latin keyboard layout like Russian, we expect to see the US
310 RunLocalizationTest("ru", "xkb:ru::rus",
314 RunLocalizationTest("ru", "xkb:us::eng,xkb:ru::rus",
318 // IMEs do not load at OOBE, so we just expect to see the (Latin) Japanese
320 RunLocalizationTest("ja", "xkb:jp::jpn",
322 "xkb:jp::jpn,[xkb:us::eng]");
325 IN_PROC_BROWSER_TEST_F(OobeLocalizationTest
, NetworkScreenKeyboardLayout
) {
326 // We don't use the Icelandic locale but the Icelandic keyboard layout
327 // should still be selected when specified as the default.
328 RunLocalizationTest("en-US", "xkb:is::ice",
329 "en-US", "xkb:is::ice",
331 "xkb:us::eng,xkb:us:intl:eng,xkb:us:altgr-intl:eng,"
332 "xkb:us:dvorak:eng,xkb:us:colemak:eng]");
335 IN_PROC_BROWSER_TEST_F(OobeLocalizationTest
, NetworkScreenFullLatin
) {
336 // French Swiss keyboard.
337 RunLocalizationTest("fr", "xkb:ch:fr:fra",
338 "fr", "xkb:ch:fr:fra",
340 "xkb:fr::fra,xkb:be::fra,xkb:ca::fra,"
341 "xkb:ca:multix:fra,xkb:us::eng]");
343 // German Swiss keyboard.
344 RunLocalizationTest("de", "xkb:ch::ger",
347 "xkb:de::ger,xkb:de:neo:ger,xkb:be::ger,xkb:us::eng"
351 IN_PROC_BROWSER_TEST_F(OobeLocalizationTest
, NetworkScreenMultipleLocales
) {
352 RunLocalizationTest("es,en-US,nl", "xkb:be::nld",
353 "es,en-US,nl", "xkb:be::nld",
354 "xkb:be::nld,[xkb:es::spa,xkb:latam::spa,xkb:us::eng]");
356 RunLocalizationTest("ru,de", "xkb:ru::rus",
361 IN_PROC_BROWSER_TEST_F(OobeLocalizationTest
, NetworkScreenRegionalLocales
) {
362 // Syntetic example to test correct merging of different locales.
363 RunLocalizationTest("fr-CH,it-CH,de-CH",
364 "xkb:fr::fra,xkb:it::ita,xkb:de::ger",
367 "xkb:fr::fra,xkb:it::ita,xkb:de::ger,["
368 "xkb:be::fra,xkb:ca::fra,xkb:ch:fr:fra,"
369 "xkb:ca:multix:fra,xkb:us::eng"
371 // Another syntetic example. Check that british keyboard is available.
372 RunLocalizationTest("en-AU",
376 "xkb:us::eng,[xkb:gb:extd:eng,xkb:gb:dvorak:eng]");
379 } // namespace chromeos