summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuilherme Iscaro <iscaro@profusion.mobi>2017-08-08 18:11:23 -0300
committerGuilherme Iscaro <iscaro@profusion.mobi>2017-09-04 10:24:00 -0300
commit3ba9f25cf7ad74986bf77b679041ce2ad6758536 (patch)
tree1dbef24daeb1b047bace8a1f209cc1b5a8f6249b
parentd12c652a965c177d35dd1e2c7f3774dc54f49f56 (diff)
downloadefl-3ba9f25cf7ad74986bf77b679041ce2ad6758536.tar.gz
Eina_Promise/Eina_Future: Add example and tests.
-rw-r--r--src/Makefile_Ecore.am1
-rw-r--r--src/examples/ecore/Makefile.am5
-rw-r--r--src/examples/ecore/ecore_promise2_example.c385
-rw-r--r--src/tests/ecore/ecore_suite.c1
-rw-r--r--src/tests/ecore/ecore_suite.h1
-rw-r--r--src/tests/ecore/ecore_test_promise2.c1286
6 files changed, 1679 insertions, 0 deletions
diff --git a/src/Makefile_Ecore.am b/src/Makefile_Ecore.am
index c5be098bf7..4158ae5185 100644
--- a/src/Makefile_Ecore.am
+++ b/src/Makefile_Ecore.am
@@ -226,6 +226,7 @@ tests/ecore/ecore_test_ecore_thread_eina_thread_queue.c \
tests/ecore/ecore_test_ecore_input.c \
tests/ecore/ecore_test_ecore_file.c \
tests/ecore/ecore_test_promise.c \
+tests/ecore/ecore_test_promise2.c \
tests/ecore/ecore_test_job.c \
tests/ecore/ecore_test_args.c \
tests/ecore/ecore_suite.h
diff --git a/src/examples/ecore/Makefile.am b/src/examples/ecore/Makefile.am
index e10dfc7827..9ab288c594 100644
--- a/src/examples/ecore/Makefile.am
+++ b/src/examples/ecore/Makefile.am
@@ -76,6 +76,7 @@ ecore_idler_example \
ecore_imf_example \
ecore_job_example \
ecore_poller_example \
+ecore_promise2_example \
ecore_server_bench \
ecore_thread_example \
ecore_time_functions_example \
@@ -292,6 +293,9 @@ endif
ecore_poller_example_SOURCES = ecore_poller_example.c
ecore_poller_example_LDADD = $(ECORE_COMMON_LDADD)
+ecore_promise2_example_SOURCES = ecore_promise2_example.c
+ecore_promise2_example_LDADD = $(ECORE_COMMON_LDADD)
+
ecore_server_bench_SOURCES = ecore_server_bench.c
ecore_server_bench_LDADD = $(ECORE_CON_COMMON_LDADD)
@@ -425,6 +429,7 @@ ecore_job_example.c \
ecore_pipe_gstreamer_example.c \
ecore_pipe_simple_example.c \
ecore_poller_example.c \
+ecore_promise2_example.c \
ecore_server_bench.c \
ecore_thread_example.c \
ecore_time_functions_example.c \
diff --git a/src/examples/ecore/ecore_promise2_example.c b/src/examples/ecore/ecore_promise2_example.c
new file mode 100644
index 0000000000..34fffc0759
--- /dev/null
+++ b/src/examples/ecore/ecore_promise2_example.c
@@ -0,0 +1,385 @@
+#define EFL_EO_API_SUPPORT 1
+#define EFL_BETA_API_SUPPORT 1
+
+#include <Ecore.h>
+#include <Eina.h>
+#include <stdlib.h>
+#include <errno.h>
+
+typedef struct _Ctx {
+ Eina_Promise *p;
+ Eina_Bool should_fail;
+ Ecore_Timer *timer;
+ Eina_Value *value;
+} Ctx;
+
+typedef struct _Inner_Promise_Ctx {
+ Eina_Future *future;
+ Eina_Promise *promise;
+} Inner_Promise_Ctx;
+
+#define DEFAULT_MSG "the simple example is working!"
+
+#define VALUE_TYPE_CHECK(_v, _type) \
+ if (_v.type != _type) \
+ { \
+ fprintf(stderr, "Value type is not '%s' - received '%s'\n", \
+ _type->name, _v.type->name); \
+ return _v; \
+ }
+
+static Eina_Bool
+_timeout(void *data)
+{
+ Ctx *ctx = data;
+ if (ctx->should_fail) eina_promise_reject(ctx->p, ENETDOWN);
+ else
+ {
+ Eina_Value v;
+ eina_value_copy(ctx->value, &v);
+ eina_promise_resolve(ctx->p, v);
+ eina_value_free(ctx->value);
+ }
+ free(ctx);
+ return EINA_FALSE;
+}
+
+static void
+_promise_cancel(void *data, const Eina_Promise *dead EINA_UNUSED)
+{
+ Ctx *ctx = data;
+ if (ctx->timer) ecore_timer_del(ctx->timer);
+ eina_value_free(ctx->value);
+ free(ctx);
+}
+
+static Eina_Future_Scheduler *
+_future_scheduler_get(void)
+{
+ return efl_loop_future_scheduler_get(ecore_main_loop_get());
+}
+
+static Ctx *
+_promise_ctx_new(Eina_Value *v)
+{
+ Ctx *ctx;
+ ctx = calloc(1, sizeof(Ctx));
+ EINA_SAFETY_ON_NULL_GOTO(ctx, err_ctx);
+ ctx->p = eina_promise_new(_future_scheduler_get(), _promise_cancel, ctx);
+ EINA_SAFETY_ON_NULL_GOTO(ctx->p, err_timer);
+ ctx->value = v;
+ return ctx;
+ err_timer:
+ free(ctx);
+ err_ctx:
+ eina_value_free(v);
+ return NULL;
+}
+
+static Eina_Future *
+_future_get(Ctx *ctx)
+{
+ Eina_Future *f;
+
+ f = eina_future_new(ctx->p);
+ EINA_SAFETY_ON_NULL_GOTO(f, err_future);
+ ctx->timer = ecore_timer_add(0.1, _timeout, ctx);
+ EINA_SAFETY_ON_NULL_GOTO(ctx->timer, err_timer);
+ return f;
+
+ err_timer:
+ eina_future_cancel(f);
+ err_future:
+ return NULL;
+}
+
+static Eina_Future *
+_fail_future_get(void)
+{
+ Ctx *ctx = _promise_ctx_new(NULL);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
+ ctx->should_fail = EINA_TRUE;
+ return _future_get(ctx);
+}
+
+static Eina_Future *
+_str_future_get(void)
+{
+ Eina_Value *v = eina_value_util_string_new(DEFAULT_MSG);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(v, NULL);
+ Ctx *ctx = _promise_ctx_new(v);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
+ return _future_get(ctx);
+}
+
+static Eina_Future *
+_int_future_get(void)
+{
+ Eina_Value *v= eina_value_util_int_new(0);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(v, NULL);
+ Ctx *ctx = _promise_ctx_new(v);
+ EINA_SAFETY_ON_NULL_RETURN_VAL(ctx, NULL);
+ return _future_get(ctx);
+}
+
+static Eina_Value
+_simple_ok(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_STRING);
+ return v;
+}
+
+static Eina_Value
+_alternate_error_cb(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Eina_Bool *should_fail = data;
+ Eina_Value new_v = EINA_VALUE_EMPTY;
+
+ if (*should_fail)
+ {
+ *should_fail = EINA_FALSE;
+ eina_value_setup(&new_v, EINA_VALUE_TYPE_ERROR);
+ eina_value_set(&new_v, ENETDOWN);
+ printf("Received succes from the previous future - Generating error for the next future...\n");
+ }
+ else
+ {
+ *should_fail = EINA_TRUE;
+ Eina_Error err;
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_ERROR);
+ eina_value_get(&v, &err);
+ printf("Received error from the previous future - value: %s. Send success\n",
+ eina_error_msg_get(err));
+ }
+ return new_v;
+}
+
+static void
+_alternate_error(void)
+{
+ static Eina_Bool should_fail = EINA_TRUE;
+
+ eina_future_chain(_str_future_get(),
+ {.cb = _alternate_error_cb, .data = &should_fail},
+ {.cb = _alternate_error_cb, .data = &should_fail},
+ {.cb = _alternate_error_cb, .data = &should_fail},
+ {.cb = _alternate_error_cb, .data = &should_fail});
+
+}
+
+static Eina_Value
+_simple_err(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_ERROR);
+ return v;
+}
+
+static void
+_simple(void)
+{
+ eina_future_chain(_str_future_get(),
+ eina_future_cb_console("Expecting the following message: "DEFAULT_MSG ". Got: ", NULL),
+ { .cb = _simple_ok, .data = NULL });
+ eina_future_chain(_fail_future_get(),
+ eina_future_cb_console("Expectig network down error. Got: ", NULL),
+ { .cb = _simple_err, .data = NULL });
+}
+
+static Eina_Value
+_chain_no_errors_cb(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ int count;
+ Eina_Value new_v;
+
+ eina_value_setup(&new_v, EINA_VALUE_TYPE_INT);
+ if (!v.type)
+ count = 1;
+ else
+ {
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_INT);
+ eina_value_get(&v, &count);
+ }
+ eina_value_set(&new_v, count * 2);
+ return new_v;
+}
+
+static void
+_chain_no_errors(void)
+{
+ eina_future_chain(_int_future_get(),
+ eina_future_cb_console("Expecting no value. Got: ", NULL),
+ {.cb = _chain_no_errors_cb, .data = NULL},
+ eina_future_cb_console("Expecting number 2. Got: ", NULL),
+ {.cb = _chain_no_errors_cb, .data = NULL},
+ eina_future_cb_console("Expecting number 4. Got: ", NULL),
+ {.cb = _chain_no_errors_cb, .data = NULL},
+ eina_future_cb_console("Expecting number 8. Got: ", NULL),
+ {.cb = _chain_no_errors_cb, .data = NULL},
+ eina_future_cb_console("Expecting number 16. Got: ", NULL));
+}
+
+static Eina_Value
+_chain_with_error_cb(void *data EINA_UNUSED, const Eina_Value v EINA_UNUSED, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Eina_Value err;
+ eina_value_setup(&err, EINA_VALUE_TYPE_ERROR);
+ eina_value_set(&err, E2BIG);
+ return err;
+}
+
+static void
+_chain_with_error(void)
+{
+ eina_future_chain(_int_future_get(),
+ { _chain_with_error_cb, NULL },
+ eina_future_cb_console("Expecting argument list too long. Got: ", NULL),
+ { .cb = _simple_err, .data = NULL });
+}
+
+static Eina_Value
+_delayed_resolve(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Inner_Promise_Ctx *ctx = data;
+ Eina_Value new_v;
+ eina_value_setup(&new_v, EINA_VALUE_TYPE_STRING);
+ eina_value_set(&new_v, "Hello from inner future");
+ eina_promise_resolve(ctx->promise, new_v);
+ free(ctx);
+ return v;
+}
+
+static Eina_Value
+_delayed_reject(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Inner_Promise_Ctx *ctx = data;
+ eina_promise_reject(ctx->promise, ENETDOWN);
+ free(ctx);
+ return v;
+}
+
+static void
+_inner_promise_cancel(void *data, const Eina_Promise *dead EINA_UNUSED)
+{
+ Inner_Promise_Ctx *ctx = data;
+ eina_future_cancel(ctx->future);
+ free(ctx);
+}
+
+static Eina_Value
+_chain_inner_cb(void *data, const Eina_Value v EINA_UNUSED, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Inner_Promise_Ctx *ctx;
+ Eina_Value r;
+
+ ctx = calloc(1, sizeof(Inner_Promise_Ctx));
+ EINA_SAFETY_ON_NULL_GOTO(ctx, err);
+ ctx->promise = eina_promise_new(_future_scheduler_get(), _inner_promise_cancel, ctx);
+ EINA_SAFETY_ON_NULL_GOTO(ctx->promise, err);
+
+ printf("Creating a new promise inside the future cb\n");
+ ctx->future = eina_future_then(_int_future_get(),
+ !data ? _delayed_resolve : _delayed_reject,
+ ctx);
+ return eina_promise_as_value(ctx->promise);
+
+ err:
+ eina_value_setup(&r, EINA_VALUE_TYPE_ERROR);
+ eina_value_set(&r, ENOMEM);
+ free(ctx);
+ return r;
+}
+
+static Eina_Value
+_chain_inner_last_cb(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_STRING);
+ return v;
+}
+
+static void
+_chain_inner_no_errors(void)
+{
+ eina_future_chain(_int_future_get(),
+ { .cb = _chain_inner_cb, .data = NULL },
+ eina_future_cb_console("Expecting message: 'Hello from inner future'. Got: ", NULL),
+ { .cb = _chain_inner_last_cb, .data = NULL });
+}
+
+static Eina_Value
+_err_inner_chain(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_ERROR);
+ ecore_main_loop_quit();
+ return v;
+}
+
+static void
+_chain_inner_errors(void)
+{
+
+ eina_future_chain(_int_future_get(),
+ { .cb = _chain_inner_cb, .data = (void *)1 },
+ eina_future_cb_console("Expection network down error. Got: ", NULL),
+ { .cb = _err_inner_chain, .data = NULL });
+}
+
+static Eina_Value
+_canceled_cb(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_ERROR);
+ return v;
+}
+
+static void
+_future_cancel(void)
+{
+ Eina_Future *f;
+
+ f = eina_future_chain(_int_future_get(),
+ eina_future_cb_console("Expecting cancelled operation error. Got: ", NULL),
+ { .cb = _canceled_cb, .data = NULL },
+ eina_future_cb_console("Expecting cancelled operation error. Got: ", NULL),
+ { .cb = _canceled_cb, .data = NULL },
+ eina_future_cb_console("Expecting cancelled operation error. Got: ", NULL),
+ { .cb = _canceled_cb, .data = NULL },
+ eina_future_cb_console("Expecting cancelled operation error. Got: ", NULL),
+ { .cb = _canceled_cb, .data = NULL },
+ eina_future_cb_console("Expecting cancelled operation error. Got: ", NULL),
+ { .cb = _canceled_cb, .data = NULL },
+ eina_future_cb_console("Expecting cancelled operation error. Got: ", NULL));
+ eina_future_cancel(f);
+}
+
+int
+main(int argc EINA_UNUSED, char *argv[] EINA_UNUSED)
+{
+ if (!eina_init())
+ {
+ fprintf(stderr, "Could not init eina\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!ecore_init())
+ {
+ fprintf(stderr, "Could not init ecore\n");
+ goto err_ecore;
+ }
+
+ _simple();
+ _alternate_error();
+ _chain_no_errors();
+ _chain_with_error();
+ _chain_inner_no_errors();
+ _chain_inner_errors();
+ _future_cancel();
+
+ ecore_main_loop_begin();
+
+ eina_shutdown();
+ ecore_shutdown();
+ return EXIT_SUCCESS;
+
+ err_ecore:
+ eina_shutdown();
+ return EXIT_FAILURE;
+}
diff --git a/src/tests/ecore/ecore_suite.c b/src/tests/ecore/ecore_suite.c
index 76656c4de7..eb81132366 100644
--- a/src/tests/ecore/ecore_suite.c
+++ b/src/tests/ecore/ecore_suite.c
@@ -29,6 +29,7 @@ static const Efl_Test_Case etc[] = {
{ "Ecore_Promise", ecore_test_ecore_promise },
{ "Ecore_Job", ecore_test_ecore_job },
{ "Ecore_Args", ecore_test_ecore_args },
+ { "Ecore_Promise2", ecore_test_ecore_promise2 },
{ NULL, NULL }
};
diff --git a/src/tests/ecore/ecore_suite.h b/src/tests/ecore/ecore_suite.h
index 31ca43d421..50df87f154 100644
--- a/src/tests/ecore/ecore_suite.h
+++ b/src/tests/ecore/ecore_suite.h
@@ -18,5 +18,6 @@ void ecore_test_ecore_file(TCase *tc);
void ecore_test_ecore_promise(TCase *tc);
void ecore_test_ecore_job(TCase *tc);
void ecore_test_ecore_args(TCase *tc);
+void ecore_test_ecore_promise2(TCase *tc);
#endif /* _ECORE_SUITE_H */
diff --git a/src/tests/ecore/ecore_test_promise2.c b/src/tests/ecore/ecore_test_promise2.c
new file mode 100644
index 0000000000..ea8d0d272e
--- /dev/null
+++ b/src/tests/ecore/ecore_test_promise2.c
@@ -0,0 +1,1286 @@
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#include <Ecore.h>
+#include <Eina.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <time.h>
+#include <stdarg.h>
+#include <Eo.h>
+#include "ecore_suite.h"
+
+#define CHAIN_SIZE (3)
+#define DEFAULT_ERROR (EFBIG)
+#define DEFAULT_MSG ("Future resolve is working!")
+#define DEFAULT_INT_VALUE (5466)
+#define DEFAULT_INT_VALUE_AS_STRING ("5466")
+#define DEFAULT_TIMEOUT (0.1)
+
+#define VALUE_TYPE_CHECK(_v, _type) \
+ do { \
+ ck_assert_ptr_eq(_v.type, _type); \
+} while(0)
+
+#define ERROR_CHECK(_v, _errno) \
+ do { \
+ Eina_Error _err; \
+ VALUE_TYPE_CHECK(_v, EINA_VALUE_TYPE_ERROR); \
+ fail_if(!eina_value_get(&_v, &_err)); \
+ ck_assert_int_eq(_err, _errno); \
+ } while (0)
+
+typedef struct _PromiseCtx {
+ Eina_Promise *p;
+ Ecore_Timer *t;
+ Eina_Bool fail;
+ Eina_Value *value;
+} PromiseCtx;
+
+typedef struct _Easy_Ctx {
+ Eina_Bool success_called;
+ Eina_Bool error_called;
+ Eina_Bool free_called;
+ Eina_Bool stop_loop;
+} Easy_Ctx;
+
+typedef struct _Race_Ctx {
+ Eina_Value value;
+ unsigned int success_idx;
+ unsigned int failed;
+ unsigned int success;
+} Race_Ctx;
+
+typedef struct _Race_Future_Ctx {
+ Race_Ctx *race_ctx;
+ unsigned int idx;
+ int value;
+} Race_Future_Ctx;
+
+#ifdef EINA_SAFETY_CHECKS
+
+#define LOG_CTX_MULTIPLE_FUNC_CTX_SET(_ctx, ...) \
+ do { \
+ _ctx.level = EINA_LOG_LEVEL_ERR; \
+ _ctx.did = EINA_FALSE; \
+ _ctx.just_fmt = EINA_FALSE; \
+ _ctx.func_ctx_idx = 0; \
+ _ctx.func_ctx = (struct Func_Ctx []){ __VA_ARGS__, {NULL, NULL}}; \
+ } while(0)
+
+#define LOG_CTX_SET(_ctx, _fnc, _msg) LOG_CTX_MULTIPLE_FUNC_CTX_SET(_ctx, {_fnc, _msg})
+
+typedef struct _Log_Ctx {
+ struct Func_Ctx {
+ const char *fnc;
+ const char *msg;
+ } *func_ctx;
+ int level;
+ int func_ctx_idx;
+ Eina_Bool did;
+ Eina_Bool just_fmt;
+} Log_Ctx;
+
+static void
+_eina_test_safety_print_cb(const Eina_Log_Domain *d,
+ Eina_Log_Level level,
+ const char *file,
+ const char *fnc, int line,
+ const char *fmt,
+ void *data,
+ va_list args)
+{
+ Log_Ctx *ctx = data;
+ va_list cp_args;
+ const char *str;
+
+ va_copy(cp_args, args);
+ str = va_arg(cp_args, const char *);
+ va_end(cp_args);
+
+ ck_assert_ptr_nonnull(ctx->func_ctx[ctx->func_ctx_idx].msg);
+ ck_assert_int_eq(level, ctx->level);
+ if (ctx->just_fmt)
+ ck_assert_str_eq(fmt, ctx->func_ctx[ctx->func_ctx_idx].msg);
+ else
+ {
+ ck_assert_str_eq(fmt, "%s");
+ ck_assert_str_eq(ctx->func_ctx[ctx->func_ctx_idx].msg, str);
+ }
+ ck_assert_str_eq(ctx->func_ctx[ctx->func_ctx_idx].fnc, fnc);
+ ctx->did = EINA_TRUE;
+ ctx->func_ctx_idx++;
+
+ (void)d;
+ (void)file;
+ (void)line;
+}
+#endif
+
+static void
+_cancel(void *data, const Eina_Promise *dead_ptr EINA_UNUSED)
+{
+ PromiseCtx *ctx = data;
+ if (ctx->t) ecore_timer_del(ctx->t);
+ ctx->t = NULL;
+ eina_value_free(ctx->value);
+ free(ctx);
+}
+
+static void
+_promise_cancel_test(void *data, const Eina_Promise *dead_ptr EINA_UNUSED)
+{
+ Eina_Bool *cancel_called = data;
+ *cancel_called = EINA_TRUE;
+}
+
+static Eina_Bool
+_simple_timeout(void *data)
+{
+ PromiseCtx *ctx = data;
+
+ if (ctx->fail) eina_promise_reject(ctx->p, DEFAULT_ERROR);
+ else
+ {
+ Eina_Value v;
+
+ fail_if(!eina_value_copy(ctx->value, &v));
+ eina_promise_resolve(ctx->p, v);
+ eina_value_free(ctx->value);
+ }
+ free(ctx);
+ return EINA_FALSE;
+}
+
+static Eina_Future_Scheduler *
+_future_scheduler_get(void)
+{
+ return efl_loop_future_scheduler_get(ecore_main_loop_get());
+}
+
+static PromiseCtx *
+_promise_ctx_new(void)
+{
+ PromiseCtx *ctx;
+ ctx = calloc(1, sizeof(PromiseCtx));
+ fail_if(!ctx);
+ ctx->p = eina_promise_new(_future_scheduler_get(), _cancel, ctx);
+ fail_if(!ctx->p);
+ return ctx;
+}
+
+static Eina_Future *
+_future_get(PromiseCtx *ctx, double timeout)
+{
+ Eina_Future *f;
+
+ f = eina_future_new(ctx->p);
+ fail_if(!f);
+ ctx->t = ecore_timer_add(timeout, _simple_timeout, ctx);
+ fail_if(!ctx->t);
+ return f;
+}
+
+static Eina_Future *
+_fail_future_get(void)
+{
+ PromiseCtx *ctx = _promise_ctx_new();
+ ctx->fail = EINA_TRUE;
+ return _future_get(ctx, DEFAULT_TIMEOUT);
+}
+
+static Eina_Future *
+_int_future_with_value_and_timeout(int value, double timeout)
+{
+ PromiseCtx *ctx = _promise_ctx_new();
+ ctx->value = eina_value_util_int_new(value);
+ fail_if(!ctx->value);
+ return _future_get(ctx, timeout);
+}
+
+static Eina_Future *
+_int_future_get(void)
+{
+ return _int_future_with_value_and_timeout(DEFAULT_INT_VALUE, DEFAULT_TIMEOUT);
+}
+
+static Eina_Future *
+_str_future_get(void)
+{
+ PromiseCtx *ctx = _promise_ctx_new();
+ ctx->value = eina_value_util_string_new(DEFAULT_MSG);
+ fail_if(!ctx->value);
+ return _future_get(ctx, DEFAULT_TIMEOUT);
+}
+
+static Eina_Value
+_simple_err(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ ERROR_CHECK(v, DEFAULT_ERROR);
+ ecore_main_loop_quit();
+ return v;
+}
+
+static Eina_Value
+_simple_ok(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ const char *msg;
+
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_STRING);
+ fail_if(!eina_value_get(&v, &msg));
+ ck_assert_str_eq(DEFAULT_MSG, msg);
+ ecore_main_loop_quit();
+ return v;
+}
+
+static Eina_Value
+_chain_stop(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ int *i = data;
+ fail_if(*i != CHAIN_SIZE);
+ ecore_main_loop_quit();
+ return v;
+}
+
+static Eina_Value
+_chain_no_error(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Eina_Value new_v;
+ static int count = DEFAULT_INT_VALUE;
+ int current_i;
+ int *i = data;
+
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_INT);
+ fail_if(!eina_value_get(&v, &current_i));
+ fail_if(current_i != count++);
+ fail_if(!eina_value_setup(&new_v, EINA_VALUE_TYPE_INT));
+ fail_if(!eina_value_set(&new_v, count));
+ (*i)++;
+ return new_v;
+}
+
+static Eina_Value
+_chain_error(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ int *i = data;
+
+ ERROR_CHECK(v, DEFAULT_ERROR);
+ (*i)++;
+ return v;
+}
+
+static Eina_Value
+_cancel_cb(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Eina_Value new_v;
+ int *cancel_count = data;
+
+ fail_if(!eina_value_setup(&new_v, EINA_VALUE_TYPE_INT));
+ ERROR_CHECK(v, ECANCELED);
+ (*cancel_count)++;
+ /* Although this function returns an INT Eina_Value, the next
+ _cancel_cb must receive a EINA_VALYE_TYPE_ERROR as ECANCELED */
+ return new_v;
+}
+
+static Eina_Value
+_inner_resolve(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Eina_Value new_v;
+ fail_if(!eina_value_setup(&new_v, EINA_VALUE_TYPE_STRING));
+ fail_if(!eina_value_set(&new_v, DEFAULT_MSG));
+ eina_promise_resolve(data, new_v);
+ return v;
+}
+
+static Eina_Value
+_inner_fail(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ eina_promise_reject(data, DEFAULT_ERROR);
+ return v;
+}
+
+static void
+_inner_promise_cancel(void *data EINA_UNUSED, const Eina_Promise *dead_ptr EINA_UNUSED)
+{
+ //This must never happen...
+ fail_if(EINA_FALSE);
+}
+
+static Eina_Value
+_future_promise_create(void *data, const Eina_Value v EINA_UNUSED, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Eina_Promise *p;
+
+ p = eina_promise_new(_future_scheduler_get(), _inner_promise_cancel, NULL);
+ fail_if(!p);
+ eina_future_then(_str_future_get(),
+ data ? _inner_fail : _inner_resolve,
+ p);
+ return eina_promise_as_value(p);
+}
+
+static Eina_Value
+_inner_future_last(void *data, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ if (data)
+ ERROR_CHECK(v, DEFAULT_ERROR);
+ else
+ {
+ const char *msg;
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_STRING);
+ fail_if(!eina_value_get(&v, &msg));
+ ck_assert_str_eq(DEFAULT_MSG, msg);
+ }
+ ecore_main_loop_quit();
+ return v;
+}
+
+static Eina_Value
+_convert_check(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ const char *number;
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_STRING);
+ fail_if(!eina_value_get(&v, &number));
+ ck_assert_str_eq(DEFAULT_INT_VALUE_AS_STRING, number);
+ ecore_main_loop_quit();
+ return v;
+}
+
+static Eina_Value
+_easy_success(void *data, const Eina_Value v)
+{
+ Easy_Ctx *ctx = data;
+ const char *msg;
+
+ ctx->success_called = EINA_TRUE;
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_STRING);
+ fail_if(!eina_value_get(&v, &msg));
+ ck_assert_str_eq(DEFAULT_MSG, msg);
+ return v;
+}
+
+static Eina_Value
+_easy_error(void *data, const Eina_Error err)
+{
+ Eina_Value v;
+ Easy_Ctx *ctx = data;
+ fail_if(err != EINVAL);
+ fail_if(!eina_value_setup(&v, EINA_VALUE_TYPE_ERROR));
+ fail_if(!eina_value_set(&v, err));
+ ctx->error_called = EINA_TRUE;
+ return v;
+}
+
+static void
+_easy_free(void *data, const Eina_Future *dead_future EINA_UNUSED)
+{
+ Easy_Ctx *ctx = data;
+ ctx->free_called = EINA_TRUE;
+ if (ctx->stop_loop) ecore_main_loop_quit();
+}
+
+static Eina_Value
+_all_cb(void *data, const Eina_Value array, const Eina_Future *dead EINA_UNUSED)
+{
+ unsigned int len, i, *expected_len = data;
+
+ VALUE_TYPE_CHECK(array, EINA_VALUE_TYPE_ARRAY);
+ len = eina_value_array_count(&array);
+ fail_if(len != *expected_len);
+
+ for (i = 0; i < len; i++)
+ {
+ Eina_Value v;
+
+ fail_if(!eina_value_array_get(&array, i, &v));
+ if (i % 2 == 0)
+ {
+ const char *msg;
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_STRING);
+ fail_if(!eina_value_get(&v, &msg));
+ ck_assert_str_eq(DEFAULT_MSG, msg);
+ }
+ else
+ {
+ int ivalue = 0;
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_INT);
+ fail_if(!eina_value_get(&v, &ivalue));
+ fail_if(ivalue != DEFAULT_INT_VALUE);
+ }
+ eina_value_flush(&v);
+ }
+ ecore_main_loop_quit();
+ return array;
+}
+
+static Eina_Value
+_future_all_count(void *data, const Eina_Value v, const Eina_Future *dead EINA_UNUSED)
+{
+ unsigned int *futures_called = data;
+ (*futures_called)++;
+ return v;
+}
+
+static Eina_Value
+_race_cb(void *data, const Eina_Value v, const Eina_Future *dead EINA_UNUSED)
+{
+ Race_Future_Ctx *future_ctx = data;
+ Race_Ctx *ctx = future_ctx->race_ctx;
+
+ if (v.type == EINA_VALUE_TYPE_ERROR)
+ {
+ Eina_Error err;
+ eina_value_get(&v, &err);
+ fail_if(err != ECANCELED);
+ ctx->failed++;
+ }
+ else if (v.type == EINA_VALUE_TYPE_INT)
+ {
+ int i;
+ fail_if(!eina_value_get(&v, &i));
+ fail_if(future_ctx->value != i);
+ ctx->success++;
+ ctx->success_idx = future_ctx->idx;
+ fail_if(!eina_value_copy(&v, &ctx->value));
+ }
+ else fail_if(EINA_TRUE); //This is not supposed to happen!
+ free(future_ctx);
+ return v;
+}
+
+static Eina_Value
+_race_end_cb(void *data, const Eina_Value v, const Eina_Future *dead EINA_UNUSED)
+{
+ Race_Ctx *ctx = data;
+ unsigned int idx;
+ Eina_Value_Struct *st;
+ Eina_Value r;
+
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_STRUCT);
+
+ st = eina_value_memory_get(&v);
+ fail_if(!st);
+ fail_if(st->desc != EINA_PROMISE_RACE_STRUCT_DESC);
+ fail_if(!eina_value_struct_get(&v, "index", &idx));
+ fail_if(idx != ctx->success_idx);
+ fail_if(!eina_value_struct_get(&v, "value", &r));
+ fail_if(eina_value_compare(&r, &ctx->value));
+ eina_value_flush(&r);
+ ecore_main_loop_quit();
+ return v;
+}
+
+START_TEST(efl_test_promise_future_success)
+{
+ Eina_Future *f;
+ fail_if(!ecore_init());
+ f = eina_future_then(_str_future_get(),
+ _simple_ok, NULL);
+ fail_if(!f);
+ ecore_main_loop_begin();
+ ecore_shutdown();
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_failure)
+{
+ Eina_Future *f;
+ fail_if(!ecore_init());
+ f = eina_future_then(_fail_future_get(),
+ _simple_err, NULL);
+ fail_if(!f);
+ ecore_main_loop_begin();
+ ecore_shutdown();
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_chain_no_error)
+{
+ Eina_Future *f;
+ static int i = 0;
+
+ fail_if(!ecore_init());
+ f = eina_future_chain(_int_future_get(),
+ {.cb = _chain_no_error, .data = &i},
+ {.cb = _chain_no_error, .data = &i},
+ {.cb = _chain_no_error, .data = &i},
+ {.cb = _chain_stop, .data = &i});
+ fail_if(!f);
+ ecore_main_loop_begin();
+ ecore_shutdown();
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_chain_error)
+{
+ Eina_Future *f;
+ static int i = 0;
+
+ fail_if(!ecore_init());
+ f = eina_future_chain(_fail_future_get(),
+ {.cb = _chain_error, .data = &i},
+ {.cb = _chain_error, .data = &i},
+ {.cb = _chain_error, .data = &i},
+ {.cb = _chain_stop, .data = &i});
+ fail_if(!f);
+ ecore_main_loop_begin();
+ ecore_shutdown();
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_cancel)
+{
+ fail_if(!ecore_init());
+ int i;
+
+ for (i = 0; i < 3; i++)
+ {
+ Eina_Promise *p;
+ Eina_Future *first, *last, *middle;
+ int cancel_count = 0;
+ Eina_Bool cancel_called = EINA_FALSE;
+
+ p = eina_promise_new(_future_scheduler_get(), _promise_cancel_test, &cancel_called);
+ fail_if(!p);
+ first = eina_future_new(p);
+ fail_if(!first);
+ if (i == 2)
+ {
+ Eina_Future *f;
+ last = NULL;
+ f = eina_future_then(first, _cancel_cb, &cancel_count);
+ fail_if(!f);
+ middle = eina_future_then(f, _cancel_cb, &cancel_count);
+ fail_if(!middle);
+ f = eina_future_then(middle, _cancel_cb, &cancel_count);
+ fail_if(!f);
+ }
+ else
+ {
+ middle = NULL;
+ last = eina_future_chain(first,
+ {.cb = _cancel_cb, .data = &cancel_count},
+ {.cb = _cancel_cb, .data = &cancel_count},
+ {.cb = _cancel_cb, .data = &cancel_count});
+ fail_if(!last);
+ }
+ if (i == 0)
+ eina_future_cancel(last);
+ else if (i == 1)
+ eina_future_cancel(first);
+ else
+ eina_future_cancel(middle);
+ fail_if(cancel_count != CHAIN_SIZE);
+ fail_if(!cancel_called);
+ }
+ ecore_shutdown();
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_inner_promise)
+{
+ Eina_Future *f;
+
+ fail_if(!ecore_init());
+ f = eina_future_chain(_str_future_get(),
+ {.cb = _future_promise_create, .data = NULL},
+ {.cb = _inner_future_last, .data = NULL});
+ fail_if(!f);
+ ecore_main_loop_begin();
+ ecore_shutdown();
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_inner_promise_fail)
+{
+ Eina_Future *f;
+ void *data =(void *) 0x01;
+
+ fail_if(!ecore_init());
+ f = eina_future_chain(_str_future_get(),
+ {.cb = _future_promise_create, .data = data},
+ {.cb = _inner_future_last, .data = data});
+ fail_if(!f);
+ ecore_main_loop_begin();
+ ecore_shutdown();
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_implicit_cancel)
+{
+ Eina_Promise *p;
+ Eina_Future *f;
+ int cancel_count = 0;
+ Eina_Bool cancel_called = EINA_FALSE;
+ Eina_Value v = EINA_VALUE_EMPTY;
+
+ fail_if(!ecore_init());
+
+ p = eina_promise_new(_future_scheduler_get(), _promise_cancel_test, &cancel_called);
+ fail_if(!p);
+ f = eina_future_new(p);
+ fail_if(!f);
+ f = eina_future_chain(f,
+ {.cb = _cancel_cb, .data = &cancel_count},
+ {.cb = _cancel_cb, .data = &cancel_count},
+ {.cb = _cancel_cb, .data = &cancel_count});
+ fail_if(!f);
+ eina_promise_resolve(p, v);
+ /*
+ The promise was resolved, but the mainloop is not running.
+ Since ecore_shutdown() will be called all the futures must be cancelled
+ */
+ ecore_shutdown();
+ //All the futures were cancelled at this point
+ fail_if(cancel_count != CHAIN_SIZE);
+ //Cancel should not be called, since we called eina_promise_resolve()
+ fail_if(cancel_called);
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_convert)
+{
+ Eina_Future *f;
+
+ fail_if(!ecore_init());
+ f = eina_future_chain(_int_future_get(),
+ eina_future_cb_convert_to(EINA_VALUE_TYPE_STRING),
+ { .cb = _convert_check, .data = NULL });
+ fail_if(!f);
+ ecore_main_loop_begin();
+ ecore_shutdown();
+
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_easy)
+{
+ Eina_Future *f;
+ Easy_Ctx easy1 = { 0 };
+ Easy_Ctx easy2 = { 0 };
+ Easy_Ctx easy3 = { 0 };
+
+ easy3.stop_loop = EINA_TRUE;
+ fail_if(!ecore_init());
+ f = eina_future_then_from_desc(_str_future_get(),
+ eina_future_cb_easy(_easy_success,
+ _easy_error,
+ _easy_free,
+ EINA_VALUE_TYPE_STRING,
+ &easy1));
+ fail_if(!f);
+ f = eina_future_then_easy(f, _easy_success, _easy_error,
+ _easy_free, NULL, &easy2);
+ fail_if(!f);
+ f = eina_future_chain_easy(f, {_easy_success, _easy_error,
+ _easy_free, EINA_VALUE_TYPE_INT, &easy3});
+ fail_if(!f);
+ ecore_main_loop_begin();
+ ecore_shutdown();
+ fail_if(!(easy1.success_called && !easy1.error_called && easy1.free_called));
+ fail_if(!(easy2.success_called && !easy2.error_called && easy2.free_called));
+ fail_if(!(!easy3.success_called && easy3.error_called && easy3.free_called));
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_all)
+{
+ Eina_Future *futures[11];
+ unsigned int i, futures_called = 0, len = EINA_C_ARRAY_LENGTH(futures);
+
+ fail_if(!ecore_init());
+ for (i = 0; i < len - 1; i++)
+ {
+ Eina_Future *f;
+ if (i % 2 == 0)
+ f = _str_future_get();
+ else
+ f = _int_future_get();
+ fail_if(!f);
+ futures[i] = eina_future_then(f, _future_all_count, &futures_called);
+ fail_if(!futures[i]);
+ }
+
+ futures[--len] = EINA_FUTURE_SENTINEL;
+ fail_if(!eina_future_then(eina_future_all_array(futures), _all_cb, &len));
+ ecore_main_loop_begin();
+ ecore_shutdown();
+ fail_if(futures_called != len);
+}
+END_TEST
+
+START_TEST(efl_test_promise_future_race)
+{
+ Race_Ctx ctx = { 0 };
+ Eina_Future *futures[11];
+ unsigned int i, len = EINA_C_ARRAY_LENGTH(futures);
+ double timeouts[10] = {
+ 2.0, 1.0, 0.5, 0.1, 4.5, 2.3, 5.6, 1.0, 0.5, 0.3
+ };
+
+ srand(time(NULL));
+ fail_if(!ecore_init());
+ for (i = 0; i < len - 1; i++)
+ {
+ Race_Future_Ctx *future_ctx = calloc(1, sizeof(Race_Future_Ctx));
+ fail_if(!future_ctx);
+ future_ctx->race_ctx = &ctx;
+ future_ctx->idx = i;
+ future_ctx->value = rand() % RAND_MAX;
+ futures[i] = eina_future_then(_int_future_with_value_and_timeout(future_ctx->value, timeouts[i]),
+ _race_cb, future_ctx);
+ fail_if(!futures[i]);
+ }
+
+ futures[--len] = EINA_FUTURE_SENTINEL;
+ fail_if(!eina_future_then(eina_future_race_array(futures),
+ _race_end_cb, &ctx));
+ ecore_main_loop_begin();
+ eina_value_flush(&ctx.value);
+ ecore_shutdown();
+ fail_if(ctx.success != 1);
+ fail_if(ctx.failed != (len - 1));
+}
+END_TEST
+
+static Eina_Value
+_eo_future1_ok(Eo *eo EINA_UNUSED, const Eina_Value v)
+{
+ const char *number;
+
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_STRING);
+ fail_if(!eina_value_get(&v, &number));
+ ck_assert_str_eq(DEFAULT_INT_VALUE_AS_STRING, number);
+ return v;
+}
+
+static Eina_Value
+_eo_future1_err(Eo *eo EINA_UNUSED, Eina_Error err EINA_UNUSED)
+{
+ //Should not happen
+ fail_if(EINA_TRUE);
+}
+
+static Eina_Value
+_eo_future2_ok(Eo *eo EINA_UNUSED, const Eina_Value v)
+{
+ //Should not happen
+ fail_if(EINA_TRUE);
+ return v;
+}
+
+static Eina_Value
+_eo_future2_err(Eo *eo EINA_UNUSED, Eina_Error err)
+{
+ Eina_Value v;
+
+ fail_if(err != EINVAL);
+ fail_if(!eina_value_setup(&v, EINA_VALUE_TYPE_INT));
+ fail_if(!eina_value_set(&v, DEFAULT_INT_VALUE));
+ return v;
+}
+
+static void
+_eo_future_free(Eo *eo, const Eina_Future *dead EINA_UNUSED)
+{
+ int *free_called = efl_key_data_get(eo, "free_called");
+ (*free_called)++;
+}
+
+static Eina_Value
+_eo_chain_stop(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead_future EINA_UNUSED)
+{
+ int ivalue = 0;
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_INT);
+ fail_if(!eina_value_get(&v, &ivalue));
+ ck_assert_int_eq(ivalue, DEFAULT_INT_VALUE);
+ ecore_main_loop_quit();
+ return v;
+}
+
+START_TEST(efl_test_promise_eo)
+{
+ Eina_Future *f;
+ Eo *obj;
+ int free_called = 0;
+
+ fail_if(!efl_object_init());
+ fail_if(!ecore_init());
+
+ //Use a random object..
+ obj = efl_add(EFL_IO_BUFFER_CLASS, NULL);
+ fail_if(!obj);
+ efl_key_data_set(obj, "free_called", &free_called);
+ f = eina_future_chain(_int_future_get(),
+ eina_future_cb_convert_to(EINA_VALUE_TYPE_STRING),
+ efl_future_cb(obj, _eo_future1_ok, _eo_future1_err, _eo_future_free, EINA_VALUE_TYPE_STRING),
+ efl_future_cb(obj, _eo_future2_ok, _eo_future2_err, _eo_future_free, EINA_VALUE_TYPE_INT),
+ { .cb = _eo_chain_stop });
+ fail_if(!f);
+ ecore_main_loop_begin();
+ efl_unref(obj);
+ ecore_shutdown();
+ efl_object_shutdown();
+ ck_assert_int_eq(free_called, 2);
+}
+END_TEST
+
+static Eina_Value
+_eo_future_link_success(Eo *eo EINA_UNUSED, const Eina_Value v)
+{
+ //This should never happen
+ fail_if(EINA_TRUE);
+ return v;
+}
+
+static Eina_Value
+_eo_future_link_err(Eo *eo, Eina_Error err)
+{
+ int *err_called = efl_key_data_get(eo, "err_called");
+ Eina_Value v;
+
+ fail_if(!err_called);
+ ck_assert_int_eq(err, ECANCELED);
+ fail_if(!eina_value_setup(&v, EINA_VALUE_TYPE_ERROR));
+ fail_if(!eina_value_set(&v, err));
+ (*err_called)++;
+ return v;
+}
+
+static Eina_Value
+_eo_link_chain_end(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead EINA_UNUSED)
+{
+ int *err_called = data;
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_ERROR);
+ ERROR_CHECK(v, ECANCELED);
+ (*err_called)++;
+ return v;
+}
+
+START_TEST(efl_test_promise_eo_link)
+{
+ Eina_Future *f;
+ Eo *obj;
+ int err_called = 0;
+
+ fail_if(!efl_object_init());
+ fail_if(!ecore_init());
+
+ //Use a random object..
+ obj = efl_add(EFL_IO_BUFFER_CLASS, NULL);
+ fail_if(!obj);
+
+ efl_key_data_set(obj, "err_called", &err_called);
+ fail_if(!efl_key_data_get(obj, "err_called"));
+ f = efl_future_chain(obj, _int_future_get(),
+ {.success = _eo_future_link_success, .error = _eo_future_link_err},
+ {.success = _eo_future_link_success, .error = _eo_future_link_err},
+ {.success = _eo_future_link_success, .error = _eo_future_link_err},
+ {.success = _eo_future_link_success, .error = _eo_future_link_err},
+ {.success = _eo_future_link_success, .error = _eo_future_link_err});
+ fail_if(!f);
+ f = eina_future_then(f, _eo_link_chain_end, &err_called);
+ fail_if(!f);
+ /*
+ Since the mainloop is not running and the object is deleted the whole chain must be cancelled.
+ */
+ efl_unref(obj);
+ ecore_shutdown();
+ efl_object_shutdown();
+ ck_assert_int_eq(err_called, 6);
+}
+END_TEST
+
+static Eina_Value
+_err_ignored(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *f EINA_UNUSED)
+{
+ //Must be NULL since the error must be ignored.
+ VALUE_TYPE_CHECK(v, NULL);
+ ecore_main_loop_quit();
+ return v;
+}
+
+START_TEST(efl_test_promise_future_ignore_error)
+{
+ Eina_Future *f;
+
+ fail_if(!ecore_init());
+ f = _fail_future_get();
+ fail_if(!f);
+ eina_future_chain(f, eina_future_cb_ignore_error(DEFAULT_ERROR),
+ {.cb = _err_ignored});
+ ecore_main_loop_begin();
+ ecore_shutdown();
+}
+END_TEST
+
+#define PROMISE_LOG_DOMAIN_STR ("promise_test_domain")
+
+typedef struct _Promise_Log_Ctx {
+ Eina_Future_Cb_Log_Desc dbg;
+ Eina_Future_Cb_Log_Desc crit;
+ Eina_Future_Cb_Log_Desc warn;
+ Eina_Future_Cb_Log_Desc info;
+ Eina_Future_Cb_Log_Desc err;
+ Eina_Bool dbg_log_ok;
+ Eina_Bool crit_log_ok;
+ Eina_Bool warn_log_ok;
+ Eina_Bool info_log_ok;
+ Eina_Bool err_log_ok;
+} Promise_Log_Ctx;
+
+static Eina_Value
+_log_quit(void *data EINA_UNUSED, const Eina_Value v, const Eina_Future *dead EINA_UNUSED)
+{
+ int ivalue;
+
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_INT);
+ fail_if(!eina_value_get(&v, &ivalue));
+ fail_if(ivalue != DEFAULT_INT_VALUE);
+ ecore_main_loop_quit();
+ return v;
+}
+
+static void
+_log_test(const Eina_Log_Domain *d,
+ Eina_Log_Level level,
+ const char *file, const char *fnc, int line,
+ const char *fmt, void *data, va_list args)
+{
+ Promise_Log_Ctx *ctx = data;
+ Eina_Bool *log_ok;
+ Eina_Future_Cb_Log_Desc *desc;
+ va_list cpy;
+ const char *prefix, *suffix, *value;
+
+ if (strcmp(d->name, PROMISE_LOG_DOMAIN_STR))
+ return;
+
+ switch (level)
+ {
+ case EINA_LOG_LEVEL_DBG:
+ log_ok = &ctx->dbg_log_ok;
+ desc = &ctx->dbg;
+ break;
+ case EINA_LOG_LEVEL_CRITICAL:
+ log_ok = &ctx->crit_log_ok;
+ desc = &ctx->crit;
+ break;
+ case EINA_LOG_LEVEL_WARN:
+ log_ok = &ctx->warn_log_ok;
+ desc = &ctx->warn;
+ break;
+ case EINA_LOG_LEVEL_INFO:
+ log_ok = &ctx->info_log_ok;
+ desc = &ctx->info;
+ break;
+ default:
+ log_ok = &ctx->err_log_ok;
+ desc = &ctx->err;
+ }
+
+ ck_assert_str_eq(fnc, desc->func);
+ ck_assert_str_eq(file, desc->file);
+ ck_assert_str_eq(fmt, "%s%s%s");
+ ck_assert_int_eq(desc->line, line);
+ //FIXME: REmove this check
+ ck_assert_int_eq(desc->level, level);
+ va_copy(cpy, args);
+ prefix = va_arg(cpy, const char *);
+ value = va_arg(cpy, const char *);
+ suffix = va_arg(cpy, const char *);
+ ck_assert_str_eq(prefix, desc->prefix ? desc->prefix : "");
+ ck_assert_str_eq(suffix, desc->suffix ? desc->suffix : "");
+ ck_assert_str_eq(value, DEFAULT_INT_VALUE_AS_STRING);
+ va_end(cpy);
+ *log_ok = EINA_TRUE;
+}
+
+START_TEST(efl_test_promise_log)
+{
+ Promise_Log_Ctx ctx = { 0 };
+ Eina_Future *f;
+ int domain;
+
+ fail_if(!ecore_init());
+
+ domain = eina_log_domain_register(PROMISE_LOG_DOMAIN_STR, EINA_COLOR_CYAN);
+ fail_if(domain < 0);
+ eina_log_domain_level_set(PROMISE_LOG_DOMAIN_STR, EINA_LOG_LEVEL_DBG);
+ ctx.dbg = (Eina_Future_Cb_Log_Desc){"dbg prefix:", " dbg suffix", __FILE__,
+ __FUNCTION__, EINA_LOG_LEVEL_DBG,
+ domain, __LINE__};
+ ctx.crit = (Eina_Future_Cb_Log_Desc){NULL, NULL, __FILE__,
+ __FUNCTION__, EINA_LOG_LEVEL_CRITICAL,
+ domain, __LINE__};
+ ctx.warn = (Eina_Future_Cb_Log_Desc){"warn prefix:", NULL, __FILE__,
+ __FUNCTION__, EINA_LOG_LEVEL_WARN,
+ domain, __LINE__};
+ ctx.err = (Eina_Future_Cb_Log_Desc){NULL, " err suffix", __FILE__,
+ __FUNCTION__, EINA_LOG_LEVEL_ERR,
+ domain, __LINE__};
+ ctx.info = (Eina_Future_Cb_Log_Desc){"info prefix:", " info suffix",
+ __FILE__, __FUNCTION__, EINA_LOG_LEVEL_INFO,
+ domain, __LINE__};
+ eina_log_print_cb_set(_log_test, &ctx);
+ f = eina_future_chain(_int_future_get(),
+ eina_future_cb_log_from_desc(ctx.dbg),
+ eina_future_cb_log_from_desc(ctx.crit),
+ eina_future_cb_log_from_desc(ctx.warn),
+ eina_future_cb_log_from_desc(ctx.err),
+ eina_future_cb_log_from_desc(ctx.info),
+ { _log_quit });
+ fail_if(!f);
+ ecore_main_loop_begin();
+ eina_log_domain_unregister(domain);
+ ecore_shutdown();
+ fail_if(!ctx.dbg_log_ok);
+ fail_if(!ctx.crit_log_ok);
+ fail_if(!ctx.warn_log_ok);
+ fail_if(!ctx.err_log_ok);
+ fail_if(!ctx.info_log_ok);
+}
+END_TEST
+
+#ifdef EINA_SAFETY_CHECKS
+
+static void
+_dummy_cancel(void *data EINA_UNUSED, const Eina_Promise *dead EINA_UNUSED)
+{
+}
+
+START_TEST(efl_test_promise_null)
+{
+ Log_Ctx ctx = { 0 };
+ Eina_Promise *p;
+
+ fail_if(!ecore_init());
+
+ eina_log_print_cb_set(_eina_test_safety_print_cb, &ctx);
+ LOG_CTX_SET(ctx, "eina_promise_new", "safety check failed: scheduler == NULL");
+ p = eina_promise_new(NULL, _dummy_cancel, NULL);
+ ck_assert_ptr_null(p);
+ fail_unless(ctx.did);
+
+ LOG_CTX_SET(ctx, "eina_promise_new", "safety check failed: cancel_cb == NULL");
+ p = eina_promise_new(_future_scheduler_get(), NULL, NULL);
+ ck_assert_ptr_null(p);
+ fail_unless(ctx.did);
+
+ ecore_shutdown();
+}
+END_TEST
+
+START_TEST(efl_test_promise_reject_resolve_null)
+{
+ Log_Ctx ctx = { 0 };
+ Eina_Value v;
+
+ fail_if(!ecore_init());
+
+ LOG_CTX_SET(ctx, "eina_promise_resolve", "safety check failed: (p) == NULL");
+ eina_log_print_cb_set(_eina_test_safety_print_cb, &ctx);
+ eina_value_setup(&v, EINA_VALUE_TYPE_INT);
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_INT);
+ eina_promise_resolve(NULL, v);
+ fail_unless(ctx.did);
+
+ LOG_CTX_SET(ctx, "eina_promise_reject", "safety check failed: (p) == NULL");
+ eina_promise_reject(NULL, DEFAULT_ERROR);
+ fail_unless(ctx.did);
+ ecore_shutdown();
+}
+END_TEST
+
+static Eina_Value
+_future_null_cb(void *data, const Eina_Value v, const Eina_Future *dead)
+{
+ int err;
+ int *cb_called = data;
+
+ ck_assert_ptr_null(dead);
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_ERROR);
+ fail_if(!eina_value_get(&v, &err));
+ ck_assert_int_eq(err, EINVAL);
+ (*cb_called)++;
+ return v;
+}
+
+static Eina_Value
+_future_easy_null_success(void *data EINA_UNUSED, const Eina_Value v)
+{
+ //Should not happen
+ fail_if(EINA_TRUE);
+ return v;
+}
+
+static Eina_Value
+_future_easy_null_err(void *data, Eina_Error err)
+{
+ int *cb_called = data;
+ ck_assert_int_eq(err, EINVAL);
+ (*cb_called)++;
+ return eina_value_error_init(err);
+}
+
+static void
+_future_easy_null_free(void *data, const Eina_Future *dead)
+{
+ int *cb_called = data;
+ ck_assert_ptr_null(dead);
+ (*cb_called)++;
+}
+
+START_TEST(efl_test_future_null)
+{
+ Eina_Future *f;
+ Log_Ctx ctx = { 0 };
+ int cb_called = 0;
+ int easy_cb_calls = 0;
+
+ fail_if(!ecore_init());
+
+ LOG_CTX_SET(ctx, "eina_future_then_from_desc", "safety check failed: (prev) == NULL");
+ eina_log_print_cb_set(_eina_test_safety_print_cb, &ctx);
+ f = eina_future_then(NULL, _future_null_cb, &cb_called);
+ ck_assert_ptr_null(f);
+
+ ck_assert_int_eq(cb_called, 1);
+
+ cb_called = 0;
+ LOG_CTX_SET(ctx, "eina_future_chain_array", "safety check failed: (prev) == NULL");
+ f = eina_future_chain(NULL,
+ eina_future_cb_easy(_future_easy_null_success,
+ _future_easy_null_err,
+ _future_easy_null_free,
+ NULL, &easy_cb_calls),
+ {_future_null_cb, &cb_called},
+ {_future_null_cb, &cb_called},
+ {_future_null_cb, &cb_called},
+ {_future_null_cb, &cb_called},
+ {_future_null_cb, &cb_called});
+ ck_assert_ptr_null(f);
+ ck_assert_int_eq(cb_called, 5);
+ ck_assert_int_eq(easy_cb_calls, 2);
+
+ easy_cb_calls = 0;
+ LOG_CTX_SET(ctx, "eina_future_chain_easy_array", "safety check failed: (prev) == NULL");
+ f = eina_future_chain_easy(NULL,
+ {_future_easy_null_success,
+ _future_easy_null_err,
+ _future_easy_null_free,
+ NULL, &easy_cb_calls});
+ ck_assert_ptr_null(f);
+ ck_assert_int_eq(easy_cb_calls, 2);
+ ecore_shutdown();
+}
+END_TEST
+
+static Eina_Value
+_future_race_all_null_cb(void *data, const Eina_Value v, const Eina_Future *dead EINA_UNUSED)
+{
+ int err;
+ int *cb_called = data;
+
+ VALUE_TYPE_CHECK(v, EINA_VALUE_TYPE_ERROR);
+ fail_if(!eina_value_get(&v, &err));
+ ck_assert_int_eq(err, ENOMEM);
+ (*cb_called)++;
+ return v;
+}
+
+START_TEST(efl_test_future_all_null)
+{
+ Log_Ctx ctx = { 0 };
+ unsigned i, len;
+ int cb_called = 0;
+ Eina_Future *futures[11] = { 0 }, *f;
+
+ fail_if(!ecore_init());
+
+ eina_log_print_cb_set(_eina_test_safety_print_cb, &ctx);
+
+ len = EINA_C_ARRAY_LENGTH(futures);
+ len--;
+ for (i = 0; i < len; i++)
+ {
+ futures[i] = eina_future_then(_int_future_get(),
+ _future_race_all_null_cb, &cb_called);
+ fail_if(!futures[i]);
+ }
+
+ LOG_CTX_MULTIPLE_FUNC_CTX_SET(ctx,
+ {"promise_proxy_of_future_array_create", "safety check failed: array[i] == NULL"},
+ {"eina_promise_all_array", "safety check failed: r is false"});
+ //The last future is NULL, which may cause the cancel.
+ f = eina_future_all_array(futures);
+ ck_assert_ptr_null(f);
+ ecore_shutdown();
+ ck_assert_int_eq(cb_called, len);
+}
+END_TEST
+
+START_TEST(efl_test_future_race_null)
+{
+ Log_Ctx ctx = { 0 };
+ unsigned i, len;
+ int cb_called = 0;
+ Eina_Future *futures[11] = { 0 }, *f;
+
+ fail_if(!ecore_init());
+
+ eina_log_print_cb_set(_eina_test_safety_print_cb, &ctx);
+
+ len = EINA_C_ARRAY_LENGTH(futures);
+ len--;
+ for (i = 0; i < len; i++)
+ {
+ futures[i] = eina_future_then(_int_future_get(),
+ _future_race_all_null_cb, &cb_called);
+ fail_if(!futures[i]);
+ }
+
+ LOG_CTX_MULTIPLE_FUNC_CTX_SET(ctx,
+ {"promise_proxy_of_future_array_create", "safety check failed: array[i] == NULL"},
+ {"eina_promise_race_array", "safety check failed: r is false"});
+ //The last future is NULL, which may cause the cancel.
+ f = eina_future_race_array(futures);
+ ck_assert_ptr_null(f);
+ ecore_shutdown();
+ ck_assert_int_eq(cb_called, len);
+}
+END_TEST
+
+#endif
+
+
+void ecore_test_ecore_promise2(TCase *tc)
+{
+ tcase_add_test(tc, efl_test_promise_future_success);
+ tcase_add_test(tc, efl_test_promise_future_failure);
+ tcase_add_test(tc, efl_test_promise_future_chain_no_error);
+ tcase_add_test(tc, efl_test_promise_future_chain_error);
+ tcase_add_test(tc, efl_test_promise_future_cancel);
+ tcase_add_test(tc, efl_test_promise_future_implicit_cancel);
+ tcase_add_test(tc, efl_test_promise_future_inner_promise);
+ tcase_add_test(tc, efl_test_promise_future_inner_promise_fail);
+ tcase_add_test(tc, efl_test_promise_future_convert);
+ tcase_add_test(tc, efl_test_promise_future_easy);
+ tcase_add_test(tc, efl_test_promise_future_all);
+ tcase_add_test(tc, efl_test_promise_future_race);
+ tcase_add_test(tc, efl_test_promise_future_ignore_error);
+ tcase_add_test(tc, efl_test_promise_log);
+ //FIXME: We should move this to EO tests, however they depend on Ecore...
+ tcase_add_test(tc, efl_test_promise_eo);
+ tcase_add_test(tc, efl_test_promise_eo_link);
+
+#ifdef EINA_SAFETY_CHECKS
+ tcase_add_test(tc, efl_test_promise_null);
+ tcase_add_test(tc, efl_test_promise_reject_resolve_null);
+ tcase_add_test(tc, efl_test_future_null);
+ tcase_add_test(tc, efl_test_future_all_null);
+ tcase_add_test(tc, efl_test_future_race_null);
+#endif
+}