1/*** 2 This file is part of PulseAudio. 3 4 PulseAudio is free software; you can redistribute it and/or modify 5 it under the terms of the GNU Lesser General Public License as published 6 by the Free Software Foundation; either version 2.1 of the License, 7 or (at your option) any later version. 8 9 PulseAudio is distributed in the hope that it will be useful, but 10 WITHOUT ANY WARRANTY; without even the implied warranty of 11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 General Public License for more details. 13 14 You should have received a copy of the GNU Lesser General Public License 15 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 16***/ 17 18#ifdef HAVE_CONFIG_H 19#include <config.h> 20#endif 21 22#include <stdlib.h> 23#include <stdio.h> 24#include <signal.h> 25 26#include <check.h> 27 28#include <pulsecore/memblockq.h> 29#include <pulsecore/log.h> 30#include <pulsecore/macro.h> 31#include <pulsecore/strbuf.h> 32#include <pulsecore/core-util.h> 33 34#include <pulse/xmalloc.h> 35 36static const char *fixed[] = { 37 "1122444411441144__22__11______3333______________________________", 38 "__________________3333__________________________________________" 39}; 40static const char *manual[] = { 41 "1122444411441144__22__11______3333______________________________", 42 "__________________3333______________________________" 43}; 44 45/* 46 * utility function to create a memchunk 47 */ 48static pa_memchunk memchunk_from_str(pa_mempool *p, const char* data) 49{ 50 pa_memchunk res; 51 size_t size = strlen(data); 52 53 res.memblock = pa_memblock_new_fixed(p, (void*)data, size, true); 54 ck_assert_ptr_ne(res.memblock, NULL); 55 56 res.index = 0; 57 res.length = pa_memblock_get_length(res.memblock); 58 59 return res; 60} 61 62static void dump_chunk(const pa_memchunk *chunk, pa_strbuf *buf) { 63 size_t n; 64 void *q; 65 char *e; 66 67 fail_unless(chunk != NULL); 68 69 q = pa_memblock_acquire(chunk->memblock); 70 for (e = (char*) q + chunk->index, n = 0; n < chunk->length; n++, e++) { 71 fprintf(stderr, "%c", *e); 72 pa_strbuf_putc(buf, *e); 73 } 74 pa_memblock_release(chunk->memblock); 75} 76 77static void dump(pa_memblockq *bq, int n) { 78 pa_memchunk out; 79 pa_strbuf *buf; 80 char *str; 81 82 pa_assert(bq); 83 84 /* First let's dump this as fixed block */ 85 fprintf(stderr, "FIXED >"); 86 pa_memblockq_peek_fixed_size(bq, 64, &out); 87 buf = pa_strbuf_new(); 88 dump_chunk(&out, buf); 89 pa_memblock_unref(out.memblock); 90 str = pa_strbuf_to_string_free(buf); 91 fail_unless(pa_streq(str, fixed[n])); 92 pa_xfree(str); 93 fprintf(stderr, "<\n"); 94 95 /* Then let's dump the queue manually */ 96 fprintf(stderr, "MANUAL>"); 97 98 buf = pa_strbuf_new(); 99 for (;;) { 100 if (pa_memblockq_peek(bq, &out) < 0) 101 break; 102 103 dump_chunk(&out, buf); 104 pa_memblock_unref(out.memblock); 105 pa_memblockq_drop(bq, out.length); 106 } 107 str = pa_strbuf_to_string_free(buf); 108 fail_unless(pa_streq(str, manual[n])); 109 pa_xfree(str); 110 fprintf(stderr, "<\n"); 111} 112 113/* 114 * utility function to validate invariants 115 * 116 * The different values like base, maxlength etc follow certain rules. 117 * This convenience function makes sure that changes don't violate 118 * these rules. 119 */ 120static void check_queue_invariants(pa_memblockq *bq) { 121 size_t base = pa_memblockq_get_base(bq); 122 size_t maxlength = pa_memblockq_get_maxlength(bq); 123 size_t tlength = pa_memblockq_get_tlength(bq); 124 size_t minreq = pa_memblockq_get_minreq(bq); 125 size_t prebuf = pa_memblockq_get_prebuf(bq); 126 size_t length = pa_memblockq_get_length(bq); 127 128 /* base > zero */ 129 ck_assert_int_gt(base, 0); 130 131 /* maxlength multiple of base 132 * maxlength >= base */ 133 ck_assert_int_eq(maxlength % base, 0); 134 ck_assert_int_ge(maxlength, base); 135 136 /* tlength multiple of base 137 * tlength >= base 138 * tlength <= maxlength */ 139 ck_assert_int_eq(tlength % base, 0); 140 ck_assert_int_ge(tlength, base); 141 ck_assert_int_le(tlength, maxlength); 142 143 /* minreq multiple of base 144 * minreq >= base 145 * minreq <= tlength */ 146 ck_assert_int_eq(minreq % base, 0); 147 ck_assert_int_ge(minreq, base); 148 ck_assert_int_le(minreq, tlength); 149 150 /* prebuf multiple of base 151 * prebuf >= 0 152 * prebuf <= tlength + base - minreq 153 * prebuf <= tlength (because minreq >= base) */ 154 ck_assert_int_eq(prebuf % base, 0); 155 ck_assert_int_ge(prebuf, 0); 156 ck_assert_int_le(prebuf, tlength + base - minreq); 157 ck_assert_int_le(prebuf, tlength); 158 159 /* length >= 0 160 * length <= maxlength */ 161 ck_assert_int_ge(length, 0); 162 ck_assert_int_le(length, maxlength); 163} 164 165START_TEST (memchunk_from_str_test) { 166 pa_mempool *p; 167 pa_memchunk chunk; 168 169 p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true); 170 ck_assert_ptr_ne(p, NULL); 171 172 /* allocate memchunk and check default settings */ 173 chunk = memchunk_from_str(p, "abcd"); 174 ck_assert_ptr_ne(chunk.memblock, NULL); 175 ck_assert_int_eq(chunk.index, 0); 176 ck_assert_int_eq(chunk.length, 4); 177 178 /* cleanup */ 179 pa_memblock_unref(chunk.memblock); 180 pa_mempool_unref(p); 181} 182END_TEST 183 184START_TEST (memblockq_test_initial_properties) { 185 pa_mempool *p; 186 pa_memblockq *bq; 187 pa_memchunk silence; 188 pa_sample_spec ss = { 189 .format = PA_SAMPLE_S32BE, 190 .rate = 48000, 191 .channels = 1 192 }; 193 int64_t idx = 0; 194 size_t maxlength = 100; 195 size_t tlength = 20; 196 size_t prebuf = 16; 197 size_t minreq = 8; 198 size_t maxrewind = 40; 199 200 p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true); 201 ck_assert_ptr_ne(p, NULL); 202 203 silence = memchunk_from_str(p, "__"); 204 205 bq = pa_memblockq_new("test memblockq", idx, maxlength, tlength, &ss, prebuf, minreq, maxrewind, &silence); 206 fail_unless(bq != NULL); 207 208 /* check initial properties */ 209 ck_assert_int_eq(pa_memblockq_is_readable(bq), false); 210 ck_assert_int_eq(pa_memblockq_get_length(bq), 0); 211 ck_assert_int_eq(pa_memblockq_get_maxlength(bq), maxlength); 212 ck_assert_int_eq(pa_memblockq_get_tlength(bq), tlength); 213 ck_assert_int_eq(pa_memblockq_get_prebuf(bq), prebuf); 214 ck_assert_int_eq(pa_memblockq_get_minreq(bq), minreq); 215 ck_assert_int_eq(pa_memblockq_get_maxrewind(bq), maxrewind); 216 ck_assert_int_eq(pa_memblockq_get_base(bq), pa_frame_size(&ss)); 217 ck_assert_int_eq(pa_memblockq_get_read_index(bq), 0); 218 ck_assert_int_eq(pa_memblockq_get_write_index(bq), 0); 219 220 check_queue_invariants(bq); 221 222 /* Check reporting of missing bytes: 223 * Initially, tlength bytes are missing. The second call doesn't 224 * report additional missing data since the first call. */ 225 ck_assert_int_eq(pa_memblockq_pop_missing(bq), tlength); 226 ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0); 227 228 /* cleanup */ 229 pa_memblockq_free(bq); 230 pa_memblock_unref(silence.memblock); 231 pa_mempool_unref(p); 232} 233END_TEST 234 235START_TEST (memblockq_test) { 236 int ret; 237 238 pa_mempool *p; 239 pa_memblockq *bq; 240 pa_memchunk chunk1, chunk2, chunk3, chunk4; 241 pa_memchunk silence; 242 pa_sample_spec ss = { 243 .format = PA_SAMPLE_S16LE, 244 .rate = 48000, 245 .channels = 1 246 }; 247 248 p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true); 249 ck_assert_ptr_ne(p, NULL); 250 251 silence = memchunk_from_str(p, "__"); 252 253 bq = pa_memblockq_new("test memblockq", 0, 200, 10, &ss, 4, 4, 40, &silence); 254 fail_unless(bq != NULL); 255 check_queue_invariants(bq); 256 257 chunk1 = memchunk_from_str(p, "11"); 258 chunk2 = memchunk_from_str(p, "XX22"); 259 chunk2.index += 2; 260 chunk2.length -= 2; 261 chunk3 = memchunk_from_str(p, "3333"); 262 chunk4 = memchunk_from_str(p, "44444444"); 263 264 ret = pa_memblockq_push(bq, &chunk1); 265 fail_unless(ret == 0); 266 267 ret = pa_memblockq_push(bq, &chunk2); 268 fail_unless(ret == 0); 269 270 ret = pa_memblockq_push(bq, &chunk3); 271 fail_unless(ret == 0); 272 273 ret = pa_memblockq_push(bq, &chunk4); 274 fail_unless(ret == 0); 275 276 check_queue_invariants(bq); 277 278 pa_memblockq_seek(bq, -6, 0, true); 279 ret = pa_memblockq_push(bq, &chunk3); 280 fail_unless(ret == 0); 281 282 pa_memblockq_seek(bq, -2, 0, true); 283 ret = pa_memblockq_push(bq, &chunk1); 284 fail_unless(ret == 0); 285 286 pa_memblockq_seek(bq, -10, 0, true); 287 ret = pa_memblockq_push(bq, &chunk4); 288 fail_unless(ret == 0); 289 290 pa_memblockq_seek(bq, 10, 0, true); 291 292 ret = pa_memblockq_push(bq, &chunk1); 293 fail_unless(ret == 0); 294 295 pa_memblockq_seek(bq, -6, 0, true); 296 ret = pa_memblockq_push(bq, &chunk2); 297 fail_unless(ret == 0); 298 299 /* Test splitting */ 300 pa_memblockq_seek(bq, -12, 0, true); 301 ret = pa_memblockq_push(bq, &chunk1); 302 fail_unless(ret == 0); 303 304 pa_memblockq_seek(bq, 20, 0, true); 305 306 /* Test merging */ 307 ret = pa_memblockq_push(bq, &chunk3); 308 fail_unless(ret == 0); 309 pa_memblockq_seek(bq, -2, 0, true); 310 311 chunk3.index += 2; 312 chunk3.length -= 2; 313 ret = pa_memblockq_push(bq, &chunk3); 314 fail_unless(ret == 0); 315 316 pa_memblockq_seek(bq, 30, PA_SEEK_RELATIVE, true); 317 318 dump(bq, 0); 319 320 pa_memblockq_rewind(bq, 52); 321 322 dump(bq, 1); 323 324 check_queue_invariants(bq); 325 326 pa_memblockq_free(bq); 327 pa_memblock_unref(silence.memblock); 328 pa_memblock_unref(chunk1.memblock); 329 pa_memblock_unref(chunk2.memblock); 330 pa_memblock_unref(chunk3.memblock); 331 pa_memblock_unref(chunk4.memblock); 332 333 pa_mempool_unref(p); 334} 335END_TEST 336 337START_TEST (memblockq_test_length_changes) { 338 pa_mempool *p; 339 pa_memblockq *bq; 340 pa_memchunk silence, data; 341 pa_sample_spec ss = { 342 .format = PA_SAMPLE_S32BE, 343 .rate = 48000, 344 .channels = 1 345 }; 346 int64_t idx = 0; 347 size_t maxlength = 60; 348 size_t tlength = 40; 349 size_t prebuf = 16; 350 size_t minreq = 20; 351 size_t maxrewind = 40; 352 353 p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true); 354 ck_assert_ptr_ne(p, NULL); 355 356 silence = memchunk_from_str(p, "____"); 357 358 bq = pa_memblockq_new("test memblockq", idx, maxlength, tlength, &ss, prebuf, minreq, maxrewind, &silence); 359 fail_unless(bq != NULL); 360 361 data = memchunk_from_str(p, "12345678"); 362 363 /* insert some data */ 364 ck_assert_int_eq(pa_memblockq_push(bq, &data), 0); 365 ck_assert_int_eq(pa_memblockq_push(bq, &data), 0); 366 ck_assert_int_eq(pa_memblockq_push(bq, &data), 0); 367 ck_assert_int_eq(pa_memblockq_push(bq, &data), 0); 368 369 /* check state */ 370 ck_assert_int_eq(pa_memblockq_get_length(bq), 32); 371 372 /* adjust maximum length 373 * This might modify tlength, prebuf, minreq, too. */ 374 pa_memblockq_set_maxlength(bq, maxlength/2); 375 check_queue_invariants(bq); 376 377 /* adjust target length 378 * This might modify minreq, too. */ 379 pa_memblockq_set_tlength(bq, tlength/2); 380 check_queue_invariants(bq); 381 382 /* adjust minimum requested length 383 * This might modify prebuf, too. */ 384 pa_memblockq_set_minreq(bq, minreq/2); 385 check_queue_invariants(bq); 386 387 /* adjust prebuffer length */ 388 pa_memblockq_set_prebuf(bq, prebuf/2); 389 check_queue_invariants(bq); 390 391 /* cleanup */ 392 pa_memblockq_free(bq); 393 pa_memblock_unref(silence.memblock); 394 pa_memblock_unref(data.memblock); 395 pa_mempool_unref(p); 396} 397END_TEST 398 399START_TEST (memblockq_test_pop_missing) { 400 pa_mempool *p; 401 pa_memblockq *bq; 402 pa_memchunk silence, data, chunk; 403 pa_sample_spec ss = { 404 .format = PA_SAMPLE_S16BE, 405 .rate = 48000, 406 .channels = 1 407 }; 408 int64_t idx = 0; 409 size_t maxlength = 200; 410 size_t tlength = 100; 411 size_t prebuf = 0; 412 size_t minreq = 80; 413 size_t maxrewind = 0; 414 415 p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true); 416 ck_assert_ptr_ne(p, NULL); 417 418 silence = memchunk_from_str(p, "____"); 419 data = memchunk_from_str(p, "1234567890"); 420 421 bq = pa_memblockq_new("test memblockq", idx, maxlength, tlength, &ss, prebuf, minreq, maxrewind, &silence); 422 fail_unless(bq != NULL); 423 424 /* The following equation regarding the internal variables of a memblockq 425 * is always true: 426 * 427 * length + missing + requested = tlength 428 * 429 * "length" is the current memblockq length (write index minus read index) 430 * and "tlength" is the target length. The intuitive meaning of "missing" 431 * would be the difference between tlength and length, but actually 432 * "missing" and "requested" together constitute the amount that is missing 433 * from the queue. Writing to the queue decrements "requested" and reading 434 * from the queue increments "missing". pa_memblockq_pop_missing() resets 435 * "missing" to zero, returns the old "missing" value and adds the 436 * equivalent amount to "requested". 437 * 438 * This test has comments between each step documenting the assumed state 439 * of those internal variables. */ 440 441 /* length + missing + requested = tlength 442 * 0 + 100 + 0 = 100 */ 443 444 ck_assert_int_eq(pa_memblockq_pop_missing(bq), tlength); 445 446 /* length + missing + requested = tlength 447 * 0 + 0 + 100 = 100 */ 448 449 for (int i = 0; i != 2; ++i) 450 ck_assert_int_eq(pa_memblockq_push(bq, &data), 0); 451 check_queue_invariants(bq); 452 453 /* length + missing + requested = tlength 454 * 20 + 0 + 80 = 100 */ 455 456 ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0); 457 458 /* length + missing + requested = tlength 459 * 20 + 0 + 80 = 100 */ 460 461 for (int i = 0; i != 8; ++i) 462 ck_assert_int_eq(pa_memblockq_push(bq, &data), 0); 463 check_queue_invariants(bq); 464 465 /* length + missing + requested = tlength 466 * 100 + 0 + 0 = 100 */ 467 468 ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0); 469 470 /* length + missing + requested = tlength 471 * 100 + 0 + 0 = 100 */ 472 473 ck_assert_int_eq(pa_memblockq_peek_fixed_size(bq, 40, &chunk), 0); 474 pa_memblockq_drop(bq, 40); 475 ck_assert_int_eq(chunk.length - chunk.index, 40); 476 pa_memblock_unref(chunk.memblock); 477 check_queue_invariants(bq); 478 479 /* length + missing + requested = tlength 480 * 60 + 40 + 0 = 100 */ 481 482 /* 40 bytes are missing, but that's less than minreq, so 0 is reported */ 483 ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0); 484 485 /* length + missing + requested = tlength 486 * 60 + 40 + 0 = 100 */ 487 488 /* Now we push 30 bytes even though it was not requested, so the requested 489 * counter goes negative! */ 490 for (int i = 0; i != 3; ++i) 491 ck_assert_int_eq(pa_memblockq_push(bq, &data), 0); 492 check_queue_invariants(bq); 493 494 /* length + missing + requested = tlength 495 * 90 + 40 + -30 = 100 */ 496 497 /* missing < minreq, so nothing is reported missing. */ 498 ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0); 499 500 /* length + missing + requested = tlength 501 * 90 + 40 + -30 = 100 */ 502 503 ck_assert_int_eq(pa_memblockq_peek_fixed_size(bq, 20, &chunk), 0); 504 pa_memblockq_drop(bq, 20); 505 ck_assert_int_eq(chunk.length - chunk.index, 20); 506 pa_memblock_unref(chunk.memblock); 507 check_queue_invariants(bq); 508 509 /* length + missing + requested = tlength 510 * 70 + 60 + -30 = 100 */ 511 512 /* missing < minreq, so nothing is reported missing. */ 513 ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0); 514 515 /* length + missing + requested = tlength 516 * 70 + 60 + -30 = 100 */ 517 518 /* We push more data again even though it was not requested, so the 519 * requested counter goes further into the negative range. */ 520 for (int i = 0; i != 5; ++i) 521 ck_assert_int_eq(pa_memblockq_push(bq, &data), 0); 522 check_queue_invariants(bq); 523 524 /* length + missing + requested = tlength 525 * 120 + 60 + -80 = 100 */ 526 527 /* missing < minreq, so nothing is reported missing. */ 528 ck_assert_int_eq(pa_memblockq_pop_missing(bq), 0); 529 530 /* length + missing + requested = tlength 531 * 120 + 60 + -80 = 100 */ 532 533 ck_assert_int_eq(pa_memblockq_peek_fixed_size(bq, 20, &chunk), 0); 534 pa_memblockq_drop(bq, 20); 535 ck_assert_int_eq(chunk.length - chunk.index, 20); 536 pa_memblock_unref(chunk.memblock); 537 check_queue_invariants(bq); 538 539 /* length + missing + requested = tlength 540 * 100 + 80 + -80 = 100 */ 541 542 /* missing has now reached the minreq threshold */ 543 ck_assert_int_eq(pa_memblockq_pop_missing(bq), 80); 544 545 /* length + missing + requested = tlength 546 * 100 + 0 + 0 = 100 */ 547 548 /* cleanup */ 549 pa_memblockq_free(bq); 550 pa_memblock_unref(silence.memblock); 551 pa_memblock_unref(data.memblock); 552 pa_mempool_unref(p); 553} 554END_TEST 555 556START_TEST (memblockq_test_tlength_change) { 557 int ret; 558 size_t missing; 559 560 pa_mempool *p; 561 pa_memblockq *bq; 562 pa_memchunk chunk; 563 char buffer[2048]; 564 pa_sample_spec ss = { 565 .format = PA_SAMPLE_S16LE, 566 .rate = 48000, 567 .channels = 1 568 }; 569 570 pa_log_set_level(PA_LOG_DEBUG); 571 572 bq = pa_memblockq_new("test memblockq", 0, 4096, 2048, &ss, 0, 512, 512, NULL); 573 fail_unless(bq != NULL); 574 575 /* Empty buffer, so expect tlength */ 576 missing = pa_memblockq_pop_missing(bq); 577 fail_unless(missing == 2048); 578 579 /* Everything requested, so should be satisfied */ 580 missing = pa_memblockq_pop_missing(bq); 581 fail_unless(missing == 0); 582 583 p = pa_mempool_new(PA_MEM_TYPE_PRIVATE, 0, true); 584 585 chunk.memblock = pa_memblock_new_fixed(p, buffer, sizeof(buffer), 1); 586 fail_unless(chunk.memblock != NULL); 587 588 chunk.index = 0; 589 chunk.length = sizeof(buffer); 590 591 /* Fill buffer (i.e. satisfy earlier request) */ 592 ret = pa_memblockq_push(bq, &chunk); 593 fail_unless(ret == 0); 594 595 /* Should still be happy */ 596 missing = pa_memblockq_pop_missing(bq); 597 fail_unless(missing == 0); 598 599 /* Check that we don't request less than minreq */ 600 pa_memblockq_drop(bq, 400); 601 missing = pa_memblockq_pop_missing(bq); 602 ck_assert_int_eq(missing, 0); 603 604 missing = pa_memblockq_pop_missing(bq); 605 fail_unless(missing == 0); 606 607 /* Reduce tlength under what's dropped and under previous minreq */ 608 pa_memblockq_set_tlength(bq, 256); 609 pa_memblockq_set_minreq(bq, 64); 610 611 /* We are now overbuffered and should not request more */ 612 missing = pa_memblockq_pop_missing(bq); 613 fail_unless(missing == 0); 614 615 /* Drop more data so we are below tlength again, but just barely */ 616 pa_memblockq_drop(bq, 1400); 617 618 /* Should still honour minreq */ 619 missing = pa_memblockq_pop_missing(bq); 620 fail_unless(missing == 0); 621 622 /* Finally drop enough to fall below minreq */ 623 pa_memblockq_drop(bq, 80); 624 625 /* And expect a request */ 626 missing = pa_memblockq_pop_missing(bq); 627 fail_unless(missing == 88); 628 629 pa_memblockq_free(bq); 630 pa_memblock_unref(chunk.memblock); 631 pa_mempool_unref(p); 632} 633END_TEST 634 635 636int main(int argc, char *argv[]) { 637 int failed = 0; 638 Suite *s; 639 TCase *tc; 640 SRunner *sr; 641 642 if (!getenv("MAKE_CHECK")) 643 pa_log_set_level(PA_LOG_DEBUG); 644 645 s = suite_create("Memblock Queue"); 646 tc = tcase_create("memblockq"); 647 tcase_add_test(tc, memchunk_from_str_test); 648 tcase_add_test(tc, memblockq_test_initial_properties); 649 tcase_add_test(tc, memblockq_test); 650 tcase_add_test(tc, memblockq_test_length_changes); 651 tcase_add_test(tc, memblockq_test_pop_missing); 652 tcase_add_test(tc, memblockq_test_tlength_change); 653 suite_add_tcase(s, tc); 654 655 sr = srunner_create(s); 656 srunner_run_all(sr, CK_NORMAL); 657 failed = srunner_ntests_failed(sr); 658 srunner_free(sr); 659 660 return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 661} 662