Convert src/tools/testint128.c into a test module.

This creates a new test module src/test/modules/test_int128 and moves
src/tools/testint128.c into it so that it can be built using the
normal build system, allowing the 128-bit integer arithmetic functions
in src/include/common/int128.h to be tested automatically. For now,
the tests are skipped on platforms that don't have native int128
support.

While at it, fix the test128 union in the test code: the "hl" member
of test128 was incorrectly defined to be a union instead of a struct,
which meant that the tests were only ever setting and checking half of
each 128-bit integer value.

Author: Dean Rasheed <dean.a.rasheed@gmail.com>
Reviewed-by: John Naylor <johncnaylorls@gmail.com>
Discussion: https://postgr.es/m/CAEZATCWgBMc9ZwKMYqQpaQz2X6gaamYRB+RnMsUNcdMcL2Mj_w@mail.gmail.com
This commit is contained in:
Dean Rasheed 2025-08-06 09:41:11 +01:00
parent 225ebfe30a
commit 8c7445a008
8 changed files with 125 additions and 18 deletions

View File

@ -6,7 +6,7 @@
* We make use of the native int128 type if there is one, otherwise
* implement things the hard way based on two int64 halves.
*
* See src/tools/testint128.c for a simple test harness for this file.
* See src/test/modules/test_int128 for a simple test harness for this file.
*
* Copyright (c) 2017-2025, PostgreSQL Global Development Group
*

View File

@ -25,6 +25,7 @@ SUBDIRS = \
test_escape \
test_extensions \
test_ginpostinglist \
test_int128 \
test_integerset \
test_json_parser \
test_lfind \

View File

@ -24,6 +24,7 @@ subdir('test_dsm_registry')
subdir('test_escape')
subdir('test_extensions')
subdir('test_ginpostinglist')
subdir('test_int128')
subdir('test_integerset')
subdir('test_json_parser')
subdir('test_lfind')

View File

@ -0,0 +1,2 @@
/tmp_check/
/test_int128

View File

@ -0,0 +1,23 @@
# src/test/modules/test_int128/Makefile
PGFILEDESC = "test_int128 - test 128-bit integer arithmetic"
PROGRAM = test_int128
OBJS = $(WIN32RES) test_int128.o
PG_CPPFLAGS = -I$(libpq_srcdir)
PG_LIBS_INTERNAL += $(libpq_pgport)
NO_INSTALL = 1
TAP_TESTS = 1
ifdef USE_PGXS
PG_CONFIG = pg_config
PGXS := $(shell $(PG_CONFIG) --pgxs)
include $(PGXS)
else
subdir = src/test/modules/test_int128
top_builddir = ../../../..
include $(top_builddir)/src/Makefile.global
include $(top_srcdir)/contrib/contrib-global.mk
endif

View File

@ -0,0 +1,33 @@
# Copyright (c) 2025, PostgreSQL Global Development Group
test_int128_sources = files(
'test_int128.c',
)
if host_system == 'windows'
test_int128_sources += rc_bin_gen.process(win32ver_rc, extra_args: [
'--NAME', 'test_int128',
'--FILEDESC', 'test int128 program',])
endif
test_int128 = executable('test_int128',
test_int128_sources,
dependencies: [frontend_code, libpq],
kwargs: default_bin_args + {
'install': false,
},
)
testprep_targets += test_int128
tests += {
'name': 'test_int128',
'sd': meson.current_source_dir(),
'bd': meson.current_build_dir(),
'tap': {
'tests': [
't/001_test_int128.pl',
],
'deps': [test_int128],
},
}

View File

@ -0,0 +1,27 @@
# Copyright (c) 2025, PostgreSQL Global Development Group
# Test 128-bit integer arithmetic code in int128.h
use strict;
use warnings FATAL => 'all';
use PostgreSQL::Test::Utils;
use Test::More;
# Run the test program with 1M iterations
my $exe = "test_int128";
my $size = 1_000_000;
note "testing executable $exe";
my ($stdout, $stderr) = run_command([ $exe, $size ]);
SKIP:
{
skip "no native int128 type", 2 if $stdout =~ /skipping tests/;
is($stdout, "", "test_int128: no stdout");
is($stderr, "", "test_int128: no stderr");
}
done_testing();

View File

@ -1,6 +1,6 @@
/*-------------------------------------------------------------------------
*
* testint128.c
* test_int128.c
* Testbed for roll-our-own 128-bit integer arithmetic.
*
* This is a standalone test program that compares the behavior of an
@ -10,13 +10,18 @@
*
*
* IDENTIFICATION
* src/tools/testint128.c
* src/test/modules/test_int128/test_int128.c
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include <time.h>
/* Require a native int128 type */
#ifdef HAVE_INT128
/*
* By default, we test the non-native implementation in int128.h; but
* by predefining USE_NATIVE_INT128 to 1, you can test the native
@ -36,7 +41,7 @@ typedef union
{
int128 i128;
INT128 I128;
union
struct
{
#ifdef WORDS_BIGENDIAN
int64 hi;
@ -48,6 +53,7 @@ typedef union
} hl;
} test128;
#define INT128_HEX_FORMAT "%016" PRIx64 "%016" PRIx64
/*
* Control version of comparator.
@ -75,7 +81,7 @@ main(int argc, char **argv)
{
long count;
pg_prng_seed(&pg_global_prng_state, 0);
pg_prng_seed(&pg_global_prng_state, (uint64) time(NULL));
if (argc >= 2)
count = strtol(argv[1], NULL, 0);
@ -99,9 +105,9 @@ main(int argc, char **argv)
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf("%016lX%016lX + unsigned %lX\n", x, y, z);
printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
printf(INT128_HEX_FORMAT " + unsigned " PRIx64 "\n", x, y, z);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
@ -114,9 +120,9 @@ main(int argc, char **argv)
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf("%016lX%016lX + signed %lX\n", x, y, z);
printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
printf(INT128_HEX_FORMAT " + signed " PRIx64 "\n", x, y, z);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
@ -128,9 +134,9 @@ main(int argc, char **argv)
if (t1.hl.hi != t2.hl.hi || t1.hl.lo != t2.hl.lo)
{
printf("%lX * %lX\n", x, y);
printf("native = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
printf("result = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
printf(PRIx64 " * " PRIx64 "\n", x, y);
printf("native = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("result = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
@ -146,8 +152,8 @@ main(int argc, char **argv)
printf("comparison failure: %d vs %d\n",
my_int128_compare(t1.i128, t2.i128),
int128_compare(t1.I128, t2.I128));
printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
printf("arg1 = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("arg2 = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
@ -160,11 +166,25 @@ main(int argc, char **argv)
printf("comparison failure: %d vs %d\n",
my_int128_compare(t1.i128, t2.i128),
int128_compare(t1.I128, t2.I128));
printf("arg1 = %016lX%016lX\n", t1.hl.hi, t1.hl.lo);
printf("arg2 = %016lX%016lX\n", t2.hl.hi, t2.hl.lo);
printf("arg1 = " INT128_HEX_FORMAT "\n", t1.hl.hi, t1.hl.lo);
printf("arg2 = " INT128_HEX_FORMAT "\n", t2.hl.hi, t2.hl.lo);
return 1;
}
}
return 0;
}
#else /* ! HAVE_INT128 */
/*
* For now, do nothing if we don't have a native int128 type.
*/
int
main(int argc, char **argv)
{
printf("skipping tests: no native int128 type\n");
return 0;
}
#endif