Files
webrtc/stats_go_test.go
Atsushi Watanabe b0d95d59da Fix TestPeerConnection_GetStats stability
Add sleep to ensure counting the last packet.
2020-04-07 13:49:20 -07:00

310 lines
9.1 KiB
Go

// +build !js
package webrtc
import (
"encoding/json"
"fmt"
"sync"
"testing"
"time"
"github.com/stretchr/testify/assert"
)
func TestStatsTimestampTime(t *testing.T) {
for _, test := range []struct {
Timestamp StatsTimestamp
WantTime time.Time
}{
{
Timestamp: 0,
WantTime: time.Unix(0, 0),
},
{
Timestamp: 1,
WantTime: time.Unix(0, 1e6),
},
{
Timestamp: 0.001,
WantTime: time.Unix(0, 1e3),
},
} {
if got, want := test.Timestamp.Time(), test.WantTime.UTC(); got != want {
t.Fatalf("StatsTimestamp(%v).Time() = %v, want %v", test.Timestamp, got, want)
}
}
}
// TODO(maxhawkins): replace with a more meaningful test
func TestStatsMarshal(t *testing.T) {
for _, test := range []Stats{
AudioReceiverStats{},
AudioSenderStats{},
CertificateStats{},
CodecStats{},
DataChannelStats{},
ICECandidatePairStats{},
ICECandidateStats{},
InboundRTPStreamStats{},
MediaStreamStats{},
OutboundRTPStreamStats{},
PeerConnectionStats{},
RemoteInboundRTPStreamStats{},
RemoteOutboundRTPStreamStats{},
RTPContributingSourceStats{},
SenderAudioTrackAttachmentStats{},
SenderAudioTrackAttachmentStats{},
SenderVideoTrackAttachmentStats{},
TransportStats{},
VideoReceiverStats{},
VideoReceiverStats{},
VideoSenderStats{},
} {
_, err := json.Marshal(test)
if err != nil {
t.Fatal(err)
}
}
}
func waitWithTimeout(t *testing.T, wg *sync.WaitGroup) {
// Wait for all of the event handlers to be triggered.
done := make(chan struct{})
go func() {
wg.Wait()
done <- struct{}{}
}()
timeout := time.After(5 * time.Second)
select {
case <-done:
break
case <-timeout:
t.Fatal("timed out waiting for waitgroup")
}
}
func getConnectionStats(t *testing.T, report StatsReport, pc *PeerConnection) PeerConnectionStats {
stats, ok := report.GetConnectionStats(pc)
assert.True(t, ok)
assert.Equal(t, stats.Type, StatsTypePeerConnection)
return stats
}
func getDataChannelStats(t *testing.T, report StatsReport, dc *DataChannel) DataChannelStats {
stats, ok := report.GetDataChannelStats(dc)
assert.True(t, ok)
assert.Equal(t, stats.Type, StatsTypeDataChannel)
return stats
}
func getTransportStats(t *testing.T, report StatsReport, statsID string) TransportStats {
stats, ok := report[statsID]
assert.True(t, ok)
transportStats, ok := stats.(TransportStats)
assert.True(t, ok)
assert.Equal(t, transportStats.Type, StatsTypeTransport)
return transportStats
}
func findLocalCandidateStats(report StatsReport) []ICECandidateStats {
result := []ICECandidateStats{}
for _, s := range report {
stats, ok := s.(ICECandidateStats)
if ok && stats.Type == StatsTypeLocalCandidate {
result = append(result, stats)
}
}
return result
}
func findRemoteCandidateStats(report StatsReport) []ICECandidateStats {
result := []ICECandidateStats{}
for _, s := range report {
stats, ok := s.(ICECandidateStats)
if ok && stats.Type == StatsTypeRemoteCandidate {
result = append(result, stats)
}
}
return result
}
func findCandidatePairStats(t *testing.T, report StatsReport) []ICECandidatePairStats {
result := []ICECandidatePairStats{}
for _, s := range report {
stats, ok := s.(ICECandidatePairStats)
if ok {
assert.Equal(t, StatsTypeCandidatePair, stats.Type)
result = append(result, stats)
}
}
return result
}
func signalPairForStats(pcOffer *PeerConnection, pcAnswer *PeerConnection) error {
offerChan := make(chan SessionDescription)
pcOffer.OnICECandidate(func(candidate *ICECandidate) {
if candidate == nil {
offerChan <- *pcOffer.PendingLocalDescription()
}
})
offer, err := pcOffer.CreateOffer(nil)
if err != nil {
return err
}
if err := pcOffer.SetLocalDescription(offer); err != nil {
return err
}
timeout := time.After(3 * time.Second)
select {
case <-timeout:
return fmt.Errorf("timed out waiting to receive offer")
case offer := <-offerChan:
if err := pcAnswer.SetRemoteDescription(offer); err != nil {
return err
}
answer, err := pcAnswer.CreateAnswer(nil)
if err != nil {
return err
}
if err = pcAnswer.SetLocalDescription(answer); err != nil {
return err
}
err = pcOffer.SetRemoteDescription(answer)
if err != nil {
return err
}
return nil
}
}
func TestPeerConnection_GetStats(t *testing.T) {
offerPC, answerPC, err := newPair()
assert.NoError(t, err)
baseLineReportPCOffer := offerPC.GetStats()
baseLineReportPCAnswer := answerPC.GetStats()
connStatsOffer := getConnectionStats(t, baseLineReportPCOffer, offerPC)
connStatsAnswer := getConnectionStats(t, baseLineReportPCAnswer, answerPC)
for _, connStats := range []PeerConnectionStats{connStatsOffer, connStatsAnswer} {
assert.Equal(t, uint32(0), connStats.DataChannelsOpened)
assert.Equal(t, uint32(0), connStats.DataChannelsClosed)
assert.Equal(t, uint32(0), connStats.DataChannelsRequested)
assert.Equal(t, uint32(0), connStats.DataChannelsAccepted)
}
// Create a DC, open it and send a message
offerDC, err := offerPC.CreateDataChannel("offerDC", nil)
assert.NoError(t, err)
msg := []byte("a classic test message")
offerDC.OnOpen(func() {
assert.NoError(t, offerDC.Send(msg))
})
dcWait := sync.WaitGroup{}
dcWait.Add(1)
answerDCChan := make(chan *DataChannel)
answerPC.OnDataChannel(func(d *DataChannel) {
d.OnOpen(func() {
answerDCChan <- d
})
d.OnMessage(func(m DataChannelMessage) {
dcWait.Done()
})
})
assert.NoError(t, signalPairForStats(offerPC, answerPC))
waitWithTimeout(t, &dcWait)
answerDC := <-answerDCChan
reportPCOffer := offerPC.GetStats()
reportPCAnswer := answerPC.GetStats()
connStatsOffer = getConnectionStats(t, reportPCOffer, offerPC)
assert.Equal(t, uint32(1), connStatsOffer.DataChannelsOpened)
assert.Equal(t, uint32(0), connStatsOffer.DataChannelsClosed)
assert.Equal(t, uint32(1), connStatsOffer.DataChannelsRequested)
assert.Equal(t, uint32(0), connStatsOffer.DataChannelsAccepted)
dcStatsOffer := getDataChannelStats(t, reportPCOffer, offerDC)
assert.Equal(t, DataChannelStateOpen, dcStatsOffer.State)
assert.Equal(t, uint32(1), dcStatsOffer.MessagesSent)
assert.Equal(t, uint64(len(msg)), dcStatsOffer.BytesSent)
assert.NotEmpty(t, findLocalCandidateStats(reportPCOffer))
assert.NotEmpty(t, findRemoteCandidateStats(reportPCOffer))
assert.NotEmpty(t, findCandidatePairStats(t, reportPCOffer))
connStatsAnswer = getConnectionStats(t, reportPCAnswer, answerPC)
assert.Equal(t, uint32(1), connStatsAnswer.DataChannelsOpened)
assert.Equal(t, uint32(0), connStatsAnswer.DataChannelsClosed)
assert.Equal(t, uint32(0), connStatsAnswer.DataChannelsRequested)
assert.Equal(t, uint32(1), connStatsAnswer.DataChannelsAccepted)
dcStatsAnswer := getDataChannelStats(t, reportPCAnswer, answerDC)
assert.Equal(t, DataChannelStateOpen, dcStatsAnswer.State)
assert.Equal(t, uint32(1), dcStatsAnswer.MessagesReceived)
assert.Equal(t, uint64(len(msg)), dcStatsAnswer.BytesReceived)
assert.NotEmpty(t, findLocalCandidateStats(reportPCAnswer))
assert.NotEmpty(t, findRemoteCandidateStats(reportPCAnswer))
assert.NotEmpty(t, findCandidatePairStats(t, reportPCAnswer))
// Close answer DC now
dcWait = sync.WaitGroup{}
dcWait.Add(1)
offerDC.OnClose(func() {
dcWait.Done()
})
assert.NoError(t, answerDC.Close())
waitWithTimeout(t, &dcWait)
time.Sleep(10 * time.Millisecond)
reportPCOffer = offerPC.GetStats()
reportPCAnswer = answerPC.GetStats()
connStatsOffer = getConnectionStats(t, reportPCOffer, offerPC)
assert.Equal(t, uint32(1), connStatsOffer.DataChannelsOpened)
assert.Equal(t, uint32(1), connStatsOffer.DataChannelsClosed)
assert.Equal(t, uint32(1), connStatsOffer.DataChannelsRequested)
assert.Equal(t, uint32(0), connStatsOffer.DataChannelsAccepted)
dcStatsOffer = getDataChannelStats(t, reportPCOffer, offerDC)
assert.Equal(t, DataChannelStateClosed, dcStatsOffer.State)
connStatsAnswer = getConnectionStats(t, reportPCAnswer, answerPC)
assert.Equal(t, uint32(1), connStatsAnswer.DataChannelsOpened)
assert.Equal(t, uint32(1), connStatsAnswer.DataChannelsClosed)
assert.Equal(t, uint32(0), connStatsAnswer.DataChannelsRequested)
assert.Equal(t, uint32(1), connStatsAnswer.DataChannelsAccepted)
dcStatsAnswer = getDataChannelStats(t, reportPCAnswer, answerDC)
assert.Equal(t, DataChannelStateClosed, dcStatsAnswer.State)
answerICETransportStats := getTransportStats(t, reportPCAnswer, "iceTransport")
offerICETransportStats := getTransportStats(t, reportPCOffer, "iceTransport")
assert.GreaterOrEqual(t, offerICETransportStats.BytesSent, answerICETransportStats.BytesReceived)
assert.GreaterOrEqual(t, answerICETransportStats.BytesSent, offerICETransportStats.BytesReceived)
answerSCTPTransportStats := getTransportStats(t, reportPCAnswer, "sctpTransport")
offerSCTPTransportStats := getTransportStats(t, reportPCOffer, "sctpTransport")
assert.GreaterOrEqual(t, offerSCTPTransportStats.BytesSent, answerSCTPTransportStats.BytesReceived)
assert.GreaterOrEqual(t, answerSCTPTransportStats.BytesSent, offerSCTPTransportStats.BytesReceived)
assert.NoError(t, offerPC.Close())
assert.NoError(t, answerPC.Close())
}
func TestPeerConnection_GetStats_Closed(t *testing.T) {
pc, err := NewPeerConnection(Configuration{})
assert.NoError(t, err)
assert.NoError(t, pc.Close())
pc.GetStats()
}