text: /*
   *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
   *
   *  Use of this source code is governed by a BSD-style license
   *  that can be found in the LICENSE file in the root of the source
   *  tree. An additional intellectual property rights grant can be found
   *  in the file PATENTS.  All contributing project authors may
   *  be found in the AUTHORS file in the root of the source tree.
   */
  
  //       Some ideas of improvements:
  //       Break out common init and maybe terminate to separate function(s).
  //       How much trace should we have enabled?
  //       API error counter, to print info and return -1 if any error.
  
  #include <assert.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <time.h>
  #if defined(_WIN32)
  #include <conio.h>
  #endif
  
  #include "webrtc/voice_engine/test/auto_test/voe_stress_test.h"
  
  #include "webrtc/system_wrappers/interface/scoped_ptr.h"
  #include "webrtc/system_wrappers/interface/sleep.h"
  #include "webrtc/system_wrappers/interface/thread_wrapper.h"
  #include "webrtc/test/channel_transport/include/channel_transport.h"
  #include "webrtc/voice_engine/test/auto_test/voe_standard_test.h"
  #include "webrtc/voice_engine/test/auto_test/voe_test_defines.h"
  #include "webrtc/voice_engine/voice_engine_defines.h"  // defines build macros
  
  using namespace webrtc;
  using namespace test;
  
  namespace voetest {
  
  #define VALIDATE_STRESS(expr)                                   \
      if (expr)                                                   \
      {                                                           \
          printf("Error at line: %i, %s \n", __LINE__, #expr);    \
          printf("Error code: %i \n", base->LastError());  \
      }
  
  #ifdef _WIN32
  // Pause if supported
  #define PAUSE_OR_SLEEP(x) PAUSE;
  #else
  // Sleep a bit instead if pause not supported
  #define PAUSE_OR_SLEEP(x) SleepMs(x);
  #endif
  
  int VoEStressTest::DoTest() {
    int test(-1);
    while (test != 0) {
      test = MenuSelection();
      switch (test) {
        case 0:
          // Quit stress test
          break;
        case 1:
          // All tests
          StartStopTest();
          CreateDeleteChannelsTest();
          MultipleThreadsTest();
          break;
        case 2:
          StartStopTest();
          break;
        case 3:
          CreateDeleteChannelsTest();
          break;
        case 4:
          MultipleThreadsTest();
          break;
        default:
          // Should not be possible
          printf("Invalid selection! (Test code error)\n");
          assert(false);
      }  // switch
    }  // while
  
    return 0;
  }
  
  int VoEStressTest::MenuSelection() {
    printf("------------------------------------------------\n");
    printf("Select stress test\n\n");
    printf(" (0)  Quit\n");
    printf(" (1)  All\n");
    printf("- - - - - - - - - - - - - - - - - - - - - - - - \n");
    printf(" (2)  Start/stop\n");
    printf(" (3)  Create/delete channels\n");
    printf(" (4)  Multiple threads\n");
  
    const int maxMenuSelection = 4;
    int selection(-1);
  
    while ((selection < 0) || (selection > maxMenuSelection)) {
      printf("\n: ");
      int retval = scanf("%d", &selection);
      if ((retval != 1) || (selection < 0) || (selection > maxMenuSelection)) {
        printf("Invalid selection!\n");
      }
    }
  
    return selection;
  }
  
  int VoEStressTest::StartStopTest() {
    printf("------------------------------------------------\n");
    printf("Running start/stop test\n");
    printf("------------------------------------------------\n");
  
    printf("\nNOTE: this thest will fail after a while if Core audio is used\n");
    printf("because MS returns AUDCLNT_E_CPUUSAGE_EXCEEDED (VoE Error 10013).\n");
  
    // Get sub-API pointers
    VoEBase* base = _mgr.BasePtr();
    VoENetwork* voe_network = _mgr.NetworkPtr();
  
    // Set trace
    //     VALIDATE_STRESS(base->SetTraceFileName(
    //         GetFilename("VoEStressTest_StartStop_trace.txt")));
    //     VALIDATE_STRESS(base->SetDebugTraceFileName(
    //         GetFilename("VoEStressTest_StartStop_trace_debug.txt")));
    //     VALIDATE_STRESS(base->SetTraceFilter(kTraceStateInfo |
    //         kTraceWarning | kTraceError |
    //         kTraceCritical | kTraceApiCall |
    //         kTraceMemory | kTraceInfo));
    VALIDATE_STRESS(base->Init());
    VALIDATE_STRESS(base->CreateChannel());
  
    ///////////// Start test /////////////
  
    int numberOfLoops(2000);
    int loopSleep(200);
    int i(0);
    int markInterval(20);
  
    printf("Running %d loops with %d ms sleep. Mark every %d loop. \n",
           numberOfLoops, loopSleep, markInterval);
    printf("Test will take approximately %d minutes. \n",
           numberOfLoops * loopSleep / 1000 / 60 + 1);
  
    scoped_ptr<VoiceChannelTransport> voice_channel_transport(
        new VoiceChannelTransport(voe_network, 0));
  
    for (i = 0; i < numberOfLoops; ++i) {
      voice_channel_transport->SetSendDestination("127.0.0.1", 4800);
      voice_channel_transport->SetLocalReceiver(4800);
      VALIDATE_STRESS(base->StartReceive(0));
      VALIDATE_STRESS(base->StartPlayout(0));
      VALIDATE_STRESS(base->StartSend(0));
      if (!(i % markInterval))
        MARK();
      SleepMs(loopSleep);
      VALIDATE_STRESS(base->StopSend(0));
      VALIDATE_STRESS(base->StopPlayout(0));
      VALIDATE_STRESS(base->StopReceive(0));
    }
    ANL();
  
    VALIDATE_STRESS(voice_channel_transport->SetSendDestination("127.0.0.1",
                                                                4800));
    VALIDATE_STRESS(voice_channel_transport->SetLocalReceiver(4800));
    VALIDATE_STRESS(base->StartReceive(0));
    VALIDATE_STRESS(base->StartPlayout(0));
    VALIDATE_STRESS(base->StartSend(0));
    printf("Verify that audio is good. \n");
    PAUSE_OR_SLEEP(20000);
    VALIDATE_STRESS(base->StopSend(0));
    VALIDATE_STRESS(base->StopPlayout(0));
    VALIDATE_STRESS(base->StopReceive(0));
  
    ///////////// End test /////////////
  
  
    // Terminate
    VALIDATE_STRESS(base->DeleteChannel(0));
    VALIDATE_STRESS(base->Terminate());
  
    printf("Test finished \n");
  
    return 0;
  }
  
  int VoEStressTest::CreateDeleteChannelsTest() {
    printf("------------------------------------------------\n");
    printf("Running create/delete channels test\n");
    printf("------------------------------------------------\n");
  
    // Get sub-API pointers
    VoEBase* base = _mgr.BasePtr();
  
    // Set trace
    //     VALIDATE_STRESS(base->SetTraceFileName(
    //          GetFilename("VoEStressTest_CreateChannels_trace.txt")));
    //     VALIDATE_STRESS(base->SetDebugTraceFileName(
    //          GetFilename("VoEStressTest_CreateChannels_trace_debug.txt")));
    //     VALIDATE_STRESS(base->SetTraceFilter(kTraceStateInfo |
    //         kTraceWarning | kTraceError |
    //         kTraceCritical | kTraceApiCall |
    //         kTraceMemory | kTraceInfo));
    VALIDATE_STRESS(base->Init());
  
    ///////////// Start test /////////////
  
    int numberOfLoops(10000);
    int loopSleep(10);
    int i(0);
    int markInterval(200);
  
    printf("Running %d loops with %d ms sleep. Mark every %d loop. \n",
           numberOfLoops, loopSleep, markInterval);
    printf("Test will take approximately %d minutes. \n",
           numberOfLoops * loopSleep / 1000 / 60 + 1);
  
    //       Some possible extensions include:
    //       Different sleep times (fixed or random) or zero.
    //       Start call on all or some channels.
    //       Two parts: first have a slight overweight to creating channels,
    //       then to deleting. (To ensure we hit max channels and go to zero.)
    //       Make sure audio is OK after test has finished.
  
    // Set up, start with maxChannels/2 channels
    const int maxChannels = 100;
    VALIDATE_STRESS(maxChannels < 1); // Should always have at least one channel
    bool* channelState = new bool[maxChannels];
    memset(channelState, 0, maxChannels * sizeof(bool));
    int channel(0);
    int noOfActiveChannels(0);
    for (i = 0; i < (maxChannels / 2); ++i) {
      channel = base->CreateChannel();
      VALIDATE_STRESS(channel < 0);
      if (channel >= 0) {
        channelState[channel] = true;
        ++noOfActiveChannels;
      }
    }
    srand((unsigned int) time(NULL));
    bool action(false);
    double rnd(0.0);
    int res(0);
  
    // Create/delete channels with slight
    for (i = 0; i < numberOfLoops; ++i) {
      // Randomize action (create or delete channel)
      action = rand() <= (RAND_MAX / 2);
      if (action) {
        if (noOfActiveChannels < maxChannels) {
          // Create new channel
          channel = base->CreateChannel();
          VALIDATE_STRESS(channel < 0);
          if (channel >= 0) {
            channelState[channel] = true;
            ++noOfActiveChannels;
          }
        }
      } else {
        if (noOfActiveChannels > 0) {
          // Delete random channel that's created [0, maxChannels - 1]
          do {
            rnd = static_cast<double> (rand());
            channel = static_cast<int> (rnd /
                                        (static_cast<double> (RAND_MAX) + 1.0f) *
                                        maxChannels);
          } while (!channelState[channel]); // Must find a created channel
  
          res = base->DeleteChannel(channel);
          VALIDATE_STRESS(0 != res);
          if (0 == res) {
            channelState[channel] = false;
            --noOfActiveChannels;
          }
        }
      }
  
      if (!(i % markInterval))
        MARK();
      SleepMs(loopSleep);
    }
    ANL();
  
    delete[] channelState;
  
    ///////////// End test /////////////
  
  
    // Terminate
    VALIDATE_STRESS(base->Terminate()); // Deletes all channels
  
    printf("Test finished \n");
  
    return 0;
  }
  
  int VoEStressTest::MultipleThreadsTest() {
    printf("------------------------------------------------\n");
    printf("Running multiple threads test\n");
    printf("------------------------------------------------\n");
  
    // Get sub-API pointers
    VoEBase* base = _mgr.BasePtr();
  
    // Set trace
    //     VALIDATE_STRESS(base->SetTraceFileName(
    //        GetFilename("VoEStressTest_MultipleThreads_trace.txt")));
    //     VALIDATE_STRESS(base->SetDebugTraceFileName(
    //        GetFilename("VoEStressTest_MultipleThreads_trace_debug.txt")));
    //     VALIDATE_STRESS(base->SetTraceFilter(kTraceStateInfo |
    //        kTraceWarning | kTraceError |
    //        kTraceCritical | kTraceApiCall |
    //        kTraceMemory | kTraceInfo));
  
    // Init
    VALIDATE_STRESS(base->Init());
    VALIDATE_STRESS(base->CreateChannel());
  
    ///////////// Start test /////////////
  
    int numberOfLoops(10000);
    int loopSleep(0);
    int i(0);
    int markInterval(1000);
  
    printf("Running %d loops with %d ms sleep. Mark every %d loop. \n",
           numberOfLoops, loopSleep, markInterval);
    printf("Test will take approximately %d minutes. \n",
           numberOfLoops * loopSleep / 1000 / 60 + 1);
  
    srand((unsigned int) time(NULL));
    int rnd(0);
  
    // Start extra thread
    const char* threadName = "StressTest Extra API Thread";
    _ptrExtraApiThread = ThreadWrapper::CreateThread(RunExtraApi, this,
                                                     kNormalPriority, threadName);
    unsigned int id(0);
    VALIDATE_STRESS(!_ptrExtraApiThread->Start(id));
  
    //       Some possible extensions include:
    //       Add more API calls to randomize
    //       More threads
    //       Different sleep times (fixed or random).
    //       Make sure audio is OK after test has finished.
  
    // Call random API functions here and in extra thread, ignore any error
    for (i = 0; i < numberOfLoops; ++i) {
      // This part should be equal to the marked part in the extra thread
      // --- BEGIN ---
      rnd = rand();
      if (rnd < (RAND_MAX / 2)) {
        // Start playout
        base->StartPlayout(0);
      } else {
        // Stop playout
        base->StopPlayout(0);
      }
      // --- END ---
  
      if (!(i % markInterval))
        MARK();
      SleepMs(loopSleep);
    }
    ANL();
  
    // Stop extra thread
    VALIDATE_STRESS(!_ptrExtraApiThread->Stop());
    delete _ptrExtraApiThread;
  
    ///////////// End test /////////////
  
    // Terminate
    VALIDATE_STRESS(base->Terminate()); // Deletes all channels
  
    printf("Test finished \n");
  
    return 0;
  }
  
  // Thread functions
  
  bool VoEStressTest::RunExtraApi(void* ptr) {
    return static_cast<VoEStressTest*> (ptr)->ProcessExtraApi();
  }
  
  bool VoEStressTest::ProcessExtraApi() {
    // Prepare
    VoEBase* base = _mgr.BasePtr();
    int rnd(0);
  
    // Call random API function, ignore any error
  
    // This part should be equal to the marked part in the main thread
    // --- BEGIN ---
    rnd = rand();
    if (rnd < (RAND_MAX / 2)) {
      // Start playout
      base->StartPlayout(0);
    } else {
      // Stop playout
      base->StopPlayout(0);
    }
    // --- END ---
  
    return true;
  }
  
  }  // namespace voetest
  