diff --git a/.gitignore b/.gitignore index a1bbe1eab8..7de6e416f1 100644 --- a/.gitignore +++ b/.gitignore @@ -1,243 +1,244 @@ # Common \#* .\#* GPATH GRTAGS GTAGS TAGS Makefile Makefile.in .deps .dirstamp .libs *.pc *.pyc *.bz2 *.tar.gz *.tgz *.la *.lo *.o *~ *.gcda *.gcno # Autobuild aclocal.m4 autoconf autoheader autom4te.cache/ automake build.counter compile /confdefs.h config.guess config.log config.status config.sub configure /conftest* depcomp install-sh include/stamp-* libtool libtool.m4 ltdl.m4 libltdl ltmain.sh missing py-compile /m4/argz.m4 /m4/ltargz.m4 /m4/ltoptions.m4 /m4/ltsugar.m4 /m4/ltversion.m4 /m4/lt~obsolete.m4 test-driver ylwrap # Configure targets /cts/CTS.py /cts/CTSlab.py /cts/CTSvars.py /cts/LSBDummy /cts/OCFIPraTest.py /cts/benchmark/clubench /cts/cluster_test /cts/cts /cts/cts-cli /cts/cts-coverage /cts/cts-exec /cts/cts-fencing /cts/cts-log-watcher /cts/cts-regression /cts/cts-scheduler /cts/cts-support /cts/fence_dummy /cts/lxc_autogen.sh /cts/pacemaker-cts-dummyd /cts/pacemaker-cts-dummyd@.service /daemons/execd/pacemaker_remote /daemons/execd/pacemaker_remote.service /daemons/fenced/fence_legacy /daemons/pacemakerd/pacemaker /daemons/pacemakerd/pacemaker.combined.upstart /daemons/pacemakerd/pacemaker.service /daemons/pacemakerd/pacemaker.upstart /doc/Doxyfile /extra/logrotate/pacemaker /extra/resources/ClusterMon /extra/resources/HealthSMART /extra/resources/SysInfo /extra/resources/ifspeed /extra/resources/o2cb include/config.h include/config.h.in include/crm_config.h publican.cfg /tools/cibsecret /tools/crm_error /tools/crm_failcount /tools/crm_master /tools/crm_mon.service /tools/crm_mon.upstart /tools/crm_report /tools/crm_rule /tools/crm_standby /tools/pcmk_simtimes /tools/report.collector /tools/report.common # Build targets *.7 *.7.xml *.7.html *.8 *.8.xml *.8.html /daemons/attrd/pacemaker-attrd /daemons/based/pacemaker-based /daemons/based/cibmon /daemons/controld/pacemaker-controld /daemons/execd/cts-exec-helper /daemons/execd/pacemaker-execd /daemons/execd/pacemaker-remoted /daemons/fenced/cts-fence-helper /daemons/fenced/pacemaker-fenced /daemons/fenced/pacemaker-fenced.xml /daemons/pacemakerd/pacemakerd /daemons/schedulerd/pacemaker-schedulerd /daemons/schedulerd/pacemaker-schedulerd.xml /doc/*/tmp/** /doc/*/publish /doc/*.build /doc/*/en-US/Ap-*.xml /doc/*/en-US/Ch-*.xml /doc/.ABI-build /doc/HTML /doc/abi_dumps /doc/abi-check /doc/api/* /doc/compat_reports /doc/crm_fencing.html /doc/publican-catalog* /doc/shared/en-US/*.xml /doc/shared/en-US/images/pcmk-*.png /doc/shared/en-US/images/Policy-Engine-*.png /doc/sphinx/*/_build /doc/sphinx/*/conf.py /lib/common/md5.c /maint/testcc_helper.cc /maint/testcc_*_h /maint/mocked/based scratch /tools/attrd_updater /tools/cibadmin /tools/crmadmin /tools/crm_attribute /tools/crm_diff /tools/crm_mon /tools/crm_node /tools/crm_resource /tools/crm_shadow /tools/crm_simulate /tools/crm_ticket /tools/crm_verify /tools/iso8601 /tools/stonith_admin xml/crm.dtd xml/pacemaker*.rng xml/versions.rng xml/api/api-result*.rng lib/gnu/libgnu.a lib/gnu/stdalign.h *.coverity # Packager artifacts *.rpm /mock /pacemaker.spec /rpm/[A-Z]* # make dist/export working directory pacemaker-[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9] # Test detritus /cts/.regression.failed.diff /cts/scheduler/*.ref /cts/scheduler/*.up /cts/scheduler/*.up.err /cts/scheduler/bug-rh-1097457.log /cts/scheduler/bug-rh-1097457.trs /cts/scheduler/shadow.* /cts/test-suite.log /xml/test-*/*.up /xml/test-*/*.up.err /xml/assets/*.rng /xml/assets/diffview.js /xml/assets/xmlcatalog # Test results *.log *.trs +/lib/common/tests/strings/pcmk__scan_double /lib/common/tests/strings/pcmk__parse_ll_range /lib/common/tests/strings/pcmk__str_any_of /lib/common/tests/strings/pcmk__strcmp /lib/common/tests/utils/pcmk_str_is_infinity /lib/common/tests/utils/pcmk_str_is_minus_infinity /lib/pengine/tests/rules/pe_cron_range_satisfied # Release maintenance detritus /maint/gnulib # Formerly built files (helps when jumping back and forth in checkout) /.ABI-build /Doxyfile /HTML /abi_dumps /abi-check /compat_reports /attrd /cib /coverage.sh /crmd /cts/HBDummy /doc/Clusters_from_Scratch.txt /doc/Pacemaker_Explained.txt /doc/acls.html /fencing /lrmd /mcp /pacemaker-*.spec /pengine #Other coverity-* logs *.patch *.diff *.sed *.orig *.rej *.swp diff --git a/lib/common/tests/strings/Makefile.am b/lib/common/tests/strings/Makefile.am index 11c3bd3c00..5c5e29181e 100644 --- a/lib/common/tests/strings/Makefile.am +++ b/lib/common/tests/strings/Makefile.am @@ -1,21 +1,22 @@ AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_builddir)/include LDADD = $(top_builddir)/lib/common/libcrmcommon.la include $(top_srcdir)/mk/glib-tap.mk # Add each test program here. Each test should be written as a little standalone # program using the glib unit testing functions. See the documentation for more # information. # # https://developer.gnome.org/glib/unstable/glib-Testing.html -test_programs = pcmk__parse_ll_range \ +test_programs = pcmk__scan_double \ + pcmk__parse_ll_range \ pcmk__str_any_of \ pcmk__strcmp # If any extra data needs to be added to the source distribution, add it to the # following list. dist_test_data = # If any extra data needs to be used by tests but should not be added to the # source distribution, add it to the following list. test_data = diff --git a/lib/common/tests/strings/pcmk__scan_double.c b/lib/common/tests/strings/pcmk__scan_double.c new file mode 100644 index 0000000000..1e6138a2e6 --- /dev/null +++ b/lib/common/tests/strings/pcmk__scan_double.c @@ -0,0 +1,161 @@ +#include // DBL_MAX, etc. +#include // fabs() + +#include + +#include + +// Ensure plenty of characters for %f display +#define LOCAL_BUF_SIZE 2 * DBL_MAX_10_EXP + +/* + * Avoids compiler warnings for floating-point equality checks. + * Use for comparing numbers (e.g., 1.0 == 1.0), not expression values. + */ +#define ASSERT_DBL_EQ(d1, d2) g_assert_cmpfloat(fabs(d1 - d2), \ + <, DBL_EPSILON); + +static void +empty_input_string(void) +{ + double result; + + // Without default_text + g_assert_cmpint(pcmk__scan_double(NULL, &result, NULL, NULL), ==, EINVAL); + ASSERT_DBL_EQ(result, PCMK__PARSE_DBL_DEFAULT); + + g_assert_cmpint(pcmk__scan_double("", &result, NULL, NULL), ==, EINVAL); + ASSERT_DBL_EQ(result, PCMK__PARSE_DBL_DEFAULT); + + // With default_text + g_assert_cmpint(pcmk__scan_double(NULL, &result, "2.0", NULL), ==, + pcmk_rc_ok); + ASSERT_DBL_EQ(result, 2.0); + + g_assert_cmpint(pcmk__scan_double("", &result, "2.0", NULL), ==, EINVAL); + ASSERT_DBL_EQ(result, PCMK__PARSE_DBL_DEFAULT); +} + +static void +bad_input_string(void) +{ + double result; + + // Without default text + g_assert_cmpint(pcmk__scan_double("asdf", &result, NULL, NULL), ==, EINVAL); + ASSERT_DBL_EQ(result, PCMK__PARSE_DBL_DEFAULT); + + g_assert_cmpint(pcmk__scan_double("as2.0", &result, NULL, NULL), ==, + EINVAL); + ASSERT_DBL_EQ(result, PCMK__PARSE_DBL_DEFAULT); + + // With default text (not used) + g_assert_cmpint(pcmk__scan_double("asdf", &result, "2.0", NULL), ==, + EINVAL); + ASSERT_DBL_EQ(result, PCMK__PARSE_DBL_DEFAULT); + + g_assert_cmpint(pcmk__scan_double("as2.0", &result, "2.0", NULL), ==, + EINVAL); + ASSERT_DBL_EQ(result, PCMK__PARSE_DBL_DEFAULT); +} + +static void +trailing_chars(void) +{ + double result; + + g_assert_cmpint(pcmk__scan_double("2.0asdf", &result, NULL, NULL), ==, + pcmk_rc_ok); + ASSERT_DBL_EQ(result, 2.0); +} + +static void +typical_case(void) +{ + char str[LOCAL_BUF_SIZE]; + double result; + + g_assert_cmpint(pcmk__scan_double("0.0", &result, NULL, NULL), ==, + pcmk_rc_ok); + ASSERT_DBL_EQ(result, 0.0); + + g_assert_cmpint(pcmk__scan_double("1.0", &result, NULL, NULL), ==, + pcmk_rc_ok); + ASSERT_DBL_EQ(result, 1.0); + + g_assert_cmpint(pcmk__scan_double("-1.0", &result, NULL, NULL), ==, + pcmk_rc_ok); + ASSERT_DBL_EQ(result, -1.0); + + snprintf(str, LOCAL_BUF_SIZE, "%f", DBL_MAX); + g_assert_cmpint(pcmk__scan_double(str, &result, NULL, NULL), ==, + pcmk_rc_ok); + ASSERT_DBL_EQ(result, DBL_MAX); + + snprintf(str, LOCAL_BUF_SIZE, "%f", -DBL_MAX); + g_assert_cmpint(pcmk__scan_double(str, &result, NULL, NULL), ==, + pcmk_rc_ok); + ASSERT_DBL_EQ(result, -DBL_MAX); +} + +static void +double_overflow(void) +{ + char str[LOCAL_BUF_SIZE]; + double result; + + /* + * 1e(DBL_MAX_10_EXP + 1) produces an inf value + * Can't use ASSERT_DBL_EQ() because (inf - inf) == NaN + */ + snprintf(str, LOCAL_BUF_SIZE, "1e%d", DBL_MAX_10_EXP + 1); + g_assert_cmpint(pcmk__scan_double(str, &result, NULL, NULL), ==, EOVERFLOW); + g_assert_cmpfloat(result, >, DBL_MAX); + + snprintf(str, LOCAL_BUF_SIZE, "-1e%d", DBL_MAX_10_EXP + 1); + g_assert_cmpint(pcmk__scan_double(str, &result, NULL, NULL), ==, EOVERFLOW); + g_assert_cmpfloat(result, <, -DBL_MAX); +} + +static void +double_underflow(void) +{ + char str[LOCAL_BUF_SIZE]; + double result; + + /* + * 1e(DBL_MIN_10_EXP - 1) produces a denormalized value (between 0 + * and DBL_MIN) + * + * C99/C11: result will be **no greater than** DBL_MIN + */ + snprintf(str, LOCAL_BUF_SIZE, "1e%d", DBL_MIN_10_EXP - 1); + g_assert_cmpint(pcmk__scan_double(str, &result, NULL, NULL), ==, + pcmk_rc_underflow); + g_assert_cmpfloat(result, >=, 0.0); + g_assert_cmpfloat(result, <=, DBL_MIN); + + snprintf(str, LOCAL_BUF_SIZE, "-1e%d", DBL_MIN_10_EXP - 1); + g_assert_cmpint(pcmk__scan_double(str, &result, NULL, NULL), ==, + pcmk_rc_underflow); + g_assert_cmpfloat(result, <=, 0.0); + g_assert_cmpfloat(result, >=, -DBL_MIN); +} + +int main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + // Test for input string issues + g_test_add_func("/common/strings/double/empty_input", empty_input_string); + g_test_add_func("/common/strings/double/bad_input", bad_input_string); + g_test_add_func("/common/strings/double/trailing_chars", trailing_chars); + + // Test for numeric issues + g_test_add_func("/common/strings/double/typical", typical_case); + g_test_add_func("/common/strings/double/overflow", double_overflow); + g_test_add_func("/common/strings/double/underflow", double_underflow); + + return g_test_run(); +} +