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