Eina_Promise/Eina_Future: Add example and tests.
authorGuilherme Iscaro <iscaro@profusion.mobi>
Tue, 8 Aug 2017 21:11:23 +0000 (18:11 -0300)
committerGuilherme Iscaro <iscaro@profusion.mobi>
Mon, 4 Sep 2017 13:24:00 +0000 (10:24 -0300)
src/Makefile_Ecore.am
src/examples/ecore/Makefile.am
src/examples/ecore/ecore_promise2_example.c [new file with mode: 0644]
src/tests/ecore/ecore_suite.c
src/tests/ecore/ecore_suite.h
src/tests/ecore/ecore_test_promise2.c [new file with mode: 0644]

index c5be098bf7d3652d38f1efe70931e166806feea7..4158ae51852ee829a01472fd2114bfbea4e3c444 100644 (file)
@@ -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
index e10dfc7827e77764935b3bf2623dd8ec05a5de58..9ab288c594c1a081932d2770d61ae152a7bcf0a4 100644 (file)
@@ -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 (file)
index 0000000..34fffc0
--- /dev/null
@@ -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;
+}
index 76656c4de7c77aa14c04c0407ec8680ab24b746d..eb811323662559305cba1b9586412708c8defe0a 100644 (file)
@@ -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 }
 };
 
index 31ca43d4212e745013baff0758e7093cf5fc63f7..50df87f154c812083429cf337e2165acb8e0c17f 100644 (file)
@@ -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 (file)
index 0000000..ea8d0d2
--- /dev/null
@@ -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
+}