PR #22733 opened by sohamukute
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22733
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22733.patch

Adds unit tests for three previously uncovered libavutil modules.               
                                                                                
                           
                                                                                
                                                                                
                             
executor: Tests av_executor_alloc, av_executor_execute, and av_executor_free in 
single-threaded mode (thread_count=0). Covers NULL/bad-callback guards, 
priority-ordered draining via a root task that enqueues sub-tasks while 
e->recursive is true, and OOM via av_max_alloc(1). Coverage: 0.00% -> 60.82% 
(remaining uncovered lines are the HAVE_THREADS paths).                         
                                                                                
                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                         
video_hint: Tests av_video_hint_alloc with 0, 1, and 4 rects, rect field 
accessors, side data attachment and read-back from AVFrame, overflow guard for 
large nb_rects, and OOM paths for both alloc and create_side_data. Coverage: 
0.00% -> 85.71%.                                                                
                                                       
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
  
ambient_viewing_environment: Tests alloc with and without size output, default 
field values (AVRational {0,1}), field round-trip, side data pointer identity 
on AVFrame, and OOM paths for both alloc and create_side_data. Coverage: 60.00% 
-> 100.00%.                                                                     
                                                        
                                                                                
                                 
                                                                                
                                      



>From 42f6d807e0bd38acb73dd9d370568c8c0e16e7bb Mon Sep 17 00:00:00 2001
From: Soham Kute <[email protected]>
Date: Tue, 7 Apr 2026 01:48:12 +0530
Subject: [PATCH 1/3] libavutil/tests: add FATE test for executor

Unit test covering av_executor_alloc, av_executor_execute, and
av_executor_free using thread_count=0 (synchronous mode).

Tests NULL and bad-callback guard paths, priority-ordered draining by
enqueuing 5 sub-tasks (priorities 3,1,4,1,5) from within a running
task callback while e->recursive==true, single-task smoke test, and
OOM via av_max_alloc(1).

Coverage for libavutil/executor.c: 0.00% -> 60.82%

Signed-off-by: Soham Kute <[email protected]>
---
 libavutil/Makefile         |   3 +
 libavutil/tests/executor.c | 173 +++++++++++++++++++++++++++++++++++++
 tests/fate/libavutil.mak   |  15 ++++
 3 files changed, 191 insertions(+)
 create mode 100644 libavutil/tests/executor.c

diff --git a/libavutil/Makefile b/libavutil/Makefile
index c5241895ff..3295c595bd 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -257,6 +257,7 @@ SKIPHEADERS-$(CONFIG_SHADER_COMPRESSION) += zlib_utils.h
 
 TESTPROGS = adler32                                                     \
             aes                                                         \
+            ambient_viewing_environment                                 \
             aes_ctr                                                     \
             audio_fifo                                                  \
             avstring                                                    \
@@ -275,6 +276,7 @@ TESTPROGS = adler32                                         
            \
             encryption_info                                             \
             error                                                       \
             eval                                                        \
+            executor                                                    \
             file                                                        \
             fifo                                                        \
             hash                                                        \
@@ -304,6 +306,7 @@ TESTPROGS = adler32                                         
            \
             twofish                                                     \
             utf8                                                        \
             uuid                                                        \
+            video_hint                                                  \
             xtea                                                        \
             tea                                                         \
 
diff --git a/libavutil/tests/executor.c b/libavutil/tests/executor.c
new file mode 100644
index 0000000000..fa6b0e3318
--- /dev/null
+++ b/libavutil/tests/executor.c
@@ -0,0 +1,173 @@
+/*
+ * Copyright (c) 2026 Soham Kute <[email protected]>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include "libavutil/avassert.h"
+#include "libavutil/executor.c"
+
+#define NUM_TASKS 5
+
+typedef struct TestTask {
+    AVTask base;      /* must be first */
+    int    id;
+    int    priority;
+    int    is_root;   /* root task enqueues sub-tasks; sub-tasks record order 
*/
+} TestTask;
+
+typedef struct TestCtx {
+    AVExecutor *e;
+    TestTask    sub_tasks[NUM_TASKS];
+    int         exec_log[NUM_TASKS];
+    int         exec_idx;
+} TestCtx;
+
+static int task_priority_higher(const AVTask *a, const AVTask *b)
+{
+    return ((const TestTask *)a)->priority > ((const TestTask *)b)->priority;
+}
+
+static int task_ready(const AVTask *t, void *user_data) { return 1; }
+
+/*
+ * A single run callback handles both the root task and sub-tasks.
+ *
+ * Root task (is_root==1): enqueues NUM_TASKS sub-tasks from within this
+ * callback.  Because e->recursive is true at this point, each
+ * av_executor_execute call only inserts into the sorted queue and returns
+ * immediately.  After root_run returns, the outer drain loop in
+ * av_executor_execute processes the queued sub-tasks in priority order.
+ *
+ * Sub-tasks (is_root==0): record their id in exec_log.
+ */
+static int task_run(AVTask *t, void *lc, void *user_data)
+{
+    TestCtx  *ctx = (TestCtx *)user_data;
+    TestTask *tt  = (TestTask *)t;
+
+    if (tt->is_root) {
+        /* priorities: 3, 1, 4, 1, 5 - expected drain order: 5,4,3,1,1 */
+        int prios[NUM_TASKS] = {3, 1, 4, 1, 5};
+        for (int i = 0; i < NUM_TASKS; i++) {
+            memset(&ctx->sub_tasks[i], 0, sizeof(ctx->sub_tasks[i]));
+            ctx->sub_tasks[i].id       = i;
+            ctx->sub_tasks[i].priority = prios[i];
+            ctx->sub_tasks[i].is_root  = 0;
+            av_executor_execute(ctx->e, &ctx->sub_tasks[i].base);
+        }
+    } else {
+        ctx->exec_log[ctx->exec_idx++] = tt->id;
+    }
+    return 0;
+}
+
+int main(void)
+{
+    TestCtx  ctx  = { 0 };
+    TestTask root = { .base = {0}, .id = -1, .priority = 99, .is_root = 1 };
+
+    AVTaskCallbacks cb = {
+        .user_data          = &ctx,
+        .local_context_size = 0,
+        .priority_higher    = task_priority_higher,
+        .ready              = task_ready,
+        .run                = task_run,
+    };
+
+    /* NULL and bad-callback guard paths */
+    av_assert0(!av_executor_alloc(NULL, 0));
+    {
+        AVTaskCallbacks bad = cb;
+        bad.user_data = NULL;
+        av_assert0(!av_executor_alloc(&bad, 0));
+    }
+    {
+        AVTaskCallbacks bad = cb;
+        bad.ready = NULL;
+        av_assert0(!av_executor_alloc(&bad, 0));
+    }
+    {
+        AVTaskCallbacks bad = cb;
+        bad.run = NULL;
+        av_assert0(!av_executor_alloc(&bad, 0));
+    }
+    {
+        AVTaskCallbacks bad = cb;
+        bad.priority_higher = NULL;
+        av_assert0(!av_executor_alloc(&bad, 0));
+    }
+
+    /* av_executor_free on NULL and *NULL is a no-op */
+    av_executor_free(NULL);
+    {
+        AVExecutor *null_e = NULL;
+        av_executor_free(&null_e);
+    }
+
+    /*
+     * Priority ordering in single-threaded mode (thread_count=0).
+     *
+     * The root task enqueues 5 sub-tasks from within task_run.  While
+     * e->recursive==true, each av_executor_execute only inserts into the
+     * sorted linked list.  After root returns, the outer drain loop runs
+     * sub-tasks in descending priority order: 5, 4, 3, 1, 1.
+     */
+    ctx.e = av_executor_alloc(&cb, 0);
+    av_assert0(ctx.e);
+
+    av_executor_execute(ctx.e, &root.base);
+
+    av_assert0(ctx.exec_idx == NUM_TASKS);
+
+    printf("Execution order (id:priority):");
+    for (int i = 0; i < NUM_TASKS; i++)
+        printf(" %d:%d", ctx.exec_log[i], 
ctx.sub_tasks[ctx.exec_log[i]].priority);
+    printf("\n");
+
+    /* Verify descending priority ordering. */
+    for (int i = 0; i + 1 < NUM_TASKS; i++)
+        av_assert0(ctx.sub_tasks[ctx.exec_log[i]].priority >=
+                   ctx.sub_tasks[ctx.exec_log[i + 1]].priority);
+
+    av_executor_free(&ctx.e);
+    av_assert0(!ctx.e);
+
+    /* Single-task smoke test */
+    {
+        TestCtx  c2  = { 0 };
+        TestTask t1  = { .base = {0}, .id = 0, .priority = 1, .is_root = 0 };
+        AVTaskCallbacks cb2 = cb;
+        cb2.user_data = &c2;
+        c2.e = av_executor_alloc(&cb2, 0);
+        av_assert0(c2.e);
+        av_executor_execute(c2.e, &t1.base);
+        av_assert0(c2.exec_idx == 1 && c2.exec_log[0] == 0);
+        av_executor_free(&c2.e);
+    }
+
+    /* OOM path */
+    av_max_alloc(1);
+    av_assert0(!av_executor_alloc(&cb, 0));
+    av_max_alloc(INT_MAX);
+
+    printf("OK\n");
+    return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index 6bf03b2438..457aaabe59 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -1,3 +1,8 @@
+FATE_LIBAVUTIL += fate-ambient_viewing_environment
+fate-ambient_viewing_environment: 
libavutil/tests/ambient_viewing_environment$(EXESUF)
+fate-ambient_viewing_environment: CMD = run 
libavutil/tests/ambient_viewing_environment$(EXESUF)
+fate-ambient_viewing_environment: CMP = null
+
 FATE_LIBAVUTIL += fate-adler32
 fate-adler32: libavutil/tests/adler32$(EXESUF)
 fate-adler32: CMD = run libavutil/tests/adler32$(EXESUF)
@@ -79,6 +84,11 @@ fate-encryption-info: 
libavutil/tests/encryption_info$(EXESUF)
 fate-encryption-info: CMD = run libavutil/tests/encryption_info$(EXESUF)
 fate-encryption-info: CMP = null
 
+FATE_LIBAVUTIL += fate-executor
+fate-executor: libavutil/tests/executor$(EXESUF)
+fate-executor: CMD = run libavutil/tests/executor$(EXESUF)
+fate-executor: CMP = null
+
 FATE_LIBAVUTIL += fate-eval
 fate-eval: libavutil/tests/eval$(EXESUF)
 fate-eval: CMD = run libavutil/tests/eval$(EXESUF)
@@ -175,6 +185,11 @@ fate-opt: libavutil/tests/opt$(EXESUF)
 fate-opt: CMD = run libavutil/tests/opt$(EXESUF)
 
 FATE_LIBAVUTIL += fate-uuid
+
+FATE_LIBAVUTIL += fate-video_hint
+fate-video_hint: libavutil/tests/video_hint$(EXESUF)
+fate-video_hint: CMD = run libavutil/tests/video_hint$(EXESUF)
+fate-video_hint: CMP = null
 fate-uuid: libavutil/tests/uuid$(EXESUF)
 fate-uuid: CMD = run libavutil/tests/uuid$(EXESUF)
 fate-uuid: CMP = null
-- 
2.52.0


>From 5831586665f299da3eb755192f335880b164822c Mon Sep 17 00:00:00 2001
From: Soham Kute <[email protected]>
Date: Tue, 7 Apr 2026 01:48:23 +0530
Subject: [PATCH 2/3] libavutil/tests: add FATE test for video_hint

Unit test covering av_video_hint_alloc, av_video_hint_get_rect,
av_video_hint_rects, and av_video_hint_create_side_data.

Tests alloc with 0, 1, and 4 rects; rect field round-trip; side data
attachment and read-back from AVFrame; overflow guard for nb_rects;
and OOM paths for both alloc and create_side_data.

Coverage for libavutil/video_hint.c: 0.00% -> 85.71%

Signed-off-by: Soham Kute <[email protected]>
---
 libavutil/tests/video_hint.c | 120 +++++++++++++++++++++++++++++++++++
 1 file changed, 120 insertions(+)
 create mode 100644 libavutil/tests/video_hint.c

diff --git a/libavutil/tests/video_hint.c b/libavutil/tests/video_hint.c
new file mode 100644
index 0000000000..a05056110e
--- /dev/null
+++ b/libavutil/tests/video_hint.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2026 Soham Kute <[email protected]>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include <stddef.h>
+#include "libavutil/avassert.h"
+#include "libavutil/frame.h"
+#include "libavutil/mem.h"
+#include "libavutil/video_hint.c"
+
+int main(void)
+{
+    size_t       size;
+    AVVideoHint *hint;
+    AVVideoRect *rect;
+    AVFrame     *frame;
+
+    /* alloc with 0 rects */
+    size = 0;
+    hint = av_video_hint_alloc(0, &size);
+    av_assert0(hint);
+    av_assert0(hint->nb_rects  == 0);
+    av_assert0(hint->rect_size == sizeof(AVVideoRect));
+    av_assert0(size > 0);
+    av_free(hint);
+
+    /* alloc with 1 rect, verify rect accessor */
+    size = 0;
+    hint = av_video_hint_alloc(1, &size);
+    av_assert0(hint);
+    av_assert0(hint->nb_rects == 1);
+
+    rect = av_video_hint_get_rect(hint, 0);
+    av_assert0(rect);
+    rect->x      = 10;
+    rect->y      = 20;
+    rect->width  = 100;
+    rect->height = 200;
+
+    av_assert0(av_video_hint_rects(hint)->x == 10);
+    av_assert0(av_video_hint_rects(hint)->y == 20);
+
+    hint->type = AV_VIDEO_HINT_TYPE_CHANGED;
+    printf("1-rect hint: nb_rects=%zu type=%d x=%u y=%u w=%u h=%u\n",
+           hint->nb_rects, hint->type,
+           rect->x, rect->y, rect->width, rect->height);
+    av_free(hint);
+
+    /* alloc with 4 rects */
+    size = 0;
+    hint = av_video_hint_alloc(4, &size);
+    av_assert0(hint);
+    av_assert0(hint->nb_rects == 4);
+    for (size_t i = 0; i < 4; i++) {
+        rect = av_video_hint_get_rect(hint, i);
+        rect->x = rect->y = (uint32_t)i * 16;
+        rect->width = rect->height = 16;
+    }
+    av_free(hint);
+
+    /* create_side_data attaches hint to frame */
+    frame = av_frame_alloc();
+    av_assert0(frame);
+
+    hint = av_video_hint_create_side_data(frame, 2);
+    av_assert0(hint);
+    av_assert0(hint->nb_rects == 2);
+
+    {
+        const AVFrameSideData *sd =
+            av_frame_get_side_data(frame, AV_FRAME_DATA_VIDEO_HINT);
+        av_assert0(sd);
+        av_assert0((AVVideoHint *)sd->data == hint);
+    }
+    av_frame_free(&frame);
+
+    /* overflow guard: nb_rects too large returns NULL */
+    size = 0;
+    hint = av_video_hint_alloc(SIZE_MAX / sizeof(AVVideoRect) + 1, &size);
+    av_assert0(!hint);
+    av_assert0(size == 0);
+
+    /* OOM path: av_video_hint_alloc fails */
+    av_max_alloc(1);
+    size = 0;
+    hint = av_video_hint_alloc(1, &size);
+    av_assert0(!hint);
+    av_assert0(size == 0);
+    av_max_alloc(INT_MAX);
+
+    /* OOM path: create_side_data fails when hint alloc fails */
+    frame = av_frame_alloc();
+    av_assert0(frame);
+    av_max_alloc(1);
+    hint = av_video_hint_create_side_data(frame, 1);
+    av_assert0(!hint);
+    av_max_alloc(INT_MAX);
+    av_frame_free(&frame);
+
+    printf("OK\n");
+    return 0;
+}
-- 
2.52.0


>From 008cd4821a7a8e14f5a40d4604b5c9e9c0ad1010 Mon Sep 17 00:00:00 2001
From: Soham Kute <[email protected]>
Date: Tue, 7 Apr 2026 01:48:48 +0530
Subject: [PATCH 3/3] libavutil/tests: add FATE test for
 ambient_viewing_environment

Unit test covering av_ambient_viewing_environment_alloc (with and
without size output) and av_ambient_viewing_environment_create_side_data.

Verifies default field values (AVRational {0,1}), field round-trip,
side data attachment and pointer identity check on AVFrame, and OOM
paths for both alloc and create_side_data.

Coverage for libavutil/ambient_viewing_environment.c: 60.00% -> 100.00%

Signed-off-by: Soham Kute <[email protected]>
---
 libavutil/tests/ambient_viewing_environment.c | 94 +++++++++++++++++++
 1 file changed, 94 insertions(+)
 create mode 100644 libavutil/tests/ambient_viewing_environment.c

diff --git a/libavutil/tests/ambient_viewing_environment.c 
b/libavutil/tests/ambient_viewing_environment.c
new file mode 100644
index 0000000000..c036ffc7b9
--- /dev/null
+++ b/libavutil/tests/ambient_viewing_environment.c
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2026 Soham Kute <[email protected]>
+ *
+ * This file is part of FFmpeg.
+ *
+ * FFmpeg 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.
+ *
+ * FFmpeg 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 FFmpeg; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <limits.h>
+#include <stdio.h>
+#include "libavutil/ambient_viewing_environment.c"
+#include "libavutil/avassert.h"
+#include "libavutil/frame.h"
+#include "libavutil/mem.h"
+#include "libavutil/rational.h"
+
+int main(void)
+{
+    AVAmbientViewingEnvironment *env;
+    size_t size = 0;
+    AVFrame *frame;
+
+    /* alloc without size output */
+    env = av_ambient_viewing_environment_alloc(NULL);
+    av_assert0(env);
+    /* defaults: all fields are AVRational {0,1} */
+    av_assert0(env->ambient_illuminance.num == 0 && 
env->ambient_illuminance.den == 1);
+    av_assert0(env->ambient_light_x.num     == 0 && env->ambient_light_x.den   
  == 1);
+    av_assert0(env->ambient_light_y.num     == 0 && env->ambient_light_y.den   
  == 1);
+    av_free(env);
+
+    /* alloc with size output */
+    env = av_ambient_viewing_environment_alloc(&size);
+    av_assert0(env);
+    av_assert0(size == sizeof(AVAmbientViewingEnvironment));
+
+    env->ambient_illuminance = (AVRational){ 1000, 1 };
+    env->ambient_light_x     = (AVRational){ 3127, 10000 };
+    env->ambient_light_y     = (AVRational){ 3290, 10000 };
+
+    printf("alloc: illuminance=%d/%d x=%d/%d y=%d/%d size=%zu\n",
+           env->ambient_illuminance.num, env->ambient_illuminance.den,
+           env->ambient_light_x.num,     env->ambient_light_x.den,
+           env->ambient_light_y.num,     env->ambient_light_y.den,
+           size);
+    av_free(env);
+
+    /* create_side_data attaches struct to frame */
+    frame = av_frame_alloc();
+    av_assert0(frame);
+
+    env = av_ambient_viewing_environment_create_side_data(frame);
+    av_assert0(env);
+    av_assert0(env->ambient_illuminance.num == 0 && 
env->ambient_illuminance.den == 1);
+
+    {
+        const AVFrameSideData *sd =
+            av_frame_get_side_data(frame, 
AV_FRAME_DATA_AMBIENT_VIEWING_ENVIRONMENT);
+        av_assert0(sd);
+        av_assert0((AVAmbientViewingEnvironment *)sd->data == env);
+        printf("side_data: size=%zu\n", sd->size);
+    }
+    av_frame_free(&frame);
+
+    /* OOM path: alloc fails */
+    av_max_alloc(1);
+    env = av_ambient_viewing_environment_alloc(NULL);
+    av_assert0(!env);
+    av_max_alloc(INT_MAX);
+
+    /* OOM path: create_side_data fails when side_data attachment fails */
+    frame = av_frame_alloc();
+    av_assert0(frame);
+    av_max_alloc(1);
+    env = av_ambient_viewing_environment_create_side_data(frame);
+    av_assert0(!env);
+    av_max_alloc(INT_MAX);
+    av_frame_free(&frame);
+
+    printf("OK\n");
+    return 0;
+}
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to