OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/chromeos/login/screenshot_tester.h" | 5 #include "chrome/browser/chromeos/login/screenshot_tester.h" |
6 | 6 |
7 #include "ash/shell.h" | 7 #include "ash/shell.h" |
8 #include "base/base_export.h" | 8 #include "base/base_export.h" |
9 #include "base/bind_internal.h" | 9 #include "base/bind_internal.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
| 11 #include "base/logging.h" |
11 #include "base/memory/weak_ptr.h" | 12 #include "base/memory/weak_ptr.h" |
12 #include "base/prefs/pref_service.h" | 13 #include "base/prefs/pref_service.h" |
13 #include "base/run_loop.h" | 14 #include "base/run_loop.h" |
14 #include "chrome/browser/browser_process.h" | 15 #include "chrome/browser/browser_process.h" |
15 #include "chrome/common/pref_names.h" | 16 #include "chrome/common/pref_names.h" |
16 #include "chromeos/chromeos_switches.h" | 17 #include "chromeos/chromeos_switches.h" |
17 #include "content/public/browser/browser_thread.h" | 18 #include "content/public/browser/browser_thread.h" |
| 19 #include "testing/gtest/include/gtest/gtest.h" |
| 20 #include "third_party/skia/include/core/SkBitmap.h" |
| 21 #include "third_party/skia/include/core/SkCanvas.h" |
18 #include "ui/compositor/compositor_switches.h" | 22 #include "ui/compositor/compositor_switches.h" |
| 23 #include "ui/gfx/codec/png_codec.h" |
19 #include "ui/gfx/image/image.h" | 24 #include "ui/gfx/image/image.h" |
20 #include "ui/snapshot/snapshot.h" | 25 #include "ui/snapshot/snapshot.h" |
21 | 26 |
| 27 namespace { |
| 28 |
| 29 // Sets test mode for screenshot testing. |
| 30 const char kTestMode[] = "test"; |
| 31 |
| 32 // Sets update mode for screenshot testing. |
| 33 const char kUpdateMode[] = "update"; |
| 34 |
| 35 } // namespace |
| 36 |
22 namespace chromeos { | 37 namespace chromeos { |
23 | 38 |
24 ScreenshotTester::ScreenshotTester() : weak_factory_(this) { | 39 ScreenshotTester::ScreenshotTester() : test_mode_(false), weak_factory_(this) { |
25 } | 40 } |
26 | 41 |
27 ScreenshotTester::~ScreenshotTester() { | 42 ScreenshotTester::~ScreenshotTester() { |
28 } | 43 } |
29 | 44 |
30 bool ScreenshotTester::TryInitialize() { | 45 bool ScreenshotTester::TryInitialize() { |
31 CommandLine& command_line = *CommandLine::ForCurrentProcess(); | 46 CommandLine& command_line = *CommandLine::ForCurrentProcess(); |
32 if (!command_line.HasSwitch(switches::kEnableScreenshotTesting)) | 47 if (!command_line.HasSwitch(switches::kEnableScreenshotTestingWithMode)) |
33 return false; | 48 return false; |
34 if (!command_line.HasSwitch(::switches::kEnablePixelOutputInTests) || | 49 if (!command_line.HasSwitch(::switches::kEnablePixelOutputInTests) || |
35 !command_line.HasSwitch(::switches::kUIEnableImplSidePainting)) { | 50 !command_line.HasSwitch(::switches::kUIEnableImplSidePainting)) { |
36 // TODO(elizavetai): make turning on --enable-pixel-output-in-tests | 51 // TODO(elizavetai): make turning on --enable-pixel-output-in-tests |
37 // and --ui-enable-impl-side-painting automatical. | 52 // and --ui-enable-impl-side-painting automatical. |
38 LOG(ERROR) << "--enable-pixel-output-in-tests and " | 53 LOG(ERROR) << "--enable-pixel-output-in-tests and " |
39 << "--ui-enable-impl-side-painting are required to take " | 54 << "--ui-enable-impl-side-painting are required to take " |
40 << "screenshots"; | 55 << "screenshots"; |
41 return false; | 56 return false; |
42 } | 57 } |
43 if (!command_line.HasSwitch(switches::kScreenshotDestinationDir)) { | 58 |
44 LOG(ERROR) << "No destination for screenshots specified"; | 59 std::string mode = command_line.GetSwitchValueASCII( |
45 return false; | 60 switches::kEnableScreenshotTestingWithMode); |
| 61 if (mode != kUpdateMode && mode != kTestMode) { |
| 62 CHECK(false) << "Invalid mode for screenshot testing: " << mode; |
46 } | 63 } |
47 screenshot_dest_ = command_line.GetSwitchValuePath( | 64 |
48 chromeos::switches::kScreenshotDestinationDir); | 65 if (!command_line.HasSwitch(chromeos::switches::kGoldenScreenshotsDir)) { |
| 66 CHECK(false) << "No directory for golden screenshots specified"; |
| 67 } |
| 68 |
| 69 golden_screenshots_dir_ = |
| 70 command_line.GetSwitchValuePath(switches::kGoldenScreenshotsDir); |
| 71 |
| 72 if (mode == kTestMode) { |
| 73 test_mode_ = true; |
| 74 if (!command_line.HasSwitch(switches::kArtifactsDir)) { |
| 75 artifacts_dir_ = golden_screenshots_dir_; |
| 76 LOG(WARNING) |
| 77 << "No directory for artifact storing specified. Artifacts will be" |
| 78 << "saved at golden screenshots directory."; |
| 79 } else { |
| 80 artifacts_dir_ = command_line.GetSwitchValuePath(switches::kArtifactsDir); |
| 81 } |
| 82 } |
49 return true; | 83 return true; |
50 } | 84 } |
51 | 85 |
52 void ScreenshotTester::Run(const std::string& file_name) { | 86 void ScreenshotTester::Run(const std::string& test_name) { |
53 TakeScreenshot(); | 87 test_name_ = test_name; |
54 PNGFile current_screenshot = screenshot_; | 88 PNGFile current_screenshot = TakeScreenshot(); |
55 UpdateGoldenScreenshot(file_name, current_screenshot); | 89 if (test_mode_) { |
| 90 PNGFile golden_screenshot = LoadGoldenScreenshot(); |
| 91 VLOG(0) << "Loaded golden screenshot"; |
| 92 CompareScreenshots(golden_screenshot, current_screenshot); |
| 93 } else { |
| 94 UpdateGoldenScreenshot(current_screenshot); |
| 95 } |
56 } | 96 } |
57 | 97 |
58 void ScreenshotTester::UpdateGoldenScreenshot(const std::string& file_name, | 98 void ScreenshotTester::UpdateGoldenScreenshot(PNGFile png_data) { |
59 PNGFile png_data) { | 99 CHECK(SaveImage("golden_screenshot", golden_screenshots_dir_, png_data)); |
| 100 } |
| 101 |
| 102 bool ScreenshotTester::SaveImage(const std::string& file_name, |
| 103 const base::FilePath& screenshot_dir, |
| 104 PNGFile png_data) { |
60 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 105 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 106 base::FilePath screenshot_path = |
| 107 screenshot_dir.AppendASCII(test_name_ + "_" + file_name + ".png"); |
61 if (!png_data) { | 108 if (!png_data) { |
62 LOG(ERROR) << "Can't take a screenshot"; | 109 LOG(ERROR) << "Can't take a screenshot"; |
63 return; | 110 return false; |
64 } | 111 } |
65 base::FilePath golden_screenshot_path = | 112 if (!base::CreateDirectory(screenshot_path.DirName())) { |
66 screenshot_dest_.AppendASCII(file_name + ".png"); | 113 LOG(ERROR) << "Can't create a directory " |
67 if (!base::CreateDirectory(golden_screenshot_path.DirName())) { | 114 << screenshot_path.DirName().value(); |
68 LOG(ERROR) << "Can't create a directory "; | 115 return false; |
69 return; | |
70 } | 116 } |
71 if (static_cast<size_t>( | 117 if (static_cast<size_t>( |
72 base::WriteFile(golden_screenshot_path, | 118 base::WriteFile(screenshot_path, |
73 reinterpret_cast<char*>(&(png_data->data()[0])), | 119 reinterpret_cast<char*>(&(png_data->data()[0])), |
74 png_data->size())) != png_data->size()) { | 120 png_data->size())) != png_data->size()) { |
75 LOG(ERROR) << "Can't save screenshot"; | 121 LOG(ERROR) << "Can't save screenshot " << file_name; |
| 122 return false; |
76 } | 123 } |
77 VLOG(0) << "Golden screenshot updated successfully"; | 124 VLOG(0) << "Screenshot " << file_name << ".png saved successfully to " |
| 125 << screenshot_dir.value(); |
| 126 return true; |
78 } | 127 } |
79 | 128 |
80 void ScreenshotTester::ReturnScreenshot(PNGFile png_data) { | 129 void ScreenshotTester::ReturnScreenshot(const PNGFile& screenshot, |
81 // TODO(elizavetai): rewrite this function so that TakeScreenshot | 130 PNGFile png_data) { |
82 // could return a |PNGFile| -- current screenshot. | |
83 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 131 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
84 screenshot_ = png_data; | 132 screenshot->data() = png_data->data(); |
85 content::BrowserThread::PostTask( | 133 content::BrowserThread::PostTask( |
86 content::BrowserThread::UI, FROM_HERE, run_loop_quitter_); | 134 content::BrowserThread::UI, FROM_HERE, run_loop_quitter_); |
87 } | 135 } |
88 | 136 |
89 void ScreenshotTester::TakeScreenshot() { | 137 ScreenshotTester::PNGFile ScreenshotTester::TakeScreenshot() { |
90 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); | 138 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); |
91 aura::Window* primary_window = ash::Shell::GetPrimaryRootWindow(); | 139 aura::Window* primary_window = ash::Shell::GetPrimaryRootWindow(); |
92 gfx::Rect rect = primary_window->bounds(); | 140 gfx::Rect rect = primary_window->bounds(); |
| 141 PNGFile screenshot = new base::RefCountedBytes; |
93 ui::GrabWindowSnapshotAsync(primary_window, | 142 ui::GrabWindowSnapshotAsync(primary_window, |
94 rect, | 143 rect, |
95 content::BrowserThread::GetBlockingPool(), | 144 content::BrowserThread::GetBlockingPool(), |
96 base::Bind(&ScreenshotTester::ReturnScreenshot, | 145 base::Bind(&ScreenshotTester::ReturnScreenshot, |
97 weak_factory_.GetWeakPtr())); | 146 weak_factory_.GetWeakPtr(), |
| 147 screenshot)); |
98 base::RunLoop run_loop; | 148 base::RunLoop run_loop; |
99 run_loop_quitter_ = run_loop.QuitClosure(); | 149 run_loop_quitter_ = run_loop.QuitClosure(); |
100 run_loop.Run(); | 150 run_loop.Run(); |
| 151 return screenshot; |
| 152 } |
| 153 |
| 154 ScreenshotTester::PNGFile ScreenshotTester::LoadGoldenScreenshot() { |
| 155 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 156 |
| 157 base::FilePath screenshot_path = golden_screenshots_dir_.AppendASCII( |
| 158 test_name_ + "_golden_screenshot.png"); |
| 159 if (!base::PathExists(screenshot_path)) { |
| 160 CHECK(false) << "Can't find a golden screenshot for this test"; |
| 161 } |
| 162 |
| 163 int64 golden_screenshot_size; |
| 164 base::GetFileSize(screenshot_path, &golden_screenshot_size); |
| 165 |
| 166 if (golden_screenshot_size == -1) { |
| 167 CHECK(false) << "Can't get golden screenshot size"; |
| 168 } |
| 169 PNGFile png_data = new base::RefCountedBytes; |
| 170 png_data->data().resize(golden_screenshot_size); |
| 171 base::ReadFile(screenshot_path, |
| 172 reinterpret_cast<char*>(&(png_data->data()[0])), |
| 173 golden_screenshot_size); |
| 174 |
| 175 return png_data; |
| 176 } |
| 177 |
| 178 void ScreenshotTester::CompareScreenshots(PNGFile model, PNGFile sample) { |
| 179 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); |
| 180 |
| 181 ASSERT_TRUE(model.get()); |
| 182 ASSERT_TRUE(sample.get()); |
| 183 |
| 184 SkBitmap model_bitmap; |
| 185 SkBitmap sample_bitmap; |
| 186 gfx::PNGCodec::Decode(reinterpret_cast<unsigned char*>(&(model->data()[0])), |
| 187 model->data().size(), |
| 188 &model_bitmap); |
| 189 gfx::PNGCodec::Decode(reinterpret_cast<unsigned char*>(&(sample->data()[0])), |
| 190 sample->data().size(), |
| 191 &sample_bitmap); |
| 192 |
| 193 ASSERT_EQ(model_bitmap.width(), sample_bitmap.width()); |
| 194 ASSERT_EQ(model_bitmap.height(), sample_bitmap.height()); |
| 195 |
| 196 bool screenshots_match = true; |
| 197 |
| 198 SkCanvas diff_canvas(sample_bitmap); |
| 199 for (int i = 0; i < model_bitmap.width(); i++) |
| 200 for (int j = 0; j < model_bitmap.height(); j++) { |
| 201 if (model_bitmap.getColor(i, j) == sample_bitmap.getColor(i, j)) { |
| 202 diff_canvas.drawPoint(i, j, SK_ColorWHITE); |
| 203 } else { |
| 204 screenshots_match = false; |
| 205 diff_canvas.drawPoint(i, j, SK_ColorRED); |
| 206 } |
| 207 } |
| 208 |
| 209 if (!screenshots_match) { |
| 210 CHECK(SaveImage("failed_screenshot", artifacts_dir_, sample)); |
| 211 gfx::PNGCodec::EncodeBGRASkBitmap(sample_bitmap, false, &sample->data()); |
| 212 CHECK(SaveImage("difference", artifacts_dir_, sample)); |
| 213 LOG(ERROR) |
| 214 << "Screenshots testing failed. Screenshots differ in some pixels"; |
| 215 VLOG(0) << "Current screenshot and diff picture saved to " |
| 216 << artifacts_dir_.value(); |
| 217 } else { |
| 218 VLOG(0) << "Current screenshot matches the golden screenshot. Screenshot " |
| 219 "testing passed."; |
| 220 } |
| 221 ASSERT_TRUE(screenshots_match); |
101 } | 222 } |
102 | 223 |
103 } // namespace chromeos | 224 } // namespace chromeos |
OLD | NEW |