Commit f0bcdcbd authored by Dominik Charousset's avatar Dominik Charousset

Implement new fixture for flow tests

parent a87ac5e6
......@@ -150,6 +150,7 @@ caf_add_component(
caf/execution_unit.cpp
caf/flow/coordinated.cpp
caf/flow/coordinator.cpp
caf/flow/observable.test.cpp
caf/flow/observable_builder.cpp
caf/flow/op/interval.cpp
caf/flow/scoped_coordinator.cpp
......
......@@ -22,6 +22,8 @@ caf_add_component(
caf/test/block.cpp
caf/test/context.cpp
caf/test/factory.cpp
caf/test/fixture/flow.cpp
caf/test/fixture/flow.test.cpp
caf/test/given.cpp
caf/test/nesting_error.cpp
caf/test/registry.cpp
......
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/fixture/flow.hpp"
namespace caf::test::fixture {
void flow::run_flows() {
// TODO: the scoped_coordinator is not the right tool for this job. We need a
// custom coordinator that allows us to control timeouts. For now, this
// is only good enough to get tests running that have no notion of time.
coordinator_->run_some();
}
} // namespace caf::test::fixture
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#pragma once
#include "caf/detail/core_export.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
#include "caf/flow/observable_builder.hpp"
#include "caf/flow/scoped_coordinator.hpp"
#include <cstddef>
#include <memory>
#include <utility>
namespace caf::test::fixture {
/// A fixture for testing the flow API.
class CAF_CORE_EXPORT flow {
public:
flow() {
coordinator_ = caf::flow::make_scoped_coordinator();
}
/// Returns a new builder for creating observables.
[[nodiscard]] auto make_observable() {
return coordinator_->make_observable();
}
/// Shortcut for creating an observable error via
/// `make_observable<T>().fail<T>(args...)`. When passing an empty parameter
/// pack, the error is constructed from sec::runtime_error.
template <class T = int, class... Args>
[[nodiscard]] auto obs_error(Args&&... args) {
if constexpr (sizeof...(Args) == 0)
return make_observable().fail<T>(make_error(sec::runtime_error));
else
return make_observable().fail<T>(make_error(std::forward<Args>(args)...));
}
/// Shortcut for `make_observable<T>().range(init, num)`.
template <class T>
[[nodiscard]] auto range(T init, size_t num) {
return make_observable().range(init, num);
}
/// Materializes an observable by calling `as_observable` on it.
template <class Observable>
[[nodiscard]] static auto mat(Observable&& x) {
return std::forward<Observable>(x).as_observable();
}
/// Collects all values from an observable into a vector.
template <class Observable>
auto collect(Observable&& observable) {
using value_type = caf::flow::output_type_t<std::decay_t<Observable>>;
using list_type = std::vector<value_type>;
using result_type = expected<list_type>;
auto fin = std::make_shared<bool>(false);
auto err = std::make_shared<error>();
auto values = std::make_shared<list_type>();
std::forward<Observable>(observable)
.do_on_complete([fin] { *fin = true; })
.do_on_error([fin, err](const error& e) {
*fin = true;
*err = std::move(e);
})
.for_each([values](const value_type& value) { //
values->emplace_back(value);
});
run_flows();
if (!*fin) {
// TODO: use fail() on the current runnable instead.
CAF_RAISE_ERROR(std::logic_error, "observable did not complete");
}
if (*err) {
return result_type{std::move(*err)};
}
return result_type{std::move(*values)};
}
/// Runs all flows created by this fixture.
void run_flows();
private:
caf::flow::scoped_coordinator_ptr coordinator_;
};
} // namespace caf::test::fixture
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include "caf/test/fixture/flow.hpp"
#include "caf/test/caf_test_main.hpp"
#include "caf/test/test.hpp"
#include "caf/flow/observable.hpp"
using namespace caf;
WITH_FIXTURE(test::fixture::flow) {
TEST("range() creates a blueprint for a range of numbers") {
auto values = std::make_shared<std::vector<int>>();
range(1, 3).for_each([values](int value) { //
values->emplace_back(value);
});
run_flows();
check_eq(*values, std::vector{1, 2, 3});
}
TEST("collect() eagerly evaluates an observable and returns a vector") {
check_eq(collect(range(1, 0)), std::vector<int>{});
check_eq(collect(range(1, 1)), std::vector{1});
check_eq(collect(range(1, 2)), std::vector{1, 2});
check_eq(collect(range(1, 3)), std::vector{1, 2, 3});
}
} // WITH_FIXTURE(test::fixture::flow)
CAF_TEST_MAIN()
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#include <string>
#include <vector>
namespace caf::test {
/// A type that can be used to check whether a list is empty.
struct nil_t {};
/// Represents the empty list.
constexpr nil_t nil = nil_t{};
/// @relates nil_t
inline std::string to_string(nil_t) {
return "nil";
}
/// @relates nil_t
template <class T>
bool operator==(const std::vector<T>& values, nil_t) {
return values.empty();
}
/// @relates nil_t
template <class T>
bool operator==(nil_t, const T& values) {
return values == nil;
}
/// @relates nil_t
template <class T>
bool operator!=(const T& values, nil_t) {
return !(values == nil);
}
/// @relates nil_t
template <class T>
bool operator!=(nil_t, const T& values) {
return !(values == nil);
}
} // namespace caf::test
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment