| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 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 // Syncer unit tests. Unfortunately a lot of these tests | 5 // Syncer unit tests. Unfortunately a lot of these tests |
| 6 // are outdated and need to be reworked and updated. | 6 // are outdated and need to be reworked and updated. |
| 7 | 7 |
| 8 #include <algorithm> | 8 #include <algorithm> |
| 9 #include <limits> | 9 #include <limits> |
| 10 #include <list> | 10 #include <list> |
| (...skipping 1706 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1717 mock_server_->AddUpdateDirectory(3, -80, "bad_parent", 10, 10); | 1717 mock_server_->AddUpdateDirectory(3, -80, "bad_parent", 10, 10); |
| 1718 | 1718 |
| 1719 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END); | 1719 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END); |
| 1720 StatusController* status = session_->mutable_status_controller(); | 1720 StatusController* status = session_->mutable_status_controller(); |
| 1721 | 1721 |
| 1722 // Id 3 should be in conflict now. | 1722 // Id 3 should be in conflict now. |
| 1723 EXPECT_EQ(1, status->TotalNumConflictingItems()); | 1723 EXPECT_EQ(1, status->TotalNumConflictingItems()); |
| 1724 { | 1724 { |
| 1725 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); | 1725 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); |
| 1726 ASSERT_TRUE(status->conflict_progress()); | 1726 ASSERT_TRUE(status->conflict_progress()); |
| 1727 EXPECT_EQ(1, status->conflict_progress()->ConflictingItemsSize()); | 1727 EXPECT_EQ(1, status->conflict_progress()->HierarchyConflictingItemsSize()); |
| 1728 } | 1728 } |
| 1729 | 1729 |
| 1730 // These entries will be used in the second set of updates. | 1730 // These entries will be used in the second set of updates. |
| 1731 mock_server_->AddUpdateDirectory(4, 0, "newer_version", 20, 10); | 1731 mock_server_->AddUpdateDirectory(4, 0, "newer_version", 20, 10); |
| 1732 mock_server_->AddUpdateDirectory(5, 0, "circular1", 10, 10); | 1732 mock_server_->AddUpdateDirectory(5, 0, "circular1", 10, 10); |
| 1733 mock_server_->AddUpdateDirectory(6, 5, "circular2", 10, 10); | 1733 mock_server_->AddUpdateDirectory(6, 5, "circular2", 10, 10); |
| 1734 mock_server_->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10); | 1734 mock_server_->AddUpdateDirectory(9, 3, "bad_parent_child", 10, 10); |
| 1735 mock_server_->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10); | 1735 mock_server_->AddUpdateDirectory(100, 9, "bad_parent_child2", 10, 10); |
| 1736 mock_server_->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10); | 1736 mock_server_->AddUpdateDirectory(10, 0, "dir_to_bookmark", 10, 10); |
| 1737 | 1737 |
| 1738 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END); | 1738 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END); |
| 1739 // The three items with an unresolved parent should be unapplied (3, 9, 100). | 1739 // The three items with an unresolved parent should be unapplied (3, 9, 100). |
| 1740 // The name clash should also still be in conflict. | 1740 // The name clash should also still be in conflict. |
| 1741 EXPECT_EQ(3, status->TotalNumConflictingItems()); | 1741 EXPECT_EQ(3, status->TotalNumConflictingItems()); |
| 1742 { | 1742 { |
| 1743 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); | 1743 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); |
| 1744 ASSERT_TRUE(status->conflict_progress()); | 1744 ASSERT_TRUE(status->conflict_progress()); |
| 1745 EXPECT_EQ(3, status->conflict_progress()->ConflictingItemsSize()); | 1745 EXPECT_EQ(3, status->conflict_progress()->HierarchyConflictingItemsSize()); |
| 1746 } | 1746 } |
| 1747 | 1747 |
| 1748 { | 1748 { |
| 1749 WriteTransaction trans(FROM_HERE, UNITTEST, dir); | 1749 WriteTransaction trans(FROM_HERE, UNITTEST, dir); |
| 1750 // Even though it has the same name, it should work. | 1750 // Even though it has the same name, it should work. |
| 1751 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2)); | 1751 Entry name_clash(&trans, GET_BY_ID, ids_.FromNumber(2)); |
| 1752 ASSERT_TRUE(name_clash.good()); | 1752 ASSERT_TRUE(name_clash.good()); |
| 1753 EXPECT_FALSE(name_clash.Get(IS_UNAPPLIED_UPDATE)) | 1753 EXPECT_FALSE(name_clash.Get(IS_UNAPPLIED_UPDATE)) |
| 1754 << "Duplicate name SHOULD be OK."; | 1754 << "Duplicate name SHOULD be OK."; |
| 1755 | 1755 |
| (...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1829 EXPECT_TRUE(circular_parent_issue.Get(ID) == | 1829 EXPECT_TRUE(circular_parent_issue.Get(ID) == |
| 1830 circular_parent_target.Get(PARENT_ID)); | 1830 circular_parent_target.Get(PARENT_ID)); |
| 1831 EXPECT_EQ(10u, circular_parent_target.Get(BASE_VERSION)); | 1831 EXPECT_EQ(10u, circular_parent_target.Get(BASE_VERSION)); |
| 1832 } | 1832 } |
| 1833 | 1833 |
| 1834 EXPECT_FALSE(saw_syncer_event_); | 1834 EXPECT_FALSE(saw_syncer_event_); |
| 1835 EXPECT_EQ(4, status->TotalNumConflictingItems()); | 1835 EXPECT_EQ(4, status->TotalNumConflictingItems()); |
| 1836 { | 1836 { |
| 1837 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); | 1837 sessions::ScopedModelSafeGroupRestriction r(status, GROUP_PASSIVE); |
| 1838 ASSERT_TRUE(status->conflict_progress()); | 1838 ASSERT_TRUE(status->conflict_progress()); |
| 1839 EXPECT_EQ(4, status->conflict_progress()->ConflictingItemsSize()); | 1839 EXPECT_EQ(4, status->conflict_progress()->HierarchyConflictingItemsSize()); |
| 1840 } | 1840 } |
| 1841 } | 1841 } |
| 1842 | 1842 |
| 1843 TEST_F(SyncerTest, CommitTimeRename) { | 1843 TEST_F(SyncerTest, CommitTimeRename) { |
| 1844 ScopedDirLookup dir(syncdb_.manager(), syncdb_.name()); | 1844 ScopedDirLookup dir(syncdb_.manager(), syncdb_.name()); |
| 1845 ASSERT_TRUE(dir.good()); | 1845 ASSERT_TRUE(dir.good()); |
| 1846 int64 metahandle_folder; | 1846 int64 metahandle_folder; |
| 1847 int64 metahandle_new_entry; | 1847 int64 metahandle_new_entry; |
| 1848 | 1848 |
| 1849 // Create a folder and an entry. | 1849 // Create a folder and an entry. |
| (...skipping 665 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2515 entry.Put(SERVER_VERSION, 1); | 2515 entry.Put(SERVER_VERSION, 1); |
| 2516 entry.Put(SERVER_PARENT_ID, ids_.FromNumber(9999)); // Bad parent. | 2516 entry.Put(SERVER_PARENT_ID, ids_.FromNumber(9999)); // Bad parent. |
| 2517 entry.Put(IS_UNSYNCED, true); | 2517 entry.Put(IS_UNSYNCED, true); |
| 2518 entry.Put(IS_UNAPPLIED_UPDATE, true); | 2518 entry.Put(IS_UNAPPLIED_UPDATE, true); |
| 2519 entry.Put(SPECIFICS, DefaultBookmarkSpecifics()); | 2519 entry.Put(SPECIFICS, DefaultBookmarkSpecifics()); |
| 2520 entry.Put(SERVER_SPECIFICS, DefaultBookmarkSpecifics()); | 2520 entry.Put(SERVER_SPECIFICS, DefaultBookmarkSpecifics()); |
| 2521 entry.Put(IS_DEL, false); | 2521 entry.Put(IS_DEL, false); |
| 2522 } | 2522 } |
| 2523 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END); | 2523 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END); |
| 2524 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END); | 2524 syncer_->SyncShare(session_.get(), SYNCER_BEGIN, SYNCER_END); |
| 2525 EXPECT_EQ(0, session_->status_controller().TotalNumConflictingItems()); | 2525 EXPECT_EQ(1, session_->status_controller().TotalNumConflictingItems()); |
| 2526 saw_syncer_event_ = false; | 2526 saw_syncer_event_ = false; |
| 2527 } | 2527 } |
| 2528 | 2528 |
| 2529 // Original problem synopsis: | 2529 // Original problem synopsis: |
| 2530 // Illegal parent | 2530 // Illegal parent |
| 2531 // Unexpected error during sync if we: | 2531 // Unexpected error during sync if we: |
| 2532 // make a new folder bob | 2532 // make a new folder bob |
| 2533 // wait for sync | 2533 // wait for sync |
| 2534 // make a new folder fred | 2534 // make a new folder fred |
| 2535 // move bob into fred | 2535 // move bob into fred |
| (...skipping 392 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 2928 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir); | 2928 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir); |
| 2929 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | 2929 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); |
| 2930 ASSERT_TRUE(A.good()); | 2930 ASSERT_TRUE(A.good()); |
| 2931 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2)); | 2931 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2)); |
| 2932 ASSERT_TRUE(B.good()); | 2932 ASSERT_TRUE(B.good()); |
| 2933 EXPECT_TRUE(A.Get(NON_UNIQUE_NAME) == "B"); | 2933 EXPECT_TRUE(A.Get(NON_UNIQUE_NAME) == "B"); |
| 2934 EXPECT_TRUE(B.Get(NON_UNIQUE_NAME) == "B"); | 2934 EXPECT_TRUE(B.Get(NON_UNIQUE_NAME) == "B"); |
| 2935 } | 2935 } |
| 2936 } | 2936 } |
| 2937 | 2937 |
| 2938 TEST_F(SyncerTest, ConflictSetClassificationError) { | |
| 2939 // This code used to cause a CHECK failure because we incorrectly thought | |
| 2940 // a set was only unapplied updates. | |
| 2941 ScopedDirLookup dir(syncdb_.manager(), syncdb_.name()); | |
| 2942 CHECK(dir.good()); | |
| 2943 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10); | |
| 2944 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10); | |
| 2945 mock_server_->set_conflict_all_commits(true); | |
| 2946 SyncShareAsDelegate(); | |
| 2947 { | |
| 2948 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir); | |
| 2949 MutableEntry A(&wtrans, GET_BY_ID, ids_.FromNumber(1)); | |
| 2950 ASSERT_TRUE(A.good()); | |
| 2951 A.Put(IS_UNSYNCED, true); | |
| 2952 A.Put(IS_UNAPPLIED_UPDATE, true); | |
| 2953 A.Put(SERVER_NON_UNIQUE_NAME, "B"); | |
| 2954 MutableEntry B(&wtrans, GET_BY_ID, ids_.FromNumber(2)); | |
| 2955 ASSERT_TRUE(B.good()); | |
| 2956 B.Put(IS_UNAPPLIED_UPDATE, true); | |
| 2957 B.Put(SERVER_NON_UNIQUE_NAME, "A"); | |
| 2958 } | |
| 2959 SyncShareAsDelegate(); | |
| 2960 saw_syncer_event_ = false; | |
| 2961 } | |
| 2962 | |
| 2963 TEST_F(SyncerTest, SwapEntryNames) { | 2938 TEST_F(SyncerTest, SwapEntryNames) { |
| 2964 // Simple transaction test. | 2939 // Simple transaction test. |
| 2965 ScopedDirLookup dir(syncdb_.manager(), syncdb_.name()); | 2940 ScopedDirLookup dir(syncdb_.manager(), syncdb_.name()); |
| 2966 CHECK(dir.good()); | 2941 CHECK(dir.good()); |
| 2967 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10); | 2942 mock_server_->AddUpdateDirectory(1, 0, "A", 10, 10); |
| 2968 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10); | 2943 mock_server_->AddUpdateDirectory(2, 0, "B", 10, 10); |
| 2969 mock_server_->set_conflict_all_commits(true); | 2944 mock_server_->set_conflict_all_commits(true); |
| 2970 SyncShareAsDelegate(); | 2945 SyncShareAsDelegate(); |
| 2971 { | 2946 { |
| 2972 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir); | 2947 WriteTransaction wtrans(FROM_HERE, UNITTEST, dir); |
| (...skipping 184 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3157 ReadTransaction trans(FROM_HERE, dir); | 3132 ReadTransaction trans(FROM_HERE, dir); |
| 3158 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1)); | 3133 Entry local_deleted(&trans, GET_BY_ID, ids_.FromNumber(1)); |
| 3159 EXPECT_TRUE(local_deleted.Get(BASE_VERSION) == 1); | 3134 EXPECT_TRUE(local_deleted.Get(BASE_VERSION) == 1); |
| 3160 EXPECT_TRUE(local_deleted.Get(IS_UNAPPLIED_UPDATE) == false); | 3135 EXPECT_TRUE(local_deleted.Get(IS_UNAPPLIED_UPDATE) == false); |
| 3161 EXPECT_TRUE(local_deleted.Get(IS_UNSYNCED) == true); | 3136 EXPECT_TRUE(local_deleted.Get(IS_UNSYNCED) == true); |
| 3162 EXPECT_TRUE(local_deleted.Get(IS_DEL) == true); | 3137 EXPECT_TRUE(local_deleted.Get(IS_DEL) == true); |
| 3163 EXPECT_TRUE(local_deleted.Get(IS_DIR) == true); | 3138 EXPECT_TRUE(local_deleted.Get(IS_DIR) == true); |
| 3164 } | 3139 } |
| 3165 } | 3140 } |
| 3166 | 3141 |
| 3167 TEST(SyncerSyncProcessState, MergeSetsTest) { | |
| 3168 TestIdFactory id_factory; | |
| 3169 syncable::Id id[7]; | |
| 3170 for (int i = 1; i < 7; i++) { | |
| 3171 id[i] = id_factory.NewServerId(); | |
| 3172 } | |
| 3173 bool is_dirty = false; | |
| 3174 ConflictProgress c(&is_dirty); | |
| 3175 c.MergeSets(id[1], id[2]); | |
| 3176 c.MergeSets(id[2], id[3]); | |
| 3177 c.MergeSets(id[4], id[5]); | |
| 3178 c.MergeSets(id[5], id[6]); | |
| 3179 EXPECT_EQ(6u, c.IdToConflictSetSize()); | |
| 3180 EXPECT_FALSE(is_dirty); | |
| 3181 for (int i = 1; i < 7; i++) { | |
| 3182 EXPECT_TRUE(NULL != c.IdToConflictSetGet(id[i])); | |
| 3183 EXPECT_TRUE(c.IdToConflictSetGet(id[(i & ~3) + 1]) == | |
| 3184 c.IdToConflictSetGet(id[i])); | |
| 3185 } | |
| 3186 c.MergeSets(id[1], id[6]); | |
| 3187 for (int i = 1; i < 7; i++) { | |
| 3188 EXPECT_TRUE(NULL != c.IdToConflictSetGet(id[i])); | |
| 3189 EXPECT_TRUE(c.IdToConflictSetGet(id[1]) == c.IdToConflictSetGet(id[i])); | |
| 3190 } | |
| 3191 | |
| 3192 // Check dupes don't cause double sets. | |
| 3193 ConflictProgress identical_set(&is_dirty); | |
| 3194 identical_set.MergeSets(id[1], id[1]); | |
| 3195 EXPECT_TRUE(identical_set.IdToConflictSetSize() == 1); | |
| 3196 EXPECT_TRUE(identical_set.IdToConflictSetGet(id[1])->size() == 1); | |
| 3197 EXPECT_FALSE(is_dirty); | |
| 3198 } | |
| 3199 | |
| 3200 // Bug Synopsis: | 3142 // Bug Synopsis: |
| 3201 // Merge conflict resolution will merge a new local entry with another entry | 3143 // Merge conflict resolution will merge a new local entry with another entry |
| 3202 // that needs updates, resulting in CHECK. | 3144 // that needs updates, resulting in CHECK. |
| 3203 TEST_F(SyncerTest, MergingExistingItems) { | 3145 TEST_F(SyncerTest, MergingExistingItems) { |
| 3204 ScopedDirLookup dir(syncdb_.manager(), syncdb_.name()); | 3146 ScopedDirLookup dir(syncdb_.manager(), syncdb_.name()); |
| 3205 CHECK(dir.good()); | 3147 CHECK(dir.good()); |
| 3206 mock_server_->set_conflict_all_commits(true); | 3148 mock_server_->set_conflict_all_commits(true); |
| 3207 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10); | 3149 mock_server_->AddUpdateBookmark(1, 0, "base", 10, 10); |
| 3208 SyncShareAsDelegate(); | 3150 SyncShareAsDelegate(); |
| 3209 { | 3151 { |
| (...skipping 232 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 3442 EXPECT_NE(foo_entry.Get(syncable::ID), in_root_id); | 3384 EXPECT_NE(foo_entry.Get(syncable::ID), in_root_id); |
| 3443 | 3385 |
| 3444 Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle); | 3386 Entry bar_entry(&trans, GET_BY_HANDLE, bar_metahandle); |
| 3445 ASSERT_TRUE(bar_entry.good()); | 3387 ASSERT_TRUE(bar_entry.good()); |
| 3446 EXPECT_EQ("bar", bar_entry.Get(NON_UNIQUE_NAME)); | 3388 EXPECT_EQ("bar", bar_entry.Get(NON_UNIQUE_NAME)); |
| 3447 EXPECT_NE(bar_entry.Get(syncable::ID), in_dir_id); | 3389 EXPECT_NE(bar_entry.Get(syncable::ID), in_dir_id); |
| 3448 EXPECT_EQ(foo_entry.Get(syncable::ID), bar_entry.Get(PARENT_ID)); | 3390 EXPECT_EQ(foo_entry.Get(syncable::ID), bar_entry.Get(PARENT_ID)); |
| 3449 } | 3391 } |
| 3450 } | 3392 } |
| 3451 | 3393 |
| 3452 TEST_F(SyncerTest, ConflictSetSizeReducedToOne) { | |
| 3453 ScopedDirLookup dir(syncdb_.manager(), syncdb_.name()); | |
| 3454 CHECK(dir.good()); | |
| 3455 | |
| 3456 syncable::Id in_root_id = ids_.NewServerId(); | |
| 3457 | |
| 3458 mock_server_->AddUpdateBookmark(in_root_id, TestIdFactory::root(), | |
| 3459 "in_root", 1, 1); | |
| 3460 SyncShareAsDelegate(); | |
| 3461 { | |
| 3462 WriteTransaction trans(FROM_HERE, UNITTEST, dir); | |
| 3463 MutableEntry oentry(&trans, GET_BY_ID, in_root_id); | |
| 3464 ASSERT_TRUE(oentry.good()); | |
| 3465 oentry.Put(NON_UNIQUE_NAME, "old_in_root"); | |
| 3466 WriteTestDataToEntry(&trans, &oentry); | |
| 3467 MutableEntry entry(&trans, CREATE, trans.root_id(), "in_root"); | |
| 3468 ASSERT_TRUE(entry.good()); | |
| 3469 WriteTestDataToEntry(&trans, &entry); | |
| 3470 } | |
| 3471 mock_server_->set_conflict_all_commits(true); | |
| 3472 // This SyncShare call used to result in a CHECK failure. | |
| 3473 SyncShareAsDelegate(); | |
| 3474 saw_syncer_event_ = false; | |
| 3475 } | |
| 3476 | |
| 3477 TEST_F(SyncerTest, TestClientCommand) { | 3394 TEST_F(SyncerTest, TestClientCommand) { |
| 3478 ScopedDirLookup dir(syncdb_.manager(), syncdb_.name()); | 3395 ScopedDirLookup dir(syncdb_.manager(), syncdb_.name()); |
| 3479 CHECK(dir.good()); | 3396 CHECK(dir.good()); |
| 3480 using sync_pb::ClientCommand; | 3397 using sync_pb::ClientCommand; |
| 3481 | 3398 |
| 3482 ClientCommand* command = mock_server_->GetNextClientCommand(); | 3399 ClientCommand* command = mock_server_->GetNextClientCommand(); |
| 3483 command->set_set_sync_poll_interval(8); | 3400 command->set_set_sync_poll_interval(8); |
| 3484 command->set_set_sync_long_poll_interval(800); | 3401 command->set_set_sync_long_poll_interval(800); |
| 3485 command->set_sessions_commit_delay_seconds(3141); | 3402 command->set_sessions_commit_delay_seconds(3141); |
| 3486 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1); | 3403 mock_server_->AddUpdateDirectory(1, 0, "in_root", 1, 1); |
| (...skipping 1306 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 4793 Add(low_id_); | 4710 Add(low_id_); |
| 4794 Add(high_id_); | 4711 Add(high_id_); |
| 4795 SyncShareAsDelegate(); | 4712 SyncShareAsDelegate(); |
| 4796 ExpectLocalOrderIsByServerId(); | 4713 ExpectLocalOrderIsByServerId(); |
| 4797 } | 4714 } |
| 4798 | 4715 |
| 4799 const SyncerTest::CommitOrderingTest | 4716 const SyncerTest::CommitOrderingTest |
| 4800 SyncerTest::CommitOrderingTest::LAST_COMMIT_ITEM = {-1, TestIdFactory::root()}; | 4717 SyncerTest::CommitOrderingTest::LAST_COMMIT_ITEM = {-1, TestIdFactory::root()}; |
| 4801 | 4718 |
| 4802 } // namespace browser_sync | 4719 } // namespace browser_sync |
| OLD | NEW |