| OLD | NEW |
| (Empty) |
| 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_ | |
| OLD | NEW |