xref: /netbsd-src/external/bsd/jemalloc/dist/test/unit/buf_writer.c (revision 7bdf38e5b7a28439665f2fdeff81e36913eef7dd)
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