| #include "test/jemalloc_test.h" |
| |
| #include "jemalloc/internal/buf_writer.h" |
| |
| #define TEST_BUF_SIZE 16 |
| #define UNIT_MAX (TEST_BUF_SIZE * 3) |
| |
| static size_t test_write_len; |
| static char test_buf[TEST_BUF_SIZE]; |
| static uint64_t arg; |
| static uint64_t arg_store; |
| |
| static void |
| test_write_cb(void *cbopaque, const char *s) { |
| size_t prev_test_write_len = test_write_len; |
| test_write_len += strlen(s); /* only increase the length */ |
| arg_store = *(uint64_t *)cbopaque; /* only pass along the argument */ |
| assert_zu_le(prev_test_write_len, test_write_len, |
| "Test write overflowed"); |
| } |
| |
| static void |
| test_buf_writer_body(tsdn_t *tsdn, buf_writer_t *buf_writer) { |
| char s[UNIT_MAX + 1]; |
| size_t n_unit, remain, i; |
| ssize_t unit; |
| |
| assert(buf_writer->buf != NULL); |
| memset(s, 'a', UNIT_MAX); |
| arg = 4; /* Starting value of random argument. */ |
| arg_store = arg; |
| for (unit = UNIT_MAX; unit >= 0; --unit) { |
| /* unit keeps decreasing, so strlen(s) is always unit. */ |
| s[unit] = '\0'; |
| for (n_unit = 1; n_unit <= 3; ++n_unit) { |
| test_write_len = 0; |
| remain = 0; |
| for (i = 1; i <= n_unit; ++i) { |
| arg = prng_lg_range_u64(&arg, 64); |
| buf_writer_cb(buf_writer, s); |
| remain += unit; |
| if (remain > buf_writer->buf_size) { |
| /* Flushes should have happened. */ |
| assert_u64_eq(arg_store, arg, "Call " |
| "back argument didn't get through"); |
| remain %= buf_writer->buf_size; |
| if (remain == 0) { |
| /* Last flush should be lazy. */ |
| remain += buf_writer->buf_size; |
| } |
| } |
| assert_zu_eq(test_write_len + remain, i * unit, |
| "Incorrect length after writing %zu strings" |
| " of length %zu", i, unit); |
| } |
| buf_writer_flush(buf_writer); |
| expect_zu_eq(test_write_len, n_unit * unit, |
| "Incorrect length after flushing at the end of" |
| " writing %zu strings of length %zu", n_unit, unit); |
| } |
| } |
| buf_writer_terminate(tsdn, buf_writer); |
| } |
| |
| TEST_BEGIN(test_buf_write_static) { |
| buf_writer_t buf_writer; |
| tsdn_t *tsdn = tsdn_fetch(); |
| assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, |
| test_buf, TEST_BUF_SIZE), |
| "buf_writer_init() should not encounter error on static buffer"); |
| test_buf_writer_body(tsdn, &buf_writer); |
| } |
| TEST_END |
| |
| TEST_BEGIN(test_buf_write_dynamic) { |
| buf_writer_t buf_writer; |
| tsdn_t *tsdn = tsdn_fetch(); |
| assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, |
| NULL, TEST_BUF_SIZE), "buf_writer_init() should not OOM"); |
| test_buf_writer_body(tsdn, &buf_writer); |
| } |
| TEST_END |
| |
| TEST_BEGIN(test_buf_write_oom) { |
| buf_writer_t buf_writer; |
| tsdn_t *tsdn = tsdn_fetch(); |
| assert_true(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, |
| NULL, SC_LARGE_MAXCLASS + 1), "buf_writer_init() should OOM"); |
| assert(buf_writer.buf == NULL); |
| |
| char s[UNIT_MAX + 1]; |
| size_t n_unit, i; |
| ssize_t unit; |
| |
| memset(s, 'a', UNIT_MAX); |
| arg = 4; /* Starting value of random argument. */ |
| arg_store = arg; |
| for (unit = UNIT_MAX; unit >= 0; unit -= UNIT_MAX / 4) { |
| /* unit keeps decreasing, so strlen(s) is always unit. */ |
| s[unit] = '\0'; |
| for (n_unit = 1; n_unit <= 3; ++n_unit) { |
| test_write_len = 0; |
| for (i = 1; i <= n_unit; ++i) { |
| arg = prng_lg_range_u64(&arg, 64); |
| buf_writer_cb(&buf_writer, s); |
| assert_u64_eq(arg_store, arg, |
| "Call back argument didn't get through"); |
| assert_zu_eq(test_write_len, i * unit, |
| "Incorrect length after writing %zu strings" |
| " of length %zu", i, unit); |
| } |
| buf_writer_flush(&buf_writer); |
| expect_zu_eq(test_write_len, n_unit * unit, |
| "Incorrect length after flushing at the end of" |
| " writing %zu strings of length %zu", n_unit, unit); |
| } |
| } |
| buf_writer_terminate(tsdn, &buf_writer); |
| } |
| TEST_END |
| |
| static int test_read_count; |
| static size_t test_read_len; |
| static uint64_t arg_sum; |
| |
| ssize_t |
| test_read_cb(void *cbopaque, void *buf, size_t limit) { |
| static uint64_t rand = 4; |
| |
| arg_sum += *(uint64_t *)cbopaque; |
| assert_zu_gt(limit, 0, "Limit for read_cb must be positive"); |
| --test_read_count; |
| if (test_read_count == 0) { |
| return -1; |
| } else { |
| size_t read_len = limit; |
| if (limit > 1) { |
| rand = prng_range_u64(&rand, (uint64_t)limit); |
| read_len -= (size_t)rand; |
| } |
| assert(read_len > 0); |
| memset(buf, 'a', read_len); |
| size_t prev_test_read_len = test_read_len; |
| test_read_len += read_len; |
| assert_zu_le(prev_test_read_len, test_read_len, |
| "Test read overflowed"); |
| return read_len; |
| } |
| } |
| |
| static void |
| test_buf_writer_pipe_body(tsdn_t *tsdn, buf_writer_t *buf_writer) { |
| arg = 4; /* Starting value of random argument. */ |
| for (int count = 5; count > 0; --count) { |
| arg = prng_lg_range_u64(&arg, 64); |
| arg_sum = 0; |
| test_read_count = count; |
| test_read_len = 0; |
| test_write_len = 0; |
| buf_writer_pipe(buf_writer, test_read_cb, &arg); |
| assert(test_read_count == 0); |
| expect_u64_eq(arg_sum, arg * count, ""); |
| expect_zu_eq(test_write_len, test_read_len, |
| "Write length should be equal to read length"); |
| } |
| buf_writer_terminate(tsdn, buf_writer); |
| } |
| |
| TEST_BEGIN(test_buf_write_pipe) { |
| buf_writer_t buf_writer; |
| tsdn_t *tsdn = tsdn_fetch(); |
| assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, |
| test_buf, TEST_BUF_SIZE), |
| "buf_writer_init() should not encounter error on static buffer"); |
| test_buf_writer_pipe_body(tsdn, &buf_writer); |
| } |
| TEST_END |
| |
| TEST_BEGIN(test_buf_write_pipe_oom) { |
| buf_writer_t buf_writer; |
| tsdn_t *tsdn = tsdn_fetch(); |
| assert_true(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, |
| NULL, SC_LARGE_MAXCLASS + 1), "buf_writer_init() should OOM"); |
| test_buf_writer_pipe_body(tsdn, &buf_writer); |
| } |
| TEST_END |
| |
| int |
| main(void) { |
| return test( |
| test_buf_write_static, |
| test_buf_write_dynamic, |
| test_buf_write_oom, |
| test_buf_write_pipe, |
| test_buf_write_pipe_oom); |
| } |