LCOV - code coverage report
Current view: top level - src/test/modules/test_binaryheap - test_binaryheap.c (source / functions) Hit Total Coverage
Test: PostgreSQL 19devel Lines: 105 118 89.0 %
Date: 2025-07-20 14:17:35 Functions: 13 13 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*--------------------------------------------------------------------------
       2             :  *
       3             :  * test_binaryheap.c
       4             :  *      Test correctness of binary heap implementation.
       5             :  *
       6             :  * Copyright (c) 2025, PostgreSQL Global Development Group
       7             :  *
       8             :  * IDENTIFICATION
       9             :  *      src/test/modules/test_binaryheap/test_binaryheap.c
      10             :  *
      11             :  * -------------------------------------------------------------------------
      12             :  */
      13             : 
      14             : #include "postgres.h"
      15             : 
      16             : #include "common/int.h"
      17             : #include "common/pg_prng.h"
      18             : #include "fmgr.h"
      19             : #include "lib/binaryheap.h"
      20             : 
      21           2 : PG_MODULE_MAGIC;
      22             : 
      23             : /*
      24             :  * Test binaryheap_comparator for max-heap of integers.
      25             :  */
      26             : static int
      27       88984 : int_cmp(Datum a, Datum b, void *arg)
      28             : {
      29       88984 :     return pg_cmp_s32(DatumGetInt32(a), DatumGetInt32(b));
      30             : }
      31             : 
      32             : /*
      33             :  * Loops through all nodes and returns the maximum value.
      34             :  */
      35             : static int
      36        2244 : get_max_from_heap(binaryheap *heap)
      37             : {
      38        2244 :     int         max = -1;
      39             : 
      40     1015706 :     for (int i = 0; i < binaryheap_size(heap); i++)
      41     1013462 :         max = Max(max, DatumGetInt32(binaryheap_get_node(heap, i)));
      42             : 
      43        2244 :     return max;
      44             : }
      45             : 
      46             : /*
      47             :  * Generate a random permutation of the integers 0..size-1.
      48             :  */
      49             : static int *
      50          36 : get_permutation(int size)
      51             : {
      52          36 :     int        *permutation = (int *) palloc(size * sizeof(int));
      53             : 
      54          36 :     permutation[0] = 0;
      55             : 
      56             :     /*
      57             :      * This is the "inside-out" variant of the Fisher-Yates shuffle algorithm.
      58             :      * Notionally, we append each new value to the array and then swap it with
      59             :      * a randomly-chosen array element (possibly including itself, else we
      60             :      * fail to generate permutations with the last integer last).  The swap
      61             :      * step can be optimized by combining it with the insertion.
      62             :      */
      63        6696 :     for (int i = 1; i < size; i++)
      64             :     {
      65        6660 :         int         j = pg_prng_uint64_range(&pg_global_prng_state, 0, i);
      66             : 
      67        6660 :         if (j < i)               /* avoid fetching undefined data if j=i */
      68        6556 :             permutation[i] = permutation[j];
      69        6660 :         permutation[j] = i;
      70             :     }
      71             : 
      72          36 :     return permutation;
      73             : }
      74             : 
      75             : /*
      76             :  * Ensure that the heap property holds for the given heap, i.e., each parent is
      77             :  * greater than or equal to its children.
      78             :  */
      79             : static void
      80        5298 : verify_heap_property(binaryheap *heap)
      81             : {
      82     2544214 :     for (int i = 0; i < binaryheap_size(heap); i++)
      83             :     {
      84     2538916 :         int         left = 2 * i + 1;
      85     2538916 :         int         right = 2 * i + 2;
      86     2538916 :         int         parent_val = DatumGetInt32(binaryheap_get_node(heap, i));
      87             : 
      88     3807052 :         if (left < binaryheap_size(heap) &&
      89     1268136 :             parent_val < DatumGetInt32(binaryheap_get_node(heap, left)))
      90           0 :             elog(ERROR, "parent node less than left child");
      91             : 
      92     3804410 :         if (right < binaryheap_size(heap) &&
      93     1265494 :             parent_val < DatumGetInt32(binaryheap_get_node(heap, right)))
      94           0 :             elog(ERROR, "parent node less than right child");
      95             :     }
      96        5298 : }
      97             : 
      98             : /*
      99             :  * Check correctness of basic operations.
     100             :  */
     101             : static void
     102          12 : test_basic(int size)
     103             : {
     104          12 :     binaryheap *heap = binaryheap_allocate(size, int_cmp, NULL);
     105          12 :     int        *permutation = get_permutation(size);
     106             : 
     107          12 :     if (!binaryheap_empty(heap))
     108           0 :         elog(ERROR, "new heap not empty");
     109          12 :     if (binaryheap_size(heap) != 0)
     110           0 :         elog(ERROR, "wrong size for new heap");
     111             : 
     112        2244 :     for (int i = 0; i < size; i++)
     113             :     {
     114        2232 :         binaryheap_add(heap, Int32GetDatum(permutation[i]));
     115        2232 :         verify_heap_property(heap);
     116             :     }
     117             : 
     118          12 :     if (binaryheap_empty(heap))
     119           0 :         elog(ERROR, "heap empty after adding values");
     120          12 :     if (binaryheap_size(heap) != size)
     121           0 :         elog(ERROR, "wrong size for heap after adding values");
     122             : 
     123          12 :     if (DatumGetInt32(binaryheap_first(heap)) != get_max_from_heap(heap))
     124           0 :         elog(ERROR, "incorrect root node after adding values");
     125             : 
     126        2244 :     for (int i = 0; i < size; i++)
     127             :     {
     128        2232 :         int         expected = get_max_from_heap(heap);
     129        2232 :         int         actual = DatumGetInt32(binaryheap_remove_first(heap));
     130             : 
     131        2232 :         if (actual != expected)
     132           0 :             elog(ERROR, "incorrect root node after removing root");
     133        2232 :         verify_heap_property(heap);
     134             :     }
     135             : 
     136          12 :     if (!binaryheap_empty(heap))
     137           0 :         elog(ERROR, "heap not empty after removing all nodes");
     138          12 : }
     139             : 
     140             : /*
     141             :  * Test building heap after unordered additions.
     142             :  */
     143             : static void
     144          12 : test_build(int size)
     145             : {
     146          12 :     binaryheap *heap = binaryheap_allocate(size, int_cmp, NULL);
     147          12 :     int        *permutation = get_permutation(size);
     148             : 
     149        2244 :     for (int i = 0; i < size; i++)
     150        2232 :         binaryheap_add_unordered(heap, Int32GetDatum(permutation[i]));
     151             : 
     152          12 :     if (binaryheap_size(heap) != size)
     153           0 :         elog(ERROR, "wrong size for heap after unordered additions");
     154             : 
     155          12 :     binaryheap_build(heap);
     156          12 :     verify_heap_property(heap);
     157          12 : }
     158             : 
     159             : /*
     160             :  * Test removing nodes.
     161             :  */
     162             : static void
     163          12 : test_remove_node(int size)
     164             : {
     165          12 :     binaryheap *heap = binaryheap_allocate(size, int_cmp, NULL);
     166          12 :     int        *permutation = get_permutation(size);
     167          24 :     int         remove_count = pg_prng_uint64_range(&pg_global_prng_state,
     168          12 :                                                     0, size - 1);
     169             : 
     170        2244 :     for (int i = 0; i < size; i++)
     171        2232 :         binaryheap_add(heap, Int32GetDatum(permutation[i]));
     172             : 
     173         798 :     for (int i = 0; i < remove_count; i++)
     174             :     {
     175        1572 :         int         idx = pg_prng_uint64_range(&pg_global_prng_state,
     176         786 :                                                0, binaryheap_size(heap) - 1);
     177             : 
     178         786 :         binaryheap_remove_node(heap, idx);
     179         786 :         verify_heap_property(heap);
     180             :     }
     181             : 
     182          12 :     if (binaryheap_size(heap) != size - remove_count)
     183           0 :         elog(ERROR, "wrong size after removing nodes");
     184          12 : }
     185             : 
     186             : /*
     187             :  * Test replacing the root node.
     188             :  */
     189             : static void
     190          12 : test_replace_first(int size)
     191             : {
     192          12 :     binaryheap *heap = binaryheap_allocate(size, int_cmp, NULL);
     193             : 
     194        2244 :     for (int i = 0; i < size; i++)
     195        2232 :         binaryheap_add(heap, Int32GetDatum(i));
     196             : 
     197             :     /*
     198             :      * Replace root with a value smaller than everything in the heap.
     199             :      */
     200          12 :     binaryheap_replace_first(heap, Int32GetDatum(-1));
     201          12 :     verify_heap_property(heap);
     202             : 
     203             :     /*
     204             :      * Replace root with a value in the middle of the heap.
     205             :      */
     206          12 :     binaryheap_replace_first(heap, Int32GetDatum(size / 2));
     207          12 :     verify_heap_property(heap);
     208             : 
     209             :     /*
     210             :      * Replace root with a larger value than everything in the heap.
     211             :      */
     212          12 :     binaryheap_replace_first(heap, Int32GetDatum(size + 1));
     213          12 :     verify_heap_property(heap);
     214          12 : }
     215             : 
     216             : /*
     217             :  * Test duplicate values.
     218             :  */
     219             : static void
     220          12 : test_duplicates(int size)
     221             : {
     222          12 :     binaryheap *heap = binaryheap_allocate(size, int_cmp, NULL);
     223          12 :     int         dup = pg_prng_uint64_range(&pg_global_prng_state, 0, size - 1);
     224             : 
     225        2244 :     for (int i = 0; i < size; i++)
     226        2232 :         binaryheap_add(heap, Int32GetDatum(dup));
     227             : 
     228        2244 :     for (int i = 0; i < size; i++)
     229             :     {
     230        2232 :         if (DatumGetInt32(binaryheap_remove_first(heap)) != dup)
     231           0 :             elog(ERROR, "unexpected value in heap with duplicates");
     232             :     }
     233          12 : }
     234             : 
     235             : /*
     236             :  * Test resetting.
     237             :  */
     238             : static void
     239          12 : test_reset(int size)
     240             : {
     241          12 :     binaryheap *heap = binaryheap_allocate(size, int_cmp, NULL);
     242             : 
     243        2244 :     for (int i = 0; i < size; i++)
     244        2232 :         binaryheap_add(heap, Int32GetDatum(i));
     245             : 
     246          12 :     binaryheap_reset(heap);
     247             : 
     248          12 :     if (!binaryheap_empty(heap))
     249           0 :         elog(ERROR, "heap not empty after resetting");
     250          12 : }
     251             : 
     252             : /*
     253             :  * SQL-callable entry point to perform all tests.
     254             :  */
     255           4 : PG_FUNCTION_INFO_V1(test_binaryheap);
     256             : 
     257             : Datum
     258           2 : test_binaryheap(PG_FUNCTION_ARGS)
     259             : {
     260             :     static const int test_sizes[] = {1, 2, 3, 10, 100, 1000};
     261             : 
     262          14 :     for (int i = 0; i < sizeof(test_sizes) / sizeof(int); i++)
     263             :     {
     264          12 :         int         size = test_sizes[i];
     265             : 
     266          12 :         test_basic(size);
     267          12 :         test_build(size);
     268          12 :         test_remove_node(size);
     269          12 :         test_replace_first(size);
     270          12 :         test_duplicates(size);
     271          12 :         test_reset(size);
     272             :     }
     273             : 
     274           2 :     PG_RETURN_VOID();
     275             : }

Generated by: LCOV version 1.16