Files
scylladb/tests/futures_test.cc
2015-03-10 16:38:43 +02:00

262 lines
6.5 KiB
C++

/*
* 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 "core/app-template.hh"
#include "core/shared_ptr.hh"
#include "core/semaphore.hh"
#include "test-utils.hh"
#include "core/future-util.hh"
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<bool>();
auto finally2 = make_shared<bool>();
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_finally_is_called_on_success_and_failure__not_ready_to_armed) {
auto finally1 = make_shared<bool>();
auto finally2 = make_shared<bool>();
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);
});
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) {});
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) {});
}
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());
auto called = std::move(f2).then([] {});
BOOST_REQUIRE(called.available());
return make_ready_future<>();
}
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_broken_semaphore) {
auto sem = make_lw_shared<semaphore>(0);
struct oops {};
auto ret = sem->wait().then_wrapped([sem] (future<> f) {
try {
f.get();
BOOST_FAIL("expecting exception");
} catch (oops& x) {
// ok
return make_ready_future<>();
} catch (...) {
BOOST_FAIL("wrong exception seen");
}
BOOST_FAIL("unreachable");
return make_ready_future<>();
});
sem->broken(oops());
return ret;
}
SEASTAR_TEST_CASE(test_bare_value_can_be_returned_from_callback) {
return now().then([] {
return 3;
}).then([] (int x) {
BOOST_REQUIRE(x == 3);
});
}