[vlc-devel] [PATCH 2/6] executor: add unit tests

Romain Vimont rom1v at videolabs.io
Mon Sep 7 17:40:07 CEST 2020


---
 test/Makefile.am         |   3 +
 test/src/misc/executor.c | 282 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 285 insertions(+)
 create mode 100644 test/src/misc/executor.c

diff --git a/test/Makefile.am b/test/Makefile.am
index c2ce32d02f..79989ad30e 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -33,6 +33,7 @@ check_PROGRAMS = \
 	test_src_misc_bits \
 	test_src_misc_epg \
 	test_src_misc_keystore \
+	test_src_misc_executor \
 	test_modules_packetizer_helpers \
 	test_modules_packetizer_hxxx \
 	test_modules_packetizer_h264 \
@@ -141,6 +142,8 @@ test_src_interface_dialog_SOURCES = src/interface/dialog.c
 test_src_interface_dialog_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_src_media_source_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_src_media_source_SOURCES = src/media_source/media_source.c
+test_src_misc_executor_LDADD = $(LIBVLCCORE) $(LIBVLC)
+test_src_misc_executor_SOURCES = src/misc/executor.c
 test_modules_packetizer_helpers_SOURCES = modules/packetizer/helpers.c
 test_modules_packetizer_helpers_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_modules_packetizer_hxxx_SOURCES = modules/packetizer/hxxx.c
diff --git a/test/src/misc/executor.c b/test/src/misc/executor.c
new file mode 100644
index 0000000000..678693b82a
--- /dev/null
+++ b/test/src/misc/executor.c
@@ -0,0 +1,282 @@
+/*****************************************************************************
+ * test/src/misc/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
+
+#include "../../libvlc/test.h"
+#include "../lib/libvlc_internal.h"
+
+#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;
+}
-- 
2.28.0



More information about the vlc-devel mailing list