test/test_buffer.cpp

Mon, 08 Aug 2022 17:12:00 +0200

author
Mike Becker <universe@uap-core.de>
date
Mon, 08 Aug 2022 17:12:00 +0200
changeset 572
f0f99dd06d9f
parent 569
cb63f3d1236a
permissions
-rw-r--r--

#201 - remove dangerous allocator config

There is no plausible use case, except using the testing
allocator in the test case, and having the possibility to
specify any allocator (including another mempool) causes
more harm than good.

     1 /*
     2  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
     3  *
     4  * Copyright 2021 Mike Becker, Olaf Wintermann All rights reserved.
     5  *
     6  * Redistribution and use in source and binary forms, with or without
     7  * modification, are permitted provided that the following conditions are met:
     8  *
     9  *   1. Redistributions of source code must retain the above copyright
    10  *      notice, this list of conditions and the following disclaimer.
    11  *
    12  *   2. Redistributions in binary form must reproduce the above copyright
    13  *      notice, this list of conditions and the following disclaimer in the
    14  *      documentation and/or other materials provided with the distribution.
    15  *
    16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
    17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
    18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
    19  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
    20  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
    21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
    22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
    23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
    24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
    25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
    26  * POSSIBILITY OF SUCH DAMAGE.
    27  */
    29 #include "cx/buffer.h"
    31 #include <gtest/gtest.h>
    32 #include "util_allocator.h"
    34 class BufferFixture : public ::testing::Test {
    35 protected:
    36     void SetUp() override {
    37         cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
    38         buf.size = 6;
    39         buf.pos = 3;
    40     }
    42     void TearDown() override {
    43         cxBufferDestroy(&buf);
    44     }
    46     CxBuffer buf{};
    47 };
    49 static void expect_default_flush_config(CxBuffer *buf) {
    50     EXPECT_EQ(buf->flush_blkmax, 0);
    51     EXPECT_EQ(buf->flush_blksize, 4096);
    52     EXPECT_EQ(buf->flush_threshold, SIZE_MAX);
    53     EXPECT_EQ(buf->flush_func, nullptr);
    54     EXPECT_EQ(buf->flush_target, nullptr);
    55 }
    57 TEST(BufferInit, WrapSpace) {
    58     CxTestingAllocator alloc;
    59     CxBuffer buf;
    60     void *space = cxMalloc(&alloc, 16);
    61     cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_DEFAULT);
    62     expect_default_flush_config(&buf);
    63     EXPECT_EQ(buf.space, space);
    64     EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
    65     EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0);
    66     EXPECT_EQ(buf.pos, 0);
    67     EXPECT_EQ(buf.size, 0);
    68     EXPECT_EQ(buf.capacity, 16);
    69     EXPECT_EQ(buf.allocator, &alloc);
    70     cxBufferDestroy(&buf);
    71     EXPECT_FALSE(alloc.verify());
    72     cxFree(&alloc, space);
    73     EXPECT_TRUE(alloc.verify());
    74 }
    76 TEST(BufferInit, WrapSpaceAutoExtend) {
    77     CxTestingAllocator alloc;
    78     CxBuffer buf;
    79     void *space = cxMalloc(&alloc, 16);
    80     cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_AUTO_EXTEND);
    81     expect_default_flush_config(&buf);
    82     EXPECT_EQ(buf.space, space);
    83     EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, CX_BUFFER_AUTO_EXTEND);
    84     EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, 0);
    85     EXPECT_EQ(buf.pos, 0);
    86     EXPECT_EQ(buf.size, 0);
    87     EXPECT_EQ(buf.capacity, 16);
    88     EXPECT_EQ(buf.allocator, &alloc);
    89     cxBufferDestroy(&buf);
    90     EXPECT_FALSE(alloc.verify());
    91     cxFree(&alloc, space);
    92     EXPECT_TRUE(alloc.verify());
    93 }
    95 TEST(BufferInit, WrapSpaceAutoFree) {
    96     CxTestingAllocator alloc;
    97     CxBuffer buf;
    98     void *space = cxMalloc(&alloc, 16);
    99     cxBufferInit(&buf, space, 16, &alloc, CX_BUFFER_FREE_CONTENTS);
   100     expect_default_flush_config(&buf);
   101     EXPECT_EQ(buf.space, space);
   102     EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
   103     EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS);
   104     EXPECT_EQ(buf.pos, 0);
   105     EXPECT_EQ(buf.size, 0);
   106     EXPECT_EQ(buf.capacity, 16);
   107     EXPECT_EQ(buf.allocator, &alloc);
   108     EXPECT_FALSE(alloc.verify());
   109     cxBufferDestroy(&buf);
   110     EXPECT_TRUE(alloc.verify());
   111 }
   113 TEST(BufferInit, FreshSpace) {
   114     CxTestingAllocator alloc;
   115     CxBuffer buf;
   116     cxBufferInit(&buf, nullptr, 8, &alloc, CX_BUFFER_DEFAULT);
   117     expect_default_flush_config(&buf);
   118     EXPECT_NE(buf.space, nullptr);
   119     EXPECT_EQ(buf.flags & CX_BUFFER_AUTO_EXTEND, 0);
   120     EXPECT_EQ(buf.flags & CX_BUFFER_FREE_CONTENTS, CX_BUFFER_FREE_CONTENTS);
   121     EXPECT_EQ(buf.pos, 0);
   122     EXPECT_EQ(buf.size, 0);
   123     EXPECT_EQ(buf.capacity, 8);
   124     EXPECT_EQ(buf.allocator, &alloc);
   125     EXPECT_FALSE(alloc.verify()); // space is still allocated
   126     cxBufferDestroy(&buf);
   127     EXPECT_TRUE(alloc.verify());
   128 }
   130 class BufferShiftFixture : public ::testing::Test {
   131 protected:
   132     void SetUp() override {
   133         ASSERT_TRUE(alloc.verify());
   134         cxBufferInit(&buf, nullptr, 16, &alloc, CX_BUFFER_DEFAULT);
   135         memcpy(buf.space, "test____________", 16);
   136         buf.capacity = 8; // purposely pretend that the buffer has less capacity s.t. we can test beyond the range
   137         buf.pos = 4;
   138         buf.size = 4;
   139     }
   141     void TearDown() override {
   142         cxBufferDestroy(&buf);
   143         EXPECT_TRUE(alloc.verify());
   144     }
   146     CxTestingAllocator alloc;
   147     CxBuffer buf{};
   148 };
   150 class BufferShiftLeft : public BufferShiftFixture {
   151 };
   153 TEST_F(BufferShiftLeft, Zero) {
   154     ASSERT_EQ(buf.pos, 4);
   155     ASSERT_EQ(buf.size, 4);
   156     int ret = cxBufferShiftLeft(&buf, 0);
   157     EXPECT_EQ(ret, 0);
   158     EXPECT_EQ(buf.pos, 4);
   159     EXPECT_EQ(buf.size, 4);
   160     EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
   161 }
   163 TEST_F(BufferShiftLeft, ZeroOffsetInterface) {
   164     ASSERT_EQ(buf.pos, 4);
   165     ASSERT_EQ(buf.size, 4);
   166     int ret = cxBufferShift(&buf, -0);
   167     EXPECT_EQ(ret, 0);
   168     EXPECT_EQ(buf.pos, 4);
   169     EXPECT_EQ(buf.size, 4);
   170     EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
   171 }
   173 TEST_F(BufferShiftLeft, Standard) {
   174     ASSERT_EQ(buf.pos, 4);
   175     ASSERT_EQ(buf.size, 4);
   176     int ret = cxBufferShiftLeft(&buf, 2);
   177     EXPECT_EQ(ret, 0);
   178     EXPECT_EQ(buf.pos, 2);
   179     EXPECT_EQ(buf.size, 2);
   180     EXPECT_TRUE(memcmp(buf.space, "stst________", 8) == 0);
   181 }
   183 TEST_F(BufferShiftLeft, Overshift) {
   184     ASSERT_LT(buf.pos, 6);
   185     ASSERT_LT(buf.size, 6);
   186     int ret = cxBufferShiftLeft(&buf, 6);
   187     EXPECT_EQ(ret, 0);
   188     EXPECT_EQ(buf.pos, 0);
   189     EXPECT_EQ(buf.size, 0);
   190     EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
   191 }
   193 TEST_F(BufferShiftLeft, OvershiftPosOnly) {
   194     buf.pos = 2;
   195     ASSERT_EQ(buf.size, 4);
   196     int ret = cxBufferShiftLeft(&buf, 3);
   197     EXPECT_EQ(ret, 0);
   198     EXPECT_EQ(buf.pos, 0);
   199     EXPECT_EQ(buf.size, 1);
   200     EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
   201 }
   203 TEST_F(BufferShiftLeft, OffsetInterface) {
   204     buf.pos = 3;
   205     ASSERT_EQ(buf.size, 4);
   206     int ret = cxBufferShift(&buf, -2);
   207     EXPECT_EQ(ret, 0);
   208     EXPECT_EQ(buf.pos, 1);
   209     EXPECT_EQ(buf.size, 2);
   210     EXPECT_TRUE(memcmp(buf.space, "stst________", 8) == 0);
   211 }
   213 class BufferShiftRight : public BufferShiftFixture {
   214 };
   216 TEST_F(BufferShiftRight, Zero) {
   217     ASSERT_EQ(buf.pos, 4);
   218     ASSERT_EQ(buf.size, 4);
   219     int ret = cxBufferShiftRight(&buf, 0);
   220     EXPECT_EQ(ret, 0);
   221     EXPECT_EQ(buf.pos, 4);
   222     EXPECT_EQ(buf.size, 4);
   223     EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
   224 }
   226 TEST_F(BufferShiftRight, ZeroOffsetInterface) {
   227     ASSERT_EQ(buf.pos, 4);
   228     ASSERT_EQ(buf.size, 4);
   229     int ret = cxBufferShift(&buf, +0);
   230     EXPECT_EQ(ret, 0);
   231     EXPECT_EQ(buf.pos, 4);
   232     EXPECT_EQ(buf.size, 4);
   233     EXPECT_TRUE(memcmp(buf.space, "test________", 8) == 0);
   234 }
   236 TEST_F(BufferShiftRight, Standard) {
   237     ASSERT_EQ(buf.pos, 4);
   238     ASSERT_EQ(buf.size, 4);
   239     int ret = cxBufferShiftRight(&buf, 3);
   240     EXPECT_EQ(ret, 0);
   241     EXPECT_EQ(buf.pos, 7);
   242     EXPECT_EQ(buf.size, 7);
   243     EXPECT_TRUE(memcmp(buf.space, "testest_____", 8) == 0);
   244 }
   246 TEST_F(BufferShiftRight, OvershiftDiscard) {
   247     ASSERT_EQ(buf.pos, 4);
   248     ASSERT_EQ(buf.size, 4);
   249     ASSERT_EQ(buf.capacity, 8);
   250     int ret = cxBufferShiftRight(&buf, 6);
   251     EXPECT_EQ(ret, 0);
   252     EXPECT_EQ(buf.pos, 8);
   253     EXPECT_EQ(buf.size, 8);
   254     EXPECT_EQ(buf.capacity, 8);
   255     EXPECT_TRUE(memcmp(buf.space, "test__te____", 8) == 0);
   256 }
   258 TEST_F(BufferShiftRight, OvershiftExtend) {
   259     ASSERT_EQ(buf.pos, 4);
   260     ASSERT_EQ(buf.size, 4);
   261     ASSERT_EQ(buf.capacity, 8);
   262     buf.flags |= CX_BUFFER_AUTO_EXTEND;
   263     int ret = cxBufferShiftRight(&buf, 6);
   264     EXPECT_EQ(ret, 0);
   265     EXPECT_EQ(buf.pos, 10);
   266     EXPECT_EQ(buf.size, 10);
   267     EXPECT_GE(buf.capacity, 10);
   268     EXPECT_TRUE(memcmp(buf.space, "test__test__", 8) == 0);
   269 }
   271 TEST_F(BufferShiftRight, OffsetInterface) {
   272     buf.pos = 3;
   273     ASSERT_EQ(buf.size, 4);
   274     int ret = cxBufferShift(&buf, 2);
   275     EXPECT_EQ(ret, 0);
   276     EXPECT_EQ(buf.pos, 5);
   277     EXPECT_EQ(buf.size, 6);
   278     EXPECT_TRUE(memcmp(buf.space, "tetest______", 8) == 0);
   279 }
   281 TEST(BufferMinimumCapacity, Sufficient) {
   282     CxTestingAllocator alloc;
   283     auto space = cxMalloc(&alloc, 8);
   284     CxBuffer buf;
   285     cxBufferInit(&buf, space, 8, &alloc, CX_BUFFER_FREE_CONTENTS);
   286     memcpy(space, "Testing", 8);
   287     buf.size = 8;
   288     cxBufferMinimumCapacity(&buf, 6);
   289     EXPECT_EQ(buf.capacity, 8);
   290     EXPECT_EQ(buf.size, 8);
   291     EXPECT_TRUE(memcmp(buf.space, "Testing", 8) == 0);
   292     cxBufferDestroy(&buf);
   293     EXPECT_TRUE(alloc.verify());
   294 }
   296 TEST(BufferMinimumCapacity, Extend) {
   297     CxTestingAllocator alloc;
   298     auto space = cxMalloc(&alloc, 8);
   299     CxBuffer buf;
   300     cxBufferInit(&buf, space, 8, &alloc, CX_BUFFER_FREE_CONTENTS); // NO auto extend!
   301     memcpy(space, "Testing", 8);
   302     buf.size = 8;
   303     cxBufferMinimumCapacity(&buf, 16);
   304     EXPECT_EQ(buf.capacity, 16);
   305     EXPECT_EQ(buf.size, 8);
   306     EXPECT_TRUE(memcmp(buf.space, "Testing", 8) == 0);
   307     cxBufferDestroy(&buf);
   308     EXPECT_TRUE(alloc.verify());
   309 }
   311 TEST(BufferClear, Test) {
   312     char space[16];
   313     strcpy(space, "clear test");
   314     CxBuffer buf;
   315     cxBufferInit(&buf, space, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
   316     ASSERT_EQ(buf.size, 0);
   317     // only clear the used part of the buffer
   318     cxBufferClear(&buf);
   319     EXPECT_EQ(memcmp(space, "clear test", 10), 0);
   320     buf.size = 5;
   321     buf.pos = 3;
   322     cxBufferClear(&buf);
   323     EXPECT_EQ(memcmp(space, "\0\0\0\0\0 test", 10), 0);
   324     EXPECT_EQ(buf.size, 0);
   325     EXPECT_EQ(buf.pos, 0);
   326     cxBufferDestroy(&buf);
   327 }
   329 class BufferWrite : public ::testing::Test {
   330 protected:
   331     CxBuffer buf{}, target{};
   333     void SetUp() override {
   334         cxBufferInit(&target, nullptr, 16, cxDefaultAllocator, CX_BUFFER_AUTO_EXTEND);
   335         cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
   336         buf.capacity = 8; // artificially reduce capacity to check OOB writes
   337         memset(buf.space, 0, 16);
   338         memcpy(buf.space, "prep", 4);
   339         buf.size = buf.pos = 4;
   340     }
   342     void TearDown() override {
   343         cxBufferDestroy(&buf);
   344         cxBufferDestroy(&target);
   345     }
   347     void enableFlushing() {
   348         buf.flush_target = &target;
   349         buf.flush_func = reinterpret_cast<cx_write_func>(cxBufferWrite);
   350         buf.flush_blkmax = 1;
   351     }
   352 };
   354 static size_t mock_write_limited_rate(
   355         void const *ptr,
   356         size_t size,
   357         __attribute__((unused)) size_t nitems,
   358         CxBuffer *buffer
   359 ) {
   360     // simulate limited target drain capacity
   361     static bool full = false;
   362     if (full) {
   363         full = false;
   364         return 0;
   365     } else {
   366         full = true;
   367         return cxBufferWrite(ptr, size, nitems > 2 ? 2 : nitems, buffer);
   368     }
   369 }
   371 TEST_F(BufferWrite, SizeOneFit) {
   372     const char *data = "test";
   373     ASSERT_EQ(buf.capacity, 8);
   374     ASSERT_EQ(buf.pos, 4);
   375     ASSERT_EQ(buf.size, 4);
   376     size_t written = cxBufferWrite(data, 1, 4, &buf);
   377     EXPECT_EQ(written, 4);
   378     EXPECT_EQ(buf.size, 8);
   379     EXPECT_EQ(buf.pos, 8);
   380     EXPECT_EQ(buf.capacity, 8);
   381     EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
   382 }
   384 TEST_F(BufferWrite, SizeOneDiscard) {
   385     const char *data = "testing";
   386     ASSERT_EQ(buf.capacity, 8);
   387     ASSERT_EQ(buf.pos, 4);
   388     ASSERT_EQ(buf.size, 4);
   389     size_t written = cxBufferWrite(data, 1, 7, &buf);
   390     EXPECT_EQ(written, 4);
   391     EXPECT_EQ(buf.size, 8);
   392     EXPECT_EQ(buf.pos, 8);
   393     EXPECT_EQ(buf.capacity, 8);
   394     EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0);
   395 }
   397 TEST_F(BufferWrite, SizeOneExtend) {
   398     buf.flags |= CX_BUFFER_AUTO_EXTEND;
   399     const char *data = "testing";
   400     ASSERT_EQ(buf.capacity, 8);
   401     ASSERT_EQ(buf.pos, 4);
   402     ASSERT_EQ(buf.size, 4);
   403     size_t written = cxBufferWrite(data, 1, 7, &buf);
   404     EXPECT_EQ(written, 7);
   405     EXPECT_EQ(buf.size, 11);
   406     EXPECT_EQ(buf.pos, 11);
   407     EXPECT_GE(buf.capacity, 11);
   408     EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0);
   409 }
   411 TEST_F(BufferWrite, MultibyteFit) {
   412     const char *data = "test";
   413     ASSERT_EQ(buf.capacity, 8);
   414     ASSERT_EQ(buf.pos, 4);
   415     ASSERT_EQ(buf.size, 4);
   416     size_t written = cxBufferWrite(data, 2, 2, &buf);
   417     EXPECT_EQ(written, 2);
   418     EXPECT_EQ(buf.size, 8);
   419     EXPECT_EQ(buf.pos, 8);
   420     EXPECT_EQ(buf.capacity, 8);
   421     EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
   422 }
   424 TEST_F(BufferWrite, MultibyteDiscard) {
   425     const char *data = "testing";
   426     ASSERT_EQ(buf.capacity, 8);
   427     ASSERT_EQ(buf.size, 4);
   428     buf.pos = 3;
   429     size_t written = cxBufferWrite(data, 2, 4, &buf);
   430     // remember: whole elements are discarded if they do not fit
   431     EXPECT_EQ(written, 2);
   432     EXPECT_EQ(buf.size, 7);
   433     EXPECT_EQ(buf.pos, 7);
   434     EXPECT_EQ(buf.capacity, 8);
   435     EXPECT_EQ(memcmp(buf.space, "pretest\0", 8), 0);
   436 }
   438 TEST_F(BufferWrite, MultibyteExtend) {
   439     buf.flags |= CX_BUFFER_AUTO_EXTEND;
   440     const char *data = "tester";
   441     ASSERT_EQ(buf.capacity, 8);
   442     ASSERT_EQ(buf.size, 4);
   443     buf.pos = 3;
   444     size_t written = cxBufferWrite(data, 2, 3, &buf);
   445     // remember: whole elements are discarded if they do not fit
   446     EXPECT_EQ(written, 3);
   447     EXPECT_EQ(buf.size, 9);
   448     EXPECT_EQ(buf.pos, 9);
   449     EXPECT_GE(buf.capacity, 9);
   450     EXPECT_EQ(memcmp(buf.space, "pretester", 9), 0);
   451 }
   453 TEST_F(BufferWrite, PutcWrapperFit) {
   454     ASSERT_EQ(buf.capacity, 8);
   455     ASSERT_EQ(buf.pos, 4);
   456     ASSERT_EQ(buf.size, 4);
   457     int c = cxBufferPut(&buf, 0x200 | 'a');
   458     EXPECT_EQ(c, 'a');
   459     EXPECT_EQ(buf.size, 5);
   460     EXPECT_EQ(buf.pos, 5);
   461     EXPECT_EQ(buf.capacity, 8);
   462     EXPECT_EQ(memcmp(buf.space, "prepa\0", 6), 0);
   463 }
   465 TEST_F(BufferWrite, PutcWrapperDiscard) {
   466     ASSERT_EQ(buf.capacity, 8);
   467     ASSERT_EQ(buf.size, 4);
   468     buf.pos = 8;
   469     int c = cxBufferPut(&buf, 0x200 | 'a');
   470     EXPECT_EQ(c, EOF);
   471     EXPECT_EQ(buf.size, 4);
   472     EXPECT_EQ(buf.pos, 8);
   473     EXPECT_EQ(buf.capacity, 8);
   474     EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0\0", 9), 0);
   475 }
   477 TEST_F(BufferWrite, PutcWrapperExtend) {
   478     buf.flags |= CX_BUFFER_AUTO_EXTEND;
   479     ASSERT_EQ(buf.capacity, 8);
   480     ASSERT_EQ(buf.size, 4);
   481     buf.pos = 8;
   482     int c = cxBufferPut(&buf, 0x200 | 'a');
   483     EXPECT_EQ(c, 'a');
   484     EXPECT_EQ(buf.size, 9);
   485     EXPECT_EQ(buf.pos, 9);
   486     EXPECT_GE(buf.capacity, 9);
   487     EXPECT_EQ(memcmp(buf.space, "prep\0\0\0\0a", 9), 0);
   488 }
   490 TEST_F(BufferWrite, PutStringWrapperFit) {
   491     const char *data = "test";
   492     ASSERT_EQ(buf.capacity, 8);
   493     ASSERT_EQ(buf.pos, 4);
   494     ASSERT_EQ(buf.size, 4);
   495     size_t written = cxBufferPutString(&buf, data);
   496     EXPECT_EQ(written, 4);
   497     EXPECT_EQ(buf.size, 8);
   498     EXPECT_EQ(buf.pos, 8);
   499     EXPECT_EQ(buf.capacity, 8);
   500     EXPECT_EQ(memcmp(buf.space, "preptest", 8), 0);
   501 }
   503 TEST_F(BufferWrite, PutStringWrapperDiscard) {
   504     const char *data = "testing";
   505     ASSERT_EQ(buf.capacity, 8);
   506     ASSERT_EQ(buf.pos, 4);
   507     ASSERT_EQ(buf.size, 4);
   508     size_t written = cxBufferPutString(&buf, data);
   509     EXPECT_EQ(written, 4);
   510     EXPECT_EQ(buf.size, 8);
   511     EXPECT_EQ(buf.pos, 8);
   512     EXPECT_EQ(buf.capacity, 8);
   513     EXPECT_EQ(memcmp(buf.space, "preptest\0", 9), 0);
   514 }
   516 TEST_F(BufferWrite, PutStringWrapperExtend) {
   517     buf.flags |= CX_BUFFER_AUTO_EXTEND;
   518     const char *data = "testing";
   519     ASSERT_EQ(buf.capacity, 8);
   520     ASSERT_EQ(buf.pos, 4);
   521     ASSERT_EQ(buf.size, 4);
   522     size_t written = cxBufferPutString(&buf, data);
   523     EXPECT_EQ(written, 7);
   524     EXPECT_EQ(buf.size, 11);
   525     EXPECT_EQ(buf.pos, 11);
   526     EXPECT_GE(buf.capacity, 11);
   527     EXPECT_EQ(memcmp(buf.space, "preptesting", 11), 0);
   528 }
   530 TEST_F(BufferWrite, MultOverflow) {
   531     const char *data = "testing";
   532     ASSERT_EQ(buf.capacity, 8);
   533     ASSERT_EQ(buf.pos, 4);
   534     ASSERT_EQ(buf.size, 4);
   535     size_t written = cxBufferWrite(data, 8, SIZE_MAX / 4, &buf);
   536     EXPECT_EQ(written, 0);
   537     EXPECT_EQ(buf.capacity, 8);
   538     EXPECT_EQ(buf.pos, 4);
   539     EXPECT_EQ(buf.size, 4);
   540     EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0);
   541 }
   543 TEST_F(BufferWrite, MaxCapaOverflow) {
   544     buf.flags |= CX_BUFFER_AUTO_EXTEND;
   545     const char *data = "testing";
   546     ASSERT_EQ(buf.capacity, 8);
   547     ASSERT_EQ(buf.pos, 4);
   548     ASSERT_EQ(buf.size, 4);
   549     size_t written = cxBufferWrite(data, 1, SIZE_MAX - 2, &buf);
   550     EXPECT_EQ(written, 0);
   551     EXPECT_EQ(buf.capacity, 8);
   552     EXPECT_EQ(buf.pos, 4);
   553     EXPECT_EQ(buf.size, 4);
   554     EXPECT_EQ(memcmp(buf.space, "prep\0", 5), 0);
   555 }
   557 TEST_F(BufferWrite, OnlyOverwrite) {
   558     buf.flags |= CX_BUFFER_AUTO_EXTEND;
   559     ASSERT_EQ(buf.capacity, 8);
   560     memcpy(buf.space, "preptest", 8);
   561     buf.pos = 3;
   562     buf.size = 8;
   563     size_t written = cxBufferWrite("XXX", 2, 2, &buf);
   564     EXPECT_EQ(written, 2);
   565     EXPECT_EQ(buf.capacity, 8);
   566     EXPECT_EQ(buf.size, 8);
   567     EXPECT_EQ(buf.pos, 7);
   568     EXPECT_EQ(memcmp(buf.space, "preXXX\0t", 8), 0);
   569 }
   571 TEST_F(BufferWrite, FlushAtCapacity) {
   572     enableFlushing();
   573     ASSERT_EQ(buf.capacity, 8);
   574     ASSERT_EQ(buf.pos, 4);
   575     size_t written = cxBufferWrite("foo", 1, 3, &buf);
   576     EXPECT_EQ(written, 3);
   577     ASSERT_EQ(buf.pos, 7);
   578     ASSERT_EQ(buf.size, 7);
   579     ASSERT_EQ(target.pos, 0);
   580     ASSERT_EQ(target.size, 0);
   581     written = cxBufferWrite("hello", 1, 5, &buf);
   582     EXPECT_EQ(written, 5);
   583     EXPECT_EQ(buf.pos, 0);
   584     EXPECT_EQ(buf.size, 0);
   585     EXPECT_EQ(buf.capacity, 8);
   586     EXPECT_EQ(target.pos, 12);
   587     ASSERT_EQ(target.size, 12);
   588     EXPECT_EQ(memcmp(target.space, "prepfoohello", 12), 0);
   589 }
   591 TEST_F(BufferWrite, FlushAtThreshold) {
   592     enableFlushing();
   593     buf.flush_threshold = 12;
   594     buf.flags |= CX_BUFFER_AUTO_EXTEND;
   595     ASSERT_EQ(buf.capacity, 8);
   596     ASSERT_EQ(buf.pos, 4);
   597     size_t written = cxBufferWrite("foobar", 1, 6, &buf);
   598     EXPECT_EQ(written, 6);
   599     ASSERT_EQ(buf.pos, 10);
   600     ASSERT_EQ(buf.size, 10);
   601     ASSERT_GE(buf.capacity, 10);
   602     ASSERT_LE(buf.capacity, 12);
   603     ASSERT_EQ(target.pos, 0);
   604     ASSERT_EQ(target.size, 0);
   605     written = cxBufferWrite("hello", 1, 5, &buf);
   606     EXPECT_EQ(written, 5);
   607     EXPECT_EQ(buf.pos, 0);
   608     EXPECT_EQ(buf.size, 0);
   609     EXPECT_LE(buf.capacity, 12);
   610     EXPECT_EQ(target.pos, 15);
   611     ASSERT_EQ(target.size, 15);
   612     EXPECT_EQ(memcmp(target.space, "prepfoobarhello", 15), 0);
   613 }
   615 TEST_F(BufferWrite, FlushRateLimited) {
   616     enableFlushing();
   617     // limit the rate of the flush function and the capacity of the target
   618     target.capacity = 16;
   619     target.flags &= ~CX_BUFFER_AUTO_EXTEND;
   620     buf.flush_func = (cx_write_func) mock_write_limited_rate;
   621     ASSERT_EQ(buf.capacity, 8);
   622     ASSERT_EQ(buf.pos, 4);
   623     size_t written = cxBufferWrite("foo", 1, 3, &buf);
   624     EXPECT_EQ(written, 3);
   625     ASSERT_EQ(buf.pos, 7);
   626     ASSERT_EQ(buf.size, 7);
   627     ASSERT_EQ(target.pos, 0);
   628     ASSERT_EQ(target.size, 0);
   629     written = cxBufferWrite("hello, world!", 1, 13, &buf);
   630     // " world!" fits into this buffer, the remaining stuff is flushed out
   631     EXPECT_EQ(written, 13);
   632     EXPECT_EQ(buf.pos, 7);
   633     EXPECT_EQ(buf.size, 7);
   634     EXPECT_EQ(buf.capacity, 8);
   635     EXPECT_EQ(memcmp(buf.space, " world!", 7), 0);
   636     EXPECT_EQ(target.pos, 13);
   637     ASSERT_EQ(target.size, 13);
   638     EXPECT_EQ(target.capacity, 16);
   639     EXPECT_EQ(memcmp(target.space, "prepfoohello,", 13), 0);
   640 }
   642 class BufferSeek : public BufferFixture {
   643 };
   645 TEST_F(BufferSeek, SetZero) {
   646     int result = cxBufferSeek(&buf, 0, SEEK_SET);
   647     EXPECT_EQ(result, 0);
   648     EXPECT_EQ(buf.pos, 0);
   649 }
   651 TEST_F(BufferSeek, SetValid) {
   652     int result = cxBufferSeek(&buf, 5, SEEK_SET);
   653     EXPECT_EQ(result, 0);
   654     EXPECT_EQ(buf.pos, 5);
   655 }
   657 TEST_F(BufferSeek, SetInvalid) {
   658     ASSERT_EQ(buf.pos, 3);
   659     int result = cxBufferSeek(&buf, 6, SEEK_SET);
   660     EXPECT_NE(result, 0);
   661     EXPECT_EQ(buf.pos, 3);
   662 }
   664 TEST_F(BufferSeek, CurZero) {
   665     ASSERT_EQ(buf.pos, 3);
   666     int result = cxBufferSeek(&buf, 0, SEEK_CUR);
   667     EXPECT_EQ(result, 0);
   668     EXPECT_EQ(buf.pos, 3);
   669 }
   671 TEST_F(BufferSeek, CurValidPositive) {
   672     ASSERT_EQ(buf.pos, 3);
   673     int result = cxBufferSeek(&buf, 2, SEEK_CUR);
   674     EXPECT_EQ(result, 0);
   675     EXPECT_EQ(buf.pos, 5);
   676 }
   678 TEST_F(BufferSeek, CurValidNegative) {
   679     ASSERT_EQ(buf.pos, 3);
   680     int result = cxBufferSeek(&buf, -3, SEEK_CUR);
   681     EXPECT_EQ(result, 0);
   682     EXPECT_EQ(buf.pos, 0);
   683 }
   685 TEST_F(BufferSeek, CurInvalidPositive) {
   686     ASSERT_EQ(buf.pos, 3);
   687     int result = cxBufferSeek(&buf, 3, SEEK_CUR);
   688     EXPECT_NE(result, 0);
   689     EXPECT_EQ(buf.pos, 3);
   690 }
   692 TEST_F(BufferSeek, CurInvalidNegative) {
   693     ASSERT_EQ(buf.pos, 3);
   694     int result = cxBufferSeek(&buf, -4, SEEK_CUR);
   695     EXPECT_NE(result, 0);
   696     EXPECT_EQ(buf.pos, 3);
   697 }
   699 TEST_F(BufferSeek, EndZero) {
   700     ASSERT_EQ(buf.size, 6);
   701     int result = cxBufferSeek(&buf, 0, SEEK_END);
   702     // the (past-the-)end position is always invalid
   703     EXPECT_NE(result, 0);
   704     EXPECT_EQ(buf.pos, 3);
   705 }
   707 TEST_F(BufferSeek, EndValid) {
   708     ASSERT_EQ(buf.size, 6);
   709     int result = cxBufferSeek(&buf, -6, SEEK_END);
   710     EXPECT_EQ(result, 0);
   711     EXPECT_EQ(buf.pos, 0);
   712 }
   714 TEST_F(BufferSeek, EndInvalid) {
   715     ASSERT_EQ(buf.size, 6);
   716     int result = cxBufferSeek(&buf, 1, SEEK_END);
   717     EXPECT_NE(result, 0);
   718     EXPECT_EQ(buf.pos, 3);
   719 }
   721 TEST_F(BufferSeek, WhenceInvalid) {
   722     ASSERT_EQ(buf.size, 6);
   723     ASSERT_EQ(buf.pos, 3);
   724     int result = cxBufferSeek(&buf, 2, 9000);
   725     EXPECT_NE(result, 0);
   726     EXPECT_EQ(buf.size, 6);
   727     EXPECT_EQ(buf.pos, 3);
   728 }
   730 class BufferEof : public BufferFixture {
   731 };
   733 TEST_F(BufferEof, Reached) {
   734     buf.pos = buf.size;
   735     EXPECT_TRUE(cxBufferEof(&buf));
   736     buf.pos = buf.size - 1;
   737     ASSERT_FALSE(cxBufferEof(&buf));
   738     cxBufferPut(&buf, 'a');
   739     EXPECT_TRUE(cxBufferEof(&buf));
   740 }
   742 TEST_F(BufferEof, NotReached) {
   743     buf.pos = buf.size - 1;
   744     EXPECT_FALSE(cxBufferEof(&buf));
   745     buf.pos = 0;
   746     cxBufferWrite("test", 1, 5, &buf);
   747     EXPECT_FALSE(cxBufferEof(&buf));
   748 }
   750 class BufferRead : public ::testing::Test {
   751 protected:
   752     CxBuffer buf{};
   754     void SetUp() override {
   755         cxBufferInit(&buf, nullptr, 16, cxDefaultAllocator, CX_BUFFER_DEFAULT);
   756         buf.capacity = 8; // artificially reduce capacity to check OOB writes
   757         memset(buf.space, 0, 16);
   758         memcpy(buf.space, "some data", 9);
   759         buf.size = 9;
   760     }
   762     void TearDown() override {
   763         cxBufferDestroy(&buf);
   764     }
   765 };
   767 TEST_F(BufferRead, GetByte) {
   768     buf.pos = 2;
   769     EXPECT_EQ(cxBufferGet(&buf), 'm');
   770     EXPECT_EQ(cxBufferGet(&buf), 'e');
   771     EXPECT_EQ(cxBufferGet(&buf), ' ');
   772     EXPECT_EQ(cxBufferGet(&buf), 'd');
   773     EXPECT_EQ(buf.pos, 6);
   774 }
   776 TEST_F(BufferRead, GetEof) {
   777     buf.pos = buf.size;
   778     EXPECT_EQ(cxBufferGet(&buf), EOF);
   779 }
   781 TEST_F(BufferRead, ReadWithinBounds) {
   782     buf.pos = 2;
   783     char target[4];
   784     auto read = cxBufferRead(&target, 1, 4, &buf);
   785     ASSERT_EQ(read, 4);
   786     EXPECT_EQ(memcmp(&target, "me d", 4), 0);
   787     EXPECT_EQ(buf.pos, 6);
   788 }
   790 TEST_F(BufferRead, ReadOutOfBounds) {
   791     buf.pos = 6;
   792     char target[4];
   793     auto read = cxBufferRead(&target, 1, 4, &buf);
   794     ASSERT_EQ(read, 3);
   795     EXPECT_EQ(memcmp(&target, "ata", 3), 0);
   796     EXPECT_EQ(buf.pos, 9);
   797 }
   799 TEST_F(BufferRead, ReadOutOfBoundsMultibyte) {
   800     buf.pos = 6;
   801     char target[4];
   802     target[2] = '\0';
   803     auto read = cxBufferRead(&target, 2, 2, &buf);
   804     ASSERT_EQ(read, 1);
   805     EXPECT_EQ(memcmp(&target, "at\0", 3), 0);
   806     EXPECT_EQ(buf.pos, 8);
   807 }
   809 TEST_F(BufferRead, ReadEof) {
   810     buf.pos = 9;
   811     char target[4];
   812     auto read = cxBufferRead(&target, 1, 1, &buf);
   813     ASSERT_EQ(read, 0);
   814     EXPECT_EQ(buf.pos, 9);
   815 }

mercurial