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