unit2601.c (7399B)
1 /*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 9 * 10 * This software is licensed as described in the file COPYING, which 11 * you should have received as part of this distribution. The terms 12 * are also available at https://curl.se/docs/copyright.html. 13 * 14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell 15 * copies of the Software, and permit persons to whom the Software is 16 * furnished to do so, under the terms of the COPYING file. 17 * 18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 19 * KIND, either express or implied. 20 * 21 * SPDX-License-Identifier: curl 22 * 23 ***************************************************************************/ 24 #include "unitcheck.h" 25 26 #include "urldata.h" 27 #include "bufq.h" 28 #include "curl_trc.h" 29 30 static const char *tail_err(struct bufq *q) 31 { 32 struct buf_chunk *chunk; 33 34 if(!q->tail) { 35 return q->head ? "tail is NULL, but head is not" : NULL; 36 } 37 38 chunk = q->head; 39 while(chunk) { 40 if(chunk == q->tail) { 41 if(chunk->next) { 42 return "tail points to queue, but not at the end"; 43 } 44 return NULL; 45 } 46 chunk = chunk->next; 47 } 48 return "tail not part of queue"; 49 } 50 51 static void dump_bufq(struct bufq *q, const char *msg) 52 { 53 struct buf_chunk *chunk; 54 const char *terr; 55 size_t n; 56 57 curl_mfprintf(stderr, "bufq[chunk_size=%zu, max_chunks=%zu] %s\n", 58 q->chunk_size, q->max_chunks, msg); 59 curl_mfprintf(stderr, "- queue[\n"); 60 chunk = q->head; 61 while(chunk) { 62 curl_mfprintf(stderr, " chunk[len=%zu, roff=%zu, woff=%zu]\n", 63 chunk->dlen, chunk->r_offset, chunk->w_offset); 64 chunk = chunk->next; 65 } 66 curl_mfprintf(stderr, " ]\n"); 67 terr = tail_err(q); 68 curl_mfprintf(stderr, "- tail: %s\n", terr ? terr : "ok"); 69 n = 0; 70 chunk = q->spare; 71 while(chunk) { 72 ++n; 73 chunk = chunk->next; 74 } 75 curl_mfprintf(stderr, "- chunks: %zu\n", q->chunk_count); 76 curl_mfprintf(stderr, "- spares: %zu\n", n); 77 } 78 79 static void check_bufq(size_t pool_spares, 80 size_t chunk_size, size_t max_chunks, 81 size_t wsize, size_t rsize, int opts) 82 { 83 static unsigned char test_data[32*1024]; 84 85 struct bufq q; 86 struct bufc_pool pool; 87 size_t max_len = chunk_size * max_chunks; 88 CURLcode result; 89 ssize_t i; 90 size_t n2; 91 size_t nwritten, nread; 92 93 if(pool_spares > 0) { 94 Curl_bufcp_init(&pool, chunk_size, pool_spares); 95 Curl_bufq_initp(&q, &pool, max_chunks, opts); 96 } 97 else { 98 Curl_bufq_init2(&q, chunk_size, max_chunks, opts); 99 } 100 101 fail_unless(q.chunk_size == chunk_size, "chunk_size init wrong"); 102 fail_unless(q.max_chunks == max_chunks, "max_chunks init wrong"); 103 fail_unless(q.head == NULL, "init: head not NULL"); 104 fail_unless(q.tail == NULL, "init: tail not NULL"); 105 fail_unless(q.spare == NULL, "init: spare not NULL"); 106 fail_unless(Curl_bufq_len(&q) == 0, "init: bufq length != 0"); 107 108 result = Curl_bufq_write(&q, test_data, wsize, &n2); 109 fail_unless(n2 <= wsize, "write: wrong size returned"); 110 fail_unless(result == CURLE_OK, "write: wrong result returned"); 111 112 /* write empty bufq full */ 113 nwritten = 0; 114 Curl_bufq_reset(&q); 115 while(!Curl_bufq_is_full(&q)) { 116 result = Curl_bufq_write(&q, test_data, wsize, &n2); 117 if(!result) { 118 nwritten += n2; 119 } 120 else if(result != CURLE_AGAIN) { 121 fail_unless(result == CURLE_AGAIN, "write-loop: unexpected result"); 122 break; 123 } 124 } 125 if(nwritten != max_len) { 126 curl_mfprintf(stderr, "%zu bytes written, but max_len=%zu\n", 127 nwritten, max_len); 128 dump_bufq(&q, "after writing full"); 129 fail_if(TRUE, "write: bufq full but nwritten wrong"); 130 } 131 132 /* read full bufq empty */ 133 nread = 0; 134 while(!Curl_bufq_is_empty(&q)) { 135 result = Curl_bufq_read(&q, test_data, rsize, &n2); 136 if(!result) { 137 nread += n2; 138 } 139 else if(result != CURLE_AGAIN) { 140 fail_unless(result == CURLE_AGAIN, "read-loop: unexpected result"); 141 break; 142 } 143 } 144 if(nread != max_len) { 145 curl_mfprintf(stderr, "%zu bytes read, but max_len=%zu\n", 146 nwritten, max_len); 147 dump_bufq(&q, "after reading empty"); 148 fail_if(TRUE, "read: bufq empty but nread wrong"); 149 } 150 if(q.tail) { 151 dump_bufq(&q, "after reading empty"); 152 fail_if(TRUE, "read empty, but tail is not NULL"); 153 } 154 155 for(i = 0; i < 1000; ++i) { 156 result = Curl_bufq_write(&q, test_data, wsize, &n2); 157 if(result && result != CURLE_AGAIN) { 158 fail_unless(result == CURLE_AGAIN, "rw-loop: unexpected write result"); 159 break; 160 } 161 result = Curl_bufq_read(&q, test_data, rsize, &n2); 162 if(result && result != CURLE_AGAIN) { 163 fail_unless(result == CURLE_AGAIN, "rw-loop: unexpected read result"); 164 break; 165 } 166 } 167 168 /* Test SOFT_LIMIT option */ 169 Curl_bufq_free(&q); 170 Curl_bufq_init2(&q, chunk_size, max_chunks, (opts|BUFQ_OPT_SOFT_LIMIT)); 171 nwritten = 0; 172 while(!Curl_bufq_is_full(&q)) { 173 result = Curl_bufq_write(&q, test_data, wsize, &n2); 174 if(result || n2 != wsize) { 175 fail_unless(!result && n2 == wsize, "write should be complete"); 176 break; 177 } 178 nwritten += n2; 179 } 180 if(nwritten < max_len) { 181 curl_mfprintf(stderr, "%zu bytes written, but max_len=%zu\n", 182 nwritten, max_len); 183 dump_bufq(&q, "after writing full"); 184 fail_if(TRUE, "write: bufq full but nwritten wrong"); 185 } 186 /* do one more write on a full bufq, should work */ 187 result = Curl_bufq_write(&q, test_data, wsize, &n2); 188 fail_unless(!result && n2 == wsize, "write should be complete"); 189 nwritten += n2; 190 /* see that we get all out again */ 191 nread = 0; 192 while(!Curl_bufq_is_empty(&q)) { 193 result = Curl_bufq_read(&q, test_data, rsize, &n2); 194 if(result) { 195 fail_unless(result, "read-loop: unexpected fail"); 196 break; 197 } 198 nread += n2; 199 } 200 fail_unless(nread == nwritten, "did not get the same out as put in"); 201 202 dump_bufq(&q, "at end of test"); 203 Curl_bufq_free(&q); 204 if(pool_spares > 0) 205 Curl_bufcp_free(&pool); 206 } 207 208 static CURLcode test_unit2601(char *arg) 209 { 210 UNITTEST_BEGIN_SIMPLE 211 212 struct bufq q; 213 size_t n; 214 CURLcode result; 215 unsigned char buf[16*1024]; 216 217 Curl_bufq_init(&q, 8*1024, 12); 218 result = Curl_bufq_read(&q, buf, 128, &n); 219 fail_unless(result && result == CURLE_AGAIN, "read empty fail"); 220 Curl_bufq_free(&q); 221 222 check_bufq(0, 1024, 4, 128, 128, BUFQ_OPT_NONE); 223 check_bufq(0, 1024, 4, 129, 127, BUFQ_OPT_NONE); 224 check_bufq(0, 1024, 4, 2000, 16000, BUFQ_OPT_NONE); 225 check_bufq(0, 1024, 4, 16000, 3000, BUFQ_OPT_NONE); 226 227 check_bufq(0, 8000, 10, 1234, 1234, BUFQ_OPT_NONE); 228 check_bufq(0, 8000, 10, 8*1024, 4*1024, BUFQ_OPT_NONE); 229 230 check_bufq(0, 1024, 4, 128, 128, BUFQ_OPT_NO_SPARES); 231 check_bufq(0, 1024, 4, 129, 127, BUFQ_OPT_NO_SPARES); 232 check_bufq(0, 1024, 4, 2000, 16000, BUFQ_OPT_NO_SPARES); 233 check_bufq(0, 1024, 4, 16000, 3000, BUFQ_OPT_NO_SPARES); 234 235 check_bufq(8, 1024, 4, 128, 128, BUFQ_OPT_NONE); 236 check_bufq(8, 8000, 10, 1234, 1234, BUFQ_OPT_NONE); 237 check_bufq(8, 1024, 4, 129, 127, BUFQ_OPT_NO_SPARES); 238 239 UNITTEST_END_SIMPLE 240 }