> On Dec. 26, 2014, 9:11 p.m., Timothy Chen wrote: > > Can you add a unit test? > > Nishant Suneja wrote: > I wanted to add one, but I would need access to the "Future" object for > the container launch, or "Timer" object for registration timeout trigger, > both of which > I cannot access, since they are local variables, and NOT the member > variables of any class. > > Any ideas as to how do I get around it, without introducting any extra > member variables ? > > Timothy Chen wrote: > you can get hold of the future by passing in a mock containerizzer and > perform a EXPECT_CALL(comtainerizer, launch(.....)) and return a future that > you instantiate in test. > > Nishant Suneja wrote: > Thats a neat way to get hold of the future object. So, for testing I have > to basically verify that the timer object for Slave's "registerExecutor" is > not created, till we reach the READY state of the future object. Now, I could > have done something crazy like iterate the map of timer objects in the Clock > class, and verify that we dont have this timer object till we are in READY > state of the future object. > However, this map is a defined as static in clock.cpp, so I wouldnt be > able to access it in the testcase, without making the map global (not a good > idea). > > So, again, any ideas ? > > Timothy Chen wrote: > The easiest way is to simply advance the clock (Clock::advance(....)) > until the timeout, and verify that the delay call is not yet called. Then you > satisfy the future and advance the time again and verify it is called. > > Nishant Suneja wrote: > Ok...And whats the best way to verify that the > "Slave::registerExecutorTimeout" has been called (after advancing the clock). > I could check the state of the executor, and verify that its TERMINATING, > which would mean that this method has executed. > > Any other cleaner way to verify this callback ? I believe that > EXPECT_CALL macro only works for classes with MOCK_METHOD set of methods > defined. So, I cant use that. > > Timothy Chen wrote: > One way to verify this is to use FUTURE_DISPATCH (you can search for > examples in existing test). FUTURE_DISPATCH returns a future, which becomes > ready when the method provided is dispatched (which means queued to serve in > libprocess). Delay shouldn't queue it until the timeout happen. > > Nishant Suneja wrote: > Yeah.. FUTURE_DISPATCH should do the job. > However, a question about the failure testcase. When we invoke > Clock::advance() by a certain duration, any registered timer object which > falls within that duration gets > called. For 2nd test, I can use the AWAIT_READY() macro on the future, > and then advancing the clock should result in our "registerExecutorTimeout" > being called, which is good. > > Although, for the 1st test case, before advancing the clock, I would want > to make sure that the launch() function has actually returned, and our > current code triggers the timer (otherwise, I would be advancing the clock, > when no timer object has actually been set, so obviously nothing will get > fired by advancing the clock). > EXPECT_CALL gives us a handle to that Future object, but how do I > determine that launch() method has returned (still in pending state though), > so i can advance the clock ? i.e. on what condition should I wait here? > > Timothy Chen wrote: > EXPECT_CALL is called when the method is invoked, and whatever action you > chain it with (.WillOnce(Returns(....)) is called immediately, so for example > if you want to wait on the call, you can use EXPECT_CALL(containerizer, > launch(_, _, _......)).WillOnce(FutureSatisify(&future)) and then > AWAIT_READY(future), which the EXPECT_CALL will set the future state to be > ready when the launch method is called. > > I think what I would do though is to create a promise, and return that > promise's future instead: > > Promise<Nothing> promise; > EXPECT_CALL(containerizer, launch(_, _, _,....)) > .WillOnce(Return(promise.future())); > > And then Clock::pause(), Clock::advance(timeout), and verify that the > executor timeout is not ready ASSERT_TRUE(timeoutFuture.isPending()) > > then satisfy the promise so the future is set to ready: > > promise.set(Nothing()) > > After wards advance the clock and verify that it is ready. > > Nishant Suneja wrote: > Hey Timothy > > So, I went through the documentation of gmock, and got a better > understanding of the magic MACROS. > > That said, I still have 2 questions: > > 1) During a unit test run, where we instantiate a master and a slave, how > many processes are we actually dealing with ? > Logic says that we have 1 test case process, and its 2 child processes > (slave and master). > I ask this because it seems that we are sharing the containerizer > object between test case processs and its child (slave) process. > Also, the callback in the ACTION part of the EXPECT_CALL, I believe > that its been invoked in the context of the slave process. > Just wanted to clarify if I am on the correct page. > > 2) So, if the above statement is correct, in the code below, we seem to > have a race. We would expect this test case to pass with our > new code. However, in my patch, the call to delay() happens in > onReady() method of the launch future object, which happens > AFTER invocation of containerizer::launch(). > Now, its very well possible, that we pause and advance the clock in > the parent test case process, before the delay() > gets called in the slave process, in which case the timer wont fire at > all. > > 3) Which brings me to my third question, as to are we sharing the same > clock across these 3 processes ? > Clock class (for eg.) is packaged as part of libprocess library, which > can be shared among these 3 processes, > but there would be separate copies of static/global data of this class > for the 3 processes (only the code segment should be shared). > So, will the timer set by one process, be visible to another process > at all ? > > > CODE: > > Future<Nothing> launch; > EXPECT_CALL(containerizer, launch(_, _, _, _, _, _, _)) > .WillOnce(DoAll(FutureSatisfy(&launch), > Return(true))); > > //future object for call to "registerExecutorTimeout" > Future<Nothing> executorRegistrationTimeout = > FUTURE_DISPATCH(_, &Slave::registerExecutorTimeout); > > driver.launchTasks(offers.get()[0].id(), tasks); > > AWAIT_READY(launch); > > Clock::pause(); > > Clock::advance(flags.executor_registration_timeout); > > //ensure that the "registerExecutorTimeout" HAS fired > ASSERT_TRUE(executorRegistrationTimeout.isReady()); > > Clock::resume(); > > Nishant Suneja wrote: > Ok..I have updated the diff with the testcase. > > I was able to answer my questions. As for question-1), For unit tests, we > are running all the components in the same process, rather than spawning one > process per component. Essentially, we seem to be relying on a single thread > event driven kind of mechanism for unit tests. > So, my question 3) becomes irrelevant. > > For question-2, the trick was to use two futures (a future and a > promise), one to track the call of the launch() method, and the 2nd one to > trigger the registration timeout. > > > Additionally, I have moved the delay() call from onReady() callback to > onAny() callback, since we want the timer to be set, even if the future fails > or is dicarded.
Hi Nishant, sorry didn't get time to reply you. Glad you figured out your questions. - Timothy ----------------------------------------------------------- This is an automatically generated e-mail. To reply, visit: https://reviews.apache.org/r/29437/#review66171 ----------------------------------------------------------- On Dec. 31, 2014, 5:11 a.m., Nishant Suneja wrote: > > ----------------------------------------------------------- > This is an automatically generated e-mail. To reply, visit: > https://reviews.apache.org/r/29437/ > ----------------------------------------------------------- > > (Updated Dec. 31, 2014, 5:11 a.m.) > > > Review request for mesos and Timothy Chen. > > > Bugs: MESOS-999 > https://issues.apache.org/jira/browse/MESOS-999 > > > Repository: mesos-git > > > Description > ------- > > As part of this bug fix, I have trigerred the executor registration timeout > timer asychronously, > when the onAny() callback is made for the container's future object, instead > of starting the > timer synchronously when the launchExecutor() method of the Framework class > is invoked. > > > Diffs > ----- > > src/slave/slave.cpp 50b57819b55bdcdb9f49f20648199badc4d3f37b > src/tests/composing_containerizer_tests.cpp > 5ab5a36cadb7f8622bad0c5814e9a5fb338753ad > src/tests/containerizer.hpp 24b014f44d9eec56840e18cf39fbf9100f2c0711 > src/tests/slave_tests.cpp f2896a1fc4521452e29fd261a6f117372345dcfc > > Diff: https://reviews.apache.org/r/29437/diff/ > > > Testing > ------- > > Added the unit test : > SlaveTest::AsynchronousExecutorRegistrationTimeoutTrigger > make check succeeds. > > > Thanks, > > Nishant Suneja > >