RTCDataChannelStats[1] added, supporting all stats members.

Also updates MockDataChannel to also mock id, messages_sent, bytes_sent,
messages_received and bytes_received.

[1] https://w3c.github.io/webrtc-stats/#dcstats-dict*

BUG=chromium:654927, chromium:627816

Review-Url: https://codereview.webrtc.org/2420473002
Cr-Commit-Position: refs/heads/master@{#14670}
diff --git a/webrtc/api/rtcstatscollector.cc b/webrtc/api/rtcstatscollector.cc
index 5f60ea1..71f4725 100644
--- a/webrtc/api/rtcstatscollector.cc
+++ b/webrtc/api/rtcstatscollector.cc
@@ -23,6 +23,8 @@
 
 namespace webrtc {
 
+namespace {
+
 const char* CandidateTypeToRTCIceCandidateType(const std::string& type) {
   if (type == cricket::LOCAL_PORT_TYPE)
     return RTCIceCandidateType::kHost;
@@ -36,6 +38,25 @@
   return nullptr;
 }
 
+const char* DataStateToRTCDataChannelState(
+    DataChannelInterface::DataState state) {
+  switch (state) {
+    case DataChannelInterface::kConnecting:
+      return RTCDataChannelState::kConnecting;
+    case DataChannelInterface::kOpen:
+      return RTCDataChannelState::kOpen;
+    case DataChannelInterface::kClosing:
+      return RTCDataChannelState::kClosing;
+    case DataChannelInterface::kClosed:
+      return RTCDataChannelState::kClosed;
+    default:
+      RTC_NOTREACHED();
+      return nullptr;
+  }
+}
+
+}  // namespace
+
 rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create(
     PeerConnection* pc, int64_t cache_lifetime_us) {
   return rtc::scoped_refptr<RTCStatsCollector>(
@@ -111,6 +132,7 @@
     ProduceIceCandidateAndPairStats_s(timestamp_us, session_stats,
                                       report.get());
   }
+  ProduceDataChannelStats_s(timestamp_us, report.get());
   ProducePeerConnectionStats_s(timestamp_us, report.get());
 
   AddPartialResults(report);
@@ -218,6 +240,28 @@
   }
 }
 
+void RTCStatsCollector::ProduceDataChannelStats_s(
+    int64_t timestamp_us, RTCStatsReport* report) const {
+  RTC_DCHECK(signaling_thread_->IsCurrent());
+  for (const rtc::scoped_refptr<DataChannel>& data_channel :
+       pc_->sctp_data_channels()) {
+    std::unique_ptr<RTCDataChannelStats> data_channel_stats(
+        new RTCDataChannelStats(
+            "RTCDataChannel_" + rtc::ToString<>(data_channel->id()),
+            timestamp_us));
+    data_channel_stats->label = data_channel->label();
+    data_channel_stats->protocol = data_channel->protocol();
+    data_channel_stats->datachannelid = data_channel->id();
+    data_channel_stats->state =
+        DataStateToRTCDataChannelState(data_channel->state());
+    data_channel_stats->messages_sent = data_channel->messages_sent();
+    data_channel_stats->bytes_sent = data_channel->bytes_sent();
+    data_channel_stats->messages_received = data_channel->messages_received();
+    data_channel_stats->bytes_received = data_channel->bytes_received();
+    report->AddStats(std::move(data_channel_stats));
+  }
+}
+
 void RTCStatsCollector::ProduceIceCandidateAndPairStats_s(
       int64_t timestamp_us, const SessionStats& session_stats,
       RTCStatsReport* report) const {
@@ -337,4 +381,14 @@
   report->AddStats(std::move(stats));
 }
 
+const char* CandidateTypeToRTCIceCandidateTypeForTesting(
+    const std::string& type) {
+  return CandidateTypeToRTCIceCandidateType(type);
+}
+
+const char* DataStateToRTCDataChannelStateForTesting(
+    DataChannelInterface::DataState state) {
+  return DataStateToRTCDataChannelState(state);
+}
+
 }  // namespace webrtc
diff --git a/webrtc/api/rtcstatscollector.h b/webrtc/api/rtcstatscollector.h
index 712f9e2..ae04e61 100644
--- a/webrtc/api/rtcstatscollector.h
+++ b/webrtc/api/rtcstatscollector.h
@@ -14,6 +14,7 @@
 #include <memory>
 #include <vector>
 
+#include "webrtc/api/datachannelinterface.h"
 #include "webrtc/api/stats/rtcstats_objects.h"
 #include "webrtc/api/stats/rtcstatsreport.h"
 #include "webrtc/base/asyncinvoker.h"
@@ -85,6 +86,9 @@
   void ProduceCertificateStatsFromSSLCertificateAndChain_s(
       int64_t timestamp_us, const rtc::SSLCertificate& certificate,
       RTCStatsReport* report) const;
+  // Produces |RTCDataChannelStats|.
+  void ProduceDataChannelStats_s(
+      int64_t timestamp_us, RTCStatsReport* report) const;
   // Produces |RTCIceCandidatePairStats| and |RTCIceCandidateStats|.
   void ProduceIceCandidateAndPairStats_s(
       int64_t timestamp_us, const SessionStats& session_stats,
@@ -116,8 +120,10 @@
   rtc::scoped_refptr<const RTCStatsReport> cached_report_;
 };
 
-// Helper function, exposed for unittests.
-const char* CandidateTypeToRTCIceCandidateType(const std::string& type);
+const char* CandidateTypeToRTCIceCandidateTypeForTesting(
+    const std::string& type);
+const char* DataStateToRTCDataChannelStateForTesting(
+    DataChannelInterface::DataState state);
 
 }  // namespace webrtc
 
diff --git a/webrtc/api/rtcstatscollector_unittest.cc b/webrtc/api/rtcstatscollector_unittest.cc
index c8eed9f..fb07e0b 100644
--- a/webrtc/api/rtcstatscollector_unittest.cc
+++ b/webrtc/api/rtcstatscollector_unittest.cc
@@ -328,6 +328,10 @@
     rtc::scoped_refptr<StatsCallback> callback = StatsCallback::Create();
     collector_->GetStatsReport(callback);
     EXPECT_TRUE_WAIT(callback->report(), kGetStatsReportTimeoutMs);
+    int64_t after = rtc::TimeUTCMicros();
+    for (const RTCStats& stats : *callback->report()) {
+      EXPECT_LE(stats.timestamp_us(), after);
+    }
     return callback->report();
   }
 
@@ -347,7 +351,7 @@
               static_cast<int32_t>(candidate.address().port()));
     EXPECT_EQ(*candidate_stats->protocol, candidate.protocol());
     EXPECT_EQ(*candidate_stats->candidate_type,
-              CandidateTypeToRTCIceCandidateType(candidate.type()));
+              CandidateTypeToRTCIceCandidateTypeForTesting(candidate.type()));
     EXPECT_EQ(*candidate_stats->priority,
               static_cast<int32_t>(candidate.priority()));
     // TODO(hbos): Define candidate_stats->url. crbug.com/632723
@@ -438,6 +442,27 @@
     }
   }
 
+  void ExpectReportContainsDataChannel(
+      const rtc::scoped_refptr<const RTCStatsReport>& report,
+      const DataChannel& data_channel) {
+    const RTCStats* stats = report->Get("RTCDataChannel_" +
+                                        rtc::ToString<>(data_channel.id()));
+    EXPECT_TRUE(stats);
+    const RTCDataChannelStats& data_channel_stats =
+        stats->cast_to<const RTCDataChannelStats>();
+    EXPECT_EQ(*data_channel_stats.label, data_channel.label());
+    EXPECT_EQ(*data_channel_stats.protocol, data_channel.protocol());
+    EXPECT_EQ(*data_channel_stats.datachannelid, data_channel.id());
+    EXPECT_EQ(*data_channel_stats.state,
+        DataStateToRTCDataChannelStateForTesting(data_channel.state()));
+    EXPECT_EQ(*data_channel_stats.messages_sent, data_channel.messages_sent());
+    EXPECT_EQ(*data_channel_stats.bytes_sent, data_channel.bytes_sent());
+    EXPECT_EQ(*data_channel_stats.messages_received,
+              data_channel.messages_received());
+    EXPECT_EQ(*data_channel_stats.bytes_received,
+              data_channel.bytes_received());
+  }
+
  protected:
   rtc::scoped_refptr<RTCStatsCollectorTestHelper> test_;
   rtc::scoped_refptr<RTCStatsCollector> collector_;
@@ -643,6 +668,44 @@
   ExpectReportContainsCertificateInfo(report, *remote_certinfo.get());
 }
 
+TEST_F(RTCStatsCollectorTest, CollectRTCDataChannelStats) {
+  test_->data_channels().push_back(
+      new MockDataChannel(0, DataChannelInterface::kConnecting));
+  test_->data_channels().push_back(
+      new MockDataChannel(1, DataChannelInterface::kOpen));
+  test_->data_channels().push_back(
+      new MockDataChannel(2, DataChannelInterface::kClosing));
+  test_->data_channels().push_back(
+      new MockDataChannel(3, DataChannelInterface::kClosed));
+
+  rtc::scoped_refptr<const RTCStatsReport> report = GetStatsReport();
+  ExpectReportContainsDataChannel(report, *test_->data_channels()[0]);
+  ExpectReportContainsDataChannel(report, *test_->data_channels()[1]);
+  ExpectReportContainsDataChannel(report, *test_->data_channels()[2]);
+  ExpectReportContainsDataChannel(report, *test_->data_channels()[3]);
+
+  test_->data_channels().clear();
+  test_->data_channels().push_back(
+      new MockDataChannel(0, DataChannelInterface::kConnecting,
+                          1, 2, 3, 4));
+  test_->data_channels().push_back(
+      new MockDataChannel(1, DataChannelInterface::kOpen,
+                          5, 6, 7, 8));
+  test_->data_channels().push_back(
+      new MockDataChannel(2, DataChannelInterface::kClosing,
+                          9, 10, 11, 12));
+  test_->data_channels().push_back(
+      new MockDataChannel(3, DataChannelInterface::kClosed,
+                          13, 14, 15, 16));
+
+  collector_->ClearCachedStatsReport();
+  report = GetStatsReport();
+  ExpectReportContainsDataChannel(report, *test_->data_channels()[0]);
+  ExpectReportContainsDataChannel(report, *test_->data_channels()[1]);
+  ExpectReportContainsDataChannel(report, *test_->data_channels()[2]);
+  ExpectReportContainsDataChannel(report, *test_->data_channels()[3]);
+}
+
 TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) {
   // Candidates in the first transport stats.
   std::unique_ptr<cricket::Candidate> a_local_host = CreateFakeCandidate(
@@ -759,15 +822,11 @@
 }
 
 TEST_F(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) {
-  int64_t before = rtc::TimeUTCMicros();
   rtc::scoped_refptr<const RTCStatsReport> report = GetStatsReport();
-  int64_t after = rtc::TimeUTCMicros();
   EXPECT_EQ(report->GetStatsOfType<RTCPeerConnectionStats>().size(),
             static_cast<size_t>(1)) << "Expecting 1 RTCPeerConnectionStats.";
   const RTCStats* stats = report->Get("RTCPeerConnection");
   EXPECT_TRUE(stats);
-  EXPECT_LE(before, stats->timestamp_us());
-  EXPECT_LE(stats->timestamp_us(), after);
   {
     // Expected stats with no data channels
     const RTCPeerConnectionStats& pcstats =
@@ -777,13 +836,13 @@
   }
 
   test_->data_channels().push_back(
-      new MockDataChannel(DataChannelInterface::kConnecting));
+      new MockDataChannel(0, DataChannelInterface::kConnecting));
   test_->data_channels().push_back(
-      new MockDataChannel(DataChannelInterface::kOpen));
+      new MockDataChannel(1, DataChannelInterface::kOpen));
   test_->data_channels().push_back(
-      new MockDataChannel(DataChannelInterface::kClosing));
+      new MockDataChannel(2, DataChannelInterface::kClosing));
   test_->data_channels().push_back(
-      new MockDataChannel(DataChannelInterface::kClosed));
+      new MockDataChannel(3, DataChannelInterface::kClosed));
 
   collector_->ClearCachedStatsReport();
   report = GetStatsReport();
diff --git a/webrtc/api/stats/rtcstats_objects.h b/webrtc/api/stats/rtcstats_objects.h
index 4738fe8..ad6a39c 100644
--- a/webrtc/api/stats/rtcstats_objects.h
+++ b/webrtc/api/stats/rtcstats_objects.h
@@ -17,6 +17,14 @@
 
 namespace webrtc {
 
+// https://w3c.github.io/webrtc-pc/#idl-def-rtcdatachannelstate
+struct RTCDataChannelState {
+  static const char* kConnecting;
+  static const char* kOpen;
+  static const char* kClosing;
+  static const char* kClosed;
+};
+
 // https://w3c.github.io/webrtc-stats/#dom-rtcstatsicecandidatepairstate
 struct RTCStatsIceCandidatePairState {
   static const char* kFrozen;
@@ -27,7 +35,7 @@
   static const char* kCancelled;
 };
 
-// https://www.w3.org/TR/webrtc/#rtcicecandidatetype-enum
+// https://w3c.github.io/webrtc-pc/#rtcicecandidatetype-enum
 struct RTCIceCandidateType {
   static const char* kHost;
   static const char* kSrflx;
@@ -129,6 +137,27 @@
   RTCStatsMember<std::string> issuer_certificate_id;
 };
 
+// https://w3c.github.io/webrtc-stats/#dcstats-dict*
+class RTCDataChannelStats final : public RTCStats {
+ public:
+  WEBRTC_RTCSTATS_DECL();
+
+  RTCDataChannelStats(const std::string& id, int64_t timestamp_us);
+  RTCDataChannelStats(std::string&& id, int64_t timestamp_us);
+  RTCDataChannelStats(const RTCDataChannelStats& other);
+  ~RTCDataChannelStats() override;
+
+  RTCStatsMember<std::string> label;
+  RTCStatsMember<std::string> protocol;
+  RTCStatsMember<int32_t> datachannelid;
+  // TODO(hbos): Support enum types? "RTCStatsMember<RTCDataChannelState>"?
+  RTCStatsMember<std::string> state;
+  RTCStatsMember<uint32_t> messages_sent;
+  RTCStatsMember<uint64_t> bytes_sent;
+  RTCStatsMember<uint32_t> messages_received;
+  RTCStatsMember<uint64_t> bytes_received;
+};
+
 // https://w3c.github.io/webrtc-stats/#pcstats-dict*
 // TODO(hbos): Tracking bug crbug.com/636818
 class RTCPeerConnectionStats final : public RTCStats {
diff --git a/webrtc/api/test/mock_datachannel.h b/webrtc/api/test/mock_datachannel.h
index a09964e..1bb3984 100644
--- a/webrtc/api/test/mock_datachannel.h
+++ b/webrtc/api/test/mock_datachannel.h
@@ -18,12 +18,35 @@
 
 class MockDataChannel : public rtc::RefCountedObject<DataChannel> {
  public:
-  explicit MockDataChannel(DataState state)
+  MockDataChannel(int id, DataState state)
+      : MockDataChannel(id, state, 0, 0, 0, 0) {
+  }
+  MockDataChannel(
+      int id,
+      DataState state,
+      uint32_t messages_sent,
+      uint64_t bytes_sent,
+      uint32_t messages_received,
+      uint64_t bytes_received)
       : rtc::RefCountedObject<DataChannel>(
             nullptr, cricket::DCT_NONE, "MockDataChannel") {
+    EXPECT_CALL(*this, id()).WillRepeatedly(testing::Return(id));
     EXPECT_CALL(*this, state()).WillRepeatedly(testing::Return(state));
+    EXPECT_CALL(*this, messages_sent()).WillRepeatedly(
+        testing::Return(messages_sent));
+    EXPECT_CALL(*this, bytes_sent()).WillRepeatedly(
+        testing::Return(bytes_sent));
+    EXPECT_CALL(*this, messages_received()).WillRepeatedly(
+        testing::Return(messages_received));
+    EXPECT_CALL(*this, bytes_received()).WillRepeatedly(
+        testing::Return(bytes_received));
   }
+  MOCK_CONST_METHOD0(id, int());
   MOCK_CONST_METHOD0(state, DataState());
+  MOCK_CONST_METHOD0(messages_sent, uint32_t());
+  MOCK_CONST_METHOD0(bytes_sent, uint64_t());
+  MOCK_CONST_METHOD0(messages_received, uint32_t());
+  MOCK_CONST_METHOD0(bytes_received, uint64_t());
 };
 
 }  // namespace webrtc