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/browser/extensions/extension_record_api.h" | |
6 | |
7 #include <string> | |
8 | |
9 #include "base/memory/ref_counted.h" | |
10 #include "base/file_path.h" | |
11 #include "base/file_util.h" | |
12 #include "base/memory/scoped_ptr.h" | |
13 #include "base/path_service.h" | |
14 #include "base/string_split.h" | |
15 #include "base/stringprintf.h" | |
16 #include "base/string_util.h" | |
17 #include "base/threading/sequenced_worker_pool.h" | |
18 #include "base/values.h" | |
19 #include "chrome/browser/extensions/extension_function_test_utils.h" | |
20 #include "chrome/browser/ui/browser.h" | |
21 #include "chrome/browser/ui/browser_window.h" | |
22 #include "chrome/common/chrome_paths.h" | |
23 #include "chrome/common/chrome_switches.h" | |
24 #include "chrome/common/extensions/api/experimental_record.h" | |
25 #include "chrome/test/base/in_process_browser_test.h" | |
26 #include "chrome/test/base/ui_test_utils.h" | |
27 #include "content/public/browser/browser_thread.h" | |
28 #include "content/public/common/content_switches.h" | |
29 | |
30 namespace utils = extension_function_test_utils; | |
31 | |
32 namespace { | |
33 | |
34 // Dummy content for mock stats file. | |
35 const std::string kTestStatistics = "Sample Stat 1\nSample Stat 2\n"; | |
36 | |
37 // Standard capture parameters, with a mix of good and bad URLs, and | |
38 // a hole for filling in the user data dir. | |
39 const char kCaptureArgs1[] = | |
40 "[[\"URL 1\", \"URL 2(bad)\", \"URL 3\", \"URL 4(bad)\"]" | |
41 ", \"%s\"]"; | |
42 | |
43 // Standard playback parameters, with the same mix of good and bad URLs | |
44 // as the capture parameters, a hole for filling in the user data dir, and | |
45 // a mocked-up extension path and repeat count (which are used only to | |
46 // verify that they made it into the CommandLine, since extension loading | |
47 // and repeat-counting are hard to emulate in the test ProcessStrategy. | |
48 const char kPlaybackArgs1[] = | |
49 "[[\"URL 1\", \"URL 2(bad)\", \"URL 3\", \"URL 4(bad)\"], \"%s\"" | |
50 ", 2, {\"extensionPath\": \"MockExtension\"}]"; | |
51 | |
52 // Use this as the value of FilePath switches (e.g. user-data-dir) that | |
53 // should be replaced by the record methods. | |
54 const FilePath::CharType kDummyDirName[] = FILE_PATH_LITERAL("ReplaceMe"); | |
55 | |
56 // Use this as the filename for a mock "cache" file in the user-data-dir. | |
57 const FilePath::CharType kMockCacheFile[] = FILE_PATH_LITERAL("MockCache"); | |
58 | |
59 // Prefix for temporary user data directory | |
60 const FilePath::CharType kUserDataDirPrefix[] | |
61 = FILE_PATH_LITERAL("PageCyclerTest"); | |
62 | |
63 } | |
64 | |
65 class TestProcessStrategy : public ProcessStrategy { | |
66 public: | |
67 explicit TestProcessStrategy(std::vector<FilePath>* temp_files) | |
68 : command_line_(CommandLine::NO_PROGRAM), temp_files_(temp_files) {} | |
69 | |
70 ~TestProcessStrategy() {} | |
71 | |
72 // Pump the blocking pool queue, since this is needed during test. | |
73 void PumpBlockingPool() OVERRIDE { | |
74 content::BrowserThread::GetBlockingPool()->FlushForTesting(); | |
75 } | |
76 | |
77 // Act somewhat like a real test browser instance. In particular: | |
78 // 1. If visit-urls, then | |
79 // a. If record-mode, then put a copy of the URL list into the user data | |
80 // directory in a mocked up cache file | |
81 // b. If playback-mode, then check for existence of that URL list copy | |
82 // in the cache | |
83 // c. Scan list of URLS, noting in |urls_visited_| all those | |
84 // visited. If there are any "bad" URLS, don't visit these, but | |
85 // create a ".errors" file listing them. | |
86 // 2. If record-stats, then create a mock stats file. | |
87 void RunProcess(const CommandLine& command_line, | |
88 std::vector<std::string>* errors) OVERRIDE { | |
89 command_line_ = command_line; | |
90 visited_urls_.clear(); | |
91 | |
92 if (command_line.HasSwitch(switches::kVisitURLs)) { | |
93 FilePath url_path = | |
94 command_line.GetSwitchValuePath(switches::kVisitURLs); | |
95 | |
96 temp_files_->push_back(url_path); | |
97 if (command_line.HasSwitch(switches::kRecordMode) || | |
98 command_line.HasSwitch(switches::kPlaybackMode)) { | |
99 FilePath url_path_copy = command_line.GetSwitchValuePath( | |
100 switches::kUserDataDir).Append( | |
101 FilePath(FilePath::StringType(kMockCacheFile))); | |
102 | |
103 if (command_line.HasSwitch(switches::kRecordMode)) { | |
104 file_util::CopyFile(url_path, url_path_copy); | |
105 } else { | |
106 if (!file_util::ContentsEqual(url_path, url_path_copy)) { | |
107 std::string contents1, contents2; | |
108 file_util::ReadFileToString(url_path, &contents1); | |
109 file_util::ReadFileToString(url_path_copy, &contents2); | |
110 LOG(ERROR) << "FILE MISMATCH" << contents1 << " VS " << contents2; | |
111 } | |
112 EXPECT_TRUE(file_util::ContentsEqual(url_path, url_path_copy)); | |
113 } | |
114 } | |
115 | |
116 std::string urls; | |
117 file_util::ReadFileToString(url_path, &urls); | |
118 | |
119 std::vector<std::string> url_vector, bad_urls; | |
120 base::SplitString(urls, '\n', &url_vector); | |
121 for (std::vector<std::string>::iterator itr = url_vector.begin(); | |
122 itr != url_vector.end(); ++itr) { | |
123 if (itr->find_first_of("bad") != std::string::npos) | |
124 bad_urls.push_back(*itr); | |
125 else | |
126 visited_urls_.push_back(*itr); | |
127 } | |
128 | |
129 if (!bad_urls.empty()) { | |
130 FilePath url_errors_path = url_path.DirName() | |
131 .Append(url_path.BaseName().value() + | |
132 FilePath::StringType(kURLErrorsSuffix)); | |
133 std::string error_content = JoinString(bad_urls, '\n'); | |
134 temp_files_->push_back(url_errors_path); | |
135 file_util::WriteFile(url_errors_path, error_content.c_str(), | |
136 error_content.size()); | |
137 } | |
138 } | |
139 | |
140 if (command_line.HasSwitch(switches::kRecordStats)) { | |
141 FilePath record_stats_path(command_line.GetSwitchValuePath( | |
142 switches::kRecordStats)); | |
143 temp_files_->push_back(record_stats_path); | |
144 file_util::WriteFile(record_stats_path, kTestStatistics.c_str(), | |
145 kTestStatistics.size()); | |
146 } | |
147 } | |
148 | |
149 const CommandLine& GetCommandLine() const { | |
150 return command_line_; | |
151 } | |
152 | |
153 const std::vector<std::string>& GetVisitedURLs() const { | |
154 return visited_urls_; | |
155 } | |
156 | |
157 private: | |
158 CommandLine command_line_; | |
159 std::vector<std::string> visited_urls_; | |
160 std::vector<FilePath>* temp_files_; | |
161 }; | |
162 | |
163 class RecordApiTest : public InProcessBrowserTest { | |
164 public: | |
165 RecordApiTest() {} | |
166 virtual ~RecordApiTest() {} | |
167 | |
168 // Override to scope known temp directories outside the scope of the running | |
169 // browser test. | |
170 virtual void SetUp() OVERRIDE { | |
171 InProcessBrowserTest::SetUp(); | |
172 if (!scoped_temp_user_data_dir_.Set(FilePath(kDummyDirName))) | |
173 NOTREACHED(); | |
174 } | |
175 | |
176 // Override to delete temp directories. | |
177 virtual void TearDown() OVERRIDE { | |
178 if (!scoped_temp_user_data_dir_.Delete()) | |
179 NOTREACHED(); | |
180 InProcessBrowserTest::TearDown(); | |
181 } | |
182 | |
183 // Override to delete temporary files created during execution. | |
184 virtual void CleanUpOnMainThread() OVERRIDE { | |
185 InProcessBrowserTest::CleanUpOnMainThread(); | |
186 for (std::vector<FilePath>::const_iterator it = temp_files_.begin(); | |
187 it != temp_files_.end(); ++it) { | |
188 if (!file_util::Delete(*it, false)) | |
189 NOTREACHED(); | |
190 } | |
191 } | |
192 | |
193 // Override SetUpCommandline to specify a dummy user_data_dir, which | |
194 // should be replaced. Clear record-mode, playback-mode, visit-urls, | |
195 // record-stats, and load-extension. | |
196 void SetUpCommandLine(CommandLine* command_line) OVERRIDE { | |
197 InProcessBrowserTest::SetUpCommandLine(command_line); | |
198 std::vector<std::string> remove_switches; | |
199 | |
200 remove_switches.push_back(switches::kUserDataDir); | |
201 remove_switches.push_back(switches::kVisitURLs); | |
202 remove_switches.push_back(switches::kPlaybackMode); | |
203 remove_switches.push_back(switches::kRecordStats); | |
204 remove_switches.push_back(switches::kLoadExtension); | |
205 *command_line = RunPageCyclerFunction::RemoveSwitches(*command_line, | |
206 remove_switches); | |
207 | |
208 command_line->AppendSwitchPath(switches::kUserDataDir, | |
209 FilePath(kDummyDirName)); | |
210 // Adding a dummy load-extension switch is rather complex since the | |
211 // preent design of InProcessBrowserTest requires a *real* extension | |
212 // for the flag, even if we're just testing its replacement. Opted | |
213 // to omit this for the sake of simplicity. | |
214 } | |
215 | |
216 // Run a capture, using standard URL test list and the specified | |
217 // user data dir. Return via |out_list| the list of error URLs, | |
218 // if any, resulting from the capture. And return directly the | |
219 // CaptureURLsFunction that was used, so that its state may be | |
220 // queried. | |
221 scoped_refptr<CaptureURLsFunction> RunCapture(const FilePath& user_data_dir, | |
222 scoped_ptr<base::ListValue>* out_list) { | |
223 | |
224 scoped_refptr<CaptureURLsFunction> capture_function( | |
225 new CaptureURLsFunction(new TestProcessStrategy(&temp_files_))); | |
226 | |
227 std::string escaped_user_data_dir; | |
228 ReplaceChars(user_data_dir.AsUTF8Unsafe(), "\\", "\\\\", | |
229 &escaped_user_data_dir); | |
230 | |
231 out_list->reset(utils::ToList( | |
232 utils::RunFunctionAndReturnResult(capture_function.get(), | |
233 base::StringPrintf(kCaptureArgs1, escaped_user_data_dir.c_str()), | |
234 browser()))); | |
235 | |
236 return capture_function; | |
237 } | |
238 | |
239 // Verify that the URL list of good and bad URLs was properly handled. | |
240 // Needed by several tests. | |
241 bool VerifyURLHandling(const ListValue* result, | |
242 const TestProcessStrategy& strategy) { | |
243 | |
244 // Check that the two bad URLs are returned. | |
245 base::Value* string_value = NULL; | |
246 StringValue badURL2("URL 2(bad)"), badURL4("URL 4(bad)"); | |
247 | |
248 EXPECT_TRUE(result->GetSize() == 2); | |
249 result->Get(0, &string_value); | |
250 EXPECT_TRUE(base::Value::Equals(string_value, &badURL2)); | |
251 result->Get(1, &string_value); | |
252 EXPECT_TRUE(base::Value::Equals(string_value, &badURL4)); | |
253 | |
254 // Check that both good URLs were visited. | |
255 std::string goodURL1("URL 1"), goodURL3("URL 3"); | |
256 EXPECT_TRUE(strategy.GetVisitedURLs()[0].compare(goodURL1) == 0 | |
257 && strategy.GetVisitedURLs()[1].compare(goodURL3) == 0); | |
258 | |
259 return true; | |
260 } | |
261 | |
262 protected: | |
263 std::vector<FilePath> temp_files_; | |
264 | |
265 private: | |
266 ScopedTempDir scoped_temp_user_data_dir_; | |
267 | |
268 DISALLOW_COPY_AND_ASSIGN(RecordApiTest); | |
269 }; | |
270 | |
271 | |
272 IN_PROC_BROWSER_TEST_F(RecordApiTest, CheckCapture) { | |
273 ScopedTempDir user_data_dir; | |
274 scoped_ptr<base::ListValue> result; | |
275 | |
276 EXPECT_TRUE(user_data_dir.CreateUniqueTempDir()); | |
277 scoped_refptr<CaptureURLsFunction> capture_URLs_function = | |
278 RunCapture(user_data_dir.path(), &result); | |
279 | |
280 // Check that user-data-dir switch has been properly overridden. | |
281 const TestProcessStrategy &strategy = | |
282 static_cast<const TestProcessStrategy &>( | |
283 capture_URLs_function->GetProcessStrategy()); | |
284 const CommandLine& command_line = strategy.GetCommandLine(); | |
285 | |
286 EXPECT_TRUE(command_line.HasSwitch(switches::kUserDataDir)); | |
287 EXPECT_TRUE(command_line.GetSwitchValuePath(switches::kUserDataDir) != | |
288 FilePath(kDummyDirName)); | |
289 | |
290 EXPECT_TRUE(VerifyURLHandling(result.get(), strategy)); | |
291 } | |
292 | |
293 #if defined(ADDRESS_SANITIZER) | |
294 // Times out under ASan, see http://crbug.com/130267. | |
295 #define MAYBE_CheckPlayback DISABLED_CheckPlayback | |
296 #else | |
297 #define MAYBE_CheckPlayback CheckPlayback | |
298 #endif | |
299 IN_PROC_BROWSER_TEST_F(RecordApiTest, MAYBE_CheckPlayback) { | |
300 ScopedTempDir user_data_dir; | |
301 | |
302 EXPECT_TRUE(user_data_dir.CreateUniqueTempDir()); | |
303 | |
304 // Assume this RunCapture operates w/o error, since it's tested | |
305 // elsewhere. | |
306 scoped_ptr<base::ListValue> capture_result; | |
307 RunCapture(user_data_dir.path(), &capture_result); | |
308 | |
309 std::string escaped_user_data_dir; | |
310 ReplaceChars(user_data_dir.path().AsUTF8Unsafe(), "\\", "\\\\", | |
311 &escaped_user_data_dir); | |
312 | |
313 scoped_refptr<ReplayURLsFunction> playback_function(new ReplayURLsFunction( | |
314 new TestProcessStrategy(&temp_files_))); | |
315 scoped_ptr<base::DictionaryValue> result(utils::ToDictionary( | |
316 utils::RunFunctionAndReturnResult(playback_function, | |
317 base::StringPrintf(kPlaybackArgs1, escaped_user_data_dir.c_str()), | |
318 browser()))); | |
319 | |
320 // Check that command line user-data-dir was overridden. (That | |
321 // it was *consistently* overridden in both capture and replay | |
322 // is verified elsewhere. | |
323 const TestProcessStrategy &strategy = | |
324 static_cast<const TestProcessStrategy &>( | |
325 playback_function->GetProcessStrategy()); | |
326 const CommandLine& command_line = strategy.GetCommandLine(); | |
327 | |
328 EXPECT_TRUE(command_line.HasSwitch(switches::kUserDataDir)); | |
329 EXPECT_TRUE(command_line.GetSwitchValuePath(switches::kUserDataDir) != | |
330 FilePath(kDummyDirName)); | |
331 | |
332 // Check that command line load-extension was overridden. | |
333 EXPECT_TRUE(command_line.HasSwitch(switches::kLoadExtension) && | |
334 command_line.GetSwitchValuePath(switches::kLoadExtension) | |
335 != FilePath(kDummyDirName)); | |
336 | |
337 // Check for return value with proper stats. | |
338 EXPECT_EQ(kTestStatistics, utils::GetString(result.get(), kStatsKey)); | |
339 | |
340 ListValue* errors = NULL; | |
341 EXPECT_TRUE(result->GetList(kErrorsKey, &errors)); | |
342 EXPECT_TRUE(VerifyURLHandling(errors, strategy)); | |
343 } | |
OLD | NEW |