diff options
Diffstat (limited to 'src/mongo/util/future_test_future_move_only.cpp')
-rw-r--r-- | src/mongo/util/future_test_future_move_only.cpp | 544 |
1 files changed, 544 insertions, 0 deletions
diff --git a/src/mongo/util/future_test_future_move_only.cpp b/src/mongo/util/future_test_future_move_only.cpp new file mode 100644 index 00000000000..7c38a739901 --- /dev/null +++ b/src/mongo/util/future_test_future_move_only.cpp @@ -0,0 +1,544 @@ +/** + * 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 <http://www.gnu.org/licenses/>. + * + * 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 { + +// A move-only type that isn't default constructible. It has binary ops with int to make it easier +// to have a common format with the above tests. +struct Widget { + explicit Widget(int val) : val(val) {} + + Widget(Widget&& other) : Widget(other.val) {} + Widget& operator=(Widget&& other) { + val = other.val; + return *this; + } + + Widget() = delete; + Widget(const Widget&) = delete; + Widget& operator=(const Widget&) = delete; + + Widget operator+(int i) const { + return Widget(val + i); + } + + bool operator==(int i) const { + return val == i; + } + + bool operator==(Widget w) const { + return val == w.val; + } + + int val; +}; + +std::ostream& operator<<(std::ostream& stream, const Widget& widget) { + return stream << "Widget(" << widget.val << ')'; +} + +TEST(Future_MoveOnly, Success_getLvalue) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { ASSERT_EQ(fut.get(), 1); }); +} + +TEST(Future_MoveOnly, Success_getConstLvalue) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](const Future<Widget>& fut) { ASSERT_EQ(fut.get(), 1); }); +} + +TEST(Future_MoveOnly, Success_getRvalue) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { ASSERT_EQ(std::move(fut).get(), 1); }); +} + +#if 0 // Needs copy +TEST(Future_MoveOnly, Success_getNothrowLvalue) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { ASSERT_EQ(fut.getNoThrow(), 1); }); +} + +TEST(Future_MoveOnly, Success_getNothrowConstLvalue) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](const Future<Widget>& fut) { ASSERT_EQ(fut.getNoThrow(), 1); }); +} +#endif + +TEST(Future_MoveOnly, Success_getNothrowRvalue) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(uassertStatusOK(std::move(fut).getNoThrow()).val, 1); + }); +} + +TEST(Future_MoveOnly, Success_getAsync) { + FUTURE_SUCCESS_TEST( + [] { return Widget(1); }, + [](Future<Widget>&& fut) { + auto pf = makePromiseFuture<Widget>(); + std::move(fut).getAsync([outside = pf.promise.share()](StatusWith<Widget> sw) mutable { + ASSERT_OK(sw); + outside.emplaceValue(std::move(sw.getValue())); + }); + ASSERT_EQ(std::move(pf.future).get(), 1); + }); +} + +TEST(Future_MoveOnly, Fail_getLvalue) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { ASSERT_THROWS_failStatus(fut.get()); }); +} + +TEST(Future_MoveOnly, Fail_getConstLvalue) { + FUTURE_FAIL_TEST<Widget>( + [](const Future<Widget>& fut) { ASSERT_THROWS_failStatus(fut.get()); }); +} + +TEST(Future_MoveOnly, Fail_getRvalue) { + FUTURE_FAIL_TEST<Widget>( + [](Future<Widget>&& fut) { ASSERT_THROWS_failStatus(std::move(fut).get()); }); +} + +#if 0 // Needs copy +TEST(Future_MoveOnly, Fail_getNothrowLvalue) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { ASSERT_EQ(fut.getNoThrow(), failStatus); }); +} + +TEST(Future_MoveOnly, Fail_getNothrowConstLvalue) { + FUTURE_FAIL_TEST<Widget>( + [](const Future<Widget>& fut) { ASSERT_EQ(fut.getNoThrow(), failStatus); }); +} +#endif + +TEST(Future_MoveOnly, Fail_getNothrowRvalue) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut).getNoThrow().getStatus(), failStatus()); + }); +} + +TEST(Future_MoveOnly, Fail_getAsync) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + auto pf = makePromiseFuture<Widget>(); + std::move(fut).getAsync([outside = pf.promise.share()](StatusWith<Widget> sw) mutable { + ASSERT(!sw.isOK()); + outside.setError(sw.getStatus()); + }); + ASSERT_EQ(std::move(pf.future).getNoThrow(), failStatus()); + }); +} + +TEST(Future_MoveOnly, Success_thenSimple) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut).then([](Widget i) { return i + 2; }).get(), 3); + }); +} + +TEST(Future_MoveOnly, Success_thenSimpleAuto) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut).then([](auto&& i) { return i + 2; }).get(), 3); + }); +} + +TEST(Future_MoveOnly, Success_thenVoid) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .then([](Widget i) { ASSERT_EQ(i, 1); }) + .then([] { return Widget(3); }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Success_thenStatus) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .then([](Widget i) { + ASSERT_EQ(i, 1); + return Status::OK(); + }) + .then([] { return Widget(3); }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Success_thenError_Status) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + auto fut2 = std::move(fut).then( + [](Widget i) { return Status(ErrorCodes::BadValue, "oh no!"); }); + MONGO_STATIC_ASSERT(std::is_same<decltype(fut2), Future<void>>::value); + ASSERT_EQ(fut2.getNoThrow(), ErrorCodes::BadValue); + }); +} + +TEST(Future_MoveOnly, Success_thenError_StatusWith) { + FUTURE_SUCCESS_TEST( + [] { return Widget(1); }, + [](Future<Widget>&& fut) { + auto fut2 = std::move(fut).then( + [](Widget i) { return StatusWith<double>(ErrorCodes::BadValue, "oh no!"); }); + MONGO_STATIC_ASSERT(std::is_same<decltype(fut2), Future<double>>::value); + ASSERT_EQ(fut2.getNoThrow(), ErrorCodes::BadValue); + }); +} + +TEST(Future_MoveOnly, Success_thenFutureImmediate) { + FUTURE_SUCCESS_TEST( + [] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .then([](Widget i) { return Future<Widget>::makeReady(Widget(i + 2)); }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Success_thenFutureReady) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .then([](Widget i) { + auto pf = makePromiseFuture<Widget>(); + pf.promise.emplaceValue(i + 2); + return std::move(pf.future); + }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Success_thenFutureAsync) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .then([&](Widget i) { + return async([i = i.val] { return Widget(i + 2); }); + }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Success_thenFutureAsyncThrow) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .then([](Widget i) { + uasserted(ErrorCodes::BadValue, "oh no!"); + return Future<Widget>(); + }) + .getNoThrow(), + ErrorCodes::BadValue); + }); +} + +TEST(Future_MoveOnly, Fail_thenSimple) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .then([](Widget i) { + FAIL("then() callback was called"); + return Widget(0); + }) + .getNoThrow(), + failStatus()); + }); +} + +TEST(Future_MoveOnly, Fail_thenFutureAsync) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .then([](Widget i) { + FAIL("then() callback was called"); + return Future<Widget>(); + }) + .getNoThrow(), + failStatus()); + }); +} + +TEST(Future_MoveOnly, Success_onErrorSimple) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .onError([](Status) { + FAIL("onError() callback was called"); + return Widget(0); + }) + .then([](Widget i) { return i + 2; }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Success_onErrorFutureAsync) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .onError([](Status) { + FAIL("onError() callback was called"); + return Future<Widget>(); + }) + .then([](Widget i) { return i + 2; }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Fail_onErrorSimple) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + ASSERT_EQ(uassertStatusOK(std::move(fut) + .onError([](Status s) { + ASSERT_EQ(s, failStatus()); + return Widget(3); + }) + .getNoThrow()), + 3); + }); +} +TEST(Future_MoveOnly, Fail_onErrorError_throw) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + auto fut2 = std::move(fut).onError([](Status s) -> Widget { + ASSERT_EQ(s, failStatus()); + uasserted(ErrorCodes::BadValue, "oh no!"); + }); + ASSERT_EQ(std::move(fut2).getNoThrow(), ErrorCodes::BadValue); + }); +} + +TEST(Future_MoveOnly, Fail_onErrorError_StatusWith) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + auto fut2 = std::move(fut).onError([](Status s) { + ASSERT_EQ(s, failStatus()); + return StatusWith<Widget>(ErrorCodes::BadValue, "oh no!"); + }); + ASSERT_EQ(std::move(fut2).getNoThrow(), ErrorCodes::BadValue); + }); +} + +TEST(Future_MoveOnly, Fail_onErrorFutureImmediate) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .onError([](Status s) { + ASSERT_EQ(s, failStatus()); + return Future<Widget>::makeReady(Widget(3)); + }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Fail_onErrorFutureReady) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .onError([](Status s) { + ASSERT_EQ(s, failStatus()); + auto pf = makePromiseFuture<Widget>(); + pf.promise.emplaceValue(3); + return std::move(pf.future); + }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Fail_onErrorFutureAsync) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .onError([&](Status s) { + ASSERT_EQ(s, failStatus()); + return async([] { return Widget(3); }); + }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Success_tap) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + bool tapCalled = false; + ASSERT_EQ(std::move(fut) + .tap([&tapCalled](const Widget& i) { + ASSERT_EQ(i, 1); + tapCalled = true; + }) + .then([](Widget i) { return i + 2; }) + .get(), + 3); + ASSERT(tapCalled); + }); +} + +TEST(Future_MoveOnly, Success_tapError) { + FUTURE_SUCCESS_TEST( + [] { return Widget(1); }, + [](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .tapError([](Status s) { FAIL("tapError() callback was called"); }) + .then([](Widget i) { return i + 2; }) + .get(), + 3); + }); +} + +#if 0 // Needs copy +TEST(Future_MoveOnly, Success_tapAll_StatusWith) { + FUTURE_SUCCESS_TEST([]{return Widget(1);}, [](Future<Widget>&& fut) { + bool tapCalled = false; + ASSERT_EQ(std::move(fut) + .tapAll([&tapCalled](StatusWith<Widget> sw) { + ASSERT_EQ(uassertStatusOK(sw).val, 1); + tapCalled = true; + }) + .then([](Widget i) { return i + 2; }) + .get(), + 3); + ASSERT(tapCalled); + }); +} +#endif + +TEST(Future_MoveOnly, Success_tapAll_Overloaded) { + FUTURE_SUCCESS_TEST([] { return Widget(1); }, + [](Future<Widget>&& fut) { + struct Callback { + void operator()(const Widget& i) { + ASSERT_EQ(i, 1); + 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([](Widget i) { return i + 2; }) + .get(), + 3); + ASSERT(callback.called); + }); +} + +TEST(Future_MoveOnly, Fail_tap) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + ASSERT_EQ(std::move(fut) + .tap([](const Widget& i) { FAIL("tap() callback was called"); }) + .onError([](Status s) { + ASSERT_EQ(s, failStatus()); + return Widget(3); + }) + .get(), + 3); + }); +} + +TEST(Future_MoveOnly, Fail_tapError) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& 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()); + return Widget(3); + }) + .get(), + 3); + ASSERT(tapCalled); + }); +} + +#if 0 // Needs copy +TEST(Future_MoveOnly, Fail_tapAll_StatusWith) { + FUTURE_FAIL_TEST<Widget>( [](Future<Widget>&& fut) { + bool tapCalled = false; + ASSERT_EQ(std::move(fut) + .tapAll([&tapCalled](StatusWith<Widget> sw) { + ASSERT_EQ(sw.getStatus(), failStatus()); + tapCalled = true; + }) + .onError([](Status s) { + ASSERT_EQ(s, failStatus()); + return Widget(3); + }) + .get(), + 3); + ASSERT(tapCalled); + }); +} +#endif + +TEST(Future_MoveOnly, Fail_tapAll_Overloaded) { + FUTURE_FAIL_TEST<Widget>([](Future<Widget>&& fut) { + struct Callback { + void operator()(const Widget& i) { + FAIL("Widget overload called with ") << i; + } + 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()); + return Widget(3); + }) + .get(), + 3); + + ASSERT(callback.called); + }); +} + +} // namespace +} // namespace mongo |