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 2012/07/11 00:03:44 UTC

svn commit: r1359927 - in /incubator/mesos/trunk/src: Makefile.am common/type_utils.hpp tests/allocator_tests.cpp tests/resource_offers_tests.cpp tests/utils.hpp

Author: benh
Date: Tue Jul 10 22:03:44 2012
New Revision: 1359927

URL: http://svn.apache.org/viewvc?rev=1359927&view=rev
Log:
Added allocator tests (contributed by Thomas Marshall,
https://reviews.apache.org/r/5532).

Added:
    incubator/mesos/trunk/src/tests/allocator_tests.cpp
Modified:
    incubator/mesos/trunk/src/Makefile.am
    incubator/mesos/trunk/src/common/type_utils.hpp
    incubator/mesos/trunk/src/tests/resource_offers_tests.cpp
    incubator/mesos/trunk/src/tests/utils.hpp

Modified: incubator/mesos/trunk/src/Makefile.am
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/Makefile.am?rev=1359927&r1=1359926&r2=1359927&view=diff
==============================================================================
--- incubator/mesos/trunk/src/Makefile.am (original)
+++ incubator/mesos/trunk/src/Makefile.am Tue Jul 10 22:03:44 2012
@@ -751,7 +751,8 @@ mesos_tests_SOURCES = tests/main.cpp tes
 	              tests/killtree_tests.cpp				\
 	              tests/exception_tests.cpp				\
 	              tests/attributes_tests.cpp			\
-	              tests/master_detector_tests.cpp
+	              tests/master_detector_tests.cpp			\
+	              tests/allocator_tests.cpp
 
 mesos_tests_CPPFLAGS = $(MESOS_CPPFLAGS)
 mesos_tests_CPPFLAGS += -DSOURCE_DIR=\"$(abs_top_srcdir)\"

Modified: incubator/mesos/trunk/src/common/type_utils.hpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/common/type_utils.hpp?rev=1359927&r1=1359926&r2=1359927&view=diff
==============================================================================
--- incubator/mesos/trunk/src/common/type_utils.hpp (original)
+++ incubator/mesos/trunk/src/common/type_utils.hpp Tue Jul 10 22:03:44 2012
@@ -96,6 +96,12 @@ inline bool operator == (const Framework
 }
 
 
+inline bool operator == (const FrameworkInfo& left, const FrameworkInfo& right)
+{
+  return (left.name() == right.name()) && (left.user() == right.user());
+}
+
+
 inline bool operator == (const OfferID& left, const OfferID& right)
 {
   return left.value() == right.value();

Added: incubator/mesos/trunk/src/tests/allocator_tests.cpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/tests/allocator_tests.cpp?rev=1359927&view=auto
==============================================================================
--- incubator/mesos/trunk/src/tests/allocator_tests.cpp (added)
+++ incubator/mesos/trunk/src/tests/allocator_tests.cpp Tue Jul 10 22:03:44 2012
@@ -0,0 +1,676 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gmock/gmock.h>
+
+#include <mesos/scheduler.hpp>
+
+#include "configurator/configuration.hpp"
+
+#include "detector/detector.hpp"
+
+#include "master/allocator.hpp"
+#include "master/dominant_share_allocator.hpp"
+#include "master/master.hpp"
+
+#include "slave/process_based_isolation_module.hpp"
+
+#include "tests/base_zookeeper_test.hpp"
+#include "tests/utils.hpp"
+
+using namespace mesos;
+using namespace mesos::internal;
+using namespace mesos::internal::test;
+
+using mesos::internal::master::Allocator;
+using mesos::internal::master::DominantShareAllocator;
+using mesos::internal::master::Master;
+
+using mesos::internal::slave::ProcessBasedIsolationModule;
+using mesos::internal::slave::Slave;
+
+using process::PID;
+
+using std::string;
+using std::vector;
+
+using testing::AnyNumber;
+using testing::ByRef;
+using testing::DoDefault;
+using testing::Eq;
+using testing::_;
+using testing::Return;
+using testing::SaveArg;
+
+void checkResources(vector<Offer> offers, int cpus, int mem)
+{
+  EXPECT_EQ(offers.size(), 1);
+
+  EXPECT_EQ(offers[0].resources().size(), 2);
+
+  foreach (const Resource& resource, offers[0].resources()) {
+    if (resource.name() == "cpus") {
+      EXPECT_EQ(resource.scalar().value(), cpus);
+    } else if (resource.name() == "mem") {
+      EXPECT_EQ(resource.scalar().value(), mem);
+    }
+  }
+}
+
+
+TEST(AllocatorTest, DominantShareAllocator)
+{
+  FrameworkInfo frameworkInfo1;
+  frameworkInfo1.set_name("framework1");
+  frameworkInfo1.set_user("user1");
+  FrameworkID frameworkId1;
+
+  FrameworkInfo frameworkInfo2;
+  frameworkInfo2.set_name("framework2");
+  frameworkInfo2.set_user("user2");
+  FrameworkID frameworkId2;
+
+  FrameworkInfo frameworkInfo3;
+  frameworkInfo3.set_name("framework3");
+  frameworkInfo3.set_user("user1");
+  FrameworkID frameworkId3;
+
+  MockAllocator<DominantShareAllocator> a;
+
+  EXPECT_CALL(a, initialize(_));
+
+  EXPECT_CALL(a, frameworkAdded(_, Eq(frameworkInfo1)))
+    .WillOnce(DoAll(InvokeFrameworkAdded(&a),
+		    SaveArg<0>(&frameworkId1)));
+
+  trigger framework2Added;
+  EXPECT_CALL(a, frameworkAdded(_, Eq(frameworkInfo2)))
+    .WillOnce(DoAll(InvokeFrameworkAdded(&a),
+		    SaveArg<0>(&frameworkId2),
+		    Trigger(&framework2Added)));
+
+  trigger framework3Added;
+  EXPECT_CALL(a, frameworkAdded(_, Eq(frameworkInfo3)))
+    .WillOnce(DoAll(InvokeFrameworkAdded(&a),
+		    SaveArg<0>(&frameworkId3),
+		    Trigger(&framework3Added)));
+
+  EXPECT_CALL(a, frameworkDeactivated(_))
+    .Times(3);
+
+  EXPECT_CALL(a, frameworkRemoved(Eq(ByRef(frameworkId1))));
+
+  EXPECT_CALL(a, frameworkRemoved(Eq(ByRef(frameworkId2))));
+
+  trigger lastFrameworkRemoved;
+  EXPECT_CALL(a, frameworkRemoved(Eq(ByRef(frameworkId3))))
+    .WillOnce(Trigger(&lastFrameworkRemoved));
+
+  SlaveID slaveId4;
+  EXPECT_CALL(a, slaveAdded(_, _, _))
+    .WillOnce(DoDefault())
+    .WillOnce(DoDefault())
+    .WillOnce(DoDefault())
+    .WillOnce(DoAll(InvokeSlaveAdded(&a),
+		    SaveArg<0>(&slaveId4)));
+
+  EXPECT_CALL(a, slaveRemoved(_))
+    .Times(3);
+
+  trigger lastSlaveRemoved;
+  EXPECT_CALL(a, slaveRemoved(Eq(ByRef(slaveId4))))
+    .WillOnce(Trigger(&lastSlaveRemoved));
+
+  EXPECT_CALL(a, resourcesRecovered(_, _, _))
+    .WillRepeatedly(DoDefault());
+
+  Master m(&a);
+  PID<Master> master = process::spawn(m);
+
+  ProcessBasedIsolationModule isolationModule;
+  Resources resources = Resources::parse("cpus:2;mem:1024");
+  Slave s(resources, true, &isolationModule);
+  PID<Slave> slave1 = process::spawn(s);
+
+  BasicMasterDetector detector(master, slave1, true);
+
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(&sched1, frameworkInfo1, master);
+
+  EXPECT_CALL(sched1, registered(_, _, _));
+
+  vector<Offer> offers1;
+  trigger resourceOfferTrigger;
+  EXPECT_CALL(sched1, resourceOffers(_, _))
+    .WillOnce(DoAll(SaveArg<1>(&offers1),
+		    Trigger(&resourceOfferTrigger)))
+    .WillRepeatedly(Return());
+
+  driver1.start();
+
+  WAIT_UNTIL(resourceOfferTrigger);
+
+  checkResources(offers1, 2, 1024);
+
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(&sched2, frameworkInfo2, master);
+
+  EXPECT_CALL(sched2, registered(_, _, _));
+
+  vector<Offer> offers2, offers3;
+  trigger resourceOfferTrigger2, resourceOfferTrigger3;
+  EXPECT_CALL(sched2, resourceOffers(_, _))
+    .WillOnce(DoAll(SaveArg<1>(&offers2),
+		    Trigger(&resourceOfferTrigger2)))
+    .WillOnce(DoAll(SaveArg<1>(&offers3),
+		    Trigger(&resourceOfferTrigger3)))
+    .WillRepeatedly(Return());
+
+  driver2.start();
+
+  WAIT_UNTIL(framework2Added);
+
+  Resources resources2 = Resources::parse("cpus:1;mem:512");
+  Slave s2(resources2, true, &isolationModule);
+  PID<Slave> slave2 = process::spawn(s2);
+
+  BasicMasterDetector detector2(master, slave2, true);
+
+  WAIT_UNTIL(resourceOfferTrigger2);
+
+  checkResources(offers2, 1, 512);
+
+  Resources resources3 = Resources::parse("cpus:3;mem:2048");
+  Slave s3(resources3, true, &isolationModule);
+  PID<Slave> slave3 = process::spawn(s3);
+  BasicMasterDetector detector3(master, slave3, true);
+
+  WAIT_UNTIL(resourceOfferTrigger3);
+
+  checkResources(offers3, 3, 2048);
+
+  MockScheduler sched3;
+  MesosSchedulerDriver driver3(&sched3, frameworkInfo3, master);
+
+  EXPECT_CALL(sched3, registered(_, _, _));
+
+  vector<Offer> offers4;
+  trigger resourceOfferTrigger4;
+  EXPECT_CALL(sched3, resourceOffers(_, _))
+    .WillOnce(DoAll(SaveArg<1>(&offers4),
+		    Trigger(&resourceOfferTrigger4)));
+
+  driver3.start();
+
+  WAIT_UNTIL(framework3Added);
+
+  Resources resources4 = Resources::parse("cpus:4;mem:4096");
+  Slave s4(resources4, true, &isolationModule);
+  PID<Slave> slave4 = process::spawn(s4);
+  BasicMasterDetector detector4(master, slave4, true);
+
+  WAIT_UNTIL(resourceOfferTrigger4);
+
+  checkResources(offers4, 4, 4096);
+
+  driver3.stop();
+  driver2.stop();
+  driver1.stop();
+
+  WAIT_UNTIL(lastFrameworkRemoved);
+
+  process::terminate(slave1);
+  process::wait(slave1);
+
+  process::terminate(slave2);
+  process::wait(slave2);
+
+  process::terminate(slave3);
+  process::wait(slave3);
+
+  process::terminate(slave4);
+  process::wait(slave4);
+
+  WAIT_UNTIL(lastSlaveRemoved);
+
+  process::terminate(master);
+  process::wait(master);
+}
+
+
+template <typename T = Allocator>
+class AllocatorTest : public ::testing::Test
+{
+protected:
+  MockAllocator<T> allocator;
+};
+
+
+// Causes all TYPED_TEST(AllocatorTest, ...) to be run for
+// each of the specified Allocator classes.
+typedef ::testing::Types<DominantShareAllocator> AllocatorTypes;
+TYPED_TEST_CASE(AllocatorTest, AllocatorTypes);
+
+
+TYPED_TEST(AllocatorTest, MockAllocator)
+{
+  EXPECT_CALL(this->allocator, initialize(_));
+
+  EXPECT_CALL(this->allocator, frameworkAdded(_, _));
+
+  EXPECT_CALL(this->allocator, frameworkDeactivated(_));
+
+  trigger frameworkRemovedTrigger;
+  EXPECT_CALL(this->allocator, frameworkRemoved(_))
+    .WillOnce(Trigger(&frameworkRemovedTrigger));
+
+  EXPECT_CALL(this->allocator, slaveAdded(_, _, _));
+
+  trigger slaveRemovedTrigger;
+  EXPECT_CALL(this->allocator, slaveRemoved(_))
+    .WillOnce(Trigger(&slaveRemovedTrigger));
+
+  EXPECT_CALL(this->allocator, resourcesRecovered(_, _, _));
+
+  Master m(&this->allocator);
+  PID<Master> master = process::spawn(&m);
+
+  ProcessBasedIsolationModule isolationModule;
+  Resources resources = Resources::parse("cpus:2;mem:1024");
+
+  Slave s(resources, true, &isolationModule);
+  PID<Slave> slave = process::spawn(&s);
+
+  BasicMasterDetector detector(master, slave, true);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(&sched, DEFAULT_FRAMEWORK_INFO, master);
+
+  trigger resourceOffers;
+
+  vector<Offer> offers;
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillOnce(DoAll(SaveArg<1>(&offers),
+		    Trigger(&resourceOffers)));
+
+  driver.start();
+
+  WAIT_UNTIL(resourceOffers);
+
+  checkResources(offers, 2, 1024);
+
+  driver.stop();
+
+  WAIT_UNTIL(frameworkRemovedTrigger);
+
+  process::terminate(slave);
+  process::wait(slave);
+
+  WAIT_UNTIL(slaveRemovedTrigger);
+
+  process::terminate(master);
+  process::wait(master);
+}
+
+
+TYPED_TEST(AllocatorTest, ResourcesUnused)
+{
+  EXPECT_CALL(this->allocator, initialize(_));
+
+  EXPECT_CALL(this->allocator, frameworkAdded(_, _))
+    .Times(2);
+
+  EXPECT_CALL(this->allocator, frameworkDeactivated(_))
+    .Times(2);
+
+  trigger frameworkRemovedTrigger;
+  EXPECT_CALL(this->allocator, frameworkRemoved(_))
+    .WillOnce(DoDefault())
+    .WillOnce(Trigger(&frameworkRemovedTrigger));
+
+  EXPECT_CALL(this->allocator, slaveAdded(_, _, _));
+
+  trigger slaveRemovedTrigger;
+  EXPECT_CALL(this->allocator, slaveRemoved(_))
+    .WillOnce(Trigger(&slaveRemovedTrigger));
+
+  trigger resourcesUnusedTrigger;
+  EXPECT_CALL(this->allocator, resourcesUnused(_, _, _, _))
+    .WillOnce(DoAll(InvokeResourcesUnused(&this->allocator),
+		    Trigger(&resourcesUnusedTrigger),
+		    Return()))
+    .WillRepeatedly(DoDefault());
+
+  // Prevent any resources from being recovered by returning instead
+  // of allowing resourcesRecovered to be invoked, so the only ones
+  // that are reallocated are those returned by resourcesUnused.
+  EXPECT_CALL(this->allocator, resourcesRecovered(_, _, _))
+    .WillRepeatedly(Return());
+
+  Master m(&(this->allocator));
+  PID<Master> master = process::spawn(m);
+
+  ProcessBasedIsolationModule isolationModule;
+  Resources resources1 = Resources::parse("cpus:2;mem:1024");
+  Slave s(resources1, true, &isolationModule);
+  PID<Slave> slave1 = process::spawn(s);
+  BasicMasterDetector(master, slave1, true);
+
+  MockScheduler sched1;
+  MesosSchedulerDriver driver1(&sched1, DEFAULT_FRAMEWORK_INFO, master);
+
+  EXPECT_CALL(sched1, registered(_, _, _));
+
+  EXPECT_CALL(sched1, resourceOffers(_, _))
+    .WillOnce(LaunchTasks(1, 1, 512))
+    .WillRepeatedly(DeclineOffers());
+
+  driver1.start();
+
+  WAIT_UNTIL(resourcesUnusedTrigger);
+
+  FrameworkInfo info2;
+  info2.set_user("user2");
+  info2.set_name("framework2");
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(&sched2, info2, master);
+
+  EXPECT_CALL(sched2, registered(_, _, _));
+
+  vector<Offer> offers;
+  trigger offered;
+  EXPECT_CALL(sched2, resourceOffers(_, _))
+    .WillOnce(DoAll(SaveArg<1>(&offers),
+		    Trigger(&offered)))
+    .WillRepeatedly(Return());
+
+  driver2.start();
+
+  WAIT_UNTIL(offered);
+
+  checkResources(offers, 1, 512);
+
+  driver1.stop();
+  driver2.stop();
+
+  WAIT_UNTIL(frameworkRemovedTrigger);
+
+  process::terminate(slave1);
+  process::wait(slave1);
+
+  WAIT_UNTIL(slaveRemovedTrigger);
+
+  process::terminate(master);
+  process::wait(master);
+}
+
+
+// Tests the situation where a frameworkRemoved call is dispatched
+// while we're doing an allocation to that framework, so that
+// resourcesRecovered is called for an already removed framework.
+TYPED_TEST(AllocatorTest, OutOfOrderDispatch)
+{
+  FrameworkInfo frameworkInfo1;
+  frameworkInfo1.set_user("user1");
+  frameworkInfo1.set_name("framework1");
+  FrameworkID frameworkId1;
+
+  FrameworkInfo frameworkInfo2;
+  frameworkInfo2.set_user("user2");
+  frameworkInfo2.set_name("framework2");
+  FrameworkID frameworkId2;
+
+  EXPECT_CALL(this->allocator, initialize(_));
+
+  EXPECT_CALL(this->allocator, frameworkAdded(_, Eq(frameworkInfo1)))
+    .WillOnce(DoAll(InvokeFrameworkAdded(&this->allocator),
+		    SaveArg<0>(&frameworkId1)));
+
+  EXPECT_CALL(this->allocator, frameworkAdded(_, Eq(frameworkInfo2)))
+    .WillOnce(DoAll(InvokeFrameworkAdded(&this->allocator),
+		    SaveArg<0>(&frameworkId2)));
+
+  EXPECT_CALL(this->allocator, frameworkDeactivated(_))
+    .Times(2);
+
+  trigger frameworkRemoved, frameworkRemoved2;
+  EXPECT_CALL(this->allocator, frameworkRemoved(Eq(ByRef(frameworkId1))))
+    .WillOnce(DoAll(InvokeFrameworkRemoved(&this->allocator),
+		    Trigger(&frameworkRemoved),
+		    Return()));
+
+  EXPECT_CALL(this->allocator, frameworkRemoved(Eq(ByRef(frameworkId2))))
+    .WillOnce(Trigger(&frameworkRemoved2));
+
+  EXPECT_CALL(this->allocator, slaveAdded(_, _, _));
+
+  trigger slaveRemovedTrigger;
+  EXPECT_CALL(this->allocator, slaveRemoved(_))
+    .WillOnce(Trigger(&slaveRemovedTrigger));
+
+  FrameworkID frameworkId;
+  SlaveID slaveId;
+  Resources savedResources;
+  EXPECT_CALL(this->allocator, resourcesRecovered(_, _, _))
+    // "Catches" the resourcesRecovered call from the master, so
+    // that it doesn't get processed until we redispatch it after
+    // the frameworkRemoved trigger.
+    .WillOnce(DoAll(SaveArg<0>(&frameworkId),
+		    SaveArg<1>(&slaveId),
+		    SaveArg<2>(&savedResources)))
+    .WillRepeatedly(DoDefault());
+
+  Master m(&(this->allocator));
+  PID<Master> master = process::spawn(&m);
+
+  ProcessBasedIsolationModule isolationModule;
+
+  Resources resources = Resources::parse("cpus:2;mem:1024");
+
+  Slave s(resources, true, &isolationModule);
+  PID<Slave> slave = process::spawn(&s);
+
+  BasicMasterDetector detector(master, slave, true);
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(&sched, frameworkInfo1, master);
+
+  trigger offered;
+  vector<Offer> offers;
+
+  EXPECT_CALL(sched, registered(_, _, _));
+
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillOnce(DoAll(SaveArg<1>(&offers),
+		    Trigger(&offered)));
+
+  driver.start();
+
+  WAIT_UNTIL(offered);
+
+  checkResources(offers, 2, 1024);
+
+  driver.stop();
+  driver.join();
+
+  WAIT_UNTIL(frameworkRemoved);
+
+  // Re-dispatch the resourcesRecovered call which we "caught"
+  // earlier now that the framework has been removed, to test
+  // that recovering resources from a removed framework works.
+  dispatch(this->allocator, &Allocator::resourcesRecovered,
+	   frameworkId, slaveId, savedResources);
+
+  MockScheduler sched2;
+  MesosSchedulerDriver driver2(&sched2, frameworkInfo2, master);
+
+  EXPECT_CALL(sched2, registered(_, _, _));
+
+  trigger offered2;
+  vector<Offer> offers2;
+  EXPECT_CALL(sched2, resourceOffers(_, _))
+    .WillOnce(DoAll(SaveArg<1>(&offers2),
+		    Trigger(&offered2)));
+
+  driver2.start();
+
+  WAIT_UNTIL(offered2);
+
+  checkResources(offers2, 2, 1024);
+
+  driver2.stop();
+  driver2.join();
+
+  WAIT_UNTIL(frameworkRemoved2);
+
+  process::terminate(slave);
+  process::wait(slave);
+
+  WAIT_UNTIL(slaveRemovedTrigger);
+
+  process::terminate(master);
+  process::wait(master);
+}
+
+
+template <typename T = Allocator>
+class MasterFailoverAllocatorTest : public BaseZooKeeperTest
+{
+public:
+  static void SetUpTestCase() {
+    BaseZooKeeperTest::SetUpTestCase();
+  }
+
+protected:
+  T allocator1;
+  MockAllocator<T> allocator2;
+};
+
+
+// Runs TYPED_TEST(MasterFailoverAllocatorTest, ...) on all AllocatorTypes.
+TYPED_TEST_CASE(MasterFailoverAllocatorTest, AllocatorTypes);
+
+
+TYPED_TEST(MasterFailoverAllocatorTest, MasterFailover)
+{
+  trigger slaveAdded;
+  EXPECT_CALL(this->allocator2, initialize(_));
+
+  trigger frameworkAdded;
+  EXPECT_CALL(this->allocator2, frameworkAdded(_, _));
+
+  EXPECT_CALL(this->allocator2, frameworkDeactivated(_));
+
+  trigger frameworkRemovedTrigger;
+  EXPECT_CALL(this->allocator2, frameworkRemoved(_))
+    .WillOnce(Trigger(&frameworkRemovedTrigger));
+
+  EXPECT_CALL(this->allocator2, slaveAdded(_, _, _));
+
+  trigger slaveRemovedTrigger;
+  EXPECT_CALL(this->allocator2, slaveRemoved(_))
+    .WillOnce(Trigger(&slaveRemovedTrigger));
+
+  EXPECT_CALL(this->allocator2, resourcesRecovered(_, _, _))
+    .WillRepeatedly(DoDefault());
+
+  MockFilter filter;
+  process::filter(&filter);
+
+  EXPECT_MESSAGE(filter, Eq(ShutdownMessage().GetTypeName()), _, _)
+    .WillRepeatedly(Return(true));
+
+  Master m1(&(this->allocator1));
+  PID<Master> master1 = process::spawn(m1);
+
+  string zk = "zk://" + this->zks->connectString() + "/znode";
+  Try<MasterDetector*> detector =
+    MasterDetector::create(zk, master1, true, true);
+  CHECK(!detector.isError())
+    << "Failed to create a master detector: " << detector.error();
+
+  ProcessBasedIsolationModule isolationModule;
+
+  Resources resources = Resources::parse("cpus:2;mem:1024");
+
+  Slave s(resources, true, &isolationModule);
+  PID<Slave> slave = process::spawn(s);
+
+  Try<MasterDetector*> slave_detector =
+    MasterDetector::create(zk, slave, false, true);
+  CHECK(!slave_detector.isError())
+    << "Failed to create a master detector: " << slave_detector.error();
+
+  MockScheduler sched;
+  MesosSchedulerDriver driver(&sched, DEFAULT_FRAMEWORK_INFO, zk);
+
+  trigger resourceOffers, resourceOffers2;
+
+  vector<Offer> offers, offers2;
+
+  EXPECT_CALL(sched, registered(_, _, _))
+    .Times(2);
+
+  EXPECT_CALL(sched, resourceOffers(_, _))
+    .WillOnce(DoAll(SaveArg<1>(&offers),
+		    Trigger(&resourceOffers)))
+    .WillOnce(DoAll(SaveArg<1>(&offers2),
+		    Trigger(&resourceOffers2)));
+
+  EXPECT_CALL(sched, disconnected(_))
+    .WillRepeatedly(DoDefault());
+
+  driver.start();
+
+  WAIT_UNTIL(resourceOffers);
+
+  checkResources(offers, 2, 1024);
+
+  process::terminate(master1);
+  process::wait(master1);
+  MasterDetector::destroy(detector.get());
+
+  Master m2(&(this->allocator2));
+  PID<Master> master2 = process::spawn(m2);
+
+  Try<MasterDetector*> detector2 =
+    MasterDetector::create(zk, master2, true, true);
+  CHECK(!detector2.isError())
+    << "Failed to create a master detector: " << detector2.error();
+
+  WAIT_UNTIL(resourceOffers2);
+
+  checkResources(offers2, 2, 1024);
+
+  driver.stop();
+
+  WAIT_UNTIL(frameworkRemovedTrigger);
+
+  process::terminate(slave);
+  process::wait(slave);
+
+  WAIT_UNTIL(slaveRemovedTrigger);
+
+  process::terminate(master2);
+  process::wait(master2);
+
+  process::filter(NULL);
+}

Modified: incubator/mesos/trunk/src/tests/resource_offers_tests.cpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/tests/resource_offers_tests.cpp?rev=1359927&r1=1359926&r2=1359927&view=diff
==============================================================================
--- incubator/mesos/trunk/src/tests/resource_offers_tests.cpp (original)
+++ incubator/mesos/trunk/src/tests/resource_offers_tests.cpp Tue Jul 10 22:03:44 2012
@@ -455,7 +455,7 @@ TEST(ResourceOffersTest, Request)
   ASSERT_TRUE(GTEST_IS_THREADSAFE);
 
   MockScheduler sched;
-  MockAllocator allocator;
+  MockAllocator<DominantShareAllocator> allocator;
 
   EXPECT_CALL(allocator, initialize(_))
     .WillOnce(Return());

Modified: incubator/mesos/trunk/src/tests/utils.hpp
URL: http://svn.apache.org/viewvc/incubator/mesos/trunk/src/tests/utils.hpp?rev=1359927&r1=1359926&r2=1359927&view=diff
==============================================================================
--- incubator/mesos/trunk/src/tests/utils.hpp (original)
+++ incubator/mesos/trunk/src/tests/utils.hpp Tue Jul 10 22:03:44 2012
@@ -42,6 +42,8 @@
 #include "slave/isolation_module.hpp"
 #include "slave/slave.hpp"
 
+using ::testing::_;
+using ::testing::Invoke;
 
 namespace mesos {
 namespace internal {
@@ -136,6 +138,78 @@ public:
   MOCK_METHOD2(error, void(SchedulerDriver*, const std::string&));
 };
 
+// For use with a MockScheduler, for example:
+// EXPECT_CALL(sched, resourceOffers(_, _))
+//   .WillOnce(LaunchTasks(TASKS, CPUS, MEM));
+// Launches up to TASKS no-op tasks, if possible,
+// each with CPUS cpus and MEM memory.
+ACTION_P3(LaunchTasks, tasks, cpus, mem)
+{
+  SchedulerDriver* driver = arg0;
+  std::vector<Offer> offers = arg1;
+  int numTasks = tasks;
+
+  int launched = 0;
+  for (int i = 0; i < offers.size(); i++) {
+    const Offer& offer = offers[i];
+    double offeredCpus = 0;
+    double offeredMem = 0;
+
+    for (int i = 0; i < offer.resources_size(); i++) {
+      const Resource& resource = offer.resources(i);
+      if (resource.name() == "cpus" &&
+	  resource.type() == Value::SCALAR) {
+	offeredCpus = resource.scalar().value();
+      } else if (resource.name() == "mem" &&
+		 resource.type() == Value::SCALAR) {
+	offeredMem = resource.scalar().value();
+      }
+    }
+
+    std::vector<TaskInfo> tasks;
+    if (offeredCpus >= cpus &&
+	offeredMem >= mem &&
+	launched < numTasks) {
+      TaskInfo task;
+      task.set_name("TestTask");
+      task.mutable_task_id()->set_value("TestTask1");
+      task.mutable_slave_id()->MergeFrom(offer.slave_id());
+
+      ExecutorInfo executor;
+      executor.mutable_executor_id()->set_value("default");
+      executor.mutable_command()->set_value(":");
+      task.mutable_executor()->MergeFrom(executor);
+
+      Resource* resource;
+      resource = task.add_resources();
+      resource->set_name("cpus");
+      resource->set_type(Value::SCALAR);
+      resource->mutable_scalar()->set_value(cpus);
+
+      resource = task.add_resources();
+      resource->set_name("mem");
+      resource->set_type(Value::SCALAR);
+      resource->mutable_scalar()->set_value(mem);
+
+      tasks.push_back(task);
+      launched++;
+    }
+
+    driver->launchTasks(offer.id(), tasks);
+  }
+}
+
+// Like LaunchTasks, but decline the entire offer and
+// don't launch any tasks.
+ACTION(DeclineOffers)
+{
+  SchedulerDriver* driver = arg0;
+  std::vector<Offer> offers = arg1;
+
+  for (int i = 0; i < offers.size(); i++) {
+    driver->declineOffer(offers[i].id());
+  }
+}
 
 /**
  * Definition of a mock Executor to be used in tests with gmock.
@@ -157,9 +231,41 @@ public:
 };
 
 
+template <typename T = master::Allocator>
 class MockAllocator : public master::Allocator
 {
 public:
+  MockAllocator() {
+    ON_CALL(*this, initialize(_))
+      .WillByDefault(Invoke(&real, &T::initialize));
+
+    ON_CALL(*this, frameworkAdded(_, _))
+      .WillByDefault(Invoke(&real, &T::frameworkAdded));
+
+    ON_CALL(*this, frameworkDeactivated(_))
+      .WillByDefault(Invoke(&real, &T::frameworkDeactivated));
+
+    ON_CALL(*this, frameworkRemoved(_))
+      .WillByDefault(Invoke(&real, &T::frameworkRemoved));
+
+    ON_CALL(*this, slaveAdded(_, _, _))
+      .WillByDefault(Invoke(&real, &T::slaveAdded));
+
+    ON_CALL(*this, slaveRemoved(_))
+      .WillByDefault(Invoke(&real, &T::slaveRemoved));
+
+    ON_CALL(*this, resourcesRequested(_, _))
+      .WillByDefault(Invoke(&real, &T::resourcesRequested));
+
+    ON_CALL(*this, resourcesUnused(_, _, _, _))
+      .WillByDefault(Invoke(&real, &T::resourcesUnused));
+
+    ON_CALL(*this, resourcesRecovered(_, _, _))
+      .WillByDefault(Invoke(&real, &T::resourcesRecovered));
+
+    ON_CALL(*this, offersRevived(_))
+      .WillByDefault(Invoke(&real, &T::offersRevived));
+  }
   MOCK_METHOD1(initialize, void(const process::PID<master::Master>&));
   MOCK_METHOD2(frameworkAdded, void(const FrameworkID&, const FrameworkInfo&));
   MOCK_METHOD1(frameworkDeactivated, void(const FrameworkID&));
@@ -179,9 +285,40 @@ public:
                                         const SlaveID&,
                                         const Resources&));
   MOCK_METHOD1(offersRevived, void(const FrameworkID&));
+
+  T real;
 };
 
 
+// The following actions make up for the fact that DoDefault
+// cannot be used inside a DoAll, for example:
+// EXPECT_CALL(allocator, frameworkAdded(_))
+//   .WillOnce(DoAll(InvokeFrameworkAdded(&allocator),
+//                   Trigger(&frameworkAddedTrigger)));
+ACTION_P(InvokeFrameworkAdded, allocator)
+{
+  allocator->real.frameworkAdded(arg0, arg1);
+}
+
+
+ACTION_P(InvokeFrameworkRemoved, allocator)
+{
+  allocator->real.frameworkRemoved(arg0);
+}
+
+
+ACTION_P(InvokeSlaveAdded, allocator)
+{
+  allocator->real.slaveAdded(arg0, arg1, arg2);
+}
+
+
+ACTION_P(InvokeResourcesUnused, allocator)
+{
+  allocator->real.resourcesUnused(arg0, arg1, arg2, arg3);
+}
+
+
 /**
  * Definition of a mock Filter so that messages can act as triggers.
  */