From 771c857cf5cde5823e6ef5f43ad3ab07f8f71192 Mon Sep 17 00:00:00 2001 From: Scott Poore Date: Thu, 28 Sep 2023 11:03:19 -0500 Subject: [PATCH] Update gating to tmt, add segfault test Moving gating to use tmt format for finding/running tests. Also adding test to cover segfault bug. Adding perf test script from Thomas Halman for jqspeed.sh. Dropping log collection in favor of printing on failure for upstream tests. Related: RHEL-12896 Related: RHEL-13431 --- .fmf/version | 1 + plans/all.fmf | 23 +++++ tests/jqtests.sh | 30 ------- tests/performance/jqspeed.sh | 20 +++++ tests/performance/main.fmf | 6 ++ tests/pkgs-yum4.yml | 0 tests/provision.fmf | 5 ++ tests/segfault/jqsegfault.sh | 19 ++++ tests/segfault/main.fmf | 8 ++ .../test_segfault_with_multithreaded_env.c | 90 +++++++++++++++++++ tests/tests.yml | 14 ++- tests/upstream/jqtests.sh | 48 ++++++++++ tests/upstream/main.fmf | 11 +++ 13 files changed, 242 insertions(+), 33 deletions(-) create mode 100644 .fmf/version create mode 100644 plans/all.fmf delete mode 100755 tests/jqtests.sh create mode 100755 tests/performance/jqspeed.sh create mode 100644 tests/performance/main.fmf create mode 100644 tests/pkgs-yum4.yml create mode 100644 tests/provision.fmf create mode 100755 tests/segfault/jqsegfault.sh create mode 100644 tests/segfault/main.fmf create mode 100644 tests/segfault/test_segfault_with_multithreaded_env.c create mode 100755 tests/upstream/jqtests.sh create mode 100644 tests/upstream/main.fmf diff --git a/.fmf/version b/.fmf/version new file mode 100644 index 0000000..d00491f --- /dev/null +++ b/.fmf/version @@ -0,0 +1 @@ +1 diff --git a/plans/all.fmf b/plans/all.fmf new file mode 100644 index 0000000..228c87e --- /dev/null +++ b/plans/all.fmf @@ -0,0 +1,23 @@ +summary: Run gating tests +discover: + how: fmf + dist-git-source: true + dist-git-type: centos +prepare: + - how: install + package: + - dnf-plugins-core + - https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm + - how: shell + script: /usr/bin/crb enable + - how: install + package: + - jq + - jq-devel + - gcc + - wget + - how: shell + script: dnf config-manager --disable epel* + +execute: + how: tmt diff --git a/tests/jqtests.sh b/tests/jqtests.sh deleted file mode 100755 index 525e19d..0000000 --- a/tests/jqtests.sh +++ /dev/null @@ -1,30 +0,0 @@ -#!/bin/sh - -die () { - echo "$1" >&2 - exit 1 -} - -# link jq binary. This make the tests to use -# installed binary instead of compiled one -cd source || die "missing source directory" -rm -f jq tests/*.log 2>/dev/null -ln -s /usr/bin/jq || die "failed to link jq binary" - -# run the tests -# List of tests is taken from Makefile -TESTS="tests/optionaltest tests/mantest tests/jqtest tests/onigtest tests/shtest tests/utf8test tests/base64test" - -for t in $TESTS; do - echo -n "Test $t ... " - ./${t} >"${t}.log" 2>&1 - RET=$? - if [ $RET = 0 ]; then - echo "ok" - else - echo "failed" - cat "${t}.log" - die "Test ${t} failed" - fi -done -exit 0 diff --git a/tests/performance/jqspeed.sh b/tests/performance/jqspeed.sh new file mode 100755 index 0000000..9840c2d --- /dev/null +++ b/tests/performance/jqspeed.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +export LC_ALL=C + +stress_jq() { + `which time` -p -f '%e\n' bash -c 'for i in `seq 1 1000` ; do echo '"'"'{"foo":"bar"}'"'"' | jq '"'"'has("bar")'"'"' > /dev/null ; done' 2>&1 | cut -d. -f1 +} + +FAIL=0 +TIME=`stress_jq` +echo -n "Test jqspeed ... " +if [ $TIME -gt 8 ] ; then + echo "failed" + FAIL=1 +else + echo "ok" +fi + +echo "Runtime: ${TIME}s " +exit $FAIL diff --git a/tests/performance/main.fmf b/tests/performance/main.fmf new file mode 100644 index 0000000..b1a8e27 --- /dev/null +++ b/tests/performance/main.fmf @@ -0,0 +1,6 @@ +summary: Run jq performance/speed test +require: + - jq + - which + - time +test: ./jqspeed.sh diff --git a/tests/pkgs-yum4.yml b/tests/pkgs-yum4.yml new file mode 100644 index 0000000..e69de29 diff --git a/tests/provision.fmf b/tests/provision.fmf new file mode 100644 index 0000000..62a6eba --- /dev/null +++ b/tests/provision.fmf @@ -0,0 +1,5 @@ +--- + +standard-inventory-qcow2: + qemu: + m: 2G diff --git a/tests/segfault/jqsegfault.sh b/tests/segfault/jqsegfault.sh new file mode 100755 index 0000000..96369c9 --- /dev/null +++ b/tests/segfault/jqsegfault.sh @@ -0,0 +1,19 @@ +#!/bin/sh + +FAIL=0 + +# Test for segfault in multithreaded environment +gcc -o test_segfault test_segfault_with_multithreaded_env.c -lpthread -ljq && \ +./test_segfault +RET=$? + +echo -n "Test jqsegfault ... " + +if [ $RET != 0 ]; then + echo "failed" + FAIL=1 +else + echo "ok" +fi + +exit $FAIL diff --git a/tests/segfault/main.fmf b/tests/segfault/main.fmf new file mode 100644 index 0000000..9b2850d --- /dev/null +++ b/tests/segfault/main.fmf @@ -0,0 +1,8 @@ +summary: Run segfault threads test +require: + - wget + - dnf-utils + - gcc + - jq + - jq-devel +test: ./jqsegfault.sh diff --git a/tests/segfault/test_segfault_with_multithreaded_env.c b/tests/segfault/test_segfault_with_multithreaded_env.c new file mode 100644 index 0000000..c8e2622 --- /dev/null +++ b/tests/segfault/test_segfault_with_multithreaded_env.c @@ -0,0 +1,90 @@ +#include +#include +#include +#include +#include +#include + +int my_jq_parse(jq_state *jq, struct jv_parser *parser) +{ + int rv = 0; + jv value; + + value = jv_parser_next(parser); + while (jv_is_valid(value)) { + jq_start(jq, value, 0); + jv result; + + while (jv_is_valid(result = jq_next(jq))) { + jv dumped = jv_dump_string(result, 0); + const char *str = jv_string_value(dumped); + printf("dumped: %s\n", str); + } + + jv_free(result); + value = jv_parser_next(parser); + } + + if (jv_invalid_has_msg(jv_copy(value))) { + jv msg = jv_invalid_get_msg(value); + printf("invalid: %s\n", jv_string_value(msg)); + jv_free(msg); + rv = 1; + } else { + jv_free(value); + } + + return rv; +} + +void *run(void *not_used) { + int rv; + jq_state *jq; + const char *prg = ".data"; + + jq = jq_init(); + printf("jq_init jq: %p prg: %s\n", jq, prg); + if (jq_compile(jq, prg) == 0) { + jq_teardown(&jq); + return NULL; + } + printf("compiled\n"); + + struct jv_parser *parser = jv_parser_new(0); + const char *buf = "{ \"data\": 1 }"; + jv_parser_set_buf(parser, buf, strlen(buf), 0); + rv = my_jq_parse(jq, parser); + if (rv != 0) { + printf("my_jq_parse failed!\n"); + } + + jv_parser_free(parser); + jq_teardown(&jq); + return NULL; +} + +#define THREADS 2 +/* calling run() twice works fine */ +/* calling them in threads causes core-dump */ +int main (int argc, char *argv[]) +{ + pthread_t threads[THREADS]; + int createerror; + int a; + + memset(&threads, 0, sizeof(threads)); + for (a = 0; a < THREADS; ++a) { + // sleep(1); // < if you want to run threads sequentionally + createerror = pthread_create(&threads[a], NULL, run, NULL); + if (createerror) { + printf("create thread error %d\n", a); + } + } + for(a = 0; a < THREADS; ++a) { + if (threads[a] != 0) { + pthread_join(threads[a], NULL); + } + } + + return 0; +} diff --git a/tests/tests.yml b/tests/tests.yml index efb1b65..f2f5ccc 100644 --- a/tests/tests.yml +++ b/tests/tests.yml @@ -10,10 +10,18 @@ - classic required_packages: - jq + - gcc + - wget + - jq-devel - valgrind - rubygem-rake tests: - - jqtests: - dir: . + - upstream: + dir: upstream run: ./jqtests.sh - save-files: ["source/tests/*.log"] + - segfault: + dir: segfault + run: ./jqsegfault.sh + - performance: + dir: performance + run: ./jqspeed.sh diff --git a/tests/upstream/jqtests.sh b/tests/upstream/jqtests.sh new file mode 100755 index 0000000..3c859f5 --- /dev/null +++ b/tests/upstream/jqtests.sh @@ -0,0 +1,48 @@ +#!/bin/sh + +die () { + echo "$1" >&2 + exit 1 +} + +# If source not found, download it with dnf +if [ ! -d ./source ]; then + # Extract source from srpm + dnf download --source jq && \ + rpm2cpio jq*.rpm|cpio -id && \ + mkdir source && \ + tar -zxf jq-*.tar.gz -C source --strip-components=1 + if [ $? != 0 ]; then + echo "Failed to download upstream tests" + exit 1 + fi +fi + +pushd ./source || die "missing source directory" +rm -f jq tests/*.log 2>/dev/null +ln -s /usr/bin/jq || die "failed to link jq binary" + +FAIL=0 + +# run the tests +# List of tests is taken from Makefile +TESTS="tests/optionaltest tests/mantest tests/jqtest tests/onigtest tests/shtest tests/utf8test tests/base64test" + +for t in $TESTS; do + echo -n "Test $t ... " + ./${t} >"${t}.log" 2>&1 + RET=$? + if [ $RET = 0 ]; then + echo "ok" + else + echo "failed" + echo "-------------------- ${t}.log start -----------------------------" + cat "${t}.log" + echo "-------------------- ${t}.log end -----------------------------" + FAIL=1 + fi +done + +popd # exit SOURCE_DIR + +exit $FAIL diff --git a/tests/upstream/main.fmf b/tests/upstream/main.fmf new file mode 100644 index 0000000..bed862c --- /dev/null +++ b/tests/upstream/main.fmf @@ -0,0 +1,11 @@ +summary: Run jq gating tests +require: + - wget + - dnf-utils + - gcc + - jq + - jq-devel + - valgrind + - diffutils +test: ./jqtests.sh +duration: 30m