diff options
Diffstat (limited to 'chrome_frame')
-rw-r--r-- | chrome_frame/test/chrome_frame_unittests.cc | 27 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_unittests.h | 3 | ||||
-rw-r--r-- | chrome_frame/test_utils.cc | 164 | ||||
-rw-r--r-- | chrome_frame/test_utils.h | 6 |
4 files changed, 192 insertions, 8 deletions
diff --git a/chrome_frame/test/chrome_frame_unittests.cc b/chrome_frame/test/chrome_frame_unittests.cc index fece220..4d62e74 100644 --- a/chrome_frame/test/chrome_frame_unittests.cc +++ b/chrome_frame/test/chrome_frame_unittests.cc @@ -24,6 +24,7 @@ #include "chrome_frame/crash_reporting/vectored_handler-impl.h" #include "chrome_frame/test/chrome_frame_test_utils.h" #include "chrome_frame/test_utils.h" +#include "chrome/common/chrome_switches.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/helper.h" @@ -78,7 +79,24 @@ _ATL_FUNC_INFO WebBrowserEventSink::kNewWindow3Info = { _ATL_FUNC_INFO WebBrowserEventSink::kVoidMethodInfo = { CC_STDCALL, VT_EMPTY, 0, {NULL}}; +void ChromeFrameTestWithWebServer::CloseAllBrowsers() { + // Web browsers tend to relaunch themselves in other processes, meaning the + // KillProcess stuff above might not have actually cleaned up all our browser + // instances, so make really sure browsers are dead. + base::KillProcesses(chrome_frame_test::kIEImageName, 0, NULL); + base::KillProcesses(chrome_frame_test::kIEBrokerImageName, 0, NULL); + base::KillProcesses(chrome_frame_test::kFirefoxImageName, 0, NULL); + base::KillProcesses(chrome_frame_test::kSafariImageName, 0, NULL); + + // Endeavour to only kill off Chrome Frame derived Chrome processes. + KillAllNamedProcessesWithArgument(chrome_frame_test::kChromeImageName, + UTF8ToWide(switches::kChromeFrame)); +} + void ChromeFrameTestWithWebServer::SetUp() { + // Make sure our playground is clean before we start. + CloseAllBrowsers(); + server_.SetUp(); results_dir_ = server_.GetDataDir(); file_util::AppendToPath(&results_dir_, L"dump"); @@ -87,14 +105,7 @@ void ChromeFrameTestWithWebServer::SetUp() { void ChromeFrameTestWithWebServer::TearDown() { CloseBrowser(); - // Web browsers tend to relaunch themselves in other processes, meaning the - // KillProcess stuff above might not have actually cleaned up all our browser - // instances, so make really sure browsers are dead. - base::KillProcesses(chrome_frame_test::kIEImageName, 0, NULL); - base::KillProcesses(chrome_frame_test::kIEBrokerImageName, 0, NULL); - base::KillProcesses(chrome_frame_test::kFirefoxImageName, 0, NULL); - base::KillProcesses(chrome_frame_test::kSafariImageName, 0, NULL); - base::KillProcesses(chrome_frame_test::kChromeImageName, 0, NULL); + CloseAllBrowsers(); server_.TearDown(); } diff --git a/chrome_frame/test/chrome_frame_unittests.h b/chrome_frame/test/chrome_frame_unittests.h index c39058b..b009faf 100644 --- a/chrome_frame/test/chrome_frame_unittests.h +++ b/chrome_frame/test/chrome_frame_unittests.h @@ -63,6 +63,9 @@ class ChromeFrameTestWithWebServer: public testing::Test { void VersionTest(BrowserKind browser, const wchar_t* page, const wchar_t* result_file_to_check); + // Closes all browsers in preparation for a test and during cleanup. + void CloseAllBrowsers(); + void CloseBrowser(); // Ensures (well, at least tries to ensure) that the browser window has focus. diff --git a/chrome_frame/test_utils.cc b/chrome_frame/test_utils.cc index c5e1f8e..bbe6c86 100644 --- a/chrome_frame/test_utils.cc +++ b/chrome_frame/test_utils.cc @@ -6,11 +6,17 @@ #include <atlbase.h> #include <atlwin.h> +#include <winternl.h> #include "base/file_path.h" #include "base/file_util.h" +#include "base/logging.h" #include "base/path_service.h" +#include "base/process_util.h" +#include "base/scoped_handle.h" +#include "base/string_util.h" #include "chrome/common/chrome_paths.h" +#include "chrome/common/chrome_switches.h" #include "testing/gtest/include/gtest/gtest.h" // Statics @@ -94,3 +100,161 @@ void ScopedChromeFrameRegistrar::RegisterReferenceChromeFrameBuild() { std::wstring ScopedChromeFrameRegistrar::GetChromeFrameDllPath() const { return new_chrome_frame_dll_path_; } + +// TODO(robertshield): The following could be factored out into its own file. +namespace { + +typedef LONG WINAPI +NtQueryInformationProcess( + IN HANDLE ProcessHandle, + IN PROCESSINFOCLASS ProcessInformationClass, + OUT PVOID ProcessInformation, + IN ULONG ProcessInformationLength, + OUT PULONG ReturnLength OPTIONAL +); + +// Get the function pointer to NtQueryInformationProcess in NTDLL.DLL +static bool GetQIP(NtQueryInformationProcess** qip_func_ptr) { + static NtQueryInformationProcess* qip_func = + reinterpret_cast<NtQueryInformationProcess*>( + GetProcAddress(GetModuleHandle(L"ntdll.dll"), + "NtQueryInformationProcess")); + DCHECK(qip_func) << "Could not get pointer to NtQueryInformationProcess."; + *qip_func_ptr = qip_func; + return qip_func != NULL; +} + +// Get the command line of a process +bool GetCommandLineForProcess(uint32 process_id, std::wstring* cmd_line) { + DCHECK(process_id != 0); + DCHECK(cmd_line); + + // Open the process + ScopedHandle process_handle(::OpenProcess( + PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, + false, + process_id)); + if (!process_handle) { + DLOG(ERROR) << "Failed to open process " << process_id << ", last error = " + << GetLastError(); + } + + // Obtain Process Environment Block + NtQueryInformationProcess* qip_func = NULL; + if (process_handle) { + GetQIP(&qip_func); + } + + // Read the address of the process params from the peb. + DWORD process_params_address = 0; + if (qip_func) { + PROCESS_BASIC_INFORMATION info = { 0 }; + // NtQueryInformationProcess returns an NTSTATUS for whom negative values + // are negative. Just check for that instead of pulling in DDK macros. + if ((qip_func(process_handle.Get(), + ProcessBasicInformation, + &info, + sizeof(info), + NULL)) < 0) { + DLOG(ERROR) << "Failed to invoke NtQueryProcessInformation, last error = " + << GetLastError(); + } else { + BYTE* peb = reinterpret_cast<BYTE*>(info.PebBaseAddress); + + // The process command line parameters are (or were once) located at + // the base address of the PEB + 0x10 for 32 bit processes. 64 bit + // processes have a different PEB struct as per + // http://msdn.microsoft.com/en-us/library/aa813706(VS.85).aspx. + // TODO(robertshield): See about doing something about this. + SIZE_T bytes_read = 0; + if (!::ReadProcessMemory(process_handle.Get(), + peb + 0x10, + &process_params_address, + sizeof(process_params_address), + &bytes_read)) { + DLOG(ERROR) << "Failed to read process params address, last error = " + << GetLastError(); + } + } + } + + // Copy all the process parameters into a buffer. + bool success = false; + std::wstring buffer; + if (process_params_address) { + SIZE_T bytes_read; + RTL_USER_PROCESS_PARAMETERS params = { 0 }; + if (!::ReadProcessMemory(process_handle.Get(), + reinterpret_cast<void*>(process_params_address), + ¶ms, + sizeof(params), + &bytes_read)) { + DLOG(ERROR) << "Failed to read RTL_USER_PROCESS_PARAMETERS, " + << "last error = " << GetLastError(); + } else { + // Read the command line parameter + const int max_cmd_line_len = std::min( + static_cast<int>(params.CommandLine.MaximumLength), + 4096); + buffer.resize(max_cmd_line_len + 1); + if (!::ReadProcessMemory(process_handle.Get(), + params.CommandLine.Buffer, + &buffer[0], + max_cmd_line_len, + &bytes_read)) { + DLOG(ERROR) << "Failed to copy process command line, " + << "last error = " << GetLastError(); + } else { + *cmd_line = buffer; + success = true; + } + } + } + + return success; +} + +// Used to filter processes by process ID. +class ArgumentFilter : public base::ProcessFilter { + public: + explicit ArgumentFilter(const std::wstring& argument) + : argument_to_find_(argument) {} + + // Returns true to indicate set-inclusion and false otherwise. This method + // should not have side-effects and should be idempotent. + virtual bool Includes(base::ProcessId pid, base::ProcessId parent_pid) const { + bool found = false; + std::wstring command_line; + if (GetCommandLineForProcess(pid, &command_line)) { + std::wstring::const_iterator it = + std::search(command_line.begin(), + command_line.end(), + argument_to_find_.begin(), + argument_to_find_.end(), + CaseInsensitiveCompareASCII<wchar_t>()); + found = (it != command_line.end()); + } + return found; + } + + protected: + std::wstring argument_to_find_; +}; + +} // namespace + +bool KillAllNamedProcessesWithArgument(const std::wstring& process_name, + const std::wstring& argument) { + bool result = true; + const ProcessEntry* entry; + ArgumentFilter filter(argument); + base::NamedProcessIterator iter(process_name, &filter); + while (entry = iter.NextProcessEntry()) { + if (!base::KillProcessById((*entry).th32ProcessID, 0, true)) { + DLOG(ERROR) << "Failed to kill process " << (*entry).th32ProcessID; + result = false; + } + } + + return result; +} diff --git a/chrome_frame/test_utils.h b/chrome_frame/test_utils.h index 260b4b5..9dc3a7e 100644 --- a/chrome_frame/test_utils.h +++ b/chrome_frame/test_utils.h @@ -76,5 +76,11 @@ class DispCallback Method method_; }; +// Kills all running processes named |process_name| that have the string +// |argument| on their command line. Useful for killing all Chrome Frame +// instances of Chrome that all have --chrome-frame in their command line. +bool KillAllNamedProcessesWithArgument(const std::wstring& process_name, + const std::wstring& argument); + #endif // CHROME_FRAME_TEST_UTILS_H_ |