1/*
2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11//       Some ideas of improvements:
12//       Break out common init and maybe terminate to separate function(s).
13//       How much trace should we have enabled?
14//       API error counter, to print info and return -1 if any error.
15
16#include <assert.h>
17#include <stdio.h>
18#include <stdlib.h>
19#include <string.h>
20#include <time.h>
21#if defined(_WIN32)
22#include <conio.h>
23#endif
24
25#include "webrtc/voice_engine/test/auto_test/voe_stress_test.h"
26
27#include "webrtc/base/scoped_ptr.h"
28#include "webrtc/system_wrappers/include/sleep.h"
29#include "webrtc/test/channel_transport/channel_transport.h"
30#include "webrtc/voice_engine/test/auto_test/voe_standard_test.h"
31#include "webrtc/voice_engine/test/auto_test/voe_test_defines.h"
32#include "webrtc/voice_engine/voice_engine_defines.h"  // defines build macros
33
34using namespace webrtc;
35using namespace test;
36
37namespace voetest {
38
39#define VALIDATE_STRESS(expr)                                   \
40    if (expr)                                                   \
41    {                                                           \
42        printf("Error at line: %i, %s \n", __LINE__, #expr);    \
43        printf("Error code: %i \n", base->LastError());  \
44    }
45
46#ifdef _WIN32
47// Pause if supported
48#define PAUSE_OR_SLEEP(x) PAUSE;
49#else
50// Sleep a bit instead if pause not supported
51#define PAUSE_OR_SLEEP(x) SleepMs(x);
52#endif
53
54int VoEStressTest::DoTest() {
55  int test(-1);
56  while (test != 0) {
57    test = MenuSelection();
58    switch (test) {
59      case 0:
60        // Quit stress test
61        break;
62      case 1:
63        // All tests
64        StartStopTest();
65        CreateDeleteChannelsTest();
66        MultipleThreadsTest();
67        break;
68      case 2:
69        StartStopTest();
70        break;
71      case 3:
72        CreateDeleteChannelsTest();
73        break;
74      case 4:
75        MultipleThreadsTest();
76        break;
77      default:
78        // Should not be possible
79        printf("Invalid selection! (Test code error)\n");
80        assert(false);
81    }  // switch
82  }  // while
83
84  return 0;
85}
86
87int VoEStressTest::MenuSelection() {
88  printf("------------------------------------------------\n");
89  printf("Select stress test\n\n");
90  printf(" (0)  Quit\n");
91  printf(" (1)  All\n");
92  printf("- - - - - - - - - - - - - - - - - - - - - - - - \n");
93  printf(" (2)  Start/stop\n");
94  printf(" (3)  Create/delete channels\n");
95  printf(" (4)  Multiple threads\n");
96
97  const int maxMenuSelection = 4;
98  int selection(-1);
99
100  while ((selection < 0) || (selection > maxMenuSelection)) {
101    printf("\n: ");
102    int retval = scanf("%d", &selection);
103    if ((retval != 1) || (selection < 0) || (selection > maxMenuSelection)) {
104      printf("Invalid selection!\n");
105    }
106  }
107
108  return selection;
109}
110
111int VoEStressTest::StartStopTest() {
112  printf("------------------------------------------------\n");
113  printf("Running start/stop test\n");
114  printf("------------------------------------------------\n");
115
116  printf("\nNOTE: this thest will fail after a while if Core audio is used\n");
117  printf("because MS returns AUDCLNT_E_CPUUSAGE_EXCEEDED (VoE Error 10013).\n");
118
119  // Get sub-API pointers
120  VoEBase* base = _mgr.BasePtr();
121  VoENetwork* voe_network = _mgr.NetworkPtr();
122
123  // Set trace
124  //     VALIDATE_STRESS(base->SetTraceFileName(
125  //         GetFilename("VoEStressTest_StartStop_trace.txt")));
126  //     VALIDATE_STRESS(base->SetDebugTraceFileName(
127  //         GetFilename("VoEStressTest_StartStop_trace_debug.txt")));
128  //     VALIDATE_STRESS(base->SetTraceFilter(kTraceStateInfo |
129  //         kTraceWarning | kTraceError |
130  //         kTraceCritical | kTraceApiCall |
131  //         kTraceMemory | kTraceInfo));
132  VALIDATE_STRESS(base->Init());
133  VALIDATE_STRESS(base->CreateChannel());
134
135  ///////////// Start test /////////////
136
137  int numberOfLoops(2000);
138  int loopSleep(200);
139  int i(0);
140  int markInterval(20);
141
142  printf("Running %d loops with %d ms sleep. Mark every %d loop. \n",
143         numberOfLoops, loopSleep, markInterval);
144  printf("Test will take approximately %d minutes. \n",
145         numberOfLoops * loopSleep / 1000 / 60 + 1);
146
147  rtc::scoped_ptr<VoiceChannelTransport> voice_channel_transport(
148      new VoiceChannelTransport(voe_network, 0));
149
150  for (i = 0; i < numberOfLoops; ++i) {
151    voice_channel_transport->SetSendDestination("127.0.0.1", 4800);
152    voice_channel_transport->SetLocalReceiver(4800);
153    VALIDATE_STRESS(base->StartReceive(0));
154    VALIDATE_STRESS(base->StartPlayout(0));
155    VALIDATE_STRESS(base->StartSend(0));
156    if (!(i % markInterval))
157      MARK();
158    SleepMs(loopSleep);
159    VALIDATE_STRESS(base->StopSend(0));
160    VALIDATE_STRESS(base->StopPlayout(0));
161    VALIDATE_STRESS(base->StopReceive(0));
162  }
163  ANL();
164
165  VALIDATE_STRESS(voice_channel_transport->SetSendDestination("127.0.0.1",
166                                                              4800));
167  VALIDATE_STRESS(voice_channel_transport->SetLocalReceiver(4800));
168  VALIDATE_STRESS(base->StartReceive(0));
169  VALIDATE_STRESS(base->StartPlayout(0));
170  VALIDATE_STRESS(base->StartSend(0));
171  printf("Verify that audio is good. \n");
172  PAUSE_OR_SLEEP(20000);
173  VALIDATE_STRESS(base->StopSend(0));
174  VALIDATE_STRESS(base->StopPlayout(0));
175  VALIDATE_STRESS(base->StopReceive(0));
176
177  ///////////// End test /////////////
178
179
180  // Terminate
181  VALIDATE_STRESS(base->DeleteChannel(0));
182  VALIDATE_STRESS(base->Terminate());
183
184  printf("Test finished \n");
185
186  return 0;
187}
188
189int VoEStressTest::CreateDeleteChannelsTest() {
190  printf("------------------------------------------------\n");
191  printf("Running create/delete channels test\n");
192  printf("------------------------------------------------\n");
193
194  // Get sub-API pointers
195  VoEBase* base = _mgr.BasePtr();
196
197  // Set trace
198  //     VALIDATE_STRESS(base->SetTraceFileName(
199  //          GetFilename("VoEStressTest_CreateChannels_trace.txt")));
200  //     VALIDATE_STRESS(base->SetDebugTraceFileName(
201  //          GetFilename("VoEStressTest_CreateChannels_trace_debug.txt")));
202  //     VALIDATE_STRESS(base->SetTraceFilter(kTraceStateInfo |
203  //         kTraceWarning | kTraceError |
204  //         kTraceCritical | kTraceApiCall |
205  //         kTraceMemory | kTraceInfo));
206  VALIDATE_STRESS(base->Init());
207
208  ///////////// Start test /////////////
209
210  int numberOfLoops(10000);
211  int loopSleep(10);
212  int i(0);
213  int markInterval(200);
214
215  printf("Running %d loops with %d ms sleep. Mark every %d loop. \n",
216         numberOfLoops, loopSleep, markInterval);
217  printf("Test will take approximately %d minutes. \n",
218         numberOfLoops * loopSleep / 1000 / 60 + 1);
219
220  //       Some possible extensions include:
221  //       Different sleep times (fixed or random) or zero.
222  //       Start call on all or some channels.
223  //       Two parts: first have a slight overweight to creating channels,
224  //       then to deleting. (To ensure we hit max channels and go to zero.)
225  //       Make sure audio is OK after test has finished.
226
227  // Set up, start with maxChannels/2 channels
228  const int maxChannels = 100;
229  VALIDATE_STRESS(maxChannels < 1); // Should always have at least one channel
230  bool* channelState = new bool[maxChannels];
231  memset(channelState, 0, maxChannels * sizeof(bool));
232  int channel(0);
233  int noOfActiveChannels(0);
234  for (i = 0; i < (maxChannels / 2); ++i) {
235    channel = base->CreateChannel();
236    VALIDATE_STRESS(channel < 0);
237    if (channel >= 0) {
238      channelState[channel] = true;
239      ++noOfActiveChannels;
240    }
241  }
242  srand((unsigned int) time(NULL));
243  bool action(false);
244  double rnd(0.0);
245  int res(0);
246
247  // Create/delete channels with slight
248  for (i = 0; i < numberOfLoops; ++i) {
249    // Randomize action (create or delete channel)
250    action = rand() <= (RAND_MAX / 2);
251    if (action) {
252      if (noOfActiveChannels < maxChannels) {
253        // Create new channel
254        channel = base->CreateChannel();
255        VALIDATE_STRESS(channel < 0);
256        if (channel >= 0) {
257          channelState[channel] = true;
258          ++noOfActiveChannels;
259        }
260      }
261    } else {
262      if (noOfActiveChannels > 0) {
263        // Delete random channel that's created [0, maxChannels - 1]
264        do {
265          rnd = static_cast<double> (rand());
266          channel = static_cast<int> (rnd /
267                                      (static_cast<double> (RAND_MAX) + 1.0f) *
268                                      maxChannels);
269        } while (!channelState[channel]); // Must find a created channel
270
271        res = base->DeleteChannel(channel);
272        VALIDATE_STRESS(0 != res);
273        if (0 == res) {
274          channelState[channel] = false;
275          --noOfActiveChannels;
276        }
277      }
278    }
279
280    if (!(i % markInterval))
281      MARK();
282    SleepMs(loopSleep);
283  }
284  ANL();
285
286  delete[] channelState;
287
288  ///////////// End test /////////////
289
290
291  // Terminate
292  VALIDATE_STRESS(base->Terminate()); // Deletes all channels
293
294  printf("Test finished \n");
295
296  return 0;
297}
298
299int VoEStressTest::MultipleThreadsTest() {
300  printf("------------------------------------------------\n");
301  printf("Running multiple threads test\n");
302  printf("------------------------------------------------\n");
303
304  // Get sub-API pointers
305  VoEBase* base = _mgr.BasePtr();
306
307  // Set trace
308  //     VALIDATE_STRESS(base->SetTraceFileName(
309  //        GetFilename("VoEStressTest_MultipleThreads_trace.txt")));
310  //     VALIDATE_STRESS(base->SetDebugTraceFileName(
311  //        GetFilename("VoEStressTest_MultipleThreads_trace_debug.txt")));
312  //     VALIDATE_STRESS(base->SetTraceFilter(kTraceStateInfo |
313  //        kTraceWarning | kTraceError |
314  //        kTraceCritical | kTraceApiCall |
315  //        kTraceMemory | kTraceInfo));
316
317  // Init
318  VALIDATE_STRESS(base->Init());
319  VALIDATE_STRESS(base->CreateChannel());
320
321  ///////////// Start test /////////////
322
323  int numberOfLoops(10000);
324  int loopSleep(0);
325  int i(0);
326  int markInterval(1000);
327
328  printf("Running %d loops with %d ms sleep. Mark every %d loop. \n",
329         numberOfLoops, loopSleep, markInterval);
330  printf("Test will take approximately %d minutes. \n",
331         numberOfLoops * loopSleep / 1000 / 60 + 1);
332
333  srand((unsigned int) time(NULL));
334  int rnd(0);
335
336  // Start extra thread
337  _ptrExtraApiThread.reset(
338      new rtc::PlatformThread(RunExtraApi, this, "StressTestExtraApiThread"));
339  _ptrExtraApiThread->Start();
340
341  //       Some possible extensions include:
342  //       Add more API calls to randomize
343  //       More threads
344  //       Different sleep times (fixed or random).
345  //       Make sure audio is OK after test has finished.
346
347  // Call random API functions here and in extra thread, ignore any error
348  for (i = 0; i < numberOfLoops; ++i) {
349    // This part should be equal to the marked part in the extra thread
350    // --- BEGIN ---
351    rnd = rand();
352    if (rnd < (RAND_MAX / 2)) {
353      // Start playout
354      base->StartPlayout(0);
355    } else {
356      // Stop playout
357      base->StopPlayout(0);
358    }
359    // --- END ---
360
361    if (!(i % markInterval))
362      MARK();
363    SleepMs(loopSleep);
364  }
365  ANL();
366
367  // Stop extra thread
368  _ptrExtraApiThread->Stop();
369
370  ///////////// End test /////////////
371
372  // Terminate
373  VALIDATE_STRESS(base->Terminate()); // Deletes all channels
374
375  printf("Test finished \n");
376
377  return 0;
378}
379
380// Thread functions
381
382bool VoEStressTest::RunExtraApi(void* ptr) {
383  return static_cast<VoEStressTest*> (ptr)->ProcessExtraApi();
384}
385
386bool VoEStressTest::ProcessExtraApi() {
387  // Prepare
388  VoEBase* base = _mgr.BasePtr();
389  int rnd(0);
390
391  // Call random API function, ignore any error
392
393  // This part should be equal to the marked part in the main thread
394  // --- BEGIN ---
395  rnd = rand();
396  if (rnd < (RAND_MAX / 2)) {
397    // Start playout
398    base->StartPlayout(0);
399  } else {
400    // Stop playout
401    base->StopPlayout(0);
402  }
403  // --- END ---
404
405  return true;
406}
407
408}  // namespace voetest
409