From 68be4ccd331bafca6967303430651f277a0e7a14 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Fri, 3 Aug 2018 15:03:26 -0400 Subject: SERVER-36384 Split up `future_test.cpp`. Because this file instantiates a great deal of templates, it consumes a lot of CPU to compile in a single compiler execution. By splitting this file up, we can improve build times for this test by a great deal. --- src/mongo/util/future_test_future_void.cpp | 538 +++++++++++++++++++++++++++++ 1 file changed, 538 insertions(+) create mode 100644 src/mongo/util/future_test_future_void.cpp (limited to 'src/mongo/util/future_test_future_void.cpp') diff --git a/src/mongo/util/future_test_future_void.cpp b/src/mongo/util/future_test_future_void.cpp new file mode 100644 index 00000000000..f179d2bab6c --- /dev/null +++ b/src/mongo/util/future_test_future_void.cpp @@ -0,0 +1,538 @@ +/** + * Copyright (C) 2018 MongoDB Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + * As a special exception, the copyright holders give permission to link the + * code of portions of this program with the OpenSSL library under certain + * conditions as described in each individual source file and distribute + * linked combinations including the program with the OpenSSL library. You + * must comply with the GNU Affero General Public License in all respects for + * all of the code used other than as permitted herein. If you modify file(s) + * with this exception, you may extend this exception to your version of the + * file(s), but you are not obligated to do so. If you do not wish to do so, + * delete this exception statement from your version. If you delete this + * exception statement from all source files in the program, then also delete + * it in the license file. + */ + +#include "mongo/platform/basic.h" + +#include "mongo/util/future.h" + +#include "mongo/stdx/thread.h" +#include "mongo/unittest/death_test.h" +#include "mongo/unittest/unittest.h" + +#include "mongo/util/future_test_utils.h" + +namespace mongo { +namespace { + +TEST(Future_Void, Success_getLvalue) { + FUTURE_SUCCESS_TEST([] {}, [](Future&& fut) { fut.get(); }); +} + +TEST(Future_Void, Success_getConstLvalue) { + FUTURE_SUCCESS_TEST([] {}, [](const Future& fut) { fut.get(); }); +} + +TEST(Future_Void, Success_getRvalue) { + FUTURE_SUCCESS_TEST([] {}, [](Future&& fut) { std::move(fut).get(); }); +} + +TEST(Future_Void, Success_getNothrowLvalue) { + FUTURE_SUCCESS_TEST([] {}, + [](Future&& fut) { ASSERT_EQ(fut.getNoThrow(), Status::OK()); }); +} + +TEST(Future_Void, Success_getNothrowConstLvalue) { + FUTURE_SUCCESS_TEST([] {}, + [](const Future& fut) { ASSERT_EQ(fut.getNoThrow(), Status::OK()); }); +} + +TEST(Future_Void, Success_getNothrowRvalue) { + FUTURE_SUCCESS_TEST( + [] {}, [](Future&& fut) { ASSERT_EQ(std::move(fut).getNoThrow(), Status::OK()); }); +} + +TEST(Future_Void, Success_getAsync) { + FUTURE_SUCCESS_TEST( + [] {}, + [](Future&& fut) { + auto pf = makePromiseFuture(); + std::move(fut).getAsync([outside = pf.promise.share()](Status status) mutable { + ASSERT_OK(status); + outside.emplaceValue(); + }); + ASSERT_EQ(std::move(pf.future).getNoThrow(), Status::OK()); + }); +} + +TEST(Future_Void, Fail_getLvalue) { + FUTURE_FAIL_TEST([](Future&& fut) { ASSERT_THROWS_failStatus(fut.get()); }); +} + +TEST(Future_Void, Fail_getConstLvalue) { + FUTURE_FAIL_TEST([](const Future& fut) { ASSERT_THROWS_failStatus(fut.get()); }); +} + +TEST(Future_Void, Fail_getRvalue) { + FUTURE_FAIL_TEST( + [](Future&& fut) { ASSERT_THROWS_failStatus(std::move(fut).get()); }); +} + +TEST(Future_Void, Fail_getNothrowLvalue) { + FUTURE_FAIL_TEST([](Future&& fut) { ASSERT_EQ(fut.getNoThrow(), failStatus()); }); +} + +TEST(Future_Void, Fail_getNothrowConstLvalue) { + FUTURE_FAIL_TEST( + [](const Future& fut) { ASSERT_EQ(fut.getNoThrow(), failStatus()); }); +} + +TEST(Future_Void, Fail_getNothrowRvalue) { + FUTURE_FAIL_TEST( + [](Future&& fut) { ASSERT_EQ(std::move(fut).getNoThrow(), failStatus()); }); +} + +TEST(Future_Void, Fail_getAsync) { + FUTURE_FAIL_TEST([](Future&& fut) { + auto pf = makePromiseFuture(); + std::move(fut).getAsync([outside = pf.promise.share()](Status status) mutable { + ASSERT(!status.isOK()); + outside.setError(status); + }); + ASSERT_EQ(std::move(pf.future).getNoThrow(), failStatus()); + }); +} + +TEST(Future_Void, Success_isReady) { + FUTURE_SUCCESS_TEST([] {}, + [](Future&& fut) { + const auto id = stdx::this_thread::get_id(); + while (!fut.isReady()) { + } + std::move(fut).getAsync([&](Status status) { + ASSERT_EQ(stdx::this_thread::get_id(), id); + ASSERT_OK(status); + }); + + }); +} + +TEST(Future_Void, Fail_isReady) { + FUTURE_FAIL_TEST([](Future&& fut) { + const auto id = stdx::this_thread::get_id(); + while (!fut.isReady()) { + } + std::move(fut).getAsync([&](Status status) { + ASSERT_EQ(stdx::this_thread::get_id(), id); + ASSERT_NOT_OK(status); + }); + + }); +} + +TEST(Future_Void, isReady_TSAN_OK) { + bool done = false; + auto fut = async([&] { done = true; }); + while (!fut.isReady()) { + } + // ASSERT(done); // Data Race! Uncomment to make sure TSAN is working. + fut.get(); + ASSERT(done); +} + +TEST(Future_Void, Success_thenSimple) { + FUTURE_SUCCESS_TEST( + [] {}, + [](Future&& fut) { ASSERT_EQ(std::move(fut).then([]() { return 3; }).get(), 3); }); +} + +TEST(Future_Void, Success_thenVoid) { + FUTURE_SUCCESS_TEST([] {}, + [](Future&& fut) { + ASSERT_EQ(std::move(fut).then([] {}).then([] { return 3; }).get(), 3); + }); +} + +TEST(Future_Void, Success_thenStatus) { + FUTURE_SUCCESS_TEST([] {}, + [](Future&& fut) { + ASSERT_EQ(std::move(fut).then([] {}).then([] { return 3; }).get(), 3); + }); +} + +TEST(Future_Void, Success_thenError_Status) { + FUTURE_SUCCESS_TEST([] {}, + [](Future&& fut) { + auto fut2 = std::move(fut).then( + []() { return Status(ErrorCodes::BadValue, "oh no!"); }); + MONGO_STATIC_ASSERT(std::is_same>::value); + ASSERT_EQ(fut2.getNoThrow(), ErrorCodes::BadValue); + }); +} + +TEST(Future_Void, Success_thenError_StatusWith) { + FUTURE_SUCCESS_TEST( + [] {}, + [](Future&& fut) { + auto fut2 = std::move(fut).then( + []() { return StatusWith(ErrorCodes::BadValue, "oh no!"); }); + MONGO_STATIC_ASSERT(std::is_same>::value); + ASSERT_EQ(fut2.getNoThrow(), ErrorCodes::BadValue); + }); +} + +TEST(Future_Void, Success_thenFutureImmediate) { + FUTURE_SUCCESS_TEST( + [] {}, + [](Future&& fut) { + ASSERT_EQ(std::move(fut).then([]() { return Future::makeReady(3); }).get(), 3); + }); +} + +TEST(Future_Void, Success_thenFutureReady) { + FUTURE_SUCCESS_TEST([] {}, + [](Future&& fut) { + ASSERT_EQ(std::move(fut) + .then([]() { + auto pf = makePromiseFuture(); + pf.promise.emplaceValue(3); + return std::move(pf.future); + }) + .get(), + 3); + }); +} + +TEST(Future_Void, Success_thenFutureAsync) { + FUTURE_SUCCESS_TEST( + [] {}, + [](Future&& fut) { + ASSERT_EQ(std::move(fut).then([]() { return async([] { return 3; }); }).get(), 3); + }); +} + +TEST(Future_Void, Fail_thenSimple) { + FUTURE_FAIL_TEST([](Future&& fut) { + ASSERT_EQ(std::move(fut) + .then([]() { + FAIL("then() callback was called"); + return int(); + }) + .getNoThrow(), + failStatus()); + }); +} + +TEST(Future_Void, Fail_thenFutureAsync) { + FUTURE_FAIL_TEST([](Future&& fut) { + ASSERT_EQ(std::move(fut) + .then([]() { + FAIL("then() callback was called"); + return Future(); + }) + .getNoThrow(), + failStatus()); + }); +} + +TEST(Future_Void, Success_onErrorSimple) { + FUTURE_SUCCESS_TEST([] {}, + [](Future&& fut) { + ASSERT_EQ( + std::move(fut) + .onError([](Status) { FAIL("onError() callback was called"); }) + .then([] { return 3; }) + .get(), + 3); + }); +} + +TEST(Future_Void, Success_onErrorFutureAsync) { + FUTURE_SUCCESS_TEST([] {}, + [](Future&& fut) { + ASSERT_EQ(std::move(fut) + .onError([](Status) { + FAIL("onError() callback was called"); + return Future(); + }) + .then([] { return 3; }) + .get(), + 3); + }); +} + +TEST(Future_Void, Fail_onErrorSimple) { + FUTURE_FAIL_TEST([](Future&& fut) { + ASSERT_EQ(std::move(fut) + .onError([](Status s) { ASSERT_EQ(s, failStatus()); }) + .then([] { return 3; }) + .getNoThrow(), + 3); + }); +} + +TEST(Future_Void, Fail_onErrorError_throw) { + FUTURE_FAIL_TEST([](Future&& fut) { + auto fut2 = std::move(fut).onError([](Status s) { + ASSERT_EQ(s, failStatus()); + uasserted(ErrorCodes::BadValue, "oh no!"); + }); + ASSERT_EQ(fut2.getNoThrow(), ErrorCodes::BadValue); + }); +} + +TEST(Future_Void, Fail_onErrorError_Status) { + FUTURE_FAIL_TEST([](Future&& fut) { + auto fut2 = std::move(fut).onError([](Status s) { + ASSERT_EQ(s, failStatus()); + return Status(ErrorCodes::BadValue, "oh no!"); + }); + ASSERT_EQ(fut2.getNoThrow(), ErrorCodes::BadValue); + }); +} + +TEST(Future_Void, Fail_onErrorFutureImmediate) { + FUTURE_FAIL_TEST([](Future&& fut) { + ASSERT_EQ(std::move(fut) + .onError([](Status s) { + ASSERT_EQ(s, failStatus()); + return Future::makeReady(); + }) + .then([] { return 3; }) + .get(), + 3); + }); +} + +TEST(Future_Void, Fail_onErrorFutureReady) { + FUTURE_FAIL_TEST([](Future&& fut) { + ASSERT_EQ(std::move(fut) + .onError([](Status s) { + ASSERT_EQ(s, failStatus()); + auto pf = makePromiseFuture(); + pf.promise.emplaceValue(); + return std::move(pf.future); + }) + .then([] { return 3; }) + .get(), + 3); + }); +} + +TEST(Future_Void, Fail_onErrorFutureAsync) { + FUTURE_FAIL_TEST([](Future&& fut) { + ASSERT_EQ(std::move(fut) + .onError([&](Status s) { + ASSERT_EQ(s, failStatus()); + return async([] {}); + }) + .then([] { return 3; }) + .get(), + 3); + }); +} + +TEST(Future_Void, Success_onErrorCode) { + FUTURE_SUCCESS_TEST([] {}, + [](Future&& fut) { + ASSERT_EQ(std::move(fut) + .onError([](Status) { + FAIL("onError() callback was called"); + }) + .then([] { return 3; }) + .get(), + 3); + }); +} + +TEST(Future_Void, Fail_onErrorCodeMatch) { + FUTURE_FAIL_TEST([](Future&& fut) { + bool called = false; + auto res = std::move(fut) + .onError([](Status s) { + ASSERT_EQ(s, failStatus()); + return Status(ErrorCodes::InternalError, ""); + }) + .onError([&](Status&&) { called = true; }) + .then([] { return 3; }) + .getNoThrow(); + ASSERT_EQ(res, 3); + ASSERT(called); + }); +} + +TEST(Future_Void, Fail_onErrorCodeMatchFuture) { + FUTURE_FAIL_TEST([](Future&& fut) { + bool called = false; + auto res = std::move(fut) + .onError([](Status s) { + ASSERT_EQ(s, failStatus()); + return Status(ErrorCodes::InternalError, ""); + }) + .onError([&](Status&&) { + called = true; + return Future::makeReady(); + }) + .then([] { return 3; }) + .getNoThrow(); + ASSERT_EQ(res, 3); + ASSERT(called); + }); +} + +TEST(Future_Void, Fail_onErrorCodeMismatch) { + FUTURE_FAIL_TEST([](Future&& fut) { + ASSERT_EQ(std::move(fut) + .onError( + [](Status s) { FAIL("Why was this called?") << s; }) + .onError([](Status s) { ASSERT_EQ(s, failStatus()); }) + .then([] { return 3; }) + .getNoThrow(), + 3); + }); +} + +TEST(Future_Void, Success_tap) { + FUTURE_SUCCESS_TEST( + [] {}, + [](Future&& fut) { + bool tapCalled = false; + ASSERT_EQ( + std::move(fut).tap([&tapCalled] { tapCalled = true; }).then([] { return 3; }).get(), + 3); + ASSERT(tapCalled); + }); +} + +TEST(Future_Void, Success_tapError) { + FUTURE_SUCCESS_TEST( + [] {}, + [](Future&& fut) { + ASSERT_EQ(std::move(fut) + .tapError([](Status s) { FAIL("tapError() callback was called"); }) + .then([] { return 3; }) + .get(), + 3); + }); +} + +TEST(Future_Void, Success_tapAll_StatusWith) { + FUTURE_SUCCESS_TEST([] {}, + [](Future&& fut) { + bool tapCalled = false; + ASSERT_EQ(std::move(fut) + .tapAll([&tapCalled](Status s) { + ASSERT_OK(s); + tapCalled = true; + }) + .then([] { return 3; }) + .get(), + 3); + ASSERT(tapCalled); + }); +} + +TEST(Future_Void, Success_tapAll_Overloaded) { + FUTURE_SUCCESS_TEST( + [] {}, + [](Future&& fut) { + struct Callback { + void operator()() { + called = true; + } + void operator()(Status status) { + FAIL("Status overload called with ") << status; + } + bool called = false; + }; + Callback callback; + + ASSERT_EQ(std::move(fut).tapAll(std::ref(callback)).then([] { return 3; }).get(), 3); + ASSERT(callback.called); + }); +} + +TEST(Future_Void, Fail_tap) { + FUTURE_FAIL_TEST([](Future&& fut) { + ASSERT_EQ(std::move(fut) + .tap([] { FAIL("tap() callback was called"); }) + .onError([](Status s) { ASSERT_EQ(s, failStatus()); }) + .then([] { return 3; }) + .get(), + 3); + }); +} + +TEST(Future_Void, Fail_tapError) { + FUTURE_FAIL_TEST([](Future&& fut) { + bool tapCalled = false; + ASSERT_EQ(std::move(fut) + .tapError([&tapCalled](Status s) { + ASSERT_EQ(s, failStatus()); + tapCalled = true; + }) + .onError([](Status s) { ASSERT_EQ(s, failStatus()); }) + .then([] { return 3; }) + .get(), + 3); + ASSERT(tapCalled); + }); +} + +TEST(Future_Void, Fail_tapAll_StatusWith) { + FUTURE_FAIL_TEST([](Future&& fut) { + bool tapCalled = false; + ASSERT_EQ(std::move(fut) + .tapAll([&tapCalled](StatusWith sw) { + ASSERT_EQ(sw.getStatus(), failStatus()); + tapCalled = true; + }) + .onError([](Status s) { ASSERT_EQ(s, failStatus()); }) + .then([] { return 3; }) + .get(), + 3); + ASSERT(tapCalled); + }); +} + +TEST(Future_Void, Fail_tapAll_Overloaded) { + FUTURE_FAIL_TEST([](Future&& fut) { + struct Callback { + void operator()() { + FAIL("() overload called"); + } + void operator()(Status status) { + ASSERT_EQ(status, failStatus()); + called = true; + } + bool called = false; + }; + Callback callback; + + ASSERT_EQ(std::move(fut) + .tapAll(std::ref(callback)) + .onError([](Status s) { ASSERT_EQ(s, failStatus()); }) + .then([] { return 3; }) + .get(), + 3); + + ASSERT(callback.called); + }); +} + +} // namespace +} // namespace mongo -- cgit v1.2.1