Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / ui / passwords / manage_passwords_state_unittest.cc
blobb36df633e2fbc3d2aefd916a477e017c278baf46
1 // Copyright 2015 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 "chrome/browser/ui/passwords/manage_passwords_state.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "components/password_manager/core/browser/password_form_manager.h"
9 #include "components/password_manager/core/browser/stub_password_manager_client.h"
10 #include "components/password_manager/core/common/credential_manager_types.h"
11 #include "testing/gmock/include/gmock/gmock.h"
12 #include "testing/gtest/include/gtest/gtest.h"
14 using ::testing::_;
15 using ::testing::Contains;
16 using ::testing::ElementsAre;
17 using ::testing::IsEmpty;
18 using ::testing::Not;
19 using ::testing::Pointee;
20 using ::testing::UnorderedElementsAre;
22 namespace {
24 class ManagePasswordsStateTest : public testing::Test {
25 public:
26 void SetUp() override {
27 test_local_form_.origin = GURL("http://example.com");
28 test_local_form_.username_value = base::ASCIIToUTF16("username");
29 test_local_form_.password_value = base::ASCIIToUTF16("12345");
31 test_submitted_form_ = test_local_form_;
32 test_submitted_form_.username_value = base::ASCIIToUTF16("new one");
33 test_submitted_form_.password_value = base::ASCIIToUTF16("asdfjkl;");
35 test_federated_form_.origin = GURL("https://idp.com");
36 test_federated_form_.username_value = base::ASCIIToUTF16("username");
38 passwords_data_.set_client(&client_);
41 autofill::PasswordForm& test_local_form() { return test_local_form_; }
42 autofill::PasswordForm& test_submitted_form() { return test_submitted_form_; }
43 autofill::PasswordForm& test_federated_form() { return test_federated_form_; }
44 ManagePasswordsState& passwords_data() { return passwords_data_; }
46 // Returns a PasswordFormManager containing test_local_form() as a best match.
47 scoped_ptr<password_manager::PasswordFormManager> CreateFormManager();
49 // Pushes irrelevant updates to |passwords_data_| and checks that they don't
50 // affect the state.
51 void TestNoisyUpdates();
53 // Pushes both relevant and irrelevant updates to |passwords_data_|.
54 void TestAllUpdates();
56 // Pushes a blacklisted form and checks that it doesn't affect the state.
57 void TestBlacklistedUpdates();
59 MOCK_METHOD1(OnChooseCredential,
60 void(const password_manager::CredentialInfo&));
62 private:
63 password_manager::StubPasswordManagerClient client_;
65 ManagePasswordsState passwords_data_;
66 autofill::PasswordForm test_local_form_;
67 autofill::PasswordForm test_submitted_form_;
68 autofill::PasswordForm test_federated_form_;
71 scoped_ptr<password_manager::PasswordFormManager>
72 ManagePasswordsStateTest::CreateFormManager() {
73 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
74 new password_manager::PasswordFormManager(
75 nullptr, &client_,
76 base::WeakPtr<password_manager::PasswordManagerDriver>(),
77 test_local_form(), false));
78 test_form_manager->SimulateFetchMatchingLoginsFromPasswordStore();
79 ScopedVector<autofill::PasswordForm> stored_forms;
80 stored_forms.push_back(new autofill::PasswordForm(test_local_form()));
81 test_form_manager->OnGetPasswordStoreResults(stored_forms.Pass());
82 EXPECT_EQ(1u, test_form_manager->best_matches().size());
83 EXPECT_EQ(test_local_form(),
84 *test_form_manager->best_matches().begin()->second);
85 return test_form_manager.Pass();
88 void ManagePasswordsStateTest::TestNoisyUpdates() {
89 const std::vector<const autofill::PasswordForm*> forms =
90 passwords_data_.GetCurrentForms();
91 const std::vector<const autofill::PasswordForm*> federated_forms =
92 passwords_data_.federated_credentials_forms();
93 const password_manager::ui::State state = passwords_data_.state();
94 const GURL origin = passwords_data_.origin();
96 // Push "Add".
97 autofill::PasswordForm form;
98 form.origin = GURL("http://3rdparty.com");
99 form.username_value = base::ASCIIToUTF16("username");
100 form.password_value = base::ASCIIToUTF16("12345");
101 password_manager::PasswordStoreChange change(
102 password_manager::PasswordStoreChange::ADD, form);
103 password_manager::PasswordStoreChangeList list(1, change);
104 passwords_data().ProcessLoginsChanged(list);
105 EXPECT_EQ(forms, passwords_data().GetCurrentForms());
106 EXPECT_EQ(federated_forms, passwords_data().federated_credentials_forms());
107 EXPECT_EQ(state, passwords_data().state());
108 EXPECT_EQ(origin, passwords_data().origin());
110 // Update the form.
111 form.password_value = base::ASCIIToUTF16("password");
112 list[0] = password_manager::PasswordStoreChange(
113 password_manager::PasswordStoreChange::UPDATE, form);
114 passwords_data().ProcessLoginsChanged(list);
115 EXPECT_EQ(forms, passwords_data().GetCurrentForms());
116 EXPECT_EQ(federated_forms, passwords_data().federated_credentials_forms());
117 EXPECT_EQ(state, passwords_data().state());
118 EXPECT_EQ(origin, passwords_data().origin());
120 // Delete the form.
121 list[0] = password_manager::PasswordStoreChange(
122 password_manager::PasswordStoreChange::REMOVE, form);
123 passwords_data().ProcessLoginsChanged(list);
124 EXPECT_EQ(forms, passwords_data().GetCurrentForms());
125 EXPECT_EQ(federated_forms, passwords_data().federated_credentials_forms());
126 EXPECT_EQ(state, passwords_data().state());
127 EXPECT_EQ(origin, passwords_data().origin());
130 void ManagePasswordsStateTest::TestAllUpdates() {
131 const std::vector<const autofill::PasswordForm*> forms =
132 passwords_data_.GetCurrentForms();
133 const std::vector<const autofill::PasswordForm*> federated_forms =
134 passwords_data_.federated_credentials_forms();
135 const password_manager::ui::State state = passwords_data_.state();
136 const GURL origin = passwords_data_.origin();
137 EXPECT_NE(GURL::EmptyGURL(), origin);
139 // Push "Add".
140 autofill::PasswordForm form;
141 form.origin = origin;
142 form.username_value = base::ASCIIToUTF16("user15");
143 form.password_value = base::ASCIIToUTF16("12345");
144 password_manager::PasswordStoreChange change(
145 password_manager::PasswordStoreChange::ADD, form);
146 password_manager::PasswordStoreChangeList list(1, change);
147 passwords_data().ProcessLoginsChanged(list);
148 EXPECT_THAT(passwords_data().GetCurrentForms(), Contains(Pointee(form)));
149 EXPECT_EQ(federated_forms, passwords_data().federated_credentials_forms());
150 EXPECT_EQ(state, passwords_data().state());
151 EXPECT_EQ(origin, passwords_data().origin());
153 // Update the form.
154 form.password_value = base::ASCIIToUTF16("password");
155 list[0] = password_manager::PasswordStoreChange(
156 password_manager::PasswordStoreChange::UPDATE, form);
157 passwords_data().ProcessLoginsChanged(list);
158 EXPECT_THAT(passwords_data().GetCurrentForms(), Contains(Pointee(form)));
159 EXPECT_EQ(federated_forms, passwords_data().federated_credentials_forms());
160 EXPECT_EQ(state, passwords_data().state());
161 EXPECT_EQ(origin, passwords_data().origin());
163 // Delete the form.
164 list[0] = password_manager::PasswordStoreChange(
165 password_manager::PasswordStoreChange::REMOVE, form);
166 passwords_data().ProcessLoginsChanged(list);
167 EXPECT_EQ(forms, passwords_data().GetCurrentForms());
168 EXPECT_EQ(federated_forms, passwords_data().federated_credentials_forms());
169 EXPECT_EQ(state, passwords_data().state());
170 EXPECT_EQ(origin, passwords_data().origin());
172 TestNoisyUpdates();
175 void ManagePasswordsStateTest::TestBlacklistedUpdates() {
176 const std::vector<const autofill::PasswordForm*> forms =
177 passwords_data_.GetCurrentForms();
178 const std::vector<const autofill::PasswordForm*> federated_forms =
179 passwords_data_.federated_credentials_forms();
180 const password_manager::ui::State state = passwords_data_.state();
181 const GURL origin = passwords_data_.origin();
182 EXPECT_NE(GURL::EmptyGURL(), origin);
184 // Process the blacklisted form.
185 autofill::PasswordForm blacklisted;
186 blacklisted.blacklisted_by_user = true;
187 blacklisted.origin = origin;
188 password_manager::PasswordStoreChangeList list;
189 list.push_back(password_manager::PasswordStoreChange(
190 password_manager::PasswordStoreChange::ADD, blacklisted));
191 passwords_data().ProcessLoginsChanged(list);
192 EXPECT_EQ(forms, passwords_data().GetCurrentForms());
193 EXPECT_EQ(federated_forms, passwords_data().federated_credentials_forms());
194 EXPECT_EQ(state, passwords_data().state());
195 EXPECT_EQ(origin, passwords_data().origin());
197 // Delete the blacklisted form.
198 list[0] = password_manager::PasswordStoreChange(
199 password_manager::PasswordStoreChange::REMOVE, blacklisted);
200 passwords_data().ProcessLoginsChanged(list);
201 EXPECT_EQ(forms, passwords_data().GetCurrentForms());
202 EXPECT_EQ(federated_forms, passwords_data().federated_credentials_forms());
203 EXPECT_EQ(state, passwords_data().state());
204 EXPECT_EQ(origin, passwords_data().origin());
207 TEST_F(ManagePasswordsStateTest, DefaultState) {
208 EXPECT_THAT(passwords_data().GetCurrentForms(), IsEmpty());
209 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
210 EXPECT_EQ(password_manager::ui::INACTIVE_STATE, passwords_data().state());
211 EXPECT_EQ(GURL::EmptyGURL(), passwords_data().origin());
212 EXPECT_FALSE(passwords_data().form_manager());
214 TestNoisyUpdates();
217 TEST_F(ManagePasswordsStateTest, PasswordSubmitted) {
218 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
219 CreateFormManager());
220 test_form_manager->ProvisionallySave(
221 test_submitted_form(),
222 password_manager::PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
223 passwords_data().OnPendingPassword(test_form_manager.Pass());
225 EXPECT_THAT(passwords_data().GetCurrentForms(),
226 ElementsAre(Pointee(test_local_form())));
227 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
228 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
229 passwords_data().state());
230 EXPECT_EQ(test_submitted_form().origin, passwords_data().origin());
231 ASSERT_TRUE(passwords_data().form_manager());
232 EXPECT_EQ(test_submitted_form(),
233 passwords_data().form_manager()->pending_credentials());
234 TestAllUpdates();
237 TEST_F(ManagePasswordsStateTest, PasswordSaved) {
238 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
239 CreateFormManager());
240 test_form_manager->ProvisionallySave(
241 test_submitted_form(),
242 password_manager::PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
243 passwords_data().OnPendingPassword(test_form_manager.Pass());
244 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
245 passwords_data().state());
247 passwords_data().TransitionToState(password_manager::ui::MANAGE_STATE);
248 EXPECT_THAT(passwords_data().GetCurrentForms(),
249 ElementsAre(Pointee(test_local_form())));
250 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
251 EXPECT_EQ(password_manager::ui::MANAGE_STATE,
252 passwords_data().state());
253 EXPECT_EQ(test_submitted_form().origin, passwords_data().origin());
254 TestAllUpdates();
257 TEST_F(ManagePasswordsStateTest, OnRequestCredentials) {
258 ScopedVector<autofill::PasswordForm> local_credentials;
259 local_credentials.push_back(new autofill::PasswordForm(test_local_form()));
260 ScopedVector<autofill::PasswordForm> federated_credentials;
261 federated_credentials.push_back(
262 new autofill::PasswordForm(test_federated_form()));
263 const GURL origin = test_local_form().origin;
264 passwords_data().OnRequestCredentials(local_credentials.Pass(),
265 federated_credentials.Pass(), origin);
266 passwords_data().set_credentials_callback(
267 base::Bind(&ManagePasswordsStateTest::OnChooseCredential,
268 base::Unretained(this)));
269 EXPECT_THAT(passwords_data().GetCurrentForms(),
270 ElementsAre(Pointee(test_local_form())));
271 EXPECT_THAT(passwords_data().federated_credentials_forms(),
272 ElementsAre(Pointee(test_federated_form())));
273 EXPECT_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE,
274 passwords_data().state());
275 EXPECT_EQ(origin, passwords_data().origin());
276 TestAllUpdates();
278 password_manager::CredentialInfo credential_info(
279 test_local_form(),
280 password_manager::CredentialType::CREDENTIAL_TYPE_PASSWORD);
281 EXPECT_CALL(*this, OnChooseCredential(_))
282 .WillOnce(testing::SaveArg<0>(&credential_info));
283 passwords_data().TransitionToState(password_manager::ui::MANAGE_STATE);
284 EXPECT_EQ(password_manager::CredentialType::CREDENTIAL_TYPE_EMPTY,
285 credential_info.type);
286 EXPECT_TRUE(passwords_data().credentials_callback().is_null());
287 EXPECT_THAT(passwords_data().GetCurrentForms(),
288 ElementsAre(Pointee(test_local_form())));
289 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
290 EXPECT_EQ(password_manager::ui::MANAGE_STATE, passwords_data().state());
291 EXPECT_EQ(origin, passwords_data().origin());
292 TestAllUpdates();
295 TEST_F(ManagePasswordsStateTest, AutoSignin) {
296 ScopedVector<autofill::PasswordForm> local_credentials;
297 local_credentials.push_back(new autofill::PasswordForm(test_local_form()));
298 passwords_data().OnAutoSignin(local_credentials.Pass());
299 EXPECT_THAT(passwords_data().GetCurrentForms(),
300 ElementsAre(Pointee(test_local_form())));
301 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
302 EXPECT_EQ(password_manager::ui::AUTO_SIGNIN_STATE, passwords_data().state());
303 EXPECT_EQ(test_local_form().origin, passwords_data().origin());
304 TestAllUpdates();
306 passwords_data().TransitionToState(password_manager::ui::MANAGE_STATE);
307 EXPECT_THAT(passwords_data().GetCurrentForms(),
308 ElementsAre(Pointee(test_local_form())));
309 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
310 EXPECT_EQ(password_manager::ui::MANAGE_STATE, passwords_data().state());
311 EXPECT_EQ(test_local_form().origin, passwords_data().origin());
312 TestAllUpdates();
315 TEST_F(ManagePasswordsStateTest, AutomaticPasswordSave) {
316 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
317 CreateFormManager());
318 test_form_manager->ProvisionallySave(
319 test_submitted_form(),
320 password_manager::PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
322 passwords_data().OnAutomaticPasswordSave(test_form_manager.Pass());
323 EXPECT_EQ(password_manager::ui::CONFIRMATION_STATE, passwords_data().state());
324 EXPECT_EQ(test_submitted_form().origin, passwords_data().origin());
325 ASSERT_TRUE(passwords_data().form_manager());
326 EXPECT_EQ(test_submitted_form(),
327 passwords_data().form_manager()->pending_credentials());
328 TestAllUpdates();
330 passwords_data().TransitionToState(password_manager::ui::MANAGE_STATE);
331 EXPECT_THAT(passwords_data().GetCurrentForms(),
332 UnorderedElementsAre(Pointee(test_local_form()),
333 Pointee(test_submitted_form())));
334 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
335 EXPECT_EQ(password_manager::ui::MANAGE_STATE, passwords_data().state());
336 EXPECT_EQ(test_submitted_form().origin, passwords_data().origin());
337 TestAllUpdates();
340 TEST_F(ManagePasswordsStateTest, PasswordAutofilled) {
341 autofill::PasswordFormMap password_form_map;
342 password_form_map.insert(
343 test_local_form().username_value,
344 make_scoped_ptr(new autofill::PasswordForm(test_local_form())));
345 passwords_data().OnPasswordAutofilled(password_form_map);
347 EXPECT_THAT(passwords_data().GetCurrentForms(),
348 ElementsAre(Pointee(test_local_form())));
349 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
350 EXPECT_EQ(password_manager::ui::MANAGE_STATE, passwords_data().state());
351 EXPECT_EQ(test_local_form().origin, passwords_data().origin());
353 // |passwords_data| should hold a separate copy of test_local_form().
354 EXPECT_THAT(passwords_data().GetCurrentForms(),
355 Not(Contains(&test_local_form())));
356 TestAllUpdates();
359 TEST_F(ManagePasswordsStateTest, InactiveOnPSLMatched) {
360 autofill::PasswordForm psl_matched_test_form = test_local_form();
361 psl_matched_test_form.original_signon_realm = "http://pslmatched.example.com";
362 autofill::PasswordFormMap password_form_map;
363 password_form_map.insert(
364 psl_matched_test_form.username_value,
365 make_scoped_ptr(new autofill::PasswordForm(psl_matched_test_form)));
366 passwords_data().OnPasswordAutofilled(password_form_map);
368 EXPECT_THAT(passwords_data().GetCurrentForms(), IsEmpty());
369 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
370 EXPECT_EQ(password_manager::ui::INACTIVE_STATE, passwords_data().state());
371 EXPECT_EQ(GURL::EmptyGURL(), passwords_data().origin());
372 EXPECT_FALSE(passwords_data().form_manager());
375 TEST_F(ManagePasswordsStateTest, OnInactive) {
376 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
377 CreateFormManager());
378 test_form_manager->ProvisionallySave(
379 test_submitted_form(),
380 password_manager::PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
381 passwords_data().OnPendingPassword(test_form_manager.Pass());
382 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
383 passwords_data().state());
384 passwords_data().OnInactive();
385 EXPECT_THAT(passwords_data().GetCurrentForms(), IsEmpty());
386 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
387 EXPECT_EQ(password_manager::ui::INACTIVE_STATE, passwords_data().state());
388 EXPECT_EQ(GURL::EmptyGURL(), passwords_data().origin());
389 EXPECT_FALSE(passwords_data().form_manager());
390 TestNoisyUpdates();
393 TEST_F(ManagePasswordsStateTest, PendingPasswordAddBlacklisted) {
394 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
395 CreateFormManager());
396 test_form_manager->ProvisionallySave(
397 test_submitted_form(),
398 password_manager::PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
399 passwords_data().OnPendingPassword(test_form_manager.Pass());
400 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_STATE,
401 passwords_data().state());
403 TestBlacklistedUpdates();
406 TEST_F(ManagePasswordsStateTest, RequestCredentialsAddBlacklisted) {
407 ScopedVector<autofill::PasswordForm> local_credentials;
408 local_credentials.push_back(new autofill::PasswordForm(test_local_form()));
409 ScopedVector<autofill::PasswordForm> federated_credentials;
410 federated_credentials.push_back(
411 new autofill::PasswordForm(test_federated_form()));
412 const GURL origin = test_local_form().origin;
413 passwords_data().OnRequestCredentials(local_credentials.Pass(),
414 federated_credentials.Pass(), origin);
415 passwords_data().set_credentials_callback(
416 base::Bind(&ManagePasswordsStateTest::OnChooseCredential,
417 base::Unretained(this)));
418 EXPECT_EQ(password_manager::ui::CREDENTIAL_REQUEST_STATE,
419 passwords_data().state());
421 TestBlacklistedUpdates();
424 TEST_F(ManagePasswordsStateTest, AutoSigninAddBlacklisted) {
425 ScopedVector<autofill::PasswordForm> local_credentials;
426 local_credentials.push_back(new autofill::PasswordForm(test_local_form()));
427 passwords_data().OnAutoSignin(local_credentials.Pass());
428 EXPECT_EQ(password_manager::ui::AUTO_SIGNIN_STATE, passwords_data().state());
430 TestBlacklistedUpdates();
433 TEST_F(ManagePasswordsStateTest, AutomaticPasswordSaveAddBlacklisted) {
434 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
435 CreateFormManager());
436 test_form_manager->ProvisionallySave(
437 test_submitted_form(),
438 password_manager::PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
439 passwords_data().OnAutomaticPasswordSave(test_form_manager.Pass());
440 EXPECT_EQ(password_manager::ui::CONFIRMATION_STATE, passwords_data().state());
442 TestBlacklistedUpdates();
445 TEST_F(ManagePasswordsStateTest, BackgroundAutofilledAddBlacklisted) {
446 autofill::PasswordFormMap password_form_map;
447 password_form_map.insert(
448 test_local_form().username_value,
449 make_scoped_ptr(new autofill::PasswordForm(test_local_form())));
450 passwords_data().OnPasswordAutofilled(password_form_map);
451 EXPECT_EQ(password_manager::ui::MANAGE_STATE, passwords_data().state());
453 TestBlacklistedUpdates();
456 TEST_F(ManagePasswordsStateTest, PasswordUpdateAddBlacklisted) {
457 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
458 CreateFormManager());
459 test_form_manager->ProvisionallySave(
460 test_submitted_form(),
461 password_manager::PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
462 passwords_data().OnUpdatePassword(test_form_manager.Pass());
463 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_UPDATE_STATE,
464 passwords_data().state());
466 TestBlacklistedUpdates();
469 TEST_F(ManagePasswordsStateTest, PasswordUpdateSubmitted) {
470 scoped_ptr<password_manager::PasswordFormManager> test_form_manager(
471 CreateFormManager());
472 test_form_manager->ProvisionallySave(
473 test_submitted_form(),
474 password_manager::PasswordFormManager::IGNORE_OTHER_POSSIBLE_USERNAMES);
475 passwords_data().OnUpdatePassword(test_form_manager.Pass());
477 EXPECT_THAT(passwords_data().GetCurrentForms(),
478 ElementsAre(Pointee(test_local_form())));
479 EXPECT_THAT(passwords_data().federated_credentials_forms(), IsEmpty());
480 EXPECT_EQ(password_manager::ui::PENDING_PASSWORD_UPDATE_STATE,
481 passwords_data().state());
482 EXPECT_EQ(test_submitted_form().origin, passwords_data().origin());
483 ASSERT_TRUE(passwords_data().form_manager());
484 EXPECT_EQ(test_submitted_form(),
485 passwords_data().form_manager()->pending_credentials());
486 TestAllUpdates();
489 } // namespace