示例#1
0
// Checks that the 'after' callback gets executed if the future is not
// completed.
TEST(FutureTest, After1)
{
  Clock::pause();

  std::atomic_bool executed(false);

  Future<Nothing> future = Future<Nothing>()
    .after(Hours(42), lambda::bind(&after, &executed, lambda::_1));

  // A pending future should stay pending until 'after' is executed.
  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());

  // After advancing all the way the future should now fail because
  // the 'after' callback gets executed.
  Clock::advance(Hours(21));

  AWAIT_FAILED(future);

  EXPECT_TRUE(executed.load());

  Clock::resume();
}
示例#2
0
TEST(IO, Read)
{
  ASSERT_TRUE(GTEST_IS_THREADSAFE);

  int pipes[2];
  char data[3];

  // Create a blocking pipe.
  ASSERT_NE(-1, ::pipe(pipes));

  // Test on a blocking file descriptor.
  AWAIT_EXPECT_FAILED(io::read(pipes[0], data, 3));

  close(pipes[0]);
  close(pipes[1]);

  // Test on a closed file descriptor.
  AWAIT_EXPECT_FAILED(io::read(pipes[0], data, 3));

  // Create a nonblocking pipe.
  ASSERT_NE(-1, ::pipe(pipes));
  ASSERT_SOME(os::nonblock(pipes[0]));
  ASSERT_SOME(os::nonblock(pipes[1]));

  // Test reading nothing.
  AWAIT_EXPECT_FAILED(io::read(pipes[0], data, 0));

  // Test successful read.
  Future<size_t> future = io::read(pipes[0], data, 3);
  ASSERT_FALSE(future.isReady());

  ASSERT_EQ(2, write(pipes[1], "hi", 2));

  AWAIT_ASSERT_EQ(2u, future);
  EXPECT_EQ('h', data[0]);
  EXPECT_EQ('i', data[1]);

  // Test cancellation.
  future = io::read(pipes[0], data, 1);
  ASSERT_FALSE(future.isReady());

  future.discard();

  ASSERT_EQ(3, write(pipes[1], "omg", 3));

  AWAIT_ASSERT_EQ(3u, io::read(pipes[0], data, 3));
  EXPECT_EQ('o', data[0]);
  EXPECT_EQ('m', data[1]);
  EXPECT_EQ('g', data[2]);

  // Test read EOF.
  future = io::read(pipes[0], data, 3);
  ASSERT_FALSE(future.isReady());

  close(pipes[1]);

  AWAIT_ASSERT_EQ(0u, future);

  close(pipes[0]);
}
示例#3
0
TEST(AwaitTest, AwaitSingleDiscard)
{
  Promise<int> promise;

  auto bar = [&]() {
    return promise.future();
  };

  auto foo = [&]() {
    return await(bar())
      .then([](const Future<int>& f) {
        return f
          .then([](int i) {
            return stringify(i);
          });
      });
  };

  Future<string> future = foo();

  future.discard();

  AWAIT_DISCARDED(future);

  EXPECT_TRUE(promise.future().hasDiscard());
}
TEST(BasicMasterContenderDetectorTest, Detector)
{
  PID<Master> master;
  master.node.ip = 10000000;
  master.node.port = 10000;

  StandaloneMasterDetector detector;

  Future<Option<MasterInfo> > detected = detector.detect();

  // No one has appointed the leader so we are pending.
  EXPECT_TRUE(detected.isPending());

  // Ensure that the future can be discarded.
  detected.discard();

  AWAIT_DISCARDED(detected);

  detected = detector.detect();

  // Still no leader appointed yet.
  EXPECT_TRUE(detected.isPending());

  detector.appoint(master);

  AWAIT_READY(detected);
}
示例#5
0
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);
}
示例#6
0
TEST_F(DockerTest, ROOT_DOCKER_CancelPull)
{
  // Delete the test image if it exists.

  Try<Subprocess> s = process::subprocess(
      tests::flags.docker + " rmi lingmann/1gb",
      Subprocess::PATH("/dev/null"),
      Subprocess::PATH("/dev/null"),
      Subprocess::PATH("/dev/null"));

  ASSERT_SOME(s);

  AWAIT_READY_FOR(s.get().status(), Seconds(30));

  Owned<Docker> docker = Docker::create(
      tests::flags.docker,
      tests::flags.docker_socket,
      false).get();

  Try<string> directory = environment->mkdtemp();

  CHECK_SOME(directory) << "Failed to create temporary directory";

  // Assume that pulling the very large image 'lingmann/1gb' will take
  // sufficiently long that we can start it and discard (i.e., cancel
  // it) right away and the future will indeed get discarded.
  Future<Docker::Image> future =
    docker->pull(directory.get(), "lingmann/1gb");

  future.discard();

  AWAIT_DISCARDED(future);
}
// 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());
}
示例#8
0
// Tests that we don't propagate a discard through a `recover()` but a
// discard can still be called and propagate later.
TEST(FutureTest, RecoverDiscard)
{
  Promise<int> promise1;
  Promise<string> promise2;
  Promise<string> promise3;
  Promise<string> promise4;

  Future<string> future = promise1.future()
    .then([]() -> string {
      return "hello";
    })
    .recover([&](const Future<string>&) {
      return promise2.future()
        .then([&]() {
          return promise3.future()
            .then([&]() {
              return promise4.future();
            });
        });
    });

  future.discard();

  promise1.discard();

  EXPECT_FALSE(promise2.future().hasDiscard());

  promise2.set(string("not world"));

  EXPECT_FALSE(promise3.future().hasDiscard());

  promise3.set(string("also not world"));

  EXPECT_FALSE(promise4.future().hasDiscard());

  future.discard();

  EXPECT_TRUE(promise4.future().hasDiscard());

  promise4.set(string("world"));

  AWAIT_EQ("world", future);
}
/*
 * Class:     org_apache_mesos_state_AbstractState
 * Method:    __expunge_cancel
 * Signature: (J)Z
 */
JNIEXPORT jboolean JNICALL Java_org_apache_mesos_state_AbstractState__1_1expunge_1cancel
  (JNIEnv* env, jobject thiz, jlong jfuture)
{
  Future<bool>* future = (Future<bool>*) jfuture;

  // We'll initiate a discard but we won't consider it cancelled since
  // we don't know if/when the future will get discarded.
  future->discard();

  return (jboolean) false;
}
/*
 * Class:     org_apache_mesos_state_ZooKeeperState
 * Method:    __names_cancel
 * Signature: (J)Z
 */
JNIEXPORT jboolean JNICALL Java_org_apache_mesos_state_ZooKeeperState__1_1names_1cancel
  (JNIEnv* env, jobject thiz, jlong jfuture)
{
  Future<vector<string> >* future = (Future<vector<string> >*) jfuture;

  if (!future->isDiscarded()) {
    future->discard();
    return (jboolean) future->isDiscarded();
  }

  return (jboolean) true;
}
示例#11
0
    void timedout()
    {
        // Need to discard all of the futures so any of their associated
        // resources can get properly cleaned up.
        typename std::list<Future<T> >::const_iterator iterator;
        for (iterator = futures.begin(); iterator != futures.end(); ++iterator) {
            Future<T> future = *iterator; // Need a non-const copy to discard.
            future.discard();
        }

        promise->fail("Collect failed: timed out");
        terminate(this);
    }
示例#12
0
TEST(FutureTest, UndiscardableFuture)
{
  Promise<int> promise;

  Future<int> f = undiscardable(promise.future());

  f.discard();

  EXPECT_TRUE(f.hasDiscard());
  EXPECT_FALSE(promise.future().hasDiscard());

  promise.set(42);

  AWAIT_ASSERT_EQ(42, f);
}
示例#13
0
TEST(CollectTest, DiscardPropagation)
{
  Future<int> future1;
  Future<bool> future2;

  future1
    .onDiscard([=](){ process::internal::discarded(future1); });
  future2
    .onDiscard([=](){ process::internal::discarded(future2); });

  Future<std::tuple<int, bool>> collect = process::collect(future1, future2);

  collect.discard();

  AWAIT_DISCARDED(future1);
  AWAIT_DISCARDED(future2);
}
示例#14
0
TEST(AwaitTest, DiscardPropagation)
{
  Future<int> future1;
  Future<bool> future2;

  future1
    .onDiscard([=](){ process::internal::discarded(future1); });
  future2
    .onDiscard([=](){ process::internal::discarded(future2); });

  Future<std::tuple<Future<int>, Future<bool>>> await =
    process::await(future1, future2);

  await.discard();

  AWAIT_DISCARDED(future1);
  AWAIT_DISCARDED(future2);
}
示例#15
0
TEST(FutureTest, UndiscardableLambda)
{
  Promise<int> promise;

  Future<int> f = Future<int>(2)
    .then(undiscardable([&](int multiplier) {
      return promise.future()
        .then([=](int i) {
          return i * multiplier;
        });
    }));

  f.discard();

  EXPECT_TRUE(f.hasDiscard());
  EXPECT_FALSE(promise.future().hasDiscard());

  promise.set(42);

  AWAIT_ASSERT_EQ(84, f);
}
示例#16
0
TEST(LoopTest, DiscardIterate)
{
  Promise<int> promise;

  promise.future().onDiscard([&]() { promise.discard(); });

  Future<Nothing> future = loop(
      [&]() {
        return promise.future();
      },
      [&](int i) -> ControlFlow<Nothing> {
        return Break();
      });

  EXPECT_TRUE(future.isPending());

  future.discard();

  AWAIT_DISCARDED(future);
  EXPECT_TRUE(promise.future().hasDiscard());
}
示例#17
0
TEST(AwaitTest, DiscardPropagation)
{
  Promise<int> promise1;
  Promise<bool> promise2;

  promise1.future()
    .onDiscard([&](){ promise1.discard(); });
  promise2.future()
    .onDiscard([&](){ promise2.discard(); });

  Future<std::tuple<Future<int>, Future<bool>>> await = process::await(
      promise1.future(),
      promise2.future());

  await.discard();

  AWAIT_DISCARDED(await);

  AWAIT_DISCARDED(promise1.future());
  AWAIT_DISCARDED(promise2.future());
}
示例#18
0
TEST(CollectTest, DiscardPropagation)
{
  Promise<int> promise1;
  Promise<bool> promise2;

  promise1.future()
    .onDiscard([&](){ promise1.discard(); });
  promise2.future()
    .onDiscard([&](){ promise2.discard(); });

  Future<std::tuple<int, bool>> collect = process::collect(
      promise1.future(),
      promise2.future());

  collect.discard();

  AWAIT_DISCARDED(collect);

  AWAIT_DISCARDED(promise1.future());
  AWAIT_DISCARDED(promise2.future());
}
示例#19
0
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]));
}
示例#20
0
// 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());
}
示例#21
0
// 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());
}
示例#22
0
// 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();
}
示例#23
0
Future<Response> RegistryClientProcess::doHttpGet(
    const URL& url,
    const Option<process::http::Headers>& headers,
    const Duration& timeout,
    bool resend,
    const Option<string>& lastResponseStatus) const
{
  return process::http::get(url, headers)
    .after(timeout, [](
        const Future<Response>& httpResponseFuture) -> Future<Response> {
      return Failure("Response timeout");
    })
    .then(defer(self(), [=](
        const Response& httpResponse) -> Future<Response> {
      VLOG(1) << "Response status: " + httpResponse.status;

      // Set the future if we get a OK response.
      if (httpResponse.status == "200 OK") {
        return httpResponse;
      } else if (httpResponse.status == "400 Bad Request") {
        Try<JSON::Object> errorResponse =
          JSON::parse<JSON::Object>(httpResponse.body);

        if (errorResponse.isError()) {
          return Failure("Failed to parse bad request response JSON: " +
                         errorResponse.error());
        }

        std::ostringstream out;
        bool first = true;
        Result<JSON::Array> errorObjects =
          errorResponse.get().find<JSON::Array>("errors");

        if (errorObjects.isError()) {
          return Failure("Failed to find 'errors' in bad request response: " +
                         errorObjects.error());
        } else if (errorObjects.isNone()) {
          return Failure("Errors not found in bad request response");
        }

        foreach (const JSON::Value& error, errorObjects.get().values) {
          Result<JSON::String> message =
            error.as<JSON::Object>().find<JSON::String>("message");
          if (message.isError()) {
            return Failure("Failed to parse bad request error message: " +
                           message.error());
          } else if (message.isNone()) {
            continue;
          }

          if (first) {
            out << message.get().value;
            first = false;
          } else {
            out << ", " << message.get().value;
          }
        }
        return Failure("Received Bad request, errors: [" + out.str() + "]");
      }

      // Prevent infinite recursion.
      if (lastResponseStatus.isSome() &&
          (lastResponseStatus.get() == httpResponse.status)) {
        return Failure("Invalid response: " + httpResponse.status);
      }

      // If resend is not set, we dont try again and stop here.
      if (!resend) {
        return Failure("Bad response: " + httpResponse.status);
      }

      // Handle 401 Unauthorized.
      if (httpResponse.status == "401 Unauthorized") {
        Try<process::http::Headers> authAttributes =
          getAuthenticationAttributes(httpResponse);

        if (authAttributes.isError()) {
          return Failure(
              "Failed to get authentication attributes: " +
              authAttributes.error());
        }

        // TODO(jojy): Currently only handling TLS/cert authentication.
        Future<Token> tokenResponse = tokenManager_->getToken(
          authAttributes.get().at("service"),
          authAttributes.get().at("scope"),
          None());

        return tokenResponse
          .after(timeout, [=](
              Future<Token> tokenResponse) -> Future<Token> {
            tokenResponse.discard();
            return Failure("Token response timeout");
          })
          .then(defer(self(), [=](
              const Future<Token>& tokenResponse) {
            // Send request with acquired token.
            process::http::Headers authHeaders = {
              {"Authorization", "Bearer " + tokenResponse.get().raw}
            };

            return doHttpGet(
                url,
                authHeaders,
                timeout,
                true,
                httpResponse.status);
        }));
      } else if (httpResponse.status == "307 Temporary Redirect") {
        // Handle redirect.

        // TODO(jojy): Add redirect functionality in http::get.

        auto toURL = [](
            const string& urlString) -> Try<URL> {
          // TODO(jojy): Need to add functionality to URL class that parses a
          // string to its URL components. For now, assuming:
          //  - scheme is https
          //  - path always ends with /

          static const string schemePrefix = "https://";

          if (!strings::contains(urlString, schemePrefix)) {
            return Error(
                "Failed to find expected token '" + schemePrefix +
                "' in redirect url");
          }

          const string schemeSuffix = urlString.substr(schemePrefix.length());

          const vector<string> components =
            strings::tokenize(schemeSuffix, "/");

          const string path = schemeSuffix.substr(components[0].length());

          const vector<string> addrComponents =
            strings::tokenize(components[0], ":");

          uint16_t port = DEFAULT_SSL_PORT;
          string domain = components[0];

          // Parse the port.
          if (addrComponents.size() == 2) {
            domain = addrComponents[0];

            Try<uint16_t> tryPort = numify<uint16_t>(addrComponents[1]);
            if (tryPort.isError()) {
              return Error(
                  "Failed to parse location: " + urlString + " for port.");
            }

            port = tryPort.get();
          }

          return URL("https", domain, port, path);
        };

        if (httpResponse.headers.find("Location") ==
            httpResponse.headers.end()) {
          return Failure(
              "Invalid redirect response: 'Location' not found in headers.");
        }

        const string& location = httpResponse.headers.at("Location");
        Try<URL> tryUrl = toURL(location);
        if (tryUrl.isError()) {
          return Failure(
              "Failed to parse '" + location + "': " + tryUrl.error());
        }

        return doHttpGet(
            tryUrl.get(),
            headers,
            timeout,
            false,
            httpResponse.status);
      } else {
        return Failure("Invalid response: " + httpResponse.status);
      }
    }));
示例#24
0
Future<Option<Variable<Slaves> > > timeout(
    Future<Option<Variable<Slaves> > > future)
{
  future.discard();
  return Failure("Timeout");
}
示例#25
0
TEST(IOTest, Peek)
{
  ASSERT_TRUE(GTEST_IS_THREADSAFE);

  int sockets[2];
  int pipes[2];
  char data[3] = {};

  // Create a blocking socketpair.
  ASSERT_NE(-1, ::socketpair(PF_LOCAL, SOCK_STREAM, 0, sockets));

  // Test on closed socket.
  ASSERT_SOME(os::close(sockets[0]));
  ASSERT_SOME(os::close(sockets[1]));
  AWAIT_EXPECT_FAILED(io::peek(sockets[0], data, sizeof(data), sizeof(data)));

  // Test on pipe.
  ASSERT_NE(-1, ::pipe(pipes));
  AWAIT_EXPECT_FAILED(io::peek(pipes[0], data, sizeof(data), sizeof(data)));

  ASSERT_SOME(os::close(pipes[0]));
  ASSERT_SOME(os::close(pipes[1]));

  // Create a non-blocking socketpair.
  ASSERT_NE(-1, ::socketpair(PF_LOCAL, SOCK_STREAM, 0, sockets));
  ASSERT_SOME(os::nonblock(sockets[0]));
  ASSERT_SOME(os::nonblock(sockets[1]));

  // Test peeking nothing.
  AWAIT_EXPECT_EQ(0, io::peek(sockets[0], data, 0, 0));

  // Test discarded peek.
  Future<size_t> future = io::peek(sockets[0], data, sizeof(data), 1);
  EXPECT_TRUE(future.isPending());
  future.discard();
  AWAIT_DISCARDED(future);

  // Test successful peek.
  future = io::peek(sockets[0], data, sizeof(data), 2);
  ASSERT_FALSE(future.isReady());

  ASSERT_EQ(2, write(sockets[1], "hi", 2));

  AWAIT_ASSERT_EQ(2u, future);
  EXPECT_EQ('h', data[0]);
  EXPECT_EQ('i', data[1]);

  // Discard what was read before and peek again.
  memset(data, 0, sizeof(data));

  future = io::peek(sockets[0], data, sizeof(data), 2);
  ASSERT_TRUE(future.isReady());

  AWAIT_ASSERT_EQ(2u, future);
  EXPECT_EQ('h', data[0]);
  EXPECT_EQ('i', data[1]);

  // Discard what was read before and now io::read.
  memset(data, 0, sizeof(data));

  future = io::read(sockets[0], data, sizeof(data));
  ASSERT_TRUE(future.isReady());

  AWAIT_ASSERT_EQ(2u, future);
  EXPECT_EQ('h', data[0]);
  EXPECT_EQ('i', data[1]);

  // Test read EOF.
  future = io::peek(sockets[0], data, sizeof(data), 2);
  ASSERT_FALSE(future.isReady());

  ASSERT_SOME(os::close(sockets[1]));

  AWAIT_ASSERT_EQ(0u, future);

  ASSERT_SOME(os::close(sockets[0]));

  // Test the auxiliary interface.
  ASSERT_NE(-1, ::socketpair(PF_LOCAL, SOCK_STREAM, 0, sockets));
  ASSERT_SOME(os::nonblock(sockets[0]));
  ASSERT_SOME(os::nonblock(sockets[1]));

  // Test exceeding read buffer size limit.
  AWAIT_EXPECT_FAILED(io::peek(sockets[0], io::BUFFERED_READ_SIZE + 1));

  // The function should return after reading some data (not
  // necessarily as much as we expect). We test that by writing less
  // than we expect to read.
  Future<string> result = io::peek(sockets[0], 4);
  EXPECT_TRUE(result.isPending());

  ASSERT_EQ(2, write(sockets[1], "Hi", 2));
  AWAIT_ASSERT_EQ("Hi", result);

  ASSERT_SOME(os::close(sockets[0]));
  ASSERT_SOME(os::close(sockets[1]));
}