| // Copyright (c) 2010 The Chromium OS 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 <gflags/gflags.h> |
| #include <gtest/gtest.h> |
| |
| #include "base/logging.h" |
| #include "cros/chromeos_wm_ipc_enums.h" |
| #include "window_manager/chrome_watchdog.h" |
| #include "window_manager/event_loop.h" |
| #include "window_manager/test_lib.h" |
| #include "window_manager/util.h" |
| #include "window_manager/window_manager.h" |
| #include "window_manager/x11/mock_x_connection.h" |
| |
| DEFINE_bool(logtostderr, false, |
| "Print debugging messages to stderr (suppressed otherwise)"); |
| |
| DECLARE_bool(kill_chrome_if_hanging); // from chrome_watchdog.cc |
| |
| using window_manager::util::GetHostname; |
| |
| namespace window_manager { |
| |
| class ChromeWatchdogTest : public BasicWindowManagerTest {}; |
| |
| TEST_F(ChromeWatchdogTest, Basic) { |
| ChromeWatchdog* watchdog = wm_->chrome_watchdog_.get(); |
| |
| const XWindow kRoot = xconn_->GetRootWindow(); |
| const XAtom kProtocolsAtom = xconn_->GetAtomOrDie("WM_PROTOCOLS"); |
| const XAtom kPingAtom = xconn_->GetAtomOrDie("_NET_WM_PING"); |
| const XAtom kPidAtom = xconn_->GetAtomOrDie("_NET_WM_PID"); |
| const XAtom kClientMachineAtom = xconn_->GetAtomOrDie("WM_CLIENT_MACHINE"); |
| const XAtom kCardinalAtom = xconn_->GetAtomOrDie("CARDINAL"); |
| |
| const int kPid = 456; |
| const int kTimestamp = 123; |
| const int kTimeoutMs = 5000; |
| |
| // Make sure that we don't leak any timeouts, either when we get a response |
| // from Chrome or when a timeout fires (http://crosbug.com/10583). |
| const int kInitialNumTimeouts = event_loop_->num_timeouts(); |
| |
| // We should fail if no windows have been mapped. |
| EXPECT_FALSE(watchdog->SendPingToChrome(kTimestamp, kTimeoutMs)); |
| |
| // Map a single non-Chrome window and check that we don't send a ping |
| // message to it. |
| XWindow non_chrome_xid = CreateSimpleWindow(); |
| AppendAtomToProperty(non_chrome_xid, kProtocolsAtom, kPingAtom); |
| xconn_->SetIntProperty(non_chrome_xid, kPidAtom, kCardinalAtom, kPid); |
| xconn_->SetStringProperty(non_chrome_xid, kClientMachineAtom, GetHostname()); |
| SendInitialEventsForWindow(non_chrome_xid); |
| xconn_->GetWindowInfoOrDie(non_chrome_xid)->client_messages.clear(); |
| EXPECT_FALSE(watchdog->SendPingToChrome(kTimestamp, kTimeoutMs)); |
| EXPECT_TRUE( |
| xconn_->GetWindowInfoOrDie(non_chrome_xid)->client_messages.empty()); |
| |
| // We should also fail if we only have a Chrome window that doesn't |
| // support _NET_WM_PING... |
| XWindow non_ping_xid = CreateToplevelWindow(2, 0, Rect(0, 0, 640, 480)); |
| xconn_->SetIntProperty(non_ping_xid, kPidAtom, kCardinalAtom, kPid); |
| xconn_->SetStringProperty(non_ping_xid, kClientMachineAtom, GetHostname()); |
| SendInitialEventsForWindow(non_ping_xid); |
| EXPECT_FALSE(watchdog->SendPingToChrome(kTimestamp, kTimeoutMs)); |
| |
| // ... or that's running on a different host... |
| XWindow other_host_xid = CreateToplevelWindow(2, 0, Rect(0, 0, 640, 480)); |
| AppendAtomToProperty(other_host_xid, kProtocolsAtom, kPingAtom); |
| xconn_->SetIntProperty(other_host_xid, kPidAtom, kCardinalAtom, kPid); |
| xconn_->SetStringProperty(other_host_xid, kClientMachineAtom, "bogus123"); |
| SendInitialEventsForWindow(other_host_xid); |
| EXPECT_FALSE(watchdog->SendPingToChrome(kTimestamp, kTimeoutMs)); |
| |
| // ... or that didn't supply its PID. |
| XWindow no_pid_xid = CreateToplevelWindow(2, 0, Rect(0, 0, 640, 480)); |
| AppendAtomToProperty(no_pid_xid, kProtocolsAtom, kPingAtom); |
| xconn_->SetStringProperty(no_pid_xid, kClientMachineAtom, GetHostname()); |
| SendInitialEventsForWindow(no_pid_xid); |
| EXPECT_FALSE(watchdog->SendPingToChrome(kTimestamp, kTimeoutMs)); |
| |
| // Now create a Chrome window that supports _NET_WM_PING and has supplied |
| // a PID and is running on the local host, and check that it receives a |
| // message. |
| XWindow toplevel_xid = CreateToplevelWindow(2, 0, Rect(0, 0, 640, 480)); |
| AppendAtomToProperty(toplevel_xid, kProtocolsAtom, kPingAtom); |
| xconn_->SetIntProperty(toplevel_xid, kPidAtom, kCardinalAtom, kPid); |
| xconn_->SetStringProperty(toplevel_xid, kClientMachineAtom, GetHostname()); |
| SendInitialEventsForWindow(toplevel_xid); |
| MockXConnection::WindowInfo* toplevel_info = |
| xconn_->GetWindowInfoOrDie(toplevel_xid); |
| toplevel_info->client_messages.clear(); |
| EXPECT_TRUE(watchdog->SendPingToChrome(kTimestamp, kTimeoutMs)); |
| |
| // Also check the contents of the message, per EWMH's specification of |
| // _NET_WM_PING. |
| ASSERT_EQ(1, toplevel_info->client_messages.size()); |
| const XClientMessageEvent& msg = toplevel_info->client_messages[0]; |
| EXPECT_EQ(kProtocolsAtom, msg.message_type); |
| EXPECT_EQ(XConnection::kLongFormat, msg.format); |
| EXPECT_EQ(kPingAtom, msg.data.l[0]); |
| EXPECT_EQ(kTimestamp, msg.data.l[1]); |
| EXPECT_EQ(toplevel_xid, msg.data.l[2]); |
| |
| // Send a response, but with an older timestamp. It should be ignored. |
| XEvent event; |
| xconn_->InitClientMessageEvent(&event, kRoot, kProtocolsAtom, |
| kPingAtom, kTimestamp - 1, toplevel_xid, 0, 0); |
| wm_->HandleEvent(&event); |
| EXPECT_TRUE(watchdog->has_outstanding_ping()); |
| |
| // A response for a different window should also be ignored. |
| xconn_->InitClientMessageEvent(&event, kRoot, kProtocolsAtom, |
| kPingAtom, kTimestamp, toplevel_xid - 1, 0, 0); |
| wm_->HandleEvent(&event); |
| EXPECT_TRUE(watchdog->has_outstanding_ping()); |
| |
| // We should cancel the ping if we get the proper response. |
| xconn_->InitClientMessageEvent(&event, kRoot, kProtocolsAtom, |
| kPingAtom, kTimestamp, toplevel_xid, 0, 0); |
| wm_->HandleEvent(&event); |
| EXPECT_FALSE(watchdog->has_outstanding_ping()); |
| EXPECT_EQ(EventLoop::kUnsetTimeoutId, watchdog->timeout_id_); |
| EXPECT_EQ(-1, watchdog->last_killed_pid_); |
| EXPECT_EQ(kInitialNumTimeouts, event_loop_->num_timeouts()); |
| |
| // If the timeout is invoked after we send the ping, we should kill |
| // Chrome (well, not really, since this is a test, but |last_killed_pid_| |
| // should be set, at least). |
| EXPECT_TRUE(watchdog->SendPingToChrome(kTimestamp, kTimeoutMs)); |
| watchdog->HandleTimeout(); |
| EXPECT_FALSE(watchdog->has_outstanding_ping()); |
| EXPECT_EQ(EventLoop::kUnsetTimeoutId, watchdog->timeout_id_); |
| EXPECT_EQ(kPid, watchdog->last_killed_pid_); |
| EXPECT_EQ(kInitialNumTimeouts, event_loop_->num_timeouts()); |
| } |
| |
| } // namespace window_manager |
| |
| int main(int argc, char** argv) { |
| // Ensure that we don't really send signals to processes. |
| window_manager::AutoReset<bool> flag_resetter( |
| &FLAGS_kill_chrome_if_hanging, false); |
| return window_manager::InitAndRunTests(&argc, argv, &FLAGS_logtostderr); |
| } |