PR #22673 opened by Marcos Ashton (MarcosAsh)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22673
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22673.patch

Three new unit tests:

  - rc4:  keystream verification against RFC 6229 vectors, encrypt/decrypt 
round-trip, inplace, and the invalid key_bits error path. Was the only cipher 
in libavutil without a FATE test. (0% -> 100%)

  - samplefmt: name/format lookups, packed/planar conversions, buffer size, 
alloc, copy, silence fill. (0% -> 89.62%, rest is alloc_array_and_samples + OOM 
paths)
 
  - mathematics: av_gcd, av_rescale variants, av_compare_ts/mode 
av_rescale_delta, av_add_stable. Hits the 128-bit multiply path. Skips 
av_bessel_i0 since it's float and not bitexact. (0% -> 82.03%, rest is bessel + 
one delta fallback)


>From 6c3ec8b3109eb244a9853586cde6bd3b46075707 Mon Sep 17 00:00:00 2001
From: marcos ashton <[email protected]>
Date: Tue, 31 Mar 2026 20:27:24 +0100
Subject: [PATCH 1/3] tests/fate/libavutil: add FATE test for rc4

Test the three public API functions: av_rc4_alloc, av_rc4_init,
and av_rc4_crypt. Verifies keystream output against RFC 6229
test vectors for 40, 56, 64, and 128-bit keys, encrypt/decrypt
round-trip, inplace operation, and the invalid key_bits error path.

Coverage for libavutil/rc4.c: 0.00% -> 100.00%
---
 libavutil/Makefile       |   1 +
 libavutil/tests/rc4.c    | 123 +++++++++++++++++++++++++++++++++++++++
 tests/fate/libavutil.mak |   4 ++
 tests/ref/fate/rc4       |   1 +
 4 files changed, 129 insertions(+)
 create mode 100644 libavutil/tests/rc4.c
 create mode 100644 tests/ref/fate/rc4

diff --git a/libavutil/Makefile b/libavutil/Makefile
index ff166cc81a..e168f1ff0e 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -296,6 +296,7 @@ TESTPROGS = adler32                                         
            \
             pixfmt_best                                                 \
             random_seed                                                 \
             rational                                                    \
+            rc4                                                         \
             ripemd                                                      \
             sha                                                         \
             sha512                                                      \
diff --git a/libavutil/tests/rc4.c b/libavutil/tests/rc4.c
new file mode 100644
index 0000000000..14f02989ed
--- /dev/null
+++ b/libavutil/tests/rc4.c
@@ -0,0 +1,123 @@
+/*
+ * 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 <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "libavutil/attributes_internal.h"
+#include "libavutil/macros.h"
+#include "libavutil/mem.h"
+#include "libavutil/rc4.h"
+
+/* RFC 6229 test vectors */
+static const struct {
+    int key_bits;
+    const uint8_t key[16];
+    const uint8_t keystream[8]; /* first 8 bytes of output */
+} test_vectors[] = {
+    /* 40-bit key: 0x0102030405 */
+    { 40,
+      { 0x01, 0x02, 0x03, 0x04, 0x05 },
+      { 0xb2, 0x39, 0x63, 0x05, 0xf0, 0x3d, 0xc0, 0x27 } },
+    /* 56-bit key: 0x01020304050607 */
+    { 56,
+      { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 },
+      { 0x29, 0x3f, 0x02, 0xd4, 0x7f, 0x37, 0xc9, 0xb6 } },
+    /* 64-bit key: 0x0102030405060708 */
+    { 64,
+      { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 },
+      { 0x97, 0xab, 0x8a, 0x1b, 0xf0, 0xaf, 0xb9, 0x61 } },
+    /* 128-bit key: 0x0102030405060708090a0b0c0d0e0f10 */
+    { 128,
+      { 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08,
+        0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 },
+      { 0x9a, 0xc7, 0xcc, 0x9a, 0x60, 0x9d, 0x1e, 0xf7 } },
+};
+
+int main(void)
+{
+    AVRC4 *ctx;
+    uint8_t buf[8], encrypted[8], decrypted[8];
+
+    ctx = av_rc4_alloc();
+    if (!ctx)
+        return 1;
+
+    /* test keystream output (src=NULL) against known vectors */
+    for (int i = 0; i < FF_ARRAY_ELEMS(test_vectors); i++) {
+        av_rc4_init(ctx, test_vectors[i].key, test_vectors[i].key_bits, 0);
+        av_rc4_crypt(ctx, buf, NULL, sizeof(buf), NULL, 0);
+        if (memcmp(buf, test_vectors[i].keystream, sizeof(buf))) {
+            printf("Keystream test %d (%d-bit key) failed.\n",
+                   i, test_vectors[i].key_bits);
+            av_free(ctx);
+            return 1;
+        }
+    }
+
+    /* test encrypt then decrypt round-trip */
+    {
+        const uint8_t key[] = { 0x01, 0x02, 0x03, 0x04, 0x05 };
+        static attribute_nonstring const uint8_t plaintext[8] = "TestData";
+
+        av_rc4_init(ctx, key, 40, 0);
+        av_rc4_crypt(ctx, encrypted, plaintext, sizeof(plaintext), NULL, 0);
+
+        /* RC4 is symmetric: re-init and encrypt again to decrypt */
+        av_rc4_init(ctx, key, 40, 0);
+        av_rc4_crypt(ctx, decrypted, encrypted, sizeof(encrypted), NULL, 0);
+
+        if (memcmp(decrypted, plaintext, sizeof(plaintext))) {
+            printf("Round-trip test failed.\n");
+            av_free(ctx);
+            return 1;
+        }
+    }
+
+    /* test inplace encrypt/decrypt */
+    {
+        const uint8_t key[] = { 0xFE, 0xDC, 0xBA, 0x98, 0x76, 0x54, 0x32, 0x10 
};
+        static attribute_nonstring const uint8_t plaintext[8] = "InPlace!";
+
+        memcpy(buf, plaintext, sizeof(plaintext));
+        av_rc4_init(ctx, key, 64, 0);
+        av_rc4_crypt(ctx, buf, buf, sizeof(buf), NULL, 0);
+
+        av_rc4_init(ctx, key, 64, 0);
+        av_rc4_crypt(ctx, buf, buf, sizeof(buf), NULL, 0);
+
+        if (memcmp(buf, plaintext, sizeof(plaintext))) {
+            printf("Inplace round-trip test failed.\n");
+            av_free(ctx);
+            return 1;
+        }
+    }
+
+    /* test invalid key_bits (not multiple of 8) */
+    if (av_rc4_init(ctx, (const uint8_t[]){ 0x01 }, 7, 0) >= 0) {
+        printf("Invalid key_bits should return error.\n");
+        av_free(ctx);
+        return 1;
+    }
+
+    printf("Test encryption/decryption success.\n");
+    av_free(ctx);
+
+    return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index d69a4de863..c705fa79a1 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -140,6 +140,10 @@ FATE_LIBAVUTIL += fate-random_seed
 fate-random_seed: libavutil/tests/random_seed$(EXESUF)
 fate-random_seed: CMD = run libavutil/tests/random_seed$(EXESUF)
 
+FATE_LIBAVUTIL += fate-rc4
+fate-rc4: libavutil/tests/rc4$(EXESUF)
+fate-rc4: CMD = run libavutil/tests/rc4$(EXESUF)
+
 FATE_LIBAVUTIL += fate-ripemd
 fate-ripemd: libavutil/tests/ripemd$(EXESUF)
 fate-ripemd: CMD = run libavutil/tests/ripemd$(EXESUF)
diff --git a/tests/ref/fate/rc4 b/tests/ref/fate/rc4
new file mode 100644
index 0000000000..fed0b4dd3b
--- /dev/null
+++ b/tests/ref/fate/rc4
@@ -0,0 +1 @@
+Test encryption/decryption success.
-- 
2.52.0


>From 70223d11125f86660139779b54ab44c968b1dfee Mon Sep 17 00:00:00 2001
From: marcos ashton <[email protected]>
Date: Tue, 31 Mar 2026 20:27:49 +0100
Subject: [PATCH 2/3] tests/fate/libavutil: add FATE test for samplefmt

Test all public API functions: name/format round-trip lookups,
bytes_per_sample, is_planar, packed/planar conversions,
alt_sample_fmt, get_sample_fmt_string, samples_get_buffer_size,
samples_alloc, samples_copy, and samples_set_silence.

Coverage for libavutil/samplefmt.c: 0.00% -> 89.62%

Remaining uncovered lines are av_samples_alloc_array_and_samples
and OOM error paths.
---
 libavutil/Makefile          |   1 +
 libavutil/tests/samplefmt.c | 168 ++++++++++++++++++++++++++++++++++++
 tests/fate/libavutil.mak    |   4 +
 tests/ref/fate/samplefmt    | 107 +++++++++++++++++++++++
 4 files changed, 280 insertions(+)
 create mode 100644 libavutil/tests/samplefmt.c
 create mode 100644 tests/ref/fate/samplefmt

diff --git a/libavutil/Makefile b/libavutil/Makefile
index e168f1ff0e..b659969368 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -300,6 +300,7 @@ TESTPROGS = adler32                                         
            \
             ripemd                                                      \
             sha                                                         \
             sha512                                                      \
+            samplefmt                                                   \
             side_data_array                                             \
             softfloat                                                   \
             stereo3d                                                    \
diff --git a/libavutil/tests/samplefmt.c b/libavutil/tests/samplefmt.c
new file mode 100644
index 0000000000..4af82b324d
--- /dev/null
+++ b/libavutil/tests/samplefmt.c
@@ -0,0 +1,168 @@
+/*
+ * 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 <stdio.h>
+#include <string.h>
+
+#include "libavutil/mem.h"
+#include "libavutil/samplefmt.h"
+
+int main(void)
+{
+    char buf[64];
+
+    /* av_get_sample_fmt_name and av_get_sample_fmt round-trip */
+    printf("Testing name/format round-trip\n");
+    for (int i = 0; i < AV_SAMPLE_FMT_NB; i++) {
+        const char *name = av_get_sample_fmt_name(i);
+        enum AVSampleFormat fmt = av_get_sample_fmt(name);
+        printf("%2d: name=%-5s roundtrip=%s\n", i, name, fmt == i ? "OK" : 
"FAIL");
+    }
+
+    /* boundary: NONE and out-of-range */
+    printf("NONE name: %s\n", av_get_sample_fmt_name(AV_SAMPLE_FMT_NONE) == 
NULL ? "(null)" : "?");
+    printf("NB name: %s\n", av_get_sample_fmt_name(AV_SAMPLE_FMT_NB) == NULL ? 
"(null)" : "?");
+    printf("unknown: %d\n", av_get_sample_fmt("nonexistent"));
+
+    /* av_get_bytes_per_sample */
+    printf("\nTesting av_get_bytes_per_sample()\n");
+    for (int i = 0; i < AV_SAMPLE_FMT_NB; i++)
+        printf("%s: %d\n", av_get_sample_fmt_name(i), 
av_get_bytes_per_sample(i));
+    printf("NONE: %d\n", av_get_bytes_per_sample(AV_SAMPLE_FMT_NONE));
+
+    /* av_sample_fmt_is_planar */
+    printf("\nTesting av_sample_fmt_is_planar()\n");
+    for (int i = 0; i < AV_SAMPLE_FMT_NB; i++)
+        printf("%s: %d\n", av_get_sample_fmt_name(i), 
av_sample_fmt_is_planar(i));
+    printf("NONE: %d\n", av_sample_fmt_is_planar(AV_SAMPLE_FMT_NONE));
+
+    /* av_get_packed_sample_fmt and av_get_planar_sample_fmt */
+    printf("\nTesting packed/planar conversions\n");
+    for (int i = 0; i < AV_SAMPLE_FMT_NB; i++) {
+        enum AVSampleFormat packed = av_get_packed_sample_fmt(i);
+        enum AVSampleFormat planar = av_get_planar_sample_fmt(i);
+        printf("%s: packed=%-4s planar=%s\n",
+               av_get_sample_fmt_name(i),
+               av_get_sample_fmt_name(packed),
+               av_get_sample_fmt_name(planar));
+    }
+
+    /* av_get_alt_sample_fmt */
+    printf("\nTesting av_get_alt_sample_fmt()\n");
+    for (int i = 0; i < AV_SAMPLE_FMT_NB; i++) {
+        enum AVSampleFormat alt_packed = av_get_alt_sample_fmt(i, 0);
+        enum AVSampleFormat alt_planar = av_get_alt_sample_fmt(i, 1);
+        printf("%s: alt_packed=%-4s alt_planar=%s\n",
+               av_get_sample_fmt_name(i),
+               av_get_sample_fmt_name(alt_packed),
+               av_get_sample_fmt_name(alt_planar));
+    }
+
+    /* av_get_sample_fmt_string */
+    printf("\nTesting av_get_sample_fmt_string()\n");
+    av_get_sample_fmt_string(buf, sizeof(buf), -1);
+    printf("header: %s\n", buf);
+    for (int i = 0; i < AV_SAMPLE_FMT_NB; i++) {
+        av_get_sample_fmt_string(buf, sizeof(buf), i);
+        printf("%s\n", buf);
+    }
+
+    /* av_samples_get_buffer_size */
+    printf("\nTesting av_samples_get_buffer_size()\n");
+    {
+        int linesize;
+        printf("2ch 1024smp s16: %d\n",
+               av_samples_get_buffer_size(NULL, 2, 1024, AV_SAMPLE_FMT_S16, 
1));
+        printf("2ch 1024smp s16p: %d\n",
+               av_samples_get_buffer_size(NULL, 2, 1024, AV_SAMPLE_FMT_S16P, 
1));
+        printf("6ch 512smp s32: %d\n",
+               av_samples_get_buffer_size(NULL, 6, 512, AV_SAMPLE_FMT_S32, 1));
+        av_samples_get_buffer_size(&linesize, 2, 1024, AV_SAMPLE_FMT_S16, 0);
+        printf("linesize (2ch 1024smp s16 align=0): %d\n", linesize);
+        printf("0ch error: %d\n",
+               av_samples_get_buffer_size(NULL, 0, 1024, AV_SAMPLE_FMT_S16, 1) 
< 0);
+    }
+
+    /* av_samples_alloc and av_samples_fill_arrays */
+    printf("\nTesting av_samples_alloc()\n");
+    {
+        uint8_t *data[8] = { 0 };
+        int linesize, ret;
+
+        ret = av_samples_alloc(data, &linesize, 2, 1024, AV_SAMPLE_FMT_S16, 0);
+        printf("alloc 2ch s16: ret=%d linesize=%d data[0]=%s\n",
+               ret > 0, linesize, data[0] ? "set" : "null");
+        av_freep(&data[0]);
+
+        ret = av_samples_alloc(data, &linesize, 2, 1024, AV_SAMPLE_FMT_S16P, 
0);
+        printf("alloc 2ch s16p: ret=%d linesize=%d data[0]=%s data[1]=%s\n",
+               ret > 0, linesize,
+               data[0] ? "set" : "null", data[1] ? "set" : "null");
+        av_freep(&data[0]);
+    }
+
+    /* av_samples_copy */
+    printf("\nTesting av_samples_copy()\n");
+    {
+        uint8_t *src[1] = { 0 }, *dst[1] = { 0 };
+        int linesize;
+
+        av_samples_alloc(src, &linesize, 1, 4, AV_SAMPLE_FMT_S16, 1);
+        av_samples_alloc(dst, &linesize, 1, 4, AV_SAMPLE_FMT_S16, 1);
+        if (src[0] && dst[0]) {
+            memset(src[0], 0xAB, 8);
+            av_samples_copy(dst, src, 0, 0, 4, 1, AV_SAMPLE_FMT_S16);
+            printf("copy: %s\n", memcmp(src[0], dst[0], 8) == 0 ? "OK" : 
"FAIL");
+        }
+        av_freep(&src[0]);
+        av_freep(&dst[0]);
+    }
+
+    /* av_samples_set_silence */
+    printf("\nTesting av_samples_set_silence()\n");
+    {
+        uint8_t *data[1] = { 0 };
+        int linesize;
+
+        av_samples_alloc(data, &linesize, 1, 4, AV_SAMPLE_FMT_S16, 1);
+        if (data[0]) {
+            memset(data[0], 0xFF, 8);
+            av_samples_set_silence(data, 0, 4, 1, AV_SAMPLE_FMT_S16);
+            /* silence for s16 is zero */
+            int silent = 1;
+            for (int i = 0; i < 8; i++)
+                if (data[0][i] != 0) silent = 0;
+            printf("silence s16: %s\n", silent ? "OK" : "FAIL");
+        }
+        av_freep(&data[0]);
+
+        av_samples_alloc(data, &linesize, 1, 4, AV_SAMPLE_FMT_U8, 1);
+        if (data[0]) {
+            memset(data[0], 0xFF, 4);
+            av_samples_set_silence(data, 0, 4, 1, AV_SAMPLE_FMT_U8);
+            /* silence for u8 is 0x80 */
+            int silent = 1;
+            for (int i = 0; i < 4; i++)
+                if (data[0][i] != 0x80) silent = 0;
+            printf("silence u8: %s\n", silent ? "OK" : "FAIL");
+        }
+        av_freep(&data[0]);
+    }
+
+    return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index c705fa79a1..f54e59db76 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -156,6 +156,10 @@ FATE_LIBAVUTIL += fate-sha512
 fate-sha512: libavutil/tests/sha512$(EXESUF)
 fate-sha512: CMD = run libavutil/tests/sha512$(EXESUF)
 
+FATE_LIBAVUTIL += fate-samplefmt
+fate-samplefmt: libavutil/tests/samplefmt$(EXESUF)
+fate-samplefmt: CMD = run libavutil/tests/samplefmt$(EXESUF)
+
 FATE_LIBAVUTIL += fate-side_data_array
 fate-side_data_array: libavutil/tests/side_data_array$(EXESUF)
 fate-side_data_array: CMD = run libavutil/tests/side_data_array$(EXESUF)
diff --git a/tests/ref/fate/samplefmt b/tests/ref/fate/samplefmt
new file mode 100644
index 0000000000..598ea48f78
--- /dev/null
+++ b/tests/ref/fate/samplefmt
@@ -0,0 +1,107 @@
+Testing name/format round-trip
+ 0: name=u8    roundtrip=OK
+ 1: name=s16   roundtrip=OK
+ 2: name=s32   roundtrip=OK
+ 3: name=flt   roundtrip=OK
+ 4: name=dbl   roundtrip=OK
+ 5: name=u8p   roundtrip=OK
+ 6: name=s16p  roundtrip=OK
+ 7: name=s32p  roundtrip=OK
+ 8: name=fltp  roundtrip=OK
+ 9: name=dblp  roundtrip=OK
+10: name=s64   roundtrip=OK
+11: name=s64p  roundtrip=OK
+NONE name: (null)
+NB name: (null)
+unknown: -1
+
+Testing av_get_bytes_per_sample()
+u8: 1
+s16: 2
+s32: 4
+flt: 4
+dbl: 8
+u8p: 1
+s16p: 2
+s32p: 4
+fltp: 4
+dblp: 8
+s64: 8
+s64p: 8
+NONE: 0
+
+Testing av_sample_fmt_is_planar()
+u8: 0
+s16: 0
+s32: 0
+flt: 0
+dbl: 0
+u8p: 1
+s16p: 1
+s32p: 1
+fltp: 1
+dblp: 1
+s64: 0
+s64p: 1
+NONE: 0
+
+Testing packed/planar conversions
+u8: packed=u8   planar=u8p
+s16: packed=s16  planar=s16p
+s32: packed=s32  planar=s32p
+flt: packed=flt  planar=fltp
+dbl: packed=dbl  planar=dblp
+u8p: packed=u8   planar=u8p
+s16p: packed=s16  planar=s16p
+s32p: packed=s32  planar=s32p
+fltp: packed=flt  planar=fltp
+dblp: packed=dbl  planar=dblp
+s64: packed=s64  planar=s64p
+s64p: packed=s64  planar=s64p
+
+Testing av_get_alt_sample_fmt()
+u8: alt_packed=u8   alt_planar=u8p
+s16: alt_packed=s16  alt_planar=s16p
+s32: alt_packed=s32  alt_planar=s32p
+flt: alt_packed=flt  alt_planar=fltp
+dbl: alt_packed=dbl  alt_planar=dblp
+u8p: alt_packed=u8   alt_planar=u8p
+s16p: alt_packed=s16  alt_planar=s16p
+s32p: alt_packed=s32  alt_planar=s32p
+fltp: alt_packed=flt  alt_planar=fltp
+dblp: alt_packed=dbl  alt_planar=dblp
+s64: alt_packed=s64  alt_planar=s64p
+s64p: alt_packed=s64  alt_planar=s64p
+
+Testing av_get_sample_fmt_string()
+header: name   depth
+u8        8 
+s16      16 
+s32      32 
+flt      32 
+dbl      64 
+u8p       8 
+s16p     16 
+s32p     32 
+fltp     32 
+dblp     64 
+s64      64 
+s64p     64 
+
+Testing av_samples_get_buffer_size()
+2ch 1024smp s16: 4096
+2ch 1024smp s16p: 4096
+6ch 512smp s32: 12288
+linesize (2ch 1024smp s16 align=0): 4096
+0ch error: 1
+
+Testing av_samples_alloc()
+alloc 2ch s16: ret=1 linesize=4096 data[0]=set
+alloc 2ch s16p: ret=1 linesize=2048 data[0]=set data[1]=set
+
+Testing av_samples_copy()
+copy: OK
+
+Testing av_samples_set_silence()
+silence s16: OK
+silence u8: OK
-- 
2.52.0


>From 9bec6309b4713aa1212ce1f5109d4911c5b5b85e Mon Sep 17 00:00:00 2001
From: marcos ashton <[email protected]>
Date: Tue, 31 Mar 2026 20:28:31 +0100
Subject: [PATCH 3/3] tests/fate/libavutil: add FATE test for mathematics

Test the integer math utility functions: av_gcd, av_rescale,
av_rescale_rnd (all rounding modes including PASS_MINMAX),
av_rescale_q, av_compare_ts, av_compare_mod, av_rescale_delta,
and av_add_stable. Includes large-value tests that exercise the
128-bit multiply path in av_rescale_rnd.

av_bessel_i0 is not tested since it uses floating point math
that is not bitexact across platforms.

Coverage for libavutil/mathematics.c: 0.00% -> 82.03%

Remaining uncovered lines are av_bessel_i0 (float, 23 lines)
and one edge case fallback in av_rescale_delta.
---
 libavutil/Makefile            |   1 +
 libavutil/tests/mathematics.c | 165 ++++++++++++++++++++++++++++++++++
 tests/fate/libavutil.mak      |   4 +
 tests/ref/fate/mathematics    |  72 +++++++++++++++
 4 files changed, 242 insertions(+)
 create mode 100644 libavutil/tests/mathematics.c
 create mode 100644 tests/ref/fate/mathematics

diff --git a/libavutil/Makefile b/libavutil/Makefile
index b659969368..bfadb5b9ad 100644
--- a/libavutil/Makefile
+++ b/libavutil/Makefile
@@ -286,6 +286,7 @@ TESTPROGS = adler32                                         
            \
             lfg                                                         \
             lls                                                         \
             log                                                         \
+            mathematics                                                 \
             md5                                                         \
             murmur3                                                     \
             opt                                                         \
diff --git a/libavutil/tests/mathematics.c b/libavutil/tests/mathematics.c
new file mode 100644
index 0000000000..a2fc9447a2
--- /dev/null
+++ b/libavutil/tests/mathematics.c
@@ -0,0 +1,165 @@
+/*
+ * 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 <inttypes.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "libavutil/macros.h"
+#include "libavutil/mathematics.h"
+#include "libavutil/rational.h"
+
+int main(void)
+{
+    int64_t last;
+
+    /* av_gcd */
+    printf("Testing av_gcd()\n");
+    static const struct { int64_t a, b, expected; } gcd_tests[] = {
+        {   0,   0,  0 },
+        {   1,   0,  1 },
+        {   0,   1,  1 },
+        {   6,   4,  2 },
+        {  12,   8,  4 },
+        {  17,  13,  1 },
+        { 100,  75, 25 },
+        {  -6,   4,  2 },
+        {   6,  -4,  2 },
+    };
+    for (int i = 0; i < FF_ARRAY_ELEMS(gcd_tests); i++)
+        printf("gcd(%"PRId64", %"PRId64") = %"PRId64" %s\n",
+               gcd_tests[i].a, gcd_tests[i].b,
+               av_gcd(gcd_tests[i].a, gcd_tests[i].b),
+               av_gcd(gcd_tests[i].a, gcd_tests[i].b) == gcd_tests[i].expected 
? "OK" : "FAIL");
+
+    /* av_rescale */
+    printf("\nTesting av_rescale()\n");
+    printf("rescale(6, 3, 2) = %"PRId64"\n", av_rescale(6, 3, 2));
+    printf("rescale(0, 3, 2) = %"PRId64"\n", av_rescale(0, 3, 2));
+    printf("rescale(1, 1, 1) = %"PRId64"\n", av_rescale(1, 1, 1));
+    printf("rescale(-6, 3, 2) = %"PRId64"\n", av_rescale(-6, 3, 2));
+    printf("rescale(90000, 1, 90000) = %"PRId64"\n", av_rescale(90000, 1, 
90000));
+
+    /* av_rescale_rnd with different rounding modes */
+    printf("\nTesting av_rescale_rnd()\n");
+    static const struct {
+        int64_t a, b, c;
+        enum AVRounding rnd;
+        int64_t expected;
+    } rnd_tests[] = {
+        {  7,  1,  2, AV_ROUND_ZERO,      3 },
+        {  7,  1,  2, AV_ROUND_INF,       4 },
+        {  7,  1,  2, AV_ROUND_DOWN,      3 },
+        {  7,  1,  2, AV_ROUND_UP,        4 },
+        {  7,  1,  2, AV_ROUND_NEAR_INF,  4 },
+        { -7,  1,  2, AV_ROUND_ZERO,     -3 },
+        { -7,  1,  2, AV_ROUND_INF,      -4 },
+        { -7,  1,  2, AV_ROUND_DOWN,     -4 },
+        { -7,  1,  2, AV_ROUND_UP,       -3 },
+        {  6,  1,  2, AV_ROUND_NEAR_INF,  3 },
+    };
+    for (int i = 0; i < FF_ARRAY_ELEMS(rnd_tests); i++) {
+        int64_t r = av_rescale_rnd(rnd_tests[i].a, rnd_tests[i].b,
+                                   rnd_tests[i].c, rnd_tests[i].rnd);
+        printf("rescale_rnd(%"PRId64", %"PRId64", %"PRId64", %d) = %"PRId64" 
%s\n",
+               rnd_tests[i].a, rnd_tests[i].b, rnd_tests[i].c,
+               rnd_tests[i].rnd, r,
+               r == rnd_tests[i].expected ? "OK" : "FAIL");
+    }
+
+    /* AV_ROUND_PASS_MINMAX */
+    printf("\nTesting AV_ROUND_PASS_MINMAX\n");
+    printf("INT64_MIN passthrough: %s\n",
+           av_rescale_rnd(INT64_MIN, 1, 2,
+                          AV_ROUND_UP | AV_ROUND_PASS_MINMAX) == INT64_MIN ? 
"OK" : "FAIL");
+    printf("INT64_MAX passthrough: %s\n",
+           av_rescale_rnd(INT64_MAX, 1, 2,
+                          AV_ROUND_UP | AV_ROUND_PASS_MINMAX) == INT64_MAX ? 
"OK" : "FAIL");
+    printf("normal with PASS_MINMAX: %"PRId64"\n",
+           av_rescale_rnd(3, 1, 2, AV_ROUND_UP | AV_ROUND_PASS_MINMAX));
+
+    /* large value rescale (exercises 128-bit multiply path) */
+    printf("\nTesting large value rescale\n");
+    printf("rescale(INT64_MAX/2, 2, 1) = %"PRId64"\n",
+           av_rescale_rnd(INT64_MAX / 2, 2, 1, AV_ROUND_ZERO));
+    printf("rescale(1000000007, 1000000009, 1000000007) = %"PRId64"\n",
+           av_rescale(1000000007LL, 1000000009LL, 1000000007LL));
+    /* b and c both > INT_MAX triggers 128-bit multiply */
+    printf("rescale_rnd(10, INT_MAX+1, INT_MAX+1, ZERO) = %"PRId64"\n",
+           av_rescale_rnd(10, (int64_t)INT32_MAX + 1, (int64_t)INT32_MAX + 1, 
AV_ROUND_ZERO));
+    printf("rescale_rnd(7, 3000000000, 2000000000, NEAR_INF) = %"PRId64"\n",
+           av_rescale_rnd(7, 3000000000LL, 2000000000LL, AV_ROUND_NEAR_INF));
+
+    /* av_rescale_q */
+    printf("\nTesting av_rescale_q()\n");
+    printf("rescale_q(90000, 1/90000, 1/1000) = %"PRId64"\n",
+           av_rescale_q(90000, (AVRational){1, 90000}, (AVRational){1, 1000}));
+    printf("rescale_q(48000, 1/48000, 1/44100) = %"PRId64"\n",
+           av_rescale_q(48000, (AVRational){1, 48000}, (AVRational){1, 
44100}));
+
+    /* av_compare_ts */
+    printf("\nTesting av_compare_ts()\n");
+    printf("compare(1, 1/1, 1, 1/1) = %d\n",
+           av_compare_ts(1, (AVRational){1, 1}, 1, (AVRational){1, 1}));
+    printf("compare(1, 1/1, 2, 1/1) = %d\n",
+           av_compare_ts(1, (AVRational){1, 1}, 2, (AVRational){1, 1}));
+    printf("compare(2, 1/1, 1, 1/1) = %d\n",
+           av_compare_ts(2, (AVRational){1, 1}, 1, (AVRational){1, 1}));
+    printf("compare(1, 1/1000, 1, 1/90000) = %d\n",
+           av_compare_ts(1, (AVRational){1, 1000}, 1, (AVRational){1, 90000}));
+    /* large values trigger rescale-based comparison path */
+    printf("compare(INT64_MAX/2, 1/1, INT64_MAX/3, 1/1) = %d\n",
+           av_compare_ts(INT64_MAX / 2, (AVRational){1, 1},
+                         INT64_MAX / 3, (AVRational){1, 1}));
+
+    /* av_compare_mod */
+    printf("\nTesting av_compare_mod()\n");
+    printf("compare_mod(3, 1, 16) = %"PRId64"\n", av_compare_mod(3, 1, 16));
+    printf("compare_mod(1, 3, 16) = %"PRId64"\n", av_compare_mod(1, 3, 16));
+    printf("compare_mod(5, 5, 16) = %"PRId64"\n", av_compare_mod(5, 5, 16));
+
+    /* av_rescale_delta */
+    printf("\nTesting av_rescale_delta()\n");
+    last = 0;
+    for (int i = 0; i < 4; i++)
+        printf("delta step %d: %"PRId64"\n", i,
+               av_rescale_delta((AVRational){1, 48000}, i * 1024,
+                                (AVRational){1, 48000}, 1024,
+                                &last, (AVRational){1, 44100}));
+    /* trigger clip-based path: use different in_tb and fs_tb */
+    last = 0;
+    for (int i = 0; i < 4; i++)
+        printf("delta clip %d: %"PRId64"\n", i,
+               av_rescale_delta((AVRational){1, 44100}, i * 940,
+                                (AVRational){1, 48000}, 1024,
+                                &last, (AVRational){1, 90000}));
+
+    /* av_add_stable */
+    printf("\nTesting av_add_stable()\n");
+    printf("add_stable(0, 1/1, 1/1000, 500) = %"PRId64"\n",
+           av_add_stable((AVRational){1, 1}, 0, (AVRational){1, 1000}, 500));
+    printf("add_stable(1000, 1/90000, 1/48000, 1024) = %"PRId64"\n",
+           av_add_stable((AVRational){1, 90000}, 1000, (AVRational){1, 48000}, 
1024));
+    /* non-exact division path (m >= d, general case) */
+    printf("add_stable(0, 1/48000, 1/90000, 90000) = %"PRId64"\n",
+           av_add_stable((AVRational){1, 48000}, 0, (AVRational){1, 90000}, 
90000));
+    printf("add_stable(100, 1/1000, 1/90000, 3000) = %"PRId64"\n",
+           av_add_stable((AVRational){1, 1000}, 100, (AVRational){1, 90000}, 
3000));
+
+    return 0;
+}
diff --git a/tests/fate/libavutil.mak b/tests/fate/libavutil.mak
index f54e59db76..d01133d8eb 100644
--- a/tests/fate/libavutil.mak
+++ b/tests/fate/libavutil.mak
@@ -112,6 +112,10 @@ FATE_LIBAVUTIL += fate-lfg
 fate-lfg: libavutil/tests/lfg$(EXESUF)
 fate-lfg: CMD = run libavutil/tests/lfg$(EXESUF)
 
+FATE_LIBAVUTIL += fate-mathematics
+fate-mathematics: libavutil/tests/mathematics$(EXESUF)
+fate-mathematics: CMD = run libavutil/tests/mathematics$(EXESUF)
+
 FATE_LIBAVUTIL += fate-md5
 fate-md5: libavutil/tests/md5$(EXESUF)
 fate-md5: CMD = run libavutil/tests/md5$(EXESUF)
diff --git a/tests/ref/fate/mathematics b/tests/ref/fate/mathematics
new file mode 100644
index 0000000000..e77cb25466
--- /dev/null
+++ b/tests/ref/fate/mathematics
@@ -0,0 +1,72 @@
+Testing av_gcd()
+gcd(0, 0) = 0 OK
+gcd(1, 0) = 1 OK
+gcd(0, 1) = 1 OK
+gcd(6, 4) = 2 OK
+gcd(12, 8) = 4 OK
+gcd(17, 13) = 1 OK
+gcd(100, 75) = 25 OK
+gcd(-6, 4) = 2 OK
+gcd(6, -4) = 2 OK
+
+Testing av_rescale()
+rescale(6, 3, 2) = 9
+rescale(0, 3, 2) = 0
+rescale(1, 1, 1) = 1
+rescale(-6, 3, 2) = -9
+rescale(90000, 1, 90000) = 1
+
+Testing av_rescale_rnd()
+rescale_rnd(7, 1, 2, 0) = 3 OK
+rescale_rnd(7, 1, 2, 1) = 4 OK
+rescale_rnd(7, 1, 2, 2) = 3 OK
+rescale_rnd(7, 1, 2, 3) = 4 OK
+rescale_rnd(7, 1, 2, 5) = 4 OK
+rescale_rnd(-7, 1, 2, 0) = -3 OK
+rescale_rnd(-7, 1, 2, 1) = -4 OK
+rescale_rnd(-7, 1, 2, 2) = -4 OK
+rescale_rnd(-7, 1, 2, 3) = -3 OK
+rescale_rnd(6, 1, 2, 5) = 3 OK
+
+Testing AV_ROUND_PASS_MINMAX
+INT64_MIN passthrough: OK
+INT64_MAX passthrough: OK
+normal with PASS_MINMAX: 2
+
+Testing large value rescale
+rescale(INT64_MAX/2, 2, 1) = 9223372036854775806
+rescale(1000000007, 1000000009, 1000000007) = 1000000009
+rescale_rnd(10, INT_MAX+1, INT_MAX+1, ZERO) = 10
+rescale_rnd(7, 3000000000, 2000000000, NEAR_INF) = 11
+
+Testing av_rescale_q()
+rescale_q(90000, 1/90000, 1/1000) = 1000
+rescale_q(48000, 1/48000, 1/44100) = 44100
+
+Testing av_compare_ts()
+compare(1, 1/1, 1, 1/1) = 0
+compare(1, 1/1, 2, 1/1) = -1
+compare(2, 1/1, 1, 1/1) = 1
+compare(1, 1/1000, 1, 1/90000) = 1
+compare(INT64_MAX/2, 1/1, INT64_MAX/3, 1/1) = 1
+
+Testing av_compare_mod()
+compare_mod(3, 1, 16) = 2
+compare_mod(1, 3, 16) = -2
+compare_mod(5, 5, 16) = 0
+
+Testing av_rescale_delta()
+delta step 0: 0
+delta step 1: 941
+delta step 2: 1882
+delta step 3: 2822
+delta clip 0: 0
+delta clip 1: 1920
+delta clip 2: 3838
+delta clip 3: 5756
+
+Testing av_add_stable()
+add_stable(0, 1/1, 1/1000, 500) = 0
+add_stable(1000, 1/90000, 1/48000, 1024) = 2920
+add_stable(0, 1/48000, 1/90000, 90000) = 48000
+add_stable(100, 1/1000, 1/90000, 3000) = 133
-- 
2.52.0

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

Reply via email to