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
36 static const char *fixed[] = {
37 "1122444411441144__22__11______3333______________________________",
38 "__________________3333__________________________________________"
39 };
40 static const char *manual[] = {
41 "1122444411441144__22__11______3333______________________________",
42 "__________________3333______________________________"
43 };
44
45 /*
46 * utility function to create a memchunk
47 */
memchunk_from_str(pa_mempool * p,const char * data)48 static 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
dump_chunk(const pa_memchunk * chunk,pa_strbuf * buf)62 static 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
dump(pa_memblockq * bq,int n)77 static 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 */
check_queue_invariants(pa_memblockq * bq)120 static 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
START_TEST(memchunk_from_str_test)165 START_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 }
182 END_TEST
183
START_TEST(memblockq_test_initial_properties)184 START_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 }
233 END_TEST
234
START_TEST(memblockq_test)235 START_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 }
335 END_TEST
336
START_TEST(memblockq_test_length_changes)337 START_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 }
397 END_TEST
398
START_TEST(memblockq_test_pop_missing)399 START_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 }
554 END_TEST
555
START_TEST(memblockq_test_tlength_change)556 START_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 }
633 END_TEST
634
635
main(int argc,char * argv[])636 int 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