Skip to content

Commit

Permalink
Add apply() error propagation.
Browse files Browse the repository at this point in the history
  • Loading branch information
tmadden committed Sep 16, 2020
1 parent 4ab5a25 commit 4e0e261
Show file tree
Hide file tree
Showing 5 changed files with 132 additions and 25 deletions.
2 changes: 1 addition & 1 deletion src/alia/flow/try_catch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ try_block::try_block(context ctx) : ctx_(ctx)
void
try_block::operator<<(function_view<void()> body)
{
ALIA_IF_(ctx_, !data_->exception)
ALIA_EVENT_DEPENDENT_IF_(ctx_, !data_->exception)
{
try
{
Expand Down
16 changes: 12 additions & 4 deletions src/alia/signals/application.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,14 @@ enum class apply_status
template<class Value>
struct apply_result_data
{
counter_type result_version = 0;
Value result;
apply_status status = apply_status::UNCOMPUTED;
// This is used to identify the result of the apply. It's incremented every
// time the inputs change.
counter_type version = 0;
// If status is READY, this is the result.
Value result;
// If status is FAILED, this is the error.
std::exception_ptr error;
};

template<class Value>
Expand All @@ -129,7 +134,7 @@ reset(apply_result_data<Value>& data)
{
if (data.status != apply_status::UNCOMPUTED)
{
++data.result_version;
++data.version;
data.status = apply_status::UNCOMPUTED;
}
}
Expand All @@ -143,7 +148,7 @@ struct apply_signal : signal<apply_signal<Value>, Value, read_only_signal>
id_interface const&
value_id() const
{
id_ = make_id(data_->result_version);
id_ = make_id(data_->version);
return id_;
}
bool
Expand Down Expand Up @@ -221,9 +226,12 @@ apply(context ctx, Function f, Args const&... args)
}
catch (...)
{
data.error = std::current_exception();
data.status = apply_status::FAILED;
}
}
if (data.status == apply_status::FAILED)
std::rethrow_exception(data.error);
}
return make_apply_signal(data);
}
Expand Down
15 changes: 12 additions & 3 deletions unit_tests/flow/testing.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,20 @@ check_log(std::string const& expected_contents)
}

void
do_object(test_context ctx, std::string name)
do_object(test_context ctx, readable<std::string> name)
{
tree_node<test_object>* node;
if (get_cached_data(ctx, &node))
node->object.name = name;
get_cached_data(ctx, &node);
if (is_refresh_event(ctx))
{
if (signal_has_value(name))
node->object.name = read_signal(name);
refresh_tree_node(get<tree_traversal_tag>(ctx), *node);
}
}

void
do_object(test_context ctx, std::string name)
{
do_object(ctx, value(name));
}
3 changes: 3 additions & 0 deletions unit_tests/flow/testing.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,9 @@ ALIA_DEFINE_TAGGED_TYPE(tree_traversal_tag, tree_traversal<test_object>&)
typedef alia::extend_context_type_t<alia::context, tree_traversal_tag>
test_context;

void
do_object(test_context ctx, readable<std::string> name);

void
do_object(test_context ctx, std::string name);

Expand Down
121 changes: 104 additions & 17 deletions unit_tests/signals/application.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
#define ALIA_LOWERCASE_MACROS

#include <alia/flow/try_catch.hpp>
#include <alia/signals/application.hpp>

#include <testing.hpp>

#include <alia/signals/basic.hpp>

#include "traversal.hpp"
#include <flow/testing.hpp>
#include <traversal.hpp>

using namespace alia;

Expand Down Expand Up @@ -43,6 +45,68 @@ TEST_CASE("lazy_apply", "[signals][application]")
REQUIRE(s3.value_id() != s4.value_id());
}

TEST_CASE("failing lazy_apply", "[signals][application]")
{
clear_log();

tree_node<test_object> root;
root.object.name = "root";

auto f = [&](size_t i) { return std::string("abcdef").substr(i); };

int n = 0;

auto controller = [&](test_context ctx) {
alia_try
{
do_object(ctx, lazy_apply(f, value(n)));
}
alia_catch(...)
{
do_object(ctx, "error");
}
alia_end
};

alia::system sys;
initialize_system(sys, [&](context vanilla_ctx) {
tree_traversal<test_object> traversal;
auto ctx = vanilla_ctx.add<tree_traversal_tag>(traversal);
if (is_refresh_event(ctx))
{
traverse_object_tree(traversal, root, [&]() { controller(ctx); });
}
else
{
controller(ctx);
}
});

n = 0;
refresh_system(sys);
REQUIRE(root.object.to_string() == "root(abcdef();)");

n = 3;
refresh_system(sys);
REQUIRE(root.object.to_string() == "root(def();)");

n = 7;
refresh_system(sys);
REQUIRE(root.object.to_string() == "root(error();)");

n = 7;
refresh_system(sys);
REQUIRE(root.object.to_string() == "root(error();)");

n = 2;
refresh_system(sys);
REQUIRE(root.object.to_string() == "root(cdef();)");

n = 17;
refresh_system(sys);
REQUIRE(root.object.to_string() == "root(error();)");
}

TEST_CASE("lazy_lift", "[signals][application]")
{
auto s = lazy_lift([](int i) { return 2 * i; })(value(1));
Expand Down Expand Up @@ -139,28 +203,51 @@ TEST_CASE("unready apply", "[signals][application]")
}
}

TEST_CASE("failed apply", "[signals][application]")
TEST_CASE("failing apply", "[signals][application]")
{
auto f = [&](int, int) -> int { throw "failed"; };
int f_call_count = 0;
auto f = [&](size_t i) {
++f_call_count;
return std::string("abcdef").substr(i);
};

{
alia::system sys;
initialize_system(sys, [](context) {});
alia::system sys;
initialize_system(sys, [](context) {});

auto make_controller = [=](auto x, auto y) {
return [=](context ctx) {
auto s = apply(ctx, f, x, y);
auto make_controller = [&](size_t i) {
return [=](context ctx) {
alia_try
{
do_text(ctx, apply(ctx, f, value(i)));
}
alia_catch(...)
{
do_text(ctx, value("(error)"));
}
alia_end
};
};

typedef decltype(s) signal_t;
REQUIRE(signal_is_readable<signal_t>::value);
REQUIRE(!signal_is_writable<signal_t>::value);
check_traversal(sys, make_controller(0), "abcdef;");
REQUIRE(f_call_count == 1);

REQUIRE(!signal_has_value(s));
};
};
check_traversal(sys, make_controller(0), "abcdef;");
REQUIRE(f_call_count == 1);

do_traversal(sys, make_controller(value(1), value(2)));
}
check_traversal(sys, make_controller(3), "def;");
REQUIRE(f_call_count == 2);

check_traversal(sys, make_controller(7), "(error);");
REQUIRE(f_call_count == 3);

check_traversal(sys, make_controller(7), "(error);");
REQUIRE(f_call_count == 3);

check_traversal(sys, make_controller(3), "def;");
REQUIRE(f_call_count == 4);

check_traversal(sys, make_controller(17), "(error);");
REQUIRE(f_call_count == 5);
}

TEST_CASE("lift", "[signals][application]")
Expand Down

0 comments on commit 4e0e261

Please sign in to comment.