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 <signal.h> 23#include <string.h> 24#include <errno.h> 25#include <unistd.h> 26#include <assert.h> 27#include <stdio.h> 28#include <stdlib.h> 29 30#include <check.h> 31 32#include <pulse/pulseaudio.h> 33#include <pulse/mainloop.h> 34 35#include <pulsecore/sink.h> 36 37/* Set the number of streams such that it allows two simultaneous instances of 38 * connect-stress to be run and not go above the max limit for streams-per-sink. 39 * This leaves enough room for a couple other streams from regular system usage, 40 * which makes a non-error abort less likely (although still easily possible of 41 * playing >=3 streams outside of the test - including internal loopback, rtp, 42 * combine, remap streams etc.) */ 43/* #define NSTREAMS ((PA_MAX_INPUTS_PER_SINK/2) - 1) */ 44 45/* This test broke when PA_MAX_INPUTS_PER_SINK was increased from 32 to 256. 46 * Because we currently don't have time to figure out why, let's just set 47 * NSTREAMS to 20 in the meantime. 48 */ 49#define NSTREAMS 20 50#define NTESTS 1000 51#define SAMPLE_HZ 44100 52 53static pa_context *context = NULL; 54static pa_stream *streams[NSTREAMS]; 55static pa_threaded_mainloop *mainloop = NULL; 56static char *bname; 57 58static const pa_sample_spec sample_spec = { 59 .format = PA_SAMPLE_FLOAT32, 60 .rate = SAMPLE_HZ, 61 .channels = 1 62}; 63 64static void context_state_callback(pa_context *c, void *userdata); 65 66/* Note: don't conflict with connect(2) declaration */ 67static void _connect(const char *name, int *try) { 68 int ret; 69 pa_mainloop_api *api; 70 71 /* Set up a new main loop */ 72 mainloop = pa_threaded_mainloop_new(); 73 fail_unless(mainloop != NULL); 74 75 api = pa_threaded_mainloop_get_api(mainloop); 76 context = pa_context_new(api, name); 77 fail_unless(context != NULL); 78 79 pa_context_set_state_callback(context, context_state_callback, try); 80 81 /* Connect the context */ 82 if (pa_context_connect(context, NULL, 0, NULL) < 0) { 83 fprintf(stderr, "pa_context_connect() failed.\n"); 84 ck_abort(); 85 } 86 87 ret = pa_threaded_mainloop_start(mainloop); 88 fail_unless(ret == 0); 89} 90 91static void _disconnect(void) { 92 int i; 93 94 fail_unless(mainloop != NULL); 95 fail_unless(context != NULL); 96 97 pa_threaded_mainloop_lock(mainloop); 98 99 for (i = 0; i < NSTREAMS; i++) 100 if (streams[i]) { 101 pa_stream_disconnect(streams[i]); 102 pa_stream_unref(streams[i]); 103 streams[i] = NULL; 104 } 105 106 pa_context_disconnect(context); 107 context = NULL; 108 109 pa_threaded_mainloop_unlock(mainloop); 110 pa_threaded_mainloop_stop(mainloop); 111 pa_threaded_mainloop_free(mainloop); 112 mainloop = NULL; 113} 114 115static const pa_buffer_attr buffer_attr = { 116 .maxlength = SAMPLE_HZ * sizeof(float) * NSTREAMS, 117 .tlength = (uint32_t) -1, 118 .prebuf = 0, /* Setting prebuf to 0 guarantees us the streams will run synchronously, no matter what */ 119 .minreq = (uint32_t) -1, 120 .fragsize = 0 121}; 122 123static void stream_write_callback(pa_stream *stream, size_t nbytes, void *userdata) { 124 char silence[8192]; 125 126 memset(silence, 0, sizeof(silence)); 127 128 while (nbytes) { 129 int n = PA_MIN(sizeof(silence), nbytes); 130 pa_stream_write(stream, silence, n, NULL, 0, 0); 131 nbytes -= n; 132 } 133} 134 135static void stream_state_callback(pa_stream *s, void *userdata) { 136 fail_unless(s != NULL); 137 138 switch (pa_stream_get_state(s)) { 139 case PA_STREAM_UNCONNECTED: 140 case PA_STREAM_CREATING: 141 case PA_STREAM_TERMINATED: 142 case PA_STREAM_READY: 143 break; 144 145 default: 146 case PA_STREAM_FAILED: 147 fprintf(stderr, "Stream error: %s\n", pa_strerror(pa_context_errno(pa_stream_get_context(s)))); 148 ck_abort(); 149 } 150} 151 152static void context_state_callback(pa_context *c, void *userdata) { 153 int *try; 154 155 fail_unless(c != NULL); 156 fail_unless(userdata != NULL); 157 158 try = (int*)userdata; 159 160 switch (pa_context_get_state(c)) { 161 case PA_CONTEXT_CONNECTING: 162 case PA_CONTEXT_AUTHORIZING: 163 case PA_CONTEXT_SETTING_NAME: 164 break; 165 166 case PA_CONTEXT_READY: { 167 168 int i; 169 fprintf(stderr, "Connection (%d of %d) established.\n", (*try)+1, NTESTS); 170 171 for (i = 0; i < NSTREAMS; i++) { 172 char name[64]; 173 174 snprintf(name, sizeof(name), "stream #%i", i); 175 streams[i] = pa_stream_new(c, name, &sample_spec, NULL); 176 fail_unless(streams[i] != NULL); 177 pa_stream_set_state_callback(streams[i], stream_state_callback, NULL); 178 pa_stream_set_write_callback(streams[i], stream_write_callback, NULL); 179 pa_stream_connect_playback(streams[i], NULL, &buffer_attr, 0, NULL, NULL); 180 } 181 182 break; 183 } 184 185 case PA_CONTEXT_TERMINATED: 186 fprintf(stderr, "Connection terminated.\n"); 187 pa_context_unref(context); 188 context = NULL; 189 break; 190 191 case PA_CONTEXT_FAILED: 192 default: 193 fprintf(stderr, "Context error: %s\n", pa_strerror(pa_context_errno(c))); 194 ck_abort(); 195 } 196} 197 198START_TEST (connect_stress_test) { 199 int i; 200 201 for (i = 0; i < NSTREAMS; i++) 202 streams[i] = NULL; 203 204 for (i = 0; i < NTESTS; i++) { 205 _connect(bname, &i); 206 usleep(rand() % 500000); 207 _disconnect(); 208 usleep(rand() % 500000); 209 } 210 211 fprintf(stderr, "Done.\n"); 212} 213END_TEST 214 215int main(int argc, char *argv[]) { 216 int failed = 0; 217 Suite *s; 218 TCase *tc; 219 SRunner *sr; 220 221 bname = argv[0]; 222 223 s = suite_create("Connect Stress"); 224 tc = tcase_create("connectstress"); 225 tcase_add_test(tc, connect_stress_test); 226 tcase_set_timeout(tc, 20 * 60); 227 suite_add_tcase(s, tc); 228 229 sr = srunner_create(s); 230 srunner_run_all(sr, CK_NORMAL); 231 failed = srunner_ntests_failed(sr); 232 srunner_free(sr); 233 234 return (failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; 235} 236