[vlc-commits] executor: add unit tests

Romain Vimont git at videolan.org
Fri Oct 23 17:48:03 CEST 2020


vlc | branch: master | Romain Vimont <rom1v at videolabs.io> | Mon Sep  7 15:12:11 2020 +0200| [fa2c984e92add942f46d04cf4408857a932b962d] | committer: Alexandre Janniaux

executor: add unit tests

Signed-off-by: Alexandre Janniaux <ajanni at videolabs.io>

> http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=fa2c984e92add942f46d04cf4408857a932b962d
---

 src/Makefile.am     |   2 +
 src/test/executor.c | 282 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 284 insertions(+)

diff --git a/src/Makefile.am b/src/Makefile.am
index 5b240edbab..4803093c93 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -575,6 +575,7 @@ fourcc_tables.h: fourcc_gen$(BUILDEXEEXT)
 check_PROGRAMS = \
 	test_block \
 	test_dictionary \
+	test_executor \
 	test_i18n_atof \
 	test_interrupt \
 	test_list \
@@ -603,6 +604,7 @@ test_block_LDADD = $(LDADD) $(LIBS_libvlccore)
 test_block_DEPENDENCIES =
 
 test_dictionary_SOURCES = test/dictionary.c
+test_executor_SOURCES = test/executor.c
 test_i18n_atof_SOURCES = test/i18n_atof.c
 test_interrupt_SOURCES = test/interrupt.c
 test_interrupt_LDADD = $(LDADD) $(LIBS_libvlccore)
diff --git a/src/test/executor.c b/src/test/executor.c
new file mode 100644
index 0000000000..9acc25c076
--- /dev/null
+++ b/src/test/executor.c
@@ -0,0 +1,282 @@
+/*****************************************************************************
+ * src/test/executor.c
+ *****************************************************************************
+ * Copyright (C) 2020 Videolabs, VLC authors and VideoLAN
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#undef NDEBUG
+
+#include <assert.h>
+
+#include <vlc_common.h>
+#include <vlc_executor.h>
+#include <vlc_tick.h>
+
+struct data
+{
+    vlc_mutex_t lock;
+    vlc_cond_t cond;
+    int started;
+    int ended;
+    vlc_tick_t delay;
+};
+
+static void InitData(struct data *data)
+{
+    vlc_mutex_init(&data->lock);
+    vlc_cond_init(&data->cond);
+    data->started = 0;
+    data->ended = 0;
+    data->delay = 0;
+}
+
+static void RunIncrement(void *userdata)
+{
+    struct data *data = userdata;
+
+    vlc_mutex_lock(&data->lock);
+    ++data->started;
+    vlc_mutex_unlock(&data->lock);
+
+    if (data->delay > 0)
+        vlc_tick_sleep(data->delay);
+
+    vlc_mutex_lock(&data->lock);
+    ++data->ended;
+    vlc_mutex_unlock(&data->lock);
+
+    vlc_cond_signal(&data->cond);
+}
+
+static void test_single_runnable(void)
+{
+    vlc_executor_t *executor = vlc_executor_New(1);
+    assert(executor);
+
+    struct data data;
+    InitData(&data);
+
+    struct vlc_runnable runnable = {
+        .run = RunIncrement,
+        .userdata = &data,
+    };
+
+    vlc_executor_Submit(executor, &runnable);
+
+    vlc_mutex_lock(&data.lock);
+    while (data.ended == 0)
+        vlc_cond_wait(&data.cond, &data.lock);
+    vlc_mutex_unlock(&data.lock);
+
+    assert(data.ended == 1);
+
+    vlc_executor_Delete(executor);
+}
+
+static void test_multiple_runnables(void)
+{
+    vlc_executor_t *executor = vlc_executor_New(3);
+    assert(executor);
+
+    struct data shared_data;
+    InitData(&shared_data);
+
+    struct vlc_runnable runnables[300];
+    for (int i = 0; i < 300; ++i)
+    {
+        struct vlc_runnable *runnable = &runnables[i];
+        runnable->run = RunIncrement;
+        runnable->userdata = &shared_data;
+        vlc_executor_Submit(executor, runnable);
+    }
+
+    vlc_mutex_lock(&shared_data.lock);
+    while (shared_data.ended < 300)
+        vlc_cond_wait(&shared_data.cond, &shared_data.lock);
+    vlc_mutex_unlock(&shared_data.lock);
+
+    assert(shared_data.ended == 300);
+
+    vlc_executor_Delete(executor);
+}
+
+static void test_blocking_delete(void)
+{
+    vlc_executor_t *executor = vlc_executor_New(1);
+    assert(executor);
+
+    struct data data;
+    InitData(&data);
+
+    data.delay = VLC_TICK_FROM_MS(100);
+
+    struct vlc_runnable runnable = {
+        .run = RunIncrement,
+        .userdata = &data,
+    };
+
+    vlc_executor_Submit(executor, &runnable);
+
+    /* Wait for the runnable to be started */
+    vlc_mutex_lock(&data.lock);
+    while (data.started == 0)
+        vlc_cond_wait(&data.cond, &data.lock);
+    vlc_mutex_unlock(&data.lock);
+
+    /* The runnable sleeps for about 100ms */
+
+    vlc_executor_Delete(executor);
+
+    vlc_mutex_lock(&data.lock);
+    /* The executor must wait the end of running tasks on delete */
+    assert(data.ended == 1);
+    vlc_mutex_unlock(&data.lock);
+}
+
+static void test_cancel(void)
+{
+    vlc_executor_t *executor = vlc_executor_New(4);
+    assert(executor);
+
+    struct data shared_data;
+    InitData(&shared_data);
+
+    shared_data.delay = VLC_TICK_FROM_MS(100);
+
+    /* Submit 40 tasks taking at least 100ms on an executor with at most 4
+     * threads */
+
+    struct vlc_runnable runnables[40];
+    for (int i = 0; i < 40; ++i)
+    {
+        struct vlc_runnable *runnable = &runnables[i];
+        runnable->run = RunIncrement;
+        runnable->userdata = &shared_data;
+        vlc_executor_Submit(executor, runnable);
+    }
+
+    /* Wait a bit (in two lines to avoid harmful_delay() warning) */
+    vlc_tick_t delay = VLC_TICK_FROM_MS(150);
+    vlc_tick_sleep(delay);
+
+    int canceled = 0;
+    for (int i = 0; i < 40; ++i)
+    {
+        if (vlc_executor_Cancel(executor, &runnables[i]))
+            ++canceled;
+    }
+
+    vlc_mutex_lock(&shared_data.lock);
+    /* All started must not be canceled (but some non-started-yet may not be
+     * canceled either) */
+    assert(canceled + shared_data.started <= 40);
+    vlc_mutex_unlock(&shared_data.lock);
+
+    vlc_executor_Delete(executor);
+
+    /* Every started task must also be ended */
+    assert(shared_data.started == shared_data.ended);
+    /* Every task is either canceled or ended */
+    assert(canceled + shared_data.ended == 40);
+}
+
+struct doubler_task
+{
+    vlc_executor_t *executor;
+    int *array;
+    size_t count;
+    struct vlc_runnable runnable;
+};
+
+static void DoublerRun(void *);
+
+static bool
+SpawnDoublerTask(vlc_executor_t *executor, int *array, size_t count)
+{
+    struct doubler_task *task = malloc(sizeof(*task));
+    if (!task)
+        return false;
+
+    task->executor = executor;
+    task->array = array;
+    task->count = count;
+    task->runnable.run = DoublerRun;
+    task->runnable.userdata = task;
+
+    vlc_executor_Submit(executor, &task->runnable);
+
+    return true;
+}
+
+static void DoublerRun(void *userdata)
+{
+    struct doubler_task *task = userdata;
+
+    if (task->count == 1)
+        task->array[0] *= 2; /* double the value */
+    else
+    {
+        /* Spawn tasks doubling halves of the array recursively */
+        bool ok;
+
+        ok = SpawnDoublerTask(task->executor, task->array, task->count / 2);
+        assert(ok);
+
+        ok = SpawnDoublerTask(task->executor, task->array + task->count / 2,
+                                              task->count - task->count / 2);
+        assert(ok);
+    }
+
+    free(task);
+}
+
+static void test_task_chain(void)
+{
+    vlc_executor_t *executor = vlc_executor_New(4);
+    assert(executor);
+
+    /* Numbers from 0 to 99 */
+    int array[100];
+    for (int i = 0; i < 100; ++i)
+        array[i] = i;
+
+    /* Double all values in the array from tasks spawning smaller tasks
+     * recursively, until the array has size 1, where the single value is
+     * doubled */
+    SpawnDoublerTask(executor, array, 100);
+
+    vlc_executor_WaitIdle(executor);
+    vlc_executor_Delete(executor);
+
+    /* All values must have been doubled */
+    for (int i = 0; i < 100; ++i)
+        assert(array[i] == 2 * i);
+}
+
+int main(void)
+{
+    test_single_runnable();
+    test_multiple_runnables();
+    test_blocking_delete();
+    test_cancel();
+    test_task_chain();
+    return 0;
+}



More information about the vlc-commits mailing list