OLD | NEW |
1 // Copyright 2012 The Chromium Authors. All rights reserved. | 1 // Copyright 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include <set> | 5 #include <set> |
6 #include <string> | 6 #include <string> |
7 #include <utility> | 7 #include <utility> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "testing/gtest/include/gtest/gtest.h" | 10 #include "testing/gtest/include/gtest/gtest.h" |
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
43 #include "components/autofill/core/browser/webdata/autocomplete_syncable_service
.h" | 43 #include "components/autofill/core/browser/webdata/autocomplete_syncable_service
.h" |
44 #include "components/autofill/core/browser/webdata/autofill_change.h" | 44 #include "components/autofill/core/browser/webdata/autofill_change.h" |
45 #include "components/autofill/core/browser/webdata/autofill_entry.h" | 45 #include "components/autofill/core/browser/webdata/autofill_entry.h" |
46 #include "components/autofill/core/browser/webdata/autofill_profile_syncable_ser
vice.h" | 46 #include "components/autofill/core/browser/webdata/autofill_profile_syncable_ser
vice.h" |
47 #include "components/autofill/core/browser/webdata/autofill_table.h" | 47 #include "components/autofill/core/browser/webdata/autofill_table.h" |
48 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" | 48 #include "components/autofill/core/browser/webdata/autofill_webdata_service.h" |
49 #include "components/signin/core/browser/account_tracker_service.h" | 49 #include "components/signin/core/browser/account_tracker_service.h" |
50 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" | 50 #include "components/signin/core/browser/fake_profile_oauth2_token_service.h" |
51 #include "components/signin/core/browser/signin_manager.h" | 51 #include "components/signin/core/browser/signin_manager.h" |
52 #include "components/sync_driver/data_type_controller.h" | 52 #include "components/sync_driver/data_type_controller.h" |
53 #include "components/sync_driver/profile_sync_components_factory.h" | 53 #include "components/sync_driver/fake_sync_client.h" |
54 #include "components/webdata/common/web_database.h" | 54 #include "components/webdata/common/web_database.h" |
55 #include "components/webdata_services/web_data_service_test_util.h" | 55 #include "components/webdata_services/web_data_service_test_util.h" |
56 #include "content/public/test/test_browser_thread.h" | 56 #include "content/public/test/test_browser_thread.h" |
57 #include "google_apis/gaia/gaia_constants.h" | 57 #include "google_apis/gaia/gaia_constants.h" |
58 #include "sync/internal_api/public/base/model_type.h" | 58 #include "sync/internal_api/public/base/model_type.h" |
59 #include "sync/internal_api/public/data_type_debug_info_listener.h" | 59 #include "sync/internal_api/public/data_type_debug_info_listener.h" |
60 #include "sync/internal_api/public/read_node.h" | 60 #include "sync/internal_api/public/read_node.h" |
61 #include "sync/internal_api/public/read_transaction.h" | 61 #include "sync/internal_api/public/read_transaction.h" |
62 #include "sync/internal_api/public/write_node.h" | 62 #include "sync/internal_api/public/write_node.h" |
63 #include "sync/internal_api/public/write_transaction.h" | 63 #include "sync/internal_api/public/write_transaction.h" |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
138 MOCK_METHOD1(AddAutofillProfile, | 138 MOCK_METHOD1(AddAutofillProfile, |
139 bool(const AutofillProfile&)); // NOLINT | 139 bool(const AutofillProfile&)); // NOLINT |
140 MOCK_METHOD1(RemoveAutofillProfile, | 140 MOCK_METHOD1(RemoveAutofillProfile, |
141 bool(const std::string&)); // NOLINT | 141 bool(const std::string&)); // NOLINT |
142 }; | 142 }; |
143 | 143 |
144 MATCHER_P(MatchProfiles, profile, "") { | 144 MATCHER_P(MatchProfiles, profile, "") { |
145 return (profile.Compare(arg) == 0); | 145 return (profile.Compare(arg) == 0); |
146 } | 146 } |
147 | 147 |
| 148 class TestSyncClient : public sync_driver::FakeSyncClient { |
| 149 public: |
| 150 TestSyncClient(PersonalDataManager* pdm, |
| 151 const scoped_refptr<AutofillWebDataService>& web_data_service) |
| 152 : pdm_(pdm), |
| 153 web_data_service_(web_data_service) {} |
| 154 ~TestSyncClient() override {} |
| 155 |
| 156 // FakeSyncClient overrides. |
| 157 autofill::PersonalDataManager* GetPersonalDataManager() override { |
| 158 return pdm_; |
| 159 } |
| 160 scoped_refptr<autofill::AutofillWebDataService> GetWebDataService() override { |
| 161 return web_data_service_; |
| 162 } |
| 163 base::WeakPtr<syncer::SyncableService> GetSyncableServiceForType( |
| 164 syncer::ModelType type) override { |
| 165 DCHECK(type == AUTOFILL || type == AUTOFILL_PROFILE); |
| 166 if (type == AUTOFILL) { |
| 167 return AutocompleteSyncableService::FromWebDataService( |
| 168 web_data_service_.get())->AsWeakPtr(); |
| 169 } else { |
| 170 return AutofillProfileSyncableService::FromWebDataService( |
| 171 web_data_service_.get())->AsWeakPtr(); |
| 172 } |
| 173 } |
| 174 |
| 175 private: |
| 176 PersonalDataManager* pdm_; |
| 177 scoped_refptr<AutofillWebDataService> web_data_service_; |
| 178 }; |
| 179 |
148 class WebDatabaseFake : public WebDatabase { | 180 class WebDatabaseFake : public WebDatabase { |
149 public: | 181 public: |
150 explicit WebDatabaseFake(AutofillTable* autofill_table) { | 182 explicit WebDatabaseFake(AutofillTable* autofill_table) { |
151 AddTable(autofill_table); | 183 AddTable(autofill_table); |
152 } | 184 } |
153 }; | 185 }; |
154 | 186 |
155 class MockAutofillBackend : public autofill::AutofillWebDataBackend { | 187 class MockAutofillBackend : public autofill::AutofillWebDataBackend { |
156 public: | 188 public: |
157 MockAutofillBackend( | 189 MockAutofillBackend( |
(...skipping 191 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
349 ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) { | 381 ACTION_P(ReturnNewDataTypeManagerWithDebugListener, debug_listener) { |
350 return new sync_driver::DataTypeManagerImpl( | 382 return new sync_driver::DataTypeManagerImpl( |
351 base::Closure(), | 383 base::Closure(), |
352 debug_listener, | 384 debug_listener, |
353 arg1, | 385 arg1, |
354 arg2, | 386 arg2, |
355 arg3, | 387 arg3, |
356 arg4); | 388 arg4); |
357 } | 389 } |
358 | 390 |
359 ACTION_P(MakeAutofillProfileSyncComponents, wds) { | |
360 EXPECT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::DB)); | |
361 if (!BrowserThread::CurrentlyOn(BrowserThread::DB)) | |
362 return base::WeakPtr<syncer::SyncableService>(); | |
363 return AutofillProfileSyncableService::FromWebDataService(wds)->AsWeakPtr(); | |
364 } | |
365 | |
366 class AbstractAutofillFactory { | |
367 public: | |
368 virtual DataTypeController* CreateDataTypeController( | |
369 ProfileSyncComponentsFactory* factory, | |
370 TestingProfile* profile, | |
371 ProfileSyncService* service) = 0; | |
372 virtual void SetExpectation(ProfileSyncComponentsFactoryMock* factory, | |
373 ProfileSyncService* service, | |
374 AutofillWebDataService* wds, | |
375 DataTypeController* dtc) = 0; | |
376 virtual ~AbstractAutofillFactory() {} | |
377 }; | |
378 | |
379 class AutofillEntryFactory : public AbstractAutofillFactory { | |
380 public: | |
381 DataTypeController* CreateDataTypeController( | |
382 ProfileSyncComponentsFactory* factory, | |
383 TestingProfile* profile, | |
384 ProfileSyncService* service) override { | |
385 return new AutofillDataTypeController(factory, profile); | |
386 } | |
387 | |
388 void SetExpectation(ProfileSyncComponentsFactoryMock* factory, | |
389 ProfileSyncService* service, | |
390 AutofillWebDataService* wds, | |
391 DataTypeController* dtc) override { | |
392 EXPECT_CALL(*factory, GetSyncableServiceForType(syncer::AUTOFILL)). | |
393 WillOnce(MakeAutocompleteSyncComponents(wds)); | |
394 } | |
395 }; | |
396 | |
397 class AutofillProfileFactory : public AbstractAutofillFactory { | |
398 public: | |
399 DataTypeController* CreateDataTypeController( | |
400 ProfileSyncComponentsFactory* factory, | |
401 TestingProfile* profile, | |
402 ProfileSyncService* service) override { | |
403 return new AutofillProfileDataTypeController(factory, profile); | |
404 } | |
405 | |
406 void SetExpectation(ProfileSyncComponentsFactoryMock* factory, | |
407 ProfileSyncService* service, | |
408 AutofillWebDataService* wds, | |
409 DataTypeController* dtc) override { | |
410 EXPECT_CALL(*factory, | |
411 GetSyncableServiceForType(syncer::AUTOFILL_PROFILE)). | |
412 WillOnce(MakeAutofillProfileSyncComponents(wds)); | |
413 } | |
414 }; | |
415 | |
416 class MockPersonalDataManager : public PersonalDataManager { | 391 class MockPersonalDataManager : public PersonalDataManager { |
417 public: | 392 public: |
418 MockPersonalDataManager() : PersonalDataManager("en-US") {} | 393 MockPersonalDataManager() : PersonalDataManager("en-US") {} |
419 MOCK_CONST_METHOD0(IsDataLoaded, bool()); | 394 MOCK_CONST_METHOD0(IsDataLoaded, bool()); |
420 MOCK_METHOD0(LoadProfiles, void()); | 395 MOCK_METHOD0(LoadProfiles, void()); |
421 MOCK_METHOD0(LoadCreditCards, void()); | 396 MOCK_METHOD0(LoadCreditCards, void()); |
422 MOCK_METHOD0(Refresh, void()); | 397 MOCK_METHOD0(Refresh, void()); |
423 | 398 |
424 static scoped_ptr<KeyedService> Build(content::BrowserContext* profile) { | 399 static scoped_ptr<KeyedService> Build(content::BrowserContext* profile) { |
425 return make_scoped_ptr(new MockPersonalDataManager()); | 400 return make_scoped_ptr(new MockPersonalDataManager()); |
(...skipping 13 matching lines...) Expand all Loading... |
439 association_stats_ = configuration_stats[0].association_stats; | 414 association_stats_ = configuration_stats[0].association_stats; |
440 } | 415 } |
441 | 416 |
442 protected: | 417 protected: |
443 ProfileSyncServiceAutofillTest() | 418 ProfileSyncServiceAutofillTest() |
444 : profile_manager_(TestingBrowserProcess::GetGlobal()), | 419 : profile_manager_(TestingBrowserProcess::GetGlobal()), |
445 debug_ptr_factory_(this) { | 420 debug_ptr_factory_(this) { |
446 } | 421 } |
447 ~ProfileSyncServiceAutofillTest() override {} | 422 ~ProfileSyncServiceAutofillTest() override {} |
448 | 423 |
449 AutofillProfileFactory profile_factory_; | |
450 AutofillEntryFactory entry_factory_; | |
451 | |
452 AbstractAutofillFactory* GetFactory(syncer::ModelType type) { | |
453 if (type == syncer::AUTOFILL) { | |
454 return &entry_factory_; | |
455 } else if (type == syncer::AUTOFILL_PROFILE) { | |
456 return &profile_factory_; | |
457 } else { | |
458 NOTREACHED(); | |
459 return NULL; | |
460 } | |
461 } | |
462 | |
463 void SetUp() override { | 424 void SetUp() override { |
464 AbstractProfileSyncServiceTest::SetUp(); | 425 AbstractProfileSyncServiceTest::SetUp(); |
465 ASSERT_TRUE(profile_manager_.SetUp()); | 426 ASSERT_TRUE(profile_manager_.SetUp()); |
466 TestingProfile::TestingFactories testing_factories; | 427 TestingProfile::TestingFactories testing_factories; |
467 testing_factories.push_back(std::make_pair( | 428 testing_factories.push_back(std::make_pair( |
468 ProfileOAuth2TokenServiceFactory::GetInstance(), | 429 ProfileOAuth2TokenServiceFactory::GetInstance(), |
469 BuildAutoIssuingFakeProfileOAuth2TokenService)); | 430 BuildAutoIssuingFakeProfileOAuth2TokenService)); |
470 profile_ = profile_manager_.CreateTestingProfile( | 431 profile_ = profile_manager_.CreateTestingProfile( |
471 kTestProfileName, | 432 kTestProfileName, |
472 scoped_ptr<PrefServiceSyncable>(), | 433 scoped_ptr<PrefServiceSyncable>(), |
(...skipping 20 matching lines...) Expand all Loading... |
493 | 454 |
494 personal_data_manager_->Init( | 455 personal_data_manager_->Init( |
495 WebDataServiceFactory::GetAutofillWebDataForProfile( | 456 WebDataServiceFactory::GetAutofillWebDataForProfile( |
496 profile_, ServiceAccessType::EXPLICIT_ACCESS), | 457 profile_, ServiceAccessType::EXPLICIT_ACCESS), |
497 profile_->GetPrefs(), | 458 profile_->GetPrefs(), |
498 AccountTrackerServiceFactory::GetForProfile(profile_), | 459 AccountTrackerServiceFactory::GetForProfile(profile_), |
499 profile_->IsOffTheRecord()); | 460 profile_->IsOffTheRecord()); |
500 | 461 |
501 web_data_service_->StartSyncableService(); | 462 web_data_service_->StartSyncableService(); |
502 | 463 |
| 464 sync_client_.reset(new TestSyncClient(personal_data_manager_, |
| 465 web_data_service_)); |
| 466 |
503 // When UpdateAutofillEntries() is called with an empty list, the return | 467 // When UpdateAutofillEntries() is called with an empty list, the return |
504 // value should be |true|, rather than the default of |false|. | 468 // value should be |true|, rather than the default of |false|. |
505 std::vector<AutofillEntry> empty; | 469 std::vector<AutofillEntry> empty; |
506 EXPECT_CALL(autofill_table_, UpdateAutofillEntries(empty)) | 470 EXPECT_CALL(autofill_table_, UpdateAutofillEntries(empty)) |
507 .WillRepeatedly(Return(true)); | 471 .WillRepeatedly(Return(true)); |
508 } | 472 } |
509 | 473 |
510 void TearDown() override { | 474 void TearDown() override { |
511 // Note: The tear down order is important. | 475 // Note: The tear down order is important. |
512 ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(profile_, NULL); | 476 ProfileSyncServiceFactory::GetInstance()->SetTestingFactory(profile_, NULL); |
(...skipping 11 matching lines...) Expand all Loading... |
524 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); | 488 syncer::ReadTransaction trans(FROM_HERE, sync_service_->GetUserShare()); |
525 syncer::ReadNode node(&trans); | 489 syncer::ReadNode node(&trans); |
526 if (node.InitTypeRoot(type) != syncer::BaseNode::INIT_OK) | 490 if (node.InitTypeRoot(type) != syncer::BaseNode::INIT_OK) |
527 return 0; | 491 return 0; |
528 return node.GetTotalNodeCount() - 1; | 492 return node.GetTotalNodeCount() - 1; |
529 } | 493 } |
530 | 494 |
531 void StartSyncService(const base::Closure& callback, | 495 void StartSyncService(const base::Closure& callback, |
532 bool will_fail_association, | 496 bool will_fail_association, |
533 syncer::ModelType type) { | 497 syncer::ModelType type) { |
534 AbstractAutofillFactory* factory = GetFactory(type); | |
535 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_); | 498 SigninManagerBase* signin = SigninManagerFactory::GetForProfile(profile_); |
536 signin->SetAuthenticatedAccountInfo("12345", "test_user@gmail.com"); | 499 signin->SetAuthenticatedAccountInfo("12345", "test_user@gmail.com"); |
537 sync_service_ = TestProfileSyncService::BuildAutoStartAsyncInit(profile_, | 500 sync_service_ = TestProfileSyncService::BuildAutoStartAsyncInit(profile_, |
538 callback); | 501 callback); |
539 | 502 |
540 ProfileSyncComponentsFactoryMock* components = | 503 ProfileSyncComponentsFactoryMock* components = |
541 sync_service_->components_factory_mock(); | 504 sync_service_->components_factory_mock(); |
542 DataTypeController* data_type_controller = | |
543 factory->CreateDataTypeController(components, profile_, sync_service_); | |
544 factory->SetExpectation(components, | |
545 sync_service_, | |
546 web_data_service_.get(), | |
547 data_type_controller); | |
548 | 505 |
549 EXPECT_CALL(*components, CreateDataTypeManager(_, _, _, _, _)). | 506 EXPECT_CALL(*components, CreateDataTypeManager(_, _, _, _, _)). |
550 WillOnce(ReturnNewDataTypeManagerWithDebugListener( | 507 WillOnce(ReturnNewDataTypeManagerWithDebugListener( |
551 syncer::MakeWeakHandle(debug_ptr_factory_.GetWeakPtr()))); | 508 syncer::MakeWeakHandle(debug_ptr_factory_.GetWeakPtr()))); |
552 | 509 |
553 EXPECT_CALL(*personal_data_manager_, IsDataLoaded()). | 510 EXPECT_CALL(*personal_data_manager_, IsDataLoaded()). |
554 WillRepeatedly(Return(true)); | 511 WillRepeatedly(Return(true)); |
555 | 512 |
556 // We need tokens to get the tests going | 513 // We need tokens to get the tests going |
557 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) | 514 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_) |
558 ->UpdateCredentials(signin->GetAuthenticatedAccountId(), | 515 ->UpdateCredentials(signin->GetAuthenticatedAccountId(), |
559 "oauth2_login_token"); | 516 "oauth2_login_token"); |
560 | 517 |
561 sync_service_->RegisterDataTypeController(data_type_controller); | 518 sync_service_->RegisterDataTypeController(CreateDataTypeController(type)); |
562 sync_service_->Initialize(); | 519 sync_service_->Initialize(); |
563 base::MessageLoop::current()->Run(); | 520 base::MessageLoop::current()->Run(); |
564 | 521 |
565 // It's possible this test triggered an unrecoverable error, in which case | 522 // It's possible this test triggered an unrecoverable error, in which case |
566 // we can't get the sync count. | 523 // we can't get the sync count. |
567 if (sync_service_->IsSyncActive()) { | 524 if (sync_service_->IsSyncActive()) { |
568 EXPECT_EQ(GetSyncCount(type), | 525 EXPECT_EQ(GetSyncCount(type), |
569 association_stats_.num_sync_items_after_association); | 526 association_stats_.num_sync_items_after_association); |
570 } | 527 } |
571 EXPECT_EQ(association_stats_.num_sync_items_after_association, | 528 EXPECT_EQ(association_stats_.num_sync_items_after_association, |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
696 AutofillKey(base::ASCIIToUTF16(name), base::ASCIIToUTF16(value)), | 653 AutofillKey(base::ASCIIToUTF16(name), base::ASCIIToUTF16(value)), |
697 date_created, date_last_used); | 654 date_created, date_last_used); |
698 } | 655 } |
699 | 656 |
700 static AutofillEntry MakeAutofillEntry(const char* name, | 657 static AutofillEntry MakeAutofillEntry(const char* name, |
701 const char* value, | 658 const char* value, |
702 int time_shift) { | 659 int time_shift) { |
703 return MakeAutofillEntry(name, value, time_shift, -1); | 660 return MakeAutofillEntry(name, value, time_shift, -1); |
704 } | 661 } |
705 | 662 |
| 663 DataTypeController* CreateDataTypeController(syncer::ModelType type) { |
| 664 DCHECK(type == AUTOFILL || type == AUTOFILL_PROFILE); |
| 665 if (type == AUTOFILL) |
| 666 return new AutofillDataTypeController(sync_client_.get()); |
| 667 else |
| 668 return new AutofillProfileDataTypeController(sync_client_.get()); |
| 669 } |
| 670 |
706 friend class AddAutofillHelper<AutofillEntry>; | 671 friend class AddAutofillHelper<AutofillEntry>; |
707 friend class AddAutofillHelper<AutofillProfile>; | 672 friend class AddAutofillHelper<AutofillProfile>; |
708 friend class FakeServerUpdater; | 673 friend class FakeServerUpdater; |
709 | 674 |
710 TestingProfileManager profile_manager_; | 675 TestingProfileManager profile_manager_; |
711 TestingProfile* profile_; | 676 TestingProfile* profile_; |
712 AutofillTableMock autofill_table_; | 677 AutofillTableMock autofill_table_; |
713 scoped_ptr<WebDatabaseFake> web_database_; | 678 scoped_ptr<WebDatabaseFake> web_database_; |
714 scoped_refptr<WebDataServiceFake> web_data_service_; | 679 scoped_refptr<WebDataServiceFake> web_data_service_; |
715 MockPersonalDataManager* personal_data_manager_; | 680 MockPersonalDataManager* personal_data_manager_; |
716 syncer::DataTypeAssociationStats association_stats_; | 681 syncer::DataTypeAssociationStats association_stats_; |
717 base::WeakPtrFactory<DataTypeDebugInfoListener> debug_ptr_factory_; | 682 base::WeakPtrFactory<DataTypeDebugInfoListener> debug_ptr_factory_; |
| 683 scoped_ptr<TestSyncClient> sync_client_; |
718 }; | 684 }; |
719 | 685 |
720 template <class T> | 686 template <class T> |
721 class AddAutofillHelper { | 687 class AddAutofillHelper { |
722 public: | 688 public: |
723 AddAutofillHelper(ProfileSyncServiceAutofillTest* test, | 689 AddAutofillHelper(ProfileSyncServiceAutofillTest* test, |
724 const std::vector<T>& entries) | 690 const std::vector<T>& entries) |
725 : callback_(base::Bind(&AddAutofillHelper::AddAutofillCallback, | 691 : callback_(base::Bind(&AddAutofillHelper::AddAutofillCallback, |
726 base::Unretained(this), test, entries)), | 692 base::Unretained(this), test, entries)), |
727 success_(false) { | 693 success_(false) { |
(...skipping 597 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1325 std::vector<AutofillEntry> sync_entries; | 1291 std::vector<AutofillEntry> sync_entries; |
1326 std::vector<AutofillProfile> sync_profiles; | 1292 std::vector<AutofillProfile> sync_profiles; |
1327 ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); | 1293 ASSERT_TRUE(GetAutofillEntriesFromSyncDB(&sync_entries, &sync_profiles)); |
1328 EXPECT_EQ(3U, sync_entries.size()); | 1294 EXPECT_EQ(3U, sync_entries.size()); |
1329 EXPECT_EQ(0U, sync_profiles.size()); | 1295 EXPECT_EQ(0U, sync_profiles.size()); |
1330 for (size_t i = 0; i < sync_entries.size(); i++) { | 1296 for (size_t i = 0; i < sync_entries.size(); i++) { |
1331 DVLOG(1) << "Entry " << i << ": " << sync_entries[i].key().name() | 1297 DVLOG(1) << "Entry " << i << ": " << sync_entries[i].key().name() |
1332 << ", " << sync_entries[i].key().value(); | 1298 << ", " << sync_entries[i].key().value(); |
1333 } | 1299 } |
1334 } | 1300 } |
OLD | NEW |