Adding a test framework for conference mode application in VoE.

BUG=
R=phoglund@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/46249004

Cr-Commit-Position: refs/heads/master@{#9286}
diff --git a/webrtc/voice_engine/test/auto_test/fakes/conference_transport.cc b/webrtc/voice_engine/test/auto_test/fakes/conference_transport.cc
new file mode 100644
index 0000000..97b95c4
--- /dev/null
+++ b/webrtc/voice_engine/test/auto_test/fakes/conference_transport.cc
@@ -0,0 +1,275 @@
+/*
+ *  Copyright (c) 2015 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.
+ */
+
+#include "webrtc/voice_engine/test/auto_test/fakes/conference_transport.h"
+
+#include <string>
+
+#include "webrtc/base/byteorder.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/test/testsupport/fileutils.h"
+#include "webrtc/system_wrappers/interface/sleep.h"
+
+namespace {
+  static const unsigned int kReflectorSsrc = 0x0000;
+  static const unsigned int kLocalSsrc = 0x0001;
+  static const unsigned int kFirstRemoteSsrc = 0x0002;
+  static const std::string kInputFileName =
+      webrtc::test::ResourcePath("voice_engine/audio_long16", "pcm");
+  static const webrtc::FileFormats kInputFileFormat =
+      webrtc::kFileFormatPcm16kHzFile;
+  static const webrtc::CodecInst kCodecInst =
+      {120, "opus", 48000, 960, 2, 64000};
+
+  static unsigned int ParseSsrc(const void* data, size_t len, bool rtcp) {
+    const size_t ssrc_pos = (!rtcp) ? 8 : 4;
+    unsigned int ssrc = 0;
+    if (len >= (ssrc_pos + sizeof(ssrc))) {
+      ssrc = rtc::GetBE32(static_cast<const char*>(data) + ssrc_pos);
+    }
+    return ssrc;
+  }
+}  // namespace
+
+namespace voetest {
+
+ConferenceTransport::ConferenceTransport()
+    : pq_crit_(webrtc::CriticalSectionWrapper::CreateCriticalSection()),
+      stream_crit_(webrtc::CriticalSectionWrapper::CreateCriticalSection()),
+      packet_event_(webrtc::EventWrapper::Create()),
+      thread_(webrtc::ThreadWrapper::CreateThread(Run,
+                                                  this,
+                                                  "ConferenceTransport")),
+      rtt_ms_(0),
+      stream_count_(0) {
+  local_voe_ = webrtc::VoiceEngine::Create();
+  local_base_ = webrtc::VoEBase::GetInterface(local_voe_);
+  local_network_ = webrtc::VoENetwork::GetInterface(local_voe_);
+  local_rtp_rtcp_ = webrtc::VoERTP_RTCP::GetInterface(local_voe_);
+
+  // In principle, we can use one VoiceEngine to achieve the same goal. Well, in
+  // here, we use two engines to make it more like reality.
+  remote_voe_ = webrtc::VoiceEngine::Create();
+  remote_base_ = webrtc::VoEBase::GetInterface(remote_voe_);
+  remote_codec_ = webrtc::VoECodec::GetInterface(remote_voe_);
+  remote_network_ = webrtc::VoENetwork::GetInterface(remote_voe_);
+  remote_rtp_rtcp_ = webrtc::VoERTP_RTCP::GetInterface(remote_voe_);
+  remote_file_ = webrtc::VoEFile::GetInterface(remote_voe_);
+
+  EXPECT_EQ(0, local_base_->Init());
+  local_sender_ = local_base_->CreateChannel();
+  EXPECT_EQ(0, local_network_->RegisterExternalTransport(local_sender_, *this));
+  EXPECT_EQ(0, local_rtp_rtcp_->SetLocalSSRC(local_sender_, kLocalSsrc));
+  EXPECT_EQ(0, local_base_->StartSend(local_sender_));
+
+  EXPECT_EQ(0, remote_base_->Init());
+  reflector_ = remote_base_->CreateChannel();
+  EXPECT_EQ(0, remote_network_->RegisterExternalTransport(reflector_, *this));
+  EXPECT_EQ(0, remote_rtp_rtcp_->SetLocalSSRC(reflector_, kReflectorSsrc));
+
+  thread_->Start();
+  thread_->SetPriority(webrtc::kHighPriority);
+}
+
+ConferenceTransport::~ConferenceTransport() {
+  // Must stop sending, otherwise DispatchPackets() cannot quit.
+  EXPECT_EQ(0, remote_network_->DeRegisterExternalTransport(reflector_));
+  EXPECT_EQ(0, local_network_->DeRegisterExternalTransport(local_sender_));
+
+  for (auto stream : streams_) {
+    RemoveStream(stream.first);
+  }
+
+  EXPECT_TRUE(thread_->Stop());
+
+  remote_file_->Release();
+  remote_rtp_rtcp_->Release();
+  remote_network_->Release();
+  remote_base_->Release();
+
+  local_rtp_rtcp_->Release();
+  local_network_->Release();
+  local_base_->Release();
+
+  EXPECT_TRUE(webrtc::VoiceEngine::Delete(remote_voe_));
+  EXPECT_TRUE(webrtc::VoiceEngine::Delete(local_voe_));
+}
+
+int ConferenceTransport::SendPacket(int channel, const void* data, size_t len) {
+  StorePacket(Packet::Rtp, channel, data, len);
+  return static_cast<int>(len);
+}
+
+int ConferenceTransport::SendRTCPPacket(int channel, const void* data,
+                                        size_t len) {
+  StorePacket(Packet::Rtcp, channel, data, len);
+  return static_cast<int>(len);
+}
+
+int ConferenceTransport::GetReceiverChannelForSsrc(unsigned int sender_ssrc)
+    const {
+  webrtc::CriticalSectionScoped lock(stream_crit_.get());
+  auto it = streams_.find(sender_ssrc);
+  if (it != streams_.end()) {
+    return it->second.second;
+  }
+  return -1;
+}
+
+void ConferenceTransport::StorePacket(Packet::Type type, int channel,
+                                      const void* data, size_t len) {
+  {
+    webrtc::CriticalSectionScoped lock(pq_crit_.get());
+    packet_queue_.push_back(Packet(type, channel, data, len, rtc::Time()));
+  }
+  packet_event_->Set();
+}
+
+// This simulates the flow of RTP and RTCP packets. Complications like that
+// a packet is first sent to the reflector, and then forwarded to the receiver
+// are simplified, in this particular case, to a direct link between the sender
+// and the receiver.
+void ConferenceTransport::SendPacket(const Packet& packet) const {
+  unsigned int sender_ssrc;
+  int destination = -1;
+  switch (packet.type_) {
+    case Packet::Rtp:
+      sender_ssrc = ParseSsrc(packet.data_, packet.len_, false);
+      if (sender_ssrc == kLocalSsrc) {
+        remote_network_->ReceivedRTPPacket(reflector_, packet.data_,
+                                           packet.len_, webrtc::PacketTime());
+      } else {
+        destination = GetReceiverChannelForSsrc(sender_ssrc);
+        if (destination != -1) {
+          local_network_->ReceivedRTPPacket(destination, packet.data_,
+                                            packet.len_,
+                                            webrtc::PacketTime());
+        }
+      }
+      break;
+    case Packet::Rtcp:
+      sender_ssrc = ParseSsrc(packet.data_, packet.len_, true);
+      if (sender_ssrc == kLocalSsrc) {
+        remote_network_->ReceivedRTCPPacket(reflector_, packet.data_,
+                                            packet.len_);
+      } else if (sender_ssrc == kReflectorSsrc) {
+        local_network_->ReceivedRTCPPacket(local_sender_, packet.data_,
+                                           packet.len_);
+      } else {
+        destination = GetReceiverChannelForSsrc(sender_ssrc);
+        if (destination != -1) {
+          local_network_->ReceivedRTCPPacket(destination, packet.data_,
+                                             packet.len_);
+        }
+      }
+      break;
+  }
+}
+
+bool ConferenceTransport::DispatchPackets() {
+  switch (packet_event_->Wait(1000)) {
+    case webrtc::kEventSignaled:
+      break;
+    case webrtc::kEventTimeout:
+      return true;
+    case webrtc::kEventError:
+      ADD_FAILURE() << "kEventError encountered.";
+      return true;
+  }
+
+  while (true) {
+    Packet packet;
+    {
+      webrtc::CriticalSectionScoped lock(pq_crit_.get());
+      if (packet_queue_.empty())
+        break;
+      packet = packet_queue_.front();
+      packet_queue_.pop_front();
+    }
+
+    int32 elapsed_time_ms = rtc::TimeSince(packet.send_time_ms_);
+    int32 sleep_ms = rtt_ms_ / 2 - elapsed_time_ms;
+    if (sleep_ms > 0) {
+      // Every packet should be delayed by half of RTT.
+      webrtc::SleepMs(sleep_ms);
+    }
+
+    SendPacket(packet);
+  }
+  return true;
+}
+
+void ConferenceTransport::SetRtt(unsigned int rtt_ms) {
+  rtt_ms_ = rtt_ms;
+}
+
+unsigned int ConferenceTransport::AddStream() {
+  const int new_sender = remote_base_->CreateChannel();
+  EXPECT_EQ(0, remote_network_->RegisterExternalTransport(new_sender, *this));
+
+  const unsigned int remote_ssrc = kFirstRemoteSsrc + stream_count_++;
+  EXPECT_EQ(0, remote_rtp_rtcp_->SetLocalSSRC(new_sender, remote_ssrc));
+
+  EXPECT_EQ(0, remote_codec_->SetSendCodec(new_sender, kCodecInst));
+  EXPECT_EQ(0, remote_base_->StartSend(new_sender));
+  EXPECT_EQ(0, remote_file_->StartPlayingFileAsMicrophone(
+      new_sender, kInputFileName.c_str(), true, false,
+      kInputFileFormat, 1.0));
+
+  const int new_receiver = local_base_->CreateChannel();
+  EXPECT_EQ(0, local_base_->AssociateSendChannel(new_receiver, local_sender_));
+
+  EXPECT_EQ(0, local_network_->RegisterExternalTransport(new_receiver, *this));
+  // Receive channels have to have the same SSRC in order to send receiver
+  // reports with this SSRC.
+  EXPECT_EQ(0, local_rtp_rtcp_->SetLocalSSRC(new_receiver, kLocalSsrc));
+
+  {
+    webrtc::CriticalSectionScoped lock(stream_crit_.get());
+    streams_[remote_ssrc] = std::make_pair(new_sender, new_receiver);
+  }
+  return remote_ssrc;  // remote ssrc used as stream id.
+}
+
+bool ConferenceTransport::RemoveStream(unsigned int id) {
+  webrtc::CriticalSectionScoped lock(stream_crit_.get());
+  auto it = streams_.find(id);
+  if (it == streams_.end()) {
+    return false;
+  }
+  EXPECT_EQ(0, remote_network_->
+      DeRegisterExternalTransport(it->second.second));
+  EXPECT_EQ(0, local_network_->
+      DeRegisterExternalTransport(it->second.first));
+  EXPECT_EQ(0, remote_base_->DeleteChannel(it->second.second));
+  EXPECT_EQ(0, local_base_->DeleteChannel(it->second.first));
+  streams_.erase(it);
+  return true;
+}
+
+bool ConferenceTransport::StartPlayout(unsigned int id) {
+  int dst = GetReceiverChannelForSsrc(id);
+  if (dst == -1) {
+    return false;
+  }
+  EXPECT_EQ(0, local_base_->StartPlayout(dst));
+  return true;
+}
+
+bool ConferenceTransport::GetReceiverStatistics(unsigned int id,
+                                                webrtc::CallStatistics* stats) {
+  int dst = GetReceiverChannelForSsrc(id);
+  if (dst == -1) {
+    return false;
+  }
+  EXPECT_EQ(0, local_rtp_rtcp_->GetRTCPStatistics(dst, *stats));
+  return true;
+}
+}  // namespace voetest
diff --git a/webrtc/voice_engine/test/auto_test/fakes/conference_transport.h b/webrtc/voice_engine/test/auto_test/fakes/conference_transport.h
new file mode 100644
index 0000000..9f5546e
--- /dev/null
+++ b/webrtc/voice_engine/test/auto_test/fakes/conference_transport.h
@@ -0,0 +1,158 @@
+/*
+ *  Copyright (c) 2015 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.
+ */
+
+#ifndef WEBRTC_VOICE_ENGINE_TEST_AUTO_TEST_FAKES_CONFERENCE_TRANSPORT_H_
+#define WEBRTC_VOICE_ENGINE_TEST_AUTO_TEST_FAKES_CONFERENCE_TRANSPORT_H_
+
+#include <deque>
+#include <map>
+#include <utility>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/basictypes.h"
+#include "webrtc/base/scoped_ptr.h"
+#include "webrtc/common_types.h"
+#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
+#include "webrtc/system_wrappers/interface/event_wrapper.h"
+#include "webrtc/system_wrappers/interface/thread_wrapper.h"
+#include "webrtc/voice_engine/include/voe_base.h"
+#include "webrtc/voice_engine/include/voe_codec.h"
+#include "webrtc/voice_engine/include/voe_file.h"
+#include "webrtc/voice_engine/include/voe_network.h"
+#include "webrtc/voice_engine/include/voe_rtp_rtcp.h"
+
+
+static const size_t kMaxPacketSizeByte = 1500;
+
+namespace voetest {
+
+// This class is to simulate a conference call. There are two Voice Engines, one
+// for local channels and the other for remote channels. There is a simulated
+// reflector, which exchanges RTCP with local channels. For simplicity, it
+// also uses the Voice Engine for remote channels. One can add streams by
+// calling AddStream(), which creates a remote sender channel and a local
+// receive channel. The remote sender channel plays a file as microphone in a
+// looped fashion. Received streams are mixed and played.
+
+class ConferenceTransport: public webrtc::Transport {
+ public:
+  ConferenceTransport();
+  virtual ~ConferenceTransport();
+
+  /* SetRtt()
+   * Set RTT between local channels and reflector.
+   *
+   * Input:
+   *   rtt_ms : RTT in milliseconds.
+   */
+  void SetRtt(unsigned int rtt_ms);
+
+  /* AddStream()
+   * Adds a stream in the conference.
+   *
+   * Returns stream id.
+   */
+  unsigned int AddStream();
+
+  /* RemoveStream()
+   * Removes a stream with specified ID from the conference.
+   *
+   * Input:
+   *   id : stream id.
+   *
+   * Returns false if the specified stream does not exist, true if succeeds.
+   */
+  bool RemoveStream(unsigned int id);
+
+  /* StartPlayout()
+   * Starts playing out the stream with specified ID, using the default device.
+   *
+   * Input:
+   *   id : stream id.
+   *
+   * Returns false if the specified stream does not exist, true if succeeds.
+   */
+  bool StartPlayout(unsigned int id);
+
+  /* GetReceiverStatistics()
+   * Gets RTCP statistics of the stream with specified ID.
+   *
+   * Input:
+   *   id : stream id;
+   *   stats : pointer to a CallStatistics to store the result.
+   *
+   * Returns false if the specified stream does not exist, true if succeeds.
+   */
+  bool GetReceiverStatistics(unsigned int id, webrtc::CallStatistics* stats);
+
+  // Inherit from class webrtc::Transport.
+  int SendPacket(int channel, const void *data, size_t len) override;
+  int SendRTCPPacket(int channel, const void *data, size_t len) override;
+
+ private:
+  struct Packet {
+    enum Type { Rtp, Rtcp, } type_;
+
+    Packet() : len_(0) {}
+    Packet(Type type, int channel, const void* data, size_t len, uint32 time_ms)
+        : type_(type),
+          channel_(channel),
+          len_(len),
+          send_time_ms_(time_ms) {
+      EXPECT_LE(len_, kMaxPacketSizeByte);
+      memcpy(data_, data, len_);
+    }
+
+    int channel_;
+    uint8_t data_[kMaxPacketSizeByte];
+    size_t len_;
+    uint32 send_time_ms_;
+  };
+
+  static bool Run(void* transport) {
+    return static_cast<ConferenceTransport*>(transport)->DispatchPackets();
+  }
+
+  int GetReceiverChannelForSsrc(unsigned int sender_ssrc) const;
+  void StorePacket(Packet::Type type, int channel, const void* data,
+                   size_t len);
+  void SendPacket(const Packet& packet) const;
+  bool DispatchPackets();
+
+  const rtc::scoped_ptr<webrtc::CriticalSectionWrapper> pq_crit_;
+  const rtc::scoped_ptr<webrtc::CriticalSectionWrapper> stream_crit_;
+  const rtc::scoped_ptr<webrtc::EventWrapper> packet_event_;
+  const rtc::scoped_ptr<webrtc::ThreadWrapper> thread_;
+
+  unsigned int rtt_ms_;
+  unsigned int stream_count_;
+
+  std::map<unsigned int, std::pair<int, int>> streams_
+    GUARDED_BY(stream_crit_.get());
+  std::deque<Packet> packet_queue_ GUARDED_BY(pq_crit_.get());
+
+  int local_sender_;  // Channel Id of local sender
+  int reflector_;
+
+  webrtc::VoiceEngine* local_voe_;
+  webrtc::VoEBase* local_base_;
+  webrtc::VoERTP_RTCP* local_rtp_rtcp_;
+  webrtc::VoENetwork* local_network_;
+
+  webrtc::VoiceEngine* remote_voe_;
+  webrtc::VoEBase* remote_base_;
+  webrtc::VoECodec* remote_codec_;
+  webrtc::VoERTP_RTCP* remote_rtp_rtcp_;
+  webrtc::VoENetwork* remote_network_;
+  webrtc::VoEFile* remote_file_;
+};
+}  // namespace voetest
+
+#endif  // WEBRTC_VOICE_ENGINE_TEST_AUTO_TEST_FAKES_CONFERENCE_TRANSPORT_H_
diff --git a/webrtc/voice_engine/test/auto_test/voe_conference_test.cc b/webrtc/voice_engine/test/auto_test/voe_conference_test.cc
new file mode 100644
index 0000000..20a74b4
--- /dev/null
+++ b/webrtc/voice_engine/test/auto_test/voe_conference_test.cc
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (c) 2015 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.
+ */
+
+#include <queue>
+
+#include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/base/format_macros.h"
+#include "webrtc/base/timeutils.h"
+#include "webrtc/system_wrappers/interface/sleep.h"
+#include "webrtc/voice_engine/test/auto_test/fakes/conference_transport.h"
+
+namespace {
+  static const int kRttMs = 25;
+
+  static bool IsNear(int ref, int comp, int error) {
+    return (ref - comp <= error) && (comp - ref >= -error);
+  }
+}
+
+namespace voetest {
+
+TEST(VoeConferenceTest, RttAndStartNtpTime) {
+  struct Stats {
+    Stats(int64_t rtt_receiver_1, int64_t rtt_receiver_2, int64_t ntp_delay)
+        : rtt_receiver_1_(rtt_receiver_1),
+          rtt_receiver_2_(rtt_receiver_2),
+          ntp_delay_(ntp_delay) {
+    }
+    int64_t rtt_receiver_1_;
+    int64_t rtt_receiver_2_;
+    int64_t ntp_delay_;
+  };
+
+  const int kDelayMs = 987;
+  ConferenceTransport trans;
+  trans.SetRtt(kRttMs);
+
+  unsigned int id_1 = trans.AddStream();
+  unsigned int id_2 = trans.AddStream();
+
+  EXPECT_TRUE(trans.StartPlayout(id_1));
+  // Start NTP time is the time when a stream is played out, rather than
+  // when it is added.
+  webrtc::SleepMs(kDelayMs);
+  EXPECT_TRUE(trans.StartPlayout(id_2));
+
+  const int kMaxRunTimeMs = 25000;
+  const int kNeedSuccessivePass = 3;
+  const int kStatsRequestIntervalMs = 1000;
+  const int kStatsBufferSize = 3;
+
+  uint32 deadline = rtc::TimeAfter(kMaxRunTimeMs);
+  // Run the following up to |kMaxRunTimeMs| milliseconds.
+  int successive_pass = 0;
+  webrtc::CallStatistics stats_1;
+  webrtc::CallStatistics stats_2;
+  std::queue<Stats> stats_buffer;
+
+  while (rtc::TimeIsLater(rtc::Time(), deadline) &&
+      successive_pass < kNeedSuccessivePass) {
+    webrtc::SleepMs(kStatsRequestIntervalMs);
+
+    EXPECT_TRUE(trans.GetReceiverStatistics(id_1, &stats_1));
+    EXPECT_TRUE(trans.GetReceiverStatistics(id_2, &stats_2));
+
+    // It is not easy to verify the NTP time directly. We verify it by testing
+    // the difference of two start NTP times.
+    int64_t captured_start_ntp_delay = stats_2.capture_start_ntp_time_ms_ -
+        stats_1.capture_start_ntp_time_ms_;
+
+    // For the checks of RTT and start NTP time, We allow 10% accuracy.
+    if (IsNear(kRttMs, stats_1.rttMs, kRttMs / 10 + 1) &&
+        IsNear(kRttMs, stats_2.rttMs, kRttMs / 10 + 1) &&
+        IsNear(kDelayMs, captured_start_ntp_delay, kDelayMs / 10 + 1)) {
+      successive_pass++;
+    } else {
+      successive_pass = 0;
+    }
+    if (stats_buffer.size() >= kStatsBufferSize) {
+      stats_buffer.pop();
+    }
+    stats_buffer.push(Stats(stats_1.rttMs, stats_2.rttMs,
+                            captured_start_ntp_delay));
+  }
+
+  EXPECT_GE(successive_pass, kNeedSuccessivePass) << "Expected to get RTT and"
+      " start NTP time estimate within 10% of the correct value over "
+      << kStatsRequestIntervalMs * kNeedSuccessivePass / 1000
+      << " seconds.";
+  if (successive_pass < kNeedSuccessivePass) {
+    printf("The most recent values (RTT for receiver 1, RTT for receiver 2, "
+        "NTP delay between receiver 1 and 2) are (from oldest):\n");
+    while (!stats_buffer.empty()) {
+      Stats stats = stats_buffer.front();
+      printf("(%" PRId64 ", %" PRId64 ", %" PRId64 ")\n", stats.rtt_receiver_1_,
+             stats.rtt_receiver_2_, stats.ntp_delay_);
+      stats_buffer.pop();
+    }
+  }
+}
+}  // namespace voetest
diff --git a/webrtc/voice_engine/voice_engine.gyp b/webrtc/voice_engine/voice_engine.gyp
index 077b193..ad4a625 100644
--- a/webrtc/voice_engine/voice_engine.gyp
+++ b/webrtc/voice_engine/voice_engine.gyp
@@ -158,6 +158,8 @@
             'test/auto_test/automated_mode.cc',
             'test/auto_test/extended/agc_config_test.cc',
             'test/auto_test/extended/ec_metrics_test.cc',
+            'test/auto_test/fakes/conference_transport.cc',
+            'test/auto_test/fakes/conference_transport.h',
             'test/auto_test/fakes/fake_external_transport.cc',
             'test/auto_test/fakes/fake_external_transport.h',
             'test/auto_test/fixtures/after_initialization_fixture.cc',
@@ -187,6 +189,7 @@
             'test/auto_test/standard/video_sync_test.cc',
             'test/auto_test/standard/volume_test.cc',
             'test/auto_test/resource_manager.cc',
+            'test/auto_test/voe_conference_test.cc',
             'test/auto_test/voe_cpu_test.cc',
             'test/auto_test/voe_cpu_test.h',
             'test/auto_test/voe_standard_test.cc',