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
3 // found in the LICENSE file.
4 
5 #include "chrome/browser/ui/toolbar/recent_tabs_builder_test_helper.h"
6 
7 #include <stddef.h>
8 
9 #include <algorithm>
10 #include <string>
11 
12 #include "base/rand_util.h"
13 #include "base/run_loop.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/strings/utf_string_conversions.h"
17 #include "components/sync/base/client_tag_hash.h"
18 #include "components/sync/engine/commit_and_get_updates_types.h"
19 #include "components/sync/engine/model_type_processor.h"
20 #include "components/sync/model/entity_data.h"
21 #include "components/sync/model_impl/in_memory_metadata_change_list.h"
22 #include "components/sync/protocol/session_specifics.pb.h"
23 #include "components/sync_sessions/open_tabs_ui_delegate.h"
24 #include "components/sync_sessions/session_store.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 
27 namespace {
28 
29 const char kBaseSessionTag[] = "session_tag";
30 const char kBaseSessionName[] = "session_name";
31 const char kBaseTabUrl[] = "http://foo/?";
32 const char kTabTitleFormat[] = "session=%d;window=%d;tab=%d";
33 const uint64_t kMaxMinutesRange = 1000;
34 
35 struct TitleTimestampPair {
36   base::string16 title;
37   base::Time timestamp;
38 };
39 
SortTabTimesByRecency(const TitleTimestampPair & t1,const TitleTimestampPair & t2)40 bool SortTabTimesByRecency(const TitleTimestampPair& t1,
41                            const TitleTimestampPair& t2) {
42   return t1.timestamp > t2.timestamp;
43 }
44 
ToSessionTag(SessionID session_id)45 std::string ToSessionTag(SessionID session_id) {
46   return std::string(kBaseSessionTag + base::NumberToString(session_id.id()));
47 }
48 
ToSessionName(SessionID session_id)49 std::string ToSessionName(SessionID session_id) {
50   return std::string(kBaseSessionName + base::NumberToString(session_id.id()));
51 }
52 
ToTabTitle(SessionID session_id,SessionID window_id,SessionID tab_id)53 std::string ToTabTitle(SessionID session_id,
54                        SessionID window_id,
55                        SessionID tab_id) {
56   return base::StringPrintf(kTabTitleFormat, session_id.id(), window_id.id(),
57                             tab_id.id());
58 }
59 
ToTabUrl(SessionID session_id,SessionID window_id,SessionID tab_id)60 std::string ToTabUrl(SessionID session_id,
61                      SessionID window_id,
62                      SessionID tab_id) {
63   return std::string(kBaseTabUrl + ToTabTitle(session_id, window_id, tab_id));
64 }
65 
66 }  // namespace
67 
68 struct RecentTabsBuilderTestHelper::TabInfo {
TabInfoRecentTabsBuilderTestHelper::TabInfo69   TabInfo() : id(SessionID::InvalidValue()) {}
70   SessionID id;
71   base::Time timestamp;
72   base::string16 title;
73 };
74 struct RecentTabsBuilderTestHelper::WindowInfo {
WindowInfoRecentTabsBuilderTestHelper::WindowInfo75   WindowInfo() : id(SessionID::InvalidValue()) {}
~WindowInfoRecentTabsBuilderTestHelper::WindowInfo76   ~WindowInfo() {}
77   SessionID id;
78   std::vector<TabInfo> tabs;
79 };
80 struct RecentTabsBuilderTestHelper::SessionInfo {
SessionInfoRecentTabsBuilderTestHelper::SessionInfo81   SessionInfo() : id(SessionID::InvalidValue()) {}
~SessionInfoRecentTabsBuilderTestHelper::SessionInfo82   ~SessionInfo() {}
83   SessionID id;
84   std::vector<WindowInfo> windows;
85 };
86 
RecentTabsBuilderTestHelper()87 RecentTabsBuilderTestHelper::RecentTabsBuilderTestHelper() {
88   start_time_ = base::Time::Now();
89 }
90 
~RecentTabsBuilderTestHelper()91 RecentTabsBuilderTestHelper::~RecentTabsBuilderTestHelper() {
92 }
93 
AddSession()94 void RecentTabsBuilderTestHelper::AddSession() {
95   SessionInfo info;
96   info.id = SessionID::NewUnique();
97   sessions_.push_back(info);
98 }
99 
GetSessionCount()100 int RecentTabsBuilderTestHelper::GetSessionCount() {
101   return sessions_.size();
102 }
103 
GetSessionID(int session_index)104 SessionID RecentTabsBuilderTestHelper::GetSessionID(int session_index) {
105   return sessions_[session_index].id;
106 }
107 
GetSessionTimestamp(int session_index)108 base::Time RecentTabsBuilderTestHelper::GetSessionTimestamp(int session_index) {
109   std::vector<base::Time> timestamps;
110   for (int w = 0; w < GetWindowCount(session_index); ++w) {
111     for (int t = 0; t < GetTabCount(session_index, w); ++t)
112       timestamps.push_back(GetTabTimestamp(session_index, w, t));
113   }
114 
115   if (timestamps.empty())
116     return base::Time::Now();
117 
118   sort(timestamps.begin(), timestamps.end());
119   return timestamps[0];
120 }
121 
AddWindow(int session_index)122 void RecentTabsBuilderTestHelper::AddWindow(int session_index) {
123   WindowInfo window_info;
124   window_info.id = SessionID::NewUnique();
125   sessions_[session_index].windows.push_back(window_info);
126 }
127 
GetWindowCount(int session_index)128 int RecentTabsBuilderTestHelper::GetWindowCount(int session_index) {
129   return sessions_[session_index].windows.size();
130 }
131 
GetWindowID(int session_index,int window_index)132 SessionID RecentTabsBuilderTestHelper::GetWindowID(int session_index,
133                                                    int window_index) {
134   return sessions_[session_index].windows[window_index].id;
135 }
136 
AddTab(int session_index,int window_index)137 void RecentTabsBuilderTestHelper::AddTab(int session_index, int window_index) {
138   base::Time timestamp =
139       start_time_ +
140       base::TimeDelta::FromMinutes(base::RandGenerator(kMaxMinutesRange));
141   AddTabWithInfo(session_index, window_index, timestamp, base::string16());
142 }
143 
AddTabWithInfo(int session_index,int window_index,base::Time timestamp,const base::string16 & title)144 void RecentTabsBuilderTestHelper::AddTabWithInfo(int session_index,
145                                                  int window_index,
146                                                  base::Time timestamp,
147                                                  const base::string16& title) {
148   TabInfo tab_info;
149   tab_info.id = SessionID::NewUnique();
150   tab_info.timestamp = timestamp;
151   tab_info.title = title;
152   sessions_[session_index].windows[window_index].tabs.push_back(tab_info);
153 }
154 
GetTabCount(int session_index,int window_index)155 int RecentTabsBuilderTestHelper::GetTabCount(int session_index,
156                                              int window_index) {
157   return sessions_[session_index].windows[window_index].tabs.size();
158 }
159 
GetTabID(int session_index,int window_index,int tab_index)160 SessionID RecentTabsBuilderTestHelper::GetTabID(int session_index,
161                                                 int window_index,
162                                                 int tab_index) {
163   return sessions_[session_index].windows[window_index].tabs[tab_index].id;
164 }
165 
GetTabTimestamp(int session_index,int window_index,int tab_index)166 base::Time RecentTabsBuilderTestHelper::GetTabTimestamp(int session_index,
167                                                         int window_index,
168                                                         int tab_index) {
169   return sessions_[session_index].windows[window_index]
170       .tabs[tab_index].timestamp;
171 }
172 
GetTabTitle(int session_index,int window_index,int tab_index)173 base::string16 RecentTabsBuilderTestHelper::GetTabTitle(int session_index,
174                                                         int window_index,
175                                                         int tab_index) {
176   base::string16 title =
177       sessions_[session_index].windows[window_index].tabs[tab_index].title;
178   if (title.empty()) {
179     title = base::UTF8ToUTF16(ToTabTitle(
180         GetSessionID(session_index),
181         GetWindowID(session_index, window_index),
182         GetTabID(session_index, window_index, tab_index)));
183   }
184   return title;
185 }
186 
ExportToSessionSync(syncer::ModelTypeProcessor * processor)187 void RecentTabsBuilderTestHelper::ExportToSessionSync(
188     syncer::ModelTypeProcessor* processor) {
189   syncer::UpdateResponseDataList updates;
190 
191   for (int s = 0; s < GetSessionCount(); ++s) {
192     sync_pb::SessionSpecifics header_specifics = BuildHeaderSpecifics(s);
193     for (int w = 0; w < GetWindowCount(s); ++w) {
194       AddWindowToHeaderSpecifics(s, w, &header_specifics);
195       for (int t = 0; t < GetTabCount(s, w); ++t) {
196         updates.push_back(BuildUpdateResponseData(BuildTabSpecifics(s, w, t),
197                                                   GetTabTimestamp(s, w, t)));
198       }
199     }
200 
201     updates.push_back(
202         BuildUpdateResponseData(header_specifics, GetSessionTimestamp(s)));
203   }
204 
205   sync_pb::ModelTypeState model_type_state;
206   model_type_state.set_initial_sync_done(true);
207   processor->OnUpdateReceived(model_type_state, std::move(updates));
208   // ClientTagBasedModelTypeProcessor uses ModelTypeProcessorProxy during
209   // activation, which involves task posting for receiving updates.
210   base::RunLoop().RunUntilIdle();
211 }
212 
VerifyExport(sync_sessions::OpenTabsUIDelegate * delegate)213 void RecentTabsBuilderTestHelper::VerifyExport(
214     sync_sessions::OpenTabsUIDelegate* delegate) {
215   DCHECK(delegate);
216   // Make sure data is populated correctly in SessionModelAssociator.
217   std::vector<const sync_sessions::SyncedSession*> sessions;
218   ASSERT_TRUE(delegate->GetAllForeignSessions(&sessions));
219   ASSERT_EQ(GetSessionCount(), static_cast<int>(sessions.size()));
220   for (int s = 0; s < GetSessionCount(); ++s) {
221     std::vector<const sessions::SessionWindow*> windows;
222     ASSERT_TRUE(delegate->GetForeignSession(ToSessionTag(GetSessionID(s)),
223                                             &windows));
224     ASSERT_EQ(GetWindowCount(s), static_cast<int>(windows.size()));
225     for (int w = 0; w < GetWindowCount(s); ++w)
226       ASSERT_EQ(GetTabCount(s, w), static_cast<int>(windows[w]->tabs.size()));
227   }
228 }
229 
230 std::vector<base::string16>
GetTabTitlesSortedByRecency()231 RecentTabsBuilderTestHelper::GetTabTitlesSortedByRecency() {
232   std::vector<TitleTimestampPair> tabs;
233   for (int s = 0; s < GetSessionCount(); ++s) {
234     for (int w = 0; w < GetWindowCount(s); ++w) {
235       for (int t = 0; t < GetTabCount(s, w); ++t) {
236         TitleTimestampPair pair;
237         pair.title = GetTabTitle(s, w, t);
238         pair.timestamp = GetTabTimestamp(s, w, t);
239         tabs.push_back(pair);
240       }
241     }
242   }
243   sort(tabs.begin(), tabs.end(), SortTabTimesByRecency);
244 
245   std::vector<base::string16> titles;
246   for (size_t i = 0; i < tabs.size(); ++i)
247     titles.push_back(tabs[i].title);
248   return titles;
249 }
250 
BuildHeaderSpecifics(int session_index)251 sync_pb::SessionSpecifics RecentTabsBuilderTestHelper::BuildHeaderSpecifics(
252     int session_index) {
253   sync_pb::SessionSpecifics specifics;
254   SessionID session_id = GetSessionID(session_index);
255   specifics.set_session_tag(ToSessionTag(session_id));
256   sync_pb::SessionHeader* header = specifics.mutable_header();
257   header->set_device_type(sync_pb::SyncEnums_DeviceType_TYPE_CROS);
258   header->set_client_name(ToSessionName(session_id));
259   return specifics;
260 }
261 
AddWindowToHeaderSpecifics(int session_index,int window_index,sync_pb::SessionSpecifics * specifics)262 void RecentTabsBuilderTestHelper::AddWindowToHeaderSpecifics(
263     int session_index,
264     int window_index,
265     sync_pb::SessionSpecifics* specifics) {
266   sync_pb::SessionWindow* window = specifics->mutable_header()->add_window();
267   SessionID window_id = GetWindowID(session_index, window_index);
268   window->set_window_id(window_id.id());
269   window->set_selected_tab_index(0);
270   window->set_browser_type(sync_pb::SessionWindow_BrowserType_TYPE_TABBED);
271   for (int i = 0; i < GetTabCount(session_index, window_index); ++i)
272     window->add_tab(GetTabID(session_index, window_index, i).id());
273 }
274 
BuildTabSpecifics(int session_index,int window_index,int tab_index)275 sync_pb::SessionSpecifics RecentTabsBuilderTestHelper::BuildTabSpecifics(
276     int session_index,
277     int window_index,
278     int tab_index) {
279   sync_pb::SessionSpecifics specifics;
280 
281   SessionID session_id = GetSessionID(session_index);
282   SessionID window_id = GetWindowID(session_index, window_index);
283   SessionID tab_id = GetTabID(session_index, window_index, tab_index);
284 
285   specifics.set_session_tag(ToSessionTag(session_id));
286   specifics.set_tab_node_id(++max_tab_node_id_);
287   sync_pb::SessionTab* tab = specifics.mutable_tab();
288   tab->set_window_id(window_id.id());
289   tab->set_tab_id(tab_id.id());
290   tab->set_tab_visual_index(1);
291   tab->set_current_navigation_index(0);
292   tab->set_pinned(true);
293   tab->set_extension_app_id("app_id");
294   sync_pb::TabNavigation* navigation = tab->add_navigation();
295   navigation->set_virtual_url(ToTabUrl(session_id, window_id, tab_id));
296   navigation->set_referrer("referrer");
297   navigation->set_title(base::UTF16ToUTF8(GetTabTitle(
298       session_index, window_index, tab_index)));
299   navigation->set_page_transition(sync_pb::SyncEnums_PageTransition_TYPED);
300 
301   return specifics;
302 }
303 
BuildUpdateResponseData(const sync_pb::SessionSpecifics & specifics,base::Time timestamp)304 syncer::UpdateResponseData RecentTabsBuilderTestHelper::BuildUpdateResponseData(
305     const sync_pb::SessionSpecifics& specifics,
306     base::Time timestamp) {
307   syncer::EntityData entity;
308   *entity.specifics.mutable_session() = specifics;
309   entity.creation_time = timestamp;
310   entity.modification_time = timestamp;
311   entity.client_tag_hash = syncer::ClientTagHash::FromUnhashed(
312       syncer::SESSIONS, sync_sessions::SessionStore::GetClientTag(specifics));
313   entity.id = entity.client_tag_hash.value();
314 
315   syncer::UpdateResponseData update;
316   update.entity = std::move(entity);
317   update.response_version = ++next_response_version_;
318   return update;
319 }
320