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 |