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