You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mesos.apache.org by be...@apache.org on 2011/06/05 10:45:35 UTC

svn commit: r1132109 [3/26] - in /incubator/mesos/trunk: ./ src/common/ src/configurator/ src/master/ src/sched/ src/slave/ src/tests/ third_party/gmock-1.5.0/ third_party/gmock-1.5.0/build-aux/ third_party/gmock-1.5.0/fused-src/ third_party/gmock-1.5....

Modified: incubator/mesos/trunk/configure.ac
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/configure.ac?rev=1132109&r1=1132108&r2=1132109&view=diff
==============================================================================
--- incubator/mesos/trunk/configure.ac (original)
+++ incubator/mesos/trunk/configure.ac Sun Jun  5 08:45:22 2011
@@ -24,8 +24,8 @@ ac_configure_args="$ac_configure_args --
 
 AC_CONFIG_FILES([Makefile src/Makefile src/examples/Makefile src/tests/Makefile src/config/config.hpp])
 AC_CONFIG_SUBDIRS([third_party/libprocess])
-AC_CONFIG_SUBDIRS([third_party/gtest-1.5.0])
 AC_CONFIG_SUBDIRS([third_party/glog-0.3.1])
+AC_CONFIG_SUBDIRS([third_party/gmock-1.5.0])
 AC_CONFIG_SUBDIRS([third_party/zookeeper-3.3.1/src/c])
 
 AC_CANONICAL_SYSTEM

Modified: incubator/mesos/trunk/src/common/foreach.hpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/common/foreach.hpp?rev=1132109&r1=1132108&r2=1132109&view=diff
==============================================================================
--- incubator/mesos/trunk/src/common/foreach.hpp (original)
+++ incubator/mesos/trunk/src/common/foreach.hpp Sun Jun  5 08:45:22 2011
@@ -25,6 +25,10 @@
 
 #include <boost/tuple/tuple.hpp>
 
+namespace foreach {
+
 const boost::tuples::detail::swallow_assign _ = boost::tuples::ignore;
 
+}
+
 #endif /* FOREACH_HPP */

Modified: incubator/mesos/trunk/src/configurator/configurator.cpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/configurator/configurator.cpp?rev=1132109&r1=1132108&r2=1132109&view=diff
==============================================================================
--- incubator/mesos/trunk/src/configurator/configurator.cpp (original)
+++ incubator/mesos/trunk/src/configurator/configurator.cpp Sun Jun  5 08:45:22 2011
@@ -14,6 +14,8 @@
 
 using namespace mesos::internal;
 
+using foreach::_;
+
 
 const char* Configurator::DEFAULT_CONFIG_DIR = "conf";
 const char* Configurator::CONFIG_FILE_NAME = "mesos.conf";

Modified: incubator/mesos/trunk/src/master/master.hpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/master/master.hpp?rev=1132109&r1=1132108&r2=1132109&view=diff
==============================================================================
--- incubator/mesos/trunk/src/master/master.hpp (original)
+++ incubator/mesos/trunk/src/master/master.hpp Sun Jun  5 08:45:22 2011
@@ -38,6 +38,9 @@
 
 namespace mesos { namespace internal { namespace master {
 
+using namespace mesos;
+using namespace mesos::internal;
+
 using std::make_pair;
 using std::map;
 using std::pair;
@@ -48,8 +51,7 @@ using std::vector;
 using boost::unordered_map;
 using boost::unordered_set;
 
-using namespace mesos;
-using namespace mesos::internal;
+using foreach::_;
 
 
 // Maximum number of slot offers to have outstanding for each framework.

Modified: incubator/mesos/trunk/src/sched/sched.cpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/sched/sched.cpp?rev=1132109&r1=1132108&r2=1132109&view=diff
==============================================================================
--- incubator/mesos/trunk/src/sched/sched.cpp (original)
+++ incubator/mesos/trunk/src/sched/sched.cpp Sun Jun  5 08:45:22 2011
@@ -493,6 +493,8 @@ void MesosSchedulerDriver::init(Schedule
   pthread_mutex_init(&mutex, &attr);
   pthread_mutexattr_destroy(&attr);
   pthread_cond_init(&cond, 0);
+
+  // TODO(benh): Initialize glog.
 }
 
 

Modified: incubator/mesos/trunk/src/slave/slave.hpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/slave/slave.hpp?rev=1132109&r1=1132108&r2=1132109&view=diff
==============================================================================
--- incubator/mesos/trunk/src/slave/slave.hpp (original)
+++ incubator/mesos/trunk/src/slave/slave.hpp Sun Jun  5 08:45:22 2011
@@ -60,6 +60,8 @@ using boost::lexical_cast;
 using boost::unordered_map;
 using boost::unordered_set;
 
+using foreach::_;
+
 
 // A description of a task that is yet to be launched
 struct TaskDescription

Modified: incubator/mesos/trunk/src/tests/Makefile.in
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/tests/Makefile.in?rev=1132109&r1=1132108&r2=1132109&view=diff
==============================================================================
--- incubator/mesos/trunk/src/tests/Makefile.in (original)
+++ incubator/mesos/trunk/src/tests/Makefile.in Sun Jun  5 08:45:22 2011
@@ -22,7 +22,7 @@ LIBPROCESS = third_party/libprocess
 LIBEV = $(LIBPROCESS)/third_party/libev-3.8
 
 GLOG = third_party/glog-0.3.1
-GTEST = third_party/gtest-1.5.0
+GMOCK = third_party/gmock-1.5.0
 
 ZOOKEEPER = third_party/zookeeper-3.3.1/src/c
 
@@ -50,9 +50,9 @@ LDFLAGS += -L@top_builddir@/$(LIBPROCESS
 # Add libev to LDFLAGS.
 LDFLAGS += -L@top_builddir@/$(LIBEV)/.libs
 
-# Add glog and gtest to include paths.
-CXXFLAGS += -I@top_srcdir@/$(GLOG)/src -I@top_builddir@/$(GLOG)/src -I@top_srcdir@/$(GTEST)/include
-LDFLAGS += -L@top_builddir@/$(GLOG)/.libs -L@top_builddir@/$(GTEST)/lib/.libs
+# Add glog, and gmock/gtest to include paths and library paths.
+CXXFLAGS += -I@top_srcdir@/$(GLOG)/src -I@top_builddir@/$(GLOG)/src -I@top_srcdir@/$(GMOCK)/include -I@top_srcdir@/$(GMOCK)/gtest/include
+LDFLAGS += -L@top_builddir@/$(GLOG)/.libs -L@top_builddir@/$(GMOCK)/lib/.libs -L@top_builddir@/$(GMOCK)/gtest/lib/.libs
 
 # Add included ZooKeeper to include and lib paths if necessary.
 ifeq ($(WITH_INCLUDED_ZOOKEEPER),1)
@@ -72,8 +72,8 @@ CXXFLAGS += -DBUILD_DATE="\"$$(date '+%Y
 CFLAGS += -DBUILD_USER="\"$$USER\""
 CXXFLAGS += -DBUILD_USER="\"$$USER\""
 
-# Add glog, gtest, libev, libprocess, pthread, and dl to LIBS.
-LIBS += -lglog -lgtest -lprocess -lev -lpthread -ldl
+# Add glog, gmock, gtest, libev, libprocess, pthread, and dl to LIBS.
+LIBS += -lglog -lgmock -lgtest -lprocess -lev -lpthread -ldl
 
 # Add ZooKeeper if necessary.
 ifeq ($(WITH_ZOOKEEPER),1)

Modified: incubator/mesos/trunk/src/tests/test_master.cpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/tests/test_master.cpp?rev=1132109&r1=1132108&r2=1132109&view=diff
==============================================================================
--- incubator/mesos/trunk/src/tests/test_master.cpp (original)
+++ incubator/mesos/trunk/src/tests/test_master.cpp Sun Jun  5 08:45:22 2011
@@ -1,7 +1,7 @@
-#include <gtest/gtest.h>
-
 #include <boost/lexical_cast.hpp>
 
+#include <gmock/gmock.h>
+
 #include <mesos_exec.hpp>
 #include <mesos_sched.hpp>
 
@@ -13,261 +13,405 @@
 #include "slave/process_based_isolation_module.hpp"
 #include "slave/slave.hpp"
 
-using std::string;
-using std::vector;
-
-using boost::lexical_cast;
-
 using namespace mesos;
 using namespace mesos::internal;
 
+using boost::lexical_cast;
+
 using mesos::internal::master::Master;
 using mesos::internal::slave::Slave;
 using mesos::internal::slave::Framework;
 using mesos::internal::slave::IsolationModule;
 using mesos::internal::slave::ProcessBasedIsolationModule;
 
+using std::string;
+using std::vector;
+
+using testing::_;
+using testing::A;
+using testing::An;
+using testing::AtMost;
+using testing::DoAll;
+using testing::Eq;
+using testing::ElementsAre;
+using testing::Ne;
+using testing::Return;
+using testing::SaveArg;
+using testing::Sequence;
+using testing::StrEq;
 
-class NoopScheduler : public Scheduler
+
+class MockScheduler : public Scheduler
 {
 public:
-  bool registeredCalled;
-  int offersGotten;
-  int slavesExpected;
+  MOCK_METHOD1(getFrameworkName, std::string(SchedulerDriver*));
+  MOCK_METHOD1(getExecutorInfo, ExecutorInfo(SchedulerDriver*));
+  MOCK_METHOD2(registered, void(SchedulerDriver*, FrameworkID));
+  MOCK_METHOD3(resourceOffer, void(SchedulerDriver*, OfferID,
+                                   const std::vector<SlaveOffer>&));
+  MOCK_METHOD2(offerRescinded, void(SchedulerDriver*, OfferID));
+  MOCK_METHOD2(statusUpdate, void(SchedulerDriver*, const TaskStatus&));
+  MOCK_METHOD2(frameworkMessage, void(SchedulerDriver*,
+                                      const FrameworkMessage&));
+  MOCK_METHOD2(slaveLost, void(SchedulerDriver*, SlaveID));
+  MOCK_METHOD3(error, void(SchedulerDriver*, int, const std::string&));
+};
+
 
+class MockExecutor : public Executor
+{
 public:
-  NoopScheduler(int _slavesExpected)
-    : slavesExpected(_slavesExpected),
-      registeredCalled(false),
-      offersGotten(0)
-  {}
+  MOCK_METHOD2(init, void(ExecutorDriver*, const ExecutorArgs&));
+  MOCK_METHOD2(launchTask, void(ExecutorDriver*, const TaskDescription&));
+  MOCK_METHOD2(killTask, void(ExecutorDriver*, TaskID));
+  MOCK_METHOD2(frameworkMessage, void(ExecutorDriver*, const FrameworkMessage&));
+  MOCK_METHOD1(shutdown, void(ExecutorDriver*));
+  MOCK_METHOD3(error, void(ExecutorDriver*, int, const std::string&));
+};
 
-  virtual ~NoopScheduler() {}
 
-  virtual ExecutorInfo getExecutorInfo(SchedulerDriver*) {
-    return ExecutorInfo("noexecutor", "");
-  }
+class MockFilter : public MessageFilter
+{
+ public:
+  MOCK_METHOD1(filter, bool(struct msg *));
+};
 
-  virtual void registered(SchedulerDriver*, FrameworkID fid) {
-    LOG(INFO) << "NoopScheduler registered with id " << fid;
-    registeredCalled = true;
-  }
 
-  virtual void resourceOffer(SchedulerDriver *d,
-                             OfferID id,
-                             const vector<SlaveOffer>& offers) {
-    LOG(INFO) << "NoopScheduler got a slot offer";
-    offersGotten++;
-    EXPECT_EQ(slavesExpected, offers.size());
-    foreach (const SlaveOffer& offer, offers) {
-      string cpus = offer.params.find("cpus")->second;
-      string mem = offer.params.find("mem")->second;
-      EXPECT_EQ("2", cpus);
-      EXPECT_EQ(lexical_cast<string>(1 * Gigabyte), mem);
-    }
-    vector<TaskDescription> tasks;
-    d->replyToOffer(id, tasks, map<string, string>());
-    d->stop();
-  }
-}; 
+struct trigger
+{
+  trigger() : value(false) {}
+  bool value;
+};
 
 
-TEST(MasterTest, NoopFrameworkWithOneSlave)
+MATCHER_P3(MsgMatcher, id, from, to, "")
 {
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
-  PID master = local::launch(1, 2, 1 * Gigabyte, false, false);
-  NoopScheduler sched(1);
-  MesosSchedulerDriver driver(&sched, master);
-  driver.run();
-  EXPECT_TRUE(sched.registeredCalled);
-  EXPECT_EQ(1, sched.offersGotten);
-  local::shutdown();
+  return (testing::Matcher<MSGID>(id).Matches(arg->id) &&
+          testing::Matcher<PID>(from).Matches(arg->from) &&
+          testing::Matcher<PID>(to).Matches(arg->to));
 }
 
 
-TEST(MasterTest, NoopFrameworkWithMultipleSlaves)
+ACTION_P(Trigger, trigger) { trigger->value = true; }
+
+
+#define EXPECT_MSG(filter, id, from, to)                \
+  EXPECT_CALL(filter, filter(MsgMatcher(id, from, to)))
+
+
+#define WAIT_UNTIL(trigger)                                             \
+  do {                                                                  \
+    int sleeps = 0;                                                     \
+    do {                                                                \
+      __sync_synchronize();                                             \
+      if (trigger.value)                                                \
+        break;                                                          \
+      usleep(10);                                                       \
+      if (sleeps++ >= 100000) {                                         \
+        ADD_FAILURE();                                                  \
+        break;                                                          \
+      }                                                                 \
+    } while (true);                                                     \
+  } while (false)
+
+
+class LocalIsolationModule : public IsolationModule
+{
+public:
+  Executor *executor;
+  MesosExecutorDriver *driver;
+  string pid;
+
+  LocalIsolationModule(Executor *_executor)
+    : executor(_executor), driver(NULL) {}
+
+  virtual ~LocalIsolationModule() {}
+
+  virtual void initialize(Slave *slave) {
+    pid = slave->self();
+  }
+
+  virtual void startExecutor(Framework *framework) {
+    // TODO(benh): Cleanup the way we launch local drivers!
+    setenv("MESOS_LOCAL", "1", 1);
+    setenv("MESOS_SLAVE_PID", pid.c_str(), 1);
+    setenv("MESOS_FRAMEWORK_ID", framework->id.c_str(), 1);
+
+    driver = new MesosExecutorDriver(executor);
+    driver->start();
+  }
+
+  virtual void killExecutor(Framework* framework) {
+    driver->stop();
+    driver->join();
+    delete driver;
+
+    // TODO(benh): Cleanup the way we launch local drivers!
+    unsetenv("MESOS_LOCAL");
+    unsetenv("MESOS_SLAVE_PID");
+    unsetenv("MESOS_FRAMEWORK_ID");
+  }
+};
+
+
+TEST(MasterTest, ResourceOfferForMultipleSlaves)
 {
   ASSERT_TRUE(GTEST_IS_THREADSAFE);
+
   PID master = local::launch(10, 2, 1 * Gigabyte, false, false);
-  NoopScheduler sched(10);
+
+  MockScheduler sched;
   MesosSchedulerDriver driver(&sched, master);
-  driver.run();
-  EXPECT_TRUE(sched.registeredCalled);
-  EXPECT_EQ(1, sched.offersGotten);
+
+  vector<SlaveOffer> offers;
+
+  trigger resourceOfferCall;
+
+  EXPECT_CALL(sched, getFrameworkName(&driver))
+    .WillOnce(Return(""));
+
+  EXPECT_CALL(sched, getExecutorInfo(&driver))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
+
+  EXPECT_CALL(sched, registered(&driver, _))
+    .Times(1);
+
+  EXPECT_CALL(sched, resourceOffer(&driver, _, _))
+    .WillOnce(DoAll(SaveArg<2>(&offers), Trigger(&resourceOfferCall)));
+
+  EXPECT_CALL(sched, offerRescinded(&driver, _))
+    .Times(AtMost(1));
+
+  driver.start();
+
+  WAIT_UNTIL(resourceOfferCall);
+
+  ASSERT_GE(10, offers.size());
+  EXPECT_EQ("2", offers[0].params["cpus"]);
+  EXPECT_EQ("1024", offers[0].params["mem"]);
+
+  driver.stop();
+  driver.join();
+
   local::shutdown();
 }
 
 
-class FixedResponseScheduler : public Scheduler
+class ReplyToOfferErrorTest : public testing::Test
 {
-public:
-  vector<TaskDescription> response;
-  string errorMessage;
-  
-  FixedResponseScheduler(vector<TaskDescription> _response)
-    : response(_response) {}
-
-  virtual ~FixedResponseScheduler() {}
+protected:
+  ReplyToOfferErrorTest() : driver(NULL)
+  {
+    PID master = local::launch(1, 3, 3 * Gigabyte, false, false);
+    driver = new MesosSchedulerDriver(&sched, master);
+  }
 
-  virtual ExecutorInfo getExecutorInfo(SchedulerDriver*) {
-    return ExecutorInfo("noexecutor", "");
+  virtual ~ReplyToOfferErrorTest()
+  {
+    delete driver;
   }
 
-  virtual void resourceOffer(SchedulerDriver* d,
-                             OfferID id,
-                             const vector<SlaveOffer>& offers) {
-    LOG(INFO) << "FixedResponseScheduler got a slot offer";
-    d->replyToOffer(id, response, map<string, string>());
+  virtual void SetUp()
+  {
+    ASSERT_TRUE(GTEST_IS_THREADSAFE);
+
+    trigger resourceOfferCall;
+
+    EXPECT_CALL(sched, getFrameworkName(driver))
+      .WillOnce(Return(""));
+
+    EXPECT_CALL(sched, getExecutorInfo(driver))
+      .WillOnce(Return(ExecutorInfo("noexecutor", "")));
+
+    EXPECT_CALL(sched, registered(driver, _))
+      .Times(1);
+
+    EXPECT_CALL(sched, resourceOffer(driver, _, ElementsAre(_)))
+      .WillOnce(DoAll(SaveArg<1>(&offerId), SaveArg<2>(&offers),
+                      Trigger(&resourceOfferCall)));
+
+    driver->start();
+
+    WAIT_UNTIL(resourceOfferCall);
+
+    ASSERT_GE(1, offers.size());
+    EXPECT_EQ("3", offers[0].params["cpus"]);
+    EXPECT_EQ("3072", offers[0].params["mem"]);
   }
-  
-  virtual void error(SchedulerDriver* d,
-                     int code,
-                     const std::string& message) {
-    errorMessage = message;
-    d->stop();
+
+  virtual void TearDown()
+  {
+    ASSERT_NE("", message);
+
+    trigger errorCall;
+
+    EXPECT_CALL(sched, error(driver, _, message))
+      .WillOnce(Trigger(&errorCall));
+
+    EXPECT_CALL(sched, offerRescinded(driver, offerId))
+      .Times(AtMost(1));
+
+    driver->replyToOffer(offerId, tasks, map<string, string>());
+
+    WAIT_UNTIL(errorCall);
+
+    driver->stop();
+    driver->join();
+
+    local::shutdown();
   }
+
+  MockScheduler sched;
+  MesosSchedulerDriver *driver;
+
+  OfferID offerId;
+  vector<SlaveOffer> offers;
+
+  map<string, string> params;
+  vector<TaskDescription> tasks;
+  string message;
 };
 
 
-TEST(MasterTest, DuplicateTaskIdsInResponse)
+TEST_F(ReplyToOfferErrorTest, DuplicateTaskIdsInResponse)
 {
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
-  PID master = local::launch(1, 3, 3 * Gigabyte, false, false);
-  vector<TaskDescription> tasks;
-  map<string, string> params;
   params["cpus"] = "1";
   params["mem"] = lexical_cast<string>(1 * Gigabyte);
-  tasks.push_back(TaskDescription(1, "0-0", "", params, ""));
-  tasks.push_back(TaskDescription(2, "0-0", "", params, ""));
-  tasks.push_back(TaskDescription(1, "0-0", "", params, ""));
-  FixedResponseScheduler sched(tasks);
-  MesosSchedulerDriver driver(&sched, master);
-  driver.run();
-  EXPECT_EQ("Duplicate task ID: 1", sched.errorMessage);
-  local::shutdown();
+
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", params, bytes()));
+  tasks.push_back(TaskDescription(2, offers[0].slaveId, "", params, bytes()));
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", params, bytes()));
+
+  message = "Duplicate task ID: 1";
 }
 
 
-TEST(MasterTest, TooMuchMemoryInTask)
+TEST_F(ReplyToOfferErrorTest, TooMuchMemoryInTask)
 {
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
-  PID master = local::launch(1, 3, 3 * Gigabyte, false, false);
-  vector<TaskDescription> tasks;
-  map<string, string> params;
   params["cpus"] = "1";
   params["mem"] = lexical_cast<string>(4 * Gigabyte);
-  tasks.push_back(TaskDescription(1, "0-0", "", params, ""));
-  FixedResponseScheduler sched(tasks);
-  MesosSchedulerDriver driver(&sched, master);
-  driver.run();
-  EXPECT_EQ("Too many resources accepted", sched.errorMessage);
-  local::shutdown();
+
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", params, bytes()));
+
+  message = "Too many resources accepted";
 }
 
 
-TEST(MasterTest, TooMuchCpuInTask)
+TEST_F(ReplyToOfferErrorTest, TooMuchCpuInTask)
 {
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
-  PID master = local::launch(1, 3, 3 * Gigabyte, false, false);
-  vector<TaskDescription> tasks;
-  map<string, string> params;
   params["cpus"] = "4";
   params["mem"] = lexical_cast<string>(1 * Gigabyte);
-  tasks.push_back(TaskDescription(1, "0-0", "", params, ""));
-  FixedResponseScheduler sched(tasks);
-  MesosSchedulerDriver driver(&sched, master);
-  driver.run();
-  EXPECT_EQ("Too many resources accepted", sched.errorMessage);
-  local::shutdown();
+
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", params, bytes()));
+  message = "Too many resources accepted";
 }
 
 
-TEST(MasterTest, TooLittleCpuInTask)
+TEST_F(ReplyToOfferErrorTest, TooLittleCpuInTask)
 {
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
-  PID master = local::launch(1, 3, 3 * Gigabyte, false, false);
-  vector<TaskDescription> tasks;
-  map<string, string> params;
   params["cpus"] = "0";
   params["mem"] = lexical_cast<string>(1 * Gigabyte);
-  tasks.push_back(TaskDescription(1, "0-0", "", params, ""));
-  FixedResponseScheduler sched(tasks);
-  MesosSchedulerDriver driver(&sched, master);
-  driver.run();
-  EXPECT_EQ("Invalid task size: <0 CPUs, 1024 MEM>", sched.errorMessage);
-  local::shutdown();
+
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", params, bytes()));
+  message = "Invalid task size: <0 CPUs, 1024 MEM>";
 }
 
 
-TEST(MasterTest, TooLittleMemoryInTask)
+TEST_F(ReplyToOfferErrorTest, TooLittleMemoryInTask)
 {
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
-  PID master = local::launch(1, 3, 3 * Gigabyte, false, false);
-  vector<TaskDescription> tasks;
-  map<string, string> params;
   params["cpus"] = "1";
   params["mem"] = "1";
-  tasks.push_back(TaskDescription(1, "0-0", "", params, ""));
-  FixedResponseScheduler sched(tasks);
-  MesosSchedulerDriver driver(&sched, master);
-  driver.run();
-  EXPECT_EQ("Invalid task size: <1 CPUs, 1 MEM>", sched.errorMessage);
-  local::shutdown();
+
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", params, bytes()));
+
+  message = "Invalid task size: <1 CPUs, 1 MEM>";
 }
 
 
-TEST(MasterTest, TooMuchMemoryAcrossTasks)
+TEST_F(ReplyToOfferErrorTest, TooMuchMemoryAcrossTasks)
 {
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
-  PID master = local::launch(1, 3, 3 * Gigabyte, false, false);
-  vector<TaskDescription> tasks;
-  map<string, string> params;
   params["cpus"] = "1";
   params["mem"] = lexical_cast<string>(2 * Gigabyte);
-  tasks.push_back(TaskDescription(1, "0-0", "", params, ""));
-  tasks.push_back(TaskDescription(2, "0-0", "", params, ""));
-  FixedResponseScheduler sched(tasks);
-  MesosSchedulerDriver driver(&sched, master);
-  driver.run();
-  EXPECT_EQ("Too many resources accepted", sched.errorMessage);
-  local::shutdown();
+
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", params, bytes()));
+  tasks.push_back(TaskDescription(2, offers[0].slaveId, "", params, bytes()));
+
+  message = "Too many resources accepted";
 }
 
 
-TEST(MasterTest, TooMuchCpuAcrossTasks)
+TEST_F(ReplyToOfferErrorTest, TooMuchCpuAcrossTasks)
 {
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
-  PID master = local::launch(1, 3, 3 * Gigabyte, false, false);
-  vector<TaskDescription> tasks;
-  map<string, string> params;
   params["cpus"] = "2";
   params["mem"] = lexical_cast<string>(1 * Gigabyte);
-  tasks.push_back(TaskDescription(1, "0-0", "", params, ""));
-  tasks.push_back(TaskDescription(2, "0-0", "", params, ""));
-  FixedResponseScheduler sched(tasks);
-  MesosSchedulerDriver driver(&sched, master);
-  driver.run();
-  EXPECT_EQ("Too many resources accepted", sched.errorMessage);
-  local::shutdown();
+
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", params, bytes()));
+  tasks.push_back(TaskDescription(2, offers[0].slaveId, "", params, bytes()));
+
+  message = "Too many resources accepted";
 }
 
 
 TEST(MasterTest, ResourcesReofferedAfterReject)
 {
   ASSERT_TRUE(GTEST_IS_THREADSAFE);
+
   PID master = local::launch(10, 2, 1 * Gigabyte, false, false);
 
-  NoopScheduler sched1(10);
+  MockScheduler sched1;
   MesosSchedulerDriver driver1(&sched1, master);
-  driver1.run();
-  EXPECT_TRUE(sched1.registeredCalled);
-  EXPECT_EQ(1, sched1.offersGotten);
 
-  NoopScheduler sched2(10);
+  OfferID offerId;
+
+  trigger sched1ResourceOfferCall;
+
+  EXPECT_CALL(sched1, getFrameworkName(&driver1))
+    .WillOnce(Return(""));
+
+  EXPECT_CALL(sched1, getExecutorInfo(&driver1))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
+
+  EXPECT_CALL(sched1, registered(&driver1, _))
+    .Times(1);
+
+  EXPECT_CALL(sched1, resourceOffer(&driver1, _, _))
+    .WillOnce(DoAll(SaveArg<1>(&offerId), Trigger(&sched1ResourceOfferCall)));
+
+  driver1.start();
+
+  WAIT_UNTIL(sched1ResourceOfferCall);
+
+  driver1.replyToOffer(offerId, vector<TaskDescription>(), map<string, string>());
+
+  driver1.stop();
+  driver1.join();
+
+  MockScheduler sched2;
   MesosSchedulerDriver driver2(&sched2, master);
-  driver2.run();
-  EXPECT_TRUE(sched2.registeredCalled);
-  EXPECT_EQ(1, sched2.offersGotten);
+
+  trigger sched2ResourceOfferCall;
+
+  EXPECT_CALL(sched2, getFrameworkName(&driver2))
+    .WillOnce(Return(""));
+
+  EXPECT_CALL(sched2, getExecutorInfo(&driver2))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
+
+  EXPECT_CALL(sched2, registered(&driver2, _))
+    .Times(1);
+
+  EXPECT_CALL(sched2, resourceOffer(&driver2, _, _))
+    .WillOnce(Trigger(&sched2ResourceOfferCall));
+
+  EXPECT_CALL(sched2, offerRescinded(&driver2, _))
+    .Times(AtMost(1));
+
+  driver2.start();
+
+  WAIT_UNTIL(sched2ResourceOfferCall);
+
+  driver2.stop();
+  driver2.join();
 
   local::shutdown();
 }
@@ -276,55 +420,87 @@ TEST(MasterTest, ResourcesReofferedAfter
 TEST(MasterTest, ResourcesReofferedAfterBadResponse)
 {
   ASSERT_TRUE(GTEST_IS_THREADSAFE);
+
   PID master = local::launch(1, 2, 1 * Gigabyte, false, false);
 
-  vector<TaskDescription> tasks;
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(&sched1, master);
+
+  OfferID offerId;
+  vector<SlaveOffer> offers;
+
+  trigger sched1ResourceOfferCall;
+
+  EXPECT_CALL(sched1, getFrameworkName(&driver1))
+    .WillOnce(Return(""));
+
+  EXPECT_CALL(sched1, getExecutorInfo(&driver1))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
+
+  EXPECT_CALL(sched1, registered(&driver1, _))
+    .Times(1);
+
+  EXPECT_CALL(sched1, resourceOffer(&driver1, _, ElementsAre(_)))
+    .WillOnce(DoAll(SaveArg<1>(&offerId), SaveArg<2>(&offers),
+                    Trigger(&sched1ResourceOfferCall)));
+
+  driver1.start();
+
+  WAIT_UNTIL(sched1ResourceOfferCall);
+
+  ASSERT_GE(1, offers.size());
+
   map<string, string> params;
   params["cpus"] = "0";
   params["mem"] = lexical_cast<string>(1 * Gigabyte);
-  tasks.push_back(TaskDescription(1, "0-0", "", params, ""));
-  FixedResponseScheduler sched1(tasks);
-  MesosSchedulerDriver driver1(&sched1, master);
-  driver1.run();
-  EXPECT_EQ("Invalid task size: <0 CPUs, 1024 MEM>", sched1.errorMessage);
 
-  NoopScheduler sched2(1);
+  vector<TaskDescription> tasks;
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", params, bytes()));
+
+  trigger errorCall;
+
+  EXPECT_CALL(sched1, error(&driver1, _, "Invalid task size: <0 CPUs, 1024 MEM>"))
+    .WillOnce(Trigger(&errorCall));
+
+  EXPECT_CALL(sched1, offerRescinded(&driver1, offerId))
+    .Times(AtMost(1));
+
+  driver1.replyToOffer(offerId, tasks, map<string, string>());
+
+  WAIT_UNTIL(errorCall);
+
+  driver1.stop();
+  driver1.join();
+
+  MockScheduler sched2;
   MesosSchedulerDriver driver2(&sched2, master);
-  driver2.run();
-  EXPECT_TRUE(sched2.registeredCalled);
-  EXPECT_EQ(1, sched2.offersGotten);
 
-  local::shutdown();
-}
+  trigger sched2ResourceOfferCall;
 
+  EXPECT_CALL(sched2, getFrameworkName(&driver2))
+    .WillOnce(Return(""));
 
-class SlaveLostScheduler : public Scheduler
-{
-public:
-  PID slave;
-  bool slaveLostCalled;
-  
-  SlaveLostScheduler(const PID &_slave)
-    : slave(_slave), slaveLostCalled(false) {}
+  EXPECT_CALL(sched2, getExecutorInfo(&driver2))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
 
-  virtual ~SlaveLostScheduler() {}
+  EXPECT_CALL(sched2, registered(&driver2, _))
+    .Times(1);
 
-  virtual ExecutorInfo getExecutorInfo(SchedulerDriver*) {
-    return ExecutorInfo("noexecutor", "");
-  }
+  EXPECT_CALL(sched2, resourceOffer(&driver2, _, _))
+    .WillOnce(Trigger(&sched2ResourceOfferCall));
 
-  virtual void resourceOffer(SchedulerDriver* d,
-                             OfferID id,
-                             const vector<SlaveOffer>& offers) {
-    LOG(INFO) << "SlaveLostScheduler got a slot offer";
-    MesosProcess::post(slave, pack<S2S_SHUTDOWN>());
-  }
-  
-  virtual void slaveLost(SchedulerDriver* d, SlaveID slaveId) {
-    slaveLostCalled = true;
-    d->stop();
-  }
-};
+  EXPECT_CALL(sched2, offerRescinded(&driver2, _))
+    .Times(AtMost(1));
+
+  driver2.start();
+
+  WAIT_UNTIL(sched2ResourceOfferCall);
+
+  driver2.stop();
+  driver2.join();
+
+  local::shutdown();
+}
 
 
 TEST(MasterTest, SlaveLost)
@@ -340,74 +516,54 @@ TEST(MasterTest, SlaveLost)
 
   BasicMasterDetector detector(master, slave, true);
 
-  SlaveLostScheduler sched(slave);
-
+  MockScheduler sched;
   MesosSchedulerDriver driver(&sched, master);
-  driver.run();
 
-  EXPECT_TRUE(sched.slaveLostCalled);
+  OfferID offerId;
+  vector<SlaveOffer> offers;
 
-  Process::wait(slave);
+  trigger resourceOfferCall;
 
-  MesosProcess::post(master, pack<M2M_SHUTDOWN>());
-  Process::wait(master);
-}
+  EXPECT_CALL(sched, getFrameworkName(&driver))
+    .WillOnce(Return(""));
 
+  EXPECT_CALL(sched, getExecutorInfo(&driver))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
 
+  EXPECT_CALL(sched, registered(&driver, _))
+    .Times(1);
 
-class FailoverScheduler : public Scheduler
-{
-public:
-  bool registeredCalled;
-  
-  FailoverScheduler() : registeredCalled(false) {}
+  EXPECT_CALL(sched, resourceOffer(&driver, _, _))
+    .WillOnce(DoAll(SaveArg<1>(&offerId), SaveArg<2>(&offers),
+                    Trigger(&resourceOfferCall)));
 
-  virtual ~FailoverScheduler() {}
+  driver.start();
 
-  virtual ExecutorInfo getExecutorInfo(SchedulerDriver*) {
-    return ExecutorInfo("noexecutor", "");
-  }
+  WAIT_UNTIL(resourceOfferCall);
 
-  virtual void registered(SchedulerDriver *d, FrameworkID fid) {
-    LOG(INFO) << "FailoverScheduler registered";
-    registeredCalled = true;
-    d->stop();
-  }
-};
+  ASSERT_GE(1, offers.size());
 
+  trigger offerRescindedCall, slaveLostCall;
 
-class FailingScheduler : public Scheduler
-{
-public:
-  Scheduler *failover;
-  PID master;
-  MesosSchedulerDriver *driver;
-  string errorMessage;
+  EXPECT_CALL(sched, offerRescinded(&driver, offerId))
+    .WillOnce(Trigger(&offerRescindedCall));
 
-  FailingScheduler(Scheduler *_failover, const PID &_master)
-    : failover(_failover), master(_master) {}
+  EXPECT_CALL(sched, slaveLost(&driver, offers[0].slaveId))
+    .WillOnce(Trigger(&slaveLostCall));
 
-  virtual ~FailingScheduler() {
-    delete driver;
-  }
+  MesosProcess::post(slave, pack<S2S_SHUTDOWN>());
 
-  virtual ExecutorInfo getExecutorInfo(SchedulerDriver*) {
-    return ExecutorInfo("noexecutor", "");
-  }
+  WAIT_UNTIL(offerRescindedCall);
+  WAIT_UNTIL(slaveLostCall);
 
-  virtual void registered(SchedulerDriver*, FrameworkID fid) {
-    LOG(INFO) << "FailingScheduler registered";
-    driver = new MesosSchedulerDriver(failover, master, fid);
-    driver->start();
-  }
+  driver.stop();
+  driver.join();
 
-  virtual void error(SchedulerDriver* d,
-                     int code,
-                     const std::string& message) {
-    errorMessage = message;
-    d->stop();
-  }
-};
+  Process::wait(slave);
+
+  MesosProcess::post(master, pack<M2M_SHUTDOWN>());
+  Process::wait(master);
+}
 
 
 TEST(MasterTest, SchedulerFailover)
@@ -416,271 +572,201 @@ TEST(MasterTest, SchedulerFailover)
 
   PID master = local::launch(1, 2, 1 * Gigabyte, false, false);
 
-  FailoverScheduler failoverSched;
-  FailingScheduler failingSched(&failoverSched, master);
+  // Launch the first (i.e., failing) scheduler and wait until
+  // registered gets called to launch the second (i.e., failover)
+  // scheduler.
 
-  MesosSchedulerDriver driver(&failingSched, master);
-  driver.run();
+  MockScheduler failingSched;
+  MesosSchedulerDriver failingDriver(&failingSched, master);
 
-  EXPECT_EQ("Framework failover", failingSched.errorMessage);
+  FrameworkID frameworkId;
 
-  failingSched.driver->join();
+  trigger failingRegisteredCall;
 
-  EXPECT_TRUE(failoverSched.registeredCalled);
+  EXPECT_CALL(failingSched, getFrameworkName(&failingDriver))
+    .WillOnce(Return(""));
 
-  local::shutdown();
-}
+  EXPECT_CALL(failingSched, getExecutorInfo(&failingDriver))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
 
+  EXPECT_CALL(failingSched, registered(&failingDriver, _))
+    .WillOnce(DoAll(SaveArg<1>(&frameworkId), Trigger(&failingRegisteredCall)));
 
-class OfferRescindedScheduler : public Scheduler
-{
-public:
-  const PID slave;
-  bool offerRescindedCalled;
-  
-  OfferRescindedScheduler(const PID &_slave)
-    : slave(_slave), offerRescindedCalled(false) {}
-
-  virtual ~OfferRescindedScheduler() {}
+  EXPECT_CALL(failingSched, resourceOffer(&failingDriver, _, _))
+    .Times(AtMost(1));
 
-  virtual ExecutorInfo getExecutorInfo(SchedulerDriver*) {
-    return ExecutorInfo("noexecutor", "");
-  }
+  EXPECT_CALL(failingSched, offerRescinded(&failingDriver, _))
+    .Times(AtMost(1));
 
-  virtual void resourceOffer(SchedulerDriver* d,
-                             OfferID id,
-                             const vector<SlaveOffer>& offers) {
-    LOG(INFO) << "OfferRescindedScheduler got a slot offer";
-    vector<TaskDescription> tasks;
-    ASSERT_TRUE(offers.size() == 1);
-    const SlaveOffer &offer = offers[0];
-    TaskDescription desc(0, offer.slaveId, "", offer.params, "");
-    tasks.push_back(desc);
-    d->replyToOffer(id, tasks, map<string, string>());
-    MesosProcess::post(slave, pack<S2S_SHUTDOWN>());
-  }
+  EXPECT_CALL(failingSched, error(&failingDriver, _, "Framework failover"))
+    .Times(1);
 
-  virtual void offerRescinded(SchedulerDriver* d, OfferID)
-  {
-    offerRescindedCalled = true;
-    d->stop();
-  }
-};
+  failingDriver.start();
 
+  WAIT_UNTIL(failingRegisteredCall);
 
-class OfferReplyMessageFilter : public MessageFilter
-{
-public:
-  virtual bool filter(struct msg *msg) {
-    return msg->id == F2M_SLOT_OFFER_REPLY;
-  }
-};
+  // Now launch the second (i.e., failover) scheduler using the
+  // framework id recorded from the first scheduler and wait until it
+  // gets a registered callback..
 
+  MockScheduler failoverSched;
+  MesosSchedulerDriver failoverDriver(&failoverSched, master, frameworkId);
 
-TEST(MasterTest, OfferRescinded)
-{
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
+  trigger failoverRegisteredCall;
 
-  OfferReplyMessageFilter filter;
-  Process::filter(&filter);
+  EXPECT_CALL(failoverSched, getFrameworkName(&failoverDriver))
+    .WillOnce(Return(""));
 
-  Master m;
-  PID master = Process::spawn(&m);
+  EXPECT_CALL(failoverSched, getExecutorInfo(&failoverDriver))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
 
-  ProcessBasedIsolationModule isolationModule;
-  Slave s(Resources(2, 1 * Gigabyte), true, &isolationModule);
-  PID slave = Process::spawn(&s);
+  EXPECT_CALL(failoverSched, registered(&failoverDriver, frameworkId))
+    .WillOnce(Trigger(&failoverRegisteredCall));
 
-  BasicMasterDetector detector(master, slave, true);
+  EXPECT_CALL(failoverSched, resourceOffer(&failoverDriver, _, _))
+    .Times(AtMost(1));
 
-  OfferRescindedScheduler sched(slave);
-  MesosSchedulerDriver driver(&sched, master);
+  EXPECT_CALL(failoverSched, offerRescinded(&failoverDriver, _))
+    .Times(AtMost(1));
 
-  driver.run();
+  failoverDriver.start();
 
-  EXPECT_TRUE(sched.offerRescindedCalled);
+  WAIT_UNTIL(failoverRegisteredCall);
 
-  Process::wait(slave);
+  failingDriver.stop();
+  failoverDriver.stop();
 
-  MesosProcess::post(master, pack<M2M_SHUTDOWN>());
-  Process::wait(master);
+  failingDriver.join();
+  failoverDriver.join();
 
-  Process::filter(NULL);
+  local::shutdown();
 }
 
 
-class SlavePartitionedScheduler : public Scheduler
+TEST(MasterTest, SlavePartitioned)
 {
-public:
-  bool slaveLostCalled;
-  
-  SlavePartitionedScheduler()
-    : slaveLostCalled(false) {}
+  ASSERT_TRUE(GTEST_IS_THREADSAFE);
 
-  virtual ~SlavePartitionedScheduler() {}
+  ProcessClock::pause();
 
-  virtual ExecutorInfo getExecutorInfo(SchedulerDriver*) {
-    return ExecutorInfo("noexecutor", "");
-  }
+  MockFilter filter;
+  Process::filter(&filter);
 
-  virtual void slaveLost(SchedulerDriver* d, SlaveID slaveId) {
-    slaveLostCalled = true;
-    d->stop();
-  }
-};
+  EXPECT_MSG(filter, _, _, _)
+    .WillRepeatedly(Return(false));
 
+  PID master = local::launch(1, 2, 1 * Gigabyte, false, false);
 
-class HeartbeatMessageFilter : public MessageFilter
-{
-public:
-  virtual bool filter(struct msg *msg) {
-    return msg->id == SH2M_HEARTBEAT;
-  }
-};
+  MockScheduler sched;
+  MesosSchedulerDriver driver(&sched, master);
 
+  trigger slaveLostCall;
 
-TEST(MasterTest, SlavePartitioned)
-{
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
+  EXPECT_CALL(sched, getFrameworkName(&driver))
+    .WillOnce(Return(""));
 
-  HeartbeatMessageFilter filter;
-  Process::filter(&filter);
+  EXPECT_CALL(sched, getExecutorInfo(&driver))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
 
-  ProcessClock::pause();
+  EXPECT_CALL(sched, registered(&driver, _))
+    .Times(1);
 
-  PID master = local::launch(1, 2, 1 * Gigabyte, false, false);
+  EXPECT_CALL(sched, resourceOffer(&driver, _, _))
+    .Times(AtMost(1));
 
-  SlavePartitionedScheduler sched;
-  MesosSchedulerDriver driver(&sched, master);
+  EXPECT_CALL(sched, offerRescinded(&driver, _))
+    .Times(AtMost(1));
+
+  EXPECT_CALL(sched, slaveLost(&driver, _))
+    .WillOnce(Trigger(&slaveLostCall));
+
+  EXPECT_MSG(filter, Eq(SH2M_HEARTBEAT), _, _)
+    .WillRepeatedly(Return(true));
 
   driver.start();
 
   ProcessClock::advance(master::HEARTBEAT_TIMEOUT);
 
-  driver.join();
+  WAIT_UNTIL(slaveLostCall);
 
-  EXPECT_TRUE(sched.slaveLostCalled);
+  driver.stop();
+  driver.join();
 
   local::shutdown();
 
-  ProcessClock::resume();
-
   Process::filter(NULL);
+
+  ProcessClock::resume();
 }
 
 
-class TaskRunningScheduler : public Scheduler
+TEST(MasterTest, TaskRunning)
 {
-public:
-  FrameworkID fid;
-  bool statusUpdateCalled;
-  string errorMessage;
-  
-  TaskRunningScheduler()
-    : statusUpdateCalled(false) {}
+  ASSERT_TRUE(GTEST_IS_THREADSAFE);
 
-  virtual ~TaskRunningScheduler() {}
+  Master m;
+  PID master = Process::spawn(&m);
 
-  virtual ExecutorInfo getExecutorInfo(SchedulerDriver*) {
-    return ExecutorInfo("noexecutor", "");
-  }
+  MockExecutor exec;
 
-  virtual void registered(SchedulerDriver*, FrameworkID fid) {
-    LOG(INFO) << "TaskRunningScheduler registered";
-    this->fid = fid;
-  }
+  EXPECT_CALL(exec, init(_, _))
+    .Times(1);
 
-  virtual void resourceOffer(SchedulerDriver* d,
-                             OfferID id,
-                             const vector<SlaveOffer>& offers) {
-    LOG(INFO) << "TaskRunningScheduler got a slot offer";
-    vector<TaskDescription> tasks;
-    ASSERT_TRUE(offers.size() == 1);
-    const SlaveOffer &offer = offers[0];
-    TaskDescription desc(0, offer.slaveId, "", offer.params, "");
-    tasks.push_back(desc);
-    d->replyToOffer(id, tasks, map<string, string>());
-  }
+  EXPECT_CALL(exec, launchTask(_, _))
+    .Times(1);
 
-  virtual void statusUpdate(SchedulerDriver* d, const TaskStatus& status) {
-    EXPECT_EQ(TASK_RUNNING, status.state);
-    statusUpdateCalled = true;
-    d->stop();
-  }
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(1);
 
-  virtual void error(SchedulerDriver* d,
-                     int code,
-                     const std::string& message) {
-    errorMessage = message;
-    d->stop();
-  }
-};
+  LocalIsolationModule isolationModule(&exec);
 
+  Slave s(Resources(2, 1 * Gigabyte), true, &isolationModule);
+  PID slave = Process::spawn(&s);
 
-class TaskRunningExecutor : public Executor {};
+  BasicMasterDetector detector(master, slave, true);
 
+  MockScheduler sched;
+  MesosSchedulerDriver driver(&sched, master);
 
-class LocalIsolationModule : public IsolationModule
-{
-public:
-  Executor *executor;
-  MesosExecutorDriver *driver;
-  string pid;
+  OfferID offerId;
+  vector<SlaveOffer> offers;
+  TaskStatus status;
 
-  LocalIsolationModule(Executor *_executor)
-    : executor(_executor), driver(NULL) {}
+  trigger resourceOfferCall, statusUpdateCall;
 
-  virtual ~LocalIsolationModule() {}
+  EXPECT_CALL(sched, getFrameworkName(&driver))
+    .WillOnce(Return(""));
 
-  virtual void initialize(Slave *slave) {
-    pid = slave->self();
-  }
+  EXPECT_CALL(sched, getExecutorInfo(&driver))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
 
-  virtual void startExecutor(Framework *framework) {
-    // TODO(benh): Cleanup the way we launch local drivers!
-    setenv("MESOS_LOCAL", "1", 1);
-    setenv("MESOS_SLAVE_PID", pid.c_str(), 1);
-    setenv("MESOS_FRAMEWORK_ID", framework->id.c_str(), 1);
+  EXPECT_CALL(sched, registered(&driver, _))
+    .Times(1);
 
-    driver = new MesosExecutorDriver(executor);
-    driver->start();
-  }
+  EXPECT_CALL(sched, resourceOffer(&driver, _, _))
+    .WillOnce(DoAll(SaveArg<1>(&offerId), SaveArg<2>(&offers),
+                    Trigger(&resourceOfferCall)));
 
-  virtual void killExecutor(Framework* framework) {
-    driver->stop();
-    driver->join();
-    delete driver;
+  EXPECT_CALL(sched, statusUpdate(&driver, _))
+    .WillOnce(DoAll(SaveArg<1>(&status), Trigger(&statusUpdateCall)));
 
-    // TODO(benh): Cleanup the way we launch local drivers!
-    unsetenv("MESOS_LOCAL");
-    unsetenv("MESOS_SLAVE_PID");
-    unsetenv("MESOS_FRAMEWORK_ID");
-  }
-};
-
-
-TEST(MasterTest, TaskRunning)
-{
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
+  driver.start();
 
-  Master m;
-  PID master = Process::spawn(&m);
+  WAIT_UNTIL(resourceOfferCall);
 
-  TaskRunningExecutor exec;
-  LocalIsolationModule isolationModule(&exec);
+  ASSERT_GE(1, offers.size());
 
-  Slave s(Resources(2, 1 * Gigabyte), true, &isolationModule);
-  PID slave = Process::spawn(&s);
+  vector<TaskDescription> tasks;
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", offers[0].params, ""));
 
-  BasicMasterDetector detector(master, slave, true);
+  driver.replyToOffer(offerId, tasks, map<string, string>());
 
-  TaskRunningScheduler sched;
-  MesosSchedulerDriver driver(&sched, master);
+  WAIT_UNTIL(statusUpdateCall);
 
-  driver.run();
+  EXPECT_EQ(TASK_RUNNING, status.state);
 
-  EXPECT_TRUE(sched.statusUpdateCalled);
-  EXPECT_EQ("", sched.errorMessage);
+  driver.stop();
+  driver.join();
 
   MesosProcess::post(slave, pack<S2S_SHUTDOWN>());
   Process::wait(slave);
@@ -690,220 +776,247 @@ TEST(MasterTest, TaskRunning)
 }
 
 
-class SchedulerFailoverStatusUpdateScheduler : public TaskRunningScheduler
+TEST(MasterTest, SchedulerFailoverStatusUpdate)
 {
- public:
-  virtual void registered(SchedulerDriver*, FrameworkID fid) {
-    Process::filter(NULL);
-    ProcessClock::advance(RELIABLE_TIMEOUT);
-  }
-};
+  ASSERT_TRUE(GTEST_IS_THREADSAFE);
 
+  ProcessClock::pause();
 
-class StatusUpdateFilter : public MessageFilter
-{
-public:
-  TaskRunningScheduler *failover;
-  TaskRunningScheduler *failing;
-  const PID master;
-  MesosSchedulerDriver *driver;
+  MockFilter filter;
+  Process::filter(&filter);
 
-  StatusUpdateFilter(TaskRunningScheduler *_failover,
-                     TaskRunningScheduler *_failing,
-                     const PID &_master)
-    : failover(_failover), failing(_failing), master(_master),
-      driver(NULL) {}
-
-  ~StatusUpdateFilter() {
-    if (driver != NULL) {
-      driver->join();
-      delete driver;
-      driver = NULL;
-    }
-  }
+  EXPECT_MSG(filter, _, _, _)
+    .WillRepeatedly(Return(false));
 
-  virtual bool filter(struct msg *msg) {
-    // TODO(benh): Fix the brokenness of this test due to blocking
-    // S2M_FT_STATUS_UPDATE!
-    if (driver == NULL &&
-        msg->id == S2M_FT_STATUS_UPDATE &&
-        !(msg->to == master)) {
-      driver = new MesosSchedulerDriver(failover, master, failing->fid);
-      driver->start();
-      return true;
-    }
+  MockExecutor exec;
 
-    return false;
-  }
-};
+  EXPECT_CALL(exec, init(_, _))
+    .Times(1);
 
+  EXPECT_CALL(exec, launchTask(_, _))
+    .Times(1);
 
-TEST(MasterTest, SchedulerFailoverStatusUpdate)
-{
-  ASSERT_TRUE(GTEST_IS_THREADSAFE);
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(1);
 
-  ProcessClock::pause();
+  LocalIsolationModule isolationModule(&exec);
 
+// <<<<<<<<<<<<<<
+//   PID master;
+//   vector<PID> slaves;
+
+//   tie(master, slaves) = 
+//     local::launch(1, 2, 1 * Gigabyte, false, false, isolationModule);
+// >>>>>>>>>>>>>>
   Master m;
   PID master = Process::spawn(&m);
 
-  TaskRunningExecutor exec;
-  LocalIsolationModule isolationModule(&exec);
-
   Slave s(Resources(2, 1 * Gigabyte), true, &isolationModule);
   PID slave = Process::spawn(&s);
 
   BasicMasterDetector detector(master, slave, true);
+// >>>>>>>>>>>>>>
 
-  SchedulerFailoverStatusUpdateScheduler failoverSched;
-  TaskRunningScheduler failingSched;
+  // Launch the first (i.e., failing) scheduler and wait until the
+  // first status update message is sent to it (drop the message).
 
-  StatusUpdateFilter filter(&failoverSched, &failingSched, master);
-  Process::filter(&filter);
+  MockScheduler failingSched;
+  MesosSchedulerDriver failingDriver(&failingSched, master);
 
-  MesosSchedulerDriver driver(&failingSched, master);
+  FrameworkID frameworkId;
+  OfferID offerId;
+  vector<SlaveOffer> offers;
 
-  driver.run();
+  trigger resourceOfferCall;
 
-  EXPECT_FALSE(failingSched.statusUpdateCalled);
-  EXPECT_EQ("Framework failover", failingSched.errorMessage);
+  EXPECT_CALL(failingSched, getFrameworkName(&failingDriver))
+    .WillOnce(Return(""));
 
-  filter.driver->join();
+  EXPECT_CALL(failingSched, getExecutorInfo(&failingDriver))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
 
-  EXPECT_TRUE(failoverSched.statusUpdateCalled);
-  EXPECT_EQ("", failoverSched.errorMessage);
+  EXPECT_CALL(failingSched, registered(&failingDriver, _))
+    .WillOnce(SaveArg<1>(&frameworkId));
 
-  Process::filter(NULL);
+  EXPECT_CALL(failingSched, resourceOffer(&failingDriver, _, _))
+    .WillOnce(DoAll(SaveArg<1>(&offerId), SaveArg<2>(&offers),
+                    Trigger(&resourceOfferCall)));
 
-  MesosProcess::post(slave, pack<S2S_SHUTDOWN>());
-  Process::wait(slave);
+  EXPECT_CALL(failingSched, error(&failingDriver, _, "Framework failover"))
+    .Times(1);
 
-  MesosProcess::post(master, pack<M2M_SHUTDOWN>());
-  Process::wait(master);
+  EXPECT_CALL(failingSched, statusUpdate(&failingDriver, _))
+    .Times(0);
 
-  ProcessClock::resume();
-}
+  trigger statusUpdateMsg;
 
+  EXPECT_MSG(filter, Eq(S2M_FT_STATUS_UPDATE), _, Ne(master))
+    .WillOnce(DoAll(Trigger(&statusUpdateMsg), Return(true)))
+    .RetiresOnSaturation();
 
-// An executor used in the framework message test that just sends a reply
-// to each message received and logs the last message.
-class FrameworkMessageExecutor : public Executor
-{
-public:
-  bool messageReceived;
-  string messageData;
-  SlaveID mySlaveId;
+  failingDriver.start();
 
-  FrameworkMessageExecutor(): messageReceived(false) {}
+  WAIT_UNTIL(resourceOfferCall);
 
-  virtual ~FrameworkMessageExecutor() {}
+  ASSERT_GE(1, offers.size());
 
-  virtual void init(ExecutorDriver* d, const ExecutorArgs& args) {
-    mySlaveId = args.slaveId;
-  }
+  vector<TaskDescription> tasks;
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", offers[0].params, ""));
 
-  virtual void frameworkMessage(ExecutorDriver* d, const FrameworkMessage& m) {
-    LOG(INFO) << "FrameworkMessageExecutor got a message";
-    messageReceived = true;
-    messageData = m.data;
-    // Send a message back to the scheduler, which will cause it to exit
-    FrameworkMessage reply(mySlaveId, 0, "reply");
-    d->sendFrameworkMessage(reply);
-    LOG(INFO) << "Sent the reply back";
-  }
-};
+  failingDriver.replyToOffer(offerId, tasks, map<string, string>());
 
+  WAIT_UNTIL(statusUpdateMsg);
 
-// A scheduler used in the framework message test that launches a task, waits
-// for it to start, sends it a framework message, and waits for a reply.
-class FrameworkMessageScheduler : public Scheduler
-{
-public:
-  FrameworkID fid;
-  string errorMessage;
-  bool messageReceived;
-  string messageData;
-  SlaveID slaveIdOfTask;
+  // Now launch the second (i.e., failover) scheduler using the
+  // framework id recorded from the first scheduler and wait until it
+  // registers, at which point advance time enough for the reliable
+  // timeout to kick in and another status update message is sent.
 
-  FrameworkMessageScheduler() {}
+  MockScheduler failoverSched;
+  MesosSchedulerDriver failoverDriver(&failoverSched, master, frameworkId);
 
-  virtual ~FrameworkMessageScheduler() {}
+  trigger registeredCall, statusUpdateCall;
 
-  virtual ExecutorInfo getExecutorInfo(SchedulerDriver*) {
-    return ExecutorInfo("noexecutor", "");
-  }
+  EXPECT_CALL(failoverSched, getFrameworkName(&failoverDriver))
+    .WillOnce(Return(""));
 
-  virtual void registered(SchedulerDriver*, FrameworkID fid) {
-    LOG(INFO) << "FrameworkMessageScheduler registered";
-    this->fid = fid;
-  }
+  EXPECT_CALL(failoverSched, getExecutorInfo(&failoverDriver))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
 
-  virtual void resourceOffer(SchedulerDriver* d,
-                             OfferID id,
-                             const vector<SlaveOffer>& offers) {
-    LOG(INFO) << "FrameworkMessageScheduler got a slot offer";
-    vector<TaskDescription> tasks;
-    ASSERT_TRUE(offers.size() == 1);
-    const SlaveOffer &offer = offers[0];
-    TaskDescription desc(0, offer.slaveId, "", offer.params, "");
-    tasks.push_back(desc);
-    slaveIdOfTask = offer.slaveId;
-    d->replyToOffer(id, tasks, map<string, string>());
-  }
+  EXPECT_CALL(failoverSched, registered(&failoverDriver, frameworkId))
+    .WillOnce(Trigger(&registeredCall));
 
+  EXPECT_CALL(failoverSched, statusUpdate(&failoverDriver, _))
+    .WillOnce(Trigger(&statusUpdateCall));
 
-  virtual void statusUpdate(SchedulerDriver* d, const TaskStatus& status) {
-    EXPECT_EQ(TASK_RUNNING, status.state);
-    LOG(INFO) << "Task is running; sending it a framework message";
-    FrameworkMessage message(slaveIdOfTask, 0, "hello");
-    d->sendFrameworkMessage(message);
-  }
+  failoverDriver.start();
 
+  WAIT_UNTIL(registeredCall);
 
-  virtual void frameworkMessage(SchedulerDriver* d, const FrameworkMessage& m) {
-    LOG(INFO) << "FrameworkMessageScheduler got a message";
-    messageReceived = true;
-    messageData = m.data;
-    // Stop our driver because the test is complete
-    d->stop();
-  }
+  ProcessClock::advance(RELIABLE_TIMEOUT);
 
-  virtual void error(SchedulerDriver* d,
-                     int code,
-                     const std::string& message) {
-    errorMessage = message;
-    d->stop();
-  }
-};
+  WAIT_UNTIL(statusUpdateCall);
+
+  failingDriver.stop();
+  failoverDriver.stop();
+
+  failingDriver.join();
+  failoverDriver.join();
+
+// <<<<<<<<<<<<<<
+//   local::shutdown();
+// >>>>>>>>>>>>>>
+  MesosProcess::post(slave, pack<S2S_SHUTDOWN>());
+  Process::wait(slave);
+
+  MesosProcess::post(master, pack<M2M_SHUTDOWN>());
+  Process::wait(master);
+// >>>>>>>>>>>>>>
+  Process::filter(NULL);
+
+  ProcessClock::resume();
+}
 
 
-// Tests that framework messages are sent correctly both in both the
-// scheduler->executor direction and the executor->scheduler direction.
 TEST(MasterTest, FrameworkMessages)
 {
   ASSERT_TRUE(GTEST_IS_THREADSAFE);
 
-  Master m;
-  PID master = Process::spawn(&m);
+  MockExecutor exec;
+
+  ExecutorDriver *execDriver;
+  ExecutorArgs args;
+  FrameworkMessage execMessage;
+
+  trigger execFrameworkMessageCall;
+
+  EXPECT_CALL(exec, init(_, _))
+    .WillOnce(DoAll(SaveArg<0>(&execDriver), SaveArg<1>(&args)));
+
+  EXPECT_CALL(exec, launchTask(_, _))
+    .Times(1);
+
+  EXPECT_CALL(exec, frameworkMessage(_, _))
+    .WillOnce(DoAll(SaveArg<1>(&execMessage),
+                    Trigger(&execFrameworkMessageCall)));
+
+  EXPECT_CALL(exec, shutdown(_))
+    .Times(1);
 
-  FrameworkMessageExecutor exec;
   LocalIsolationModule isolationModule(&exec);
 
+  Master m;
+  PID master = Process::spawn(&m);
+
   Slave s(Resources(2, 1 * Gigabyte), true, &isolationModule);
   PID slave = Process::spawn(&s);
 
   BasicMasterDetector detector(master, slave, true);
 
-  FrameworkMessageScheduler sched;
-  MesosSchedulerDriver driver(&sched, master);
+  // Launch the first (i.e., failing) scheduler and wait until the
+  // first status update message is sent to it (drop the message).
+
+  MockScheduler sched;
+  MesosSchedulerDriver schedDriver(&sched, master);
+
+  OfferID offerId;
+  vector<SlaveOffer> offers;
+  TaskStatus status;
+  FrameworkMessage schedMessage;
+
+  trigger resourceOfferCall, statusUpdateCall, schedFrameworkMessageCall;
+
+  EXPECT_CALL(sched, getFrameworkName(&schedDriver))
+    .WillOnce(Return(""));
+
+  EXPECT_CALL(sched, getExecutorInfo(&schedDriver))
+    .WillOnce(Return(ExecutorInfo("noexecutor", "")));
+
+  EXPECT_CALL(sched, registered(&schedDriver, _))
+    .Times(1);
+
+  EXPECT_CALL(sched, resourceOffer(&schedDriver, _, _))
+    .WillOnce(DoAll(SaveArg<1>(&offerId), SaveArg<2>(&offers),
+                    Trigger(&resourceOfferCall)));
+
+  EXPECT_CALL(sched, statusUpdate(&schedDriver, _))
+    .WillOnce(DoAll(SaveArg<1>(&status), Trigger(&statusUpdateCall)));
+
+  EXPECT_CALL(sched, frameworkMessage(&schedDriver, _))
+    .WillOnce(DoAll(SaveArg<1>(&schedMessage),
+                    Trigger(&schedFrameworkMessageCall)));
+
+  schedDriver.start();
+
+  WAIT_UNTIL(resourceOfferCall);
+
+  ASSERT_GE(1, offers.size());
+
+  vector<TaskDescription> tasks;
+  tasks.push_back(TaskDescription(1, offers[0].slaveId, "", offers[0].params, ""));
+
+  schedDriver.replyToOffer(offerId, tasks, map<string, string>());
+
+  WAIT_UNTIL(statusUpdateCall);
+
+  EXPECT_EQ(TASK_RUNNING, status.state);
+
+  FrameworkMessage hello(offers[0].slaveId, 1, "hello");
+  schedDriver.sendFrameworkMessage(hello);
+
+  WAIT_UNTIL(execFrameworkMessageCall);
+
+  EXPECT_EQ("hello", execMessage.data);
+
+  FrameworkMessage reply(args.slaveId, 1, "reply");
+  execDriver->sendFrameworkMessage(reply);
+
+  WAIT_UNTIL(schedFrameworkMessageCall);
 
-  driver.run();
+  EXPECT_EQ("reply", schedMessage.data);
 
-  EXPECT_EQ("", sched.errorMessage);
-  EXPECT_TRUE(exec.messageReceived);
-  EXPECT_EQ("hello", exec.messageData);
-  EXPECT_TRUE(sched.messageReceived);
-  EXPECT_EQ("reply", sched.messageData);
+  schedDriver.stop();
+  schedDriver.join();
 
   MesosProcess::post(slave, pack<S2S_SHUTDOWN>());
   Process::wait(slave);

Added: incubator/mesos/trunk/third_party/gmock-1.5.0/CHANGES
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/third_party/gmock-1.5.0/CHANGES?rev=1132109&view=auto
==============================================================================
--- incubator/mesos/trunk/third_party/gmock-1.5.0/CHANGES (added)
+++ incubator/mesos/trunk/third_party/gmock-1.5.0/CHANGES Sun Jun  5 08:45:22 2011
@@ -0,0 +1,66 @@
+Changes for 1.5.0:
+
+ * New feature: Google Mock can be safely used in multi-threaded tests
+   on platforms having pthreads.
+ * New feature: function for printing a value of arbitrary type.
+ * New feature: function ExplainMatchResult() for easy definition of
+   composite matchers.
+ * The new matcher API lets user-defined matchers generate custom
+   explanations more directly and efficiently.
+ * Better failure messages all around.
+ * NotNull() and IsNull() now work with smart pointers.
+ * Field() and Property() now work when the matcher argument is a pointer
+   passed by reference.
+ * Regular expression matchers on all platforms.
+ * Added GCC 4.0 support for Google Mock Doctor.
+ * Added gmock_all_test.cc for compiling most Google Mock tests
+   in a single file.
+ * Significantly cleaned up compiler warnings.
+ * Bug fixes, better test coverage, and implementation clean-ups.
+
+ Potentially breaking changes:
+
+ * Custom matchers defined using MatcherInterface or MakePolymorphicMatcher()
+   need to be updated after upgrading to Google Mock 1.5.0; matchers defined
+   using MATCHER or MATCHER_P* aren't affected.
+ * Dropped support for 'make install'.
+
+Changes for 1.4.0 (we skipped 1.2.* and 1.3.* to match the version of
+Google Test):
+
+ * Works in more environments: Symbian and minGW, Visual C++ 7.1.
+ * Lighter weight: comes with our own implementation of TR1 tuple (no
+   more dependency on Boost!).
+ * New feature: --gmock_catch_leaked_mocks for detecting leaked mocks.
+ * New feature: ACTION_TEMPLATE for defining templatized actions.
+ * New feature: the .After() clause for specifying expectation order.
+ * New feature: the .With() clause for for specifying inter-argument
+   constraints.
+ * New feature: actions ReturnArg<k>(), ReturnNew<T>(...), and
+   DeleteArg<k>().
+ * New feature: matchers Key(), Pair(), Args<...>(), AllArgs(), IsNull(),
+   and Contains().
+ * New feature: utility class MockFunction<F>, useful for checkpoints, etc.
+ * New feature: functions Value(x, m) and SafeMatcherCast<T>(m).
+ * New feature: copying a mock object is rejected at compile time.
+ * New feature: a script for fusing all Google Mock and Google Test
+   source files for easy deployment.
+ * Improved the Google Mock doctor to diagnose more diseases.
+ * Improved the Google Mock generator script.
+ * Compatibility fixes for Mac OS X and gcc.
+ * Bug fixes and implementation clean-ups.
+
+Changes for 1.1.0:
+
+ * New feature: ability to use Google Mock with any testing framework.
+ * New feature: macros for easily defining new matchers
+ * New feature: macros for easily defining new actions.
+ * New feature: more container matchers.
+ * New feature: actions for accessing function arguments and throwing
+   exceptions.
+ * Improved the Google Mock doctor script for diagnosing compiler errors.
+ * Bug fixes and implementation clean-ups.
+
+Changes for 1.0.0:
+
+ * Initial Open Source release of Google Mock

Added: incubator/mesos/trunk/third_party/gmock-1.5.0/CONTRIBUTORS
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/third_party/gmock-1.5.0/CONTRIBUTORS?rev=1132109&view=auto
==============================================================================
--- incubator/mesos/trunk/third_party/gmock-1.5.0/CONTRIBUTORS (added)
+++ incubator/mesos/trunk/third_party/gmock-1.5.0/CONTRIBUTORS Sun Jun  5 08:45:22 2011
@@ -0,0 +1,40 @@
+# This file contains a list of people who've made non-trivial
+# contribution to the Google C++ Mocking Framework project.  People
+# who commit code to the project are encouraged to add their names
+# here.  Please keep the list sorted by first names.
+
+Benoit Sigoure <ts...@google.com>
+Bogdan Piloca <bo...@google.com>
+Chandler Carruth <ch...@google.com>
+Dave MacLachlan <dm...@gmail.com>
+David Anderson <da...@google.com>
+Dean Sturtevant
+Gene Volovich <gv...@cite.com>
+Hal Burch <gm...@hburch.com>
+Jeffrey Yasskin <jy...@google.com>
+Jim Keller <ji...@google.com>
+Joe Walnes <jo...@truemesh.com>
+Jon Wray <jw...@google.com>
+Keir Mierle <mi...@gmail.com>
+Keith Ray <ke...@gmail.com>
+Kostya Serebryany <kc...@google.com>
+Lev Makhlis
+Manuel Klimek <kl...@google.com>
+Mario Tanev <ra...@google.com>
+Mark Paskin
+Markus Heule <ma...@gmail.com>
+Matthew Simmons <si...@acm.org>
+Mike Bland <mb...@google.com>
+Neal Norwitz <nn...@gmail.com>
+Nermin Ozkiranartli <ne...@google.com>
+Owen Carlsen <oc...@google.com>
+Paneendra Ba <pa...@google.com>
+Paul Menage <me...@google.com>
+Piotr Kaminski <pi...@google.com>
+Russ Rufer <ru...@pentad.com>
+Sverre Sundsdal <su...@gmail.com>
+Takeshi Yoshino <ty...@google.com>
+Vadim Berman <va...@google.com>
+Vlad Losev <vl...@google.com>
+Wolfgang Klier <wk...@google.com>
+Zhanyong Wan <wa...@google.com>

Copied: incubator/mesos/trunk/third_party/gmock-1.5.0/COPYING (from r1132108, incubator/mesos/trunk/third_party/gtest-1.5.0/COPYING)
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/third_party/gmock-1.5.0/COPYING?p2=incubator/mesos/trunk/third_party/gmock-1.5.0/COPYING&p1=incubator/mesos/trunk/third_party/gtest-1.5.0/COPYING&r1=1132108&r2=1132109&rev=1132109&view=diff
==============================================================================
    (empty)

Added: incubator/mesos/trunk/third_party/gmock-1.5.0/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/third_party/gmock-1.5.0/Makefile.am?rev=1132109&view=auto
==============================================================================
--- incubator/mesos/trunk/third_party/gmock-1.5.0/Makefile.am (added)
+++ incubator/mesos/trunk/third_party/gmock-1.5.0/Makefile.am Sun Jun  5 08:45:22 2011
@@ -0,0 +1,192 @@
+# Automake file
+
+# Nonstandard package files for distribution.
+EXTRA_DIST =
+
+# We may need to build our internally packaged gtest. If so, it will be
+# included in the 'subdirs' variable.
+SUBDIRS = $(subdirs)
+
+# Scripts and utilities to be installed by 'make install'.
+dist_bin_SCRIPTS = scripts/gmock_doctor.py
+bin_SCRIPTS = scripts/gmock-config
+
+# This is generated by the configure script, so clean it for distribution.
+DISTCLEANFILES = scripts/gmock-config
+
+# We define the global AM_CPPFLAGS as everything we compile includes from these
+# directories.
+AM_CPPFLAGS = $(GTEST_CPPFLAGS) -I$(srcdir)/include
+
+# Modifies compiler and linker flags for pthreads compatibility.
+if HAVE_PTHREADS
+  AM_CXXFLAGS = @PTHREAD_CFLAGS@ -DGTEST_HAS_PTHREAD=1
+  AM_LIBS = @PTHREAD_LIBS@
+endif
+
+# Build rules for libraries.
+lib_LTLIBRARIES = lib/libgmock.la lib/libgmock_main.la
+
+lib_libgmock_la_SOURCES = src/gmock-all.cc
+
+pkginclude_HEADERS = include/gmock/gmock.h \
+                     include/gmock/gmock-actions.h \
+                     include/gmock/gmock-cardinalities.h \
+                     include/gmock/gmock-generated-actions.h \
+                     include/gmock/gmock-generated-function-mockers.h \
+                     include/gmock/gmock-generated-matchers.h \
+                     include/gmock/gmock-generated-nice-strict.h \
+                     include/gmock/gmock-matchers.h \
+                     include/gmock/gmock-more-actions.h \
+                     include/gmock/gmock-printers.h \
+                     include/gmock/gmock-spec-builders.h
+
+pkginclude_internaldir = $(pkgincludedir)/internal
+pkginclude_internal_HEADERS = \
+  include/gmock/internal/gmock-generated-internal-utils.h \
+  include/gmock/internal/gmock-internal-utils.h \
+  include/gmock/internal/gmock-port.h
+
+lib_libgmock_main_la_SOURCES = src/gmock_main.cc
+lib_libgmock_main_la_LIBADD = lib/libgmock.la
+
+# Build rules for tests. Automake's naming for some of these variables isn't
+# terribly obvious, so this is a brief reference:
+#
+# TESTS -- Programs run automatically by "make check"
+# check_PROGRAMS -- Programs built by "make check" but not necessarily run
+
+TESTS=
+check_PROGRAMS=
+AM_LDFLAGS = $(GTEST_LDFLAGS)
+
+# This exercises all major components of Google Mock.  It also
+# verifies that libgmock works.
+TESTS += test/gmock-spec-builders_test
+check_PROGRAMS += test/gmock-spec-builders_test
+test_gmock_spec_builders_test_SOURCES = test/gmock-spec-builders_test.cc
+test_gmock_spec_builders_test_LDADD = $(GTEST_LIBS) lib/libgmock.la
+
+# This tests using Google Mock in multiple translation units.  It also
+# verifies that libgmock_main works.
+TESTS += test/gmock_link_test
+check_PROGRAMS += test/gmock_link_test
+test_gmock_link_test_SOURCES = test/gmock_link_test.cc \
+                               test/gmock_link2_test.cc \
+                               test/gmock_link_test.h
+test_gmock_link_test_LDADD = $(GTEST_LIBS) lib/libgmock_main.la
+
+# Tests that fused gmock files compile and work.
+TESTS += test/gmock_fused_test
+check_PROGRAMS += test/gmock_fused_test
+test_gmock_fused_test_SOURCES = fused-src/gmock-gtest-all.cc \
+                                fused-src/gmock_main.cc \
+                                fused-src/gmock/gmock.h \
+                                fused-src/gtest/gtest.h \
+                                test/gmock_test.cc
+test_gmock_fused_test_CPPFLAGS = -I"$(srcdir)/fused-src"
+
+# Google Mock source files that we don't compile directly.
+GMOCK_SOURCE_INGLUDES = \
+  src/gmock.cc \
+  src/gmock-cardinalities.cc \
+  src/gmock-internal-utils.cc \
+  src/gmock-matchers.cc \
+  src/gmock-printers.cc \
+  src/gmock-spec-builders.cc
+
+EXTRA_DIST += $(GMOCK_SOURCE_INGLUDES)
+
+# C++ tests that we don't compile using autotools.
+EXTRA_DIST += \
+  test/gmock_all_test.cc \
+  test/gmock-actions_test.cc \
+  test/gmock-cardinalities_test.cc \
+  test/gmock-generated-actions_test.cc \
+  test/gmock-generated-function-mockers_test.cc \
+  test/gmock-generated-internal-utils_test.cc \
+  test/gmock-generated-matchers_test.cc \
+  test/gmock-internal-utils_test.cc \
+  test/gmock-matchers_test.cc \
+  test/gmock-more-actions_test.cc \
+  test/gmock-nice-strict_test.cc \
+  test/gmock-port_test.cc \
+  test/gmock-printers_test.cc
+
+# Python tests, which we don't run using autotools.
+EXTRA_DIST += \
+  test/gmock_test_utils.py \
+  test/gmock_leak_test_.cc \
+  test/gmock_leak_test.py \
+  test/gmock_output_test_.cc \
+  test/gmock_output_test.py \
+  test/gmock_output_test_golden.txt
+
+# Nonstandard package files for distribution.
+EXTRA_DIST += \
+    CHANGES \
+    CONTRIBUTORS \
+    make/Makefile
+
+# Pump scripts for generating Google Mock headers.
+# TODO(chandlerc@google.com): automate the generation of *.h from *.h.pump.
+EXTRA_DIST += include/gmock/gmock-generated-actions.h.pump \
+              include/gmock/gmock-generated-function-mockers.h.pump \
+              include/gmock/gmock-generated-matchers.h.pump \
+              include/gmock/gmock-generated-nice-strict.h.pump \
+              include/gmock/internal/gmock-generated-internal-utils.h.pump
+
+# Script for fusing Google Mock and Google Test source files.
+EXTRA_DIST += \
+    scripts/fuse_gmock_files.py \
+    scripts/test/Makefile
+
+# The Google Mock Generator tool from the cppclean project.
+EXTRA_DIST += \
+    scripts/generator/COPYING \
+    scripts/generator/README \
+    scripts/generator/README.cppclean \
+    scripts/generator/cpp/__init__.py \
+    scripts/generator/cpp/ast.py \
+    scripts/generator/cpp/gmock_class.py \
+    scripts/generator/cpp/keywords.py \
+    scripts/generator/cpp/tokenize.py \
+    scripts/generator/cpp/utils.py \
+    scripts/generator/gmock_gen.py
+
+# Microsoft Visual Studio 2005 projects.
+EXTRA_DIST += \
+    msvc/gmock.sln \
+    msvc/gmock.vcproj \
+    msvc/gmock_config.vsprops \
+    msvc/gmock_link_test.vcproj \
+    msvc/gmock_main.vcproj \
+    msvc/gmock-spec-builders_test.vcproj \
+    msvc/gmock_test.vcproj
+
+# gmock_test.cc does not really depend on files generated by the
+# fused-gmock-internal rule.  However, gmock_test.o does, and it is
+# important to include test/gmock_test.cc as part of this rule in order to
+# prevent compiling gmock_test.o until all dependent files have been
+# generated.
+$(test_gmock_fused_test_SOURCES): fused-gmock-internal
+
+# TODO(vladl@google.com): Find a way to add Google Tests's sources here.
+fused-gmock-internal: $(pkginclude_HEADERS) $(pkginclude_internal_HEADERS) \
+                      $(lib_libgmock_la_SOURCES) $(GMOCK_SOURCE_INGLUDES) \
+                      $(lib_libgmock_main_la_SOURCES) \
+                      scripts/fuse_gmock_files.py
+	mkdir -p "$(srcdir)/fused-src"
+	chmod -R u+w "$(srcdir)/fused-src"
+	rm -f "$(srcdir)/fused-src/gtest/gtest.h"
+	rm -f "$(srcdir)/fused-src/gmock/gmock.h"
+	rm -f "$(srcdir)/fused-src/gmock-gtest-all.cc"
+	"$(srcdir)/scripts/fuse_gmock_files.py" "$(srcdir)/fused-src"
+	cp -f "$(srcdir)/src/gmock_main.cc" "$(srcdir)/fused-src"
+
+maintainer-clean-local:
+	rm -rf "$(srcdir)/fused-src"
+
+# Death tests may produce core dumps in the build directory. In case
+# this happens, clean them to keep distcleancheck happy.
+CLEANFILES = core