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 "base/command_line.h" | |
6 #include "base/files/file_enumerator.h" | |
7 #include "base/files/file_path.h" | |
8 #include "base/files/file_util.h" | |
9 #include "base/location.h" | |
10 #include "base/macros.h" | |
11 #include "base/message_loop/message_loop.h" | |
12 #include "base/path_service.h" | |
13 #include "base/process/process_handle.h" | |
14 #include "base/single_thread_task_runner.h" | |
15 #include "base/strings/string_util.h" | |
16 #include "base/strings/utf_string_conversions.h" | |
17 #include "base/test/test_file_util.h" | |
18 #include "base/threading/simple_thread.h" | |
19 #include "base/threading/thread_task_runner_handle.h" | |
20 #include "chrome/browser/chrome_notification_types.h" | |
21 #include "chrome/browser/printing/print_job.h" | |
22 #include "chrome/browser/printing/print_view_manager.h" | |
23 #include "chrome/browser/ui/browser.h" | |
24 #include "chrome/browser/ui/browser_commands.h" | |
25 #include "chrome/browser/ui/tabs/tab_strip_model.h" | |
26 #include "chrome/common/chrome_paths.h" | |
27 #include "chrome/common/chrome_switches.h" | |
28 #include "chrome/test/base/in_process_browser_test.h" | |
29 #include "chrome/test/base/ui_test_utils.h" | |
30 #include "content/public/browser/notification_observer.h" | |
31 #include "content/public/browser/notification_registrar.h" | |
32 #include "content/public/browser/notification_service.h" | |
33 #include "net/test/embedded_test_server/embedded_test_server.h" | |
34 #include "printing/image.h" | |
35 #include "printing/printing_test.h" | |
36 | |
37 namespace { | |
38 | |
39 using printing::Image; | |
40 | |
41 const char kGenerateSwitch[] = "print-layout-generate"; | |
42 | |
43 class PrintingLayoutTest : public PrintingTest<InProcessBrowserTest>, | |
44 public content::NotificationObserver { | |
45 public: | |
46 PrintingLayoutTest() { | |
47 base::FilePath browser_directory; | |
48 PathService::Get(chrome::DIR_APP, &browser_directory); | |
49 emf_path_ = browser_directory.AppendASCII("metafile_dumps"); | |
50 } | |
51 | |
52 void SetUp() override { | |
53 // Make sure there is no left overs. | |
54 CleanupDumpDirectory(); | |
55 InProcessBrowserTest::SetUp(); | |
56 } | |
57 | |
58 void TearDown() override { | |
59 InProcessBrowserTest::TearDown(); | |
60 base::DeleteFile(emf_path_, true); | |
61 } | |
62 | |
63 void SetUpCommandLine(base::CommandLine* command_line) override { | |
64 command_line->AppendSwitchPath(switches::kDebugPrint, emf_path_); | |
65 } | |
66 | |
67 protected: | |
68 void PrintNowTab() { | |
69 registrar_.Add(this, chrome::NOTIFICATION_PRINT_JOB_EVENT, | |
70 content::NotificationService::AllSources()); | |
71 | |
72 content::WebContents* web_contents = | |
73 browser()->tab_strip_model()->GetActiveWebContents(); | |
74 printing::PrintViewManager::FromWebContents(web_contents)->PrintNow(); | |
75 content::RunMessageLoop(); | |
76 registrar_.RemoveAll(); | |
77 } | |
78 | |
79 virtual void Observe(int type, | |
80 const content::NotificationSource& source, | |
81 const content::NotificationDetails& details) { | |
82 ASSERT_EQ(chrome::NOTIFICATION_PRINT_JOB_EVENT, type); | |
83 switch (content::Details<printing::JobEventDetails>(details)->type()) { | |
84 case printing::JobEventDetails::JOB_DONE: { | |
85 // Succeeded. | |
86 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
87 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); | |
88 break; | |
89 } | |
90 case printing::JobEventDetails::USER_INIT_CANCELED: | |
91 case printing::JobEventDetails::FAILED: { | |
92 // Failed. | |
93 ASSERT_TRUE(false); | |
94 base::ThreadTaskRunnerHandle::Get()->PostTask( | |
95 FROM_HERE, base::MessageLoop::QuitWhenIdleClosure()); | |
96 break; | |
97 } | |
98 case printing::JobEventDetails::NEW_DOC: | |
99 case printing::JobEventDetails::USER_INIT_DONE: | |
100 case printing::JobEventDetails::DEFAULT_INIT_DONE: | |
101 case printing::JobEventDetails::NEW_PAGE: | |
102 case printing::JobEventDetails::PAGE_DONE: | |
103 case printing::JobEventDetails::DOC_DONE: | |
104 case printing::JobEventDetails::ALL_PAGES_REQUESTED: { | |
105 // Don't care. | |
106 break; | |
107 } | |
108 default: { | |
109 NOTREACHED(); | |
110 break; | |
111 } | |
112 } | |
113 } | |
114 | |
115 // Finds the dump for the last print job and compares it to the data named | |
116 // |verification_name|. Compares the saved printed job pixels with the test | |
117 // data pixels and returns the percentage of different pixels; 0 for success, | |
118 // [0, 100] for failure. | |
119 double CompareWithResult(const std::wstring& verification_name) { | |
120 base::FilePath test_result(ScanFiles(verification_name)); | |
121 if (test_result.value().empty()) { | |
122 // 100% different, the print job buffer is not there. | |
123 return 100.; | |
124 } | |
125 | |
126 base::FilePath base_path(ui_test_utils::GetTestFilePath( | |
127 base::FilePath().AppendASCII("printing"), base::FilePath())); | |
128 base::FilePath emf(base_path.Append(verification_name + L".emf")); | |
129 base::FilePath png(base_path.Append(verification_name + L".png")); | |
130 | |
131 base::FilePath cleartype( | |
132 base_path.Append(verification_name + L"_cleartype.png")); | |
133 // Looks for Cleartype override. | |
134 if (base::PathExists(cleartype) && IsClearTypeEnabled()) | |
135 png = cleartype; | |
136 | |
137 if (GenerateFiles()) { | |
138 // Copy the .emf and generate an .png. | |
139 base::CopyFile(test_result, emf); | |
140 Image emf_content(emf); | |
141 emf_content.SaveToPng(png); | |
142 // Saving is always fine. | |
143 return 0; | |
144 } else { | |
145 // File compare between test and result. | |
146 Image emf_content(emf); | |
147 Image test_content(test_result); | |
148 Image png_content(png); | |
149 double diff_emf = emf_content.PercentageDifferent(test_content); | |
150 | |
151 EXPECT_EQ(0., diff_emf) << base::WideToUTF8(verification_name) << | |
152 " original size:" << emf_content.size().ToString() << | |
153 " result size:" << test_content.size().ToString(); | |
154 if (diff_emf) { | |
155 // Backup the result emf file. | |
156 base::FilePath failed( | |
157 base_path.Append(verification_name + L"_failed.emf")); | |
158 base::CopyFile(test_result, failed); | |
159 } | |
160 | |
161 // This verification is only to know that the EMF rendering stays | |
162 // immutable. | |
163 double diff_png = emf_content.PercentageDifferent(png_content); | |
164 EXPECT_EQ(0., diff_png) << base::WideToUTF8(verification_name) << | |
165 " original size:" << emf_content.size().ToString() << | |
166 " result size:" << test_content.size().ToString(); | |
167 if (diff_png) { | |
168 // Backup the rendered emf file to detect the rendering difference. | |
169 base::FilePath rendering( | |
170 base_path.Append(verification_name + L"_rendering.png")); | |
171 emf_content.SaveToPng(rendering); | |
172 } | |
173 return std::max(diff_png, diff_emf); | |
174 } | |
175 } | |
176 | |
177 // Makes sure the directory exists and is empty. | |
178 void CleanupDumpDirectory() { | |
179 EXPECT_TRUE(base::DieFileDie(emf_path_, true)); | |
180 EXPECT_TRUE(base::CreateDirectory(emf_path_)); | |
181 } | |
182 | |
183 // Returns if Clear Type is currently enabled. | |
184 static bool IsClearTypeEnabled() { | |
185 BOOL ct_enabled = 0; | |
186 if (SystemParametersInfo(SPI_GETCLEARTYPE, 0, &ct_enabled, 0) && ct_enabled) | |
187 return true; | |
188 UINT smoothing = 0; | |
189 if (SystemParametersInfo(SPI_GETFONTSMOOTHINGTYPE, 0, &smoothing, 0) && | |
190 smoothing == FE_FONTSMOOTHINGCLEARTYPE) | |
191 return true; | |
192 return false; | |
193 } | |
194 | |
195 private: | |
196 // Verifies that there is one .emf and one .prn file in the dump directory. | |
197 // Returns the path of the .emf file and deletes the .prn file. | |
198 std::wstring ScanFiles(const std::wstring& verification_name) { | |
199 // Try to 10 seconds. | |
200 std::wstring emf_file; | |
201 std::wstring prn_file; | |
202 bool found_emf = false; | |
203 bool found_prn = false; | |
204 for (int i = 0; i < 100; ++i) { | |
205 base::FileEnumerator enumerator(emf_path_, false, | |
206 base::FileEnumerator::FILES); | |
207 emf_file.clear(); | |
208 prn_file.clear(); | |
209 found_emf = false; | |
210 found_prn = false; | |
211 base::FilePath file; | |
212 while (!(file = enumerator.Next()).empty()) { | |
213 std::wstring ext = file.Extension(); | |
214 if (base::EqualsCaseInsensitiveASCII(base::WideToUTF8(ext), ".emf")) { | |
215 EXPECT_FALSE(found_emf) << "Found a leftover .EMF file: \"" << | |
216 emf_file << "\" and \"" << file.value() << | |
217 "\" when looking for \"" << verification_name << "\""; | |
218 found_emf = true; | |
219 emf_file = file.value(); | |
220 continue; | |
221 } | |
222 if (base::EqualsCaseInsensitiveASCII(base::WideToUTF8(ext), ".prn")) { | |
223 EXPECT_FALSE(found_prn) << "Found a leftover .PRN file: \"" << | |
224 prn_file << "\" and \"" << file.value() << | |
225 "\" when looking for \"" << verification_name << "\""; | |
226 prn_file = file.value(); | |
227 found_prn = true; | |
228 base::DeleteFile(file, false); | |
229 continue; | |
230 } | |
231 EXPECT_TRUE(false); | |
232 } | |
233 if (found_emf && found_prn) | |
234 break; | |
235 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(100)); | |
236 } | |
237 EXPECT_TRUE(found_emf) << ".PRN file is: " << prn_file; | |
238 EXPECT_TRUE(found_prn) << ".EMF file is: " << emf_file; | |
239 return emf_file; | |
240 } | |
241 | |
242 static bool GenerateFiles() { | |
243 return base::CommandLine::ForCurrentProcess()->HasSwitch(kGenerateSwitch); | |
244 } | |
245 | |
246 base::FilePath emf_path_; | |
247 content::NotificationRegistrar registrar_; | |
248 | |
249 DISALLOW_COPY_AND_ASSIGN(PrintingLayoutTest); | |
250 }; | |
251 | |
252 class PrintingLayoutTextTest : public PrintingLayoutTest { | |
253 typedef PrintingLayoutTest Parent; | |
254 public: | |
255 // Returns if the test is disabled. | |
256 // http://crbug.com/64869 Until the issue is fixed, disable the test if | |
257 // ClearType is enabled. | |
258 static bool IsTestCaseDisabled() { | |
259 return Parent::IsTestCaseDisabled() || IsClearTypeEnabled(); | |
260 } | |
261 }; | |
262 | |
263 // Finds the first dialog window owned by owner_process. | |
264 HWND FindDialogWindow(DWORD owner_process) { | |
265 HWND dialog_window(NULL); | |
266 for (;;) { | |
267 dialog_window = FindWindowEx(NULL, | |
268 dialog_window, | |
269 MAKEINTATOM(32770), | |
270 NULL); | |
271 if (!dialog_window) | |
272 break; | |
273 | |
274 // The dialog must be owned by our target process. | |
275 DWORD process_id = 0; | |
276 GetWindowThreadProcessId(dialog_window, &process_id); | |
277 if (process_id == owner_process) | |
278 break; | |
279 } | |
280 return dialog_window; | |
281 } | |
282 | |
283 // Tries to close a dialog window. | |
284 bool CloseDialogWindow(HWND dialog_window) { | |
285 LRESULT res = SendMessage(dialog_window, DM_GETDEFID, 0, 0); | |
286 if (!res) | |
287 return false; | |
288 EXPECT_EQ(DC_HASDEFID, HIWORD(res)); | |
289 WORD print_button_id = LOWORD(res); | |
290 res = SendMessage( | |
291 dialog_window, | |
292 WM_COMMAND, | |
293 print_button_id, | |
294 reinterpret_cast<LPARAM>(GetDlgItem(dialog_window, print_button_id))); | |
295 return res == 0; | |
296 } | |
297 | |
298 // Dismiss the first dialog box owned by owner_process by "executing" the | |
299 // default button. | |
300 class DismissTheWindow : public base::DelegateSimpleThread::Delegate { | |
301 public: | |
302 DismissTheWindow() | |
303 : owner_process_(base::GetCurrentProcId()) { | |
304 } | |
305 | |
306 virtual void Run() { | |
307 HWND dialog_window; | |
308 for (;;) { | |
309 // First enumerate the windows. | |
310 dialog_window = FindDialogWindow(owner_process_); | |
311 | |
312 // Try to close it. | |
313 if (dialog_window) { | |
314 if (CloseDialogWindow(dialog_window)) { | |
315 break; | |
316 } | |
317 } | |
318 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); | |
319 } | |
320 | |
321 // Now verify that it indeed closed itself. | |
322 while (IsWindow(dialog_window)) { | |
323 CloseDialogWindow(dialog_window); | |
324 base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10)); | |
325 } | |
326 } | |
327 | |
328 DWORD owner_process() { return owner_process_; } | |
329 | |
330 private: | |
331 DWORD owner_process_; | |
332 }; | |
333 | |
334 } // namespace | |
335 | |
336 // Fails, see http://crbug.com/7721. | |
337 IN_PROC_BROWSER_TEST_F(PrintingLayoutTextTest, DISABLED_Complex) { | |
338 if (IsTestCaseDisabled()) | |
339 return; | |
340 | |
341 DismissTheWindow dismisser; | |
342 base::DelegateSimpleThread close_printdlg_thread(&dismisser, | |
343 "close_printdlg_thread"); | |
344 | |
345 // Print a document, check its output. | |
346 ASSERT_TRUE(embedded_test_server()->Start()); | |
347 | |
348 ui_test_utils::NavigateToURL( | |
349 browser(), embedded_test_server()->GetURL("/printing/test1.html")); | |
350 close_printdlg_thread.Start(); | |
351 PrintNowTab(); | |
352 close_printdlg_thread.Join(); | |
353 EXPECT_EQ(0., CompareWithResult(L"test1")); | |
354 } | |
355 | |
356 struct TestPool { | |
357 const char* source; | |
358 const wchar_t* result; | |
359 }; | |
360 | |
361 const TestPool kTestPool[] = { | |
362 // ImagesB&W | |
363 "/printing/test2.html", L"test2", | |
364 // ImagesTransparent | |
365 "/printing/test3.html", L"test3", | |
366 // ImageColor | |
367 "/printing/test4.html", L"test4", | |
368 }; | |
369 | |
370 // http://crbug.com/7721 | |
371 IN_PROC_BROWSER_TEST_F(PrintingLayoutTest, DISABLED_ManyTimes) { | |
372 if (IsTestCaseDisabled()) | |
373 return; | |
374 | |
375 ASSERT_TRUE(embedded_test_server()->Start()); | |
376 | |
377 DismissTheWindow dismisser; | |
378 | |
379 ASSERT_GT(arraysize(kTestPool), 0u); | |
380 for (int i = 0; i < arraysize(kTestPool); ++i) { | |
381 if (i) | |
382 CleanupDumpDirectory(); | |
383 const TestPool& test = kTestPool[i % arraysize(kTestPool)]; | |
384 ui_test_utils::NavigateToURL(browser(), | |
385 embedded_test_server()->GetURL(test.source)); | |
386 base::DelegateSimpleThread close_printdlg_thread1(&dismisser, | |
387 "close_printdlg_thread"); | |
388 EXPECT_EQ(NULL, FindDialogWindow(dismisser.owner_process())); | |
389 close_printdlg_thread1.Start(); | |
390 PrintNowTab(); | |
391 close_printdlg_thread1.Join(); | |
392 EXPECT_EQ(0., CompareWithResult(test.result)) << test.result; | |
393 CleanupDumpDirectory(); | |
394 base::DelegateSimpleThread close_printdlg_thread2(&dismisser, | |
395 "close_printdlg_thread"); | |
396 EXPECT_EQ(NULL, FindDialogWindow(dismisser.owner_process())); | |
397 close_printdlg_thread2.Start(); | |
398 PrintNowTab(); | |
399 close_printdlg_thread2.Join(); | |
400 EXPECT_EQ(0., CompareWithResult(test.result)) << test.result; | |
401 CleanupDumpDirectory(); | |
402 base::DelegateSimpleThread close_printdlg_thread3(&dismisser, | |
403 "close_printdlg_thread"); | |
404 EXPECT_EQ(NULL, FindDialogWindow(dismisser.owner_process())); | |
405 close_printdlg_thread3.Start(); | |
406 PrintNowTab(); | |
407 close_printdlg_thread3.Join(); | |
408 EXPECT_EQ(0., CompareWithResult(test.result)) << test.result; | |
409 CleanupDumpDirectory(); | |
410 base::DelegateSimpleThread close_printdlg_thread4(&dismisser, | |
411 "close_printdlg_thread"); | |
412 EXPECT_EQ(NULL, FindDialogWindow(dismisser.owner_process())); | |
413 close_printdlg_thread4.Start(); | |
414 PrintNowTab(); | |
415 close_printdlg_thread4.Join(); | |
416 EXPECT_EQ(0., CompareWithResult(test.result)) << test.result; | |
417 } | |
418 } | |
419 | |
420 // Prints a popup and immediately closes it. Disabled because it crashes. | |
421 IN_PROC_BROWSER_TEST_F(PrintingLayoutTest, DISABLED_Delayed) { | |
422 if (IsTestCaseDisabled()) | |
423 return; | |
424 | |
425 ASSERT_TRUE(embedded_test_server()->Start()); | |
426 | |
427 { | |
428 bool is_timeout = true; | |
429 GURL url = | |
430 embedded_test_server()->GetURL("/printing/popup_delayed_print.htm"); | |
431 ui_test_utils::NavigateToURL(browser(), url); | |
432 | |
433 DismissTheWindow dismisser; | |
434 base::DelegateSimpleThread close_printdlg_thread(&dismisser, | |
435 "close_printdlg_thread"); | |
436 close_printdlg_thread.Start(); | |
437 close_printdlg_thread.Join(); | |
438 | |
439 // Force a navigation elsewhere to verify that it's fine with it. | |
440 url = embedded_test_server()->GetURL("/printing/test1.html"); | |
441 ui_test_utils::NavigateToURL(browser(), url); | |
442 } | |
443 chrome::CloseWindow(browser()); | |
444 content::RunAllPendingInMessageLoop(); | |
445 | |
446 EXPECT_EQ(0., CompareWithResult(L"popup_delayed_print")) | |
447 << L"popup_delayed_print"; | |
448 } | |
449 | |
450 // Prints a popup and immediately closes it. http://crbug.com/7721 | |
451 IN_PROC_BROWSER_TEST_F(PrintingLayoutTest, DISABLED_IFrame) { | |
452 if (IsTestCaseDisabled()) | |
453 return; | |
454 | |
455 ASSERT_TRUE(embedded_test_server()->Start()); | |
456 | |
457 { | |
458 GURL url = embedded_test_server()->GetURL("/printing/iframe.htm"); | |
459 ui_test_utils::NavigateToURL(browser(), url); | |
460 | |
461 DismissTheWindow dismisser; | |
462 base::DelegateSimpleThread close_printdlg_thread(&dismisser, | |
463 "close_printdlg_thread"); | |
464 close_printdlg_thread.Start(); | |
465 close_printdlg_thread.Join(); | |
466 | |
467 // Force a navigation elsewhere to verify that it's fine with it. | |
468 url = embedded_test_server()->GetURL("/printing/test1.html"); | |
469 ui_test_utils::NavigateToURL(browser(), url); | |
470 } | |
471 chrome::CloseWindow(browser()); | |
472 content::RunAllPendingInMessageLoop(); | |
473 | |
474 EXPECT_EQ(0., CompareWithResult(L"iframe")) << L"iframe"; | |
475 } | |
OLD | NEW |