0

CallSetupStateTracker and unittests added.

This helper class keeps track of an offerer state and an answerer state
and makes sure that the transitions between different states are valid.
By having a single state that reports whether the call setup was
successful or if (and in which step) it failed we can be sure to get
trustworthy comparisons between % success and % failures, which is
important for the Unified Plan experiments.

Follow-up CL(s) will wire up RTCPeerConnection to use this helper class
and make sure the states are reported with UMA.

Bug: 906029
Change-Id: Iafb77533c66c7625269e3c6e33ef954d88e54cdb
Reviewed-on: https://chromium-review.googlesource.com/c/1340299
Reviewed-by: Philip Jägenstedt <foolip@chromium.org>
Reviewed-by: Harald Alvestrand <hta@chromium.org>
Reviewed-by: Guido Urdaneta <guidou@chromium.org>
Commit-Queue: Henrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#609685}
This commit is contained in:
Henrik Boström
2018-11-20 13:53:24 +00:00
committed by Commit Bot
parent 49c1cd633c
commit 87866bee9d
5 changed files with 480 additions and 0 deletions

@ -318,6 +318,7 @@ jumbo_source_set("unit_tests") {
"peerconnection/adapters/p2p_quic_stream_unittest.cc",
"peerconnection/adapters/p2p_quic_transport_test.cc",
"peerconnection/byte_buffer_queue_test.cc",
"peerconnection/call_setup_state_tracker_unittest.cc",
"peerconnection/rtc_data_channel_test.cc",
"peerconnection/rtc_ice_transport_test.cc",
"peerconnection/rtc_ice_transport_test.h",

@ -36,6 +36,8 @@ blink_modules_sources("peerconnection") {
"adapters/web_rtc_cross_thread_copier.h",
"byte_buffer_queue.cc",
"byte_buffer_queue.h",
"call_setup_state_tracker.cc",
"call_setup_state_tracker.h",
"rtc_certificate.cc",
"rtc_certificate.h",
"rtc_data_channel.cc",

@ -0,0 +1,98 @@
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/peerconnection/call_setup_state_tracker.h"
namespace blink {
CallSetupStateTracker::CallSetupStateTracker()
: valid_offerer_transitions_(
{std::make_pair(OffererState::kNotStarted,
OffererState::kCreateOfferPending),
// createOffer()
std::make_pair(OffererState::kCreateOfferPending,
OffererState::kCreateOfferRejected),
std::make_pair(OffererState::kCreateOfferPending,
OffererState::kCreateOfferResolved),
std::make_pair(OffererState::kCreateOfferRejected,
OffererState::kCreateOfferResolved),
std::make_pair(OffererState::kCreateOfferResolved,
OffererState::kSetLocalOfferPending),
// setLocalDescription(offer)
std::make_pair(OffererState::kSetLocalOfferPending,
OffererState::kSetLocalOfferRejected),
std::make_pair(OffererState::kSetLocalOfferPending,
OffererState::kSetLocalOfferResolved),
std::make_pair(OffererState::kSetLocalOfferRejected,
OffererState::kSetLocalOfferResolved),
std::make_pair(OffererState::kSetLocalOfferResolved,
OffererState::kSetRemoteAnswerPending),
// setRemoteDescription(answer)
std::make_pair(OffererState::kSetRemoteAnswerPending,
OffererState::kSetRemoteAnswerRejected),
std::make_pair(OffererState::kSetRemoteAnswerPending,
OffererState::kSetRemoteAnswerResolved),
std::make_pair(OffererState::kSetRemoteAnswerRejected,
OffererState::kSetRemoteAnswerResolved)}),
valid_answerer_transitions_({
std::make_pair(AnswererState::kNotStarted,
AnswererState::kSetRemoteOfferPending),
// setRemoteDescription(answer)
std::make_pair(AnswererState::kSetRemoteOfferPending,
AnswererState::kSetRemoteOfferRejected),
std::make_pair(AnswererState::kSetRemoteOfferPending,
AnswererState::kSetRemoteOfferResolved),
std::make_pair(AnswererState::kSetRemoteOfferRejected,
AnswererState::kSetRemoteOfferResolved),
std::make_pair(AnswererState::kSetRemoteOfferResolved,
AnswererState::kCreateAnswerPending),
// createAnswer()
std::make_pair(AnswererState::kCreateAnswerPending,
AnswererState::kCreateAnswerRejected),
std::make_pair(AnswererState::kCreateAnswerPending,
AnswererState::kCreateAnswerResolved),
std::make_pair(AnswererState::kCreateAnswerRejected,
AnswererState::kCreateAnswerResolved),
std::make_pair(AnswererState::kCreateAnswerResolved,
AnswererState::kSetLocalAnswerPending),
// setLocalDescription(answer)
std::make_pair(AnswererState::kSetLocalAnswerPending,
AnswererState::kSetLocalAnswerRejected),
std::make_pair(AnswererState::kSetLocalAnswerPending,
AnswererState::kSetLocalAnswerResolved),
std::make_pair(AnswererState::kSetLocalAnswerRejected,
AnswererState::kSetLocalAnswerResolved),
}),
offerer_state_(OffererState::kNotStarted),
answerer_state_(AnswererState::kNotStarted) {}
OffererState CallSetupStateTracker::offerer_state() const {
return offerer_state_;
}
AnswererState CallSetupStateTracker::answerer_state() const {
return answerer_state_;
}
bool CallSetupStateTracker::NoteOffererStateEvent(OffererState event) {
auto transition = std::make_pair(offerer_state_, event);
if (valid_offerer_transitions_.find(transition) ==
valid_offerer_transitions_.end()) {
return false;
}
offerer_state_ = event;
return true;
}
bool CallSetupStateTracker::NoteAnswererStateEvent(AnswererState event) {
auto transition = std::make_pair(answerer_state_, event);
if (valid_answerer_transitions_.find(transition) ==
valid_answerer_transitions_.end()) {
return false;
}
answerer_state_ = event;
return true;
}
} // namespace blink

@ -0,0 +1,89 @@
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_CALL_SETUP_STATE_TRACKER_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_CALL_SETUP_STATE_TRACKER_H_
#include <set>
#include <utility>
#include "third_party/blink/renderer/modules/modules_export.h"
namespace blink {
// Represents the different states that an offerer can go through during call
// setup, where later steps involve SDP exchange.
//
// Valid transitions are from an operation's "pending" to "resolved" or
// "rejected" state. "Rejected" can transition to "resolved" if another attempt
// is made (without going to "pending" in-between). Only "resolved" can
// transition to the next operation's "pending" state. Transition between
// operations are only valid in the defined order.
//
// The model we are using is one that measures how close we get to establishing
// a connection. In reality, the peer connection may make multiple tries, and
// follow multiple paths towards reaching a connected state, but we're only
// interested in seeing how far it got on its most successful attempt.
enum class OffererState {
kNotStarted = 0,
// createOffer()
kCreateOfferPending = 1,
kCreateOfferRejected = 2,
kCreateOfferResolved = 3,
// setLocalDescription(offer)
kSetLocalOfferPending = 4,
kSetLocalOfferRejected = 5,
kSetLocalOfferResolved = 6,
// setRemoteDescription(answer)
kSetRemoteAnswerPending = 7,
kSetRemoteAnswerRejected = 8,
kSetRemoteAnswerResolved = 9,
kMaxValue = kSetRemoteAnswerResolved,
};
// Represents the different states that an answerer can go through during call
// setup, where initial steps involve SDP exchange. The transition graph for
// this enum follows the same logic as OffererState, see above.
enum class AnswererState {
kNotStarted = 0,
// setRemoteDescription(offer)
kSetRemoteOfferPending = 1,
kSetRemoteOfferRejected = 2,
kSetRemoteOfferResolved = 3,
// createAnswer()
kCreateAnswerPending = 4,
kCreateAnswerRejected = 5,
kCreateAnswerResolved = 6,
// setLocalDescription(answer)
kSetLocalAnswerPending = 7,
kSetLocalAnswerRejected = 8,
kSetLocalAnswerResolved = 9,
kMaxValue = kSetLocalAnswerResolved,
};
class MODULES_EXPORT CallSetupStateTracker {
public:
CallSetupStateTracker();
OffererState offerer_state() const;
AnswererState answerer_state() const;
bool NoteOffererStateEvent(OffererState event);
bool NoteAnswererStateEvent(AnswererState event);
private:
const std::set<std::pair<OffererState, OffererState>>
valid_offerer_transitions_;
const std::set<std::pair<AnswererState, AnswererState>>
valid_answerer_transitions_;
OffererState offerer_state_;
AnswererState answerer_state_;
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_CALL_SETUP_STATE_TRACKER_H_

@ -0,0 +1,290 @@
// Copyright (c) 2018 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/peerconnection/call_setup_state_tracker.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace blink {
namespace {
template <typename StateType>
std::vector<StateType> GetAllCallSetupStates();
template <>
std::vector<OffererState> GetAllCallSetupStates() {
std::vector<OffererState> states = {OffererState::kNotStarted,
OffererState::kCreateOfferPending,
OffererState::kCreateOfferRejected,
OffererState::kCreateOfferResolved,
OffererState::kSetLocalOfferPending,
OffererState::kSetLocalOfferRejected,
OffererState::kSetLocalOfferResolved,
OffererState::kSetRemoteAnswerPending,
OffererState::kSetRemoteAnswerRejected,
OffererState::kSetRemoteAnswerResolved};
EXPECT_EQ(static_cast<size_t>(OffererState::kMaxValue) + 1u, states.size());
return states;
}
template <>
std::vector<AnswererState> GetAllCallSetupStates() {
std::vector<AnswererState> states = {AnswererState::kNotStarted,
AnswererState::kSetRemoteOfferPending,
AnswererState::kSetRemoteOfferRejected,
AnswererState::kSetRemoteOfferResolved,
AnswererState::kCreateAnswerPending,
AnswererState::kCreateAnswerRejected,
AnswererState::kCreateAnswerResolved,
AnswererState::kSetLocalAnswerPending,
AnswererState::kSetLocalAnswerRejected,
AnswererState::kSetLocalAnswerResolved};
EXPECT_EQ(static_cast<size_t>(AnswererState::kMaxValue) + 1u, states.size());
return states;
}
} // namespace
class CallSetupStateTrackerTest : public testing::Test {
public:
enum class Reachability {
kReachable,
kUnreachable,
};
template <typename StateType>
StateType current_state() const;
template <>
OffererState current_state() const {
return tracker_.offerer_state();
}
template <>
AnswererState current_state() const {
return tracker_.answerer_state();
}
template <typename StateType>
bool NoteStateEvent(CallSetupStateTracker* tracker, StateType event) const;
template <>
bool NoteStateEvent(CallSetupStateTracker* tracker,
OffererState event) const {
return tracker->NoteOffererStateEvent(event);
}
template <>
bool NoteStateEvent(CallSetupStateTracker* tracker,
AnswererState event) const {
return tracker->NoteAnswererStateEvent(event);
}
template <typename StateType>
bool VerifyReachability(Reachability reachability,
std::vector<StateType> states) const {
bool expected_state_reached = (reachability == Reachability::kReachable);
for (const auto& state : states) {
bool did_reach_state;
if (state == current_state<StateType>()) {
// The current state always counts as reachable.
did_reach_state = true;
} else {
// Perform the test on a copy to avoid mutating |tracker_|.
CallSetupStateTracker tracker_copy = tracker_;
did_reach_state = NoteStateEvent<StateType>(&tracker_copy, state);
}
if (did_reach_state != expected_state_reached)
return false;
}
return true;
}
template <typename StateType>
bool VerifyOnlyReachableStates(std::vector<StateType> reachable_states,
bool include_current = true) const {
if (include_current)
reachable_states.push_back(current_state<StateType>());
std::vector<StateType> unreachable_states =
GetAllCallSetupStates<StateType>();
for (const auto& reachable_state : reachable_states) {
unreachable_states.erase(std::find(unreachable_states.begin(),
unreachable_states.end(),
reachable_state));
}
return VerifyReachability<StateType>(Reachability::kReachable,
reachable_states) &&
VerifyReachability<StateType>(Reachability::kUnreachable,
unreachable_states);
}
protected:
CallSetupStateTracker tracker_;
};
TEST_F(CallSetupStateTrackerTest, InitialState) {
EXPECT_EQ(OffererState::kNotStarted, tracker_.offerer_state());
EXPECT_EQ(AnswererState::kNotStarted, tracker_.answerer_state());
}
TEST_F(CallSetupStateTrackerTest, OffererSuccessfulNegotiation) {
EXPECT_TRUE(VerifyOnlyReachableStates<OffererState>(
{OffererState::kCreateOfferPending}));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kCreateOfferPending));
EXPECT_EQ(OffererState::kCreateOfferPending, tracker_.offerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<OffererState>(
{OffererState::kCreateOfferResolved,
OffererState::kCreateOfferRejected}));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kCreateOfferResolved));
EXPECT_EQ(OffererState::kCreateOfferResolved, tracker_.offerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<OffererState>(
{OffererState::kSetLocalOfferPending}));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kSetLocalOfferPending));
EXPECT_EQ(OffererState::kSetLocalOfferPending, tracker_.offerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<OffererState>(
{OffererState::kSetLocalOfferResolved,
OffererState::kSetLocalOfferRejected}));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kSetLocalOfferResolved));
EXPECT_EQ(OffererState::kSetLocalOfferResolved, tracker_.offerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<OffererState>(
{OffererState::kSetRemoteAnswerPending}));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kSetRemoteAnswerPending));
EXPECT_EQ(OffererState::kSetRemoteAnswerPending, tracker_.offerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<OffererState>(
{OffererState::kSetRemoteAnswerResolved,
OffererState::kSetRemoteAnswerRejected}));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kSetRemoteAnswerResolved));
EXPECT_EQ(OffererState::kSetRemoteAnswerResolved, tracker_.offerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<OffererState>({}));
}
TEST_F(CallSetupStateTrackerTest, OffererCreateOfferRejected) {
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kCreateOfferPending));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kCreateOfferRejected));
EXPECT_EQ(OffererState::kCreateOfferRejected, tracker_.offerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<OffererState>(
{OffererState::kCreateOfferResolved}));
}
TEST_F(CallSetupStateTrackerTest, OffererSetLocalOfferRejected) {
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kCreateOfferPending));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kCreateOfferResolved));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kSetLocalOfferPending));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kSetLocalOfferRejected));
EXPECT_EQ(OffererState::kSetLocalOfferRejected, tracker_.offerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<OffererState>(
{OffererState::kSetLocalOfferResolved}));
}
TEST_F(CallSetupStateTrackerTest, OffererSetRemoteAnswerRejected) {
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kCreateOfferPending));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kCreateOfferResolved));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kSetLocalOfferPending));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kSetLocalOfferResolved));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kSetRemoteAnswerPending));
EXPECT_TRUE(
tracker_.NoteOffererStateEvent(OffererState::kSetRemoteAnswerRejected));
EXPECT_EQ(OffererState::kSetRemoteAnswerRejected, tracker_.offerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<OffererState>(
{OffererState::kSetRemoteAnswerResolved}));
}
TEST_F(CallSetupStateTrackerTest, AnswererSuccessfulNegotiation) {
EXPECT_TRUE(VerifyOnlyReachableStates<AnswererState>(
{AnswererState::kSetRemoteOfferPending}));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetRemoteOfferPending));
EXPECT_EQ(AnswererState::kSetRemoteOfferPending, tracker_.answerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<AnswererState>(
{AnswererState::kSetRemoteOfferResolved,
AnswererState::kSetRemoteOfferRejected}));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetRemoteOfferResolved));
EXPECT_EQ(AnswererState::kSetRemoteOfferResolved, tracker_.answerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<AnswererState>(
{AnswererState::kCreateAnswerPending}));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kCreateAnswerPending));
EXPECT_EQ(AnswererState::kCreateAnswerPending, tracker_.answerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<AnswererState>(
{AnswererState::kCreateAnswerResolved,
AnswererState::kCreateAnswerRejected}));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kCreateAnswerResolved));
EXPECT_EQ(AnswererState::kCreateAnswerResolved, tracker_.answerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<AnswererState>(
{AnswererState::kSetLocalAnswerPending}));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetLocalAnswerPending));
EXPECT_EQ(AnswererState::kSetLocalAnswerPending, tracker_.answerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<AnswererState>(
{AnswererState::kSetLocalAnswerResolved,
AnswererState::kSetLocalAnswerRejected}));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetLocalAnswerResolved));
EXPECT_EQ(AnswererState::kSetLocalAnswerResolved, tracker_.answerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<AnswererState>({}));
}
TEST_F(CallSetupStateTrackerTest, AnswererSetRemoteOfferRejected) {
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetRemoteOfferPending));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetRemoteOfferRejected));
EXPECT_EQ(AnswererState::kSetRemoteOfferRejected, tracker_.answerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<AnswererState>(
{AnswererState::kSetRemoteOfferResolved}));
}
TEST_F(CallSetupStateTrackerTest, AnswererCreateAnswerRejected) {
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetRemoteOfferPending));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetRemoteOfferResolved));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kCreateAnswerPending));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kCreateAnswerRejected));
EXPECT_EQ(AnswererState::kCreateAnswerRejected, tracker_.answerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<AnswererState>(
{AnswererState::kCreateAnswerResolved}));
}
TEST_F(CallSetupStateTrackerTest, AnswererSetLocalAnswerRejected) {
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetRemoteOfferPending));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetRemoteOfferResolved));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kCreateAnswerPending));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kCreateAnswerResolved));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetLocalAnswerPending));
EXPECT_TRUE(
tracker_.NoteAnswererStateEvent(AnswererState::kSetLocalAnswerRejected));
EXPECT_EQ(AnswererState::kSetLocalAnswerRejected, tracker_.answerer_state());
EXPECT_TRUE(VerifyOnlyReachableStates<AnswererState>(
{AnswererState::kSetLocalAnswerResolved}));
}
} // namespace blink