From 7b43a7492f9dfe8761beb25fe4e24b48d60ff314 Mon Sep 17 00:00:00 2001 From: Jehan Date: Wed, 28 Feb 2024 22:27:28 +0100 Subject: [PATCH] libgimp: new unit testing framework for libgimp. With Python binding, it gets very easy to test new functions. I've been wondering if we need C counterparts, but really since it's a GObject Introspection binding, if a function succeeds there, it should also succeed in C code. For now, I'm testing a few of GimpPalette API. Not all of it yet. Also I test both the direct C binding and PDB procedure since in some cases, one or the other may not properly working. See #10885. --- libgimp/tests/libgimp-run-python-test.sh | 27 ++++++++++++ libgimp/tests/meson.build | 46 ++++++++++++++++++++ libgimp/tests/pygimp/utils.py | 21 +++++++++ libgimp/tests/test-palette.py | 55 ++++++++++++++++++++++++ libgimpconfig/gimpconfig-path.c | 15 +++++-- meson.build | 3 ++ 6 files changed, 163 insertions(+), 4 deletions(-) create mode 100644 libgimp/tests/libgimp-run-python-test.sh create mode 100644 libgimp/tests/meson.build create mode 100755 libgimp/tests/pygimp/utils.py create mode 100644 libgimp/tests/test-palette.py diff --git a/libgimp/tests/libgimp-run-python-test.sh b/libgimp/tests/libgimp-run-python-test.sh new file mode 100644 index 0000000000..6973bbf334 --- /dev/null +++ b/libgimp/tests/libgimp-run-python-test.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +GIMP_EXE=$1 +TEST_FILE=$2 +SRC_DIR=`dirname $TEST_FILE` +SRC_DIR=`realpath $SRC_DIR` + +if [ ! -f "$TEST_FILE" ]; then + echo "ERROR: file '$TEST_FILE' does not exist!" + return 1; +fi + +first_char=`head -c1 "$TEST_FILE"` + +if [ $first_char != '#' ]; then + # Note: I don't actually care that it's a shebang, just that it's a comment, + # hence a useless line, because I'm going to remove it and replace it with + # gimp_assert() import line. + # This will make much easier to debug tests with meaningful line numbers. + echo "ERROR: file '$TEST_FILE' should start with a shebang: #!/usr/bin/env python3" + return 1; +fi + +header="import os; import sys; sys.path.insert(0, '$SRC_DIR'); from pygimp.utils import gimp_assert;" +header="$header import pygimp.utils; pygimp.utils.gimp_test_filename = '$TEST_FILE'" + +(echo "$header" && tail -n +2 "$TEST_FILE") | "$GIMP_EXE" -nis --batch-interpreter "python-fu-eval" -b - --quit diff --git a/libgimp/tests/meson.build b/libgimp/tests/meson.build new file mode 100644 index 0000000000..53bd0f4717 --- /dev/null +++ b/libgimp/tests/meson.build @@ -0,0 +1,46 @@ +# XXX: we have a bunch of (manually run?) tests inside libgimp/test/. +# These should either be deleted or transformed into real unit tests. + +tests = [ + 'palette', +] + +# make GIMP runnable without being installed. +env=environment() + +menu_paths=meson.project_build_root() / 'menus:' + meson.project_source_root() / 'menus' +env.set('GIMP_TESTING_MENUS_PATH', menu_paths) + +env.set('GIMP_TESTING_PLUGINDIRS', meson.project_build_root() / 'plug-ins:') +env.append('GIMP_TESTING_PLUGINDIRS', meson.project_build_root() / 'plug-ins/python') +env.append('GIMP_TESTING_PLUGINDIRS', meson.project_build_root() / 'plug-ins/common/test-plug-ins/') + +env.prepend('GI_TYPELIB_PATH', meson.project_build_root() / 'libgimp') +env.prepend('LD_LIBRARY_PATH', meson.project_build_root() / 'libgimp') +env.prepend('LD_LIBRARY_PATH', meson.project_build_root() / 'libgimpbase') +env.prepend('LD_LIBRARY_PATH', meson.project_build_root() / 'libgimpcolor') +env.prepend('LD_LIBRARY_PATH', meson.project_build_root() / 'libgimpconfig') +env.prepend('LD_LIBRARY_PATH', meson.project_build_root() / 'libgimpmath') +env.prepend('LD_LIBRARY_PATH', meson.project_build_root() / 'libgimpmodule') +env.prepend('LD_LIBRARY_PATH', meson.project_build_root() / 'libgimpthumb') +env.prepend('LD_LIBRARY_PATH', meson.project_build_root() / 'libgimpwidgets') + +env.set('GIMP_TESTING_ABS_TOP_SRCDIR', meson.project_source_root()) + +if enable_console_bin + gimp_exe=gimpconsole_exe +else + gimp_exe=gimpmain_exe +endif + +run_python_test = find_program('./libgimp-run-python-test.sh') +foreach test_name : tests + basename = 'test-' + test_name + py_test = meson.current_source_dir() / basename + '.py' + + test(test_name, run_python_test, + args: [ gimp_exe, py_test ], + env: env, + suite: ['libgimp', 'python3'], + timeout: 60) +endforeach diff --git a/libgimp/tests/pygimp/utils.py b/libgimp/tests/pygimp/utils.py new file mode 100755 index 0000000000..28172ed725 --- /dev/null +++ b/libgimp/tests/pygimp/utils.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +import inspect +import sys + +gimp_test_filename = '' + +def gimp_assert(subtest_name, test): + ''' + Please call me like this, for instance, if I were testing if gimp_image_new() + succeeded: + gimp_assert("gimp_image_new()", image is not None) + ''' + if not test: + frames = inspect.getouterframes(inspect.currentframe()) + sys.stderr.write("\n**** START FAILED SUBTEST *****\n") + sys.stderr.write("ERROR: {} - line {}: {}\n".format(gimp_test_filename, + frames[1].lineno, + subtest_name)) + sys.stderr.write("***** END FAILED SUBTEST ******\n\n") + assert test diff --git a/libgimp/tests/test-palette.py b/libgimp/tests/test-palette.py new file mode 100644 index 0000000000..e8e88fd08b --- /dev/null +++ b/libgimp/tests/test-palette.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +pal = Gimp.Palette.get_by_name('Bears') +gimp_assert('gimp_palette_get_by_name()', type(pal) == Gimp.Palette) + +pal2 = Gimp.Palette.get_by_name('Bears') +gimp_assert('gimp_palette_get_by_name() is unique', pal == pal2) + +colors = pal.get_colors() +gimp_assert('gimp_palette_get_colors()', len(colors) == 256 and type(colors[0]) == Gegl.Color) + +n_colors = pal.get_color_count() +gimp_assert('gimp_palette_get_color_count()', len(colors) == n_colors) + +# Run the same tests through PDB: + +proc = Gimp.get_pdb().lookup_procedure('gimp-palette-get-by-name') +config = proc.create_config() +config.set_property('name', 'Bears') +result = proc.run(config) +status = result.index(0) +gimp_assert('gimp-palette-get-by-name', status == Gimp.PDBStatusType.SUCCESS) + +pal2 = result.index(1) +gimp_assert('gimp-palette-get-by-name and gimp_palette_get_by_name() get identical result', pal == pal2) + +proc = Gimp.get_pdb().lookup_procedure('gimp-palette-get-colors') +config = proc.create_config() +config.set_property('palette', pal) +result = proc.run(config) +status = result.index(0) +gimp_assert('gimp-palette-get-colors', status == Gimp.PDBStatusType.SUCCESS) + +colors = result.index(1) +# XXX This test is actually what happens right now, but not what should happen. +# See: https://gitlab.gnome.org/GNOME/gimp/-/issues/10885#note_2030308 +# And: https://gitlab.gnome.org/GNOME/gobject-introspection/-/issues/492 +# If some day this test fails, and in particular if it can return a list of +# GeglColor, then we should remove the test and deprecate +# gimp_value_array_get_color_array() in favor of the generic +# gimp_value_array_index(). +gimp_assert('gimp-palette-get-colors', type(colors) == GObject.GBoxed) + +colors = result.get_color_array(1) +gimp_assert('gimp_palette_get_colors()', type(colors) == list and len(colors) == 256 and type(colors[0]) == Gegl.Color) + +proc = Gimp.get_pdb().lookup_procedure('gimp-palette-get-color-count') +config = proc.create_config() +config.set_property('palette', pal) +result = proc.run(config) +status = result.index(0) +gimp_assert('gimp-palette-get-color-count', status == Gimp.PDBStatusType.SUCCESS) + +n_colors = result.index(1) +gimp_assert('gimp_palette_get_color_count()', len(colors) == n_colors) diff --git a/libgimpconfig/gimpconfig-path.c b/libgimpconfig/gimpconfig-path.c index 4ac713d64c..ede64cf956 100644 --- a/libgimpconfig/gimpconfig-path.c +++ b/libgimpconfig/gimpconfig-path.c @@ -213,10 +213,17 @@ static gchar * gimp_config_path_unexpand_only (const gchar *path) G_GNUC gchar * gimp_config_build_data_path (const gchar *name) { - return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name, - G_SEARCHPATH_SEPARATOR_S, - "${gimp_data_dir}", G_DIR_SEPARATOR_S, name, - NULL); + if (g_getenv ("GIMP_TESTING_ABS_TOP_SRCDIR")) + /* Unit-testing mode: the source directory is where data is found. */ + return g_strconcat (g_getenv ("GIMP_TESTING_ABS_TOP_SRCDIR"), + G_DIR_SEPARATOR_S, "data", + G_DIR_SEPARATOR_S, name, + NULL); + else + return g_strconcat ("${gimp_dir}", G_DIR_SEPARATOR_S, name, + G_SEARCHPATH_SEPARATOR_S, + "${gimp_data_dir}", G_DIR_SEPARATOR_S, name, + NULL); } /** diff --git a/meson.build b/meson.build index 1cccb9c0e5..32831d17ef 100644 --- a/meson.build +++ b/meson.build @@ -1840,6 +1840,9 @@ subdir('plug-ins') subdir('app') subdir('app-tools') +# Unit testing +subdir('libgimp/tests') + # Docs subdir('docs') subdir('devel-docs')