Test for overflow limits in gnc_numeric_add.

For analysis of Bug 665707.

git-svn-id: svn+ssh://svn.gnucash.org/repo/gnucash/trunk@23494 57a11ea4-9604-0410-9ed3-97b8803252fd
This commit is contained in:
John Ralls 2013-12-05 21:54:28 +00:00
parent de3a6e3df2
commit e4fc44dd1c
3 changed files with 170 additions and 0 deletions

View File

@ -546,8 +546,113 @@ check_add_subtract (void)
}
}
extern gint64 pwr64 (gint64 op, int exp);
static void
check_add_subtract_overflow (void)
{
int i;
for (i = 0; i < NREPS; i++)
{
/* Div to avoid addition overflows; we're looking for lcd conversion overflows here. */
int exp_a = rand () % 1000;
int exp_b = rand () % 1000;
gint64 bin_deno_a = (exp_a == 0 ? 1 : exp_a);
gint64 bin_deno_b = (exp_b == 0 ? 1 : exp_b);
/*
int exp_a = rand () % 11;
int exp_b = rand () % 11;
gint64 bin_deno_a = (1 << exp_a);
gint64 bin_deno_b = (1 << exp_a);
*/
gint64 dec_deno_a = pwr64 (10, exp_a % 7);
gint64 dec_deno_b = pwr64 (10, exp_b % 7);
gint64 na = get_random_gint64 () % (1000000 * dec_deno_a);
gint64 nb = get_random_gint64 () % (1000000 * dec_deno_b);
gnc_numeric result;
GNCNumericErrorCode err;
gchar *errmsg;
gnc_numeric ba = gnc_numeric_create(na, bin_deno_a);
gnc_numeric bb = gnc_numeric_create(nb, bin_deno_b);
gnc_numeric da = gnc_numeric_create(na, dec_deno_a);
gnc_numeric db = gnc_numeric_create(nb, dec_deno_b);
gchar *ba_str = gnc_numeric_to_string (ba);
gchar *bb_str = gnc_numeric_to_string (bb);
gchar *da_str = gnc_numeric_to_string (da);
gchar *db_str = gnc_numeric_to_string (db);
/* Add */
result = gnc_numeric_add(ba, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
err = gnc_numeric_check (result);
errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, bb_str,
gnc_numeric_errorCode_to_string (err));
do_test (err == 0, errmsg);
g_free (errmsg);
result = gnc_numeric_add(da, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
err = gnc_numeric_check (result);
errmsg = g_strdup_printf ("%s + %s raised %s", da_str, bb_str,
gnc_numeric_errorCode_to_string (err));
do_test (err == 0, errmsg);
g_free (errmsg);
result = gnc_numeric_add(ba, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
err = gnc_numeric_check (result);
errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, db_str,
gnc_numeric_errorCode_to_string (err));
do_test (err == 0, errmsg);
g_free (errmsg);
result = gnc_numeric_add(da, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
err = gnc_numeric_check (result);
errmsg = g_strdup_printf ("%s + %s raised %s", da_str, db_str,
gnc_numeric_errorCode_to_string (err));
do_test (err == 0, errmsg);
g_free (errmsg);
/* Subtract */
result = gnc_numeric_sub(ba, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
err = gnc_numeric_check (result);
errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, bb_str,
gnc_numeric_errorCode_to_string (err));
do_test (err == 0, errmsg);
g_free (errmsg);
result = gnc_numeric_sub(da, bb, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
err = gnc_numeric_check (result);
errmsg = g_strdup_printf ("%s + %s raised %s", da_str, bb_str,
gnc_numeric_errorCode_to_string (err));
do_test (err == 0, errmsg);
g_free (errmsg);
result = gnc_numeric_sub(ba, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
err = gnc_numeric_check (result);
errmsg = g_strdup_printf ("%s + %s raised %s", ba_str, db_str,
gnc_numeric_errorCode_to_string (err));
do_test (err == 0, errmsg);
g_free (errmsg);
result = gnc_numeric_sub(da, db, GNC_DENOM_AUTO, GNC_HOW_DENOM_EXACT);
err = gnc_numeric_check (result);
errmsg = g_strdup_printf ("%s + %s raised %s", da_str, db_str,
gnc_numeric_errorCode_to_string (err));
do_test (err == 0, errmsg);
g_free (errmsg);
g_free (ba_str);
g_free (bb_str);
g_free (da_str);
g_free (db_str);
}
}
/* ======================================================= */
static void
check_mult_div (void)
{
@ -871,6 +976,7 @@ run_test (void)
check_double();
check_neg();
check_add_subtract();
check_add_subtract_overflow ();
check_mult_div ();
check_reciprocal();
}

View File

@ -45,6 +45,23 @@
/* static short module = MOD_ENGINE; */
gint64
pwr64 (gint64 op, int exp)
{
qofint128 tmp;
if (exp == 0) return 1;
if (exp % 2)
{
tmp = mult128 (op, pwr64 (op, exp - 1));
if (tmp.isbig) return 0;
return tmp.lo;
}
tmp.lo = pwr64 (op, exp / 2);
tmp = mult128 (tmp.lo, tmp.lo);
if (tmp.isbig) return 0;
return tmp.lo;
}
/* =============================================================== */
/* This function is small, simple, and used everywhere below,
* lets try to inline it.

View File

@ -0,0 +1,47 @@
/********************************************************************
* test_qofbackend.c: GLib g_test test suite for qofbackend. *
* Copyright 2011 John Ralls <jralls@ceridwen.us> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
* published by the Free Software Foundation; either version 2 of *
* the License, or (at your option) any later version. *
* *
* This program 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 General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License*
* along with this program; if not, contact: *
* *
* Free Software Foundation Voice: +1-617-542-5942 *
* 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652 *
* Boston, MA 02110-1301, USA gnu@gnu.org *
\********************************************************************/
#include "config.h"
#include <string.h>
#include <glib.h>
#include <qof.h>
#include <unittest-support.h>
static const gchar *suitename = "/qof/gnc_numeric";
static void
test_gnc_numeric_add (void)
{
gnc_numeric a = { 123456789987654321, 1000000000 };
gnc_numeric b = { 65432198765432198, 100000000 };
gnc_numeric goal_ab = { 777778777641976301, 1000000000 };
gnc_numeric result;
result = gnc_numeric_add (a, b, GNC_DENOM_AUTO,
GNC_HOW_DENOM_EXACT | GNC_HOW_RND_NEVER);
g_assert (gnc_numeric_equal (result, goal_ab));
}
void
test_suite_gnc_numeric ( void )
{
GNC_TEST_ADD_FUNC( suitename, "gnc-numeric add", test_gnc_numeric_add );
}