LCOV - code coverage report
Current view: top level - libs/mem/tests/src - test_libmem_0012.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 100.0 % 80 80
Test Date: 2026-07-12 01:01:34 Functions: 100.0 % 3 3
Branches: 100.0 % 4 4

             Branch data     Line data    Source code
       1                 :             : #include "test_libmem_all.h"
       2                 :             : 
       3                 :             : /**
       4                 :             :  * @brief Verify RELEASE_UNUSED physically shrinks a typed point descriptor while preserving the surviving prefix
       5                 :             :  *
       6                 :             :  * Grows the descriptor past one allocation slab, fills it with a
       7                 :             :  * deterministic point pattern, then shrinks it back to four elements
       8                 :             :  * with RELEASE_UNUSED. The allocation reserve must drop to one slab,
       9                 :             :  * and the surviving prefix must remain readable after the physical
      10                 :             :  * shrink. The descriptor is then grown again to prove that the same
      11                 :             :  * prefix still survives the shrink/regrow cycle
      12                 :             :  *
      13                 :             :  * @return Return describing success or failure
      14                 :             :  */
      15                 :           1 : static Return test_libmem_0012_1(void)
      16                 :             : {
      17                 :           1 :         INITTEST;
      18                 :             : 
      19                 :           1 :         m_create(point,points);
      20                 :             : 
      21                 :             :         /* Choose a source length that forces the descriptor to reserve more than one allocation slab */
      22                 :           1 :         const size_t points_per_slab = MEMORY_BLOCK_BYTES / sizeof(point);
      23                 :           1 :         const size_t large_length = points_per_slab + 1;
      24                 :           1 :         const size_t surviving_length = 4;
      25                 :             : 
      26                 :           1 :         ASSERT(points_per_slab > 0);
      27                 :           1 :         ASSERT(large_length > surviving_length);
      28                 :             : 
      29                 :             :         /* Grow past one slab and seed every element with the deterministic point pattern */
      30                 :           1 :         ASSERT(SUCCESS == m_resize(points,large_length));
      31                 :           1 :         ASSERT(SUCCESS == fill_points(points));
      32                 :             : 
      33                 :             :         /* Record the large reserve so the later RELEASE_UNUSED shrink can prove that capacity was returned */
      34                 :           1 :         const size_t allocated_before_shrink = points->actually_allocated_bytes;
      35                 :           1 :         ASSERT(allocated_before_shrink > MEMORY_BLOCK_BYTES);
      36                 :             : 
      37                 :             :         /* Shrink to a small prefix and require the reserve to drop back to exactly one slab */
      38                 :           1 :         ASSERT(SUCCESS == m_resize(points,surviving_length,RELEASE_UNUSED));
      39                 :           1 :         ASSERT(points->length == surviving_length);
      40                 :           1 :         ASSERT(points->actually_allocated_bytes == MEMORY_BLOCK_BYTES);
      41                 :           1 :         ASSERT(points->actually_allocated_bytes < allocated_before_shrink);
      42                 :             : 
      43                 :             :         /* Read the remaining prefix after the physical shrink to catch corrupted surviving elements */
      44                 :           1 :         const point *shrunk_points = m_data_ro(point,points);
      45                 :           1 :         ASSERT(shrunk_points != NULL);
      46                 :             : 
      47                 :           1 :         IF(shrunk_points != NULL)
      48                 :             :         {
      49         [ +  + ]:           5 :                 for(size_t point_index = 0; point_index < surviving_length; point_index++)
      50                 :             :                 {
      51                 :           4 :                         ASSERT(shrunk_points[point_index].x == (int)(point_index * 2 + 1));
      52                 :           4 :                         ASSERT(shrunk_points[point_index].y == (int)(point_index * 2 + 2));
      53                 :             :                 }
      54                 :             :         }
      55                 :             : 
      56                 :             :         /* Grow again inside the retained slab; the already-surviving prefix must still be intact */
      57                 :           1 :         ASSERT(SUCCESS == m_resize(points,8));
      58                 :           1 :         ASSERT(points->length == 8);
      59                 :           1 :         ASSERT(points->actually_allocated_bytes == MEMORY_BLOCK_BYTES);
      60                 :             : 
      61                 :             :         /* Re-read through a fresh view because any resize may invalidate cached data pointers */
      62                 :           1 :         const point *regrown_points = m_data_ro(point,points);
      63                 :           1 :         ASSERT(regrown_points != NULL);
      64                 :             : 
      65                 :           1 :         IF(regrown_points != NULL)
      66                 :             :         {
      67         [ +  + ]:           5 :                 for(size_t point_index = 0; point_index < surviving_length; point_index++)
      68                 :             :                 {
      69                 :           4 :                         ASSERT(regrown_points[point_index].x == (int)(point_index * 2 + 1));
      70                 :           4 :                         ASSERT(regrown_points[point_index].y == (int)(point_index * 2 + 2));
      71                 :             :                 }
      72                 :             :         }
      73                 :             : 
      74                 :             :         /* Confirm the resize cycle did not accidentally switch the descriptor into string mode */
      75                 :           1 :         ASSERT(points->string_length == 0);
      76                 :           1 :         ASSERT(points->is_string == false);
      77                 :           1 :         call(m_del(points));
      78                 :             : 
      79                 :           1 :         RETURN_STATUS;
      80                 :             : }
      81                 :             : 
      82                 :             : /**
      83                 :             :  * @brief Verify m_concat_data appends a typed source after a RELEASE_UNUSED shrink/regrow cycle
      84                 :             :  *
      85                 :             :  * Builds a five-element destination prefix and a source descriptor
      86                 :             :  * that first crosses a slab boundary, then shrinks with RELEASE_UNUSED,
      87                 :             :  * then grows back to eight elements. The final byte-for-byte check
      88                 :             :  * proves that m_concat_data keeps the destination prefix intact and
      89                 :             :  * appends the full regrown source in order
      90                 :             :  *
      91                 :             :  * @return Return describing success or failure
      92                 :             :  */
      93                 :           1 : static Return test_libmem_0012_2(void)
      94                 :             : {
      95                 :           1 :         INITTEST;
      96                 :             : 
      97                 :           1 :         m_create(point,points);
      98                 :           1 :         m_create(point,mirror);
      99                 :             : 
     100                 :             :         /* Use the same slab-crossing source size as the shrink/regrow test */
     101                 :           1 :         const size_t points_per_slab = MEMORY_BLOCK_BYTES / sizeof(point);
     102                 :           1 :         const size_t large_length = points_per_slab + 1;
     103                 :             : 
     104                 :           1 :         ASSERT(points_per_slab > 0);
     105                 :             : 
     106                 :             :         /* Prepare the destination prefix that must survive the append untouched */
     107                 :           1 :         ASSERT(SUCCESS == m_resize(mirror,5));
     108                 :           1 :         ASSERT(SUCCESS == fill_points(mirror));
     109                 :             : 
     110                 :             :         /* Prepare a source descriptor large enough for RELEASE_UNUSED to reclaim real capacity */
     111                 :           1 :         ASSERT(SUCCESS == m_resize(points,large_length));
     112                 :           1 :         ASSERT(SUCCESS == fill_points(points));
     113                 :             : 
     114                 :             :         /* Capture the oversized reserve before shrinking the source */
     115                 :           1 :         const size_t allocated_before_shrink = points->actually_allocated_bytes;
     116                 :           1 :         ASSERT(allocated_before_shrink > MEMORY_BLOCK_BYTES);
     117                 :             : 
     118                 :             :         /* Physically shrink the source to its first four points before regrowing it for the append */
     119                 :           1 :         ASSERT(SUCCESS == m_resize(points,4,RELEASE_UNUSED));
     120                 :           1 :         ASSERT(points->length == 4);
     121                 :           1 :         ASSERT(points->actually_allocated_bytes == MEMORY_BLOCK_BYTES);
     122                 :           1 :         ASSERT(points->actually_allocated_bytes < allocated_before_shrink);
     123                 :             : 
     124                 :             :         /* Regrow the source and write the newly exposed tail so the expected concat payload is complete */
     125                 :           1 :         ASSERT(SUCCESS == m_resize(points,8));
     126                 :             : 
     127                 :           1 :         point *regrown_points = m_data(point,points);
     128                 :           1 :         ASSERT(regrown_points != NULL);
     129                 :             : 
     130                 :           1 :         IF(regrown_points != NULL)
     131                 :             :         {
     132                 :           1 :                 regrown_points[4] = (point){9,10};
     133                 :           1 :                 regrown_points[5] = (point){11,12};
     134                 :           1 :                 regrown_points[6] = (point){13,14};
     135                 :           1 :                 regrown_points[7] = (point){15,16};
     136                 :             :         }
     137                 :             : 
     138                 :             :         /* Append all eight source points after the five-point destination prefix */
     139                 :           1 :         ASSERT(SUCCESS == m_concat_data(mirror,points));
     140                 :             : 
     141                 :             :         /* Check the entire destination, not only the last appended point */
     142                 :           1 :         const point *mirror_view = m_data_ro(point,mirror);
     143                 :           1 :         ASSERT(mirror_view != NULL);
     144                 :             : 
     145                 :           1 :         IF(mirror_view != NULL)
     146                 :             :         {
     147                 :           1 :                 const point expected[] = {
     148                 :             :                         {1,2},{3,4},{5,6},{7,8},{9,10},
     149                 :             :                         {1,2},{3,4},{5,6},{7,8},{9,10},{11,12},{13,14},{15,16}
     150                 :             :                 };
     151                 :           1 :                 const size_t expected_length = sizeof(expected) / sizeof(point);
     152                 :             : 
     153                 :           1 :                 ASSERT(mirror->length == expected_length);
     154                 :           1 :                 ASSERT(memcmp(mirror_view,expected,sizeof(expected)) == 0);
     155                 :             :         }
     156                 :             : 
     157                 :             :         /* Both descriptors must remain ordinary data descriptors after concat */
     158                 :           1 :         ASSERT(points->string_length == 0);
     159                 :           1 :         ASSERT(points->is_string == false);
     160                 :           1 :         ASSERT(mirror->string_length == 0);
     161                 :           1 :         ASSERT(mirror->is_string == false);
     162                 :           1 :         call(m_del(points));
     163                 :           1 :         call(m_del(mirror));
     164                 :             : 
     165                 :           1 :         RETURN_STATUS;
     166                 :             : }
     167                 :             : 
     168                 :             : /**
     169                 :             :  * @brief Verify typed point shrink/regrow behavior and concatenation after RELEASE_UNUSED
     170                 :             :  *
     171                 :             :  * In plain terms, this suite checks that an aggressively shrunk typed
     172                 :             :  * descriptor can be safely regrown and then used as a concatenation
     173                 :             :  * source. The first nested test proves that RELEASE_UNUSED really
     174                 :             :  * returns spare slab capacity while keeping the surviving prefix
     175                 :             :  * intact. The second nested test appends the regrown source to a
     176                 :             :  * five-point destination and verifies the complete thirteen-point
     177                 :             :  * result, so overwrites, missing source elements, and ordering bugs
     178                 :             :  * are visible
     179                 :             :  *
     180                 :             :  * Groups the typed-data checks that prove RELEASE_UNUSED can physically
     181                 :             :  * reduce a descriptor's reserve without corrupting the surviving prefix,
     182                 :             :  * and that m_concat_data can append a regrown typed source without
     183                 :             :  * overwriting the destination
     184                 :             :  *
     185                 :             :  * @return Return describing success or failure
     186                 :             :  */
     187                 :           1 : Return test_libmem_0012(void)
     188                 :             : {
     189                 :           1 :         INITTEST;
     190                 :             : 
     191                 :             :         /* Keep the physical shrink/regrow contract separate from the concat contract */
     192                 :           1 :         TEST(test_libmem_0012_1,"RELEASE_UNUSED shrink across a slab boundary preserves the typed point prefix");
     193                 :           1 :         TEST(test_libmem_0012_2,"m_concat_data appends a regrown typed point source after the destination prefix");
     194                 :             : 
     195                 :           1 :         RETURN_STATUS;
     196                 :             : }
        

Generated by: LCOV version 2.0-1