/* * This file is open source software, licensed to you under the terms * of the Apache License, Version 2.0 (the "License"). See the NOTICE file * distributed with this work for additional information regarding copyright * ownership. You may not use this file except in compliance with the License. * * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ /* * Copyright (C) 2014 Cloudius Systems, Ltd. */ #include #include #include #include #include #include #include #include using namespace seastar; using namespace std::chrono_literals; class expected_exception : std::runtime_error { public: expected_exception() : runtime_error("expected") {} }; SEASTAR_TEST_CASE(test_finally_is_called_on_success_and_failure) { auto finally1 = make_shared(); auto finally2 = make_shared(); return make_ready_future().then([] { }).finally([=] { *finally1 = true; }).then([] { throw std::runtime_error(""); }).finally([=] { *finally2 = true; }).then_wrapped([=] (auto&& f) { BOOST_REQUIRE(*finally1); BOOST_REQUIRE(*finally2); // Should be failed. try { f.get(); BOOST_REQUIRE(false); } catch (...) {} }); } SEASTAR_TEST_CASE(test_get_on_promise) { auto p = promise(); p.set_value(10); BOOST_REQUIRE_EQUAL(10u, p.get_future().get0()); return make_ready_future(); } SEASTAR_TEST_CASE(test_finally_waits_for_inner) { auto finally = make_shared(); auto p = make_shared>(); auto f = make_ready_future().then([] { }).finally([=] { return p->get_future().then([=] { *finally = true; }); }).then([=] { BOOST_REQUIRE(*finally); }); BOOST_REQUIRE(!*finally); p->set_value(); return f; } SEASTAR_TEST_CASE(test_finally_is_called_on_success_and_failure__not_ready_to_armed) { auto finally1 = make_shared(); auto finally2 = make_shared(); promise<> p; auto f = p.get_future().finally([=] { *finally1 = true; }).then([] { throw std::runtime_error(""); }).finally([=] { *finally2 = true; }).then_wrapped([=] (auto &&f) { BOOST_REQUIRE(*finally1); BOOST_REQUIRE(*finally2); try { f.get(); } catch (...) {} // silence exceptional future ignored messages }); p.set_value(); return f; } SEASTAR_TEST_CASE(test_exception_from_finally_fails_the_target) { promise<> pr; auto f = pr.get_future().finally([=] { throw std::runtime_error(""); }).then([] { BOOST_REQUIRE(false); }).then_wrapped([] (auto&& f) { try { f.get(); } catch (...) {} // silence exceptional future ignored messages }); pr.set_value(); return f; } SEASTAR_TEST_CASE(test_exception_from_finally_fails_the_target_on_already_resolved) { return make_ready_future().finally([=] { throw std::runtime_error(""); }).then([] { BOOST_REQUIRE(false); }).then_wrapped([] (auto&& f) { try { f.get(); } catch (...) {} // silence exceptional future ignored messages }); } SEASTAR_TEST_CASE(test_exception_thrown_from_then_wrapped_causes_future_to_fail) { return make_ready_future().then_wrapped([] (auto&& f) { throw std::runtime_error(""); }).then_wrapped([] (auto&& f) { try { f.get(); BOOST_REQUIRE(false); } catch (...) {} }); } SEASTAR_TEST_CASE(test_exception_thrown_from_then_wrapped_causes_future_to_fail__async_case) { promise<> p; auto f = p.get_future().then_wrapped([] (auto&& f) { throw std::runtime_error(""); }).then_wrapped([] (auto&& f) { try { f.get(); BOOST_REQUIRE(false); } catch (...) {} }); p.set_value(); return f; } SEASTAR_TEST_CASE(test_failing_intermediate_promise_should_fail_the_master_future) { promise<> p1; promise<> p2; auto f = p1.get_future().then([f = std::move(p2.get_future())] () mutable { return std::move(f); }).then([] { BOOST_REQUIRE(false); }); p1.set_value(); p2.set_exception(std::runtime_error("boom")); return std::move(f).then_wrapped([](auto&& f) { try { f.get(); BOOST_REQUIRE(false); } catch (...) {} }); } SEASTAR_TEST_CASE(test_future_forwarding__not_ready_to_unarmed) { promise<> p1; promise<> p2; auto f1 = p1.get_future(); auto f2 = p2.get_future(); f1.forward_to(std::move(p2)); BOOST_REQUIRE(!f2.available()); auto called = f2.then([] {}); p1.set_value(); return called; } SEASTAR_TEST_CASE(test_future_forwarding__not_ready_to_armed) { promise<> p1; promise<> p2; auto f1 = p1.get_future(); auto f2 = p2.get_future(); auto called = f2.then([] {}); f1.forward_to(std::move(p2)); BOOST_REQUIRE(!f2.available()); p1.set_value(); return called; } SEASTAR_TEST_CASE(test_future_forwarding__ready_to_unarmed) { promise<> p2; auto f1 = make_ready_future<>(); auto f2 = p2.get_future(); std::move(f1).forward_to(std::move(p2)); BOOST_REQUIRE(f2.available()); return std::move(f2).then_wrapped([] (future<> f) { BOOST_REQUIRE(!f.failed()); }); } SEASTAR_TEST_CASE(test_future_forwarding__ready_to_armed) { promise<> p2; auto f1 = make_ready_future<>(); auto f2 = p2.get_future(); auto called = std::move(f2).then([] {}); BOOST_REQUIRE(f1.available()); f1.forward_to(std::move(p2)); return called; } static void forward_dead_unarmed_promise_with_dead_future_to(promise<>& p) { promise<> p2; p.get_future().forward_to(std::move(p2)); } SEASTAR_TEST_CASE(test_future_forwarding__ready_to_unarmed_soon_to_be_dead) { promise<> p1; forward_dead_unarmed_promise_with_dead_future_to(p1); make_ready_future<>().forward_to(std::move(p1)); return make_ready_future<>(); } SEASTAR_TEST_CASE(test_exception_can_be_thrown_from_do_until_body) { return do_until([] { return false; }, [] { throw expected_exception(); return now(); }).then_wrapped([] (auto&& f) { try { f.get(); BOOST_FAIL("should have failed"); } catch (const expected_exception& e) { // expected } }); } SEASTAR_TEST_CASE(test_bare_value_can_be_returned_from_callback) { return now().then([] { return 3; }).then([] (int x) { BOOST_REQUIRE(x == 3); }); } SEASTAR_TEST_CASE(test_when_all_iterator_range) { std::vector> futures; for (size_t i = 0; i != 1000000; ++i) { // .then() usually returns a ready future, but sometimes it // doesn't, so call it a million times. This exercises both // available and unavailable paths in when_all(). futures.push_back(make_ready_future<>().then([i] { return i; })); } // Verify the above statement is correct BOOST_REQUIRE(!std::all_of(futures.begin(), futures.end(), [] (auto& f) { return f.available(); })); auto p = make_shared(std::move(futures)); return when_all(p->begin(), p->end()).then([p] (std::vector> ret) { BOOST_REQUIRE(std::all_of(ret.begin(), ret.end(), [] (auto& f) { return f.available(); })); BOOST_REQUIRE(std::all_of(ret.begin(), ret.end(), [&ret] (auto& f) { return std::get<0>(f.get()) == size_t(&f - ret.data()); })); }); } SEASTAR_TEST_CASE(test_map_reduce) { auto square = [] (long x) { return make_ready_future(x*x); }; long n = 1000; return map_reduce(boost::make_counting_iterator(0), boost::make_counting_iterator(n), square, long(0), std::plus()).then([n] (auto result) { auto m = n - 1; // counting does not include upper bound BOOST_REQUIRE_EQUAL(result, (m * (m + 1) * (2*m + 1)) / 6); }); } // This test doesn't actually test anything - it just waits for the future // returned by sleep to complete. However, a bug we had in sleep() caused // this test to fail the sanitizer in the debug build, so this is a useful // regression test. SEASTAR_TEST_CASE(test_sleep) { return sleep(std::chrono::milliseconds(100)); } SEASTAR_TEST_CASE(test_do_with_1) { return do_with(1, [] (int& one) { BOOST_REQUIRE_EQUAL(one, 1); return make_ready_future<>(); }); } SEASTAR_TEST_CASE(test_do_with_2) { return do_with(1, 2L, [] (int& one, long two) { BOOST_REQUIRE_EQUAL(one, 1); BOOST_REQUIRE_EQUAL(two, 2); return make_ready_future<>(); }); } SEASTAR_TEST_CASE(test_do_with_3) { return do_with(1, 2L, 3, [] (int& one, long two, int three) { BOOST_REQUIRE_EQUAL(one, 1); BOOST_REQUIRE_EQUAL(two, 2); BOOST_REQUIRE_EQUAL(three, 3); return make_ready_future<>(); }); } SEASTAR_TEST_CASE(test_do_with_4) { return do_with(1, 2L, 3, 4, [] (int& one, long two, int three, int four) { BOOST_REQUIRE_EQUAL(one, 1); BOOST_REQUIRE_EQUAL(two, 2); BOOST_REQUIRE_EQUAL(three, 3); BOOST_REQUIRE_EQUAL(four, 4); return make_ready_future<>(); }); } SEASTAR_TEST_CASE(test_do_while_stopping_immediately) { return do_with(int(0), [] (int& count) { return repeat([&count] { ++count; return stop_iteration::yes; }).then([&count] { BOOST_REQUIRE(count == 1); }); }); } SEASTAR_TEST_CASE(test_do_while_stopping_after_two_iterations) { return do_with(int(0), [] (int& count) { return repeat([&count] { ++count; return count == 2 ? stop_iteration::yes : stop_iteration::no; }).then([&count] { BOOST_REQUIRE(count == 2); }); }); } SEASTAR_TEST_CASE(test_do_while_failing_in_the_first_step) { return repeat([] { throw expected_exception(); return stop_iteration::no; }).then_wrapped([](auto&& f) { try { f.get(); BOOST_FAIL("should not happen"); } catch (const expected_exception&) { // expected } }); } SEASTAR_TEST_CASE(test_do_while_failing_in_the_second_step) { return do_with(int(0), [] (int& count) { return repeat([&count] { ++count; if (count > 1) { throw expected_exception(); } return later().then([] { return stop_iteration::no; }); }).then_wrapped([&count](auto&& f) { try { f.get(); BOOST_FAIL("should not happen"); } catch (const expected_exception&) { BOOST_REQUIRE(count == 2); } }); }); } SEASTAR_TEST_CASE(test_parallel_for_each) { return async([] { // empty parallel_for_each(std::vector(), [] (int) -> future<> { BOOST_FAIL("should not reach"); abort(); }).get(); // immediate result auto range = boost::copy_range>(boost::irange(1, 6)); auto sum = 0; parallel_for_each(range, [&sum] (int v) { sum += v; return make_ready_future<>(); }).get(); BOOST_REQUIRE_EQUAL(sum, 15); // all suspend sum = 0; parallel_for_each(range, [&sum] (int v) { return later().then([&sum, v] { sum += v; }); }).get(); BOOST_REQUIRE_EQUAL(sum, 15); // throws immediately BOOST_CHECK_EXCEPTION(parallel_for_each(range, [&sum] (int) -> future<> { throw 5; }).get(), int, [] (int v) { return v == 5; }); // throws after suspension BOOST_CHECK_EXCEPTION(parallel_for_each(range, [&sum] (int) { return later().then([] { throw 5; }); }).get(), int, [] (int v) { return v == 5; }); }); } SEASTAR_TEST_CASE(test_parallel_for_each_early_failure) { return do_with(0, [] (int& counter) { return parallel_for_each(boost::irange(0, 11000), [&counter] (int i) { using namespace std::chrono_literals; // force scheduling return sleep((i % 31 + 1) * 1ms).then([&counter, i] { ++counter; if (i % 1777 == 1337) { return make_exception_future<>(i); } return make_ready_future<>(); }); }).then_wrapped([&counter] (future<> f) { BOOST_REQUIRE_EQUAL(counter, 11000); BOOST_REQUIRE(f.failed()); try { f.get(); BOOST_FAIL("wanted an exception"); } catch (int i) { BOOST_REQUIRE(i % 1777 == 1337); } catch (...) { BOOST_FAIL("bad exception type"); } }); }); } SEASTAR_TEST_CASE(test_parallel_for_each_waits_for_all_fibers_even_if_one_of_them_failed) { auto can_exit = make_lw_shared(false); return parallel_for_each(boost::irange(0, 2), [can_exit] (int i) { return later().then([i, can_exit] { if (i == 1) { throw expected_exception(); } else { using namespace std::chrono_literals; return sleep(300ms).then([can_exit] { *can_exit = true; }); } }); }).then_wrapped([can_exit] (auto&& f) { try { f.get(); } catch (...) { // expected } BOOST_REQUIRE(*can_exit); }); } #ifndef SEASTAR_SHUFFLE_TASK_QUEUE SEASTAR_TEST_CASE(test_high_priority_task_runs_before_ready_continuations) { return now().then([] { auto flag = make_lw_shared(false); engine().add_high_priority_task(make_task([flag] { *flag = true; })); make_ready_future().then([flag] { BOOST_REQUIRE(*flag); }); }); } SEASTAR_TEST_CASE(test_high_priority_task_runs_in_the_middle_of_loops) { auto counter = make_lw_shared(0); auto flag = make_lw_shared(false); return repeat([counter, flag] { if (*counter == 1) { BOOST_REQUIRE(*flag); return stop_iteration::yes; } engine().add_high_priority_task(make_task([flag] { *flag = true; })); ++(*counter); return stop_iteration::no; }); } #endif SEASTAR_TEST_CASE(futurize_apply_val_exception) { return futurize::apply([] (int arg) { throw expected_exception(); return arg; }, 1).then_wrapped([] (future f) { try { f.get(); BOOST_FAIL("should have thrown"); } catch (expected_exception& e) {} }); } SEASTAR_TEST_CASE(futurize_apply_val_ok) { return futurize::apply([] (int arg) { return arg * 2; }, 2).then_wrapped([] (future f) { try { auto x = f.get0(); BOOST_REQUIRE_EQUAL(x, 4); } catch (expected_exception& e) { BOOST_FAIL("should not have thrown"); } }); } SEASTAR_TEST_CASE(futurize_apply_val_future_exception) { return futurize::apply([] (int a) { return sleep(std::chrono::milliseconds(100)).then([] { throw expected_exception(); return make_ready_future(0); }); }, 0).then_wrapped([] (future f) { try { f.get(); BOOST_FAIL("should have thrown"); } catch (expected_exception& e) { } }); } SEASTAR_TEST_CASE(futurize_apply_val_future_ok) { return futurize::apply([] (int a) { return sleep(std::chrono::milliseconds(100)).then([a] { return make_ready_future(a * 100); }); }, 2).then_wrapped([] (future f) { try { auto x = f.get0(); BOOST_REQUIRE_EQUAL(x, 200); } catch (expected_exception& e) { BOOST_FAIL("should not have thrown"); } }); } SEASTAR_TEST_CASE(futurize_apply_void_exception) { return futurize::apply([] (auto arg) { throw expected_exception(); }, 0).then_wrapped([] (future<> f) { try { f.get(); BOOST_FAIL("should have thrown"); } catch (expected_exception& e) {} }); } SEASTAR_TEST_CASE(futurize_apply_void_ok) { return futurize::apply([] (auto arg) { }, 0).then_wrapped([] (future<> f) { try { f.get(); } catch (expected_exception& e) { BOOST_FAIL("should not have thrown"); } }); } SEASTAR_TEST_CASE(futurize_apply_void_future_exception) { return futurize::apply([] (auto a) { return sleep(std::chrono::milliseconds(100)).then([] { throw expected_exception(); }); }, 0).then_wrapped([] (future<> f) { try { f.get(); BOOST_FAIL("should have thrown"); } catch (expected_exception& e) { } }); } SEASTAR_TEST_CASE(futurize_apply_void_future_ok) { auto a = make_lw_shared(1); return futurize::apply([] (int& a) { return sleep(std::chrono::milliseconds(100)).then([&a] { a *= 100; }); }, *a).then_wrapped([a] (future<> f) { try { f.get(); BOOST_REQUIRE_EQUAL(*a, 100); } catch (expected_exception& e) { BOOST_FAIL("should not have thrown"); } }); } SEASTAR_TEST_CASE(test_shared_future_propagates_value_to_all) { return seastar::async([] { promise> p; // shared_ptr<> to check it deals with emptyable types shared_future> f(p.get_future()); auto f1 = f.get_future(); auto f2 = f.get_future(); p.set_value(make_shared(1)); BOOST_REQUIRE(*f1.get0() == 1); BOOST_REQUIRE(*f2.get0() == 1); }); } template void check_fails_with_expected(future f) { try { f.get(); BOOST_FAIL("Should have failed"); } catch (expected_exception&) { // expected } } SEASTAR_TEST_CASE(test_shared_future_propagates_value_to_copies) { return seastar::async([] { promise p; auto sf1 = shared_future(p.get_future()); auto sf2 = sf1; auto f1 = sf1.get_future(); auto f2 = sf2.get_future(); p.set_value(1); BOOST_REQUIRE(f1.get0() == 1); BOOST_REQUIRE(f2.get0() == 1); }); } SEASTAR_TEST_CASE(test_obtaining_future_from_shared_future_after_it_is_resolved) { promise p1; promise p2; auto sf1 = shared_future(p1.get_future()); auto sf2 = shared_future(p2.get_future()); p1.set_value(1); p2.set_exception(expected_exception()); return sf2.get_future().then_wrapped([f1 = sf1.get_future()] (auto&& f) mutable { check_fails_with_expected(std::move(f)); return std::move(f1); }).then_wrapped([] (auto&& f) { BOOST_REQUIRE(f.get0() == 1); }); } SEASTAR_TEST_CASE(test_valueless_shared_future) { return seastar::async([] { promise<> p; shared_future<> f(p.get_future()); auto f1 = f.get_future(); auto f2 = f.get_future(); p.set_value(); f1.get(); f2.get(); }); } SEASTAR_TEST_CASE(test_shared_future_propagates_errors_to_all) { promise p; shared_future f(p.get_future()); auto f1 = f.get_future(); auto f2 = f.get_future(); p.set_exception(expected_exception()); return f1.then_wrapped([f2 = std::move(f2)] (auto&& f) mutable { check_fails_with_expected(std::move(f)); return std::move(f2); }).then_wrapped([] (auto&& f) mutable { check_fails_with_expected(std::move(f)); }); } SEASTAR_TEST_CASE(test_futurize_from_tuple) { std::tuple v1 = std::make_tuple(3); std::tuple<> v2 = {}; BOOST_REQUIRE(futurize::from_tuple(v1).get() == v1); BOOST_REQUIRE(futurize::from_tuple(v2).get() == v2); return make_ready_future<>(); } SEASTAR_TEST_CASE(test_repeat_until_value) { return do_with(int(), [] (int& counter) { return repeat_until_value([&counter] () -> future> { if (counter == 10000) { return make_ready_future>(counter); } else { ++counter; return make_ready_future>(compat::nullopt); } }).then([&counter] (int result) { BOOST_REQUIRE(counter == 10000); BOOST_REQUIRE(result == counter); }); }); } SEASTAR_TEST_CASE(test_when_allx) { return when_all(later(), later(), make_ready_future()).discard_result(); } template static void check_failed_with(future&& f) { BOOST_REQUIRE(f.failed()); try { f.get(); BOOST_FAIL("exception expected"); } catch (const E& e) { // expected } catch (...) { BOOST_FAIL(format("wrong exception: {}", std::current_exception())); } } template static void check_timed_out(future&& f) { check_failed_with(std::move(f)); } SEASTAR_TEST_CASE(test_with_timeout_when_it_times_out) { return seastar::async([] { promise<> pr; auto f = with_timeout(manual_clock::now() + 2s, pr.get_future()); BOOST_REQUIRE(!f.available()); manual_clock::advance(1s); later().get(); BOOST_REQUIRE(!f.available()); manual_clock::advance(1s); later().get(); check_timed_out(std::move(f)); pr.set_value(); }); } SEASTAR_TEST_CASE(test_custom_exception_factory_in_with_timeout) { return seastar::async([] { class custom_error : public std::exception { public: virtual const char* what() const noexcept { return "timedout"; } }; struct my_exception_factory { static auto timeout() { return custom_error(); } }; promise<> pr; auto f = with_timeout(manual_clock::now() + 1s, pr.get_future()); manual_clock::advance(1s); later().get(); check_failed_with(std::move(f)); }); } SEASTAR_TEST_CASE(test_with_timeout_when_it_does_not_time_out) { return seastar::async([] { { promise pr; auto f = with_timeout(manual_clock::now() + 1s, pr.get_future()); pr.set_value(42); BOOST_REQUIRE_EQUAL(f.get0(), 42); } // Check that timer was indeed cancelled manual_clock::advance(1s); later().get(); }); } SEASTAR_TEST_CASE(test_shared_future_with_timeout) { return seastar::async([] { shared_promise, int> pr; auto f1 = pr.get_shared_future(manual_clock::now() + 1s); auto f2 = pr.get_shared_future(manual_clock::now() + 2s); auto f3 = pr.get_shared_future(); BOOST_REQUIRE(!f1.available()); BOOST_REQUIRE(!f2.available()); BOOST_REQUIRE(!f3.available()); manual_clock::advance(1s); later().get(); check_timed_out(std::move(f1)); BOOST_REQUIRE(!f2.available()); BOOST_REQUIRE(!f3.available()); manual_clock::advance(1s); later().get(); check_timed_out(std::move(f2)); BOOST_REQUIRE(!f3.available()); pr.set_value(42); BOOST_REQUIRE_EQUAL(42, f3.get0()); }); } SEASTAR_TEST_CASE(test_when_all_succeed_tuples) { return seastar::when_all_succeed( make_ready_future<>(), make_ready_future("hello world"), make_ready_future(42), make_ready_future<>(), make_ready_future(84, "hi"), make_ready_future(true) ).then([] (sstring msg, int v, std::tuple t, bool b) { BOOST_REQUIRE_EQUAL(msg, "hello world"); BOOST_REQUIRE_EQUAL(v, 42); BOOST_REQUIRE_EQUAL(std::get<0>(t), 84); BOOST_REQUIRE_EQUAL(std::get<1>(t), "hi"); BOOST_REQUIRE_EQUAL(b, true); return seastar::when_all_succeed( make_exception_future<>(42), make_ready_future("hello world"), make_exception_future(43), make_ready_future<>() ).then([] (sstring, int) { BOOST_FAIL("shouldn't reach"); return false; }).handle_exception([] (auto excp) { try { std::rethrow_exception(excp); } catch (int v) { BOOST_REQUIRE(v == 42 || v == 43); return true; } catch (...) { } return false; }).then([] (auto ret) { BOOST_REQUIRE(ret); }); }); } SEASTAR_TEST_CASE(test_when_all_succeed_vector) { std::vector> vecs; vecs.emplace_back(make_ready_future<>()); vecs.emplace_back(make_ready_future<>()); vecs.emplace_back(make_ready_future<>()); vecs.emplace_back(make_ready_future<>()); return seastar::when_all_succeed(vecs.begin(), vecs.end()).then([] { std::vector> vecs; vecs.emplace_back(make_ready_future<>()); vecs.emplace_back(make_ready_future<>()); vecs.emplace_back(make_exception_future<>(42)); vecs.emplace_back(make_exception_future<>(43)); return seastar::when_all_succeed(vecs.begin(), vecs.end()); }).then([] { BOOST_FAIL("shouldn't reach"); return false; }).handle_exception([] (auto excp) { try { std::rethrow_exception(excp); } catch (int v) { BOOST_REQUIRE(v == 42 || v == 43); return true; } catch (...) { } return false; }).then([] (auto ret) { BOOST_REQUIRE(ret); std::vector> vecs; vecs.emplace_back(make_ready_future(1)); vecs.emplace_back(make_ready_future(2)); vecs.emplace_back(make_ready_future(3)); return seastar::when_all_succeed(vecs.begin(), vecs.end()); }).then([] (std::vector vals) { BOOST_REQUIRE_EQUAL(vals.size(), 3u); BOOST_REQUIRE_EQUAL(vals[0], 1); BOOST_REQUIRE_EQUAL(vals[1], 2); BOOST_REQUIRE_EQUAL(vals[2], 3); std::vector> vecs; vecs.emplace_back(make_ready_future(1)); vecs.emplace_back(make_ready_future(2)); vecs.emplace_back(make_exception_future(42)); vecs.emplace_back(make_exception_future(43)); return seastar::when_all_succeed(vecs.begin(), vecs.end()); }).then([] (std::vector) { BOOST_FAIL("shouldn't reach"); return false; }).handle_exception([] (auto excp) { try { std::rethrow_exception(excp); } catch (int v) { BOOST_REQUIRE(v == 42 || v == 43); return true; } catch (...) { } return false; }).then([] (auto ret) { BOOST_REQUIRE(ret); }); } SEASTAR_TEST_CASE(test_futurize_mutable) { int count = 0; return seastar::repeat([count]() mutable { ++count; if (count == 3) { return seastar::stop_iteration::yes; } return seastar::stop_iteration::no; }); }