| OLD | NEW |
| (Empty) |
| 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/test/perf/generate_profile.h" | |
| 6 | |
| 7 #include "base/at_exit.h" | |
| 8 #include "base/command_line.h" | |
| 9 #include "base/file_util.h" | |
| 10 #include "base/files/file_enumerator.h" | |
| 11 #include "base/files/file_path.h" | |
| 12 #include "base/i18n/icu_util.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/message_loop/message_loop.h" | |
| 15 #include "base/path_service.h" | |
| 16 #include "base/strings/string_number_conversions.h" | |
| 17 #include "base/strings/utf_string_conversions.h" | |
| 18 #include "base/time/time.h" | |
| 19 #include "chrome/browser/history/history_service.h" | |
| 20 #include "chrome/browser/history/history_service_factory.h" | |
| 21 #include "chrome/browser/history/top_sites.h" | |
| 22 #include "chrome/common/chrome_paths.h" | |
| 23 #include "chrome/common/thumbnail_score.h" | |
| 24 #include "chrome/test/base/testing_browser_process.h" | |
| 25 #include "chrome/test/base/testing_profile.h" | |
| 26 #include "chrome/tools/profiles/thumbnail-inl.h" | |
| 27 #include "content/public/browser/browser_thread.h" | |
| 28 #include "content/public/browser/notification_service.h" | |
| 29 #include "content/public/test/test_browser_thread.h" | |
| 30 #include "third_party/skia/include/core/SkBitmap.h" | |
| 31 #include "ui/base/resource/resource_bundle.h" | |
| 32 #include "ui/base/ui_base_paths.h" | |
| 33 #include "ui/gfx/codec/jpeg_codec.h" | |
| 34 | |
| 35 using base::Time; | |
| 36 using content::BrowserThread; | |
| 37 | |
| 38 namespace { | |
| 39 | |
| 40 // Probabilities of different word lengths, as measured from Darin's profile. | |
| 41 // kWordLengthProbabilities[n-1] = P(word of length n) | |
| 42 const float kWordLengthProbabilities[] = { 0.069f, 0.132f, 0.199f, | |
| 43 0.137f, 0.088f, 0.115f, 0.081f, 0.055f, 0.034f, 0.021f, 0.019f, 0.018f, | |
| 44 0.007f, 0.007f, 0.005f, 0.004f, 0.003f, 0.003f, 0.003f }; | |
| 45 | |
| 46 // Return a float uniformly in [0,1]. | |
| 47 // Useful for making probabilistic decisions. | |
| 48 inline float RandomFloat() { | |
| 49 return rand() / static_cast<float>(RAND_MAX); | |
| 50 } | |
| 51 | |
| 52 // Return an integer uniformly in [min,max). | |
| 53 inline int RandomInt(int min, int max) { | |
| 54 return min + (rand() % (max-min)); | |
| 55 } | |
| 56 | |
| 57 // Return a string of |count| lowercase random characters. | |
| 58 base::string16 RandomChars(int count) { | |
| 59 base::string16 str; | |
| 60 for (int i = 0; i < count; ++i) | |
| 61 str += L'a' + rand() % 26; | |
| 62 return str; | |
| 63 } | |
| 64 | |
| 65 base::string16 RandomWord() { | |
| 66 // TODO(evanm): should we instead use the markov chain based | |
| 67 // version of this that I already wrote? | |
| 68 | |
| 69 // Sample a word length from kWordLengthProbabilities. | |
| 70 float sample = RandomFloat(); | |
| 71 size_t i; | |
| 72 for (i = 0; i < arraysize(kWordLengthProbabilities); ++i) { | |
| 73 sample -= kWordLengthProbabilities[i]; | |
| 74 if (sample < 0) break; | |
| 75 } | |
| 76 const int word_length = i + 1; | |
| 77 return RandomChars(word_length); | |
| 78 } | |
| 79 | |
| 80 // Return a string of |count| random words. | |
| 81 base::string16 RandomWords(int count) { | |
| 82 base::string16 str; | |
| 83 for (int i = 0; i < count; ++i) { | |
| 84 if (!str.empty()) | |
| 85 str += L' '; | |
| 86 str += RandomWord(); | |
| 87 } | |
| 88 return str; | |
| 89 } | |
| 90 | |
| 91 // Return a random URL-looking string. | |
| 92 GURL ConstructRandomURL() { | |
| 93 return GURL(base::ASCIIToUTF16("http://") + RandomChars(3) + | |
| 94 base::ASCIIToUTF16(".com/") + RandomChars(RandomInt(5, 20))); | |
| 95 } | |
| 96 | |
| 97 // Return a random page title-looking string. | |
| 98 base::string16 ConstructRandomTitle() { | |
| 99 return RandomWords(RandomInt(3, 15)); | |
| 100 } | |
| 101 | |
| 102 // Insert a batch of |batch_size| URLs, starting at pageid |page_id|. | |
| 103 void InsertURLBatch(Profile* profile, | |
| 104 int page_id, | |
| 105 int batch_size, | |
| 106 int types) { | |
| 107 HistoryService* history_service = | |
| 108 HistoryServiceFactory::GetForProfile(profile, Profile::EXPLICIT_ACCESS); | |
| 109 | |
| 110 // Probability of following a link on the current "page" | |
| 111 // (vs randomly jumping to a new page). | |
| 112 const float kFollowLinkProbability = 0.85f; | |
| 113 // Probability of visiting a page we've visited before. | |
| 114 const float kRevisitLinkProbability = 0.1f; | |
| 115 // Probability of a URL being "good enough" to revisit. | |
| 116 const float kRevisitableURLProbability = 0.05f; | |
| 117 // Probability of a URL being the end of a redirect chain. | |
| 118 const float kRedirectProbability = 0.05f; | |
| 119 | |
| 120 // A list of URLs that we sometimes revisit. | |
| 121 std::vector<GURL> revisit_urls; | |
| 122 | |
| 123 // Scoping value for page IDs (required by the history service). | |
| 124 void* id_scope = reinterpret_cast<void*>(1); | |
| 125 | |
| 126 scoped_refptr<base::RefCountedMemory> google_bitmap( | |
| 127 new base::RefCountedStaticMemory(kGoogleThumbnail, | |
| 128 sizeof(kGoogleThumbnail))); | |
| 129 scoped_refptr<base::RefCountedMemory> weewar_bitmap( | |
| 130 new base::RefCountedStaticMemory(kWeewarThumbnail, | |
| 131 sizeof(kWeewarThumbnail))); | |
| 132 | |
| 133 printf("Inserting %d URLs...\n", batch_size); | |
| 134 GURL previous_url; | |
| 135 content::PageTransition transition = content::PAGE_TRANSITION_TYPED; | |
| 136 const int end_page_id = page_id + batch_size; | |
| 137 history::TopSites* top_sites = profile->GetTopSites(); | |
| 138 for (; page_id < end_page_id; ++page_id) { | |
| 139 // Randomly decide whether this new URL simulates following a link or | |
| 140 // whether it's a jump to a new URL. | |
| 141 if (!previous_url.is_empty() && RandomFloat() < kFollowLinkProbability) { | |
| 142 transition = content::PAGE_TRANSITION_LINK; | |
| 143 } else { | |
| 144 previous_url = GURL(); | |
| 145 transition = content::PAGE_TRANSITION_TYPED; | |
| 146 } | |
| 147 | |
| 148 // Pick a URL, either newly at random or from our list of previously | |
| 149 // visited URLs. | |
| 150 GURL url; | |
| 151 if (!revisit_urls.empty() && RandomFloat() < kRevisitLinkProbability) { | |
| 152 // Draw a URL from revisit_urls at random. | |
| 153 url = revisit_urls[RandomInt(0, static_cast<int>(revisit_urls.size()))]; | |
| 154 } else { | |
| 155 url = ConstructRandomURL(); | |
| 156 } | |
| 157 | |
| 158 // Randomly construct a redirect chain. | |
| 159 history::RedirectList redirects; | |
| 160 if (RandomFloat() < kRedirectProbability) { | |
| 161 const int redir_count = RandomInt(1, 4); | |
| 162 for (int i = 0; i < redir_count; ++i) | |
| 163 redirects.push_back(ConstructRandomURL()); | |
| 164 redirects.push_back(url); | |
| 165 } | |
| 166 | |
| 167 // Add all of this information to the history service. | |
| 168 history_service->AddPage(url, base::Time::Now(), | |
| 169 id_scope, page_id, | |
| 170 previous_url, redirects, | |
| 171 transition, history::SOURCE_BROWSED, true); | |
| 172 ThumbnailScore score(0.75, false, false); | |
| 173 history_service->SetPageTitle(url, ConstructRandomTitle()); | |
| 174 if (types & TOP_SITES && top_sites) { | |
| 175 top_sites->SetPageThumbnailToJPEGBytes( | |
| 176 url, | |
| 177 (RandomInt(0, 2) == 0) ? google_bitmap.get() : weewar_bitmap.get(), | |
| 178 score); | |
| 179 } | |
| 180 | |
| 181 previous_url = url; | |
| 182 | |
| 183 if (revisit_urls.empty() || RandomFloat() < kRevisitableURLProbability) | |
| 184 revisit_urls.push_back(url); | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 } // namespace | |
| 189 | |
| 190 bool GenerateProfile(GenerateProfileTypes types, | |
| 191 int url_count, | |
| 192 const base::FilePath& dst_dir) { | |
| 193 if (!base::CreateDirectory(dst_dir)) { | |
| 194 PLOG(ERROR) << "Unable to create directory " << dst_dir.value().c_str(); | |
| 195 return false; | |
| 196 } | |
| 197 | |
| 198 // We want this profile to be as deterministic as possible, so seed the | |
| 199 // random number generator with the number of urls we're generating. | |
| 200 srand(static_cast<unsigned int>(url_count)); | |
| 201 | |
| 202 printf("Creating profiles for testing...\n"); | |
| 203 | |
| 204 TestingBrowserProcessInitializer initialize_browser_process; | |
| 205 base::MessageLoopForUI message_loop; | |
| 206 content::TestBrowserThread ui_thread(BrowserThread::UI, &message_loop); | |
| 207 content::TestBrowserThread db_thread(BrowserThread::DB, &message_loop); | |
| 208 TestingProfile profile; | |
| 209 if (!profile.CreateHistoryService(false, false)) { | |
| 210 PLOG(ERROR) << "Creating history service failed"; | |
| 211 return false; | |
| 212 } | |
| 213 if (types & TOP_SITES) { | |
| 214 profile.CreateTopSites(); | |
| 215 profile.BlockUntilTopSitesLoaded(); | |
| 216 } | |
| 217 | |
| 218 // The maximum number of URLs to insert into history in one batch. | |
| 219 const int kBatchSize = 2000; | |
| 220 int page_id = 0; | |
| 221 while (page_id < url_count) { | |
| 222 const int batch_size = std::min(kBatchSize, url_count - page_id); | |
| 223 InsertURLBatch(&profile, page_id, batch_size, types); | |
| 224 // Run all pending messages to give TopSites a chance to catch up. | |
| 225 message_loop.RunUntilIdle(); | |
| 226 page_id += batch_size; | |
| 227 } | |
| 228 | |
| 229 profile.DestroyTopSites(); | |
| 230 profile.DestroyHistoryService(); | |
| 231 | |
| 232 message_loop.RunUntilIdle(); | |
| 233 | |
| 234 base::FileEnumerator file_iterator(profile.GetPath(), false, | |
| 235 base::FileEnumerator::FILES); | |
| 236 base::FilePath path = file_iterator.Next(); | |
| 237 while (!path.empty()) { | |
| 238 base::FilePath dst_file = dst_dir.Append(path.BaseName()); | |
| 239 base::DeleteFile(dst_file, false); | |
| 240 if (!base::CopyFile(path, dst_file)) { | |
| 241 PLOG(ERROR) << "Copying file failed"; | |
| 242 return false; | |
| 243 } | |
| 244 path = file_iterator.Next(); | |
| 245 } | |
| 246 | |
| 247 printf("Finished creating profiles for testing.\n"); | |
| 248 | |
| 249 // Restore the random seed. | |
| 250 srand(static_cast<unsigned int>(Time::Now().ToInternalValue())); | |
| 251 | |
| 252 return true; | |
| 253 } | |
| OLD | NEW |