TEST_F(ZooKeeperTest, LeaderDetector) { Group group(server->connectString(), NO_TIMEOUT, "/test/"); // Initialize two members. Future<Group::Membership> membership1 = group.join("member 1"); AWAIT_READY(membership1); Future<Group::Membership> membership2 = group.join("member 2"); AWAIT_READY(membership2); LeaderDetector detector(&group); // Detect the leader. Future<Option<Group::Membership> > leader = detector.detect(None()); AWAIT_READY(leader); ASSERT_SOME_EQ(membership1.get(), leader.get()); // Detect next leader change. leader = detector.detect(leader.get()); EXPECT_TRUE(leader.isPending()); // Leader doesn't change after cancelling the follower. Future<bool> cancellation = group.cancel(membership2.get()); AWAIT_READY(cancellation); EXPECT_TRUE(cancellation.get()); EXPECT_TRUE(leader.isPending()); // Join member 2 back. membership2 = group.join("member 2"); AWAIT_READY(membership2); EXPECT_TRUE(leader.isPending()); // Cancelling the incumbent leader allows member 2 to be elected. cancellation = group.cancel(membership1.get()); AWAIT_READY(cancellation); EXPECT_TRUE(cancellation.get()); AWAIT_READY(leader); EXPECT_SOME_EQ(membership2.get(), leader.get()); // Cancelling the only member results in no leader elected. leader = detector.detect(leader.get().get()); EXPECT_TRUE(leader.isPending()); cancellation = group.cancel(membership2.get()); AWAIT_READY(cancellation); EXPECT_TRUE(cancellation.get()); AWAIT_READY(leader); ASSERT_TRUE(leader.get().isNone()); }
TEST(HTTPTest, PipeEOF) { http::Pipe pipe; http::Pipe::Reader reader = pipe.reader(); http::Pipe::Writer writer = pipe.writer(); // A 'read' on an empty pipe should block. Future<string> read = reader.read(); EXPECT_TRUE(read.isPending()); // Writing an empty string should have no effect. EXPECT_TRUE(writer.write("")); EXPECT_TRUE(read.isPending()); // After a 'write' the pending 'read' should complete. EXPECT_TRUE(writer.write("hello")); ASSERT_TRUE(read.isReady()); EXPECT_EQ("hello", read.get()); // After a 'write' a call to 'read' should be completed immediately. ASSERT_TRUE(writer.write("world")); read = reader.read(); ASSERT_TRUE(read.isReady()); EXPECT_EQ("world", read.get()); // Close the write end of the pipe and ensure the remaining // data can be read. EXPECT_TRUE(writer.write("!")); EXPECT_TRUE(writer.close()); AWAIT_EQ("!", reader.read()); // End of file should be reached. AWAIT_EQ("", reader.read()); AWAIT_EQ("", reader.read()); // Writes to a pipe with the write end closed are ignored. EXPECT_FALSE(writer.write("!")); AWAIT_EQ("", reader.read()); // The write end cannot be closed twice. EXPECT_FALSE(writer.close()); // Close the read end, this should not notify the writer // since the write end was already closed. EXPECT_TRUE(reader.close()); EXPECT_TRUE(writer.readerClosed().isPending()); }
TEST_F(CoordinatorTest, ElectNoQuorum) { const string path = os::getcwd() + "/.log"; initializer.flags.path = path; initializer.execute(); Shared<Replica> replica(new Replica(path)); set<UPID> pids; pids.insert(replica->pid()); Shared<Network> network(new Network(pids)); Coordinator coord(2, replica, network); Clock::pause(); Future<Option<uint64_t> > electing = coord.elect(); Clock::advance(Seconds(10)); Clock::settle(); EXPECT_TRUE(electing.isPending()); Clock::resume(); }
void RegistrarProcess::_recover( const MasterInfo& info, const Future<Variable<Registry> >& recovery) { updating = false; CHECK(!recovery.isPending()); if (!recovery.isReady()) { recovered.get()->fail("Failed to recover registrar: " + (recovery.isFailed() ? recovery.failure() : "discarded")); } else { Duration elapsed = metrics.state_fetch.stop(); LOG(INFO) << "Successfully fetched the registry" << " (" << Bytes(recovery.get().get().ByteSize()) << ")" << " in " << elapsed; // Save the registry. variable = recovery.get(); // Perform the Recover operation to add the new MasterInfo. Owned<Operation> operation(new Recover(info)); operations.push_back(operation); operation->future() .onAny(defer(self(), &Self::__recover, lambda::_1)); update(); } }
/* * Class: org_apache_mesos_state_AbstractState * Method: __expunge_is_done * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_apache_mesos_state_AbstractState__1_1expunge_1is_1done (JNIEnv* env, jobject thiz, jlong jfuture) { Future<bool>* future = (Future<bool>*) jfuture; return (jboolean) !future->isPending() || future->hasDiscard(); }
// This test verifies that when the write end of the `reader` used in // `transform` fails, a failure is returned to the caller. TEST(RecordIOTransformTest, ReaderWriterEndFail) { // Write some data to the pipe so that records // are available before any reads occur. ::recordio::Encoder<string> encoder(strings::upper); string data; data += encoder.encode("hello "); data += encoder.encode("world! "); process::http::Pipe pipeA; pipeA.writer().write(data); process::Owned<mesos::internal::recordio::Reader<string>> reader( new mesos::internal::recordio::Reader<string>( ::recordio::Decoder<string>(strings::lower), pipeA.reader())); process::http::Pipe pipeB; auto trim = [](const string& str) { return strings::trim(str); }; Future<Nothing> transform = mesos::internal::recordio::transform<string>( std::move(reader), trim, pipeB.writer()); Future<string> future = pipeB.reader().readAll(); pipeA.writer().fail("Writer failure"); AWAIT_FAILED(transform); ASSERT_TRUE(future.isPending()); }
Future<bool> LeaderContenderProcess::withdraw() { if (contending.isNone()) { // Nothing to withdraw because the contender has not contended. return false; } if (withdrawing.isSome()) { // Repeated calls to withdraw get the same result. return withdrawing.get(); } withdrawing = new Promise<bool>(); CHECK(!candidacy.isDiscarded()); if (candidacy.isPending()) { // If we have not obtained the candidacy yet, we withdraw after // it is obtained. LOG(INFO) << "Withdraw requested before the candidacy is obtained; will " << "withdraw after it happens"; candidacy.onAny(defer(self(), &Self::cancel)); } else if (candidacy.isReady()) { cancel(); } else { // We have failed to obtain the candidacy so we do not need to // cancel it. return false; } return withdrawing.get()->future(); }
/* * Class: org_apache_mesos_state_ZooKeeperState * Method: __names_is_done * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_apache_mesos_state_ZooKeeperState__1_1names_1is_1done (JNIEnv* env, jobject thiz, jlong jfuture) { Future<vector<string> >* future = (Future<vector<string> >*) jfuture; return (jboolean) !future->isPending(); }
/* * Class: org_apache_mesos_state_ZooKeeperState * Method: __store_is_done * Signature: (J)Z */ JNIEXPORT jboolean JNICALL Java_org_apache_mesos_state_ZooKeeperState__1_1store_1is_1done (JNIEnv* env, jobject thiz, jlong jfuture) { Future<Option<Variable> >* future = (Future<Option<Variable> >*) jfuture; return (jboolean) !future->isPending(); }
TEST(FutureTest, Select) { Promise<int> promise1; Promise<int> promise2; Promise<int> promise3; Promise<int> promise4; std::set<Future<int>> futures = { promise1.future(), promise2.future(), promise3.future(), promise4.future() }; promise1.set(42); Future<Future<int>> future = select(futures); AWAIT_READY(future); AWAIT_READY(future.get()); EXPECT_EQ(42, future->get()); futures.erase(promise1.future()); future = select(futures); EXPECT_TRUE(future.isPending()); future.discard(); AWAIT_DISCARDED(future); }
TEST(FutureTest, Chain) { Future<string> s = readyFuture() .then(lambda::bind(&second, lambda::_1)) .then(lambda::bind(&third, lambda::_1)); s.await(); ASSERT_TRUE(s.isReady()); EXPECT_EQ("true", s.get()); s = failedFuture() .then(lambda::bind(&second, lambda::_1)) .then(lambda::bind(&third, lambda::_1)); s.await(); ASSERT_TRUE(s.isFailed()); Promise<bool> promise; s = pendingFuture(promise.future()) .then(lambda::bind(&second, lambda::_1)) .then(lambda::bind(&third, lambda::_1)); ASSERT_TRUE(s.isPending()); promise.discard(); AWAIT_DISCARDED(s); }
// The test verifies that callbacks are properly serialized by the // Sequence object. TEST(SequenceTest, Serialize) { TestProcess process; spawn(process); Sequence sequence; Future<Nothing> bar = FUTURE_DISPATCH(_, &TestProcess::bar); lambda::function<Future<Nothing>(void)> f; f = defer(process, &TestProcess::foo); sequence.add(f); f = defer(process, &TestProcess::bar); sequence.add(f); // Flush the event queue to make sure that if the method 'bar' could // have been invoked, the future 'bar' would be satisfied before the // pending check below. Clock::pause(); Clock::settle(); Clock::resume(); EXPECT_TRUE(bar.isPending()); process.promise.set(Nothing()); AWAIT_READY(bar); terminate(process); wait(process); }
// This test checks that the we can reap a child process and obtain // the correct exit status. TEST(Reap, ChildProcess) { ASSERT_TRUE(GTEST_IS_THREADSAFE); // The child process sleeps and will be killed by the parent. Try<ProcessTree> tree = Fork(None(), Exec("sleep 10"))(); ASSERT_SOME(tree); pid_t child = tree.get(); // Reap the child process. Future<Option<int> > status = process::reap(child); // Now kill the child. EXPECT_EQ(0, kill(child, SIGKILL)); Clock::pause(); // Now advance time until the reaper reaps the child. while (status.isPending()) { Clock::advance(Seconds(1)); Clock::settle(); } AWAIT_READY(status); // Check if the status is correct. ASSERT_SOME(status.get()); int status_ = status.get().get(); ASSERT_TRUE(WIFSIGNALED(status_)); ASSERT_EQ(SIGKILL, WTERMSIG(status_)); Clock::resume(); }
TEST(NetworkTest, Watch) { UPID pid1 = ProcessBase().self(); UPID pid2 = ProcessBase().self(); Network network; // Test the default parameter. Future<size_t> future = network.watch(1u); AWAIT_READY(future); EXPECT_EQ(0u, future.get()); future = network.watch(2u, Network::NOT_EQUAL_TO); AWAIT_READY(future); EXPECT_EQ(0u, future.get()); future = network.watch(0u, Network::GREATER_THAN_OR_EQUAL_TO); AWAIT_READY(future); EXPECT_EQ(0u, future.get()); future = network.watch(1u, Network::LESS_THAN); AWAIT_READY(future); EXPECT_EQ(0u, future.get()); network.add(pid1); future = network.watch(1u, Network::EQUAL_TO); AWAIT_READY(future); EXPECT_EQ(1u, future.get()); future = network.watch(1u, Network::GREATER_THAN); ASSERT_TRUE(future.isPending()); network.add(pid2); AWAIT_READY(future); EXPECT_EQ(2u, future.get()); future = network.watch(1u, Network::LESS_THAN_OR_EQUAL_TO); ASSERT_TRUE(future.isPending()); network.remove(pid2); AWAIT_READY(future); EXPECT_EQ(1u, future.get()); }
TEST(CollectTest, Failure) { Promise<int> promise1; Promise<bool> promise2; Future<std::tuple<int, bool>> collect = process::collect(promise1.future(), promise2.future()); ASSERT_TRUE(collect.isPending()); promise1.set(42); ASSERT_TRUE(collect.isPending()); promise2.set(true); AWAIT_READY(collect); std::tuple<int, bool> values = collect.get(); ASSERT_EQ(42, std::get<0>(values)); ASSERT_TRUE(std::get<1>(values)); // Collect should fail when a future fails. Promise<bool> promise3; collect = process::collect(promise1.future(), promise3.future()); ASSERT_TRUE(collect.isPending()); promise3.fail("failure"); AWAIT_FAILED(collect); // Collect should fail when a future is discarded. Promise<bool> promise4; collect = process::collect(promise1.future(), promise4.future()); ASSERT_TRUE(collect.isPending()); promise4.discard(); AWAIT_FAILED(collect); }
TEST(DecoderTest, StreamingResponseFailure) { StreamingResponseDecoder decoder; const string headers = "HTTP/1.1 200 OK\r\n" "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" "Content-Type: text/plain\r\n" "Content-Length: 2\r\n" "\r\n"; // The body is shorter than the content length! const string body = "1"; deque<Response*> responses = decoder.decode(headers.data(), headers.length()); ASSERT_FALSE(decoder.failed()); ASSERT_EQ(1, responses.size()); Response* response = responses[0]; EXPECT_EQ("200 OK", response->status); EXPECT_EQ(3, response->headers.size()); ASSERT_EQ(Response::PIPE, response->type); ASSERT_SOME(response->reader); http::Pipe::Reader reader = response->reader.get(); Future<string> read = reader.read(); EXPECT_TRUE(read.isPending()); decoder.decode(body.data(), body.length()); EXPECT_TRUE(read.isReady()); EXPECT_EQ("1", read.get()); // Body is not yet complete. read = reader.read(); EXPECT_TRUE(read.isPending()); // Feeding EOF to the decoder should trigger a failure! decoder.decode("", 0); EXPECT_TRUE(read.isFailed()); EXPECT_EQ("failed to decode body", read.failure()); }
// A single contender gets elected automatically. TEST_F(ZooKeeperMasterContenderDetectorTest, MasterContender) { Try<zookeeper::URL> url = zookeeper::URL::parse( "zk://" + server->connectString() + "/mesos"); ASSERT_SOME(url); Owned<zookeeper::Group> group( new Group(url.get(), MASTER_CONTENDER_ZK_SESSION_TIMEOUT)); ZooKeeperMasterContender* contender = new ZooKeeperMasterContender(group); PID<Master> pid; pid.node.ip = 10000000; pid.node.port = 10000; MasterInfo master = internal::protobuf::createMasterInfo(pid); contender->initialize(master); Future<Future<Nothing> > contended = contender->contend(); AWAIT_READY(contended); ZooKeeperMasterDetector detector(url.get()); Future<Option<MasterInfo> > leader = detector.detect(); AWAIT_READY(leader); EXPECT_SOME_EQ(master, leader.get()); leader = detector.detect(leader.get()); // No change to leadership. ASSERT_TRUE(leader.isPending()); // Ensure we can discard the future. leader.discard(); AWAIT_DISCARDED(leader); // After the discard, we can re-detect correctly. leader = detector.detect(None()); AWAIT_READY(leader); EXPECT_SOME_EQ(master, leader.get()); // Now test that a session expiration causes candidacy to be lost // and the future to become ready. Future<Nothing> lostCandidacy = contended.get(); leader = detector.detect(leader.get()); Future<Option<int64_t> > sessionId = group.get()->session(); AWAIT_READY(sessionId); server->expireSession(sessionId.get().get()); AWAIT_READY(lostCandidacy); AWAIT_READY(leader); EXPECT_NONE(leader.get()); }
// This test verifies that the pending future returned by // 'Authenticator::authenticate()' is properly failed when the Authenticator is // destructed in the middle of authentication. TYPED_TEST(CRAMMD5Authentication, AuthenticatorDestructionRace) { // Launch a dummy process (somebody to send the AuthenticateMessage). UPID pid = spawn(new ProcessBase(), true); Credential credential1; credential1.set_principal("benh"); credential1.set_secret("secret"); Credentials credentials; Credential* credential2 = credentials.add_credentials(); credential2->set_principal(credential1.principal()); credential2->set_secret(credential1.secret()); secrets::load(credentials); Future<Message> message = FUTURE_MESSAGE(Eq(AuthenticateMessage().GetTypeName()), _, _); Try<Authenticatee*> authenticatee = TypeParam::TypeAuthenticatee::create(); CHECK_SOME(authenticatee); Future<bool> client = authenticatee.get()->authenticate(pid, UPID(), credential1); AWAIT_READY(message); Try<Authenticator*> authenticator = TypeParam::TypeAuthenticator::create(); CHECK_SOME(authenticator); authenticator.get()->initialize(message.get().from); // Drop the AuthenticationStepMessage from authenticator to keep // the authentication from getting completed. Future<AuthenticationStepMessage> authenticationStepMessage = DROP_PROTOBUF(AuthenticationStepMessage(), _, _); Future<Option<string>> principal = authenticator.get()->authenticate(); AWAIT_READY(authenticationStepMessage); // At this point 'AuthenticatorProcess::authenticate()' has been // executed and its promise associated with the promise returned // by 'Authenticator::authenticate()'. // Authentication should be pending. ASSERT_TRUE(principal.isPending()); // Now delete the authenticator. delete authenticator.get(); // The future should be failed at this point. AWAIT_FAILED(principal); terminate(pid); delete authenticatee.get(); }
// This test checks if destroy is called while container is being // launched, the composing containerizer still calls the underlying // containerizer's destroy and skip calling the rest of the // containerizers. TEST_F(ComposingContainerizerTest, DestroyWhileLaunching) { vector<Containerizer*> containerizers; MockContainerizer* mockContainerizer = new MockContainerizer(); MockContainerizer* mockContainerizer2 = new MockContainerizer(); containerizers.push_back(mockContainerizer); containerizers.push_back(mockContainerizer2); ComposingContainerizer containerizer(containerizers); ContainerID containerId; containerId.set_value("container"); TaskInfo taskInfo; ExecutorInfo executorInfo; SlaveID slaveId; std::map<std::string, std::string> environment; Promise<bool> launchPromise; EXPECT_CALL(*mockContainerizer, launch(_, _, _, _, _, _, _, _)) .WillOnce(Return(launchPromise.future())); Future<Nothing> destroy; EXPECT_CALL(*mockContainerizer, destroy(_)) .WillOnce(FutureSatisfy(&destroy)); Future<bool> launch = containerizer.launch( containerId, taskInfo, executorInfo, "dir", "user", slaveId, environment, false); Resources resources = Resources::parse("cpus:1;mem:256").get(); EXPECT_TRUE(launch.isPending()); containerizer.destroy(containerId); EXPECT_CALL(*mockContainerizer2, launch(_, _, _, _, _, _, _, _)) .Times(0); // We make sure the destroy is being called on the first containerizer. // The second containerizer shouldn't be called as well since the // container is already destroyed. AWAIT_READY(destroy); launchPromise.set(false); AWAIT_FAILED(launch); }
// This test ensures that destroy can be called at the end of the // launch loop. The composing containerizer still calls the // underlying containerizer's destroy (because it's not sure // if the containerizer can handle the type of container being // launched). If the launch is not supported by any containerizers // both the launch and destroy futures should be false. TEST_F(ComposingContainerizerTest, DestroyAfterLaunchLoop) { vector<Containerizer*> containerizers; MockContainerizer* mockContainerizer1 = new MockContainerizer(); containerizers.push_back(mockContainerizer1); ComposingContainerizer containerizer(containerizers); ContainerID containerId; containerId.set_value("container"); TaskInfo taskInfo; ExecutorInfo executorInfo; SlaveID slaveId; std::map<std::string, std::string> environment; Promise<bool> launchPromise; EXPECT_CALL(*mockContainerizer1, launch(_, _, _, _, _, _, _, _)) .WillOnce(Return(launchPromise.future())); Future<Nothing> destroy; Promise<bool> destroyPromise; EXPECT_CALL(*mockContainerizer1, destroy(_)) .WillOnce(DoAll(FutureSatisfy(&destroy), Return(destroyPromise.future()))); Future<bool> launched = containerizer.launch( containerId, taskInfo, executorInfo, "dir", "user", slaveId, environment, false); Resources resources = Resources::parse("cpus:1;mem:256").get(); EXPECT_TRUE(launched.isPending()); Future<bool> destroyed = containerizer.destroy(containerId); // We make sure the destroy is being called on the containerizer. AWAIT_READY(destroy); launchPromise.set(false); destroyPromise.set(false); // `launch` should return false and `destroyed` should return false // because none of the containerizers support the launch. AWAIT_EXPECT_EQ(false, launched); AWAIT_EXPECT_EQ(false, destroyed); }
TEST(ProcessTest, PercentEncodedURLs) { PercentEncodedIDProcess process; spawn(process); // Construct the PID using percent-encoding. http::URL url = http::URL( "http", process.self().address.ip, process.self().address.port, http::encode(process.self().id) + "/handler1"); Future<http::Connection> connect = http::connect(url); AWAIT_READY(connect); http::Connection connection = connect.get(); // Mimic a libprocess message sent to an installed handler. Future<Nothing> handler1; EXPECT_CALL(process, handler1(_, _)) .WillOnce(FutureSatisfy(&handler1)); http::Request request; request.method = "POST"; request.url = url; request.headers["User-Agent"] = "libprocess/"; // Send the libprocess request. Note that we will not // receive a 202 due to the use of the `User-Agent` // header, therefore we need to explicitly disconnect! Future<http::Response> response = connection.send(request); AWAIT_READY(handler1); EXPECT_TRUE(response.isPending()); AWAIT_READY(connection.disconnect()); // Now an HTTP request. EXPECT_CALL(process, handler2(_)) .WillOnce(Return(http::OK())); // Construct the PID using percent-encoding. UPID pid(http::encode(process.self().id), process.self().address); response = http::get(pid, "handler2"); AWAIT_READY(response); EXPECT_EQ(http::Status::OK, response->code); EXPECT_EQ(http::Status::string(http::Status::OK), response->status); terminate(process); wait(process); }
TEST(IOTest, Poll) { ASSERT_TRUE(GTEST_IS_THREADSAFE); int pipes[2]; ASSERT_NE(-1, pipe(pipes)); // Test discard when polling. Future<short> future = io::poll(pipes[0], io::READ); EXPECT_TRUE(future.isPending()); future.discard(); AWAIT_DISCARDED(future); // Test successful polling. future = io::poll(pipes[0], io::READ); EXPECT_TRUE(future.isPending()); ASSERT_EQ(3, write(pipes[1], "hi", 3)); AWAIT_EXPECT_EQ(io::READ, future); ASSERT_SOME(os::close(pipes[0])); ASSERT_SOME(os::close(pipes[1])); }
// Tests that Future::discard does not complete the future and // Promise::set can still be invoked to complete the future. TEST(FutureTest, Discard2) { Promise<bool> promise1; Promise<int> promise2; std::atomic_bool executed(false); Future<int> future = Future<string>("hello world") .then(lambda::bind(&inner1, promise1.future())) .then(lambda::bind(&inner2, &executed, promise2.future())); ASSERT_TRUE(future.isPending()); future.discard(); // The future should remain pending, even though we discarded it. ASSERT_TRUE(future.hasDiscard()); ASSERT_TRUE(future.isPending()); // The future associated with the lambda already executed in the // first 'then' should have the discard propagated to it. ASSERT_TRUE(promise1.future().hasDiscard()); // But the future assocaited with the lambda that hasn't yet been // executed should not have the discard propagated to it. ASSERT_FALSE(promise2.future().hasDiscard()); // Now setting the promise should cause the outer future to be // discarded rather than executing the last lambda because the // implementation of Future::then does not continue the chain once a // discard occurs. ASSERT_TRUE(promise1.set(true)); AWAIT_DISCARDED(future); // And the final lambda should never have executed. ASSERT_FALSE(executed.load()); ASSERT_TRUE(promise2.future().isPending()); }
// This test verifies that a non-VOTING replica does not reply to // promise or write requests. TEST_F(ReplicaTest, NonVoting) { const string path = os::getcwd() + "/.log"; Replica replica(path); PromiseRequest promiseRequest; promiseRequest.set_proposal(2); Future<PromiseResponse> promiseResponse = protocol::promise(replica.pid(), promiseRequest); // Flush the event queue to make sure that if the replica could // reply to the promise request, the future 'promiseResponse' would // be satisfied before the pending check below. Clock::pause(); Clock::settle(); Clock::resume(); EXPECT_TRUE(promiseResponse.isPending()); WriteRequest writeRequest; writeRequest.set_proposal(3); writeRequest.set_position(1); writeRequest.set_type(Action::APPEND); writeRequest.mutable_append()->set_bytes("hello world"); Future<WriteResponse> writeResponse = protocol::write(replica.pid(), writeRequest); // Flush the event queue to make sure that if the replica could // reply to the write request, the future 'writeResponse' would be // satisfied before the pending check below. Clock::pause(); Clock::settle(); Clock::resume(); EXPECT_TRUE(writeResponse.isPending()); }
// Checks that completing a promise will keep the 'after' callback // from executing. TEST(FutureTest, After2) { Clock::pause(); std::atomic_bool executed(false); Promise<Nothing> promise; Future<Nothing> future = promise.future() .after(Hours(42), lambda::bind(&after, &executed, lambda::_1)); EXPECT_TRUE(future.isPending()); // Only advanced halfway, future should remain pending. Clock::advance(Hours(21)); EXPECT_TRUE(future.isPending()); // Even doing a discard on the future should keep it pending. future.discard(); EXPECT_TRUE(future.isPending()); // Now set the promise, the 'after' timer should be cancelled and // the pending future should be completed. promise.set(Nothing()); AWAIT_READY(future); // Advancing time the rest of the way should not cause the 'after' // callback to execute. Clock::advance(Hours(21)); EXPECT_FALSE(executed.load()); Clock::resume(); }
// Tests that Future::discard does not complete the future and // Promise::fail can still be invoked to complete the future. TEST(FutureTest, Discard3) { Promise<bool> promise1; Promise<int> promise2; std::atomic_bool executed(false); Future<int> future = Future<string>("hello world") .then(lambda::bind(&inner1, promise1.future())) .then(lambda::bind(&inner2, &executed, promise2.future())); ASSERT_TRUE(future.isPending()); future.discard(); // The future should remain pending, even though we discarded it. ASSERT_TRUE(future.hasDiscard()); ASSERT_TRUE(future.isPending()); // The future associated with the lambda already executed in the // first 'then' should have the discard propagated to it. ASSERT_TRUE(promise1.future().hasDiscard()); // But the future assocaited with the lambda that hasn't yet been // executed should not have the discard propagated to it. ASSERT_FALSE(promise2.future().hasDiscard()); // Now failing the promise should cause the outer future to be // failed also. ASSERT_TRUE(promise1.fail("failure message")); AWAIT_FAILED(future); // And the final lambda should never have executed. ASSERT_FALSE(executed.load()); ASSERT_TRUE(promise2.future().isPending()); }
// Verifies that contender does not recontend if the current election // is still pending. TEST_F(ZooKeeperMasterContenderDetectorTest, ContenderPendingElection) { Try<zookeeper::URL> url = zookeeper::URL::parse( "zk://" + server->connectString() + "/mesos"); ASSERT_SOME(url); ZooKeeperMasterContender contender(url.get()); PID<Master> pid; pid.node.ip = 10000000; pid.node.port = 10000; MasterInfo master = internal::protobuf::createMasterInfo(pid); contender.initialize(master); // Drop Group::join so that 'contended' will stay pending. Future<Nothing> join = DROP_DISPATCH(_, &GroupProcess::join); Future<Future<Nothing> > contended = contender.contend(); AWAIT_READY(join); Clock::pause(); // Make sure GroupProcess::join is dispatched (and dropped). Clock::settle(); EXPECT_TRUE(contended.isPending()); process::filter(NULL); process::TestsFilter* filter = process::FilterTestEventListener::instance()->install(); pthread_mutex_lock(&filter->mutex); // Expect GroupProcess::join not getting called because // ZooKeeperMasterContender directly returns. EXPECT_CALL(filter->mock, filter(testing::A<const process::DispatchEvent&>())) .With(DispatchMatcher(_, &GroupProcess::join)) .Times(0); pthread_mutex_lock(&filter->mutex); // Recontend and settle so that if ZooKeeperMasterContender is not // directly returning, GroupProcess::join is dispatched. contender.contend(); Clock::settle(); Clock::resume(); }
TEST(DecoderTest, StreamingResponse) { StreamingResponseDecoder decoder; const string headers = "HTTP/1.1 200 OK\r\n" "Date: Fri, 31 Dec 1999 23:59:59 GMT\r\n" "Content-Type: text/plain\r\n" "Content-Length: 2\r\n" "\r\n"; const string body = "hi"; deque<http::Response*> responses = decoder.decode(headers.data(), headers.length()); EXPECT_FALSE(decoder.failed()); EXPECT_TRUE(decoder.writingBody()); ASSERT_EQ(1, responses.size()); Owned<http::Response> response(responses[0]); EXPECT_EQ("200 OK", response->status); EXPECT_EQ(3, response->headers.size()); ASSERT_EQ(http::Response::PIPE, response->type); ASSERT_SOME(response->reader); http::Pipe::Reader reader = response->reader.get(); Future<string> read = reader.read(); EXPECT_TRUE(read.isPending()); decoder.decode(body.data(), body.length()); EXPECT_FALSE(decoder.failed()); EXPECT_FALSE(decoder.writingBody()); // Feeding EOF to the decoder should be ok. decoder.decode("", 0); EXPECT_FALSE(decoder.failed()); EXPECT_FALSE(decoder.writingBody()); EXPECT_TRUE(read.isReady()); EXPECT_EQ("hi", read.get()); // Response should be complete. read = reader.read(); EXPECT_TRUE(read.isReady()); EXPECT_EQ("", read.get()); }
// Check that the Reaper can monitor a child process that exits // before monitor() is called on it. TEST(ReaperTest, TerminatedChildProcess) { ASSERT_TRUE(GTEST_IS_THREADSAFE); // The child process immediately exits. Try<ProcessTree> tree = Fork(None(), Exec("exit 0"))(); ASSERT_SOME(tree); pid_t child = tree.get(); ASSERT_SOME(os::process(child)); Clock::pause(); Reaper reaper; // Because reaper reaps all child processes even if they aren't // registered, we advance time until that happens. while (os::process(child).isSome()) { Clock::advance(Seconds(1)); Clock::settle(); } // Now we request to monitor the child process which is already // reaped. Future<Nothing> monitor = FUTURE_DISPATCH(_, &ReaperProcess::monitor); // Ask the reaper to monitor the child process. Future<Option<int> > status = reaper.monitor(child); AWAIT_READY(monitor); // Now advance time until the reaper sends the notification. while (status.isPending()) { Clock::advance(Seconds(1)); Clock::settle(); } // Ensure the reaper notifies of the terminated process. AWAIT_READY(status); // Invalid status is returned because it is reaped before being // monitored. ASSERT_NONE(status.get()); Clock::resume(); }
TEST_F(RoutingVethTest, ROOT_LinkWait) { AWAIT_READY(link::removed(TEST_VETH_LINK)); ASSERT_SOME(link::create(TEST_VETH_LINK, TEST_PEER_LINK, None())); EXPECT_SOME_TRUE(link::exists(TEST_VETH_LINK)); EXPECT_SOME_TRUE(link::exists(TEST_PEER_LINK)); Future<Nothing> removed = link::removed(TEST_VETH_LINK); EXPECT_TRUE(removed.isPending()); ASSERT_SOME_TRUE(link::remove(TEST_VETH_LINK)); AWAIT_READY(removed); }