1 1.1 christos #include "test/jemalloc_test.h" 2 1.1 christos 3 1.1 christos #include "jemalloc/internal/buf_writer.h" 4 1.1 christos 5 1.1 christos #define TEST_BUF_SIZE 16 6 1.1 christos #define UNIT_MAX (TEST_BUF_SIZE * 3) 7 1.1 christos 8 1.1 christos static size_t test_write_len; 9 1.1 christos static char test_buf[TEST_BUF_SIZE]; 10 1.1 christos static uint64_t arg; 11 1.1 christos static uint64_t arg_store; 12 1.1 christos 13 1.1 christos static void 14 1.1 christos test_write_cb(void *cbopaque, const char *s) { 15 1.1 christos size_t prev_test_write_len = test_write_len; 16 1.1 christos test_write_len += strlen(s); /* only increase the length */ 17 1.1 christos arg_store = *(uint64_t *)cbopaque; /* only pass along the argument */ 18 1.1 christos assert_zu_le(prev_test_write_len, test_write_len, 19 1.1 christos "Test write overflowed"); 20 1.1 christos } 21 1.1 christos 22 1.1 christos static void 23 1.1 christos test_buf_writer_body(tsdn_t *tsdn, buf_writer_t *buf_writer) { 24 1.1 christos char s[UNIT_MAX + 1]; 25 1.1 christos size_t n_unit, remain, i; 26 1.1 christos ssize_t unit; 27 1.1 christos 28 1.1 christos assert(buf_writer->buf != NULL); 29 1.1 christos memset(s, 'a', UNIT_MAX); 30 1.1 christos arg = 4; /* Starting value of random argument. */ 31 1.1 christos arg_store = arg; 32 1.1 christos for (unit = UNIT_MAX; unit >= 0; --unit) { 33 1.1 christos /* unit keeps decreasing, so strlen(s) is always unit. */ 34 1.1 christos s[unit] = '\0'; 35 1.1 christos for (n_unit = 1; n_unit <= 3; ++n_unit) { 36 1.1 christos test_write_len = 0; 37 1.1 christos remain = 0; 38 1.1 christos for (i = 1; i <= n_unit; ++i) { 39 1.1 christos arg = prng_lg_range_u64(&arg, 64); 40 1.1 christos buf_writer_cb(buf_writer, s); 41 1.1 christos remain += unit; 42 1.1 christos if (remain > buf_writer->buf_size) { 43 1.1 christos /* Flushes should have happened. */ 44 1.1 christos assert_u64_eq(arg_store, arg, "Call " 45 1.1 christos "back argument didn't get through"); 46 1.1 christos remain %= buf_writer->buf_size; 47 1.1 christos if (remain == 0) { 48 1.1 christos /* Last flush should be lazy. */ 49 1.1 christos remain += buf_writer->buf_size; 50 1.1 christos } 51 1.1 christos } 52 1.1 christos assert_zu_eq(test_write_len + remain, i * unit, 53 1.1 christos "Incorrect length after writing %zu strings" 54 1.1 christos " of length %zu", i, unit); 55 1.1 christos } 56 1.1 christos buf_writer_flush(buf_writer); 57 1.1 christos expect_zu_eq(test_write_len, n_unit * unit, 58 1.1 christos "Incorrect length after flushing at the end of" 59 1.1 christos " writing %zu strings of length %zu", n_unit, unit); 60 1.1 christos } 61 1.1 christos } 62 1.1 christos buf_writer_terminate(tsdn, buf_writer); 63 1.1 christos } 64 1.1 christos 65 1.1 christos TEST_BEGIN(test_buf_write_static) { 66 1.1 christos buf_writer_t buf_writer; 67 1.1 christos tsdn_t *tsdn = tsdn_fetch(); 68 1.1 christos assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, 69 1.1 christos test_buf, TEST_BUF_SIZE), 70 1.1 christos "buf_writer_init() should not encounter error on static buffer"); 71 1.1 christos test_buf_writer_body(tsdn, &buf_writer); 72 1.1 christos } 73 1.1 christos TEST_END 74 1.1 christos 75 1.1 christos TEST_BEGIN(test_buf_write_dynamic) { 76 1.1 christos buf_writer_t buf_writer; 77 1.1 christos tsdn_t *tsdn = tsdn_fetch(); 78 1.1 christos assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, 79 1.1 christos NULL, TEST_BUF_SIZE), "buf_writer_init() should not OOM"); 80 1.1 christos test_buf_writer_body(tsdn, &buf_writer); 81 1.1 christos } 82 1.1 christos TEST_END 83 1.1 christos 84 1.1 christos TEST_BEGIN(test_buf_write_oom) { 85 1.1 christos buf_writer_t buf_writer; 86 1.1 christos tsdn_t *tsdn = tsdn_fetch(); 87 1.1 christos assert_true(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, 88 1.1 christos NULL, SC_LARGE_MAXCLASS + 1), "buf_writer_init() should OOM"); 89 1.1 christos assert(buf_writer.buf == NULL); 90 1.1 christos 91 1.1 christos char s[UNIT_MAX + 1]; 92 1.1 christos size_t n_unit, i; 93 1.1 christos ssize_t unit; 94 1.1 christos 95 1.1 christos memset(s, 'a', UNIT_MAX); 96 1.1 christos arg = 4; /* Starting value of random argument. */ 97 1.1 christos arg_store = arg; 98 1.1 christos for (unit = UNIT_MAX; unit >= 0; unit -= UNIT_MAX / 4) { 99 1.1 christos /* unit keeps decreasing, so strlen(s) is always unit. */ 100 1.1 christos s[unit] = '\0'; 101 1.1 christos for (n_unit = 1; n_unit <= 3; ++n_unit) { 102 1.1 christos test_write_len = 0; 103 1.1 christos for (i = 1; i <= n_unit; ++i) { 104 1.1 christos arg = prng_lg_range_u64(&arg, 64); 105 1.1 christos buf_writer_cb(&buf_writer, s); 106 1.1 christos assert_u64_eq(arg_store, arg, 107 1.1 christos "Call back argument didn't get through"); 108 1.1 christos assert_zu_eq(test_write_len, i * unit, 109 1.1 christos "Incorrect length after writing %zu strings" 110 1.1 christos " of length %zu", i, unit); 111 1.1 christos } 112 1.1 christos buf_writer_flush(&buf_writer); 113 1.1 christos expect_zu_eq(test_write_len, n_unit * unit, 114 1.1 christos "Incorrect length after flushing at the end of" 115 1.1 christos " writing %zu strings of length %zu", n_unit, unit); 116 1.1 christos } 117 1.1 christos } 118 1.1 christos buf_writer_terminate(tsdn, &buf_writer); 119 1.1 christos } 120 1.1 christos TEST_END 121 1.1 christos 122 1.1 christos static int test_read_count; 123 1.1 christos static size_t test_read_len; 124 1.1 christos static uint64_t arg_sum; 125 1.1 christos 126 1.1 christos ssize_t 127 1.1 christos test_read_cb(void *cbopaque, void *buf, size_t limit) { 128 1.1 christos static uint64_t rand = 4; 129 1.1 christos 130 1.1 christos arg_sum += *(uint64_t *)cbopaque; 131 1.1 christos assert_zu_gt(limit, 0, "Limit for read_cb must be positive"); 132 1.1 christos --test_read_count; 133 1.1 christos if (test_read_count == 0) { 134 1.1 christos return -1; 135 1.1 christos } else { 136 1.1 christos size_t read_len = limit; 137 1.1 christos if (limit > 1) { 138 1.1 christos rand = prng_range_u64(&rand, (uint64_t)limit); 139 1.1 christos read_len -= (size_t)rand; 140 1.1 christos } 141 1.1 christos assert(read_len > 0); 142 1.1 christos memset(buf, 'a', read_len); 143 1.1 christos size_t prev_test_read_len = test_read_len; 144 1.1 christos test_read_len += read_len; 145 1.1 christos assert_zu_le(prev_test_read_len, test_read_len, 146 1.1 christos "Test read overflowed"); 147 1.1 christos return read_len; 148 1.1 christos } 149 1.1 christos } 150 1.1 christos 151 1.1 christos static void 152 1.1 christos test_buf_writer_pipe_body(tsdn_t *tsdn, buf_writer_t *buf_writer) { 153 1.1 christos arg = 4; /* Starting value of random argument. */ 154 1.1 christos for (int count = 5; count > 0; --count) { 155 1.1 christos arg = prng_lg_range_u64(&arg, 64); 156 1.1 christos arg_sum = 0; 157 1.1 christos test_read_count = count; 158 1.1 christos test_read_len = 0; 159 1.1 christos test_write_len = 0; 160 1.1 christos buf_writer_pipe(buf_writer, test_read_cb, &arg); 161 1.1 christos assert(test_read_count == 0); 162 1.1 christos expect_u64_eq(arg_sum, arg * count, ""); 163 1.1 christos expect_zu_eq(test_write_len, test_read_len, 164 1.1 christos "Write length should be equal to read length"); 165 1.1 christos } 166 1.1 christos buf_writer_terminate(tsdn, buf_writer); 167 1.1 christos } 168 1.1 christos 169 1.1 christos TEST_BEGIN(test_buf_write_pipe) { 170 1.1 christos buf_writer_t buf_writer; 171 1.1 christos tsdn_t *tsdn = tsdn_fetch(); 172 1.1 christos assert_false(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, 173 1.1 christos test_buf, TEST_BUF_SIZE), 174 1.1 christos "buf_writer_init() should not encounter error on static buffer"); 175 1.1 christos test_buf_writer_pipe_body(tsdn, &buf_writer); 176 1.1 christos } 177 1.1 christos TEST_END 178 1.1 christos 179 1.1 christos TEST_BEGIN(test_buf_write_pipe_oom) { 180 1.1 christos buf_writer_t buf_writer; 181 1.1 christos tsdn_t *tsdn = tsdn_fetch(); 182 1.1 christos assert_true(buf_writer_init(tsdn, &buf_writer, test_write_cb, &arg, 183 1.1 christos NULL, SC_LARGE_MAXCLASS + 1), "buf_writer_init() should OOM"); 184 1.1 christos test_buf_writer_pipe_body(tsdn, &buf_writer); 185 1.1 christos } 186 1.1 christos TEST_END 187 1.1 christos 188 1.1 christos int 189 1.1 christos main(void) { 190 1.1 christos return test( 191 1.1 christos test_buf_write_static, 192 1.1 christos test_buf_write_dynamic, 193 1.1 christos test_buf_write_oom, 194 1.1 christos test_buf_write_pipe, 195 1.1 christos test_buf_write_pipe_oom); 196 1.1 christos } 197