Fix datachannel stats id and timestamp.

Makes the id now be "datachannel_#####" where '####' is the id number for the datachannel.

Adds a timestamp to the data channel reports.

Implements unit tests to verify that the timestamp is set correctly.

BUG=1805
R=juberti@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#8236}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8236 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/talk/app/webrtc/statscollector.cc b/talk/app/webrtc/statscollector.cc
index 125e52c..499df66 100644
--- a/talk/app/webrtc/statscollector.cc
+++ b/talk/app/webrtc/statscollector.cc
@@ -56,10 +56,6 @@
 const char* STATSREPORT_ADAPTER_TYPE_WWAN = "wwan";
 const char* STATSREPORT_ADAPTER_TYPE_VPN = "vpn";
 
-double GetTimeNow() {
-  return rtc::Timing::WallTimeNow() * rtc::kNumMillisecsPerSec;
-}
-
 bool GetTransportIdFromProxy(const cricket::ProxyTransportMap& map,
                              const std::string& proxy,
                              std::string* transport) {
@@ -379,6 +375,10 @@
   ASSERT(session_->signaling_thread()->IsCurrent());
 }
 
+double StatsCollector::GetTimeNow() {
+  return rtc::Timing::WallTimeNow() * rtc::kNumMillisecsPerSec;
+}
+
 // Adds a MediaStream with tracks that can be used as a |selector| in a call
 // to GetStats.
 void StatsCollector::AddStream(MediaStreamInterface* stream) {
@@ -830,10 +830,10 @@
 
   for (const auto& dc :
            session_->mediastream_signaling()->sctp_data_channels()) {
-    rtc::scoped_ptr<StatsReport::Id> id(
-        StatsReport::NewTypedId(StatsReport::kStatsReportTypeDataChannel,
-                                dc->label()));
+    rtc::scoped_ptr<StatsReport::Id> id(StatsReport::NewTypedIntId(
+        StatsReport::kStatsReportTypeDataChannel, dc->id()));
     StatsReport* report = reports_.ReplaceOrAddNew(id.Pass());
+    report->set_timestamp(stats_gathering_started_);
     report->AddValue(StatsReport::kStatsValueNameLabel, dc->label());
     report->AddValue(StatsReport::kStatsValueNameDataChannelId, dc->id());
     report->AddValue(StatsReport::kStatsValueNameProtocol, dc->protocol());
diff --git a/talk/app/webrtc/statscollector.h b/talk/app/webrtc/statscollector.h
index 499da44..a4e42c9 100644
--- a/talk/app/webrtc/statscollector.h
+++ b/talk/app/webrtc/statscollector.h
@@ -97,6 +97,9 @@
  private:
   friend class StatsCollectorTest;
 
+  // Overridden in unit tests to fake timing.
+  virtual double GetTimeNow();
+
   bool CopySelectedReports(const std::string& selector, StatsReports* reports);
 
   // Helper method for AddCertificateReports.
diff --git a/talk/app/webrtc/statscollector_unittest.cc b/talk/app/webrtc/statscollector_unittest.cc
index b8c983b..96975c2 100644
--- a/talk/app/webrtc/statscollector_unittest.cc
+++ b/talk/app/webrtc/statscollector_unittest.cc
@@ -455,6 +455,20 @@
   voice_receiver_info->expand_rate = 121;
 }
 
+class StatsCollectorForTest : public webrtc::StatsCollector {
+ public:
+  explicit StatsCollectorForTest(WebRtcSession* session) :
+      StatsCollector(session), time_now_(19477) {
+  }
+
+  double GetTimeNow() override {
+    return time_now_;
+  }
+
+ private:
+  double time_now_;
+};
+
 class StatsCollectorTest : public testing::Test {
  protected:
   StatsCollectorTest()
@@ -615,7 +629,7 @@
                               const std::vector<std::string>& local_ders,
                               const rtc::FakeSSLCertificate& remote_cert,
                               const std::vector<std::string>& remote_ders) {
-    webrtc::StatsCollector stats(&session_);
+    StatsCollectorForTest stats(&session_);
 
     StatsReports reports;  // returned values.
 
@@ -718,12 +732,22 @@
   config.id = id;
   signaling_.AddDataChannel(DataChannel::Create(
       &data_channel_provider_, cricket::DCT_SCTP, label, config));
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   stats.UpdateStats(PeerConnectionInterface::kStatsOutputLevelStandard);
 
   StatsReports reports;
   stats.GetStats(NULL, &reports);
+
+  const StatsReport* report =
+      FindNthReportByType(reports, StatsReport::kStatsReportTypeDataChannel, 1);
+
+  scoped_ptr<StatsReport::Id> reportId = StatsReport::NewTypedIntId(
+      StatsReport::kStatsReportTypeDataChannel, id);
+
+  EXPECT_TRUE(reportId->Equals(report->id()));
+
+  EXPECT_EQ(stats.GetTimeNow(), report->timestamp());
   EXPECT_EQ(label, ExtractStatsValue(StatsReport::kStatsReportTypeDataChannel,
                                      reports,
                                      StatsReport::kStatsValueNameLabel));
@@ -741,7 +765,7 @@
 
 // This test verifies that 64-bit counters are passed successfully.
 TEST_F(StatsCollectorTest, BytesCounterHandles64Bits) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
@@ -775,7 +799,7 @@
 
 // Test that BWE information is reported via stats.
 TEST_F(StatsCollectorTest, BandwidthEstimationInfoIsReported) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
@@ -820,7 +844,7 @@
 // This test verifies that an object of type "googSession" always
 // exists in the returned stats.
 TEST_F(StatsCollectorTest, SessionObjectExists) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   StatsReports reports;  // returned values.
   EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull());
@@ -835,7 +859,7 @@
 // This test verifies that only one object of type "googSession" exists
 // in the returned stats.
 TEST_F(StatsCollectorTest, OnlyOneSessionObjectExists) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   StatsReports reports;  // returned values.
   EXPECT_CALL(session_, video_channel()).WillRepeatedly(ReturnNull());
@@ -854,7 +878,7 @@
 // This test verifies that the empty track report exists in the returned stats
 // without calling StatsCollector::UpdateStats.
 TEST_F(StatsCollectorTest, TrackObjectExistsWithoutUpdateStats) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
@@ -878,7 +902,7 @@
 // This test verifies that the empty track report exists in the returned stats
 // when StatsCollector::UpdateStats is called with ssrc stats.
 TEST_F(StatsCollectorTest, TrackAndSsrcObjectExistAfterUpdateSsrcStats) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
@@ -934,7 +958,7 @@
 // This test verifies that an SSRC object has the identifier of a Transport
 // stats object, and that this transport stats object exists in stats.
 TEST_F(StatsCollectorTest, TransportObjectLinkedFromSsrcObject) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   // Ignore unused callback (logspam).
   EXPECT_CALL(session_, GetTransport(_))
@@ -995,7 +1019,7 @@
 // This test verifies that a remote stats object will not be created for
 // an outgoing SSRC where remote stats are not returned.
 TEST_F(StatsCollectorTest, RemoteSsrcInfoIsAbsent) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   // The content_name known by the video channel.
@@ -1019,7 +1043,7 @@
 // This test verifies that a remote stats object will be created for
 // an outgoing SSRC where stats are returned.
 TEST_F(StatsCollectorTest, RemoteSsrcInfoIsPresent) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   // Ignore unused callback (logspam).
   EXPECT_CALL(session_, GetTransport(_))
@@ -1062,13 +1086,13 @@
   const StatsReport* remote_report = FindNthReportByType(reports,
       StatsReport::kStatsReportTypeRemoteSsrc, 1);
   EXPECT_FALSE(remote_report == NULL);
-  EXPECT_NE(0, remote_report->timestamp());
+  EXPECT_EQ(12345.678, remote_report->timestamp());
 }
 
 // This test verifies that the empty track report exists in the returned stats
 // when StatsCollector::UpdateStats is called with ssrc stats.
 TEST_F(StatsCollectorTest, ReportsFromRemoteTrack) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
@@ -1114,7 +1138,7 @@
 // This test verifies the Ice Candidate report should contain the correct
 // information from local/remote candidates.
 TEST_F(StatsCollectorTest, IceCandidateReport) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   StatsReports reports;                     // returned values.
 
@@ -1244,7 +1268,7 @@
 // This test verifies that the stats are generated correctly when no
 // transport is present.
 TEST_F(StatsCollectorTest, NoTransport) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   StatsReports reports;  // returned values.
 
@@ -1291,7 +1315,7 @@
 // This test verifies that the stats are generated correctly when the transport
 // does not have any certificates.
 TEST_F(StatsCollectorTest, NoCertificates) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   StatsReports reports;  // returned values.
 
@@ -1360,7 +1384,7 @@
 // Verifies the correct optons are passed to the VideoMediaChannel when using
 // verbose output level.
 TEST_F(StatsCollectorTest, StatsOutputLevelVerbose) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   MockVideoMediaChannel* media_channel = new MockVideoMediaChannel();
   cricket::VideoChannel video_channel(rtc::Thread::Current(),
@@ -1406,7 +1430,7 @@
 // This test verifies that a local stats object can get statistics via
 // AudioTrackInterface::GetStats() method.
 TEST_F(StatsCollectorTest, GetStatsFromLocalAudioTrack) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   // Ignore unused callback (logspam).
   EXPECT_CALL(session_, GetTransport(_))
@@ -1440,7 +1464,7 @@
 // This test verifies that audio receive streams populate stats reports
 // correctly.
 TEST_F(StatsCollectorTest, GetStatsFromRemoteStream) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   // Ignore unused callback (logspam).
   EXPECT_CALL(session_, GetTransport(_))
@@ -1467,7 +1491,7 @@
 // This test verifies that a local stats object won't update its statistics
 // after a RemoveLocalAudioTrack() call.
 TEST_F(StatsCollectorTest, GetStatsAfterRemoveAudioStream) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   // Ignore unused callback (logspam).
   EXPECT_CALL(session_, GetTransport(_))
@@ -1525,7 +1549,7 @@
 // This test verifies that when ongoing and incoming audio tracks are using
 // the same ssrc, they populate stats reports correctly.
 TEST_F(StatsCollectorTest, LocalAndRemoteTracksWithSameSsrc) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   // Ignore unused callback (logspam).
   EXPECT_CALL(session_, GetTransport(_))
@@ -1608,7 +1632,7 @@
 // TODO(xians): Figure out if it is possible to encapsulate the setup and
 // avoid duplication of code in test cases.
 TEST_F(StatsCollectorTest, TwoLocalTracksWithSameSsrc) {
-  webrtc::StatsCollector stats(&session_);
+  StatsCollectorForTest stats(&session_);
 
   // Ignore unused callback (logspam).
   EXPECT_CALL(session_, GetTransport(_))
diff --git a/talk/app/webrtc/statstypes.cc b/talk/app/webrtc/statstypes.cc
index a9f30e5..31ae740 100644
--- a/talk/app/webrtc/statstypes.cc
+++ b/talk/app/webrtc/statstypes.cc
@@ -76,7 +76,6 @@
 
 class TypedId : public StatsReport::Id {
  public:
-  static const char kSeparator = '_';
   TypedId(StatsReport::StatsType type, const std::string& id)
       : StatsReport::Id(type), id_(id) {}
 
@@ -93,6 +92,26 @@
   const std::string id_;
 };
 
+class TypedIntId : public StatsReport::Id {
+ public:
+  TypedIntId(StatsReport::StatsType type, int id)
+      : StatsReport::Id(type), id_(id) {}
+
+  bool Equals(const Id& other) const override {
+    return Id::Equals(other) &&
+           static_cast<const TypedIntId&>(other).id_ == id_;
+  }
+
+  std::string ToString() const override {
+    return std::string(InternalTypeToString(type_)) +
+           kSeparator +
+           rtc::ToString<int>(id_);
+  }
+
+ protected:
+  const int id_;
+};
+
 class IdWithDirection : public TypedId {
  public:
   IdWithDirection(StatsReport::StatsType type, const std::string& id,
@@ -106,7 +125,7 @@
 
   std::string ToString() const override {
     std::string ret(TypedId::ToString());
-    ret += '_';
+    ret += kSeparator;
     ret += direction_ == StatsReport::kSend ? "send" : "recv";
     return ret;
   }
@@ -437,6 +456,11 @@
 }
 
 // static
+scoped_ptr<StatsReport::Id> StatsReport::NewTypedIntId(StatsType type, int id) {
+  return scoped_ptr<Id>(new TypedIntId(type, id)).Pass();
+}
+
+// static
 scoped_ptr<StatsReport::Id> StatsReport::NewIdWithDirection(
     StatsType type, const std::string& id, StatsReport::Direction direction) {
   return scoped_ptr<Id>(new IdWithDirection(type, id, direction)).Pass();
diff --git a/talk/app/webrtc/statstypes.h b/talk/app/webrtc/statstypes.h
index 8f132db..a3abc2e 100644
--- a/talk/app/webrtc/statstypes.h
+++ b/talk/app/webrtc/statstypes.h
@@ -229,6 +229,8 @@
    protected:
     Id(StatsType type);  // Only meant for derived classes.
     const StatsType type_;
+
+    static const char kSeparator = '_';
   };
 
   struct Value {
@@ -254,6 +256,7 @@
   // Factory functions for various types of stats IDs.
   static rtc::scoped_ptr<Id> NewBandwidthEstimationId();
   static rtc::scoped_ptr<Id> NewTypedId(StatsType type, const std::string& id);
+  static rtc::scoped_ptr<Id> NewTypedIntId(StatsType type, int id);
   static rtc::scoped_ptr<Id> NewIdWithDirection(
       StatsType type, const std::string& id, Direction direction);
   static rtc::scoped_ptr<Id> NewCandidateId(bool local, const std::string& id);