public inbox for [email protected]
help / color / mirror / Atom feedFrom: Greg Burd <[email protected]>
To: PostgreSQL Hackers <[email protected]>
Cc: Nathan Bossart <[email protected]>
Cc: Masahiko Sawada <[email protected]>
Cc: Michael Paquier <[email protected]>
Subject: Re: [PATCH] Add tests for Bitmapset
Date: Mon, 15 Sep 2025 14:00:18 -0400
Message-ID: <[email protected]> (raw)
In-Reply-To: <[email protected]>
References: <[email protected]>
On Sep 13 2025, at 10:23 am, Greg Burd <[email protected]> wrote:
>
> Sawada-san, Michael,
>
> Thank you both for the push to measure. Before the patch as it stands
> now the
> coverage for src/backend/nodes/bitmapset.c is 63.5% and after it is
> 66.5%. Not
> an amazing difference, but something. I guess I expected this to be
> higher given
> the degree to which this datatype is used.
>
> I'll review the gaps in coverage and update the tests. I'll look for
> a way to add meaningful randomization.
>
> -greg
Hello hackers,
Here's the progress I've made in coverage for testing Bitmapset.
coverage: HEAD v1 v2
lines......: 87.1 87.7 91.3
functions..: 100 100 100
branches...: 63.5 66.5 77.9
I've also added a function that performs a variety of operations using
random data.
For reference radixtree has:
coverage: HEAD
lines......: 98.3
functions..: 97.2
branches...: 89.4
best,
-greg
Attachments:
[application/octet-stream] v2-0001-Add-a-module-that-tests-Bitmapset.patch (25.7K, 2-v2-0001-Add-a-module-that-tests-Bitmapset.patch)
download | inline diff:
From 7ef400ec9e7ac0842a593de01db462e80d5ef19a Mon Sep 17 00:00:00 2001
From: Greg Burd <[email protected]>
Date: Wed, 13 Aug 2025 15:40:31 -0400
Subject: [PATCH v2] Add a module that tests Bitmapset
Basic tests for Bitmapset to ensure functionality and help prevent
unintentional breaking changes in the future.
---
src/test/modules/Makefile | 1 +
src/test/modules/meson.build | 1 +
src/test/modules/test_bitmapset/.gitignore | 4 +
src/test/modules/test_bitmapset/Makefile | 23 +
.../expected/test_bitmapset.out | 24 +
src/test/modules/test_bitmapset/meson.build | 33 +
.../test_bitmapset/sql/test_bitmapset.sql | 7 +
.../test_bitmapset/test_bitmapset--1.0.sql | 8 +
.../modules/test_bitmapset/test_bitmapset.c | 767 ++++++++++++++++++
.../test_bitmapset/test_bitmapset.control | 4 +
10 files changed, 872 insertions(+)
create mode 100644 src/test/modules/test_bitmapset/.gitignore
create mode 100644 src/test/modules/test_bitmapset/Makefile
create mode 100644 src/test/modules/test_bitmapset/expected/test_bitmapset.out
create mode 100644 src/test/modules/test_bitmapset/meson.build
create mode 100644 src/test/modules/test_bitmapset/sql/test_bitmapset.sql
create mode 100644 src/test/modules/test_bitmapset/test_bitmapset--1.0.sql
create mode 100644 src/test/modules/test_bitmapset/test_bitmapset.c
create mode 100644 src/test/modules/test_bitmapset/test_bitmapset.control
diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 903a8ac151a..94071ec0e16 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -16,6 +16,7 @@ SUBDIRS = \
spgist_name_ops \
test_aio \
test_binaryheap \
+ test_bitmapset \
test_bloomfilter \
test_copy_callbacks \
test_custom_rmgrs \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 93be0f57289..d8f5c9c7494 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -15,6 +15,7 @@ subdir('spgist_name_ops')
subdir('ssl_passphrase_callback')
subdir('test_aio')
subdir('test_binaryheap')
+subdir('test_bitmapset')
subdir('test_bloomfilter')
subdir('test_copy_callbacks')
subdir('test_custom_rmgrs')
diff --git a/src/test/modules/test_bitmapset/.gitignore b/src/test/modules/test_bitmapset/.gitignore
new file mode 100644
index 00000000000..5dcb3ff9723
--- /dev/null
+++ b/src/test/modules/test_bitmapset/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/src/test/modules/test_bitmapset/Makefile b/src/test/modules/test_bitmapset/Makefile
new file mode 100644
index 00000000000..67199d40d73
--- /dev/null
+++ b/src/test/modules/test_bitmapset/Makefile
@@ -0,0 +1,23 @@
+# src/test/modules/test_bitmapset/Makefile
+
+MODULE_big = test_bitmapset
+OBJS = \
+ $(WIN32RES) \
+ test_bitmapset.o
+PGFILEDESC = "test_bitmapset - test code for src/include/nodes/bitmapset.h"
+
+EXTENSION = test_bitmapset
+DATA = test_bitmapset--1.0.sql
+
+REGRESS = test_bitmapset
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_bitmapset
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_bitmapset/expected/test_bitmapset.out b/src/test/modules/test_bitmapset/expected/test_bitmapset.out
new file mode 100644
index 00000000000..f2669f14501
--- /dev/null
+++ b/src/test/modules/test_bitmapset/expected/test_bitmapset.out
@@ -0,0 +1,24 @@
+CREATE EXTENSION test_bitmapset;
+--
+-- All the logic is in the test_bitmapset() function. It will throw
+-- an error if something fails.
+--
+SELECT test_bitmapset();
+NOTICE: starting bitmapset tests
+NOTICE: testing empty bitmapset operations
+NOTICE: testing single element bitmapset operations
+NOTICE: testing multiple elements and set operations
+NOTICE: testing error conditions
+NOTICE: testing prev_member edge cases
+NOTICE: testing edge cases and boundary conditions
+NOTICE: testing iteration functions
+NOTICE: testing comprehensive set operations
+NOTICE: testing randomized operations
+NOTICE: randomized testing completed successfully
+NOTICE: testing advanced functions
+NOTICE: all bitmapset tests passed
+ test_bitmapset
+----------------
+
+(1 row)
+
diff --git a/src/test/modules/test_bitmapset/meson.build b/src/test/modules/test_bitmapset/meson.build
new file mode 100644
index 00000000000..848f7a44eb9
--- /dev/null
+++ b/src/test/modules/test_bitmapset/meson.build
@@ -0,0 +1,33 @@
+# Copyright (c) 2024-2025, PostgreSQL Global Development Group
+
+test_bitmapset_sources = files(
+ 'test_bitmapset.c',
+)
+
+if host_system == 'windows'
+ test_bitmapset_sources += rc_lib_gen.process(win32ver_rc, extra_args: [
+ '--NAME', 'test_bitmapset',
+ '--FILEDESC', 'test_bitmapset - test code for src/include/nodes/bitmapset.h',])
+endif
+
+test_bitmapset = shared_module('test_bitmapset',
+ test_bitmapset_sources,
+ kwargs: pg_test_mod_args,
+)
+test_install_libs += test_bitmapset
+
+test_install_data += files(
+ 'test_bitmapset.control',
+ 'test_bitmapset--1.0.sql',
+)
+
+tests += {
+ 'name': 'test_bitmapset',
+ 'sd': meson.current_source_dir(),
+ 'bd': meson.current_build_dir(),
+ 'regress': {
+ 'sql': [
+ 'test_bitmapset',
+ ],
+ },
+}
diff --git a/src/test/modules/test_bitmapset/sql/test_bitmapset.sql b/src/test/modules/test_bitmapset/sql/test_bitmapset.sql
new file mode 100644
index 00000000000..ceb524ee51b
--- /dev/null
+++ b/src/test/modules/test_bitmapset/sql/test_bitmapset.sql
@@ -0,0 +1,7 @@
+CREATE EXTENSION test_bitmapset;
+
+--
+-- All the logic is in the test_bitmapset() function. It will throw
+-- an error if something fails.
+--
+SELECT test_bitmapset();
diff --git a/src/test/modules/test_bitmapset/test_bitmapset--1.0.sql b/src/test/modules/test_bitmapset/test_bitmapset--1.0.sql
new file mode 100644
index 00000000000..ba8af997044
--- /dev/null
+++ b/src/test/modules/test_bitmapset/test_bitmapset--1.0.sql
@@ -0,0 +1,8 @@
+/* src/test/modules/test_bitmapset/test_bitmapset--1.0.sql */
+
+-- complain if script is sourced in psql, rather than via CREATE EXTENSION
+\echo Use "CREATE EXTENSION test_bitmapset" to load this file. \quit
+
+CREATE FUNCTION test_bitmapset()
+RETURNS pg_catalog.void STRICT
+AS 'MODULE_PATHNAME' LANGUAGE C;
diff --git a/src/test/modules/test_bitmapset/test_bitmapset.c b/src/test/modules/test_bitmapset/test_bitmapset.c
new file mode 100644
index 00000000000..70614bd6e71
--- /dev/null
+++ b/src/test/modules/test_bitmapset/test_bitmapset.c
@@ -0,0 +1,767 @@
+/*
+ * test_bitmapset.c
+ * Test module for bitmapset data structure
+ *
+ * This module tests the bitmapset implementation in PostgreSQL,
+ * covering all public API functions, edge cases, and memory usage.
+ *
+ * src/test/modules/test_bitmapset/test_bitmapset.c
+ */
+
+#include "postgres.h"
+
+#include "common/pg_prng.h"
+#include "utils/timestamp.h"
+#include "fmgr.h"
+#include "nodes/pg_list.h"
+#include "nodes/bitmapset.h"
+
+PG_MODULE_MAGIC;
+
+PG_FUNCTION_INFO_V1(test_bitmapset);
+
+#define EXPECT_TRUE(expr) \
+ do { \
+ if (!(expr)) \
+ elog(ERROR, \
+ "%s was unexpectedly false in file \"%s\" line %u", \
+ #expr, __FILE__, __LINE__); \
+ } while (0)
+
+#define EXPECT_FALSE(expr) \
+ do { \
+ if (expr) \
+ elog(ERROR, \
+ "%s was unexpectedly true in file \"%s\" line %u", \
+ #expr, __FILE__, __LINE__); \
+ } while (0)
+
+#define EXPECT_EQ_INT(result_expr, expected_expr) \
+ do { \
+ int _result = (result_expr); \
+ int _expected = (expected_expr); \
+ if (_result != _expected) \
+ elog(ERROR, \
+ "%s yielded %d, expected %d (%s) in file \"%s\" line %u", \
+ #result_expr, _result, _expected, #expected_expr, __FILE__, __LINE__); \
+ } while (0)
+
+#define EXPECT_NULL(expr) \
+ do { \
+ if ((expr) != NULL) \
+ elog(ERROR, \
+ "%s was unexpectedly non-NULL in file \"%s\" line %u", \
+ #expr, __FILE__, __LINE__); \
+ } while (0)
+
+#define EXPECT_NOT_NULL(expr) \
+ do { \
+ if ((expr) == NULL) \
+ elog(ERROR, \
+ "%s was unexpectedly NULL in file \"%s\" line %u", \
+ #expr, __FILE__, __LINE__); \
+ } while (0)
+
+#ifdef DEBUG
+
+static void
+elog_bitmapset(int elevel, const char *label, const Bitmapset *bms)
+{
+ StringInfoData buf;
+ int member;
+ bool first = true;
+
+ initStringInfo(&buf);
+
+ if (label)
+ appendStringInfo(&buf, "%s: ", label);
+
+ if (!bms_is_empty(bms))
+ {
+ appendStringInfoChar(&buf, '{');
+ member = -1;
+ while ((member = bms_next_member(bms, member)) >= 0)
+ {
+ if (!first)
+ appendStringInfoString(&buf, ", ");
+ appendStringInfo(&buf, "%d", member);
+ first = false;
+ }
+ appendStringInfoChar(&buf, '}');
+ }
+ else
+ appendStringInfoString(&buf, "EMPTY");
+
+ elog(elevel, "%s", buf.data);
+ pfree(buf.data);
+}
+
+#endif
+
+/* Test error conditions for negative members */
+static void
+test_error_conditions(void)
+{
+ Bitmapset *bms = NULL;
+ bool error_caught;
+
+ elog(NOTICE, "testing error conditions");
+
+ /* Test negative member in bms_make_singleton */
+ error_caught = false;
+ PG_TRY();
+ {
+ bms_make_singleton(-1);
+ }
+ PG_CATCH();
+ {
+ error_caught = true;
+ FlushErrorState();
+ }
+ PG_END_TRY();
+ EXPECT_TRUE(error_caught);
+
+ /* Test negative member in bms_is_member */
+ error_caught = false;
+ PG_TRY();
+ {
+ bms_is_member(-5, bms);
+ }
+ PG_CATCH();
+ {
+ error_caught = true;
+ FlushErrorState();
+ }
+ PG_END_TRY();
+ EXPECT_TRUE(error_caught);
+
+ /* Test negative member in bms_add_member */
+ error_caught = false;
+ PG_TRY();
+ {
+ bms = bms_add_member(bms, -10);
+ }
+ PG_CATCH();
+ {
+ error_caught = true;
+ FlushErrorState();
+ }
+ PG_END_TRY();
+ EXPECT_TRUE(error_caught);
+
+ /* Test negative member in bms_del_member */
+ error_caught = false;
+ PG_TRY();
+ {
+ bms = bms_del_member(bms, -20);
+ }
+ PG_CATCH();
+ {
+ error_caught = true;
+ FlushErrorState();
+ }
+ PG_END_TRY();
+ EXPECT_TRUE(error_caught);
+
+ /* Test negative member in bms_add_range */
+ error_caught = false;
+ PG_TRY();
+ {
+ bms = bms_add_range(bms, -5, 10);
+ }
+ PG_CATCH();
+ {
+ error_caught = true;
+ FlushErrorState();
+ }
+ PG_END_TRY();
+ EXPECT_TRUE(error_caught);
+
+ /* Test empty set error in bms_singleton_member */
+ error_caught = false;
+ PG_TRY();
+ {
+ bms_singleton_member(NULL);
+ }
+ PG_CATCH();
+ {
+ error_caught = true;
+ FlushErrorState();
+ }
+ PG_END_TRY();
+ EXPECT_TRUE(error_caught);
+
+ /* Test multiple members error in bms_singleton_member */
+ bms = bms_add_member(bms, 1);
+ bms = bms_add_member(bms, 2);
+ error_caught = false;
+ PG_TRY();
+ {
+ bms_singleton_member(bms);
+ }
+ PG_CATCH();
+ {
+ error_caught = true;
+ FlushErrorState();
+ }
+ PG_END_TRY();
+ EXPECT_TRUE(error_caught);
+}
+
+/* Test hash functions and advanced operations */
+static void
+test_advanced_functions(void)
+{
+ Bitmapset *bms1 = NULL;
+ Bitmapset *bms2 = NULL;
+ Bitmapset *result = NULL;
+ List *int_list = NIL;
+ uint32 hash1,
+ hash2;
+ Bitmapset *ptr1,
+ *ptr2;
+
+ elog(NOTICE, "testing advanced functions");
+
+ /* Test hash functions */
+ bms1 = bms_add_member(bms1, 10);
+ bms1 = bms_add_member(bms1, 20);
+ bms2 = bms_add_member(bms2, 10);
+ bms2 = bms_add_member(bms2, 20);
+
+ hash1 = bms_hash_value(bms1);
+ hash2 = bms_hash_value(bms2);
+ EXPECT_EQ_INT(hash1, hash2);
+
+ /* Test bitmap_hash and bitmap_match */
+ ptr1 = bms1;
+ ptr2 = bms2;
+ EXPECT_EQ_INT(bitmap_hash(&ptr1, sizeof(Bitmapset *)),
+ bitmap_hash(&ptr2, sizeof(Bitmapset *)));
+ EXPECT_EQ_INT(bitmap_match(&ptr1, &ptr2, sizeof(Bitmapset *)), 0);
+
+ /* Test bms_overlap_list */
+ int_list = lappend_int(int_list, 5);
+ int_list = lappend_int(int_list, 10);
+ int_list = lappend_int(int_list, 30);
+
+ EXPECT_TRUE(bms_overlap_list(bms1, int_list));
+ EXPECT_FALSE(bms_overlap_list(NULL, int_list));
+ EXPECT_FALSE(bms_overlap_list(bms1, NIL));
+
+ /* Test bms_add_range */
+ result = bms_add_range(NULL, 5, 8);
+ EXPECT_EQ_INT(bms_num_members(result), 4); /* 5, 6, 7, 8 */
+ for (int i = 5; i <= 8; i++)
+ EXPECT_TRUE(bms_is_member(i, result));
+ bms_free(result);
+
+ /* Test empty range */
+ result = bms_add_range(NULL, 10, 5);
+ EXPECT_NULL(result);
+
+ /* Test recycling operations */
+ result = bms_add_members(bms1, bms2);
+ EXPECT_TRUE(bms1 == result); /* bms1 is recycled */
+ EXPECT_EQ_INT(bms_num_members(result), 2);
+
+ result = bms_replace_members(result, bms2);
+ EXPECT_TRUE(bms_equal(result, bms2));
+
+ bms_free(result); /* which is bms1 */
+ bms_free(bms2);
+ list_free(int_list);
+}
+
+/* Test prev_member edge cases */
+static void
+test_prev_member_edge_cases(void)
+{
+ Bitmapset *bms = NULL;
+
+ elog(NOTICE, "testing prev_member edge cases");
+
+ /* Test prev_member with -1 on empty set */
+ EXPECT_EQ_INT(bms_prev_member(bms, -1), -2);
+
+ /* Test prev_member with -1 on non-empty set (find highest) */
+ bms = bms_add_member(bms, 100);
+ EXPECT_EQ_INT(bms_prev_member(bms, -1), 100);
+
+ bms_free(bms);
+}
+
+/* Randomized testing similar to test_radixtree.c */
+static void
+test_random_operations(void)
+{
+ Bitmapset *bms1 = NULL;
+ Bitmapset *bms2 = NULL;
+ Bitmapset *result = NULL;
+ pg_prng_state state;
+ uint64 seed = GetCurrentTimestamp();
+ int num_ops = 5000;
+ int max_range = 2000;
+ int *members;
+ int num_members = 0;
+
+ elog(NOTICE, "testing randomized operations");
+
+ pg_prng_seed(&state, seed);
+ members = palloc(sizeof(int) * num_ops);
+
+ /* Phase 1: Random insertions */
+ for (int i = 0; i < num_ops / 2; i++)
+ {
+ int member = pg_prng_uint32(&state) % max_range;
+
+ if (!bms_is_member(member, bms1))
+ {
+ members[num_members++] = member;
+ bms1 = bms_add_member(bms1, member);
+ }
+ }
+
+ /* Phase 2: Random set operations */
+ for (int i = 0; i < num_ops / 4; i++)
+ {
+ int member = pg_prng_uint32(&state) % max_range;
+
+ bms2 = bms_add_member(bms2, member);
+ }
+
+ /* Test union */
+ result = bms_union(bms1, bms2);
+ EXPECT_NOT_NULL(result);
+
+ /* Verify union contains all members from first set */
+ for (int i = 0; i < num_members; i++)
+ {
+ if (!bms_is_member(members[i], result))
+ elog(ERROR, "union missing member %d", members[i]);
+ }
+ bms_free(result);
+
+ /* Test intersection */
+ result = bms_intersect(bms1, bms2);
+ if (result != NULL)
+ {
+ int member = -1;
+
+ while ((member = bms_next_member(result, member)) >= 0)
+ {
+ if (!bms_is_member(member, bms1) || !bms_is_member(member, bms2))
+ elog(ERROR, "intersection contains invalid member %d", member);
+ }
+ bms_free(result);
+ }
+
+ /* Phase 3: Test range operations */
+ result = NULL;
+ for (int i = 0; i < 5; i++)
+ {
+ int lower = pg_prng_uint32(&state) % 100;
+ int upper = lower + (pg_prng_uint32(&state) % 20);
+
+ result = bms_add_range(result, lower, upper);
+ }
+ if (result != NULL)
+ {
+ EXPECT_TRUE(bms_num_members(result) > 0);
+ bms_free(result);
+ }
+
+ /* Cleanup */
+ pfree(members);
+ bms_free(bms1);
+ bms_free(bms2);
+
+ elog(NOTICE, "randomized testing completed successfully");
+}
+
+/* Test empty bitmapset operations */
+static void
+test_empty_bitmapset(void)
+{
+ Bitmapset *result,
+ *bms = NULL;
+
+ elog(NOTICE, "testing empty bitmapset operations");
+
+ /* Test operations on NULL bitmapset */
+ EXPECT_TRUE(bms_is_empty(bms));
+ EXPECT_EQ_INT(bms_num_members(bms), 0);
+ EXPECT_FALSE(bms_is_member(0, bms));
+ EXPECT_FALSE(bms_is_member(1, bms));
+ EXPECT_FALSE(bms_is_member(100, bms));
+ EXPECT_EQ_INT(bms_next_member(bms, -1), -2);
+ EXPECT_EQ_INT(bms_prev_member(bms, -1), -2);
+
+ /* Test hash of empty set */
+ EXPECT_EQ_INT(bms_hash_value(bms), 0);
+
+ /* Test comparison with empty sets */
+ EXPECT_EQ_INT(bms_compare(bms, NULL), 0);
+
+ /* Test copy of empty set */
+ result = bms_copy(bms);
+ EXPECT_NULL(result);
+
+ /* Test union with empty set */
+ result = bms_union(bms, NULL);
+ EXPECT_NULL(result);
+
+ /* Test intersection with empty set */
+ result = bms_intersect(bms, NULL);
+ EXPECT_NULL(result);
+
+ /* Test difference with empty set */
+ result = bms_difference(bms, NULL);
+ EXPECT_NULL(result);
+
+ /* Test equal comparison */
+ EXPECT_TRUE(bms_equal(bms, NULL));
+ EXPECT_TRUE(bms_equal(NULL, bms));
+
+ /* Test subset operations */
+ EXPECT_TRUE(bms_is_subset(bms, NULL));
+ EXPECT_TRUE(bms_is_subset(NULL, bms));
+
+ /* Test overlap */
+ EXPECT_FALSE(bms_overlap(bms, NULL));
+ EXPECT_FALSE(bms_overlap(NULL, bms));
+}
+
+/* Test single element bitmapset operations */
+static void
+test_single_element(void)
+{
+ Bitmapset *result,
+ *bms = NULL;
+ int test_member = 42;
+
+ elog(NOTICE, "testing single element bitmapset operations");
+
+ /* Add single element */
+ bms = bms_add_member(bms, test_member);
+ EXPECT_NOT_NULL(bms);
+ EXPECT_FALSE(bms_is_empty(bms));
+ EXPECT_EQ_INT(bms_num_members(bms), 1);
+ EXPECT_TRUE(bms_is_member(test_member, bms));
+ EXPECT_FALSE(bms_is_member(test_member - 1, bms));
+ EXPECT_FALSE(bms_is_member(test_member + 1, bms));
+
+ /* Test iteration */
+ EXPECT_EQ_INT(bms_next_member(bms, -1), test_member);
+ EXPECT_EQ_INT(bms_next_member(bms, test_member), -2);
+ EXPECT_EQ_INT(bms_prev_member(bms, test_member + 1), test_member);
+ EXPECT_EQ_INT(bms_prev_member(bms, test_member), -2);
+
+ /* Test copy */
+ result = bms_copy(bms);
+ EXPECT_NOT_NULL(result);
+ EXPECT_TRUE(bms_equal(bms, result));
+ EXPECT_TRUE(bms_is_member(test_member, result));
+
+ /* Test member index */
+ EXPECT_EQ_INT(bms_member_index(bms, 42), 0);
+ EXPECT_EQ_INT(bms_member_index(bms, 43), -1);
+
+ /* Test comparison */
+ EXPECT_EQ_INT(bms_compare(bms, result), 0);
+
+ /* Test remove member */
+ result = bms_del_member(result, test_member);
+ EXPECT_NULL(result);
+
+ /* Test comparison again */
+ EXPECT_EQ_INT(bms_compare(bms, result), 1);
+
+ /* Test remove non-existent member */
+ result = bms_copy(bms);
+ EXPECT_NOT_NULL(result);
+ result = bms_del_member(result, test_member + 1);
+ EXPECT_NOT_NULL(result);
+ EXPECT_TRUE(bms_equal(bms, result));
+
+ bms_free(bms);
+ bms_free(result);
+}
+
+/* Test multiple elements and set operations */
+static void
+test_multiple_elements(void)
+{
+ Bitmapset *bms1 = NULL;
+ Bitmapset *bms2 = NULL;
+ Bitmapset *result = NULL;
+ int elements1[] = {1, 5, 10, 15, 20, 100};
+ int elements2[] = {3, 5, 12, 15, 25, 200};
+ int num_elements1 = sizeof(elements1) / sizeof(elements1[0]);
+ int num_elements2 = sizeof(elements2) / sizeof(elements2[0]);
+
+ elog(NOTICE, "testing multiple elements and set operations");
+
+ /* Build first set */
+ for (int i = 0; i < num_elements1; i++)
+ bms1 = bms_add_member(bms1, elements1[i]);
+
+ EXPECT_EQ_INT(bms_num_members(bms1), num_elements1);
+
+ /* Build second set */
+ for (int i = 0; i < num_elements2; i++)
+ bms2 = bms_add_member(bms2, elements2[i]);
+
+ EXPECT_EQ_INT(bms_num_members(bms2), num_elements2);
+
+ /* Test membership */
+ for (int i = 0; i < num_elements1; i++)
+ EXPECT_TRUE(bms_is_member(elements1[i], bms1));
+
+ for (int i = 0; i < num_elements2; i++)
+ EXPECT_TRUE(bms_is_member(elements2[i], bms2));
+
+ /* Test union */
+ result = bms_union(bms1, bms2);
+ EXPECT_NOT_NULL(result);
+ for (int i = 0; i < num_elements1; i++)
+ EXPECT_TRUE(bms_is_member(elements1[i], result));
+ for (int i = 0; i < num_elements2; i++)
+ EXPECT_TRUE(bms_is_member(elements2[i], result));
+ EXPECT_EQ_INT(bms_num_members(result), 10); /* 1,3,5,10,12,15,20,25,100,200 */
+ bms_free(result);
+
+ /* Test intersection */
+ result = bms_intersect(bms1, bms2);
+ EXPECT_NOT_NULL(result);
+ EXPECT_TRUE(bms_is_member(5, result));
+ EXPECT_TRUE(bms_is_member(15, result));
+ EXPECT_EQ_INT(bms_num_members(result), 2); /* 5, 15 */
+ bms_free(result);
+
+ /* Test difference */
+ result = bms_difference(bms1, bms2);
+ EXPECT_NOT_NULL(result);
+ EXPECT_TRUE(bms_is_member(1, result));
+ EXPECT_TRUE(bms_is_member(10, result));
+ EXPECT_TRUE(bms_is_member(20, result));
+ EXPECT_TRUE(bms_is_member(100, result));
+ EXPECT_FALSE(bms_is_member(5, result));
+ EXPECT_FALSE(bms_is_member(15, result));
+ EXPECT_EQ_INT(bms_num_members(result), 4); /* 1, 10, 20, 100 */
+ bms_free(result);
+
+ /* Test overlap */
+ EXPECT_TRUE(bms_overlap(bms1, bms2));
+
+ /* Test subset operations */
+ result = NULL;
+ result = bms_add_member(result, 5);
+ result = bms_add_member(result, 15);
+ EXPECT_TRUE(bms_is_subset(result, bms1));
+ EXPECT_TRUE(bms_is_subset(result, bms2));
+ EXPECT_FALSE(bms_is_subset(bms1, result));
+ bms_free(result);
+
+ /* Test subset comparison */
+ bms2 = bms_add_member(NULL, 5);
+ EXPECT_EQ_INT(bms_subset_compare(bms2, bms1), BMS_SUBSET1);
+ EXPECT_EQ_INT(bms_subset_compare(bms1, bms2), BMS_SUBSET2);
+
+ result = bms_add_member(NULL, 999);
+ EXPECT_EQ_INT(bms_subset_compare(bms2, result), BMS_DIFFERENT);
+ bms_free(result);
+
+ bms_free(bms1);
+ bms_free(bms2);
+}
+
+/* Test edge cases and boundary conditions */
+static void
+test_edge_cases(void)
+{
+ int large_element = 10000;
+ int count;
+ int member;
+ Bitmapset *result;
+ Bitmapset *bms = NULL;
+
+ elog(NOTICE, "testing edge cases and boundary conditions");
+
+ /* Test element 0 */
+ bms = bms_add_member(bms, 0);
+ EXPECT_TRUE(bms_is_member(0, bms));
+ EXPECT_EQ_INT(bms_num_members(bms), 1);
+ EXPECT_EQ_INT(bms_next_member(bms, -1), 0);
+ bms_free(bms);
+ bms = NULL;
+
+ /* Test large element numbers */
+ bms = bms_add_member(bms, large_element);
+ EXPECT_TRUE(bms_is_member(large_element, bms));
+ EXPECT_EQ_INT(bms_num_members(bms), 1);
+ bms_free(bms);
+ bms = NULL;
+
+ /* Test adding same element multiple times */
+ bms = bms_add_member(bms, 42);
+ bms = bms_add_member(bms, 42);
+ EXPECT_EQ_INT(bms_num_members(bms), 1);
+ EXPECT_TRUE(bms_is_member(42, bms));
+ bms_free(bms);
+ bms = NULL;
+
+ /* Test removing from single-element set */
+ bms = bms_add_member(bms, 99);
+ result = bms_del_member(bms, 99);
+ EXPECT_NULL(result);
+ bms_free(bms);
+ bms = NULL;
+
+ /* Test dense range */
+ for (int i = 0; i < 64; i++)
+ bms = bms_add_member(bms, i);
+ EXPECT_EQ_INT(bms_num_members(bms), 64);
+
+ /* Test iteration over dense range */
+ count = 0;
+ member = -1;
+ while ((member = bms_next_member(bms, member)) >= 0)
+ {
+ EXPECT_EQ_INT(member, count);
+ count++;
+ }
+ EXPECT_EQ_INT(count, 64);
+
+ bms_free(bms);
+}
+
+/* Test iterator functions */
+static void
+test_iteration(void)
+{
+ Bitmapset *bms = NULL;
+ int member;
+ int index;
+ int elements[] = {2, 7, 15, 31, 63, 127, 255, 511, 1023};
+ int num_elements = sizeof(elements) / sizeof(elements[0]);
+
+ elog(NOTICE, "testing iteration functions");
+
+ /* Build test set */
+ for (int i = 0; i < num_elements; i++)
+ bms = bms_add_member(bms, elements[i]);
+
+ /* Test forward iteration */
+ member = -1;
+ index = 0;
+ while ((member = bms_next_member(bms, member)) >= 0)
+ {
+ EXPECT_EQ_INT(member, elements[index]);
+ index++;
+ }
+ EXPECT_EQ_INT(index, num_elements);
+
+ /* Test backward iteration */
+ member = bms->nwords * BITS_PER_BITMAPWORD;
+ index = num_elements - 1;
+ while ((member = bms_prev_member(bms, member)) >= 0)
+ {
+ EXPECT_EQ_INT(member, elements[index]);
+ index--;
+ }
+ EXPECT_EQ_INT(index, -1);
+
+ /* Test iteration bounds */
+ EXPECT_EQ_INT(bms_next_member(bms, 1023), -2);
+ EXPECT_EQ_INT(bms_prev_member(bms, 2), -2);
+
+ bms_free(bms);
+}
+
+/* Test set operations with various combinations */
+static void
+test_set_operations(void)
+{
+ Bitmapset *empty = NULL;
+ Bitmapset *single = NULL;
+ Bitmapset *multi = NULL;
+ Bitmapset *result = NULL;
+ Bitmapset *single_copy = NULL;
+
+ elog(NOTICE, "testing comprehensive set operations");
+
+ single = bms_make_singleton(10);
+ multi = bms_add_range(multi, 9, 11);
+
+ result = bms_union(single, multi);
+ EXPECT_EQ_INT(bms_num_members(single), 1);
+ EXPECT_EQ_INT(bms_num_members(multi), 3);
+ EXPECT_EQ_INT(bms_num_members(result), 3);
+ EXPECT_TRUE(bms_is_member(9, result));
+ EXPECT_TRUE(bms_is_member(10, result));
+ EXPECT_TRUE(bms_is_member(11, result));
+ bms_free(result);
+
+ /* Union operations */
+ result = bms_union(empty, single);
+ EXPECT_TRUE(bms_equal(result, single));
+ bms_free(result);
+
+ result = bms_union(single, empty);
+ EXPECT_TRUE(bms_equal(result, single));
+ bms_free(result);
+
+ /* Intersection operations */
+ result = bms_intersect(empty, single);
+ EXPECT_NULL(result);
+
+ result = bms_intersect(single, multi);
+ EXPECT_EQ_INT(bms_num_members(result), 1);
+ EXPECT_TRUE(bms_is_member(10, result));
+ bms_free(result);
+
+ /* Difference operations */
+ result = bms_difference(single, multi);
+ EXPECT_NULL(result);
+
+ result = bms_difference(multi, single);
+ EXPECT_EQ_INT(bms_num_members(result), 2);
+ EXPECT_TRUE(bms_is_member(9, result));
+ EXPECT_FALSE(bms_is_member(10, result));
+ EXPECT_TRUE(bms_is_member(11, result));
+ bms_free(result);
+
+ /* Equality tests */
+ EXPECT_FALSE(bms_equal(single, multi));
+ EXPECT_TRUE(bms_equal(empty, NULL));
+
+ single_copy = bms_copy(single);
+ EXPECT_TRUE(bms_equal(single, single_copy));
+ bms_free(single_copy);
+
+ bms_free(single);
+ bms_free(multi);
+}
+
+/* Main test function */
+Datum
+test_bitmapset(PG_FUNCTION_ARGS)
+{
+ elog(NOTICE, "starting bitmapset tests");
+
+ test_empty_bitmapset();
+ test_single_element();
+ test_multiple_elements();
+ test_error_conditions();
+ test_prev_member_edge_cases();
+ test_edge_cases();
+ test_iteration();
+ test_set_operations();
+ test_random_operations();
+ test_advanced_functions();
+
+ elog(NOTICE, "all bitmapset tests passed");
+
+ PG_RETURN_VOID();
+}
diff --git a/src/test/modules/test_bitmapset/test_bitmapset.control b/src/test/modules/test_bitmapset/test_bitmapset.control
new file mode 100644
index 00000000000..8d02ec8bf0a
--- /dev/null
+++ b/src/test/modules/test_bitmapset/test_bitmapset.control
@@ -0,0 +1,4 @@
+comment = 'Test code for Bitmapset'
+default_version = '1.0'
+module_pathname = '$libdir/test_bitmapset'
+relocatable = true
--
2.49.0
view thread (81+ messages) latest in thread
reply
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Reply to all the recipients using the --to and --cc options:
reply via email
To: [email protected]
Cc: [email protected], [email protected], [email protected], [email protected], [email protected]
Subject: Re: [PATCH] Add tests for Bitmapset
In-Reply-To: <[email protected]>
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
This inbox is served by agora; see mirroring instructions
for how to clone and mirror all data and code used for this inbox