test: integrate unittests with meson

This reworks all of mpv's unit tests so they are compiled as separate
executables (optional) and run via meson test. Because most of the tests
are dependant on mpv's internals, existing compiled objects are
leveraged to create static libs and used when necessary. As an aside, a
function was moved into video/out/gpu/utils for sanity's sake (otherwise
most of vo would have been needed). As a plus, meson multithreads
running tests automatically and also the output no longer pollutes the
source directory. There are tests that can break due to ffmpeg changes,
so they require a specific minimum libavutil version to be built.
This commit is contained in:
Dudemanguy 2023-02-25 21:50:08 -06:00
parent 6153242157
commit 9db818279a
30 changed files with 547 additions and 460 deletions

View File

@ -27,6 +27,7 @@ that you get for free. In some cases, these overlapped with custom waf options.
supported in the meson build. Instead, pass the generic pkg-config values supported in the meson build. Instead, pass the generic pkg-config values
such as ``lua52``, ``lua5.2``, etc. such as ``lua52``, ``lua5.2``, etc.
* ``--lgpl`` was changed to ``gpl``. If ``gpl`` is false, the build is LGPL2.1+. * ``--lgpl`` was changed to ``gpl``. If ``gpl`` is false, the build is LGPL2.1+.
* ``--tests`` was removed since unit tests now explictly require meson to run
### Boolean Options ### Boolean Options

View File

@ -393,10 +393,11 @@ if features['cocoa']
endif endif
if posix if posix
sources += files('input/ipc-unix.c', path_source = files('osdep/path-unix.c')
'osdep/path-unix.c', subprocess_source = files('osdep/subprocess-posix.c')
sources += path_source + subprocess_source + \
files('input/ipc-unix.c',
'osdep/polldev.c', 'osdep/polldev.c',
'osdep/subprocess-posix.c',
'osdep/terminal-unix.c', 'osdep/terminal-unix.c',
'sub/filter_regex.c') 'sub/filter_regex.c')
endif endif
@ -479,10 +480,11 @@ if features['win32-desktop']
cc.find_library('version'), cc.find_library('version'),
cc.find_library('winmm')] cc.find_library('winmm')]
dependencies += win32_desktop_libs dependencies += win32_desktop_libs
sources += files('input/ipc-win.c', path_source = files('osdep/path-win.c')
subprocess_source = files('osdep/subprocess-win.c')
sources += path_source + subprocess_source + \
files('input/ipc-win.c',
'osdep/main-fn-win.c', 'osdep/main-fn-win.c',
'osdep/path-win.c',
'osdep/subprocess-win.c',
'osdep/terminal-win.c', 'osdep/terminal-win.c',
'video/out/w32_common.c', 'video/out/w32_common.c',
'video/out/win32/displayconfig.c', 'video/out/win32/displayconfig.c',
@ -490,9 +492,8 @@ if features['win32-desktop']
endif endif
if not posix and not features['win32-desktop'] if not posix and not features['win32-desktop']
sources += files('input/ipc-dummy.c', subprocess_source = files('osdep/subprocess-dummy.c')
'osdep/subprocess-dummy.c', sources += subprocess_source + files('input/ipc-dummy.c')
'osdep/terminal-dummy.c')
endif endif
features += {'glob-posix': cc.has_function('glob', prefix: '#include <glob.h>')} features += {'glob-posix': cc.has_function('glob', prefix: '#include <glob.h>')}
@ -1685,9 +1686,6 @@ if get_option('libmpv')
headers = ['libmpv/client.h', 'libmpv/render.h', headers = ['libmpv/client.h', 'libmpv/render.h',
'libmpv/render_gl.h', 'libmpv/stream_cb.h'] 'libmpv/render_gl.h', 'libmpv/stream_cb.h']
install_headers(headers, subdir: 'mpv') install_headers(headers, subdir: 'mpv')
libmpv_test = executable('libmpv-test', 'libmpv/test.c', link_with: [libmpv])
test('libmpv', libmpv_test)
endif endif
if get_option('cplayer') if get_option('cplayer')
@ -1718,8 +1716,12 @@ if get_option('cplayer')
rename: 'mpv.svg') rename: 'mpv.svg')
install_data('etc/mpv-symbolic.svg', install_dir: join_paths(hicolor_dir, 'symbolic', 'apps')) install_data('etc/mpv-symbolic.svg', install_dir: join_paths(hicolor_dir, 'symbolic', 'apps'))
executable('mpv', objects: libmpv.extract_all_objects(recursive: true), dependencies: dependencies, mpv = executable('mpv', objects: libmpv.extract_all_objects(recursive: true), dependencies: dependencies,
win_subsystem: 'windows,6.0', include_directories: includedir, install: true) win_subsystem: 'windows,6.0', include_directories: includedir, install: true)
endif
if get_option('tests')
subdir('test')
endif endif
summary({'d3d11': features['d3d11'], summary({'d3d11': features['d3d11'],

View File

@ -3,7 +3,7 @@ option('gpl', type: 'boolean', value: true, description: 'GPL (version 2 or late
option('cplayer', type: 'boolean', value: true, description: 'mpv CLI player') option('cplayer', type: 'boolean', value: true, description: 'mpv CLI player')
option('libmpv', type: 'boolean', value: false, description: 'libmpv library') option('libmpv', type: 'boolean', value: false, description: 'libmpv library')
option('build-date', type: 'boolean', value: true, description: 'whether to include binary compile time') option('build-date', type: 'boolean', value: true, description: 'whether to include binary compile time')
option('tests', type: 'boolean', value: false, description: 'unit tests (development only)') option('tests', type: 'boolean', value: false, description: 'meson unit tests')
# Reminder: normally always built, but enabled by MPV_LEAK_REPORT. # Reminder: normally always built, but enabled by MPV_LEAK_REPORT.
# Building it can be disabled only by defining NDEBUG through CFLAGS. # Building it can be disabled only by defining NDEBUG through CFLAGS.
option('ta-leak-report', type: 'boolean', value: false, description: 'enable ta leak report by default (development only)') option('ta-leak-report', type: 'boolean', value: false, description: 'enable ta leak report by default (development only)')

View File

@ -840,6 +840,14 @@ struct mp_draw_sub_cache *mp_draw_sub_alloc(void *ta_parent, struct mpv_global *
return c; return c;
} }
// For tests.
struct mp_draw_sub_cache *mp_draw_sub_alloc_test(struct mp_image *dst)
{
struct mp_draw_sub_cache *c = talloc_zero(NULL, struct mp_draw_sub_cache);
reinit_to_video(c);
return c;
}
bool mp_draw_sub_bitmaps(struct mp_draw_sub_cache *p, struct mp_image *dst, bool mp_draw_sub_bitmaps(struct mp_draw_sub_cache *p, struct mp_image *dst,
struct sub_bitmap_list *sbs_list) struct sub_bitmap_list *sbs_list)
{ {

View File

@ -10,6 +10,9 @@ struct mp_draw_sub_cache;
struct mp_draw_sub_cache *mp_draw_sub_alloc(void *ta_parent, struct mpv_global *g); struct mp_draw_sub_cache *mp_draw_sub_alloc(void *ta_parent, struct mpv_global *g);
// Only for use in tests.
struct mp_draw_sub_cache *mp_draw_sub_alloc_test(struct mp_image *dst);
// Render the sub-bitmaps in sbs_list to dst. sbs_list must have been rendered // Render the sub-bitmaps in sbs_list to dst. sbs_list must have been rendered
// for an OSD resolution equivalent to dst's size (UB if not). // for an OSD resolution equivalent to dst's size (UB if not).
// Warning: if dst is a format with alpha, and dst is not set to MP_ALPHA_PREMUL // Warning: if dst is a format with alpha, and dst is not set to MP_ALPHA_PREMUL

View File

@ -1,8 +1,11 @@
#include "audio/chmap.h" #include "audio/chmap.h"
#include "audio/chmap_avchannel.h"
#include "audio/chmap_sel.h" #include "audio/chmap_sel.h"
#include "common/msg.h" #include "config.h"
#include "tests.h" #include "test_utils.h"
#if HAVE_AV_CHANNEL_LAYOUT
#include "audio/chmap_avchannel.h"
#endif
#define LAYOUTS(...) (char*[]){__VA_ARGS__, NULL} #define LAYOUTS(...) (char*[]){__VA_ARGS__, NULL}
@ -65,13 +68,13 @@ static bool layout_matches(const AVChannelLayout *av_layout,
return true; return true;
} }
static void test_mp_chmap_to_av_channel_layout(const struct test_ctx *ctx) static void test_mp_chmap_to_av_channel_layout(void)
{ {
mp_ch_layout_tuple *mapping_array = NULL; mp_ch_layout_tuple *mapping_array = NULL;
void *iter = NULL; void *iter = NULL;
bool anything_failed = false; bool anything_failed = false;
MP_VERBOSE(ctx, "Testing mp_chmap -> AVChannelLayout conversions\n"); printf("Testing mp_chmap -> AVChannelLayout conversions\n");
while ((mapping_array = mp_iterate_builtin_layouts(&iter))) { while ((mapping_array = mp_iterate_builtin_layouts(&iter))) {
const char *mapping_name = (*mapping_array)[0]; const char *mapping_name = (*mapping_array)[0];
@ -93,8 +96,7 @@ static void test_mp_chmap_to_av_channel_layout(const struct test_ctx *ctx)
if (!success) if (!success)
anything_failed = true; anything_failed = true;
MP_MSG(ctx, success ? MSGL_V : MSGL_FATAL, printf("%s: %s (%s) -> %s\n",
"%s: %s (%s) -> %s\n",
success ? "Success" : "Failure", success ? "Success" : "Failure",
mapping_str, mapping_name, layout_desc); mapping_str, mapping_name, layout_desc);
@ -104,13 +106,13 @@ static void test_mp_chmap_to_av_channel_layout(const struct test_ctx *ctx)
assert_false(anything_failed); assert_false(anything_failed);
} }
static void test_av_channel_layout_to_mp_chmap(const struct test_ctx *ctx) static void test_av_channel_layout_to_mp_chmap(void)
{ {
const AVChannelLayout *av_layout = NULL; const AVChannelLayout *av_layout = NULL;
void *iter = NULL; void *iter = NULL;
bool anything_failed = false; bool anything_failed = false;
MP_VERBOSE(ctx, "Testing AVChannelLayout -> mp_chmap conversions\n"); printf("Testing AVChannelLayout -> mp_chmap conversions\n");
while ((av_layout = av_channel_layout_standard(&iter))) { while ((av_layout = av_channel_layout_standard(&iter))) {
struct mp_chmap mp_layout = { 0 }; struct mp_chmap mp_layout = { 0 };
@ -123,12 +125,11 @@ static void test_av_channel_layout_to_mp_chmap(const struct test_ctx *ctx)
if (!ret) { if (!ret) {
bool too_many_channels = bool too_many_channels =
av_layout->nb_channels > MP_NUM_CHANNELS; av_layout->nb_channels > MP_NUM_CHANNELS;
MP_MSG(ctx, too_many_channels ? MSGL_V : MSGL_FATAL, printf("Conversion from '%s' to mp_chmap failed (%s)!\n",
"Conversion from '%s' to mp_chmap failed (%s)!\n", layout_desc,
layout_desc, too_many_channels ?
too_many_channels ? "channel count was over max, ignoring" :
"channel count was over max, ignoring" : "unexpected, failing");
"unexpected, failing");
// we should for now only fail with things such as 22.2 // we should for now only fail with things such as 22.2
// due to mp_chmap being currently limited to 16 channels // due to mp_chmap being currently limited to 16 channels
@ -143,8 +144,7 @@ static void test_av_channel_layout_to_mp_chmap(const struct test_ctx *ctx)
if (!success) if (!success)
anything_failed = true; anything_failed = true;
MP_MSG(ctx, success ? MSGL_V : MSGL_FATAL, printf("%s: %s -> %s\n",
"%s: %s -> %s\n",
success ? "Success" : "Failure", success ? "Success" : "Failure",
layout_desc, mp_chmap_to_str(&mp_layout)); layout_desc, mp_chmap_to_str(&mp_layout));
} }
@ -154,7 +154,7 @@ static void test_av_channel_layout_to_mp_chmap(const struct test_ctx *ctx)
#endif #endif
static void run(struct test_ctx *ctx) int main(void)
{ {
struct mp_chmap a; struct mp_chmap a;
struct mp_chmap b; struct mp_chmap b;
@ -211,12 +211,8 @@ static void run(struct test_ctx *ctx)
assert_int_equal(mp_chmap_diffn(&b, &a), 3); assert_int_equal(mp_chmap_diffn(&b, &a), 3);
#if HAVE_AV_CHANNEL_LAYOUT #if HAVE_AV_CHANNEL_LAYOUT
test_av_channel_layout_to_mp_chmap(ctx); test_av_channel_layout_to_mp_chmap();
test_mp_chmap_to_av_channel_layout(ctx); test_mp_chmap_to_av_channel_layout();
#endif #endif
return 0;
} }
const struct unittest test_chmap = {
.name = "chmap",
.run = run,
};

View File

@ -1,7 +1,7 @@
#include "tests.h" #include "test_utils.h"
#include "video/out/gpu/video.h" #include "video/out/gpu/utils.h"
static void run(struct test_ctx *ctx) int main(void)
{ {
float x; float x;
@ -21,9 +21,5 @@ static void run(struct test_ctx *ctx)
x = gl_video_scale_ambient_lux(16.0, 64.0, 2.40, 1.961, 32.0); x = gl_video_scale_ambient_lux(16.0, 64.0, 2.40, 1.961, 32.0);
float mid_gamma = (2.40 - 1.961) / 2 + 1.961; float mid_gamma = (2.40 - 1.961) / 2 + 1.961;
assert_float_equal(x, mid_gamma, FLT_EPSILON); assert_float_equal(x, mid_gamma, FLT_EPSILON);
return 0;
} }
const struct unittest test_gl_video = {
.name = "gl_video",
.run = run,
};

View File

@ -1,55 +1,16 @@
#include <libavutil/frame.h> #include <libavutil/frame.h>
#include <libavutil/pixdesc.h> #include <libavutil/pixdesc.h>
#include "tests.h" #include "img_utils.h"
#include "options/path.h"
#include "test_utils.h"
#include "video/fmt-conversion.h" #include "video/fmt-conversion.h"
#include "video/img_format.h" #include "video/img_format.h"
#include "video/mp_image.h" #include "video/mp_image.h"
#include "video/sws_utils.h" #include "video/sws_utils.h"
int imgfmts[IMGFMT_AVPIXFMT_END - IMGFMT_AVPIXFMT_START + 100];
int num_imgfmts;
static enum AVPixelFormat pixfmt_unsup[100]; static enum AVPixelFormat pixfmt_unsup[100];
static int num_pixfmt_unsup; static int num_pixfmt_unsup;
static bool imgfmts_initialized;
static int cmp_imgfmt_name(const void *a, const void *b)
{
char *name_a = mp_imgfmt_to_name(*(int *)a);
char *name_b = mp_imgfmt_to_name(*(int *)b);
return strcmp(name_a, name_b);
}
void init_imgfmts_list(void)
{
if (imgfmts_initialized)
return;
const AVPixFmtDescriptor *avd = av_pix_fmt_desc_next(NULL);
for (; avd; avd = av_pix_fmt_desc_next(avd)) {
enum AVPixelFormat fmt = av_pix_fmt_desc_get_id(avd);
int mpfmt = pixfmt2imgfmt(fmt);
if (!mpfmt) {
assert(num_pixfmt_unsup < MP_ARRAY_SIZE(pixfmt_unsup));
pixfmt_unsup[num_pixfmt_unsup++] = fmt;
}
}
for (int fmt = IMGFMT_START; fmt <= IMGFMT_END; fmt++) {
struct mp_imgfmt_desc d = mp_imgfmt_get_desc(fmt);
enum AVPixelFormat pixfmt = imgfmt2pixfmt(fmt);
if (d.id || pixfmt != AV_PIX_FMT_NONE) {
assert(num_imgfmts < MP_ARRAY_SIZE(imgfmts)); // enlarge that array
imgfmts[num_imgfmts++] = fmt;
}
}
qsort(imgfmts, num_imgfmts, sizeof(imgfmts[0]), cmp_imgfmt_name);
imgfmts_initialized = true;
}
static const char *comp_type(enum mp_component_type type) static const char *comp_type(enum mp_component_type type)
{ {
@ -60,11 +21,13 @@ static const char *comp_type(enum mp_component_type type)
} }
} }
static void run(struct test_ctx *ctx) int main(int argc, char *argv[])
{ {
init_imgfmts_list(); init_imgfmts_list();
const char *refdir = argv[1];
const char *outdir = argv[2];
FILE *f = test_open_out(ctx, "img_formats.txt"); FILE *f = test_open_out(outdir, "img_formats.txt");
for (int z = 0; z < num_imgfmts; z++) { for (int z = 0; z < num_imgfmts; z++) {
int mpfmt = imgfmts[z]; int mpfmt = imgfmts[z];
@ -248,11 +211,7 @@ static void run(struct test_ctx *ctx)
fclose(f); fclose(f);
assert_text_files_equal(ctx, "img_formats.txt", "img_formats.txt", assert_text_files_equal(refdir, outdir, "img_formats.txt",
"This can fail if FFmpeg adds new formats or flags."); "This can fail if FFmpeg adds new formats or flags.");
return 0;
} }
const struct unittest test_img_format = {
.name = "img_format",
.run = run,
};

63
test/img_utils.c Normal file
View File

@ -0,0 +1,63 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include <assert.h>
#include <libavutil/frame.h>
#include <libavutil/pixdesc.h>
#include "common/common.h"
#include "img_utils.h"
#include "video/img_format.h"
#include "video/fmt-conversion.h"
int imgfmts[IMGFMT_AVPIXFMT_END - IMGFMT_AVPIXFMT_START + 100];
int num_imgfmts;
static enum AVPixelFormat pixfmt_unsup[100];
static int num_pixfmt_unsup;
static int cmp_imgfmt_name(const void *a, const void *b)
{
char *name_a = mp_imgfmt_to_name(*(int *)a);
char *name_b = mp_imgfmt_to_name(*(int *)b);
return strcmp(name_a, name_b);
}
void init_imgfmts_list(void)
{
const AVPixFmtDescriptor *avd = av_pix_fmt_desc_next(NULL);
for (; avd; avd = av_pix_fmt_desc_next(avd)) {
enum AVPixelFormat fmt = av_pix_fmt_desc_get_id(avd);
int mpfmt = pixfmt2imgfmt(fmt);
if (!mpfmt) {
assert(num_pixfmt_unsup < MP_ARRAY_SIZE(pixfmt_unsup));
pixfmt_unsup[num_pixfmt_unsup++] = fmt;
}
}
for (int fmt = IMGFMT_START; fmt <= IMGFMT_END; fmt++) {
struct mp_imgfmt_desc d = mp_imgfmt_get_desc(fmt);
enum AVPixelFormat pixfmt = imgfmt2pixfmt(fmt);
if (d.id || pixfmt != AV_PIX_FMT_NONE) {
assert(num_imgfmts < MP_ARRAY_SIZE(imgfmts)); // enlarge that array
imgfmts[num_imgfmts++] = fmt;
}
}
qsort(imgfmts, num_imgfmts, sizeof(imgfmts[0]), cmp_imgfmt_name);
}

24
test/img_utils.h Normal file
View File

@ -0,0 +1,24 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
// Sorted list of valid imgfmts. Call init_imgfmts_list() before use.
extern int imgfmts[];
extern int num_imgfmts;
void init_imgfmts_list(void);

View File

@ -1,4 +0,0 @@
GAMEPAD_RIGHT_STICK_LEFT seek -10
GAMEPAD_RIGHT_STICK_RIGHT seek 10
GAMEPAD_LEFT_TRIGGER seek -1
GAMEPAD_RIGHT_TRIGGER seek 1

View File

@ -1,7 +1,6 @@
#include "common/common.h"
#include "misc/json.h" #include "misc/json.h"
#include "misc/node.h" #include "misc/node.h"
#include "tests.h" #include "test_utils.h"
struct entry { struct entry {
const char *src; const char *src;
@ -66,7 +65,7 @@ static const struct entry entries[] = {
#define MAX_DEPTH 10 #define MAX_DEPTH 10
static void run(struct test_ctx *ctx) int main(void)
{ {
for (int n = 0; n < MP_ARRAY_SIZE(entries); n++) { for (int n = 0; n < MP_ARRAY_SIZE(entries); n++) {
const struct entry *e = &entries[n]; const struct entry *e = &entries[n];
@ -86,9 +85,5 @@ static void run(struct test_ctx *ctx)
assert_true(equal_mpv_node(&e->out_data, &res)); assert_true(equal_mpv_node(&e->out_data, &res));
talloc_free(tmp); talloc_free(tmp);
} }
return 0;
} }
const struct unittest test_json = {
.name = "json",
.run = run,
};

View File

@ -1,6 +1,6 @@
#include "common/common.h" #include "common/common.h"
#include "misc/linked_list.h" #include "misc/linked_list.h"
#include "tests.h" #include "test_utils.h"
struct list_item { struct list_item {
int v; int v;
@ -55,7 +55,7 @@ static bool do_check_list(struct the_list *lst, int *c, int num_c)
return true; return true;
} }
static void run(struct test_ctx *ctx) int main(void)
{ {
struct the_list lst = {0}; struct the_list lst = {0};
struct list_item e1 = {1}; struct list_item e1 = {1};
@ -156,9 +156,5 @@ static void run(struct test_ctx *ctx)
LL_INSERT_AFTER(list_node, &lst, STUPID_SHIT(&e1), &e6); LL_INSERT_AFTER(list_node, &lst, STUPID_SHIT(&e1), &e6);
check_list(3, 4, 5, 2, 1, 6); check_list(3, 4, 5, 2, 1, 6);
return 0;
} }
const struct unittest test_linked_list = {
.name = "linked_list",
.run = run,
};

135
test/meson.build Normal file
View File

@ -0,0 +1,135 @@
# So we don't have to reorganize the entire directory tree.
incdir = include_directories('../')
outdir = join_paths(build_root, 'test', 'out')
refdir = join_paths(source_root, 'test', 'ref')
# Convenient testing libraries. An adhoc collection of
# mpv objects that test_utils.c needs. Paths and subprocesses
# are required in order to run a diff command when comparing
# different files. Stuff will probably break if core things are
# carelessly moved around.
test_utils_args = []
test_utils_files = [
'audio/chmap.c',
'audio/format.c',
'common/common.c',
'misc/bstr.c',
'misc/dispatch.c',
'misc/json.c',
'misc/node.c',
'misc/random.c',
'misc/thread_tools.c',
'options/m_config_core.c',
'options/m_config_frontend.c',
'options/m_option.c',
'options/path.c',
'osdep/io.c',
'osdep/subprocess.c',
path_source,
subprocess_source,
'ta/ta.c',
'ta/ta_talloc.c',
'ta/ta_utils.c'
]
# The zimg code requires using threads. On windows, threads
# also requires timer code so this is added.
if features['win32-internal-pthreads']
test_utils_args += '-DWIN32_TESTS'
test_utils_files += ['osdep/timer.c',
'osdep/timer-win2.c',
'osdep/win32/pthread.c',
'osdep/windows_utils.c']
endif
test_utils_deps = [libavutil, libm, pthreads]
if features['win32-desktop']
test_utils_deps += cc.find_library('winmm')
endif
test_utils_objects = libmpv.extract_objects(test_utils_files)
test_utils = static_library('test-utils', 'test_utils.c', include_directories: incdir,
c_args: test_utils_args, objects: test_utils_objects,
dependencies: test_utils_deps)
# For getting imgfmts and stuff.
img_utils_files = [
'misc/thread_pool.c',
'osdep/threads.c',
'video/csputils.c',
'video/fmt-conversion.c',
'video/img_format.c',
'video/mp_image.c',
'video/sws_utils.c'
]
if features['zimg']
img_utils_files += ['video/repack.c', 'video/zimg.c']
endif
img_utils_objects = libmpv.extract_objects(img_utils_files)
img_utils = static_library('img-utils', 'img_utils.c', include_directories: incdir,
dependencies: [libavcodec], objects: img_utils_objects)
# The actual tests.
chmap_files = [
'audio/chmap_sel.c'
]
if features['av-channel-layout']
chmap_files += 'audio/chmap_avchannel.c'
endif
chmap_objects = libmpv.extract_objects(chmap_files)
chmap = executable('chmap', 'chmap.c', include_directories: incdir,
objects: chmap_objects, link_with: test_utils)
test('chmap', chmap)
gl_video_objects = libmpv.extract_objects('video/out/gpu/ra.c',
'video/out/gpu/utils.c')
gl_video = executable('gl-video', 'gl_video.c', objects: gl_video_objects,
include_directories: incdir, link_with: [img_utils, test_utils])
test('gl-video', gl_video)
json = executable('json', 'json.c', include_directories: incdir, link_with: test_utils)
test('json', json)
linked_list = executable('linked-list', files('linked_list.c'), include_directories: incdir)
test('linked-list', linked_list)
paths_objects = libmpv.extract_objects('options/path.c', path_source)
paths = executable('paths', 'paths.c', include_directories: incdir,
objects: paths_objects, link_with: test_utils)
test('paths', paths)
if get_option('libmpv')
libmpv_test = executable('libmpv-test', 'libmpv_test.c',
include_directories: incdir, link_with: libmpv)
test('libmpv', libmpv_test)
endif
# Minimum required libavutil version that works with these tests.
# Will need to be manually updated when ffmpeg adds/removes more formats in the future.
if libavutil.version().version_compare('>= 57.10.101')
# The CI can randomly fail if libavutil isn't explictly linked again here.
img_format = executable('img-format', 'img_format.c', include_directories: incdir,
dependencies: libavutil, link_with: [img_utils, test_utils])
test('img-format', img_format, args: [refdir, outdir], suite: 'ffmpeg')
scale_sws_objects = libmpv.extract_objects('video/image_writer.c',
'video/repack.c')
scale_sws = executable('scale-sws', ['scale_sws.c', 'scale_test.c'], include_directories: incdir,
objects: scale_sws_objects, dependencies: [libswscale, jpeg, zimg],
link_with: [img_utils, test_utils])
test('scale-sws', scale_sws, args: [refdir, outdir], suite: 'ffmpeg')
if features['zimg']
repack_objects = libmpv.extract_objects('sub/draw_bmp.c')
repack = executable('repack', 'repack.c', include_directories: incdir, objects: repack_objects,
dependencies: [libswscale, zimg], link_with: [img_utils, test_utils])
test('repack', repack, args: [refdir, outdir], suite: 'ffmpeg')
scale_zimg_objects = libmpv.extract_objects('video/image_writer.c')
scale_zimg = executable('scale-zimg', ['scale_test.c', 'scale_zimg.c'], include_directories: incdir,
objects: scale_zimg_objects, dependencies:[libswscale, jpeg, zimg],
link_with: [img_utils, test_utils])
test('scale-zimg', scale_zimg, args: [refdir, outdir], suite: 'ffmpeg')
endif
endif

View File

@ -1,7 +1,8 @@
#include "common/common.h" #include "common/common.h"
#include "common/msg.h"
#include "config.h" #include "config.h"
#include "options/path.h" #include "options/path.h"
#include "tests.h" #include "test_utils.h"
static void test_join(char *file, int line, char *a, char *b, char *c) static void test_join(char *file, int line, char *a, char *b, char *c)
{ {
@ -29,7 +30,7 @@ static void test_abs(char *file, int line, bool abs, char *a)
#define TEST_ABS(abs, a) \ #define TEST_ABS(abs, a) \
test_abs(__FILE__, __LINE__, abs, a) test_abs(__FILE__, __LINE__, abs, a)
static void run(struct test_ctx *ctx) int main(void)
{ {
TEST_ABS(true, "/ab"); TEST_ABS(true, "/ab");
TEST_ABS(false, "ab"); TEST_ABS(false, "ab");
@ -60,9 +61,5 @@ static void run(struct test_ctx *ctx)
TEST_JOIN("c:a", "b", "c:a/b"); TEST_JOIN("c:a", "b", "c:a/b");
TEST_JOIN("c:", "b", "c:b"); TEST_JOIN("c:", "b", "c:b");
#endif #endif
return 0;
} }
const struct unittest test_paths = {
.name = "paths",
.run = run,
};

View File

@ -3,10 +3,13 @@
#include <libavutil/pixfmt.h> #include <libavutil/pixfmt.h>
#include "common/common.h" #include "common/common.h"
#include "common/global.h"
#include "img_utils.h"
#include "sub/draw_bmp.h" #include "sub/draw_bmp.h"
#include "sub/osd.h" #include "sub/osd.h"
#include "tests.h" #include "test_utils.h"
#include "video/fmt-conversion.h" #include "video/fmt-conversion.h"
#include "video/mp_image.h"
#include "video/img_format.h" #include "video/img_format.h"
#include "video/repack.h" #include "video/repack.h"
#include "video/sws_utils.h" #include "video/sws_utils.h"
@ -185,8 +188,7 @@ static bool is_true_planar(int imgfmt)
return true; return true;
} }
static int try_repack(struct test_ctx *ctx, FILE *f, int imgfmt, int flags, static int try_repack(FILE *f, int imgfmt, int flags, int not_if_fmt)
int not_if_fmt)
{ {
char *head = mp_tprintf(80, "%-15s =>", mp_imgfmt_to_name(imgfmt)); char *head = mp_tprintf(80, "%-15s =>", mp_imgfmt_to_name(imgfmt));
struct mp_repack *un = mp_repack_create_planar(imgfmt, false, flags); struct mp_repack *un = mp_repack_create_planar(imgfmt, false, flags);
@ -324,7 +326,7 @@ static int try_repack(struct test_ctx *ctx, FILE *f, int imgfmt, int flags,
return b; return b;
} }
static void check_float_repack(struct test_ctx *ctx, int imgfmt, enum mp_csp csp, static void check_float_repack(int imgfmt, enum mp_csp csp,
enum mp_csp_levels levels) enum mp_csp_levels levels)
{ {
imgfmt = UNFUCK(imgfmt); imgfmt = UNFUCK(imgfmt);
@ -339,10 +341,8 @@ static void check_float_repack(struct test_ctx *ctx, int imgfmt, enum mp_csp csp
int w = 1 << (bpp * 8); int w = 1 << (bpp * 8);
if (w > ZIMG_IMAGE_DIMENSION_MAX) { if (w > ZIMG_IMAGE_DIMENSION_MAX) {
MP_WARN(ctx, printf("Image dimension (%d) exceeded maximum allowed by zimg (%zu)."
"Image dimension (%d) exceeded maximum allowed by zimg (%zu)." " Skipping test...\n", w, ZIMG_IMAGE_DIMENSION_MAX);
" Skipping test...\n",
w, ZIMG_IMAGE_DIMENSION_MAX);
return; return;
} }
@ -433,7 +433,7 @@ static void check_float_repack(struct test_ctx *ctx, int imgfmt, enum mp_csp csp
talloc_free(from_f); talloc_free(from_f);
} }
static bool try_draw_bmp(struct mpv_global *g, FILE *f, int imgfmt) static bool try_draw_bmp(FILE *f, int imgfmt)
{ {
bool ok = false; bool ok = false;
@ -465,7 +465,7 @@ static bool try_draw_bmp(struct mpv_global *g, FILE *f, int imgfmt)
.num_items = 1, .num_items = 1,
}; };
struct mp_draw_sub_cache *c = mp_draw_sub_alloc(NULL, g); struct mp_draw_sub_cache *c = mp_draw_sub_alloc_test(dst);
if (mp_draw_sub_bitmaps(c, dst, &sbs_list)) { if (mp_draw_sub_bitmaps(c, dst, &sbs_list)) {
char *info = mp_draw_sub_get_dbg_info(c); char *info = mp_draw_sub_get_dbg_info(c);
fprintf(f, "%s\n", info); fprintf(f, "%s\n", info);
@ -482,53 +482,51 @@ done:
return ok; return ok;
} }
static void run(struct test_ctx *ctx) int main(int argc, char *argv[])
{ {
FILE *f = test_open_out(ctx, "repack.txt"); const char *refdir = argv[1];
const char *outdir = argv[2];
FILE *f = test_open_out(outdir, "repack.txt");
init_imgfmts_list(); init_imgfmts_list();
for (int n = 0; n < num_imgfmts; n++) { for (int n = 0; n < num_imgfmts; n++) {
int imgfmt = imgfmts[n]; int imgfmt = imgfmts[n];
int other = try_repack(ctx, f, imgfmt, 0, 0); int other = try_repack(f, imgfmt, 0, 0);
try_repack(ctx, f, imgfmt, REPACK_CREATE_ROUND_DOWN, other); try_repack(f, imgfmt, REPACK_CREATE_ROUND_DOWN, other);
try_repack(ctx, f, imgfmt, REPACK_CREATE_EXPAND_8BIT, other); try_repack(f, imgfmt, REPACK_CREATE_EXPAND_8BIT, other);
try_repack(ctx, f, imgfmt, REPACK_CREATE_PLANAR_F32, other); try_repack(f, imgfmt, REPACK_CREATE_PLANAR_F32, other);
} }
fclose(f); fclose(f);
assert_text_files_equal(ctx, "repack.txt", "repack.txt", assert_text_files_equal(refdir, outdir, "repack.txt",
"This can fail if FFmpeg/libswscale adds or removes pixfmts."); "This can fail if FFmpeg/libswscale adds or removes pixfmts.");
check_float_repack(ctx, -AV_PIX_FMT_GBRAP, MP_CSP_RGB, MP_CSP_LEVELS_PC); check_float_repack(-AV_PIX_FMT_GBRAP, MP_CSP_RGB, MP_CSP_LEVELS_PC);
check_float_repack(ctx, -AV_PIX_FMT_GBRAP10, MP_CSP_RGB, MP_CSP_LEVELS_PC); check_float_repack(-AV_PIX_FMT_GBRAP10, MP_CSP_RGB, MP_CSP_LEVELS_PC);
check_float_repack(ctx, -AV_PIX_FMT_GBRAP16, MP_CSP_RGB, MP_CSP_LEVELS_PC); check_float_repack(-AV_PIX_FMT_GBRAP16, MP_CSP_RGB, MP_CSP_LEVELS_PC);
check_float_repack(ctx, -AV_PIX_FMT_YUVA444P, MP_CSP_BT_709, MP_CSP_LEVELS_PC); check_float_repack(-AV_PIX_FMT_YUVA444P, MP_CSP_BT_709, MP_CSP_LEVELS_PC);
check_float_repack(ctx, -AV_PIX_FMT_YUVA444P, MP_CSP_BT_709, MP_CSP_LEVELS_TV); check_float_repack(-AV_PIX_FMT_YUVA444P, MP_CSP_BT_709, MP_CSP_LEVELS_TV);
check_float_repack(ctx, -AV_PIX_FMT_YUVA444P10, MP_CSP_BT_709, MP_CSP_LEVELS_PC); check_float_repack(-AV_PIX_FMT_YUVA444P10, MP_CSP_BT_709, MP_CSP_LEVELS_PC);
check_float_repack(ctx, -AV_PIX_FMT_YUVA444P10, MP_CSP_BT_709, MP_CSP_LEVELS_TV); check_float_repack(-AV_PIX_FMT_YUVA444P10, MP_CSP_BT_709, MP_CSP_LEVELS_TV);
check_float_repack(ctx, -AV_PIX_FMT_YUVA444P16, MP_CSP_BT_709, MP_CSP_LEVELS_PC); check_float_repack(-AV_PIX_FMT_YUVA444P16, MP_CSP_BT_709, MP_CSP_LEVELS_PC);
check_float_repack(ctx, -AV_PIX_FMT_YUVA444P16, MP_CSP_BT_709, MP_CSP_LEVELS_TV); check_float_repack(-AV_PIX_FMT_YUVA444P16, MP_CSP_BT_709, MP_CSP_LEVELS_TV);
// Determine the list of possible draw_bmp input formats. Do this here // Determine the list of possible draw_bmp input formats. Do this here
// because it mostly depends on repack and imgformat stuff. // because it mostly depends on repack and imgformat stuff.
f = test_open_out(ctx, "draw_bmp.txt"); f = test_open_out(outdir, "draw_bmp.txt");
for (int n = 0; n < num_imgfmts; n++) { for (int n = 0; n < num_imgfmts; n++) {
int imgfmt = imgfmts[n]; int imgfmt = imgfmts[n];
fprintf(f, "%-12s= ", mp_imgfmt_to_name(imgfmt)); fprintf(f, "%-12s= ", mp_imgfmt_to_name(imgfmt));
try_draw_bmp(ctx->global, f, imgfmt); try_draw_bmp(f, imgfmt);
} }
fclose(f); fclose(f);
assert_text_files_equal(ctx, "draw_bmp.txt", "draw_bmp.txt", assert_text_files_equal(refdir, outdir, "draw_bmp.txt",
"This can fail if FFmpeg/libswscale adds or removes pixfmts."); "This can fail if FFmpeg/libswscale adds or removes pixfmts.");
return 0;
} }
const struct unittest test_repack = {
.name = "repack",
.run = run,
};

View File

@ -23,7 +23,7 @@ static const struct scale_test_fns fns = {
.supports_fmts = supports_fmts, .supports_fmts = supports_fmts,
}; };
static void run(struct test_ctx *ctx) int main(int argc, char *argv[])
{ {
struct mp_sws_context *sws = mp_sws_alloc(NULL); struct mp_sws_context *sws = mp_sws_alloc(NULL);
@ -31,15 +31,12 @@ static void run(struct test_ctx *ctx)
stest->fns = &fns; stest->fns = &fns;
stest->fns_priv = sws; stest->fns_priv = sws;
stest->test_name = "repack_sws"; stest->test_name = "repack_sws";
stest->ctx = ctx; stest->refdir = talloc_strdup(stest, argv[1]);
stest->outdir = talloc_strdup(stest, argv[2]);
repack_test_run(stest); repack_test_run(stest);
talloc_free(stest); talloc_free(stest);
talloc_free(sws); talloc_free(sws);
return 0;
} }
const struct unittest test_repack_sws = {
.name = "repack_sws",
.run = run,
};

View File

@ -55,13 +55,13 @@ static struct mp_image *gen_repack_test_img(int w, int h, int bytes, bool rgb,
static void dump_image(struct scale_test *stest, const char *name, static void dump_image(struct scale_test *stest, const char *name,
struct mp_image *img) struct mp_image *img)
{ {
char *path = mp_tprintf(4096, "%s/%s.png", stest->ctx->out_path, name); char *path = mp_tprintf(4096, "%s/%s.png", stest->outdir, name);
struct image_writer_opts opts = image_writer_opts_defaults; struct image_writer_opts opts = image_writer_opts_defaults;
opts.format = AV_CODEC_ID_PNG; opts.format = AV_CODEC_ID_PNG;
if (!write_image(img, &opts, path, stest->ctx->global, stest->ctx->log)) { if (!write_image(img, &opts, path, NULL, NULL)) {
MP_FATAL(stest->ctx, "Failed to write '%s'.\n", path); printf("Failed to write '%s'.\n", path);
abort(); abort();
} }
} }
@ -102,7 +102,7 @@ static void assert_imgs_equal(struct scale_test *stest, FILE *f,
void repack_test_run(struct scale_test *stest) void repack_test_run(struct scale_test *stest)
{ {
char *logname = mp_tprintf(80, "%s.log", stest->test_name); char *logname = mp_tprintf(80, "%s.log", stest->test_name);
FILE *f = test_open_out(stest->ctx, logname); FILE *f = test_open_out(stest->outdir, logname);
if (!stest->sws) { if (!stest->sws) {
init_imgfmts_list(); init_imgfmts_list();
@ -187,6 +187,6 @@ void repack_test_run(struct scale_test *stest)
fclose(f); fclose(f);
assert_text_files_equal(stest->ctx, logname, logname, assert_text_files_equal(stest->refdir, stest->outdir, logname,
"This can fail if FFmpeg adds or removes pixfmts."); "This can fail if FFmpeg adds or removes pixfmts.");
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include "tests.h" #include "img_utils.h"
#include "test_utils.h"
#include "video/mp_image.h" #include "video/mp_image.h"
struct scale_test_fns { struct scale_test_fns {
@ -13,7 +14,8 @@ struct scale_test {
const struct scale_test_fns *fns; const struct scale_test_fns *fns;
void *fns_priv; void *fns_priv;
const char *test_name; const char *test_name;
struct test_ctx *ctx; const char *refdir;
const char *outdir;
// Private. // Private.
struct mp_image *img_repack_rgb8; struct mp_image *img_repack_rgb8;

View File

@ -21,7 +21,7 @@ static const struct scale_test_fns fns = {
.supports_fmts = supports_fmts, .supports_fmts = supports_fmts,
}; };
static void run(struct test_ctx *ctx) int main(int argc, char *argv[])
{ {
struct mp_zimg_context *zimg = mp_zimg_alloc(); struct mp_zimg_context *zimg = mp_zimg_alloc();
zimg->opts.threads = 1; zimg->opts.threads = 1;
@ -30,12 +30,12 @@ static void run(struct test_ctx *ctx)
stest->fns = &fns; stest->fns = &fns;
stest->fns_priv = zimg; stest->fns_priv = zimg;
stest->test_name = "repack_zimg"; stest->test_name = "repack_zimg";
stest->ctx = ctx; stest->refdir = talloc_strdup(stest, argv[1]);
stest->outdir = talloc_strdup(stest, argv[2]);
repack_test_run(stest); repack_test_run(stest);
FILE *f = test_open_out(ctx, "zimg_formats.txt"); FILE *f = test_open_out(stest->outdir, "zimg_formats.txt");
init_imgfmts_list();
for (int n = 0; n < num_imgfmts; n++) { for (int n = 0; n < num_imgfmts; n++) {
int imgfmt = imgfmts[n]; int imgfmt = imgfmts[n];
fprintf(f, "%15s%7s%7s%7s%8s |\n", mp_imgfmt_to_name(imgfmt), fprintf(f, "%15s%7s%7s%7s%8s |\n", mp_imgfmt_to_name(imgfmt),
@ -47,14 +47,10 @@ static void run(struct test_ctx *ctx)
} }
fclose(f); fclose(f);
assert_text_files_equal(stest->ctx, "zimg_formats.txt", "zimg_formats.txt", assert_text_files_equal(stest->refdir, stest->outdir, "zimg_formats.txt",
"This can fail if FFmpeg/libswscale adds or removes pixfmts."); "This can fail if FFmpeg/libswscale adds or removes pixfmts.");
talloc_free(stest); talloc_free(stest);
talloc_free(zimg); talloc_free(zimg);
return 0;
} }
const struct unittest test_repack_zimg = {
.name = "repack_zimg",
.run = run,
};

View File

@ -1,15 +0,0 @@
function subtimes() {
mp.msg.info("sub-start: " + mp.get_property_number("sub-start"));
mp.msg.info("sub-end: " + mp.get_property_number("sub-end"));
mp.msg.info("sub-text: " + mp.get_property_native("sub-text"));
}
mp.add_key_binding("t", "subtimes", subtimes);
function secondary_subtimes() {
mp.msg.info("secondary-sub-start: " + mp.get_property_number("secondary-sub-start"));
mp.msg.info("secondary-sub-end: " + mp.get_property_number("secondary-sub-end"));
mp.msg.info("secondary-sub-text: " + mp.get_property_native("secondary-sub-text"));
}
mp.add_key_binding("T", "secondary_subtimes", secondary_subtimes);

114
test/test_utils.c Normal file
View File

@ -0,0 +1,114 @@
#include <libavutil/common.h>
#include "options/m_option.h"
#include "options/path.h"
#include "osdep/subprocess.h"
#include "test_utils.h"
#ifdef NDEBUG
static_assert(false, "don't define NDEBUG for tests");
#endif
void assert_int_equal_impl(const char *file, int line, int64_t a, int64_t b)
{
if (a != b) {
printf("%s:%d: %"PRId64" != %"PRId64"\n", file, line, a, b);
abort();
}
}
void assert_string_equal_impl(const char *file, int line,
const char *a, const char *b)
{
if (strcmp(a, b) != 0) {
printf("%s:%d: '%s' != '%s'\n", file, line, a, b);
abort();
}
}
void assert_float_equal_impl(const char *file, int line,
double a, double b, double tolerance)
{
if (fabs(a - b) > tolerance) {
printf("%s:%d: %f != %f\n", file, line, a, b);
abort();
}
}
FILE *test_open_out(const char *outdir, const char *name)
{
mp_mkdirp(outdir);
assert(mp_path_isdir(outdir));
char *path = mp_tprintf(4096, "%s/%s", outdir, name);
FILE *f = fopen(path, "wb");
if (!f) {
printf("Could not open '%s' for writing: %s\n", path,
mp_strerror(errno));
abort();
}
return f;
}
void assert_text_files_equal_impl(const char *file, int line,
const char *refdir, const char *outdir,
const char *ref, const char *new,
const char *err)
{
char *path_ref = mp_tprintf(4096, "%s/%s", refdir, ref);
char *path_new = mp_tprintf(4096, "%s/%s", outdir, new);
struct mp_subprocess_opts opts = {
.exe = "diff",
.args = (char*[]){"diff", "-u", "--", path_ref, path_new, 0},
.fds = { {0, .src_fd = 0}, {1, .src_fd = 1}, {2, .src_fd = 2} },
.num_fds = 3,
};
struct mp_subprocess_result res;
mp_subprocess2(&opts, &res);
if (res.error || res.exit_status) {
if (res.error)
printf("Note: %s\n", mp_subprocess_err_str(res.error));
printf("Giving up.\n");
abort();
}
}
static void hexdump(const uint8_t *d, size_t size)
{
printf("|");
while (size--) {
printf(" %02x", d[0]);
d++;
}
printf(" |\n");
}
void assert_memcmp_impl(const char *file, int line,
const void *a, const void *b, size_t size)
{
if (memcmp(a, b, size) == 0)
return;
printf("%s:%d: mismatching data:\n", file, line);
hexdump(a, size);
hexdump(b, size);
abort();
}
/* Stubs: see test_utils.h */
struct mp_log *mp_null_log;
const char *mp_help_text;
void mp_msg(struct mp_log *log, int lev, const char *format, ...) {};
int mp_msg_find_level(const char *s) {return 0;};
int mp_msg_level(struct mp_log *log) {return 0;};
void mp_write_console_ansi(void) {};
#ifndef WIN32_TESTS
void mp_add_timeout(void) {};
void mp_rel_time_to_timespec(void) {};
void mp_time_us(void) {};
void mp_time_us_to_timespec(void) {};
#endif

64
test/test_utils.h Normal file
View File

@ -0,0 +1,64 @@
#pragma once
#include <float.h>
#include <inttypes.h>
#include <math.h>
#include "common/common.h"
#define assert_true(x) assert(x)
#define assert_false(x) assert(!(x))
#define assert_int_equal(a, b) \
assert_int_equal_impl(__FILE__, __LINE__, (a), (b))
#define assert_string_equal(a, b) \
assert_string_equal_impl(__FILE__, __LINE__, (a), (b))
#define assert_float_equal(a, b, tolerance) \
assert_float_equal_impl(__FILE__, __LINE__, (a), (b), (tolerance))
// Assert that memcmp(a,b,s)==0, or hexdump output on failure.
#define assert_memcmp(a, b, s) \
assert_memcmp_impl(__FILE__, __LINE__, (a), (b), (s))
// Require that the files "ref" and "new" are the same. The paths can be
// relative to ref_path and out_path respectively. If they're not the same,
// the output of "diff" is shown, the err message (if not NULL), and the test
// fails.
#define assert_text_files_equal(refdir, outdir, name, err) \
assert_text_files_equal_impl(__FILE__, __LINE__, (refdir), (outdir), (name), (name), (err))
void assert_int_equal_impl(const char *file, int line, int64_t a, int64_t b);
void assert_string_equal_impl(const char *file, int line,
const char *a, const char *b);
void assert_float_equal_impl(const char *file, int line,
double a, double b, double tolerance);
void assert_text_files_equal_impl(const char *file, int line,
const char *refdir, const char *outdir,
const char *ref, const char *new,
const char *err);
void assert_memcmp_impl(const char *file, int line,
const void *a, const void *b, size_t size);
// Open a new file in the build dir path. Always succeeds.
FILE *test_open_out(const char *outdir, const char *name);
/* Stubs */
// Files commonly import common/msg.h which requires these to be
// defined. We don't actually need mpv's logging system here so
// just define these as stubs that do nothing.
struct mp_log;
void mp_msg(struct mp_log *log, int lev, const char *format, ...)
PRINTF_ATTRIBUTE(3, 4);
int mp_msg_find_level(const char *s);
int mp_msg_level(struct mp_log *log);
void mp_write_console_ansi(void);
// Windows additionally requires timer related code so it will actually
// import the real versions of these functions and use them. On other
// platforms, these can just be stubs for simplicity.
#ifndef WIN32_TESTS
void mp_add_timeout(void);
void mp_rel_time_to_timespec(void);
void mp_time_us(void);
void mp_time_us_to_timespec(void);
#endif

View File

@ -1,159 +0,0 @@
#include "options/path.h"
#include "osdep/subprocess.h"
#include "player/core.h"
#include "tests.h"
static const struct unittest *unittests[] = {
&test_chmap,
&test_gl_video,
&test_img_format,
&test_json,
&test_linked_list,
&test_paths,
&test_repack_sws,
#if HAVE_ZIMG
&test_repack, // zimg only due to cross-checking with zimg.c
&test_repack_zimg,
#endif
NULL
};
bool run_tests(struct MPContext *mpctx)
{
char *sel = mpctx->opts->test_mode;
assert(sel && sel[0]);
if (strcmp(sel, "help") == 0) {
MP_INFO(mpctx, "Available tests:\n");
for (int n = 0; unittests[n]; n++)
MP_INFO(mpctx, " %s\n", unittests[n]->name);
MP_INFO(mpctx, " all-simple\n");
return true;
}
struct test_ctx ctx = {
.global = mpctx->global,
.log = mpctx->log,
.ref_path = "test/ref",
.out_path = "test/out",
};
if (!mp_path_isdir(ctx.ref_path)) {
MP_FATAL(mpctx, "Must be run from git repo root dir.\n");
abort();
}
mp_mkdirp(ctx.out_path);
assert(mp_path_isdir(ctx.out_path));
int num_run = 0;
for (int n = 0; unittests[n]; n++) {
const struct unittest *t = unittests[n];
// Exactly 1 entrypoint please.
assert(MP_IS_POWER_OF_2(
(t->run ? (1 << 1) : 0)));
bool run = false;
run |= strcmp(sel, "all-simple") == 0 && !t->is_complex;
run |= strcmp(sel, t->name) == 0;
if (run) {
if (t->run)
t->run(&ctx);
num_run++;
}
}
MP_INFO(mpctx, "%d unittests successfully run.\n", num_run);
return num_run > 0; // still error if none
}
#ifdef NDEBUG
static_assert(false, "don't define NDEBUG for tests");
#endif
void assert_int_equal_impl(const char *file, int line, int64_t a, int64_t b)
{
if (a != b) {
printf("%s:%d: %"PRId64" != %"PRId64"\n", file, line, a, b);
abort();
}
}
void assert_string_equal_impl(const char *file, int line,
const char *a, const char *b)
{
if (strcmp(a, b) != 0) {
printf("%s:%d: '%s' != '%s'\n", file, line, a, b);
abort();
}
}
void assert_float_equal_impl(const char *file, int line,
double a, double b, double tolerance)
{
if (fabs(a - b) > tolerance) {
printf("%s:%d: %f != %f\n", file, line, a, b);
abort();
}
}
FILE *test_open_out(struct test_ctx *ctx, const char *name)
{
char *path = mp_tprintf(4096, "%s/%s", ctx->out_path, name);
FILE *f = fopen(path, "wb");
if (!f) {
MP_FATAL(ctx, "Could not open '%s' for writing.\n", path);
abort();
}
return f;
}
void assert_text_files_equal_impl(const char *file, int line,
struct test_ctx *ctx, const char *ref,
const char *new, const char *err)
{
char *path_ref = mp_tprintf(4096, "%s/%s", ctx->ref_path, ref);
char *path_new = mp_tprintf(4096, "%s/%s", ctx->out_path, new);
struct mp_subprocess_opts opts = {
.exe = "diff",
.args = (char*[]){"diff", "-u", "--", path_ref, path_new, 0},
.fds = { {0, .src_fd = 0}, {1, .src_fd = 1}, {2, .src_fd = 2} },
.num_fds = 3,
};
struct mp_subprocess_result res;
mp_subprocess2(&opts, &res);
if (res.error || res.exit_status) {
if (res.error)
MP_WARN(ctx, "Note: %s\n", mp_subprocess_err_str(res.error));
MP_FATAL(ctx, "Giving up.\n");
abort();
}
}
static void hexdump(const uint8_t *d, size_t size)
{
printf("|");
while (size--) {
printf(" %02x", d[0]);
d++;
}
printf(" |\n");
}
void assert_memcmp_impl(const char *file, int line,
const void *a, const void *b, size_t size)
{
if (memcmp(a, b, size) == 0)
return;
printf("%s:%d: mismatching data:\n", file, line);
hexdump(a, size);
hexdump(b, size);
abort();
}

View File

@ -1,87 +0,0 @@
#pragma once
#include <float.h>
#include <inttypes.h>
#include <math.h>
#include "common/common.h"
struct MPContext;
bool run_tests(struct MPContext *mpctx);
struct test_ctx {
struct mpv_global *global;
struct mp_log *log;
// Path for ref files, without trailing "/".
const char *ref_path;
// Path for result files, without trailing "/".
const char *out_path;
};
struct unittest {
// This is used to select the test on command line with --unittest=<name>.
const char *name;
// Cannot run without additional arguments supplied.
bool is_complex;
// Entrypoints. There are various for various purposes. Only 1 of them must
// be set.
// Entrypoint for tests which have a simple dependency on the mpv core. The
// core is sufficiently initialized at this point.
void (*run)(struct test_ctx *ctx);
};
extern const struct unittest test_chmap;
extern const struct unittest test_gl_video;
extern const struct unittest test_img_format;
extern const struct unittest test_json;
extern const struct unittest test_linked_list;
extern const struct unittest test_repack_sws;
extern const struct unittest test_repack_zimg;
extern const struct unittest test_repack;
extern const struct unittest test_paths;
#define assert_true(x) assert(x)
#define assert_false(x) assert(!(x))
#define assert_int_equal(a, b) \
assert_int_equal_impl(__FILE__, __LINE__, (a), (b))
#define assert_string_equal(a, b) \
assert_string_equal_impl(__FILE__, __LINE__, (a), (b))
#define assert_float_equal(a, b, tolerance) \
assert_float_equal_impl(__FILE__, __LINE__, (a), (b), (tolerance))
// Assert that memcmp(a,b,s)==0, or hexdump output on failure.
#define assert_memcmp(a, b, s) \
assert_memcmp_impl(__FILE__, __LINE__, (a), (b), (s))
// Require that the files "ref" and "new" are the same. The paths can be
// relative to ref_path and out_path respectively. If they're not the same,
// the output of "diff" is shown, the err message (if not NULL), and the test
// fails.
#define assert_text_files_equal(ctx, ref, new, err) \
assert_text_files_equal_impl(__FILE__, __LINE__, (ctx), (ref), (new), (err))
void assert_int_equal_impl(const char *file, int line, int64_t a, int64_t b);
void assert_string_equal_impl(const char *file, int line,
const char *a, const char *b);
void assert_float_equal_impl(const char *file, int line,
double a, double b, double tolerance);
void assert_text_files_equal_impl(const char *file, int line,
struct test_ctx *ctx, const char *ref,
const char *new, const char *err);
void assert_memcmp_impl(const char *file, int line,
const void *a, const void *b, size_t size);
// Open a new file in the out_path. Always succeeds.
FILE *test_open_out(struct test_ctx *ctx, const char *name);
// Sorted list of valid imgfmts. Call init_imgfmts_list() before use.
extern int imgfmts[];
extern int num_imgfmts;
void init_imgfmts_list(void);

View File

@ -39,6 +39,21 @@ void gl_transform_ortho_fbo(struct gl_transform *t, struct ra_fbo fbo)
gl_transform_ortho(t, 0, fbo.tex->params.w, 0, fbo.tex->params.h * y_dir); gl_transform_ortho(t, 0, fbo.tex->params.w, 0, fbo.tex->params.h * y_dir);
} }
float gl_video_scale_ambient_lux(float lmin, float lmax,
float rmin, float rmax, float lux)
{
assert(lmax > lmin);
float num = (rmax - rmin) * (log10(lux) - log10(lmin));
float den = log10(lmax) - log10(lmin);
float result = num / den + rmin;
// clamp the result
float max = MPMAX(rmax, rmin);
float min = MPMIN(rmax, rmin);
return MPMAX(MPMIN(result, max), min);
}
void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool) void ra_buf_pool_uninit(struct ra *ra, struct ra_buf_pool *pool)
{ {
for (int i = 0; i < pool->num_buffers; i++) for (int i = 0; i < pool->num_buffers; i++)

View File

@ -65,6 +65,9 @@ void gl_transform_trans(struct gl_transform t, struct gl_transform *x);
void gl_transform_ortho_fbo(struct gl_transform *t, struct ra_fbo fbo); void gl_transform_ortho_fbo(struct gl_transform *t, struct ra_fbo fbo);
float gl_video_scale_ambient_lux(float lmin, float lmax,
float rmin, float rmax, float lux);
// A pool of buffers, which can grow as needed // A pool of buffers, which can grow as needed
struct ra_buf_pool { struct ra_buf_pool {
struct ra_buf_params current_params; struct ra_buf_params current_params;

View File

@ -4239,21 +4239,6 @@ static int validate_error_diffusion_opt(struct mp_log *log, const m_option_t *op
return r; return r;
} }
float gl_video_scale_ambient_lux(float lmin, float lmax,
float rmin, float rmax, float lux)
{
assert(lmax > lmin);
float num = (rmax - rmin) * (log10(lux) - log10(lmin));
float den = log10(lmax) - log10(lmin);
float result = num / den + rmin;
// clamp the result
float max = MPMAX(rmax, rmin);
float min = MPMIN(rmax, rmin);
return MPMAX(MPMIN(result, max), min);
}
void gl_video_set_ambient_lux(struct gl_video *p, int lux) void gl_video_set_ambient_lux(struct gl_video *p, int lux)
{ {
if (p->opts.gamma_auto) { if (p->opts.gamma_auto) {

View File

@ -213,6 +213,9 @@ struct mp_sws_context *mp_sws_alloc(void *talloc_ctx)
// if the user changes any options. // if the user changes any options.
void mp_sws_enable_cmdline_opts(struct mp_sws_context *ctx, struct mpv_global *g) void mp_sws_enable_cmdline_opts(struct mp_sws_context *ctx, struct mpv_global *g)
{ {
// Should only ever be NULL for tests.
if (!g)
return;
if (ctx->opts_cache) if (ctx->opts_cache)
return; return;