Home | History | Annotate | Download | only in test
      1 // Copyright (c) 2012 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 #ifndef CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_
      6 #define CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_
      7 
      8 #include <windows.h>
      9 
     10 #include <atlbase.h>
     11 #include <atlwin.h>
     12 
     13 #include <string>
     14 
     15 #include "base/basictypes.h"
     16 #include "base/cancelable_callback.h"
     17 #include "base/compiler_specific.h"
     18 #include "base/files/file_path.h"
     19 #include "base/memory/scoped_ptr.h"
     20 #include "base/message_loop/message_loop.h"
     21 #include "base/process/process_handle.h"
     22 #include "base/run_loop.h"
     23 #include "base/test/test_reg_util_win.h"
     24 #include "base/time/time.h"
     25 #include "base/time/time.h"
     26 #include "base/win/registry.h"
     27 #include "base/win/scoped_comptr.h"
     28 #include "chrome_frame/chrome_tab.h"
     29 #include "chrome_frame/test/simulate_input.h"
     30 #include "chrome_frame/test_utils.h"
     31 #include "chrome_frame/utils.h"
     32 
     33 #include "gtest/gtest.h"
     34 
     35 // Needed for CreateFunctor.
     36 #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
     37 #include "testing/gmock_mutant.h"
     38 
     39 interface IWebBrowser2;
     40 
     41 namespace base {
     42 class FilePath;
     43 }
     44 
     45 namespace chrome_frame_test {
     46 
     47 int CloseVisibleWindowsOnAllThreads(HANDLE process);
     48 
     49 base::ProcessHandle LaunchIE(const std::wstring& url);
     50 base::ProcessHandle LaunchChrome(const std::wstring& url,
     51                                  const base::FilePath& user_data_dir);
     52 
     53 // Attempts to close all open IE windows.
     54 // The return value is the number of windows closed.
     55 // @note: this function requires COM to be initialized on the calling thread.
     56 // Since the caller might be running in either MTA or STA, the function does
     57 // not perform this initialization itself.
     58 int CloseAllIEWindows();
     59 
     60 // Saves a screen snapshot to the desktop and logs its location.
     61 bool TakeSnapshotAndLog();
     62 
     63 extern const wchar_t kIEImageName[];
     64 extern const wchar_t kIEBrokerImageName[];
     65 extern const char kChromeImageName[];
     66 extern const wchar_t kChromeLauncher[];
     67 extern const base::TimeDelta kChromeFrameLongNavigationTimeout;
     68 extern const base::TimeDelta kChromeFrameVeryLongNavigationTimeout;
     69 
     70 // Temporarily impersonate the current thread to low integrity for the lifetime
     71 // of the object. Destructor will automatically revert integrity level.
     72 class LowIntegrityToken {
     73  public:
     74   LowIntegrityToken();
     75   ~LowIntegrityToken();
     76   BOOL Impersonate();
     77   BOOL RevertToSelf();
     78  protected:
     79   static bool IsImpersonated();
     80   bool impersonated_;
     81 };
     82 
     83 // This class implements the COM IMessageFilter interface and is used to detect
     84 // hung outgoing COM calls which are typically to IE. If IE is hung for any
     85 // reason the chrome frame tests should not hang indefinitely. This class
     86 // basically cancels the outgoing call if the WM_TIMER which is set by the
     87 // TimedMsgLoop object is posted to the message queue.
     88 class HungCOMCallDetector
     89     : public CComObjectRootEx<CComMultiThreadModel>,
     90       public IMessageFilter,
     91       public CWindowImpl<HungCOMCallDetector> {
     92  public:
     93   HungCOMCallDetector()
     94       : is_hung_(false) {
     95   }
     96 
     97   ~HungCOMCallDetector() {
     98     TearDown();
     99   }
    100 
    101   BEGIN_MSG_MAP(HungCOMCallDetector)
    102     MESSAGE_HANDLER(WM_TIMER, OnTimer)
    103   END_MSG_MAP()
    104 
    105   BEGIN_COM_MAP(HungCOMCallDetector)
    106     COM_INTERFACE_ENTRY(IMessageFilter)
    107   END_COM_MAP()
    108 
    109   static CComObject<HungCOMCallDetector>* Setup(int timeout_seconds) {
    110     CComObject<HungCOMCallDetector>* this_instance  = NULL;
    111     CComObject<HungCOMCallDetector>::CreateInstance(&this_instance);
    112     EXPECT_TRUE(this_instance != NULL);
    113     scoped_refptr<HungCOMCallDetector> ref_instance = this_instance;
    114 
    115     HRESULT hr = ref_instance->Initialize(timeout_seconds);
    116     if (FAILED(hr)) {
    117       LOG(ERROR) << "Failed to Initialize Hung COM call detector. Error:"
    118                  << hr;
    119       return NULL;
    120     }
    121     return this_instance;
    122   }
    123 
    124   void TearDown() {
    125     if (prev_filter_) {
    126       base::win::ScopedComPtr<IMessageFilter> prev_filter;
    127       CoRegisterMessageFilter(prev_filter_.get(), prev_filter.Receive());
    128       DCHECK(prev_filter.IsSameObject(this));
    129       prev_filter_.Release();
    130     }
    131     if (IsWindow()) {
    132       DestroyWindow();
    133       m_hWnd = NULL;
    134     }
    135   }
    136 
    137   STDMETHOD_(DWORD, HandleInComingCall)(DWORD call_type,
    138                                         HTASK caller,
    139                                         DWORD tick_count,
    140                                         LPINTERFACEINFO interface_info) {
    141     return SERVERCALL_ISHANDLED;
    142   }
    143 
    144   STDMETHOD_(DWORD, RetryRejectedCall)(HTASK callee,
    145                                        DWORD tick_count,
    146                                        DWORD reject_type) {
    147     return -1;
    148   }
    149 
    150   STDMETHOD_(DWORD, MessagePending)(HTASK callee,
    151                                     DWORD tick_count,
    152                                     DWORD pending_type) {
    153     MSG msg = {0};
    154     if (is_hung_) {
    155       return PENDINGMSG_CANCELCALL;
    156     }
    157     return PENDINGMSG_WAITDEFPROCESS;
    158   }
    159 
    160   bool is_hung() const {
    161     return is_hung_;
    162   }
    163 
    164   LRESULT OnTimer(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) {  // NOLINT
    165     is_hung_ = true;
    166     return 1;
    167   }
    168 
    169  private:
    170   HRESULT Initialize(int timeout_seconds) {
    171     // Create a window for the purpose of setting a windows timer for
    172     // detecting whether any outgoing COM call issued in this context
    173     // hangs.
    174     // We then register a COM message filter which basically looks for the
    175     // timer posted to this window and cancels the outgoing call.
    176     Create(HWND_MESSAGE);
    177     if (!IsWindow()) {
    178       LOG(ERROR) << "Failed to create window. Error:" << GetLastError();
    179       return E_FAIL;
    180     }
    181 
    182     HRESULT hr = CoRegisterMessageFilter(this, prev_filter_.Receive());
    183     if (FAILED(hr)) {
    184       LOG(ERROR) << "Failed to register message filter. Error:" << hr;
    185       return hr;
    186     }
    187 
    188     static const int kHungDetectTimerId = 0x0000baba;
    189     SetTimer(kHungDetectTimerId, 1000 * (timeout_seconds + 40), NULL);
    190     return S_OK;
    191   }
    192 
    193   // used to detect if outgoing COM calls hung.
    194   bool is_hung_;
    195   base::win::ScopedComPtr<IMessageFilter> prev_filter_;
    196 };
    197 
    198 // MessageLoopForUI wrapper that runs only for a limited time.
    199 // We need a UI message loop in the main thread.
    200 class TimedMsgLoop {
    201  public:
    202   TimedMsgLoop() : snapshot_on_timeout_(false), quit_loop_invoked_(false) {
    203   }
    204 
    205   void set_snapshot_on_timeout(bool value) {
    206     snapshot_on_timeout_ = value;
    207   }
    208 
    209   void RunFor(base::TimeDelta duration) {
    210     quit_loop_invoked_ = false;
    211     if (snapshot_on_timeout_)
    212       timeout_closure_.Reset(base::Bind(&TimedMsgLoop::SnapshotAndQuit));
    213     else
    214       timeout_closure_.Reset(base::MessageLoop::QuitClosure());
    215     loop_.PostDelayedTask(FROM_HERE, timeout_closure_.callback(), duration);
    216     loop_.base::MessageLoop::Run();
    217     timeout_closure_.Cancel();
    218   }
    219 
    220   void PostTask(const tracked_objects::Location& from_here,
    221                 const base::Closure& task) {
    222     loop_.PostTask(from_here, task);
    223   }
    224 
    225   void PostDelayedTask(const tracked_objects::Location& from_here,
    226                        const base::Closure& task, base::TimeDelta delay) {
    227     loop_.PostDelayedTask(from_here, task, delay);
    228   }
    229 
    230   void Quit() {
    231     // Quit after no delay.
    232     QuitAfter(base::TimeDelta());
    233   }
    234 
    235   void QuitAfter(base::TimeDelta delay) {
    236     timeout_closure_.Cancel();
    237     quit_loop_invoked_ = true;
    238     loop_.PostDelayedTask(FROM_HERE, base::MessageLoop::QuitClosure(), delay);
    239   }
    240 
    241   bool WasTimedOut() const {
    242     return !quit_loop_invoked_;
    243   }
    244 
    245   void RunUntilIdle() {
    246     loop_.RunUntilIdle();
    247   }
    248 
    249  private:
    250   static void SnapshotAndQuit() {
    251     TakeSnapshotAndLog();
    252     base::MessageLoop::current()->Quit();
    253   }
    254 
    255   base::MessageLoopForUI loop_;
    256   base::CancelableClosure timeout_closure_;
    257   bool snapshot_on_timeout_;
    258   bool quit_loop_invoked_;
    259 };
    260 
    261 // Saves typing. It's somewhat hard to create a wrapper around
    262 // testing::InvokeWithoutArgs since it returns a
    263 // non-public (testing::internal) type.
    264 #define QUIT_LOOP(loop) testing::InvokeWithoutArgs(\
    265   testing::CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::Quit))
    266 
    267 #define QUIT_LOOP_SOON(loop, delay) testing::InvokeWithoutArgs(\
    268   testing::CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::QuitAfter, \
    269                          delay))
    270 
    271 // Launches IE as a COM server and returns the corresponding IWebBrowser2
    272 // interface pointer.
    273 // Returns S_OK on success.
    274 HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser);
    275 
    276 // Returns the path of the exe passed in.
    277 std::wstring GetExecutableAppPath(const std::wstring& file);
    278 
    279 // Returns the profile path to be used for IE. This varies as per version.
    280 base::FilePath GetProfilePathForIE();
    281 
    282 // Returns the version of the exe passed in.
    283 std::wstring GetExeVersion(const std::wstring& exe_path);
    284 
    285 // Returns the version of Internet Explorer on the machine.
    286 IEVersion GetInstalledIEVersion();
    287 
    288 // Returns the folder for CF test data.
    289 base::FilePath GetTestDataFolder();
    290 
    291 // Returns the folder for Selenium core.
    292 base::FilePath GetSeleniumTestFolder();
    293 
    294 // Returns the path portion of the url.
    295 std::wstring GetPathFromUrl(const std::wstring& url);
    296 
    297 // Returns the path and query portion of the url.
    298 std::wstring GetPathAndQueryFromUrl(const std::wstring& url);
    299 
    300 // Adds the CF meta tag to the html page. Returns true if successful.
    301 bool AddCFMetaTag(std::string* html_data);
    302 
    303 // Get text data from the clipboard.
    304 std::wstring GetClipboardText();
    305 
    306 // Destroys the clipboard for the current thread. This function must be called
    307 // if GetClipboardText() or SetClipboardText() have been invoked.
    308 void DestroyClipboard();
    309 
    310 // Puts the given text data on the clipboard. All previous items on the
    311 // clipboard are removed.
    312 void SetClipboardText(const std::wstring& text);
    313 
    314 // A convenience class to close all open IE windows at the end
    315 // of a scope.  It's more convenient to do it this way than to
    316 // explicitly call chrome_frame_test::CloseAllIEWindows at the
    317 // end of a test since part of the test's cleanup code may be
    318 // in object destructors that would run after CloseAllIEWindows
    319 // would get called.
    320 // Ideally all IE windows should be closed when this happens so
    321 // if the test ran normally, we should not have any windows to
    322 // close at this point.
    323 class CloseIeAtEndOfScope {
    324  public:
    325   CloseIeAtEndOfScope() {}
    326   ~CloseIeAtEndOfScope();
    327 };
    328 
    329 // Starts the Chrome crash service which enables us to gather crash dumps
    330 // during test runs.
    331 base::ProcessHandle StartCrashService();
    332 
    333 // Used in tests where we reference the registry and don't want to run into
    334 // problems where existing registry settings might conflict with the
    335 // expectations of the test.
    336 class ScopedVirtualizeHklmAndHkcu {
    337  public:
    338   ScopedVirtualizeHklmAndHkcu();
    339   ~ScopedVirtualizeHklmAndHkcu();
    340 
    341  protected:
    342   registry_util::RegistryOverrideManager override_manager_;
    343 };
    344 
    345 // Attempts to kill all the processes on the current machine that were launched
    346 // from the given executable name, ending them with the given exit code.  If
    347 // filter is non-null, then only processes selected by the filter are killed.
    348 // Returns true if all processes were able to be killed off, false if at least
    349 // one couldn't be killed.
    350 // NOTE: This function is based on the base\process_util.h helper function
    351 // KillProcesses. Takes in the wait flag as a parameter.
    352 bool KillProcesses(const std::wstring& executable_name, int exit_code,
    353                    bool wait);
    354 
    355 // Returns the type of test bed, PER_USER or SYSTEM_LEVEL.
    356 ScopedChromeFrameRegistrar::RegistrationType GetTestBedType();
    357 
    358 // Clears IE8 session restore history.
    359 void ClearIESessionHistory();
    360 
    361 // Returns a local IPv4 address for the current machine. The address
    362 // corresponding to a NIC is preferred over the loopback address.
    363 std::string GetLocalIPv4Address();
    364 
    365 }  // namespace chrome_frame_test
    366 
    367 // TODO(tommi): This is a temporary workaround while we're getting our
    368 // Singleton story straight.  Ideally each test should clear up any singletons
    369 // it might have created, but test cases do not implicitly have their own
    370 // AtExitManager, so we have this workaround method for tests that depend on
    371 // "fresh" singletons.  The implementation is in chrome_frame_unittest_main.cc.
    372 void DeleteAllSingletons();
    373 
    374 #endif  // CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_
    375