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