Rewrite WebRtcSession DTLS/SDES crypto tests as PeerConnection tests
Bug: webrtc:8222
Change-Id: I6be2c5a5735b77a5c577472b88ff830204dd69eb
Reviewed-on: https://webrtc-review.googlesource.com/1160
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20193}
diff --git a/pc/peerconnection_crypto_unittest.cc b/pc/peerconnection_crypto_unittest.cc
new file mode 100644
index 0000000..0f54995
--- /dev/null
+++ b/pc/peerconnection_crypto_unittest.cc
@@ -0,0 +1,605 @@
+/*
+ * Copyright 2017 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 "p2p/base/fakeportallocator.h"
+#include "pc/mediasession.h"
+#include "pc/peerconnectionwrapper.h"
+#include "pc/sdputils.h"
+#ifdef WEBRTC_ANDROID
+#include "pc/test/androidtestinitializer.h"
+#endif
+#include "pc/test/fakeaudiocapturemodule.h"
+#include "pc/test/fakertccertificategenerator.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/ptr_util.h"
+#include "rtc_base/virtualsocketserver.h"
+
+namespace webrtc {
+
+using RTCConfiguration = PeerConnectionInterface::RTCConfiguration;
+using ::testing::Values;
+using ::testing::Combine;
+
+constexpr int kGenerateCertTimeout = 1000;
+
+class PeerConnectionCryptoUnitTest : public ::testing::Test {
+ protected:
+ typedef std::unique_ptr<PeerConnectionWrapper> WrapperPtr;
+
+ PeerConnectionCryptoUnitTest()
+ : vss_(new rtc::VirtualSocketServer()), main_(vss_.get()) {
+#ifdef WEBRTC_ANDROID
+ InitializeAndroidObjects();
+#endif
+ pc_factory_ = CreatePeerConnectionFactory(
+ rtc::Thread::Current(), rtc::Thread::Current(), rtc::Thread::Current(),
+ FakeAudioCaptureModule::Create(), nullptr, nullptr);
+ }
+
+ WrapperPtr CreatePeerConnection(const RTCConfiguration& config) {
+ return CreatePeerConnection(config, nullptr);
+ }
+
+ WrapperPtr CreatePeerConnection(
+ const RTCConfiguration& config,
+ std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_gen) {
+ auto fake_port_allocator = rtc::MakeUnique<cricket::FakePortAllocator>(
+ rtc::Thread::Current(), nullptr);
+ auto observer = rtc::MakeUnique<MockPeerConnectionObserver>();
+ auto pc = pc_factory_->CreatePeerConnection(
+ config, std::move(fake_port_allocator), std::move(cert_gen),
+ observer.get());
+ if (!pc) {
+ return nullptr;
+ }
+
+ return rtc::MakeUnique<PeerConnectionWrapper>(pc_factory_, pc,
+ std::move(observer));
+ }
+
+ // Accepts the same arguments as CreatePeerConnection and adds default audio
+ // and video tracks.
+ template <typename... Args>
+ WrapperPtr CreatePeerConnectionWithAudioVideo(Args&&... args) {
+ auto wrapper = CreatePeerConnection(std::forward<Args>(args)...);
+ if (!wrapper) {
+ return nullptr;
+ }
+ wrapper->AddAudioVideoStream("s", "a", "v");
+ return wrapper;
+ }
+
+ std::unique_ptr<rtc::VirtualSocketServer> vss_;
+ rtc::AutoSocketServerThread main_;
+ rtc::scoped_refptr<PeerConnectionFactoryInterface> pc_factory_;
+};
+
+SdpContentPredicate HaveDtlsFingerprint() {
+ return [](const cricket::ContentInfo* content,
+ const cricket::TransportInfo* transport) {
+ return transport->description.identity_fingerprint != nullptr;
+ };
+}
+
+SdpContentPredicate HaveSdesCryptos() {
+ return [](const cricket::ContentInfo* content,
+ const cricket::TransportInfo* transport) {
+ const auto* media_desc =
+ static_cast<const cricket::MediaContentDescription*>(
+ content->description);
+ return !media_desc->cryptos().empty();
+ };
+}
+
+SdpContentPredicate HaveProtocol(const std::string& protocol) {
+ return [protocol](const cricket::ContentInfo* content,
+ const cricket::TransportInfo* transport) {
+ const auto* media_desc =
+ static_cast<const cricket::MediaContentDescription*>(
+ content->description);
+ return media_desc->protocol() == protocol;
+ };
+}
+
+SdpContentPredicate HaveSdesGcmCryptos(size_t num_crypto_suites) {
+ return [num_crypto_suites](const cricket::ContentInfo* content,
+ const cricket::TransportInfo* transport) {
+ const auto* media_desc =
+ static_cast<const cricket::MediaContentDescription*>(
+ content->description);
+ if (media_desc->cryptos().size() != num_crypto_suites) {
+ return false;
+ }
+ const cricket::CryptoParams first_params = media_desc->cryptos()[0];
+ return first_params.key_params.size() == 67U &&
+ first_params.cipher_suite == "AEAD_AES_256_GCM";
+ };
+}
+
+SdpContentMutator RemoveSdesCryptos() {
+ return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
+ auto* media_desc =
+ static_cast<cricket::MediaContentDescription*>(content->description);
+ media_desc->set_cryptos({});
+ };
+}
+
+SdpContentMutator RemoveDtlsFingerprint() {
+ return [](cricket::ContentInfo* content, cricket::TransportInfo* transport) {
+ transport->description.identity_fingerprint.reset();
+ };
+}
+
+// When DTLS is enabled, the SDP offer/answer should have a DTLS fingerprint and
+// no SDES cryptos.
+TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWhenDtlsEnabled) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(true);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOffer();
+ ASSERT_TRUE(offer);
+
+ ASSERT_FALSE(offer->description()->contents().empty());
+ EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), offer->description()));
+ EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
+ EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
+ offer->description()));
+}
+TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWhenDtlsEnabled) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(true);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ callee->SetRemoteDescription(caller->CreateOffer());
+ auto answer = callee->CreateAnswer();
+ ASSERT_TRUE(answer);
+
+ ASSERT_FALSE(answer->description()->contents().empty());
+ EXPECT_TRUE(SdpContentsAll(HaveDtlsFingerprint(), answer->description()));
+ EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
+ EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolDtlsSavpf),
+ answer->description()));
+}
+
+// When DTLS is disabled, the SDP offer/answer should include SDES cryptos and
+// should not have a DTLS fingerprint.
+TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWhenDtlsDisabled) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOffer();
+ ASSERT_TRUE(offer);
+
+ ASSERT_FALSE(offer->description()->contents().empty());
+ EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), offer->description()));
+ EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
+ EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
+ offer->description()));
+}
+TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWhenDtlsDisabled) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ callee->SetRemoteDescription(caller->CreateOffer());
+ auto answer = callee->CreateAnswer();
+ ASSERT_TRUE(answer);
+
+ ASSERT_FALSE(answer->description()->contents().empty());
+ EXPECT_TRUE(SdpContentsAll(HaveSdesCryptos(), answer->description()));
+ EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
+ EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolSavpf),
+ answer->description()));
+}
+
+// When encryption is disabled, the SDP offer/answer should have neither a DTLS
+// fingerprint nor any SDES crypto options.
+TEST_F(PeerConnectionCryptoUnitTest,
+ CorrectCryptoInOfferWhenEncryptionDisabled) {
+ PeerConnectionFactoryInterface::Options options;
+ options.disable_encryption = true;
+ pc_factory_->SetOptions(options);
+
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOffer();
+ ASSERT_TRUE(offer);
+
+ ASSERT_FALSE(offer->description()->contents().empty());
+ EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), offer->description()));
+ EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), offer->description()));
+ EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
+ offer->description()));
+}
+TEST_F(PeerConnectionCryptoUnitTest,
+ CorrectCryptoInAnswerWhenEncryptionDisabled) {
+ PeerConnectionFactoryInterface::Options options;
+ options.disable_encryption = true;
+ pc_factory_->SetOptions(options);
+
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ callee->SetRemoteDescription(caller->CreateOffer());
+ auto answer = callee->CreateAnswer();
+ ASSERT_TRUE(answer);
+
+ ASSERT_FALSE(answer->description()->contents().empty());
+ EXPECT_TRUE(SdpContentsNone(HaveSdesCryptos(), answer->description()));
+ EXPECT_TRUE(SdpContentsNone(HaveDtlsFingerprint(), answer->description()));
+ EXPECT_TRUE(SdpContentsAll(HaveProtocol(cricket::kMediaProtocolAvpf),
+ answer->description()));
+}
+
+// When DTLS is disabled and GCM cipher suites are enabled, the SDP offer/answer
+// should have the correct ciphers in the SDES crypto options.
+// With GCM cipher suites enabled, there will be 3 cryptos in the offer and 1
+// in the answer.
+TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInOfferWithSdesAndGcm) {
+ PeerConnectionFactoryInterface::Options options;
+ options.crypto_options.enable_gcm_crypto_suites = true;
+ pc_factory_->SetOptions(options);
+
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOffer();
+ ASSERT_TRUE(offer);
+
+ ASSERT_FALSE(offer->description()->contents().empty());
+ EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(3), offer->description()));
+}
+TEST_F(PeerConnectionCryptoUnitTest, CorrectCryptoInAnswerWithSdesAndGcm) {
+ PeerConnectionFactoryInterface::Options options;
+ options.crypto_options.enable_gcm_crypto_suites = true;
+ pc_factory_->SetOptions(options);
+
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ callee->SetRemoteDescription(caller->CreateOffer());
+ auto answer = callee->CreateAnswer();
+ ASSERT_TRUE(answer);
+
+ ASSERT_FALSE(answer->description()->contents().empty());
+ EXPECT_TRUE(SdpContentsAll(HaveSdesGcmCryptos(1), answer->description()));
+}
+
+TEST_F(PeerConnectionCryptoUnitTest, CanSetSdesGcmRemoteOfferAndLocalAnswer) {
+ PeerConnectionFactoryInterface::Options options;
+ options.crypto_options.enable_gcm_crypto_suites = true;
+ pc_factory_->SetOptions(options);
+
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOffer();
+ ASSERT_TRUE(offer);
+ ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
+
+ auto answer = callee->CreateAnswer();
+ ASSERT_TRUE(answer);
+ ASSERT_TRUE(callee->SetLocalDescription(std::move(answer)));
+}
+
+// The following group tests that two PeerConnections can successfully exchange
+// an offer/answer when DTLS is off and that they will refuse any offer/answer
+// applied locally/remotely if it does not include SDES cryptos.
+TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenSdesOn) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOfferAndSetAsLocal();
+ ASSERT_TRUE(offer);
+ ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
+
+ auto answer = callee->CreateAnswerAndSetAsLocal();
+ ASSERT_TRUE(answer);
+ ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
+}
+TEST_F(PeerConnectionCryptoUnitTest,
+ FailToSetLocalOfferWithNoCryptosWhenSdesOn) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOffer();
+ SdpContentsForEach(RemoveSdesCryptos(), offer->description());
+
+ EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
+}
+TEST_F(PeerConnectionCryptoUnitTest,
+ FailToSetRemoteOfferWithNoCryptosWhenSdesOn) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOffer();
+ SdpContentsForEach(RemoveSdesCryptos(), offer->description());
+
+ EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
+}
+TEST_F(PeerConnectionCryptoUnitTest,
+ FailToSetLocalAnswerWithNoCryptosWhenSdesOn) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
+ auto answer = callee->CreateAnswer();
+ SdpContentsForEach(RemoveSdesCryptos(), answer->description());
+
+ EXPECT_FALSE(callee->SetLocalDescription(std::move(answer)));
+}
+TEST_F(PeerConnectionCryptoUnitTest,
+ FailToSetRemoteAnswerWithNoCryptosWhenSdesOn) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
+ auto answer = callee->CreateAnswerAndSetAsLocal();
+ SdpContentsForEach(RemoveSdesCryptos(), answer->description());
+
+ EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
+}
+
+// The following group tests that two PeerConnections can successfully exchange
+// an offer/answer when DTLS is on and that they will refuse any offer/answer
+// applied locally/remotely if it does not include a DTLS fingerprint.
+TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenDtlsOn) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(true);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOfferAndSetAsLocal();
+ ASSERT_TRUE(offer);
+ ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
+
+ auto answer = callee->CreateAnswerAndSetAsLocal();
+ ASSERT_TRUE(answer);
+ ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
+}
+TEST_F(PeerConnectionCryptoUnitTest,
+ FailToSetLocalOfferWithNoFingerprintWhenDtlsOn) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(true);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOffer();
+ SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
+
+ EXPECT_FALSE(caller->SetLocalDescription(std::move(offer)));
+}
+TEST_F(PeerConnectionCryptoUnitTest,
+ FailToSetRemoteOfferWithNoFingerprintWhenDtlsOn) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(true);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOffer();
+ SdpContentsForEach(RemoveDtlsFingerprint(), offer->description());
+
+ EXPECT_FALSE(callee->SetRemoteDescription(std::move(offer)));
+}
+TEST_F(PeerConnectionCryptoUnitTest,
+ FailToSetLocalAnswerWithNoFingerprintWhenDtlsOn) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(true);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
+ auto answer = callee->CreateAnswer();
+ SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
+}
+TEST_F(PeerConnectionCryptoUnitTest,
+ FailToSetRemoteAnswerWithNoFingerprintWhenDtlsOn) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(true);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ callee->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
+ auto answer = callee->CreateAnswerAndSetAsLocal();
+ SdpContentsForEach(RemoveDtlsFingerprint(), answer->description());
+
+ EXPECT_FALSE(caller->SetRemoteDescription(std::move(answer)));
+}
+
+// Test that an offer/answer can be exchanged when encryption is disabled.
+TEST_F(PeerConnectionCryptoUnitTest, ExchangeOfferAnswerWhenNoEncryption) {
+ PeerConnectionFactoryInterface::Options options;
+ options.disable_encryption = true;
+ pc_factory_->SetOptions(options);
+
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(false);
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ auto callee = CreatePeerConnectionWithAudioVideo(config);
+
+ auto offer = caller->CreateOfferAndSetAsLocal();
+ ASSERT_TRUE(offer);
+ ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
+
+ auto answer = callee->CreateAnswerAndSetAsLocal();
+ ASSERT_TRUE(answer);
+ ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
+}
+
+// Tests that a DTLS call can be established when the certificate is specified
+// in the PeerConnection config and no certificate generator is specified.
+TEST_F(PeerConnectionCryptoUnitTest,
+ ExchangeOfferAnswerWhenDtlsCertificateInConfig) {
+ RTCConfiguration caller_config;
+ caller_config.enable_dtls_srtp.emplace(true);
+ caller_config.certificates.push_back(
+ FakeRTCCertificateGenerator::GenerateCertificate());
+ auto caller = CreatePeerConnectionWithAudioVideo(caller_config);
+
+ RTCConfiguration callee_config;
+ callee_config.enable_dtls_srtp.emplace(true);
+ callee_config.certificates.push_back(
+ FakeRTCCertificateGenerator::GenerateCertificate());
+ auto callee = CreatePeerConnectionWithAudioVideo(callee_config);
+
+ auto offer = caller->CreateOfferAndSetAsLocal();
+ ASSERT_TRUE(offer);
+ ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
+
+ auto answer = callee->CreateAnswerAndSetAsLocal();
+ ASSERT_TRUE(answer);
+ ASSERT_TRUE(caller->SetRemoteDescription(std::move(answer)));
+}
+
+// The following parameterized test verifies that CreateOffer/CreateAnswer
+// returns successfully (or with failure if the underlying certificate generator
+// fails) no matter when the DTLS certificate is generated. If multiple
+// CreateOffer/CreateAnswer calls are made while waiting for the certificate,
+// they all finish after the certificate is generated.
+
+// Whether the test will call CreateOffer or CreateAnswer.
+enum class SdpType { kOffer, kAnswer };
+std::ostream& operator<<(std::ostream& out, SdpType value) {
+ switch (value) {
+ case SdpType::kOffer:
+ return out << "offer";
+ case SdpType::kAnswer:
+ return out << "answer";
+ default:
+ return out << "unknown";
+ }
+}
+
+// Whether the certificate will be generated before calling CreateOffer or
+// while CreateOffer is executing.
+enum class CertGenTime { kBefore, kDuring };
+std::ostream& operator<<(std::ostream& out, CertGenTime value) {
+ switch (value) {
+ case CertGenTime::kBefore:
+ return out << "before";
+ case CertGenTime::kDuring:
+ return out << "during";
+ default:
+ return out << "unknown";
+ }
+}
+
+// Whether the fake certificate generator will produce a certificate or fail.
+enum class CertGenResult { kSucceed, kFail };
+std::ostream& operator<<(std::ostream& out, CertGenResult value) {
+ switch (value) {
+ case CertGenResult::kSucceed:
+ return out << "succeed";
+ case CertGenResult::kFail:
+ return out << "fail";
+ default:
+ return out << "unknown";
+ }
+}
+
+class PeerConnectionCryptoDtlsCertGenUnitTest
+ : public PeerConnectionCryptoUnitTest,
+ public ::testing::WithParamInterface<
+ ::testing::tuple<SdpType, CertGenTime, CertGenResult, size_t>> {
+ protected:
+ PeerConnectionCryptoDtlsCertGenUnitTest() {
+ sdp_type_ = ::testing::get<0>(GetParam());
+ cert_gen_time_ = ::testing::get<1>(GetParam());
+ cert_gen_result_ = ::testing::get<2>(GetParam());
+ concurrent_calls_ = ::testing::get<3>(GetParam());
+ }
+
+ SdpType sdp_type_;
+ CertGenTime cert_gen_time_;
+ CertGenResult cert_gen_result_;
+ size_t concurrent_calls_;
+};
+
+TEST_P(PeerConnectionCryptoDtlsCertGenUnitTest, TestCertificateGeneration) {
+ RTCConfiguration config;
+ config.enable_dtls_srtp.emplace(true);
+ auto owned_fake_certificate_generator =
+ rtc::MakeUnique<FakeRTCCertificateGenerator>();
+ auto* fake_certificate_generator = owned_fake_certificate_generator.get();
+ fake_certificate_generator->set_should_fail(cert_gen_result_ ==
+ CertGenResult::kFail);
+ fake_certificate_generator->set_should_wait(cert_gen_time_ ==
+ CertGenTime::kDuring);
+ WrapperPtr pc;
+ if (sdp_type_ == SdpType::kOffer) {
+ pc = CreatePeerConnectionWithAudioVideo(
+ config, std::move(owned_fake_certificate_generator));
+ } else {
+ auto caller = CreatePeerConnectionWithAudioVideo(config);
+ pc = CreatePeerConnectionWithAudioVideo(
+ config, std::move(owned_fake_certificate_generator));
+ pc->SetRemoteDescription(caller->CreateOfferAndSetAsLocal());
+ }
+ if (cert_gen_time_ == CertGenTime::kBefore) {
+ ASSERT_TRUE_WAIT(fake_certificate_generator->generated_certificates() +
+ fake_certificate_generator->generated_failures() >
+ 0,
+ kGenerateCertTimeout);
+ } else {
+ ASSERT_EQ(fake_certificate_generator->generated_certificates(), 0);
+ fake_certificate_generator->set_should_wait(false);
+ }
+ std::vector<rtc::scoped_refptr<MockCreateSessionDescriptionObserver>>
+ observers;
+ for (size_t i = 0; i < concurrent_calls_; i++) {
+ rtc::scoped_refptr<MockCreateSessionDescriptionObserver> observer =
+ new rtc::RefCountedObject<MockCreateSessionDescriptionObserver>();
+ observers.push_back(observer);
+ if (sdp_type_ == SdpType::kOffer) {
+ pc->pc()->CreateOffer(observer, nullptr);
+ } else {
+ pc->pc()->CreateAnswer(observer, nullptr);
+ }
+ }
+ for (auto& observer : observers) {
+ EXPECT_TRUE_WAIT(observer->called(), 1000);
+ if (cert_gen_result_ == CertGenResult::kSucceed) {
+ EXPECT_TRUE(observer->result());
+ } else {
+ EXPECT_FALSE(observer->result());
+ }
+ }
+}
+
+INSTANTIATE_TEST_CASE_P(
+ PeerConnectionCryptoUnitTest,
+ PeerConnectionCryptoDtlsCertGenUnitTest,
+ Combine(Values(SdpType::kOffer, SdpType::kAnswer),
+ Values(CertGenTime::kBefore, CertGenTime::kDuring),
+ Values(CertGenResult::kSucceed, CertGenResult::kFail),
+ Values(1, 3)));
+
+} // namespace webrtc