diff options
author | elizavetai@chromium.org <elizavetai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-21 21:43:59 +0000 |
---|---|---|
committer | elizavetai@chromium.org <elizavetai@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-21 21:43:59 +0000 |
commit | 4d1b6920f31b3d19ed54e01593304be8e5d1a7be (patch) | |
tree | 9d56d6edc1cf716dfc635288e11444ccab8856a5 | |
parent | b22de071523b8ebc09837f5a5b1662708ced71f1 (diff) | |
download | chromium_src-4d1b6920f31b3d19ed54e01593304be8e5d1a7be.zip chromium_src-4d1b6920f31b3d19ed54e01593304be8e5d1a7be.tar.gz chromium_src-4d1b6920f31b3d19ed54e01593304be8e5d1a7be.tar.bz2 |
Taking screenshots while running a test
Adds a class ScreenshotTester, which now adds an opportunity to take a screenshot while running a test and then
save it to a directory specified by some switch. it is now fully included in LoginUIVisible test.
Taking screenshots also requires waiting until all the animation finishes loading. To handle with it, a class AnimationDelayHandler
is implemented (in LoginUIVisible).
To turn taking screenshots on, switch --enable-test-screenshots should be used. To specify the directory where all the screenshots will be
stored, use --screenshot-dest=value, where value is the full path to the directory.
It is planned that working with this class will have two modes: taking a new screenshot and saving it, and taking a screenshot and comparing
it with the previously stored one. Now only the first mode is implemented, and running it also requires a switch --update-golden-screenshots.
Pixel output is usually off in the tests, so before taking screenshots switches --enable-pixel-output-in-tests and --ui-enable-impl-side-painting should be also used.
BUG=395653
Review URL: https://codereview.chromium.org/405093003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@284504 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | chrome/browser/chromeos/login/login_ui_browsertest.cc | 140 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/screenshot_tester.cc | 103 | ||||
-rw-r--r-- | chrome/browser/chromeos/login/screenshot_tester.h | 67 | ||||
-rw-r--r-- | chrome/chrome_tests.gypi | 2 | ||||
-rw-r--r-- | chromeos/chromeos_switches.cc | 7 | ||||
-rw-r--r-- | chromeos/chromeos_switches.h | 4 |
6 files changed, 321 insertions, 2 deletions
diff --git a/chrome/browser/chromeos/login/login_ui_browsertest.cc b/chrome/browser/chromeos/login/login_ui_browsertest.cc index 4f545ed..29bf936 100644 --- a/chrome/browser/chromeos/login/login_ui_browsertest.cc +++ b/chrome/browser/chromeos/login/login_ui_browsertest.cc @@ -2,12 +2,23 @@ // 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/prefs/pref_service.h" +#include "base/timer/timer.h" #include "chrome/browser/browser_process.h" +#include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/login/login_manager_test.h" +#include "chrome/browser/chromeos/login/screenshot_tester.h" #include "chrome/browser/chromeos/login/startup_utils.h" #include "chrome/browser/chromeos/login/test/oobe_screen_waiter.h" +#include "chrome/browser/ui/webui/chromeos/login/signin_screen_handler.h" #include "chrome/common/pref_names.h" +#include "content/public/browser/browser_thread.h" +#include "content/public/browser/notification_observer.h" +#include "content/public/browser/notification_registrar.h" +#include "content/public/browser/notification_service.h" +#include "content/public/browser/notification_types.h" +#include "ui/compositor/compositor_switches.h" namespace chromeos { @@ -16,12 +27,136 @@ namespace { const char kTestUser1[] = "test-user1@gmail.com"; const char kTestUser2[] = "test-user2@gmail.com"; +// A class that provides a way to wait until all the animation +// has loaded and is properly shown on the screen. +class AnimationDelayHandler : public content::NotificationObserver { + public: + AnimationDelayHandler(); + + // Should be run as early as possible on order not to miss notifications. + // It seems though that it can't be moved to constructor(?). + void Initialize(); + + // Override from content::NotificationObserver. + virtual void Observe(int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) OVERRIDE; + + // This method checks if animation is loaded, and, if not, + // waits until it is loaded and properly shown on the screen. + void WaitUntilAnimationLoads(); + + private: + void InitializeForWaiting(const base::Closure& quitter); + + // It turns out that it takes some more time for the animation + // to finish loading even after all the notifications have been sent. + // That happens due to some properties of compositor. + // This method should be used after getting all the necessary notifications + // to wait for the actual load of animation. + void SynchronizeAnimationLoadWithCompositor(); + + // This method exists only because of the current implementation of + // SynchronizeAnimationLoadWithCompositor. + void HandleAnimationLoad(); + + // Returns true if, according to the notificatons received, animation has + // finished loading by now. + bool IsAnimationLoaded(); + + base::OneShotTimer<AnimationDelayHandler> timer_; + bool waiter_loop_is_on_; + bool login_or_lock_webui_visible_; + base::Closure animation_waiter_quitter_; + content::NotificationRegistrar registrar_; +}; + } // anonymous namespace +AnimationDelayHandler::AnimationDelayHandler() + : waiter_loop_is_on_(false), login_or_lock_webui_visible_(false) { +} + +void AnimationDelayHandler::Initialize() { + waiter_loop_is_on_ = false; + registrar_.Add(this, + chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, + content::NotificationService::AllSources()); +} + +bool AnimationDelayHandler::IsAnimationLoaded() { + return login_or_lock_webui_visible_; +} + +void AnimationDelayHandler::Observe( + int type, + const content::NotificationSource& source, + const content::NotificationDetails& details) { + if (chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE == type) { + login_or_lock_webui_visible_ = true; + registrar_.Remove(this, + chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, + content::NotificationService::AllSources()); + } + if (waiter_loop_is_on_ && IsAnimationLoaded()) { + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, animation_waiter_quitter_); + } +} + +void AnimationDelayHandler::InitializeForWaiting(const base::Closure& quitter) { + waiter_loop_is_on_ = true; + animation_waiter_quitter_ = quitter; +} + +void AnimationDelayHandler::HandleAnimationLoad() { + timer_.Stop(); + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, animation_waiter_quitter_); +} + +// Current implementation is a mockup. +// It simply waits for 5 seconds, assuming that this time is enough for +// animation to load completely. +// TODO(elizavetai): Replace this temporary hack with getting a +// valid notification from compositor. +void AnimationDelayHandler::SynchronizeAnimationLoadWithCompositor() { + base::RunLoop waiter; + animation_waiter_quitter_ = waiter.QuitClosure(); + timer_.Start(FROM_HERE, + base::TimeDelta::FromSeconds(5), + this, + &AnimationDelayHandler::HandleAnimationLoad); + waiter.Run(); +} + +void AnimationDelayHandler::WaitUntilAnimationLoads() { + if (!IsAnimationLoaded()) { + base::RunLoop animation_waiter; + InitializeForWaiting(animation_waiter.QuitClosure()); + animation_waiter.Run(); + } + SynchronizeAnimationLoadWithCompositor(); +} + class LoginUITest : public chromeos::LoginManagerTest { public: + bool enable_test_screenshots_; LoginUITest() : LoginManagerTest(false) {} virtual ~LoginUITest() {} + virtual void SetUpOnMainThread() OVERRIDE { + enable_test_screenshots_ = screenshot_tester.TryInitialize(); + if (enable_test_screenshots_) { + animation_delay_handler.Initialize(); + } else { + LOG(WARNING) << "Screenshots will not be taken"; + } + LoginManagerTest::SetUpOnMainThread(); + } + + protected: + AnimationDelayHandler animation_delay_handler; + ScreenshotTester screenshot_tester; }; IN_PROC_BROWSER_TEST_F(LoginUITest, PRE_LoginUIVisible) { @@ -41,11 +176,14 @@ IN_PROC_BROWSER_TEST_F(LoginUITest, LoginUIVisible) { ".user.emailAddress == '" + std::string(kTestUser1) + "'"); JSExpect("document.querySelectorAll('.pod:not(#user-pod-template)')[1]" ".user.emailAddress == '" + std::string(kTestUser2) + "'"); + if (enable_test_screenshots_) { + animation_delay_handler.WaitUntilAnimationLoads(); + screenshot_tester.Run("LoginUITest-LoginUIVisible"); + } } IN_PROC_BROWSER_TEST_F(LoginUITest, PRE_InterruptedAutoStartEnrollment) { StartupUtils::MarkOobeCompleted(); - PrefService* prefs = g_browser_process->local_state(); prefs->SetBoolean(prefs::kDeviceEnrollmentAutoStart, true); } diff --git a/chrome/browser/chromeos/login/screenshot_tester.cc b/chrome/browser/chromeos/login/screenshot_tester.cc new file mode 100644 index 0000000..683f987 --- /dev/null +++ b/chrome/browser/chromeos/login/screenshot_tester.cc @@ -0,0 +1,103 @@ +// Copyright 2014 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 "chrome/browser/chromeos/login/screenshot_tester.h" + +#include "ash/shell.h" +#include "base/base_export.h" +#include "base/bind_internal.h" +#include "base/command_line.h" +#include "base/memory/weak_ptr.h" +#include "base/prefs/pref_service.h" +#include "base/run_loop.h" +#include "chrome/browser/browser_process.h" +#include "chrome/common/pref_names.h" +#include "chromeos/chromeos_switches.h" +#include "content/public/browser/browser_thread.h" +#include "ui/compositor/compositor_switches.h" +#include "ui/gfx/image/image.h" +#include "ui/snapshot/snapshot.h" + +namespace chromeos { + +ScreenshotTester::ScreenshotTester() : weak_factory_(this) { +} + +ScreenshotTester::~ScreenshotTester() { +} + +bool ScreenshotTester::TryInitialize() { + CommandLine& command_line = *CommandLine::ForCurrentProcess(); + if (!command_line.HasSwitch(switches::kEnableScreenshotTesting)) + return false; + if (!command_line.HasSwitch(::switches::kEnablePixelOutputInTests) || + !command_line.HasSwitch(::switches::kUIEnableImplSidePainting)) { + // TODO(elizavetai): make turning on --enable-pixel-output-in-tests + // and --ui-enable-impl-side-painting automatical. + LOG(ERROR) << "--enable-pixel-output-in-tests and " + << "--ui-enable-impl-side-painting are required to take " + << "screenshots"; + return false; + } + if (!command_line.HasSwitch(switches::kScreenshotDestinationDir)) { + LOG(ERROR) << "No destination for screenshots specified"; + return false; + } + screenshot_dest_ = command_line.GetSwitchValuePath( + chromeos::switches::kScreenshotDestinationDir); + return true; +} + +void ScreenshotTester::Run(const std::string& file_name) { + TakeScreenshot(); + PNGFile current_screenshot = screenshot_; + UpdateGoldenScreenshot(file_name, current_screenshot); +} + +void ScreenshotTester::UpdateGoldenScreenshot(const std::string& file_name, + PNGFile png_data) { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + if (!png_data) { + LOG(ERROR) << "Can't take a screenshot"; + return; + } + base::FilePath golden_screenshot_path = + screenshot_dest_.AppendASCII(file_name + ".png"); + if (!base::CreateDirectory(golden_screenshot_path.DirName())) { + LOG(ERROR) << "Can't create a directory "; + return; + } + if (static_cast<size_t>( + base::WriteFile(golden_screenshot_path, + reinterpret_cast<char*>(&(png_data->data()[0])), + png_data->size())) != png_data->size()) { + LOG(ERROR) << "Can't save screenshot"; + } + VLOG(0) << "Golden screenshot updated successfully"; +} + +void ScreenshotTester::ReturnScreenshot(PNGFile png_data) { + // TODO(elizavetai): rewrite this function so that TakeScreenshot + // could return a |PNGFile| -- current screenshot. + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + screenshot_ = png_data; + content::BrowserThread::PostTask( + content::BrowserThread::UI, FROM_HERE, run_loop_quitter_); +} + +void ScreenshotTester::TakeScreenshot() { + DCHECK_CURRENTLY_ON(content::BrowserThread::UI); + aura::Window* primary_window = ash::Shell::GetPrimaryRootWindow(); + gfx::Rect rect = primary_window->bounds(); + ui::GrabWindowSnapshotAsync(primary_window, + rect, + content::BrowserThread::GetBlockingPool(), + base::Bind(&ScreenshotTester::ReturnScreenshot, + weak_factory_.GetWeakPtr())); + base::RunLoop run_loop; + run_loop_quitter_ = run_loop.QuitClosure(); + run_loop.Run(); +} + +} // namespace chromeos diff --git a/chrome/browser/chromeos/login/screenshot_tester.h b/chrome/browser/chromeos/login/screenshot_tester.h new file mode 100644 index 0000000..5a1d052 --- /dev/null +++ b/chrome/browser/chromeos/login/screenshot_tester.h @@ -0,0 +1,67 @@ +// Copyright 2014 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. + +#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SCREENSHOT_TESTER_H_ +#define CHROME_BROWSER_CHROMEOS_LOGIN_SCREENSHOT_TESTER_H_ + +#include "base/base_export.h" +#include "base/bind_internal.h" +#include "base/files/file_path.h" +#include "base/macros.h" +#include "base/memory/ref_counted_memory.h" +#include "base/memory/weak_ptr.h" +#include "base/run_loop.h" + +namespace chromeos { + +// A class that allows taking, saving and comparing screnshots while +// running tests. +class ScreenshotTester { + public: + ScreenshotTester(); + virtual ~ScreenshotTester(); + + // Returns true if the screenshots should be taken and will be taken, + // false otherwise. Also gets all the information from the command line + // swithes. + bool TryInitialize(); + + // Does all the work that has been stated through switches: + // updates golden screenshot or takes a new screenshot and compares it + // with the golden one (this part is not implemented yet). + void Run(const std::string& file_name); + + private: + typedef scoped_refptr<base::RefCountedBytes> PNGFile; + + // Takes a screenshot and puts it to |screenshot_| field. + void TakeScreenshot(); + + // Saves |png_data| as a new golden screenshot for this test. + void UpdateGoldenScreenshot(const std::string& file_name, PNGFile png_data); + + // Saves |png_data| as a current screenshot. + void ReturnScreenshot(PNGFile png_data); + + // Path to the directory for golden screenshots. + base::FilePath screenshot_dest_; + + // |run_loop_| and |run_loop_quitter_| are used to synchronize + // with ui::GrabWindowSnapshotAsync. + base::RunLoop run_loop_; + base::Closure run_loop_quitter_; + + // Current screenshot. + PNGFile screenshot_; + + // Is true when we running updating golden screenshots mode. + bool update_golden_screenshot_; + base::WeakPtrFactory<ScreenshotTester> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(ScreenshotTester); +}; + +} // namespace chromeos + +#endif // CHROME_BROWSER_CHROMEOS_LOGIN_SCREENSHOT_TESTER_H_ diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 0baab89..a9f6a3f 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -285,6 +285,8 @@ 'browser/chromeos/login/login_manager_test.h', 'browser/chromeos/login/login_ui_browsertest.cc', 'browser/chromeos/login/oobe_browsertest.cc', + 'browser/chromeos/login/screenshot_tester.h', + 'browser/chromeos/login/screenshot_tester.cc', 'browser/chromeos/login/users/wallpaper/wallpaper_manager_browsertest.cc', 'browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.cc', 'browser/chromeos/login/users/wallpaper/wallpaper_manager_test_utils.h', diff --git a/chromeos/chromeos_switches.cc b/chromeos/chromeos_switches.cc index 96667d2..52b60a0 100644 --- a/chromeos/chromeos_switches.cc +++ b/chromeos/chromeos_switches.cc @@ -96,6 +96,9 @@ const char kEnableNetworkPortalNotification[] = // Enables activation of voice search by saying 'Ok Google'. const char kEnableOkGoogleVoiceSearch[] = "enable-ok-google-voice-search"; +// Enables using screenshots in tests. +const char kEnableScreenshotTesting[] = "enable-screenshot-testing"; + // Enables touchpad three-finger-click as middle button. const char kEnableTouchpadThreeFingerClick[] = "enable-touchpad-three-finger-click"; @@ -236,6 +239,10 @@ const char kEnableFirstRunUITransitions[] = "enable-first-run-ui-transitions"; // Forces first-run UI to be shown for every login. const char kForceFirstRunUI[] = "force-first-run-ui"; +// Turns on screenshot testing and specifies the directory where the +// golden screenshots are stored. +const char kScreenshotDestinationDir[] = "screenshot-destination-dir"; + // Enables testing for auto update UI. const char kTestAutoUpdateUI[] = "test-auto-update-ui"; diff --git a/chromeos/chromeos_switches.h b/chromeos/chromeos_switches.h index e31aac7..b7bc3b3 100644 --- a/chromeos/chromeos_switches.h +++ b/chromeos/chromeos_switches.h @@ -45,13 +45,13 @@ CHROMEOS_EXPORT extern const char kEnableConsumerManagement[]; CHROMEOS_EXPORT extern const char kEnableEmbeddedSignin[]; CHROMEOS_EXPORT extern const char kEnableExtensionAssetsSharing[]; CHROMEOS_EXPORT extern const char kEnableFileManagerMTP[]; -CHROMEOS_EXPORT extern const char kEnableVideoPlayerChromecastSupport[]; CHROMEOS_EXPORT extern const char kEnableFirstRunUITransitions[]; CHROMEOS_EXPORT extern const char kEnableKioskMode[]; CHROMEOS_EXPORT extern const char kEnableNetworkPortalNotification[]; CHROMEOS_EXPORT extern const char kEnableOkGoogleVoiceSearch[]; CHROMEOS_EXPORT extern const char kEnableRequestTabletSite[]; CHROMEOS_EXPORT extern const char kEnableTouchpadThreeFingerClick[]; +CHROMEOS_EXPORT extern const char kEnableVideoPlayerChromecastSupport[]; CHROMEOS_EXPORT extern const char kEnterpriseEnableForcedReEnrollment[]; CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentInitialModulus[]; CHROMEOS_EXPORT extern const char kEnterpriseEnrollmentModulusLimit[]; @@ -80,6 +80,8 @@ CHROMEOS_EXPORT extern const char kShowHostPairingDemo[]; CHROMEOS_EXPORT extern const char kSmsTestMessages[]; CHROMEOS_EXPORT extern const char kStubCrosSettings[]; CHROMEOS_EXPORT extern const char kTestAutoUpdateUI[]; +CHROMEOS_EXPORT extern const char kEnableScreenshotTesting[]; +CHROMEOS_EXPORT extern const char kScreenshotDestinationDir[]; } // namespace switches } // namespace chromeos |