Home | History | Annotate | Download | only in printing
      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 
     18 namespace {
     19 
     20 using printing::Image;
     21 
     22 const char kGenerateSwitch[] = "print-layout-generate";
     23 const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data");
     24 
     25 class 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.
    189 class PrintingLayoutTestHidden : public PrintingLayoutTest {
    190  public:
    191   PrintingLayoutTestHidden() {
    192     show_window_ = false;
    193   }
    194 };
    195 
    196 class 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.
    208 HWND 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.
    228 bool 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.
    244 class 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.
    281 TEST_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 
    300 struct TestPool {
    301   const char* source;
    302   const wchar_t* result;
    303 };
    304 
    305 const 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
    315 TEST_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.
    365 TEST_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
    398 TEST_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