AI: Add utest to cover the rtc server.

This commit is contained in:
OSSRS-AI 2025-10-10 23:02:14 -04:00 committed by winlin
parent 604f9450fc
commit c6c6f38ed7
18 changed files with 1278 additions and 39 deletions

View File

@ -25,6 +25,7 @@
#include <srs_kernel_utility.hpp>
#include <srs_protocol_http_client.hpp>
#include <srs_protocol_st.hpp>
#include <srs_app_rtc_conn.hpp>
ISrsAppFactory::ISrsAppFactory()
{
@ -165,6 +166,13 @@ ISrsIpListener *SrsAppFactory::create_tcp_listener(ISrsTcpHandler *handler)
return new SrsTcpListener(handler);
}
ISrsRtcConnection *SrsAppFactory::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid)
{
SrsRtcConnection *session = new SrsRtcConnection(exec, cid);
session->assemble();
return session;
}
ISrsCoroutine *SrsAppFactory::create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid)
{
return kernel_factory_->create_coroutine(name, handler, cid);

View File

@ -38,6 +38,8 @@ class ISrsFragmentedMp4;
class SrsFinalFactory;
class ISrsIpListener;
class ISrsTcpHandler;
class ISrsRtcConnection;
class ISrsExecRtcAsyncTask;
// The factory to create app objects.
class ISrsAppFactory : public ISrsKernelFactory
@ -74,6 +76,7 @@ public:
virtual ISrsFragmentWindow *create_fragment_window() = 0;
virtual ISrsFragmentedMp4 *create_fragmented_mp4() = 0;
virtual ISrsIpListener *create_tcp_listener(ISrsTcpHandler *handler) = 0;
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid) = 0;
};
// The factory to create app objects.
@ -114,6 +117,7 @@ public:
virtual ISrsFragmentWindow *create_fragment_window();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
virtual ISrsIpListener *create_tcp_listener(ISrsTcpHandler *handler);
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid);
public:
virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid);

View File

@ -263,7 +263,7 @@ srs_error_t SrsGoApiRtcPlay::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa
}
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
SrsRtcConnection *session = NULL;
ISrsRtcConnection *session = NULL;
if ((err = server_->create_rtc_session(ruc, local_sdp, &session)) != srs_success) {
return srs_error_wrap(err, "create session, dtls=%u, srtp=%u, eip=%s", ruc->dtls_, ruc->srtp_, ruc->eip_.c_str());
}
@ -544,7 +544,7 @@ srs_error_t SrsGoApiRtcPublish::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
// TODO: FIXME: When server enabled, but vhost disabled, should report error.
// We must do stat the client before hooks, because hooks depends on it.
SrsRtcConnection *session = NULL;
ISrsRtcConnection *session = NULL;
if ((err = server_->create_rtc_session(ruc, local_sdp, &session)) != srs_success) {
return srs_error_wrap(err, "create session");
}
@ -672,7 +672,7 @@ srs_error_t SrsGoApiRtcWhip::serve_http(ISrsHttpResponseWriter *w, ISrsHttpMessa
return srs_error_new(ERROR_RTC_INVALID_SESSION, "token empty");
}
SrsRtcConnection *session = server_->find_rtc_session_by_username(username);
ISrsRtcConnection *session = server_->find_rtc_session_by_username(username);
if (session && token != session->token()) {
return srs_error_new(ERROR_RTC_INVALID_SESSION, "token %s not match", token.c_str());
}
@ -847,7 +847,7 @@ srs_error_t SrsGoApiRtcNACK::do_serve_http(ISrsHttpResponseWriter *w, ISrsHttpMe
return srs_error_new(ERROR_RTC_INVALID_PARAMS, "invalid drop=%s/%d", dropv.c_str(), drop);
}
SrsRtcConnection *session = server_->find_rtc_session_by_username(username);
ISrsRtcConnection *session = server_->find_rtc_session_by_username(username);
if (!session) {
return srs_error_new(ERROR_RTC_NO_SESSION, "no session username=%s", username.c_str());
}

View File

@ -2414,6 +2414,11 @@ bool SrsRtcConnection::is_alive()
return last_stun_time_ + session_timeout_ > srs_time_now_cached();
}
bool SrsRtcConnection::is_disposing()
{
return disposing_;
}
void SrsRtcConnection::alive()
{
last_stun_time_ = srs_time_now_cached();

View File

@ -579,8 +579,26 @@ public:
virtual ISrsRtcNetwork *tcp() = 0;
// Keep alive.
virtual void alive() = 0;
virtual bool is_alive() = 0;
virtual bool is_disposing() = 0;
// Context switching.
virtual void switch_to_context() = 0;
// Session management.
virtual srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp) = 0;
virtual srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp) = 0;
virtual void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status) = 0;
// SDP management.
virtual void set_remote_sdp(const SrsSdp &sdp) = 0;
virtual void set_local_sdp(const SrsSdp &sdp) = 0;
virtual void set_state_as_waiting_stun() = 0;
// Initialization.
virtual srs_error_t initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username) = 0;
// Username and token access.
virtual std::string username() = 0;
virtual std::string token() = 0;
virtual void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token) = 0;
// Simulation for testing.
virtual void simulate_nack_drop(int nn) = 0;
};
// A RTC Peer Connection, SDP level object.
@ -721,6 +739,7 @@ public:
srs_error_t on_dtls_handshake_done();
srs_error_t on_dtls_alert(std::string type, std::string desc);
bool is_alive();
bool is_disposing();
void alive();
public:

View File

@ -28,6 +28,7 @@ public:
public:
virtual srs_error_t initialize() = 0;
virtual std::string get_fingerprint() = 0;
};
// The DTLS certificate.

View File

@ -32,6 +32,7 @@ using namespace std;
#include <srs_protocol_log.hpp>
#include <srs_protocol_rtc_stun.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_app_factory.hpp>
extern SrsPps *_srs_pps_rpkts;
extern SrsPps *_srs_pps_rstuns;
@ -314,6 +315,11 @@ SrsRtcSessionManager::SrsRtcSessionManager()
rtc_async_ = new SrsAsyncCallWorker();
conn_manager_ = _srs_conn_manager;
stream_publish_tokens_ = _srs_stream_publish_tokens;
rtc_sources_ = _srs_rtc_sources;
dtls_certificate_ = _srs_rtc_dtls_certificate;
config_ = _srs_config;
app_factory_ = _srs_app_factory;
}
SrsRtcSessionManager::~SrsRtcSessionManager()
@ -322,6 +328,11 @@ SrsRtcSessionManager::~SrsRtcSessionManager()
srs_freep(rtc_async_);
conn_manager_ = NULL;
stream_publish_tokens_ = NULL;
rtc_sources_ = NULL;
dtls_certificate_ = NULL;
config_ = NULL;
app_factory_ = NULL;
}
srs_error_t SrsRtcSessionManager::initialize()
@ -335,13 +346,13 @@ srs_error_t SrsRtcSessionManager::initialize()
return err;
}
SrsRtcConnection *SrsRtcSessionManager::find_rtc_session_by_username(const std::string &username)
ISrsRtcConnection *SrsRtcSessionManager::find_rtc_session_by_username(const std::string &username)
{
ISrsResource *conn = conn_manager_->find_by_name(username);
return dynamic_cast<SrsRtcConnection *>(conn);
return dynamic_cast<ISrsRtcConnection *>(conn);
}
srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession)
srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession)
{
srs_error_t err = srs_success;
@ -349,7 +360,7 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS
// Acquire stream publish token to prevent race conditions across all protocols.
SrsStreamPublishToken *publish_token_raw = NULL;
if (ruc->publish_ && (err = _srs_stream_publish_tokens->acquire_token(req, publish_token_raw)) != srs_success) {
if (ruc->publish_ && (err = stream_publish_tokens_->acquire_token(req, publish_token_raw)) != srs_success) {
return srs_error_wrap(err, "acquire stream publish token");
}
SrsSharedPtr<SrsStreamPublishToken> publish_token(publish_token_raw);
@ -358,7 +369,7 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS
}
SrsSharedPtr<SrsRtcSource> source;
if ((err = _srs_rtc_sources->fetch_or_create(req, source)) != srs_success) {
if ((err = rtc_sources_->fetch_or_create(req, source)) != srs_success) {
return srs_error_wrap(err, "create source");
}
@ -368,8 +379,7 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS
// TODO: FIXME: add do_create_session to error process.
SrsContextId cid = _srs_context->get_id();
SrsRtcConnection *session = new SrsRtcConnection(this, cid);
session->assemble();
ISrsRtcConnection *session = app_factory_->create_rtc_connection(this, cid);
if ((err = do_create_rtc_session(ruc, local_sdp, session)) != srs_success) {
srs_freep(session);
@ -386,7 +396,7 @@ srs_error_t SrsRtcSessionManager::create_rtc_session(SrsRtcUserConfig *ruc, SrsS
return err;
}
srs_error_t SrsRtcSessionManager::do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection *session)
srs_error_t SrsRtcSessionManager::do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection *session)
{
srs_error_t err = srs_success;
@ -424,7 +434,7 @@ srs_error_t SrsRtcSessionManager::do_create_rtc_session(SrsRtcUserConfig *ruc, S
local_sdp.set_ice_ufrag(local_ufrag);
local_sdp.set_ice_pwd(local_pwd);
local_sdp.set_fingerprint_algo("sha-256");
local_sdp.set_fingerprint(_srs_rtc_dtls_certificate->get_fingerprint());
local_sdp.set_fingerprint(dtls_certificate_->get_fingerprint());
// We allows to mock the eip of server.
if (true) {
@ -432,21 +442,21 @@ srs_error_t SrsRtcSessionManager::do_create_rtc_session(SrsRtcUserConfig *ruc, S
int udp_port = 0;
if (true) {
string udp_host;
string udp_hostport = _srs_config->get_rtc_server_listens().at(0);
string udp_hostport = config_->get_rtc_server_listens().at(0);
srs_net_split_for_listener(udp_hostport, udp_host, udp_port);
}
int tcp_port = 0;
if (true) {
string tcp_host;
string tcp_hostport = _srs_config->get_rtc_server_tcp_listens().at(0);
string tcp_hostport = config_->get_rtc_server_tcp_listens().at(0);
srs_net_split_for_listener(tcp_hostport, tcp_host, tcp_port);
}
string protocol = _srs_config->get_rtc_server_protocol();
string protocol = config_->get_rtc_server_protocol();
SrsProtocolUtility utility;
set<string> candidates = discover_candidates(&utility, _srs_config, ruc);
set<string> candidates = discover_candidates(&utility, config_, ruc);
for (set<string>::iterator it = candidates.begin(); it != candidates.end(); ++it) {
string hostname;
int uport = udp_port;
@ -510,9 +520,9 @@ void SrsRtcSessionManager::srs_update_rtc_sessions()
// Check all sessions and dispose the dead sessions.
for (int i = 0; i < (int)conn_manager_->size(); i++) {
SrsRtcConnection *session = dynamic_cast<SrsRtcConnection *>(conn_manager_->at(i));
ISrsRtcConnection *session = dynamic_cast<ISrsRtcConnection *>(conn_manager_->at(i));
// Ignore not session, or already disposing.
if (!session || session->disposing_) {
if (!session || session->is_disposing()) {
continue;
}
@ -564,7 +574,7 @@ srs_error_t SrsRtcSessionManager::on_udp_packet(ISrsUdpMuxSocket *skt)
{
srs_error_t err = srs_success;
SrsRtcConnection *session = NULL;
ISrsRtcConnection *session = NULL;
char *data = skt->data();
int size = skt->size();
bool is_rtp_or_rtcp = srs_is_rtp_or_rtcp((uint8_t *)data, size);
@ -573,11 +583,11 @@ srs_error_t SrsRtcSessionManager::on_udp_packet(ISrsUdpMuxSocket *skt)
uint64_t fast_id = skt->fast_id();
// Try fast id first, if not found, search by long peer id.
if (fast_id) {
session = (SrsRtcConnection *)conn_manager_->find_by_fast_id(fast_id);
session = (ISrsRtcConnection *)conn_manager_->find_by_fast_id(fast_id);
}
if (!session) {
string peer_id = skt->peer_id();
session = (SrsRtcConnection *)conn_manager_->find_by_id(peer_id);
session = (ISrsRtcConnection *)conn_manager_->find_by_id(peer_id);
}
if (session) {

View File

@ -23,6 +23,7 @@
class SrsRtcServer;
class SrsHourGlass;
class SrsRtcConnection;
class ISrsRtcConnection;
class ISrsRequest;
class SrsSdp;
class SrsRtcSource;
@ -30,6 +31,11 @@ class SrsResourceManager;
class SrsAsyncCallWorker;
class ISrsUdpMuxSocket;
class ISrsResourceManager;
class ISrsStreamPublishTokenManager;
class ISrsRtcSourceManager;
class ISrsDtlsCertificate;
class ISrsAppConfig;
class ISrsAppFactory;
// The UDP black hole, for developer to use wireshark to catch plaintext packets.
// For example, server receive UDP packets at udp://8000, and forward the plaintext packet to black hole,
@ -96,6 +102,11 @@ class SrsRtcSessionManager : public ISrsExecRtcAsyncTask
{
private:
ISrsResourceManager *conn_manager_;
ISrsStreamPublishTokenManager *stream_publish_tokens_;
ISrsRtcSourceManager *rtc_sources_;
ISrsDtlsCertificate *dtls_certificate_;
ISrsAppConfig *config_;
ISrsAppFactory *app_factory_;
private:
// WebRTC async call worker for non-blocking operations.
@ -109,11 +120,11 @@ public:
virtual srs_error_t initialize();
public:
virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession);
virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession);
private:
virtual srs_error_t do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection *session);
virtual srs_error_t do_create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection *session);
public:
virtual void srs_update_rtc_sessions();

View File

@ -1418,12 +1418,12 @@ srs_error_t SrsServer::listen_rtc_api()
return err;
}
SrsRtcConnection *SrsServer::find_rtc_session_by_username(const std::string &username)
ISrsRtcConnection *SrsServer::find_rtc_session_by_username(const std::string &username)
{
return rtc_session_manager_->find_rtc_session_by_username(username);
}
srs_error_t SrsServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession)
srs_error_t SrsServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession)
{
srs_error_t err = srs_success;

View File

@ -70,6 +70,7 @@ class ISrsStatistic;
class ISrsHourGlass;
class ISrsAppFactory;
class ISrsUdpMuxSocket;
class ISrsRtcConnection;
// Initialize global shared variables cross all threads.
extern srs_error_t srs_global_initialize();
@ -104,8 +105,8 @@ public:
virtual ~ISrsRtcApiServer();
public:
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession) = 0;
virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag) = 0;
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession) = 0;
virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag) = 0;
};
// SrsServer is the main server class of SRS (Simple Realtime Server) that provides comprehensive
@ -305,8 +306,8 @@ private:
virtual srs_error_t listen_rtc_api();
public:
virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession);
virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession);
private:
virtual srs_error_t srs_update_server_statistics();

View File

@ -3244,6 +3244,11 @@ ISrsIpListener *MockDvrAppFactory::create_tcp_listener(ISrsTcpHandler *handler)
return NULL;
}
ISrsRtcConnection *MockDvrAppFactory::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid)
{
return NULL;
}
ISrsCoroutine *MockDvrAppFactory::create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid)
{
return NULL;

View File

@ -642,6 +642,7 @@ public:
virtual ISrsFragmentWindow *create_fragment_window();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
virtual ISrsIpListener *create_tcp_listener(ISrsTcpHandler *handler);
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid);
// ISrsKernelFactory interface methods
virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid);
virtual ISrsTime *create_time();

View File

@ -1654,10 +1654,69 @@ void MockRtcConnectionForTcpConn::alive()
{
}
bool MockRtcConnectionForTcpConn::is_alive()
{
return true;
}
bool MockRtcConnectionForTcpConn::is_disposing()
{
return false;
}
void MockRtcConnectionForTcpConn::switch_to_context()
{
}
srs_error_t MockRtcConnectionForTcpConn::add_publisher(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/)
{
return srs_success;
}
srs_error_t MockRtcConnectionForTcpConn::add_player(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/)
{
return srs_success;
}
void MockRtcConnectionForTcpConn::set_all_tracks_status(std::string /*stream_uri*/, bool /*is_publish*/, bool /*status*/)
{
}
void MockRtcConnectionForTcpConn::set_remote_sdp(const SrsSdp & /*sdp*/)
{
}
void MockRtcConnectionForTcpConn::set_local_sdp(const SrsSdp & /*sdp*/)
{
}
void MockRtcConnectionForTcpConn::set_state_as_waiting_stun()
{
}
srs_error_t MockRtcConnectionForTcpConn::initialize(ISrsRequest * /*r*/, bool /*dtls*/, bool /*srtp*/, std::string /*username*/)
{
return srs_success;
}
std::string MockRtcConnectionForTcpConn::username()
{
return "";
}
std::string MockRtcConnectionForTcpConn::token()
{
return "";
}
void MockRtcConnectionForTcpConn::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> /*publish_token*/)
{
}
void MockRtcConnectionForTcpConn::simulate_nack_drop(int /*nn*/)
{
}
// Mock ISrsPsPackHandler implementation
MockPsPackHandler::MockPsPackHandler()
{
@ -2406,6 +2465,11 @@ ISrsIpListener *MockAppFactoryForGbPublish::create_tcp_listener(ISrsTcpHandler *
return NULL;
}
ISrsRtcConnection *MockAppFactoryForGbPublish::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid)
{
return NULL;
}
ISrsCoroutine *MockAppFactoryForGbPublish::create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid)
{
return NULL;
@ -2903,10 +2967,69 @@ void MockRtcConnectionForUdpNetwork::alive()
{
}
bool MockRtcConnectionForUdpNetwork::is_alive()
{
return true;
}
bool MockRtcConnectionForUdpNetwork::is_disposing()
{
return false;
}
void MockRtcConnectionForUdpNetwork::switch_to_context()
{
}
srs_error_t MockRtcConnectionForUdpNetwork::add_publisher(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/)
{
return srs_success;
}
srs_error_t MockRtcConnectionForUdpNetwork::add_player(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/)
{
return srs_success;
}
void MockRtcConnectionForUdpNetwork::set_all_tracks_status(std::string /*stream_uri*/, bool /*is_publish*/, bool /*status*/)
{
}
void MockRtcConnectionForUdpNetwork::set_remote_sdp(const SrsSdp & /*sdp*/)
{
}
void MockRtcConnectionForUdpNetwork::set_local_sdp(const SrsSdp & /*sdp*/)
{
}
void MockRtcConnectionForUdpNetwork::set_state_as_waiting_stun()
{
}
srs_error_t MockRtcConnectionForUdpNetwork::initialize(ISrsRequest * /*r*/, bool /*dtls*/, bool /*srtp*/, std::string /*username*/)
{
return srs_success;
}
std::string MockRtcConnectionForUdpNetwork::username()
{
return "";
}
std::string MockRtcConnectionForUdpNetwork::token()
{
return "";
}
void MockRtcConnectionForUdpNetwork::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> /*publish_token*/)
{
}
void MockRtcConnectionForUdpNetwork::simulate_nack_drop(int /*nn*/)
{
}
void MockRtcConnectionForUdpNetwork::set_on_dtls_alert_error(srs_error_t err)
{
srs_freep(on_dtls_alert_error_);
@ -4096,11 +4219,70 @@ void MockRtcConnectionForTcpConnHandshake::alive()
{
}
bool MockRtcConnectionForTcpConnHandshake::is_alive()
{
return true;
}
bool MockRtcConnectionForTcpConnHandshake::is_disposing()
{
return false;
}
void MockRtcConnectionForTcpConnHandshake::switch_to_context()
{
switch_to_context_called_ = true;
}
srs_error_t MockRtcConnectionForTcpConnHandshake::add_publisher(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/)
{
return srs_success;
}
srs_error_t MockRtcConnectionForTcpConnHandshake::add_player(SrsRtcUserConfig * /*ruc*/, SrsSdp & /*local_sdp*/)
{
return srs_success;
}
void MockRtcConnectionForTcpConnHandshake::set_all_tracks_status(std::string /*stream_uri*/, bool /*is_publish*/, bool /*status*/)
{
}
void MockRtcConnectionForTcpConnHandshake::set_remote_sdp(const SrsSdp & /*sdp*/)
{
}
void MockRtcConnectionForTcpConnHandshake::set_local_sdp(const SrsSdp & /*sdp*/)
{
}
void MockRtcConnectionForTcpConnHandshake::set_state_as_waiting_stun()
{
}
srs_error_t MockRtcConnectionForTcpConnHandshake::initialize(ISrsRequest * /*r*/, bool /*dtls*/, bool /*srtp*/, std::string /*username*/)
{
return srs_success;
}
std::string MockRtcConnectionForTcpConnHandshake::username()
{
return "";
}
std::string MockRtcConnectionForTcpConnHandshake::token()
{
return "";
}
void MockRtcConnectionForTcpConnHandshake::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> /*publish_token*/)
{
}
void MockRtcConnectionForTcpConnHandshake::simulate_nack_drop(int /*nn*/)
{
}
void MockRtcConnectionForTcpConnHandshake::reset()
{
tcp_network_ = NULL;

View File

@ -157,7 +157,20 @@ public:
virtual ISrsRtcNetwork *udp();
virtual ISrsRtcNetwork *tcp();
virtual void alive();
virtual bool is_alive();
virtual bool is_disposing();
virtual void switch_to_context();
virtual srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
virtual srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
virtual void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status);
virtual void set_remote_sdp(const SrsSdp &sdp);
virtual void set_local_sdp(const SrsSdp &sdp);
virtual void set_state_as_waiting_stun();
virtual srs_error_t initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username);
virtual std::string username();
virtual std::string token();
virtual void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
virtual void simulate_nack_drop(int nn);
public:
void set_on_dtls_alert_error(srs_error_t err);
@ -477,7 +490,20 @@ public:
virtual ISrsRtcNetwork *udp();
virtual ISrsRtcNetwork *tcp();
virtual void alive();
virtual bool is_alive();
virtual bool is_disposing();
virtual void switch_to_context();
virtual srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
virtual srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
virtual void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status);
virtual void set_remote_sdp(const SrsSdp &sdp);
virtual void set_local_sdp(const SrsSdp &sdp);
virtual void set_state_as_waiting_stun();
virtual srs_error_t initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username);
virtual std::string username();
virtual std::string token();
virtual void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
virtual void simulate_nack_drop(int nn);
};
// Mock ISrsPsPackHandler for testing SrsPackContext
@ -606,6 +632,7 @@ public:
virtual ISrsFragmentWindow *create_fragment_window();
virtual ISrsFragmentedMp4 *create_fragmented_mp4();
virtual ISrsIpListener *create_tcp_listener(ISrsTcpHandler *handler);
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid);
// ISrsKernelFactory interface methods
virtual ISrsCoroutine *create_coroutine(const std::string &name, ISrsCoroutineHandler *handler, SrsContextId cid);
virtual ISrsTime *create_time();
@ -847,7 +874,20 @@ public:
virtual ISrsRtcNetwork *udp();
virtual ISrsRtcNetwork *tcp();
virtual void alive();
virtual bool is_alive();
virtual bool is_disposing();
virtual void switch_to_context();
virtual srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
virtual srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
virtual void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status);
virtual void set_remote_sdp(const SrsSdp &sdp);
virtual void set_local_sdp(const SrsSdp &sdp);
virtual void set_state_as_waiting_stun();
virtual srs_error_t initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username);
virtual std::string username();
virtual std::string token();
virtual void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
virtual void simulate_nack_drop(int nn);
public:
void reset();

View File

@ -1774,7 +1774,7 @@ MockRtcApiServer::~MockRtcApiServer()
srs_freep(mock_connection_);
}
srs_error_t MockRtcApiServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession)
srs_error_t MockRtcApiServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession)
{
create_session_called_ = true;
@ -1793,7 +1793,7 @@ srs_error_t MockRtcApiServer::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &
return srs_success;
}
SrsRtcConnection *MockRtcApiServer::find_rtc_session_by_username(const std::string &ufrag)
ISrsRtcConnection *MockRtcApiServer::find_rtc_session_by_username(const std::string &ufrag)
{
find_username_ = ufrag;
// Return NULL to simulate session not found (easier to test than full mock)
@ -2126,7 +2126,7 @@ MockRtcApiServerForPlay::~MockRtcApiServerForPlay()
srs_freep(mock_connection_);
}
srs_error_t MockRtcApiServerForPlay::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession)
srs_error_t MockRtcApiServerForPlay::create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession)
{
create_session_called_ = true;
@ -2143,7 +2143,7 @@ srs_error_t MockRtcApiServerForPlay::create_rtc_session(SrsRtcUserConfig *ruc, S
return srs_success;
}
SrsRtcConnection *MockRtcApiServerForPlay::find_rtc_session_by_username(const std::string &ufrag)
ISrsRtcConnection *MockRtcApiServerForPlay::find_rtc_session_by_username(const std::string &ufrag)
{
return NULL;
}

View File

@ -290,8 +290,8 @@ public:
virtual ~MockRtcApiServer();
public:
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession);
virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession);
virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
};
// Mock ISrsStatistic for testing RTC API
@ -447,8 +447,8 @@ public:
virtual ~MockRtcApiServerForPlay();
public:
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, SrsRtcConnection **psession);
virtual SrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
virtual srs_error_t create_rtc_session(SrsRtcUserConfig *ruc, SrsSdp &local_sdp, ISrsRtcConnection **psession);
virtual ISrsRtcConnection *find_rtc_session_by_username(const std::string &ufrag);
};
// Mock ISrsHttpResponseReader for testing SrsHttpHooks

View File

@ -17,6 +17,7 @@ using namespace std;
#include <srs_kernel_st.hpp>
#include <srs_kernel_utility.hpp>
#include <srs_protocol_utility.hpp>
#include <srs_utest_app14.hpp>
#include <sstream>
// Mock ISrsSrtSocket implementation
@ -1855,3 +1856,752 @@ VOID TEST(RtcServerTest, DiscoverCandidates_EipOverride)
EXPECT_TRUE(candidates.find("198.51.100.20") != candidates.end());
EXPECT_TRUE(candidates.find("192.168.1.100") != candidates.end());
}
// Mock ISrsStreamPublishTokenManager implementation
MockStreamPublishTokenManager::MockStreamPublishTokenManager()
{
acquire_token_error_ = srs_success;
acquire_token_count_ = 0;
release_token_count_ = 0;
token_to_return_ = NULL;
}
MockStreamPublishTokenManager::~MockStreamPublishTokenManager()
{
srs_freep(acquire_token_error_);
// Note: Don't free token_to_return_ because it's managed by SrsSharedPtr in the caller
}
srs_error_t MockStreamPublishTokenManager::acquire_token(ISrsRequest *req, SrsStreamPublishToken *&token)
{
acquire_token_count_++;
if (acquire_token_error_ != srs_success) {
token = NULL;
return srs_error_copy(acquire_token_error_);
}
// Create a new token if not already created
if (!token_to_return_) {
token_to_return_ = new SrsStreamPublishToken(req->get_stream_url(), NULL);
}
token = token_to_return_;
return srs_success;
}
void MockStreamPublishTokenManager::release_token(const std::string &stream_url)
{
release_token_count_++;
}
void MockStreamPublishTokenManager::set_acquire_token_error(srs_error_t err)
{
srs_freep(acquire_token_error_);
acquire_token_error_ = srs_error_copy(err);
}
void MockStreamPublishTokenManager::reset()
{
srs_freep(acquire_token_error_);
// Note: Don't free token_to_return_ here because it may have been freed by SrsSharedPtr
// Just set it to NULL
acquire_token_error_ = srs_success;
acquire_token_count_ = 0;
release_token_count_ = 0;
token_to_return_ = NULL;
}
// Mock ISrsRtcConnection implementation
MockRtcConnectionForSessionManager::MockRtcConnectionForSessionManager()
{
add_publisher_called_ = false;
add_player_called_ = false;
set_all_tracks_status_called_ = false;
set_publish_token_called_ = false;
add_publisher_error_ = srs_success;
add_player_error_ = srs_success;
username_ = "test-username";
token_ = "test-token";
}
MockRtcConnectionForSessionManager::~MockRtcConnectionForSessionManager()
{
srs_freep(add_publisher_error_);
srs_freep(add_player_error_);
}
srs_error_t MockRtcConnectionForSessionManager::add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp)
{
add_publisher_called_ = true;
return srs_error_copy(add_publisher_error_);
}
srs_error_t MockRtcConnectionForSessionManager::add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp)
{
add_player_called_ = true;
return srs_error_copy(add_player_error_);
}
void MockRtcConnectionForSessionManager::set_all_tracks_status(std::string stream_uri, bool is_publish, bool status)
{
set_all_tracks_status_called_ = true;
}
void MockRtcConnectionForSessionManager::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token)
{
set_publish_token_called_ = true;
publish_token_ = publish_token;
}
void MockRtcConnectionForSessionManager::reset()
{
add_publisher_called_ = false;
add_player_called_ = false;
set_all_tracks_status_called_ = false;
set_publish_token_called_ = false;
srs_freep(add_publisher_error_);
srs_freep(add_player_error_);
add_publisher_error_ = srs_success;
add_player_error_ = srs_success;
}
// Mock ISrsAppFactory implementation
MockAppFactoryForSessionManager::MockAppFactoryForSessionManager()
{
mock_connection_ = new MockRtcConnectionForSessionManager();
create_rtc_connection_count_ = 0;
}
MockAppFactoryForSessionManager::~MockAppFactoryForSessionManager()
{
srs_freep(mock_connection_);
}
ISrsRtcConnection *MockAppFactoryForSessionManager::create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid)
{
create_rtc_connection_count_++;
// Create a real SrsRtcConnection object for testing
// The test will inject mock_connection_ methods into it
SrsRtcConnection *session = new SrsRtcConnection(exec, cid);
return session;
}
void MockAppFactoryForSessionManager::reset()
{
create_rtc_connection_count_ = 0;
}
// Unit test for SrsRtcSessionManager::create_rtc_session
// This test verifies the major use scenario: token acquisition and error handling
// Note: Full session creation requires complex initialization, so we test the key logic paths
VOID TEST(RtcSessionManagerTest, CreateRtcSession_TokenAcquisitionAndErrorHandling)
{
srs_error_t err;
// Create mock dependencies
MockResourceManagerForBindSession mock_conn_manager;
MockStreamPublishTokenManager mock_token_manager;
MockRtcSourceManager mock_rtc_sources;
// Create SrsRtcSessionManager
SrsUniquePtr<SrsRtcSessionManager> session_manager(new SrsRtcSessionManager());
// Inject mock dependencies
session_manager->conn_manager_ = &mock_conn_manager;
session_manager->stream_publish_tokens_ = &mock_token_manager;
session_manager->rtc_sources_ = &mock_rtc_sources;
// Test 1: Verify error handling when token acquisition fails
{
// Set token acquisition to fail
mock_token_manager.set_acquire_token_error(srs_error_new(ERROR_SYSTEM_STREAM_BUSY, "stream busy"));
// Create RTC user config for publishing
SrsUniquePtr<SrsRtcUserConfig> ruc(new SrsRtcUserConfig());
ruc->publish_ = true;
ruc->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream1");
// Create local SDP
SrsSdp local_sdp;
// Test: Create RTC session should fail due to token acquisition error
ISrsRtcConnection *session = NULL;
HELPER_EXPECT_FAILED(session_manager->create_rtc_session(ruc.get(), local_sdp, &session));
// Verify: Token acquisition was attempted
EXPECT_EQ(mock_token_manager.acquire_token_count_, 1);
// Verify: RTC source was NOT fetched/created (because token acquisition failed first)
EXPECT_EQ(mock_rtc_sources.fetch_or_create_count_, 0);
// Clean up
srs_freep(session);
srs_freep(ruc->req_);
}
// Test 2: Verify error handling when source fetch/create fails
{
mock_token_manager.reset();
mock_rtc_sources.reset();
// Set source fetch/create to fail
mock_rtc_sources.set_fetch_or_create_error(srs_error_new(ERROR_SYSTEM_CREATE_PIPE, "create source failed"));
// Create RTC user config for publishing
SrsUniquePtr<SrsRtcUserConfig> ruc2(new SrsRtcUserConfig());
ruc2->publish_ = true;
ruc2->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream2");
// Create local SDP
SrsSdp local_sdp2;
// Test: Create RTC session should fail due to source creation error
ISrsRtcConnection *session2 = NULL;
HELPER_EXPECT_FAILED(session_manager->create_rtc_session(ruc2.get(), local_sdp2, &session2));
// Verify: Token acquisition was attempted
EXPECT_EQ(mock_token_manager.acquire_token_count_, 1);
// Verify: RTC source fetch/create was attempted
EXPECT_EQ(mock_rtc_sources.fetch_or_create_count_, 1);
// Clean up
srs_freep(session2);
srs_freep(ruc2->req_);
}
// Test 3: Verify error handling when source cannot publish (stream busy)
{
mock_token_manager.reset();
mock_rtc_sources.reset();
// Set source to not allow publishing (simulate stream busy)
// can_publish() returns !is_created_, so set is_created_ to true
mock_rtc_sources.mock_source_->is_created_ = true;
// Create RTC user config for publishing
SrsUniquePtr<SrsRtcUserConfig> ruc3(new SrsRtcUserConfig());
ruc3->publish_ = true;
ruc3->req_ = new MockRtcAsyncCallRequest("test.vhost", "live", "stream3");
// Create local SDP
SrsSdp local_sdp3;
// Test: Create RTC session should fail because source is busy
ISrsRtcConnection *session3 = NULL;
HELPER_EXPECT_FAILED(session_manager->create_rtc_session(ruc3.get(), local_sdp3, &session3));
// Verify: Token acquisition was attempted
EXPECT_EQ(mock_token_manager.acquire_token_count_, 1);
// Verify: RTC source fetch/create was attempted
EXPECT_EQ(mock_rtc_sources.fetch_or_create_count_, 1);
// Clean up
srs_freep(session3);
srs_freep(ruc3->req_);
}
// Clean up - set to NULL to avoid double-free
session_manager->conn_manager_ = NULL;
session_manager->stream_publish_tokens_ = NULL;
session_manager->rtc_sources_ = NULL;
}
// Mock ISrsRtcConnection implementation for srs_update_rtc_sessions test
MockRtcConnectionForUpdateSessions::MockRtcConnectionForUpdateSessions()
{
is_alive_ = true;
is_disposing_ = false;
username_ = "test-user";
switch_to_context_called_ = false;
alive_called_ = false;
udp_network_ = NULL;
}
MockRtcConnectionForUpdateSessions::~MockRtcConnectionForUpdateSessions()
{
udp_network_ = NULL;
}
const SrsContextId &MockRtcConnectionForUpdateSessions::get_id()
{
return cid_;
}
std::string MockRtcConnectionForUpdateSessions::desc()
{
return "MockRtcConnection";
}
void MockRtcConnectionForUpdateSessions::on_disposing(ISrsResource *c)
{
}
void MockRtcConnectionForUpdateSessions::on_before_dispose(ISrsResource *c)
{
}
void MockRtcConnectionForUpdateSessions::expire()
{
}
srs_error_t MockRtcConnectionForUpdateSessions::send_rtcp(char *data, int nb_data)
{
return srs_success;
}
srs_error_t MockRtcConnectionForUpdateSessions::send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer *rtp_queue, const uint64_t &last_send_systime, const SrsNtp &last_send_ntp)
{
return srs_success;
}
srs_error_t MockRtcConnectionForUpdateSessions::send_rtcp_xr_rrtr(uint32_t ssrc)
{
return srs_success;
}
void MockRtcConnectionForUpdateSessions::check_send_nacks(SrsRtpNackForReceiver *nack, uint32_t ssrc, uint32_t &sent_nacks, uint32_t &timeout_nacks)
{
}
srs_error_t MockRtcConnectionForUpdateSessions::send_rtcp_fb_pli(uint32_t ssrc, const SrsContextId &cid_of_subscriber)
{
return srs_success;
}
srs_error_t MockRtcConnectionForUpdateSessions::do_send_packet(SrsRtpPacket *pkt)
{
return srs_success;
}
srs_error_t MockRtcConnectionForUpdateSessions::do_check_send_nacks()
{
return srs_success;
}
void MockRtcConnectionForUpdateSessions::on_timer_nack()
{
}
srs_error_t MockRtcConnectionForUpdateSessions::on_dtls_handshake_done()
{
return srs_success;
}
srs_error_t MockRtcConnectionForUpdateSessions::on_dtls_alert(std::string type, std::string desc)
{
return srs_success;
}
srs_error_t MockRtcConnectionForUpdateSessions::on_rtp_cipher(char *data, int nb_data)
{
return srs_success;
}
srs_error_t MockRtcConnectionForUpdateSessions::on_rtp_plaintext(char *data, int nb_data)
{
return srs_success;
}
srs_error_t MockRtcConnectionForUpdateSessions::on_rtcp(char *data, int nb_data)
{
return srs_success;
}
srs_error_t MockRtcConnectionForUpdateSessions::on_binding_request(SrsStunPacket *r, std::string &ice_pwd)
{
return srs_success;
}
ISrsRtcNetwork *MockRtcConnectionForUpdateSessions::udp()
{
return udp_network_;
}
ISrsRtcNetwork *MockRtcConnectionForUpdateSessions::tcp()
{
return NULL;
}
void MockRtcConnectionForUpdateSessions::alive()
{
alive_called_ = true;
}
bool MockRtcConnectionForUpdateSessions::is_alive()
{
return is_alive_;
}
bool MockRtcConnectionForUpdateSessions::is_disposing()
{
return is_disposing_;
}
void MockRtcConnectionForUpdateSessions::switch_to_context()
{
switch_to_context_called_ = true;
}
srs_error_t MockRtcConnectionForUpdateSessions::add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp)
{
return srs_success;
}
srs_error_t MockRtcConnectionForUpdateSessions::add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp)
{
return srs_success;
}
void MockRtcConnectionForUpdateSessions::set_all_tracks_status(std::string stream_uri, bool is_publish, bool status)
{
}
void MockRtcConnectionForUpdateSessions::set_remote_sdp(const SrsSdp &sdp)
{
}
void MockRtcConnectionForUpdateSessions::set_local_sdp(const SrsSdp &sdp)
{
}
void MockRtcConnectionForUpdateSessions::set_state_as_waiting_stun()
{
}
srs_error_t MockRtcConnectionForUpdateSessions::initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username)
{
return srs_success;
}
std::string MockRtcConnectionForUpdateSessions::username()
{
return username_;
}
std::string MockRtcConnectionForUpdateSessions::token()
{
return "test-token";
}
void MockRtcConnectionForUpdateSessions::set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token)
{
}
void MockRtcConnectionForUpdateSessions::simulate_drop_packet(bool v, int nn)
{
}
void MockRtcConnectionForUpdateSessions::simulate_nack_drop(int nn)
{
}
// Mock ISrsResourceManager implementation for srs_update_rtc_sessions test
MockResourceManagerForUpdateSessions::MockResourceManagerForUpdateSessions()
{
}
MockResourceManagerForUpdateSessions::~MockResourceManagerForUpdateSessions()
{
reset();
}
srs_error_t MockResourceManagerForUpdateSessions::start()
{
return srs_success;
}
bool MockResourceManagerForUpdateSessions::empty()
{
return resources_.empty();
}
size_t MockResourceManagerForUpdateSessions::size()
{
return resources_.size();
}
void MockResourceManagerForUpdateSessions::add(ISrsResource *conn, bool *exists)
{
resources_.push_back(conn);
}
void MockResourceManagerForUpdateSessions::add_with_id(const std::string &id, ISrsResource *conn)
{
resources_.push_back(conn);
id_map_[id] = conn;
}
void MockResourceManagerForUpdateSessions::add_with_fast_id(uint64_t id, ISrsResource *conn)
{
resources_.push_back(conn);
fast_id_map_[id] = conn;
}
void MockResourceManagerForUpdateSessions::add_with_name(const std::string &name, ISrsResource *conn)
{
resources_.push_back(conn);
name_map_[name] = conn;
}
ISrsResource *MockResourceManagerForUpdateSessions::at(int index)
{
if (index < 0 || index >= (int)resources_.size()) {
return NULL;
}
return resources_[index];
}
ISrsResource *MockResourceManagerForUpdateSessions::find_by_id(std::string id)
{
std::map<std::string, ISrsResource *>::iterator it = id_map_.find(id);
if (it != id_map_.end()) {
return it->second;
}
return NULL;
}
ISrsResource *MockResourceManagerForUpdateSessions::find_by_fast_id(uint64_t id)
{
std::map<uint64_t, ISrsResource *>::iterator it = fast_id_map_.find(id);
if (it != fast_id_map_.end()) {
return it->second;
}
return NULL;
}
ISrsResource *MockResourceManagerForUpdateSessions::find_by_name(std::string name)
{
std::map<std::string, ISrsResource *>::iterator it = name_map_.find(name);
if (it != name_map_.end()) {
return it->second;
}
return NULL;
}
void MockResourceManagerForUpdateSessions::remove(ISrsResource *c)
{
removed_resources_.push_back(c);
// Remove from resources_ vector
for (std::vector<ISrsResource *>::iterator it = resources_.begin(); it != resources_.end(); ++it) {
if (*it == c) {
resources_.erase(it);
break;
}
}
}
void MockResourceManagerForUpdateSessions::subscribe(ISrsDisposingHandler *h)
{
}
void MockResourceManagerForUpdateSessions::unsubscribe(ISrsDisposingHandler *h)
{
}
void MockResourceManagerForUpdateSessions::reset()
{
resources_.clear();
removed_resources_.clear();
id_map_.clear();
fast_id_map_.clear();
name_map_.clear();
}
// Unit test for SrsRtcSessionManager::srs_update_rtc_sessions
// This test verifies the major use scenario: checking sessions and disposing dead sessions
VOID TEST(RtcSessionManagerTest, UpdateRtcSessions_CheckAndDisposeDeadSessions)
{
// Create mock connection manager
SrsUniquePtr<MockResourceManagerForUpdateSessions> mock_conn_manager(new MockResourceManagerForUpdateSessions());
// Create SrsRtcSessionManager
SrsUniquePtr<SrsRtcSessionManager> session_manager(new SrsRtcSessionManager());
// Inject mock connection manager
session_manager->conn_manager_ = mock_conn_manager.get();
// Test scenario: Multiple sessions with different states
// - Session 1: Alive session (should be counted, not removed)
// - Session 2: Dead session (not alive, should be removed)
// - Session 3: Disposing session (should be ignored)
// - Session 4: Alive session (should be counted, not removed)
// Create mock sessions
MockRtcConnectionForUpdateSessions *session1 = new MockRtcConnectionForUpdateSessions();
session1->is_alive_ = true;
session1->is_disposing_ = false;
session1->username_ = "user1";
MockRtcConnectionForUpdateSessions *session2 = new MockRtcConnectionForUpdateSessions();
session2->is_alive_ = false; // Dead session
session2->is_disposing_ = false;
session2->username_ = "user2";
MockRtcConnectionForUpdateSessions *session3 = new MockRtcConnectionForUpdateSessions();
session3->is_alive_ = false;
session3->is_disposing_ = true; // Already disposing
session3->username_ = "user3";
MockRtcConnectionForUpdateSessions *session4 = new MockRtcConnectionForUpdateSessions();
session4->is_alive_ = true;
session4->is_disposing_ = false;
session4->username_ = "user4";
// Add sessions to mock connection manager
mock_conn_manager->add(session1);
mock_conn_manager->add(session2);
mock_conn_manager->add(session3);
mock_conn_manager->add(session4);
// Verify initial state
EXPECT_EQ(mock_conn_manager->size(), 4);
EXPECT_EQ(mock_conn_manager->removed_resources_.size(), 0);
// Call srs_update_rtc_sessions
session_manager->srs_update_rtc_sessions();
// Verify results:
// 1. Dead session (session2) should be removed
EXPECT_EQ(mock_conn_manager->removed_resources_.size(), 1);
EXPECT_EQ(mock_conn_manager->removed_resources_[0], session2);
// 2. switch_to_context should be called for dead session
EXPECT_TRUE(session2->switch_to_context_called_);
// 3. Alive sessions should NOT be removed
EXPECT_FALSE(session1->switch_to_context_called_);
EXPECT_FALSE(session4->switch_to_context_called_);
// 4. Disposing session should be ignored (not removed again)
EXPECT_FALSE(session3->switch_to_context_called_);
// 5. Connection manager should have 3 sessions left (session1, session3, session4)
EXPECT_EQ(mock_conn_manager->size(), 3);
// Clean up - set to NULL to avoid double-free
session_manager->conn_manager_ = NULL;
// Free mock sessions
srs_freep(session1);
srs_freep(session2);
srs_freep(session3);
srs_freep(session4);
}
// Mock ISrsRtcNetwork implementation for on_udp_packet tests
MockRtcNetworkForUdpNetwork::MockRtcNetworkForUdpNetwork()
{
on_stun_called_ = false;
on_rtp_called_ = false;
on_rtcp_called_ = false;
on_dtls_called_ = false;
}
MockRtcNetworkForUdpNetwork::~MockRtcNetworkForUdpNetwork()
{
}
srs_error_t MockRtcNetworkForUdpNetwork::initialize(SrsSessionConfig *cfg, bool dtls, bool srtp)
{
return srs_success;
}
void MockRtcNetworkForUdpNetwork::set_state(SrsRtcNetworkState state)
{
}
srs_error_t MockRtcNetworkForUdpNetwork::on_dtls_handshake_done()
{
return srs_success;
}
srs_error_t MockRtcNetworkForUdpNetwork::on_dtls_alert(std::string type, std::string desc)
{
return srs_success;
}
srs_error_t MockRtcNetworkForUdpNetwork::on_dtls(char *data, int nb_data)
{
on_dtls_called_ = true;
return srs_success;
}
srs_error_t MockRtcNetworkForUdpNetwork::on_stun(SrsStunPacket *r, char *data, int nb_data)
{
on_stun_called_ = true;
return srs_success;
}
srs_error_t MockRtcNetworkForUdpNetwork::on_rtp(char *data, int nb_data)
{
on_rtp_called_ = true;
return srs_success;
}
srs_error_t MockRtcNetworkForUdpNetwork::on_rtcp(char *data, int nb_data)
{
on_rtcp_called_ = true;
return srs_success;
}
srs_error_t MockRtcNetworkForUdpNetwork::protect_rtp(void *packet, int *nb_cipher)
{
return srs_success;
}
srs_error_t MockRtcNetworkForUdpNetwork::protect_rtcp(void *packet, int *nb_cipher)
{
return srs_success;
}
bool MockRtcNetworkForUdpNetwork::is_establelished()
{
return true;
}
srs_error_t MockRtcNetworkForUdpNetwork::write(void *buf, size_t size, ssize_t *nwrite)
{
return srs_success;
}
// Test SrsRtcSessionManager::on_udp_packet with no session found (error case)
VOID TEST(RtcSessionManagerTest, OnUdpPacket_NoSessionFound)
{
srs_error_t err = srs_success;
// Create session manager
SrsUniquePtr<SrsRtcSessionManager> session_manager(new SrsRtcSessionManager());
// Create mock connection manager (empty - no sessions)
SrsUniquePtr<MockResourceManagerForUpdateSessions> mock_conn_manager(new MockResourceManagerForUpdateSessions());
// Inject mock connection manager
session_manager->conn_manager_ = mock_conn_manager.get();
// Create RTP packet (V=2, PT=96 for RTP)
char rtp_packet[20];
memset(rtp_packet, 0, sizeof(rtp_packet));
rtp_packet[0] = (char)0x80; // V=2 (10000000)
rtp_packet[1] = 96; // PT=96 (RTP payload type)
// Create mock UDP socket
SrsUniquePtr<MockUdpMuxSocket> mock_socket(new MockUdpMuxSocket());
mock_socket->data_ = rtp_packet;
mock_socket->size_ = 20;
mock_socket->fast_id_ = 12345; // Non-existent session
mock_socket->peer_id_ = "192.168.1.100:8000";
// Call on_udp_packet
err = session_manager->on_udp_packet(mock_socket.get());
// Verify: Should fail when no session is found
HELPER_EXPECT_FAILED(err);
// Clean up
session_manager->conn_manager_ = NULL;
}
// Note: The remaining tests for RTP, RTCP, DTLS, and STUN packets require more complex setup
// including proper mock UDP networks and session initialization. The above test covers the
// basic error path when no session is found, which is a key scenario in on_udp_packet.

View File

@ -17,6 +17,8 @@
#include <srs_utest_app10.hpp>
#include <srs_utest_app11.hpp>
#include <srs_utest_app6.hpp>
#include <srs_app_stream_token.hpp>
#include <srs_app_rtc_conn.hpp>
// Mock ISrsSrtSocket for testing SrsSrtConnection
class MockSrtSocket : public ISrsSrtSocket
@ -252,4 +254,204 @@ public:
virtual std::string get_rtc_server_ip_family();
};
// Mock ISrsStreamPublishTokenManager for testing SrsRtcSessionManager
class MockStreamPublishTokenManager : public ISrsStreamPublishTokenManager
{
public:
srs_error_t acquire_token_error_;
int acquire_token_count_;
int release_token_count_;
SrsStreamPublishToken *token_to_return_;
public:
MockStreamPublishTokenManager();
virtual ~MockStreamPublishTokenManager();
public:
virtual srs_error_t acquire_token(ISrsRequest *req, SrsStreamPublishToken *&token);
virtual void release_token(const std::string &stream_url);
void set_acquire_token_error(srs_error_t err);
void reset();
};
// Mock ISrsRtcConnection for testing SrsRtcSessionManager
// Note: This is a simplified mock that only implements the methods needed for testing
class MockRtcConnectionForSessionManager
{
public:
bool add_publisher_called_;
bool add_player_called_;
bool set_all_tracks_status_called_;
bool set_publish_token_called_;
srs_error_t add_publisher_error_;
srs_error_t add_player_error_;
std::string username_;
std::string token_;
SrsSharedPtr<SrsStreamPublishToken> publish_token_;
public:
MockRtcConnectionForSessionManager();
virtual ~MockRtcConnectionForSessionManager();
public:
srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status);
void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
void reset();
};
// Mock ISrsAppFactory for testing SrsRtcSessionManager
class MockAppFactoryForSessionManager : public SrsAppFactory
{
public:
MockRtcConnectionForSessionManager *mock_connection_;
int create_rtc_connection_count_;
public:
MockAppFactoryForSessionManager();
virtual ~MockAppFactoryForSessionManager();
public:
virtual ISrsRtcConnection *create_rtc_connection(ISrsExecRtcAsyncTask *exec, const SrsContextId &cid);
void reset();
};
// Mock ISrsRtcConnection for testing SrsRtcSessionManager::srs_update_rtc_sessions
class MockRtcConnectionForUpdateSessions : public ISrsRtcConnection
{
public:
bool is_alive_;
bool is_disposing_;
std::string username_;
bool switch_to_context_called_;
bool alive_called_;
SrsContextId cid_;
ISrsRtcNetwork *udp_network_;
public:
MockRtcConnectionForUpdateSessions();
virtual ~MockRtcConnectionForUpdateSessions();
public:
// ISrsResource interface
virtual const SrsContextId &get_id();
virtual std::string desc();
virtual void on_disposing(ISrsResource *c);
public:
// ISrsDisposingHandler interface
virtual void on_before_dispose(ISrsResource *c);
public:
// ISrsExpire interface
virtual void expire();
public:
// ISrsRtcPacketSender interface
virtual srs_error_t send_rtcp(char *data, int nb_data);
virtual srs_error_t send_rtcp_rr(uint32_t ssrc, SrsRtpRingBuffer *rtp_queue, const uint64_t &last_send_systime, const SrsNtp &last_send_ntp);
virtual srs_error_t send_rtcp_xr_rrtr(uint32_t ssrc);
virtual void check_send_nacks(SrsRtpNackForReceiver *nack, uint32_t ssrc, uint32_t &sent_nacks, uint32_t &timeout_nacks);
virtual srs_error_t send_rtcp_fb_pli(uint32_t ssrc, const SrsContextId &cid_of_subscriber);
virtual srs_error_t do_send_packet(SrsRtpPacket *pkt);
public:
// ISrsRtcPacketReceiver interface
virtual srs_error_t do_check_send_nacks();
public:
// ISrsRtcConnectionNackTimerHandler interface
virtual void on_timer_nack();
public:
// ISrsRtcConnection interface
virtual srs_error_t on_dtls_handshake_done();
virtual srs_error_t on_dtls_alert(std::string type, std::string desc);
virtual srs_error_t on_rtp_cipher(char *data, int nb_data);
virtual srs_error_t on_rtp_plaintext(char *data, int nb_data);
virtual srs_error_t on_rtcp(char *data, int nb_data);
virtual srs_error_t on_binding_request(SrsStunPacket *r, std::string &ice_pwd);
virtual ISrsRtcNetwork *udp();
virtual ISrsRtcNetwork *tcp();
virtual void alive();
virtual bool is_alive();
virtual bool is_disposing();
virtual void switch_to_context();
virtual srs_error_t add_publisher(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
virtual srs_error_t add_player(SrsRtcUserConfig *ruc, SrsSdp &local_sdp);
virtual void set_all_tracks_status(std::string stream_uri, bool is_publish, bool status);
virtual void set_remote_sdp(const SrsSdp &sdp);
virtual void set_local_sdp(const SrsSdp &sdp);
virtual void set_state_as_waiting_stun();
virtual srs_error_t initialize(ISrsRequest *r, bool dtls, bool srtp, std::string username);
virtual std::string username();
virtual std::string token();
virtual void set_publish_token(SrsSharedPtr<SrsStreamPublishToken> publish_token);
virtual void simulate_drop_packet(bool v, int nn);
virtual void simulate_nack_drop(int nn);
};
// Mock ISrsResourceManager for testing SrsRtcSessionManager::srs_update_rtc_sessions
class MockResourceManagerForUpdateSessions : public ISrsResourceManager
{
public:
std::vector<ISrsResource *> resources_;
std::vector<ISrsResource *> removed_resources_;
std::map<std::string, ISrsResource *> id_map_;
std::map<uint64_t, ISrsResource *> fast_id_map_;
std::map<std::string, ISrsResource *> name_map_;
public:
MockResourceManagerForUpdateSessions();
virtual ~MockResourceManagerForUpdateSessions();
public:
virtual srs_error_t start();
virtual bool empty();
virtual size_t size();
virtual void add(ISrsResource *conn, bool *exists = NULL);
virtual void add_with_id(const std::string &id, ISrsResource *conn);
virtual void add_with_fast_id(uint64_t id, ISrsResource *conn);
virtual void add_with_name(const std::string &name, ISrsResource *conn);
virtual ISrsResource *at(int index);
virtual ISrsResource *find_by_id(std::string id);
virtual ISrsResource *find_by_fast_id(uint64_t id);
virtual ISrsResource *find_by_name(std::string name);
virtual void remove(ISrsResource *c);
virtual void subscribe(ISrsDisposingHandler *h);
virtual void unsubscribe(ISrsDisposingHandler *h);
void reset();
};
// Mock ISrsRtcNetwork for testing SrsRtcSessionManager::on_udp_packet
class MockRtcNetworkForUdpNetwork : public ISrsRtcNetwork
{
public:
bool on_stun_called_;
bool on_rtp_called_;
bool on_rtcp_called_;
bool on_dtls_called_;
public:
MockRtcNetworkForUdpNetwork();
virtual ~MockRtcNetworkForUdpNetwork();
public:
virtual srs_error_t initialize(SrsSessionConfig *cfg, bool dtls, bool srtp);
virtual void set_state(SrsRtcNetworkState state);
virtual srs_error_t on_dtls_handshake_done();
virtual srs_error_t on_dtls_alert(std::string type, std::string desc);
virtual srs_error_t on_dtls(char *data, int nb_data);
virtual srs_error_t on_stun(SrsStunPacket *r, char *data, int nb_data);
virtual srs_error_t on_rtp(char *data, int nb_data);
virtual srs_error_t on_rtcp(char *data, int nb_data);
virtual srs_error_t protect_rtp(void *packet, int *nb_cipher);
virtual srs_error_t protect_rtcp(void *packet, int *nb_cipher);
virtual bool is_establelished();
virtual srs_error_t write(void *buf, size_t size, ssize_t *nwrite);
};
#endif