Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Buildbot embedded unittests #2755

Merged
merged 21 commits into from
May 6, 2024
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
51 changes: 49 additions & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ endif

vpath %.c $(SRC_PATH)

all: version
all: version unit_tests
$(MAKE) -C src all
$(MAKE) -C applications all
ifneq ($(STATIC_BINARY),yes)
Expand Down Expand Up @@ -54,7 +54,7 @@ depend:
$(MAKE) -C applications dep
$(MAKE) -C modules dep

clean:
clean: unit_tests_clean
$(MAKE) -C src clean
$(MAKE) -C applications clean
$(MAKE) -C modules clean
Expand All @@ -77,6 +77,53 @@ doc:
man:
@cd $(SRC_PATH)/share/doc/man && MP4Box -genman && gpac -genman


UT_CFG_PATH:=unittests/build/config

unit_tests:
ifeq ($(UNIT_TESTS),yes)
@echo "Unit Tests:"
@echo "- configuring"
@mkdir -p unittests/build/bin/gcc

@cp config.mak unittests/build/
@sed 's|BUILD_PATH=$(BUILD_PATH)|BUILD_PATH=$(BUILD_PATH)/unittests/build|g' config.mak | \
sed 's|-I"$(BUILD_PATH)"|-I"$(BUILD_PATH)/unittests/build"|g' > $(UT_CFG_PATH).mak.new
@if [ -e $(UT_CFG_PATH).mak ]; then \
if ! diff -q $(UT_CFG_PATH).mak $(UT_CFG_PATH).mak.new >/dev/null ; then \
mv $(UT_CFG_PATH).mak.new $(UT_CFG_PATH).mak; \
fi; \
else \
mv $(UT_CFG_PATH).mak.new $(UT_CFG_PATH).mak; \
fi

@sed 's/GF_STATIC static/GF_STATIC GF_EXPORT/' config.h > $(UT_CFG_PATH).h.new
@if [ -e $(UT_CFG_PATH).h ]; then \
if ! diff -q $(UT_CFG_PATH).h $(UT_CFG_PATH).h.new >/dev/null ; then \
mv $(UT_CFG_PATH).h.new $(UT_CFG_PATH).h; \
fi; \
else \
mv $(UT_CFG_PATH).h.new $(UT_CFG_PATH).h; \
fi

@$(SRC_PATH)/unittests/build.sh > unittests/build/bin/gcc/unittests.c

@echo "- building"
@cd unittests/build && $(MAKE) -C src && $(MAKE) -C src unit_tests

@echo "- executing"
@LD_LIBRARY_PATH=unittests/build/bin/gcc unittests/build/bin/gcc/unittests

@echo "- done"
endif

unit_tests_clean:
ifeq ($(UNIT_TESTS),yes)
@echo "Cleaning unit tests artifacts"
@rm -rf unittests/build/bin
@cd unittests/build && $(MAKE) -C src clean
endif

test_suite:
@cd $(SRC_PATH)/testsuite && ./make_tests.sh -precommit -p=0

Expand Down
51 changes: 33 additions & 18 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ libgpac_cflags=""
libgpac_extralibs=""
static_build="no"
static_bin="no"
unit_tests="no"
static_modules="no"
lm_lib=""
has_directx="no"
Expand Down Expand Up @@ -390,6 +391,7 @@ GPAC build options:
--disable-opt disable GCC optimizations
--static-build link statically against libgpac but still allow dependencies to shared libraries (enable --static-modules)
--static-bin enable static linking of MP4Box and gpac only (will enable --static-build), disable all libraries not linkable statically.
--unittests enable unit tests [default=no]
--sdl-cfg=SDL_PATH specify path to sdl-config for local install [$sdl_path]
--enable-sdl-static use static SDL linking [default=no]
--X11-path=X11_PATH specify path for X11 includes and libraries [$X11_PATH]
Expand Down Expand Up @@ -648,6 +650,8 @@ for opt do
;;
--enable-sanitizer) enable_sanitizer="yes"
;;
--unittests) unit_tests="yes"
;;
--static-modules) static_modules="yes"
;;
--sdl-cfg=*) sdl_path=`echo $opt | cut -d '=' -f 2`; sdl_local="yes"
Expand Down Expand Up @@ -2167,6 +2171,8 @@ if test $cpu = "mips"; then
fi
echo ""
echo "** GPAC $version rev$revision Core Configuration **"
echo "Unit Tests: $unit_tests ('make unit_tests')"
echo "#define GF_STATIC static" >> $TMPH
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this should also be defined in configuration.h (or setup.h?) for platforms that don't use configure/config.h ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added in 672f666. Should we also add a target in the msvc Solution ? xcode ? What's the usual way to add this?

if test "$static_bin" = "yes" ; then
echo "Static binaries enabled"
echo "#define GPAC_STATIC_BIN" >> $TMPH
Expand Down Expand Up @@ -2631,6 +2637,9 @@ man_dir=${mandir#"$pf"}
echo "man_dir=$man_dir" >> config.mak


echo "UNIT_TESTS=$unit_tests" >> config.mak


echo "STATIC_MODULES=$static_modules" >> config.mak

#for cross-compilation
Expand Down Expand Up @@ -3078,57 +3087,63 @@ fi
echo "PKG_CONFIG=$pkg_config" >> config.mak


#build tree in object directory if source path is different from current one
if test "$source_path_used" = "yes" ; then

echo "Creating compilation tree image"
build_object_files_directory_tree()
{
SRC_DIRS="src src/utils src/isomedia src/ietf src/odf src/bifs src/scenegraph src/filter_core src/filters src/crypto src/media_tools src/scene_manager src/compositor src/laser src/evg src/quickjs src/jsmods"

APP_DIRS="applications/gpac applications/mp4box"

for dir in $SRC_DIRS ; do
mkdir -p "$dir"
done
ln -sf "$source_path/Makefile" Makefile
ln -sf "$source_path/static.mak" static.mak
ln -sf "$source_path/src/Makefile" src/Makefile
ln -sf "$1/Makefile" Makefile
ln -sf "$1/static.mak" static.mak
ln -sf "$1/src/Makefile" src/Makefile

mkdir -p applications
ln -sf "$source_path/applications/Makefile" applications/Makefile
ln -sf "$1/applications/Makefile" applications/Makefile
mkdir -p applications/testapps

for dir in $APP_DIRS ; do
mkdir -p "$dir"
ln -sf "$source_path/$dir/Makefile" "$dir/Makefile"
ln -sf "$1/$dir/Makefile" "$dir/Makefile"
done


cur_dir="`pwd`"
cd "$cur_dir"

mkdir -p modules
ln -sf "$source_path/modules/Makefile" modules/Makefile
ln -sf "$1/modules/Makefile" modules/Makefile

for moddir in "$source_path/modules/"* ; do
for moddir in "$1/modules/"* ; do
if [ -d "$moddir" -a -f "$moddir/Makefile" ]; then
dir="${moddir#"$source_path/"}"
dir="${moddir#"$1/"}"
mkdir -p "$dir"
ln -sf "$moddir/Makefile" "$dir/Makefile"
fi
done
if test "$has_directx" = "yes"; then
ln -sf "$source_path/modules/dx_hw/hand.cur" modules/dx_hw/hand.cur
ln -sf "$source_path/modules/dx_hw/collide.cur" modules/dx_hw/collide.cur
ln -sf "$1/modules/dx_hw/hand.cur" modules/dx_hw/hand.cur
ln -sf "$1/modules/dx_hw/collide.cur" modules/dx_hw/collide.cur
fi
}

cd "$cur_dir"
#build tree in object directory if source path is different from current one
if test "$source_path_used" = "yes" ; then
echo "Creating compilation tree image"
build_object_files_directory_tree "$source_path"
fi

echo "SRC_PATH=$source_path" >> config.mak
echo "BUILD_PATH=$build_path" >> config.mak
echo "LOCAL_INC_PATH=$local_inc" >> config.mak


if test "$unit_tests" = "yes" ; then
mkdir -p unittests/build && cd unittests/build
build_object_files_directory_tree "$source_path"
cd -
fi


echo "#endif" >> $TMPH


Expand Down
60 changes: 60 additions & 0 deletions include/tests.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
#pragma once

#include <gpac/setup.h>

#define unittest(suffix) void test_##suffix(void)

extern int checks_passed;
extern int checks_failed;

static Bool verbose_ut = GF_FALSE;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a way to set this to true via compile flags / configure?

Copy link
Member Author

@rbouqueau rbouqueau Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will do. What option do you envision? At the moment we just have --unittests.


// provide some flavoured asserts

#define custom_message_assert(expr, msg) \
do { \
if (expr) { \
checks_passed++; \
} else { \
checks_failed++; \
} \
((expr) \
? gf_fatal_assert(0) \
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we assert(0) is the expr is true here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

outdated comment

: printf("Assertion failed: %s\nMessage: %s\n\tFile: %s\nLine: %d\nFunction: %s\n", #expr, msg, __FILE__, __LINE__, __ASSERT_FUNCTION)); \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In both scenarios, an assertion failure occurs. Within the if statement, checks_passed is incremented, but it results in a call to gf_fatal_assert.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I propose to remove all the elements in that section that are unused. @soheibthriber Let me know if you disagree.

} while (0)

#define custom_action_assert(expr, action) \
do { \
if (expr) { \
checks_passed++; \
} else { \
checks_failed++; \
} \
((expr) \
? gf_fatal_assert(0) \
: (action, gf_fatal_assert(0))); \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here. in both cases, gf_fatal_assert is called.

} while (0)

#define assert_true(expr) \
do { \
if (expr) { \
if (verbose_ut) printf("Assertion passed: \"%s\", File: \"%s\", Line: %d, Function: \"%s\"\n", #expr, __FILE__, __LINE__, __ASSERT_FUNCTION); \
checks_passed++; \
} else { \
checks_failed++; \
gf_assert(0); \
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there any reason for alternating between gf_assert and gf_fatal_assert at times, given their similarity?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No but using similar names is confusing, for sure :/

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

currently if a test fails the whole testing is aborted because of the assert:

unittests: ut_os_config_init.c:36: test_gf_sys_word_match: Assertion `0' failed.
Aborted (core dumped)

and you don't get the summary with the number of tests passed/failed

Copy link
Member Author

@rbouqueau rbouqueau Mar 19, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's right, it is a design choice. Usually unit tests are executed at every build. So this is kind of some incremental approach. However we could add the option for failure to be non-fatal. Adding this in a7d736d.

} \
} while (0)

#define assert_false(expr) assert_true(!(expr))

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Other macros could be added. Here are some suggestions:

Suggested change
#define assert_not_equal_str(str1, str2) assert_true(strcmp((str1), (str2)))
#define assert_equal(a, b) assert_true((a) == (b))
#define assert_greater(a, b) assert_true((a) > (b))
#define assert_greater_equal(a, b) assert_true((a) >= (b))
#define assert_less(a, b) assert_true((a) < (b))
#define assert_less_equal(a, b) assert_true((a) <= (b))
#define assert_not_null(ptr) assert_true((ptr) != NULL)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like them.

  1. Maybe I would put them in MAJ to make it clear this belongs to the UT framework (and not GPAC in general).

  2. Also we could limit to ASSERT() and ASSERT_STR_EQ() I guess. Opinion?

#define assert_equal_str(str1, str2) \
do { \
if (!strcmp(str1, str2)) { \
if (verbose_ut) printf("Assertion passed: Value: (%s, %s), File: %s, Line: %d, Function: %s\n", #str1, #str2, __FILE__, __LINE__, __ASSERT_FUNCTION); \
checks_passed++; \
} else { \
checks_failed++; \
gf_assert(0); \
} \
} while (0)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this be rewritten as follows?

Suggested change
#define assert_equal_str(str1, str2) \
do { \
if (!strcmp(str1, str2)) { \
if (verbose_ut) printf("Assertion passed: Value: (%s, %s), File: %s, Line: %d, Function: %s\n", #str1, #str2, __FILE__, __LINE__, __ASSERT_FUNCTION); \
checks_passed++; \
} else { \
checks_failed++; \
gf_assert(0); \
} \
} while (0)
#define assert_equal_str(str1, str2) assert_false(strcmp(str1, str2))

11 changes: 11 additions & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,17 @@ ifeq ($(shell fgrep "Libs.private:" ../gpac.pc 1>&2 2> /dev/null ; echo $$?),1)
@echo "Libs.private: -lgpac_static $(ALL_LIBS)" >> ../gpac.pc
endif


unit_tests:
ifeq ($(UNIT_TESTS),yes)
$(CC) -I.. -I$(SRC_PATH)/include -DGPAC_HAVE_CONFIG_H \
-o ../bin/gcc/unittests \
../bin/gcc/unittests.c \
$(shell find $(SRC_PATH) -path "*/unittests/*.c" | grep -v bin | sort) \
-Wl,-rpath=$(realpath ../bin/gcc) -L../bin/gcc -lgpac
endif


dep:

DEPS := $(SRCS:%.c=.deps/%.dep)
Expand Down
15 changes: 13 additions & 2 deletions src/utils/os_config_init.c
Original file line number Diff line number Diff line change
Expand Up @@ -2546,10 +2546,21 @@ Bool gf_sys_word_match(const char *orig, const char *dst)
s32 dist = 0;
u32 match = 0;
u32 i;
u32 olen = (u32) strlen(orig);
u32 dlen = (u32) strlen(dst);
u32 olen, dlen;
u32 *run;

if (orig == NULL && dst == NULL) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, (" gf_sys_word_match: NULL arguments \n"));
return GF_TRUE;
}
if (orig == NULL || dst == NULL) {
GF_LOG(GF_LOG_WARNING, GF_LOG_CORE, (" gf_sys_word_match: NULL argument \n"));
return GF_FALSE;
}

olen = (u32) strlen(orig);
dlen = (u32) strlen(dst);

if ((olen>=3) && (olen<dlen) && !strncmp(orig, dst, olen))
return GF_TRUE;
if ((dlen>=3) && (dlen<olen) && !strncmp(orig, dst, dlen))
Expand Down
63 changes: 63 additions & 0 deletions src/utils/unittests/ut_os_config_init.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#include <gpac/main.h>
#include "tests.h"

unittest(gf_sys_word_match)
{
// gf_assert(gf_sys_word_match("toto", "toto") == GF_TRUE); //TODO: provide more assert flavors
// verbose_assert(gf_sys_word_match("toto", "toto") == GF_TRUE);
// custom_message_assert(gf_sys_word_match("toto", "toto") == GF_TRUE, "toto must match");
// custom_action_assert( gf_sys_word_match("toto", "tot") == GF_TRUE , printf("Assertion failed: toto has lost an o \n"));

// Test exact match scenario
assert_true(gf_sys_word_match("abc", "abc"));

// Test short orig, longer dst scenario
assert_true(gf_sys_word_match("abc", "abcd"));

// Test short dst, longer orig scenario
assert_true(gf_sys_word_match("abcd", "abc"));

// Test scenario with ':' in orig but not in dst
assert_false(gf_sys_word_match("a:b:c", "xyz"));

// Test scenario with ':' in dst but not in orig
assert_false(gf_sys_word_match("abc", "x:y:z"));

// Test strnistr match scenario
assert_true(gf_sys_word_match("abc", "xabcz"));

// Test repeated characters scenario
assert_false(gf_sys_word_match("aabbc", "axayabaz"));

// Test match*2 < olen scenario
assert_false(gf_sys_word_match("abc", "xyz"));

// Test half characters in order scenario
assert_true(gf_sys_word_match("abcd", "aebfcd"));

// Test null pointer
assert_true(gf_sys_word_match(NULL, NULL));
assert_false(gf_sys_word_match("abc", NULL));
assert_false(gf_sys_word_match(NULL, "abc"));

// Test empty string
assert_true(gf_sys_word_match("", ""));
assert_false(gf_sys_word_match("abc", ""));
assert_false(gf_sys_word_match("", "abc"));

// Test a very long string
const char *longString = "a very long string that exceeds the normal limit of the function, it may go further and further until it can break the behaviour of the function apparently not easy so lets write more and more";
assert_true(gf_sys_word_match(longString, longString));
assert_false(gf_sys_word_match("abc", longString));
assert_false(gf_sys_word_match(longString, "abc"));

// Test a non-ASCII buffer
const char *nonAsciiBuffer = "\x01\x02\x03\xFF\xFE\xFD";
assert_false(gf_sys_word_match("abc", nonAsciiBuffer));
assert_false(gf_sys_word_match(nonAsciiBuffer, "abc"));
assert_true(gf_sys_word_match(nonAsciiBuffer, nonAsciiBuffer));

// Test non-string: will crash because this exported function doesn't provide a str max len argument
char str[3] = {'a', 'b', 'c'};
//DISABLED: gf_sys_word_match("abc", str);
}
7 changes: 7 additions & 0 deletions src/utils/unittests/ut_xml_parser.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "tests.h"

char *xml_translate_xml_string(char *str);
unittest(xml_translate_xml_string)
{
assert_equal_str(xml_translate_xml_string("&amp;"), "&");
}
2 changes: 1 addition & 1 deletion src/utils/xml_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@

static GF_Err gf_xml_sax_parse_intern(GF_SAXParser *parser, char *current);

static char *xml_translate_xml_string(char *str)
GF_STATIC char *xml_translate_xml_string(char *str)
{
char *value;
u32 size, i, j;
Expand Down