Repo created

This commit is contained in:
Fr4nz D13trich 2025-11-22 14:04:28 +01:00
parent 81b91f4139
commit f8c34fa5ee
22732 changed files with 4815320 additions and 2 deletions

View file

@ -0,0 +1,39 @@
specific_include_rules = {
".*": [
"+modules/video_coding",
],
".*": [
"+video"
],
"dummy_peer_connection\.h": [
"+rtc_base/ref_counted_object.h",
],
"neteq_factory_with_codecs\.h": [
"+system_wrappers/include/clock.h",
],
"network_emulation_manager\.h": [
"+rtc_base/thread.h",
"+rtc_base/network.h",
"+rtc_base/network_constants.h",
],
"peer_network_dependencies\.h": [
"+rtc_base/network.h",
"+rtc_base/thread.h",
],
"peerconnection_quality_test_fixture\.h": [
"+logging/rtc_event_log/rtc_event_log_factory_interface.h",
"+rtc_base/network.h",
"+rtc_base/rtc_certificate_generator.h",
"+rtc_base/ssl_certificate.h",
"+rtc_base/thread.h",
"+media/base/media_constants.h",
"+modules/audio_processing/include/audio_processing.h",
],
"time_controller\.h": [
"+rtc_base/synchronization/yield_policy.h",
"+system_wrappers/include/clock.h",
],
"create_frame_generator\.h": [
"+system_wrappers/include/clock.h",
],
}

View file

@ -0,0 +1,5 @@
mbonadei@webrtc.org
sprang@webrtc.org
srte@webrtc.org
titovartem@webrtc.org

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_AUDIO_QUALITY_ANALYZER_INTERFACE_H_
#define API_TEST_AUDIO_QUALITY_ANALYZER_INTERFACE_H_
#include <string>
#include "api/test/stats_observer_interface.h"
#include "api/test/track_id_stream_info_map.h"
namespace webrtc {
namespace webrtc_pc_e2e {
// API is in development. Can be changed/removed without notice.
class AudioQualityAnalyzerInterface : public StatsObserverInterface {
public:
~AudioQualityAnalyzerInterface() override = default;
// Will be called by the framework before the test.
// `test_case_name` is name of test case, that should be used to report all
// audio metrics.
// `analyzer_helper` is a pointer to a class that will allow track_id to
// stream_id matching. The caller is responsible for ensuring the
// AnalyzerHelper outlives the instance of the AudioQualityAnalyzerInterface.
virtual void Start(std::string test_case_name,
TrackIdStreamInfoMap* analyzer_helper) = 0;
// Will be called by the framework at the end of the test. The analyzer
// has to finalize all its stats and it should report them.
virtual void Stop() = 0;
};
} // namespace webrtc_pc_e2e
} // namespace webrtc
#endif // API_TEST_AUDIO_QUALITY_ANALYZER_INTERFACE_H_

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2018 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 "api/test/audioproc_float.h"
#include <utility>
#include "modules/audio_processing/test/audioproc_float_impl.h"
namespace webrtc {
namespace test {
int AudioprocFloat(rtc::scoped_refptr<AudioProcessing> audio_processing,
int argc,
char* argv[]) {
return AudioprocFloatImpl(std::move(audio_processing), argc, argv);
}
int AudioprocFloat(std::unique_ptr<AudioProcessingBuilder> ap_builder,
int argc,
char* argv[]) {
return AudioprocFloatImpl(std::move(ap_builder), argc, argv,
/*input_aecdump=*/"",
/*processed_capture_samples=*/nullptr);
}
int AudioprocFloat(std::unique_ptr<AudioProcessingBuilder> ap_builder,
int argc,
char* argv[],
absl::string_view input_aecdump,
std::vector<float>* processed_capture_samples) {
return AudioprocFloatImpl(std::move(ap_builder), argc, argv, input_aecdump,
processed_capture_samples);
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_AUDIOPROC_FLOAT_H_
#define API_TEST_AUDIOPROC_FLOAT_H_
#include <memory>
#include <vector>
#include "modules/audio_processing/include/audio_processing.h"
namespace webrtc {
namespace test {
// This is an interface for the audio processing simulation utility. This
// utility can be used to simulate the audioprocessing module using a recording
// (either an AEC dump or wav files), and generate the output as a wav file.
// Any audio_processing object specified in the input is used for the
// simulation. The optional `audio_processing` object provides the
// AudioProcessing instance that is used during the simulation. Note that when
// the audio_processing object is specified all functionality that relies on
// using the AudioProcessingBuilder is deactivated, since the AudioProcessing
// object is already created and the builder is not used in the simulation. It
// is needed to pass the command line flags as `argc` and `argv`, so these can
// be interpreted properly by the utility. To see a list of all supported
// command line flags, run the executable with the '--help' flag.
int AudioprocFloat(rtc::scoped_refptr<AudioProcessing> audio_processing,
int argc,
char* argv[]);
// This is an interface for the audio processing simulation utility. This
// utility can be used to simulate the audioprocessing module using a recording
// (either an AEC dump or wav files), and generate the output as a wav file.
// The `ap_builder` object will be used to create the AudioProcessing instance
// that is used during the simulation. The `ap_builder` supports setting of
// injectable components, which will be passed on to the created AudioProcessing
// instance. It is needed to pass the command line flags as `argc` and `argv`,
// so these can be interpreted properly by the utility.
// To get a fully-working audioproc_f utility, all that is needed is to write a
// main function, create an AudioProcessingBuilder, optionally set custom
// processing components on it, and pass the builder together with the command
// line arguments into this function.
// To see a list of all supported command line flags, run the executable with
// the '--help' flag.
int AudioprocFloat(std::unique_ptr<AudioProcessingBuilder> ap_builder,
int argc,
char* argv[]);
// Interface for the audio processing simulation utility, which is similar to
// the one above, but which adds the option of receiving the input as a string
// and returning the output as an array. The first three arguments fulfill the
// same purpose as above. Pass the `input_aecdump` to provide the content of an
// AEC dump file as a string. After the simulation is completed,
// `processed_capture_samples` will contain the the samples processed on the
// capture side.
int AudioprocFloat(std::unique_ptr<AudioProcessingBuilder> ap_builder,
int argc,
char* argv[],
absl::string_view input_aecdump,
std::vector<float>* processed_capture_samples);
} // namespace test
} // namespace webrtc
#endif // API_TEST_AUDIOPROC_FLOAT_H_

View file

@ -0,0 +1,54 @@
/*
* Copyright 2019 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.
*/
// This file verifies that all include files in this directory can be
// compiled without errors or other required includes.
// Note: The following header files are not not tested here, as their
// associated targets are not included in all configurations.
// "api/test/audioproc_float.h"
// "api/test/create_video_quality_test_fixture.h"
// "api/test/neteq_simulator_factory.h"
// "api/test/video_quality_test_fixture.h"
// The following header files are also not tested:
// "api/test/create_simulcast_test_fixture.h"
// "api/test/create_videocodec_test_fixture.h"
// "api/test/neteq_simulator.h"
// "api/test/simulated_network.h"
// "api/test/simulcast_test_fixture.h"
// "api/test/test_dependency_factory.h"
// "api/test/videocodec_test_fixture.h"
// "api/test/videocodec_test_stats.h"
#include "api/test/fake_frame_decryptor.h"
#include "api/test/fake_frame_encryptor.h"
#include "api/test/mock_async_dns_resolver.h"
#include "api/test/mock_audio_mixer.h"
#include "api/test/mock_audio_sink.h"
#include "api/test/mock_data_channel.h"
#include "api/test/mock_dtmf_sender.h"
#include "api/test/mock_frame_decryptor.h"
#include "api/test/mock_frame_encryptor.h"
#include "api/test/mock_media_stream_interface.h"
#include "api/test/mock_peer_connection_factory_interface.h"
#include "api/test/mock_peerconnectioninterface.h"
#include "api/test/mock_rtp_transceiver.h"
#include "api/test/mock_rtpreceiver.h"
#include "api/test/mock_rtpsender.h"
#include "api/test/mock_session_description_interface.h"
#include "api/test/mock_transformable_frame.h"
#include "api/test/mock_transformable_video_frame.h"
#include "api/test/mock_video_bitrate_allocator.h"
#include "api/test/mock_video_bitrate_allocator_factory.h"
#include "api/test/mock_video_decoder.h"
#include "api/test/mock_video_decoder_factory.h"
#include "api/test/mock_video_encoder.h"
#include "api/test/mock_video_encoder_factory.h"
#include "api/test/mock_video_track.h"

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2019 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 "api/test/create_frame_generator.h"
#include <cstdio>
#include <utility>
#include "rtc_base/checks.h"
#include "test/frame_generator.h"
#include "test/testsupport/ivf_video_frame_generator.h"
namespace webrtc {
namespace test {
std::unique_ptr<FrameGeneratorInterface> CreateSquareFrameGenerator(
int width,
int height,
absl::optional<FrameGeneratorInterface::OutputType> type,
absl::optional<int> num_squares) {
return std::make_unique<SquareGenerator>(
width, height, type.value_or(FrameGeneratorInterface::OutputType::kI420),
num_squares.value_or(10));
}
std::unique_ptr<FrameGeneratorInterface> CreateFromYuvFileFrameGenerator(
std::vector<std::string> filenames,
size_t width,
size_t height,
int frame_repeat_count) {
RTC_DCHECK(!filenames.empty());
std::vector<FILE*> files;
for (const std::string& filename : filenames) {
FILE* file = fopen(filename.c_str(), "rb");
RTC_DCHECK(file != nullptr) << "Failed to open: '" << filename << "'\n";
files.push_back(file);
}
return std::make_unique<YuvFileGenerator>(files, width, height,
frame_repeat_count);
}
std::unique_ptr<FrameGeneratorInterface> CreateFromNV12FileFrameGenerator(
std::vector<std::string> filenames,
size_t width,
size_t height,
int frame_repeat_count) {
RTC_DCHECK(!filenames.empty());
std::vector<FILE*> files;
for (const std::string& filename : filenames) {
FILE* file = fopen(filename.c_str(), "rb");
RTC_DCHECK(file != nullptr) << "Failed to open: '" << filename << "'\n";
files.push_back(file);
}
return std::make_unique<NV12FileGenerator>(files, width, height,
frame_repeat_count);
}
std::unique_ptr<FrameGeneratorInterface> CreateFromIvfFileFrameGenerator(
std::string filename) {
return std::make_unique<IvfVideoFrameGenerator>(std::move(filename));
}
std::unique_ptr<FrameGeneratorInterface>
CreateScrollingInputFromYuvFilesFrameGenerator(
Clock* clock,
std::vector<std::string> filenames,
size_t source_width,
size_t source_height,
size_t target_width,
size_t target_height,
int64_t scroll_time_ms,
int64_t pause_time_ms) {
RTC_DCHECK(!filenames.empty());
std::vector<FILE*> files;
for (const std::string& filename : filenames) {
FILE* file = fopen(filename.c_str(), "rb");
RTC_DCHECK(file != nullptr);
files.push_back(file);
}
return std::make_unique<ScrollingImageFrameGenerator>(
clock, files, source_width, source_height, target_width, target_height,
scroll_time_ms, pause_time_ms);
}
std::unique_ptr<FrameGeneratorInterface>
CreateSlideFrameGenerator(int width, int height, int frame_repeat_count) {
return std::make_unique<SlideGenerator>(width, height, frame_repeat_count);
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef API_TEST_CREATE_FRAME_GENERATOR_H_
#define API_TEST_CREATE_FRAME_GENERATOR_H_
#include <memory>
#include <string>
#include <vector>
#include "absl/types/optional.h"
#include "api/test/frame_generator_interface.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
namespace test {
// Creates a frame generator that produces frames with small squares that
// move randomly towards the lower right corner.
// `type` has the default value FrameGeneratorInterface::OutputType::I420.
// `num_squares` has the default value 10.
std::unique_ptr<FrameGeneratorInterface> CreateSquareFrameGenerator(
int width,
int height,
absl::optional<FrameGeneratorInterface::OutputType> type,
absl::optional<int> num_squares);
// Creates a frame generator that repeatedly plays a set of yuv files.
// The frame_repeat_count determines how many times each frame is shown,
// with 1 = show each frame once, etc.
std::unique_ptr<FrameGeneratorInterface> CreateFromYuvFileFrameGenerator(
std::vector<std::string> filenames,
size_t width,
size_t height,
int frame_repeat_count);
// Creates a frame generator that repeatedly plays a set of nv12 files.
// The frame_repeat_count determines how many times each frame is shown,
// with 1 = show each frame once, etc.
std::unique_ptr<FrameGeneratorInterface> CreateFromNV12FileFrameGenerator(
std::vector<std::string> filenames,
size_t width,
size_t height,
int frame_repeat_count = 1);
// Creates a frame generator that repeatedly plays an ivf file.
std::unique_ptr<FrameGeneratorInterface> CreateFromIvfFileFrameGenerator(
std::string filename);
// Creates a frame generator which takes a set of yuv files (wrapping a
// frame generator created by CreateFromYuvFile() above), but outputs frames
// that have been cropped to specified resolution: source_width/source_height
// is the size of the source images, target_width/target_height is the size of
// the cropped output. For each source image read, the cropped viewport will
// be scrolled top to bottom/left to right for scroll_tim_ms milliseconds.
// After that the image will stay in place for pause_time_ms milliseconds,
// and then this will be repeated with the next file from the input set.
std::unique_ptr<FrameGeneratorInterface>
CreateScrollingInputFromYuvFilesFrameGenerator(
Clock* clock,
std::vector<std::string> filenames,
size_t source_width,
size_t source_height,
size_t target_width,
size_t target_height,
int64_t scroll_time_ms,
int64_t pause_time_ms);
// Creates a frame generator that produces randomly generated slides. It fills
// the frames with randomly sized and colored squares.
// `frame_repeat_count` determines how many times each slide is shown.
std::unique_ptr<FrameGeneratorInterface>
CreateSlideFrameGenerator(int width, int height, int frame_repeat_count);
} // namespace test
} // namespace webrtc
#endif // API_TEST_CREATE_FRAME_GENERATOR_H_

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2019 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 "api/test/create_network_emulation_manager.h"
#include <memory>
#include "api/field_trials_view.h"
#include "test/network/network_emulation_manager.h"
namespace webrtc {
std::unique_ptr<NetworkEmulationManager> CreateNetworkEmulationManager(
TimeMode time_mode,
EmulatedNetworkStatsGatheringMode stats_gathering_mode,
const FieldTrialsView* field_trials) {
return std::make_unique<test::NetworkEmulationManagerImpl>(
time_mode, stats_gathering_mode, field_trials);
}
} // namespace webrtc

View file

@ -0,0 +1,30 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef API_TEST_CREATE_NETWORK_EMULATION_MANAGER_H_
#define API_TEST_CREATE_NETWORK_EMULATION_MANAGER_H_
#include <memory>
#include "api/field_trials_view.h"
#include "api/test/network_emulation_manager.h"
namespace webrtc {
// Returns a non-null NetworkEmulationManager instance.
std::unique_ptr<NetworkEmulationManager> CreateNetworkEmulationManager(
TimeMode time_mode = TimeMode::kRealTime,
EmulatedNetworkStatsGatheringMode stats_gathering_mode =
EmulatedNetworkStatsGatheringMode::kDefault,
const FieldTrialsView* field_trials = nullptr);
} // namespace webrtc
#endif // API_TEST_CREATE_NETWORK_EMULATION_MANAGER_H_

View file

@ -0,0 +1,103 @@
/*
* Copyright (c) 2020 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 "api/test/create_peer_connection_quality_test_frame_generator.h"
#include <utility>
#include <vector>
#include "api/test/create_frame_generator.h"
#include "api/test/pclf/media_configuration.h"
#include "rtc_base/checks.h"
#include "test/testsupport/file_utils.h"
namespace webrtc {
namespace webrtc_pc_e2e {
void ValidateScreenShareConfig(const VideoConfig& video_config,
const ScreenShareConfig& screen_share_config) {
if (screen_share_config.slides_yuv_file_names.empty()) {
if (screen_share_config.scrolling_params) {
// If we have scrolling params, then its `source_width` and `source_heigh`
// will be used as width and height of video input, so we have to validate
// it against width and height of default input.
RTC_CHECK_EQ(screen_share_config.scrolling_params->source_width,
kDefaultSlidesWidth);
RTC_CHECK_EQ(screen_share_config.scrolling_params->source_height,
kDefaultSlidesHeight);
} else {
RTC_CHECK_EQ(video_config.width, kDefaultSlidesWidth);
RTC_CHECK_EQ(video_config.height, kDefaultSlidesHeight);
}
}
if (screen_share_config.scrolling_params) {
RTC_CHECK_LE(screen_share_config.scrolling_params->duration,
screen_share_config.slide_change_interval);
RTC_CHECK_GE(screen_share_config.scrolling_params->source_width,
video_config.width);
RTC_CHECK_GE(screen_share_config.scrolling_params->source_height,
video_config.height);
}
}
std::unique_ptr<test::FrameGeneratorInterface> CreateSquareFrameGenerator(
const VideoConfig& video_config,
absl::optional<test::FrameGeneratorInterface::OutputType> type) {
return test::CreateSquareFrameGenerator(
video_config.width, video_config.height, std::move(type), absl::nullopt);
}
std::unique_ptr<test::FrameGeneratorInterface> CreateFromYuvFileFrameGenerator(
const VideoConfig& video_config,
std::string filename) {
return test::CreateFromYuvFileFrameGenerator(
{std::move(filename)}, video_config.width, video_config.height,
/*frame_repeat_count=*/1);
}
std::unique_ptr<test::FrameGeneratorInterface> CreateScreenShareFrameGenerator(
const VideoConfig& video_config,
const ScreenShareConfig& screen_share_config) {
ValidateScreenShareConfig(video_config, screen_share_config);
if (screen_share_config.generate_slides) {
return test::CreateSlideFrameGenerator(
video_config.width, video_config.height,
screen_share_config.slide_change_interval.seconds() * video_config.fps);
}
std::vector<std::string> slides = screen_share_config.slides_yuv_file_names;
if (slides.empty()) {
// If slides is empty we need to add default slides as source. In such case
// video width and height is validated to be equal to kDefaultSlidesWidth
// and kDefaultSlidesHeight.
slides.push_back(test::ResourcePath("web_screenshot_1850_1110", "yuv"));
slides.push_back(test::ResourcePath("presentation_1850_1110", "yuv"));
slides.push_back(test::ResourcePath("photo_1850_1110", "yuv"));
slides.push_back(test::ResourcePath("difficult_photo_1850_1110", "yuv"));
}
if (!screen_share_config.scrolling_params) {
// Cycle image every slide_change_interval seconds.
return test::CreateFromYuvFileFrameGenerator(
slides, video_config.width, video_config.height,
screen_share_config.slide_change_interval.seconds() * video_config.fps);
}
TimeDelta pause_duration = screen_share_config.slide_change_interval -
screen_share_config.scrolling_params->duration;
RTC_DCHECK(pause_duration >= TimeDelta::Zero());
return test::CreateScrollingInputFromYuvFilesFrameGenerator(
Clock::GetRealTimeClock(), slides,
screen_share_config.scrolling_params->source_width,
screen_share_config.scrolling_params->source_height, video_config.width,
video_config.height, screen_share_config.scrolling_params->duration.ms(),
pause_duration.ms());
}
} // namespace webrtc_pc_e2e
} // namespace webrtc

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef API_TEST_CREATE_PEER_CONNECTION_QUALITY_TEST_FRAME_GENERATOR_H_
#define API_TEST_CREATE_PEER_CONNECTION_QUALITY_TEST_FRAME_GENERATOR_H_
#include <memory>
#include <string>
#include "absl/types/optional.h"
#include "api/test/frame_generator_interface.h"
#include "api/test/pclf/media_configuration.h"
namespace webrtc {
namespace webrtc_pc_e2e {
// Creates a frame generator that produces frames with small squares that move
// randomly towards the lower right corner. `type` has the default value
// FrameGeneratorInterface::OutputType::I420. video_config specifies frame
// weight and height.
std::unique_ptr<test::FrameGeneratorInterface> CreateSquareFrameGenerator(
const VideoConfig& video_config,
absl::optional<test::FrameGeneratorInterface::OutputType> type);
// Creates a frame generator that plays frames from the yuv file.
std::unique_ptr<test::FrameGeneratorInterface> CreateFromYuvFileFrameGenerator(
const VideoConfig& video_config,
std::string filename);
// Creates a proper frame generator for testing screen sharing.
std::unique_ptr<test::FrameGeneratorInterface> CreateScreenShareFrameGenerator(
const VideoConfig& video_config,
const ScreenShareConfig& screen_share_config);
} // namespace webrtc_pc_e2e
} // namespace webrtc
#endif // API_TEST_CREATE_PEER_CONNECTION_QUALITY_TEST_FRAME_GENERATOR_H_

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019 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 "api/test/create_peerconnection_quality_test_fixture.h"
#include <memory>
#include <utility>
#include "api/test/metrics/global_metrics_logger_and_exporter.h"
#include "api/test/time_controller.h"
#include "test/pc/e2e/peer_connection_quality_test.h"
namespace webrtc {
namespace webrtc_pc_e2e {
std::unique_ptr<PeerConnectionE2EQualityTestFixture>
CreatePeerConnectionE2EQualityTestFixture(
std::string test_case_name,
TimeController& time_controller,
std::unique_ptr<AudioQualityAnalyzerInterface> audio_quality_analyzer,
std::unique_ptr<VideoQualityAnalyzerInterface> video_quality_analyzer) {
return std::make_unique<PeerConnectionE2EQualityTest>(
std::move(test_case_name), time_controller,
std::move(audio_quality_analyzer), std::move(video_quality_analyzer),
test::GetGlobalMetricsLogger());
}
} // namespace webrtc_pc_e2e
} // namespace webrtc

View file

@ -0,0 +1,43 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef API_TEST_CREATE_PEERCONNECTION_QUALITY_TEST_FIXTURE_H_
#define API_TEST_CREATE_PEERCONNECTION_QUALITY_TEST_FIXTURE_H_
#include <memory>
#include <string>
#include "api/test/audio_quality_analyzer_interface.h"
#include "api/test/peerconnection_quality_test_fixture.h"
#include "api/test/time_controller.h"
#include "api/test/video_quality_analyzer_interface.h"
namespace webrtc {
namespace webrtc_pc_e2e {
// API is in development. Can be changed/removed without notice.
// Create test fixture to establish test call between Alice and Bob.
// During the test Alice will be caller and Bob will answer the call.
// `test_case_name` is a name of test case, that will be used for all metrics
// reporting.
// `time_controller` is used to manage all rtc::Thread's and TaskQueue
// instances. Instance of `time_controller` have to outlive created fixture.
// Returns a non-null PeerConnectionE2EQualityTestFixture instance.
std::unique_ptr<PeerConnectionE2EQualityTestFixture>
CreatePeerConnectionE2EQualityTestFixture(
std::string test_case_name,
TimeController& time_controller,
std::unique_ptr<AudioQualityAnalyzerInterface> audio_quality_analyzer,
std::unique_ptr<VideoQualityAnalyzerInterface> video_quality_analyzer);
} // namespace webrtc_pc_e2e
} // namespace webrtc
#endif // API_TEST_CREATE_PEERCONNECTION_QUALITY_TEST_FIXTURE_H_

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2018 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 "api/test/create_simulcast_test_fixture.h"
#include <memory>
#include <utility>
#include "api/test/simulcast_test_fixture.h"
#include "modules/video_coding/utility/simulcast_test_fixture_impl.h"
namespace webrtc {
namespace test {
std::unique_ptr<SimulcastTestFixture> CreateSimulcastTestFixture(
std::unique_ptr<VideoEncoderFactory> encoder_factory,
std::unique_ptr<VideoDecoderFactory> decoder_factory,
SdpVideoFormat video_format) {
return std::make_unique<SimulcastTestFixtureImpl>(
std::move(encoder_factory), std::move(decoder_factory), video_format);
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_CREATE_SIMULCAST_TEST_FIXTURE_H_
#define API_TEST_CREATE_SIMULCAST_TEST_FIXTURE_H_
#include <memory>
#include "api/test/simulcast_test_fixture.h"
#include "api/video_codecs/sdp_video_format.h"
#include "api/video_codecs/video_decoder_factory.h"
#include "api/video_codecs/video_encoder_factory.h"
namespace webrtc {
namespace test {
std::unique_ptr<SimulcastTestFixture> CreateSimulcastTestFixture(
std::unique_ptr<VideoEncoderFactory> encoder_factory,
std::unique_ptr<VideoDecoderFactory> decoder_factory,
SdpVideoFormat video_format);
} // namespace test
} // namespace webrtc
#endif // API_TEST_CREATE_SIMULCAST_TEST_FIXTURE_H_

View file

@ -0,0 +1,77 @@
/*
* Copyright 2019 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 "api/test/create_time_controller.h"
#include <memory>
#include <utility>
#include "absl/base/nullability.h"
#include "api/enable_media_with_defaults.h"
#include "api/environment/environment.h"
#include "api/environment/environment_factory.h"
#include "api/peer_connection_interface.h"
#include "call/call.h"
#include "call/call_config.h"
#include "pc/media_factory.h"
#include "rtc_base/checks.h"
#include "system_wrappers/include/clock.h"
#include "test/time_controller/external_time_controller.h"
#include "test/time_controller/simulated_time_controller.h"
namespace webrtc {
std::unique_ptr<TimeController> CreateTimeController(
ControlledAlarmClock* alarm) {
return std::make_unique<ExternalTimeController>(alarm);
}
std::unique_ptr<TimeController> CreateSimulatedTimeController() {
return std::make_unique<GlobalSimulatedTimeController>(
Timestamp::Seconds(10000));
}
void EnableMediaWithDefaultsAndTimeController(
TimeController& time_controller,
PeerConnectionFactoryDependencies& deps) {
class TimeControllerBasedFactory : public MediaFactory {
public:
TimeControllerBasedFactory(
absl::Nonnull<Clock*> clock,
absl::Nonnull<std::unique_ptr<MediaFactory>> media_factory)
: clock_(clock), media_factory_(std::move(media_factory)) {}
std::unique_ptr<Call> CreateCall(const CallConfig& config) override {
EnvironmentFactory env_factory(config.env);
env_factory.Set(clock_);
CallConfig config_with_custom_clock = config;
config_with_custom_clock.env = env_factory.Create();
return media_factory_->CreateCall(config_with_custom_clock);
}
std::unique_ptr<cricket::MediaEngineInterface> CreateMediaEngine(
const Environment& env,
PeerConnectionFactoryDependencies& dependencies) override {
return media_factory_->CreateMediaEngine(env, dependencies);
}
private:
absl::Nonnull<Clock*> clock_;
absl::Nonnull<std::unique_ptr<MediaFactory>> media_factory_;
};
EnableMediaWithDefaults(deps);
RTC_CHECK(deps.media_factory);
deps.media_factory = std::make_unique<TimeControllerBasedFactory>(
time_controller.GetClock(), std::move(deps.media_factory));
}
} // namespace webrtc

View file

@ -0,0 +1,36 @@
/*
* Copyright 2019 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.
*/
#ifndef API_TEST_CREATE_TIME_CONTROLLER_H_
#define API_TEST_CREATE_TIME_CONTROLLER_H_
#include <memory>
#include "api/peer_connection_interface.h"
#include "api/test/time_controller.h"
namespace webrtc {
// Creates a time coltroller that wraps `alarm`.
std::unique_ptr<TimeController> CreateTimeController(
ControlledAlarmClock* alarm);
// Creates a time controller that runs in simulated time.
std::unique_ptr<TimeController> CreateSimulatedTimeController();
// Adjusts media `deps` to use clock `time_controller` provides, fills media
// related dependencies, and enables media support for a PeerConnectionFactory
// created from `deps`.
void EnableMediaWithDefaultsAndTimeController(
TimeController& time_controller,
PeerConnectionFactoryDependencies& deps);
} // namespace webrtc
#endif // API_TEST_CREATE_TIME_CONTROLLER_H_

View file

@ -0,0 +1,76 @@
/*
* Copyright 2019 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 "api/test/create_time_controller.h"
#include "api/test/time_controller.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace {
class FakeAlarm : public ControlledAlarmClock {
public:
explicit FakeAlarm(Timestamp start_time);
Clock* GetClock() override;
bool ScheduleAlarmAt(Timestamp deadline) override;
void SetCallback(std::function<void()> callback) override;
void Sleep(TimeDelta duration) override;
private:
SimulatedClock clock_;
Timestamp deadline_;
std::function<void()> callback_;
};
FakeAlarm::FakeAlarm(Timestamp start_time)
: clock_(start_time),
deadline_(Timestamp::PlusInfinity()),
callback_([] {}) {}
Clock* FakeAlarm::GetClock() {
return &clock_;
}
bool FakeAlarm::ScheduleAlarmAt(Timestamp deadline) {
if (deadline < deadline_) {
deadline_ = deadline;
return true;
}
return false;
}
void FakeAlarm::SetCallback(std::function<void()> callback) {
callback_ = callback;
}
void FakeAlarm::Sleep(TimeDelta duration) {
Timestamp end_time = clock_.CurrentTime() + duration;
while (deadline_ <= end_time) {
clock_.AdvanceTime(deadline_ - clock_.CurrentTime());
deadline_ = Timestamp::PlusInfinity();
callback_();
}
clock_.AdvanceTime(end_time - clock_.CurrentTime());
}
TEST(CreateTimeControllerTest, CreatesNonNullController) {
FakeAlarm alarm(Timestamp::Millis(100));
EXPECT_NE(CreateTimeController(&alarm), nullptr);
}
} // namespace
} // namespace webrtc

View file

@ -0,0 +1,40 @@
/*
* Copyright (c) 2018 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 "api/test/create_video_quality_test_fixture.h"
#include <memory>
#include <utility>
#include "video/video_quality_test.h"
namespace webrtc {
std::unique_ptr<VideoQualityTestFixtureInterface>
CreateVideoQualityTestFixture() {
// By default, we don't override the FEC module, so pass an empty factory.
return std::make_unique<VideoQualityTest>(nullptr);
}
std::unique_ptr<VideoQualityTestFixtureInterface> CreateVideoQualityTestFixture(
std::unique_ptr<FecControllerFactoryInterface> fec_controller_factory) {
auto components =
std::make_unique<VideoQualityTestFixtureInterface::InjectionComponents>();
components->fec_controller_factory = std::move(fec_controller_factory);
return std::make_unique<VideoQualityTest>(std::move(components));
}
std::unique_ptr<VideoQualityTestFixtureInterface> CreateVideoQualityTestFixture(
std::unique_ptr<VideoQualityTestFixtureInterface::InjectionComponents>
components) {
return std::make_unique<VideoQualityTest>(std::move(components));
}
} // namespace webrtc

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_CREATE_VIDEO_QUALITY_TEST_FIXTURE_H_
#define API_TEST_CREATE_VIDEO_QUALITY_TEST_FIXTURE_H_
#include <memory>
#include "api/fec_controller.h"
#include "api/test/video_quality_test_fixture.h"
namespace webrtc {
std::unique_ptr<VideoQualityTestFixtureInterface>
CreateVideoQualityTestFixture();
std::unique_ptr<VideoQualityTestFixtureInterface> CreateVideoQualityTestFixture(
std::unique_ptr<FecControllerFactoryInterface> fec_controller_factory);
std::unique_ptr<VideoQualityTestFixtureInterface> CreateVideoQualityTestFixture(
std::unique_ptr<VideoQualityTestFixtureInterface::InjectionComponents>
components);
} // namespace webrtc
#endif // API_TEST_CREATE_VIDEO_QUALITY_TEST_FIXTURE_H_

View file

@ -0,0 +1,38 @@
/*
* Copyright (c) 2018 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 "api/test/create_videocodec_test_fixture.h"
#include <memory>
#include <utility>
#include "api/test/videocodec_test_fixture.h"
#include "modules/video_coding/codecs/test/videocodec_test_fixture_impl.h"
namespace webrtc {
namespace test {
using Config = VideoCodecTestFixture::Config;
std::unique_ptr<VideoCodecTestFixture> CreateVideoCodecTestFixture(
const Config& config) {
return std::make_unique<VideoCodecTestFixtureImpl>(config);
}
std::unique_ptr<VideoCodecTestFixture> CreateVideoCodecTestFixture(
const Config& config,
std::unique_ptr<VideoDecoderFactory> decoder_factory,
std::unique_ptr<VideoEncoderFactory> encoder_factory) {
return std::make_unique<VideoCodecTestFixtureImpl>(
config, std::move(decoder_factory), std::move(encoder_factory));
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_CREATE_VIDEOCODEC_TEST_FIXTURE_H_
#define API_TEST_CREATE_VIDEOCODEC_TEST_FIXTURE_H_
#include <memory>
#include "api/test/videocodec_test_fixture.h"
#include "api/video_codecs/video_decoder_factory.h"
#include "api/video_codecs/video_encoder_factory.h"
namespace webrtc {
namespace test {
std::unique_ptr<VideoCodecTestFixture> CreateVideoCodecTestFixture(
const VideoCodecTestFixture::Config& config);
std::unique_ptr<VideoCodecTestFixture> CreateVideoCodecTestFixture(
const VideoCodecTestFixture::Config& config,
std::unique_ptr<VideoDecoderFactory> decoder_factory,
std::unique_ptr<VideoEncoderFactory> encoder_factory);
} // namespace test
} // namespace webrtc
#endif // API_TEST_CREATE_VIDEOCODEC_TEST_FIXTURE_H_

View file

@ -0,0 +1,71 @@
/*
* Copyright 2018 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 "api/test/fake_frame_decryptor.h"
#include <vector>
#include "rtc_base/checks.h"
namespace webrtc {
FakeFrameDecryptor::FakeFrameDecryptor(uint8_t fake_key,
uint8_t expected_postfix_byte)
: fake_key_(fake_key), expected_postfix_byte_(expected_postfix_byte) {}
FakeFrameDecryptor::Result FakeFrameDecryptor::Decrypt(
cricket::MediaType media_type,
const std::vector<uint32_t>& csrcs,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> encrypted_frame,
rtc::ArrayView<uint8_t> frame) {
if (fail_decryption_) {
return Result(Status::kFailedToDecrypt, 0);
}
RTC_CHECK_EQ(frame.size() + 1, encrypted_frame.size());
for (size_t i = 0; i < frame.size(); i++) {
frame[i] = encrypted_frame[i] ^ fake_key_;
}
if (encrypted_frame[frame.size()] != expected_postfix_byte_) {
return Result(Status::kFailedToDecrypt, 0);
}
return Result(Status::kOk, frame.size());
}
size_t FakeFrameDecryptor::GetMaxPlaintextByteSize(
cricket::MediaType media_type,
size_t encrypted_frame_size) {
return encrypted_frame_size - 1;
}
void FakeFrameDecryptor::SetFakeKey(uint8_t fake_key) {
fake_key_ = fake_key;
}
uint8_t FakeFrameDecryptor::GetFakeKey() const {
return fake_key_;
}
void FakeFrameDecryptor::SetExpectedPostfixByte(uint8_t expected_postfix_byte) {
expected_postfix_byte_ = expected_postfix_byte;
}
uint8_t FakeFrameDecryptor::GetExpectedPostfixByte() const {
return expected_postfix_byte_;
}
void FakeFrameDecryptor::SetFailDecryption(bool fail_decryption) {
fail_decryption_ = fail_decryption;
}
} // namespace webrtc

View file

@ -0,0 +1,70 @@
/*
* Copyright 2018 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.
*/
#ifndef API_TEST_FAKE_FRAME_DECRYPTOR_H_
#define API_TEST_FAKE_FRAME_DECRYPTOR_H_
#include <stddef.h>
#include <stdint.h>
#include <vector>
#include "api/array_view.h"
#include "api/crypto/frame_decryptor_interface.h"
#include "api/media_types.h"
namespace webrtc {
// The FakeFrameDecryptor is a TEST ONLY fake implementation of the
// FrameDecryptorInterface. It is constructed with a simple single digit key and
// a fixed postfix byte. This is just to validate that the core code works
// as expected.
class FakeFrameDecryptor : public FrameDecryptorInterface {
public:
// Provide a key (0,255) and some postfix byte (0,255) this should match the
// byte you expect from the FakeFrameEncryptor.
explicit FakeFrameDecryptor(uint8_t fake_key = 0xAA,
uint8_t expected_postfix_byte = 255);
// Fake decryption that just xors the payload with the 1 byte key and checks
// the postfix byte. This will always fail if fail_decryption_ is set to true.
Result Decrypt(cricket::MediaType media_type,
const std::vector<uint32_t>& csrcs,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> encrypted_frame,
rtc::ArrayView<uint8_t> frame) override;
// Always returns 1 less than the size of the encrypted frame.
size_t GetMaxPlaintextByteSize(cricket::MediaType media_type,
size_t encrypted_frame_size) override;
// Sets the fake key to use for encryption.
void SetFakeKey(uint8_t fake_key);
// Returns the fake key used for encryption.
uint8_t GetFakeKey() const;
// Set the Postfix byte that is expected in the encrypted payload.
void SetExpectedPostfixByte(uint8_t expected_postfix_byte);
// Returns the postfix byte that will be checked for in the encrypted payload.
uint8_t GetExpectedPostfixByte() const;
// If set to true will force all encryption to fail.
void SetFailDecryption(bool fail_decryption);
// Simple error codes for tests to validate against.
enum class FakeDecryptStatus : int {
OK = 0,
FORCED_FAILURE = 1,
INVALID_POSTFIX = 2
};
private:
uint8_t fake_key_ = 0;
uint8_t expected_postfix_byte_ = 0;
bool fail_decryption_ = false;
};
} // namespace webrtc
#endif // API_TEST_FAKE_FRAME_DECRYPTOR_H_

View file

@ -0,0 +1,66 @@
/*
* Copyright 2018 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 "api/test/fake_frame_encryptor.h"
#include "rtc_base/checks.h"
namespace webrtc {
FakeFrameEncryptor::FakeFrameEncryptor(uint8_t fake_key, uint8_t postfix_byte)
: fake_key_(fake_key), postfix_byte_(postfix_byte) {}
// FrameEncryptorInterface implementation
int FakeFrameEncryptor::Encrypt(cricket::MediaType media_type,
uint32_t ssrc,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> frame,
rtc::ArrayView<uint8_t> encrypted_frame,
size_t* bytes_written) {
if (fail_encryption_) {
return static_cast<int>(FakeEncryptionStatus::FORCED_FAILURE);
}
RTC_CHECK_EQ(frame.size() + 1, encrypted_frame.size());
for (size_t i = 0; i < frame.size(); i++) {
encrypted_frame[i] = frame[i] ^ fake_key_;
}
encrypted_frame[frame.size()] = postfix_byte_;
*bytes_written = encrypted_frame.size();
return static_cast<int>(FakeEncryptionStatus::OK);
}
size_t FakeFrameEncryptor::GetMaxCiphertextByteSize(
cricket::MediaType media_type,
size_t frame_size) {
return frame_size + 1;
}
void FakeFrameEncryptor::SetFakeKey(uint8_t fake_key) {
fake_key_ = fake_key;
}
uint8_t FakeFrameEncryptor::GetFakeKey() const {
return fake_key_;
}
void FakeFrameEncryptor::SetPostfixByte(uint8_t postfix_byte) {
postfix_byte_ = postfix_byte;
}
uint8_t FakeFrameEncryptor::GetPostfixByte() const {
return postfix_byte_;
}
void FakeFrameEncryptor::SetFailEncryption(bool fail_encryption) {
fail_encryption_ = fail_encryption;
}
} // namespace webrtc

View file

@ -0,0 +1,69 @@
/*
* Copyright 2018 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.
*/
#ifndef API_TEST_FAKE_FRAME_ENCRYPTOR_H_
#define API_TEST_FAKE_FRAME_ENCRYPTOR_H_
#include <stddef.h>
#include <stdint.h>
#include "api/array_view.h"
#include "api/crypto/frame_encryptor_interface.h"
#include "api/media_types.h"
#include "rtc_base/ref_counted_object.h"
namespace webrtc {
// The FakeFrameEncryptor is a TEST ONLY fake implementation of the
// FrameEncryptorInterface. It is constructed with a simple single digit key and
// a fixed postfix byte. This is just to validate that the core code works
// as expected.
class FakeFrameEncryptor
: public rtc::RefCountedObject<FrameEncryptorInterface> {
public:
// Provide a key (0,255) and some postfix byte (0,255).
explicit FakeFrameEncryptor(uint8_t fake_key = 0xAA,
uint8_t postfix_byte = 255);
// Simply xors each payload with the provided fake key and adds the postfix
// bit to the end. This will always fail if fail_encryption_ is set to true.
int Encrypt(cricket::MediaType media_type,
uint32_t ssrc,
rtc::ArrayView<const uint8_t> additional_data,
rtc::ArrayView<const uint8_t> frame,
rtc::ArrayView<uint8_t> encrypted_frame,
size_t* bytes_written) override;
// Always returns 1 more than the size of the frame.
size_t GetMaxCiphertextByteSize(cricket::MediaType media_type,
size_t frame_size) override;
// Sets the fake key to use during encryption.
void SetFakeKey(uint8_t fake_key);
// Returns the fake key used during encryption.
uint8_t GetFakeKey() const;
// Set the postfix byte to use.
void SetPostfixByte(uint8_t expected_postfix_byte);
// Return a postfix byte added to each outgoing payload.
uint8_t GetPostfixByte() const;
// Force all encryptions to fail.
void SetFailEncryption(bool fail_encryption);
enum class FakeEncryptionStatus : int {
OK = 0,
FORCED_FAILURE = 1,
};
private:
uint8_t fake_key_ = 0;
uint8_t postfix_byte_ = 0;
bool fail_encryption_ = false;
};
} // namespace webrtc
#endif // API_TEST_FAKE_FRAME_ENCRYPTOR_H_

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2020 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 "api/test/frame_generator_interface.h"
namespace webrtc {
namespace test {
// static
const char* FrameGeneratorInterface::OutputTypeToString(
FrameGeneratorInterface::OutputType type) {
switch (type) {
case OutputType::kI420:
return "I420";
case OutputType::kI420A:
return "I420A";
case OutputType::kI010:
return "I010";
case OutputType::kNV12:
return "NV12";
default:
RTC_DCHECK_NOTREACHED();
}
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,62 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef API_TEST_FRAME_GENERATOR_INTERFACE_H_
#define API_TEST_FRAME_GENERATOR_INTERFACE_H_
#include <utility>
#include "absl/types/optional.h"
#include "api/scoped_refptr.h"
#include "api/video/video_frame.h"
#include "api/video/video_frame_buffer.h"
namespace webrtc {
namespace test {
class FrameGeneratorInterface {
public:
struct Resolution {
size_t width;
size_t height;
};
struct VideoFrameData {
VideoFrameData(rtc::scoped_refptr<VideoFrameBuffer> buffer,
absl::optional<VideoFrame::UpdateRect> update_rect)
: buffer(std::move(buffer)), update_rect(update_rect) {}
rtc::scoped_refptr<VideoFrameBuffer> buffer;
absl::optional<VideoFrame::UpdateRect> update_rect;
};
enum class OutputType { kI420, kI420A, kI010, kNV12 };
static const char* OutputTypeToString(OutputType type);
virtual ~FrameGeneratorInterface() = default;
// Returns VideoFrameBuffer and area where most of update was done to set them
// on the VideoFrame object.
virtual VideoFrameData NextFrame() = 0;
// Change the capture resolution.
virtual void ChangeResolution(size_t width, size_t height) = 0;
virtual Resolution GetResolution() const = 0;
// Returns the frames per second this generator is supposed to provide
// according to its data source. Not all frame generators know the frames per
// second of the data source, in such case this method returns absl::nullopt.
virtual absl::optional<int> fps() const = 0;
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_FRAME_GENERATOR_INTERFACE_H_

View file

@ -0,0 +1,281 @@
# Copyright (c) 2022 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.
import("../../../webrtc.gni")
if (rtc_enable_protobuf) {
import("//third_party/protobuf/proto_library.gni")
}
group("metrics") {
deps = [
":global_metrics_logger_and_exporter",
":metric",
":metrics_accumulator",
":metrics_exporter",
":metrics_logger",
":stdout_metrics_exporter",
]
}
if (rtc_include_tests) {
group("metrics_unittests") {
testonly = true
deps = [
":global_metrics_logger_and_exporter_test",
":metrics_accumulator_test",
":metrics_logger_test",
":print_result_proxy_metrics_exporter_test",
":stdout_metrics_exporter_test",
]
if (rtc_enable_protobuf) {
deps += [
":chrome_perf_dashboard_metrics_exporter_test",
":metrics_set_proto_file_exporter_test",
]
}
}
}
rtc_library("metric") {
visibility = [ "*" ]
sources = [
"metric.cc",
"metric.h",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
deps = [ "../../../api/units:timestamp" ]
}
rtc_library("metrics_logger") {
visibility = [ "*" ]
sources = [
"metrics_logger.cc",
"metrics_logger.h",
]
deps = [
":metric",
":metrics_accumulator",
"../..:array_view",
"../../../rtc_base/synchronization:mutex",
"../../../system_wrappers",
"../../numerics",
]
absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
}
rtc_library("metrics_accumulator") {
visibility = [ "*" ]
sources = [
"metrics_accumulator.cc",
"metrics_accumulator.h",
]
deps = [
":metric",
"../../../rtc_base:macromagic",
"../../../rtc_base/synchronization:mutex",
"../../numerics",
"../../units:timestamp",
]
absl_deps = [ "//third_party/abseil-cpp/absl/strings" ]
}
rtc_library("metrics_exporter") {
visibility = [ "*" ]
sources = [ "metrics_exporter.h" ]
deps = [
":metric",
"../..:array_view",
]
}
rtc_library("stdout_metrics_exporter") {
visibility = [ "*" ]
sources = [
"stdout_metrics_exporter.cc",
"stdout_metrics_exporter.h",
]
deps = [
":metric",
":metrics_exporter",
"../..:array_view",
"../../../rtc_base:stringutils",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
rtc_library("chrome_perf_dashboard_metrics_exporter") {
visibility = [ "*" ]
testonly = true
sources = [
"chrome_perf_dashboard_metrics_exporter.cc",
"chrome_perf_dashboard_metrics_exporter.h",
]
deps = [
":metric",
":metrics_exporter",
"../../../api:array_view",
"../../../test:fileutils",
"../../../test:perf_test",
]
absl_deps = [
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/strings",
]
}
if (rtc_enable_protobuf) {
proto_library("metric_proto") {
visibility = [ "*" ]
sources = [ "proto/metric.proto" ]
proto_out_dir = "api/test/metrics/proto"
cc_generator_options = "lite"
}
}
rtc_library("metrics_set_proto_file_exporter") {
visibility = [ "*" ]
testonly = true
sources = [
"metrics_set_proto_file_exporter.cc",
"metrics_set_proto_file_exporter.h",
]
deps = [
":metric",
":metrics_exporter",
"../..:array_view",
"../../../rtc_base:logging",
"../../../test:fileutils",
]
if (rtc_enable_protobuf) {
deps += [ ":metric_proto" ]
}
}
rtc_library("print_result_proxy_metrics_exporter") {
visibility = [ "*" ]
testonly = true
sources = [
"print_result_proxy_metrics_exporter.cc",
"print_result_proxy_metrics_exporter.h",
]
deps = [
":metric",
":metrics_exporter",
"../..:array_view",
"../../../test:perf_test",
]
}
rtc_library("global_metrics_logger_and_exporter") {
visibility = [ "*" ]
sources = [
"global_metrics_logger_and_exporter.cc",
"global_metrics_logger_and_exporter.h",
]
deps = [
":metrics_exporter",
":metrics_logger",
"../../../rtc_base:checks",
"../../../system_wrappers",
]
}
if (rtc_include_tests) {
rtc_library("metrics_logger_test") {
testonly = true
sources = [ "metrics_logger_test.cc" ]
deps = [
":metric",
":metrics_logger",
"../../../system_wrappers",
"../../../test:test_support",
"../../numerics",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
rtc_library("metrics_accumulator_test") {
testonly = true
sources = [ "metrics_accumulator_test.cc" ]
deps = [
":metric",
":metrics_accumulator",
"../../../test:test_support",
"../../units:timestamp",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
rtc_library("stdout_metrics_exporter_test") {
testonly = true
sources = [ "stdout_metrics_exporter_test.cc" ]
deps = [
":metric",
":stdout_metrics_exporter",
"../../../test:test_support",
"../../units:timestamp",
]
}
rtc_library("print_result_proxy_metrics_exporter_test") {
testonly = true
sources = [ "print_result_proxy_metrics_exporter_test.cc" ]
deps = [
":metric",
":print_result_proxy_metrics_exporter",
"../../../test:test_support",
"../../units:timestamp",
]
}
rtc_library("global_metrics_logger_and_exporter_test") {
testonly = true
sources = [ "global_metrics_logger_and_exporter_test.cc" ]
deps = [
":global_metrics_logger_and_exporter",
":metric",
":metrics_exporter",
":metrics_logger",
"../../../system_wrappers",
"../../../test:test_support",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
if (rtc_enable_protobuf) {
rtc_library("metrics_set_proto_file_exporter_test") {
testonly = true
sources = [ "metrics_set_proto_file_exporter_test.cc" ]
deps = [
":metric",
":metric_proto",
":metrics_set_proto_file_exporter",
"../../../rtc_base:protobuf_utils",
"../../../test:fileutils",
"../../../test:test_support",
"../../units:timestamp",
]
}
rtc_library("chrome_perf_dashboard_metrics_exporter_test") {
testonly = true
sources = [ "chrome_perf_dashboard_metrics_exporter_test.cc" ]
deps = [
":chrome_perf_dashboard_metrics_exporter",
":metric",
"../../../api/units:timestamp",
"../../../test:fileutils",
"../../../test:test_support",
"//third_party/catapult/tracing/tracing:histogram",
]
}
}
}

View file

@ -0,0 +1,14 @@
specific_include_rules = {
"metrics_logger_and_exporter\.h": [
"+rtc_base/synchronization/mutex.h",
"+system_wrappers/include/clock.h",
],
"metrics_logger\.h": [
"+rtc_base/synchronization/mutex.h",
"+system_wrappers/include/clock.h",
],
"metrics_accumulator\.h": [
"+rtc_base/synchronization/mutex.h",
"+rtc_base/thread_annotations.h",
],
}

View file

@ -0,0 +1,146 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/chrome_perf_dashboard_metrics_exporter.h"
#include <stdio.h>
#include <memory>
#include <string>
#include <vector>
#include "absl/memory/memory.h"
#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "api/test/metrics/metric.h"
#include "test/testsupport/file_utils.h"
#include "test/testsupport/perf_test_histogram_writer.h"
#include "test/testsupport/perf_test_result_writer.h"
namespace webrtc {
namespace test {
namespace {
std::string ToChromePerfDashboardUnit(Unit unit) {
switch (unit) {
case Unit::kMilliseconds:
return "msBestFitFormat";
case Unit::kPercent:
return "n%";
case Unit::kBytes:
return "sizeInBytes";
case Unit::kKilobitsPerSecond:
// Chrome Perf Dashboard doesn't have kpbs units, so we change the unit
// and value accordingly.
return "bytesPerSecond";
case Unit::kHertz:
return "Hz";
case Unit::kUnitless:
return "unitless";
case Unit::kCount:
return "count";
}
}
double ToChromePerfDashboardValue(double value, Unit unit) {
switch (unit) {
case Unit::kKilobitsPerSecond:
// Chrome Perf Dashboard doesn't have kpbs units, so we change the unit
// and value accordingly.
return value * 1000 / 8;
default:
return value;
}
}
ImproveDirection ToChromePerfDashboardImproveDirection(
ImprovementDirection direction) {
switch (direction) {
case ImprovementDirection::kBiggerIsBetter:
return ImproveDirection::kBiggerIsBetter;
case ImprovementDirection::kNeitherIsBetter:
return ImproveDirection::kNone;
case ImprovementDirection::kSmallerIsBetter:
return ImproveDirection::kSmallerIsBetter;
}
}
bool WriteMetricsToFile(const std::string& path, const std::string& data) {
CreateDir(DirName(path));
FILE* output = fopen(path.c_str(), "wb");
if (output == NULL) {
printf("Failed to write to %s.\n", path.c_str());
return false;
}
size_t written = fwrite(data.c_str(), sizeof(char), data.size(), output);
fclose(output);
if (written != data.size()) {
size_t expected = data.size();
printf("Wrote %zu, tried to write %zu\n", written, expected);
return false;
}
return true;
}
bool IsEmpty(const Metric::Stats& stats) {
return !stats.mean.has_value() && !stats.stddev.has_value() &&
!stats.min.has_value() && !stats.max.has_value();
}
} // namespace
ChromePerfDashboardMetricsExporter::ChromePerfDashboardMetricsExporter(
absl::string_view export_file_path)
: export_file_path_(export_file_path) {}
bool ChromePerfDashboardMetricsExporter::Export(
rtc::ArrayView<const Metric> metrics) {
std::unique_ptr<PerfTestResultWriter> writer =
absl::WrapUnique<PerfTestResultWriter>(CreateHistogramWriter());
for (const Metric& metric : metrics) {
if (metric.time_series.samples.empty() && IsEmpty(metric.stats)) {
// If there were no data collected for the metric it is expected that 0
// will be exported, so add 0 to the samples.
writer->LogResult(
metric.name, metric.test_case,
ToChromePerfDashboardValue(0, metric.unit),
ToChromePerfDashboardUnit(metric.unit),
/*important=*/false,
ToChromePerfDashboardImproveDirection(metric.improvement_direction));
continue;
}
if (metric.time_series.samples.empty()) {
writer->LogResultMeanAndError(
metric.name, metric.test_case,
ToChromePerfDashboardValue(*metric.stats.mean, metric.unit),
ToChromePerfDashboardValue(*metric.stats.stddev, metric.unit),
ToChromePerfDashboardUnit(metric.unit),
/*important=*/false,
ToChromePerfDashboardImproveDirection(metric.improvement_direction));
continue;
}
std::vector<double> samples(metric.time_series.samples.size());
for (size_t i = 0; i < metric.time_series.samples.size(); ++i) {
samples[i] = ToChromePerfDashboardValue(
metric.time_series.samples[i].value, metric.unit);
}
writer->LogResultList(
metric.name, metric.test_case, samples,
ToChromePerfDashboardUnit(metric.unit),
/*important=*/false,
ToChromePerfDashboardImproveDirection(metric.improvement_direction));
}
return WriteMetricsToFile(export_file_path_, writer->Serialize());
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef API_TEST_METRICS_CHROME_PERF_DASHBOARD_METRICS_EXPORTER_H_
#define API_TEST_METRICS_CHROME_PERF_DASHBOARD_METRICS_EXPORTER_H_
#include <string>
#include "absl/strings/string_view.h"
#include "api/array_view.h"
#include "api/test/metrics/metric.h"
#include "api/test/metrics/metrics_exporter.h"
namespace webrtc {
namespace test {
// Exports all collected metrics in the Chrome Perf Dashboard proto format.
class ChromePerfDashboardMetricsExporter : public MetricsExporter {
public:
// `export_file_path` - path where the proto file will be written.
explicit ChromePerfDashboardMetricsExporter(
absl::string_view export_file_path);
~ChromePerfDashboardMetricsExporter() override = default;
bool Export(rtc::ArrayView<const Metric> metrics) override;
private:
const std::string export_file_path_;
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_METRICS_CHROME_PERF_DASHBOARD_METRICS_EXPORTER_H_

View file

@ -0,0 +1,248 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/chrome_perf_dashboard_metrics_exporter.h"
#include <fstream>
#include <map>
#include <vector>
#include "api/test/metrics/metric.h"
#include "api/units/timestamp.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/testsupport/file_utils.h"
#include "third_party/catapult/tracing/tracing/value/histogram.h"
namespace webrtc {
namespace test {
namespace {
using ::testing::DoubleNear;
using ::testing::Eq;
using ::testing::Test;
namespace proto = ::catapult::tracing::tracing::proto;
std::map<std::string, std::string> DefaultMetadata() {
return std::map<std::string, std::string>{{"key", "value"}};
}
Metric::TimeSeries::Sample Sample(double value) {
return Metric::TimeSeries::Sample{.timestamp = Timestamp::Seconds(1),
.value = value,
.sample_metadata = DefaultMetadata()};
}
std::string ReadFileAsString(const std::string& filename) {
std::ifstream infile(filename, std::ios_base::binary);
auto buffer = std::vector<char>(std::istreambuf_iterator<char>(infile),
std::istreambuf_iterator<char>());
return std::string(buffer.begin(), buffer.end());
}
class ChromePerfDashboardMetricsExporterTest : public Test {
protected:
~ChromePerfDashboardMetricsExporterTest() override = default;
void SetUp() override {
temp_filename_ = webrtc::test::TempFilename(
webrtc::test::OutputPath(),
"chrome_perf_dashboard_metrics_exporter_test");
}
void TearDown() override {
ASSERT_TRUE(webrtc::test::RemoveFile(temp_filename_));
}
std::string temp_filename_;
};
TEST_F(ChromePerfDashboardMetricsExporterTest, ExportMetricFormatCorrect) {
Metric metric1{
.name = "test_metric1",
.unit = Unit::kMilliseconds,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case_name1",
.metric_metadata = DefaultMetadata(),
.time_series =
Metric::TimeSeries{.samples = std::vector{Sample(10), Sample(20)}},
.stats =
Metric::Stats{.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}};
Metric metric2{
.name = "test_metric2",
.unit = Unit::kKilobitsPerSecond,
.improvement_direction = ImprovementDirection::kSmallerIsBetter,
.test_case = "test_case_name2",
.metric_metadata = DefaultMetadata(),
.time_series =
Metric::TimeSeries{.samples = std::vector{Sample(20), Sample(40)}},
.stats = Metric::Stats{
.mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}};
ChromePerfDashboardMetricsExporter exporter(temp_filename_);
ASSERT_TRUE(exporter.Export(std::vector<Metric>{metric1, metric2}));
proto::HistogramSet actual_histogram_set;
actual_histogram_set.ParseFromString(ReadFileAsString(temp_filename_));
EXPECT_THAT(actual_histogram_set.histograms().size(), Eq(2));
// Validate output for `metric1`
EXPECT_THAT(actual_histogram_set.histograms(0).name(), Eq("test_metric1"));
EXPECT_THAT(actual_histogram_set.histograms(0).unit().unit(),
Eq(proto::Unit::MS_BEST_FIT_FORMAT));
EXPECT_THAT(actual_histogram_set.histograms(0).unit().improvement_direction(),
Eq(proto::ImprovementDirection::BIGGER_IS_BETTER));
EXPECT_THAT(
actual_histogram_set.histograms(0).diagnostics().diagnostic_map().size(),
Eq(1lu));
EXPECT_THAT(actual_histogram_set.histograms(0)
.diagnostics()
.diagnostic_map()
.at("stories")
.generic_set()
.values(0),
Eq("\"test_case_name1\""));
EXPECT_THAT(actual_histogram_set.histograms(0).sample_values().size(), Eq(2));
EXPECT_THAT(actual_histogram_set.histograms(0).sample_values(0), Eq(10.0));
EXPECT_THAT(actual_histogram_set.histograms(0).sample_values(1), Eq(20.0));
EXPECT_THAT(actual_histogram_set.histograms(0).running().count(), Eq(2));
EXPECT_THAT(actual_histogram_set.histograms(0).running().max(), Eq(20));
EXPECT_THAT(actual_histogram_set.histograms(0).running().meanlogs(),
DoubleNear(2.64916, 0.1));
EXPECT_THAT(actual_histogram_set.histograms(0).running().mean(), Eq(15));
EXPECT_THAT(actual_histogram_set.histograms(0).running().min(), Eq(10));
EXPECT_THAT(actual_histogram_set.histograms(0).running().sum(), Eq(30));
EXPECT_THAT(actual_histogram_set.histograms(0).running().variance(), Eq(50));
// Validate output for `metric2`
EXPECT_THAT(actual_histogram_set.histograms(1).name(), Eq("test_metric2"));
EXPECT_THAT(actual_histogram_set.histograms(1).unit().unit(),
Eq(proto::Unit::BYTES_PER_SECOND));
EXPECT_THAT(actual_histogram_set.histograms(1).unit().improvement_direction(),
Eq(proto::ImprovementDirection::SMALLER_IS_BETTER));
EXPECT_THAT(
actual_histogram_set.histograms(1).diagnostics().diagnostic_map().size(),
Eq(1lu));
EXPECT_THAT(actual_histogram_set.histograms(1)
.diagnostics()
.diagnostic_map()
.at("stories")
.generic_set()
.values(0),
Eq("\"test_case_name2\""));
EXPECT_THAT(actual_histogram_set.histograms(1).sample_values().size(), Eq(2));
EXPECT_THAT(actual_histogram_set.histograms(1).sample_values(0), Eq(2500.0));
EXPECT_THAT(actual_histogram_set.histograms(1).sample_values(1), Eq(5000.0));
EXPECT_THAT(actual_histogram_set.histograms(1).running().count(), Eq(2));
EXPECT_THAT(actual_histogram_set.histograms(1).running().max(), Eq(5000));
EXPECT_THAT(actual_histogram_set.histograms(1).running().meanlogs(),
DoubleNear(8.17062, 0.1));
EXPECT_THAT(actual_histogram_set.histograms(1).running().mean(), Eq(3750));
EXPECT_THAT(actual_histogram_set.histograms(1).running().min(), Eq(2500));
EXPECT_THAT(actual_histogram_set.histograms(1).running().sum(), Eq(7500));
EXPECT_THAT(actual_histogram_set.histograms(1).running().variance(),
Eq(3125000));
}
TEST_F(ChromePerfDashboardMetricsExporterTest,
ExportEmptyMetricExportsZeroValue) {
Metric metric{.name = "test_metric",
.unit = Unit::kMilliseconds,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case_name",
.metric_metadata = DefaultMetadata(),
.time_series = Metric::TimeSeries{.samples = {}},
.stats = Metric::Stats{}};
ChromePerfDashboardMetricsExporter exporter(temp_filename_);
ASSERT_TRUE(exporter.Export(std::vector<Metric>{metric}));
proto::HistogramSet actual_histogram_set;
actual_histogram_set.ParseFromString(ReadFileAsString(temp_filename_));
EXPECT_THAT(actual_histogram_set.histograms().size(), Eq(1));
// Validate values for `metric`
EXPECT_THAT(actual_histogram_set.histograms(0).sample_values().size(), Eq(1));
EXPECT_THAT(actual_histogram_set.histograms(0).sample_values(0), Eq(0.0));
EXPECT_THAT(actual_histogram_set.histograms(0).running().count(), Eq(1));
EXPECT_THAT(actual_histogram_set.histograms(0).running().max(),
DoubleNear(0, 1e-6));
EXPECT_THAT(actual_histogram_set.histograms(0).running().meanlogs(), Eq(0));
EXPECT_THAT(actual_histogram_set.histograms(0).running().mean(), Eq(0));
EXPECT_THAT(actual_histogram_set.histograms(0).running().min(), Eq(0));
EXPECT_THAT(actual_histogram_set.histograms(0).running().sum(), Eq(0));
EXPECT_THAT(actual_histogram_set.histograms(0).running().variance(), Eq(0));
}
TEST_F(ChromePerfDashboardMetricsExporterTest,
ExportMetricWithOnlyStatsExportsMeanValues) {
Metric metric{.name = "test_metric",
.unit = Unit::kMilliseconds,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case_name",
.metric_metadata = DefaultMetadata(),
.time_series = Metric::TimeSeries{.samples = {}},
.stats = Metric::Stats{
.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}};
ChromePerfDashboardMetricsExporter exporter(temp_filename_);
ASSERT_TRUE(exporter.Export(std::vector<Metric>{metric}));
proto::HistogramSet actual_histogram_set;
actual_histogram_set.ParseFromString(ReadFileAsString(temp_filename_));
EXPECT_THAT(actual_histogram_set.histograms().size(), Eq(1));
// Validate values for `metric`
EXPECT_THAT(actual_histogram_set.histograms(0).sample_values().size(), Eq(1));
EXPECT_THAT(actual_histogram_set.histograms(0).sample_values(0), Eq(15.0));
EXPECT_THAT(actual_histogram_set.histograms(0).running().count(), Eq(1));
EXPECT_THAT(actual_histogram_set.histograms(0).running().max(), Eq(15));
EXPECT_THAT(actual_histogram_set.histograms(0).running().meanlogs(),
DoubleNear(2.70805, 0.1));
EXPECT_THAT(actual_histogram_set.histograms(0).running().mean(), Eq(15));
EXPECT_THAT(actual_histogram_set.histograms(0).running().min(), Eq(15));
EXPECT_THAT(actual_histogram_set.histograms(0).running().sum(), Eq(15));
EXPECT_THAT(actual_histogram_set.histograms(0).running().variance(), Eq(0));
}
TEST_F(ChromePerfDashboardMetricsExporterTest,
ExportMetricWithOnlyStatsConvertsMeanValuesWhenRequired) {
Metric metric{.name = "test_metric",
.unit = Unit::kKilobitsPerSecond,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case_name",
.metric_metadata = DefaultMetadata(),
.time_series = Metric::TimeSeries{.samples = {}},
.stats = Metric::Stats{
.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}};
ChromePerfDashboardMetricsExporter exporter(temp_filename_);
ASSERT_TRUE(exporter.Export(std::vector<Metric>{metric}));
proto::HistogramSet actual_histogram_set;
actual_histogram_set.ParseFromString(ReadFileAsString(temp_filename_));
EXPECT_THAT(actual_histogram_set.histograms().size(), Eq(1));
// Validate values for `metric`
EXPECT_THAT(actual_histogram_set.histograms(0).sample_values().size(), Eq(1));
EXPECT_THAT(actual_histogram_set.histograms(0).sample_values(0), Eq(1875.0));
EXPECT_THAT(actual_histogram_set.histograms(0).running().count(), Eq(1));
EXPECT_THAT(actual_histogram_set.histograms(0).running().max(), Eq(1875));
EXPECT_THAT(actual_histogram_set.histograms(0).running().meanlogs(),
DoubleNear(7.53636, 0.1));
EXPECT_THAT(actual_histogram_set.histograms(0).running().mean(), Eq(1875));
EXPECT_THAT(actual_histogram_set.histograms(0).running().min(), Eq(1875));
EXPECT_THAT(actual_histogram_set.histograms(0).running().sum(), Eq(1875));
EXPECT_THAT(actual_histogram_set.histograms(0).running().variance(), Eq(0));
}
} // namespace
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/global_metrics_logger_and_exporter.h"
#include <memory>
#include <utility>
#include <vector>
#include "api/test/metrics/metrics_exporter.h"
#include "api/test/metrics/metrics_logger.h"
#include "rtc_base/checks.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
namespace test {
DefaultMetricsLogger* GetGlobalMetricsLogger() {
static DefaultMetricsLogger* logger_ =
new DefaultMetricsLogger(Clock::GetRealTimeClock());
return logger_;
}
bool ExportPerfMetric(MetricsLogger& logger,
std::vector<std::unique_ptr<MetricsExporter>> exporters) {
std::vector<Metric> metrics = logger.GetCollectedMetrics();
bool success = true;
for (auto& exporter : exporters) {
bool export_result = exporter->Export(metrics);
success = success && export_result;
}
return success;
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef API_TEST_METRICS_GLOBAL_METRICS_LOGGER_AND_EXPORTER_H_
#define API_TEST_METRICS_GLOBAL_METRICS_LOGGER_AND_EXPORTER_H_
#include <memory>
#include <vector>
#include "api/test/metrics/metrics_exporter.h"
#include "api/test/metrics/metrics_logger.h"
namespace webrtc {
namespace test {
// Returns non-null global `MetricsLogger` to log metrics.
DefaultMetricsLogger* GetGlobalMetricsLogger();
bool ExportPerfMetric(MetricsLogger& logger,
std::vector<std::unique_ptr<MetricsExporter>> exporters);
} // namespace test
} // namespace webrtc
#endif // API_TEST_METRICS_GLOBAL_METRICS_LOGGER_AND_EXPORTER_H_

View file

@ -0,0 +1,131 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/global_metrics_logger_and_exporter.h"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "absl/types/optional.h"
#include "api/test/metrics/metric.h"
#include "api/test/metrics/metrics_exporter.h"
#include "api/test/metrics/metrics_logger.h"
#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace test {
namespace {
using ::testing::Eq;
using ::testing::IsEmpty;
std::map<std::string, std::string> DefaultMetadata() {
return std::map<std::string, std::string>{{"key", "value"}};
}
struct TestMetricsExporterFactory {
public:
std::unique_ptr<MetricsExporter> CreateExporter() {
return std::make_unique<TestMetricsExporter>(this, /*export_result=*/true);
}
std::unique_ptr<MetricsExporter> CreateFailureExporter() {
return std::make_unique<TestMetricsExporter>(this, /*export_result=*/false);
}
std::vector<Metric> exported_metrics;
private:
class TestMetricsExporter : public MetricsExporter {
public:
TestMetricsExporter(TestMetricsExporterFactory* factory, bool export_result)
: factory_(factory), export_result_(export_result) {}
~TestMetricsExporter() override = default;
bool Export(rtc::ArrayView<const Metric> metrics) override {
factory_->exported_metrics =
std::vector<Metric>(metrics.begin(), metrics.end());
return export_result_;
}
TestMetricsExporterFactory* factory_;
bool export_result_;
};
};
TEST(ExportPerfMetricTest, CollectedMetricsAreExporter) {
TestMetricsExporterFactory exporter_factory;
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
logger.LogSingleValueMetric(
"metric_name", "test_case_name",
/*value=*/10, Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter,
std::map<std::string, std::string>{{"key", "value"}});
std::vector<std::unique_ptr<MetricsExporter>> exporters;
exporters.push_back(exporter_factory.CreateExporter());
ASSERT_TRUE(ExportPerfMetric(logger, std::move(exporters)));
std::vector<Metric> metrics = exporter_factory.exported_metrics;
ASSERT_THAT(metrics.size(), Eq(1lu));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kBiggerIsBetter));
EXPECT_THAT(metric.metric_metadata,
Eq(std::map<std::string, std::string>{{"key", "value"}}));
ASSERT_THAT(metric.time_series.samples.size(), Eq(1lu));
EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metric.time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{}));
ASSERT_THAT(metric.stats.mean, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.stddev, absl::nullopt);
ASSERT_THAT(metric.stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.max, absl::optional<double>(10.0));
}
TEST(ExportPerfMetricTest, OneFailedExporterDoesNotPreventExportToOthers) {
TestMetricsExporterFactory exporter_factory1;
TestMetricsExporterFactory exporter_factory2;
TestMetricsExporterFactory exporter_factory3;
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
logger.LogSingleValueMetric("metric_name", "test_case_name",
/*value=*/10, Unit::kMilliseconds,
ImprovementDirection::kBiggerIsBetter,
DefaultMetadata());
std::vector<std::unique_ptr<MetricsExporter>> exporters;
exporters.push_back(exporter_factory1.CreateExporter());
exporters.push_back(exporter_factory2.CreateFailureExporter());
exporters.push_back(exporter_factory3.CreateExporter());
ASSERT_FALSE(ExportPerfMetric(logger, std::move(exporters)));
std::vector<Metric> metrics1 = exporter_factory1.exported_metrics;
std::vector<Metric> metrics2 = exporter_factory2.exported_metrics;
std::vector<Metric> metrics3 = exporter_factory3.exported_metrics;
ASSERT_THAT(metrics1.size(), Eq(1lu))
<< metrics1[0].name << "; " << metrics1[1].name;
EXPECT_THAT(metrics1[0].name, Eq("metric_name"));
ASSERT_THAT(metrics2.size(), Eq(1lu));
EXPECT_THAT(metrics2[0].name, Eq("metric_name"));
ASSERT_THAT(metrics3.size(), Eq(1lu));
EXPECT_THAT(metrics3[0].name, Eq("metric_name"));
}
} // namespace
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,48 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/metric.h"
#include <string>
namespace webrtc {
namespace test {
absl::string_view ToString(Unit unit) {
switch (unit) {
case Unit::kMilliseconds:
return "Milliseconds";
case Unit::kPercent:
return "Percent";
case Unit::kBytes:
return "Bytes";
case Unit::kKilobitsPerSecond:
return "KilobitsPerSecond";
case Unit::kHertz:
return "Hertz";
case Unit::kUnitless:
return "Unitless";
case Unit::kCount:
return "Count";
}
}
absl::string_view ToString(ImprovementDirection direction) {
switch (direction) {
case ImprovementDirection::kBiggerIsBetter:
return "BiggerIsBetter";
case ImprovementDirection::kNeitherIsBetter:
return "NeitherIsBetter";
case ImprovementDirection::kSmallerIsBetter:
return "SmallerIsBetter";
}
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,96 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef API_TEST_METRICS_METRIC_H_
#define API_TEST_METRICS_METRIC_H_
#include <map>
#include <string>
#include <vector>
#include "absl/types/optional.h"
#include "api/units/timestamp.h"
namespace webrtc {
namespace test {
enum class Unit {
kMilliseconds,
kPercent,
kBytes,
kKilobitsPerSecond,
kHertz,
// General unitless value. Can be used either for dimensionless quantities
// (ex ratio) or for units not presented in this enum and too specific to add
// to this enum.
kUnitless,
kCount
};
absl::string_view ToString(Unit unit);
enum class ImprovementDirection {
kBiggerIsBetter,
kNeitherIsBetter,
kSmallerIsBetter
};
absl::string_view ToString(ImprovementDirection direction);
struct Metric {
struct TimeSeries {
struct Sample {
// Timestamp in microseconds associated with a sample. For example,
// the timestamp when the sample was collected.
webrtc::Timestamp timestamp;
double value;
// Metadata associated with this particular sample.
std::map<std::string, std::string> sample_metadata;
};
// All samples collected for this metric. It can be empty if the Metric
// object only contains `stats`.
std::vector<Sample> samples;
};
// Contains metric's precomputed statistics based on the `time_series` or if
// `time_series` is omitted (has 0 samples) contains precomputed statistics
// provided by the metric's calculator.
struct Stats {
// Sample mean of the metric
// (https://en.wikipedia.org/wiki/Sample_mean_and_covariance).
absl::optional<double> mean;
// Standard deviation (https://en.wikipedia.org/wiki/Standard_deviation).
// Is undefined if `time_series` contains only a single value.
absl::optional<double> stddev;
absl::optional<double> min;
absl::optional<double> max;
};
// Metric name, for example PSNR, SSIM, decode_time, etc.
std::string name;
Unit unit;
ImprovementDirection improvement_direction;
// If the metric is generated by a test, this field can be used to specify
// this information.
std::string test_case;
// Metadata associated with the whole metric.
std::map<std::string, std::string> metric_metadata;
// Contains all samples of the metric collected during test execution.
// It can be empty if the user only stores precomputed statistics into
// `stats`.
TimeSeries time_series;
Stats stats;
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_METRICS_METRIC_H_

View file

@ -0,0 +1,132 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/metrics_accumulator.h"
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "api/numerics/samples_stats_counter.h"
#include "api/test/metrics/metric.h"
#include "api/units/timestamp.h"
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
namespace test {
namespace {
Metric::Stats ToStats(const SamplesStatsCounter& values) {
if (values.IsEmpty()) {
return Metric::Stats();
}
return Metric::Stats{.mean = values.GetAverage(),
.stddev = values.GetStandardDeviation(),
.min = values.GetMin(),
.max = values.GetMax()};
}
Metric SetTimeseries(const Metric& prototype,
const SamplesStatsCounter& counter) {
Metric output(prototype);
Metric::TimeSeries time_series;
for (const SamplesStatsCounter::StatsSample& sample :
counter.GetTimedSamples()) {
time_series.samples.push_back(
Metric::TimeSeries::Sample{.timestamp = sample.time,
.value = sample.value,
.sample_metadata = sample.metadata});
}
output.time_series = std::move(time_series);
output.stats = ToStats(counter);
return output;
}
} // namespace
bool operator<(const MetricsAccumulator::MetricKey& a,
const MetricsAccumulator::MetricKey& b) {
if (a.test_case_name < b.test_case_name) {
return true;
} else if (a.test_case_name > b.test_case_name) {
return false;
} else {
return a.metric_name < b.metric_name;
}
}
bool MetricsAccumulator::AddSample(
absl::string_view metric_name,
absl::string_view test_case_name,
double value,
Timestamp timestamp,
std::map<std::string, std::string> point_metadata) {
MutexLock lock(&mutex_);
bool created;
MetricValue* metric_value =
GetOrCreateMetric(metric_name, test_case_name, &created);
metric_value->counter.AddSample(
SamplesStatsCounter::StatsSample{.value = value,
.time = timestamp,
.metadata = std::move(point_metadata)});
return created;
}
bool MetricsAccumulator::AddMetricMetadata(
absl::string_view metric_name,
absl::string_view test_case_name,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metric_metadata) {
MutexLock lock(&mutex_);
bool created;
MetricValue* metric_value =
GetOrCreateMetric(metric_name, test_case_name, &created);
metric_value->metric.unit = unit;
metric_value->metric.improvement_direction = improvement_direction;
metric_value->metric.metric_metadata = std::move(metric_metadata);
return created;
}
std::vector<Metric> MetricsAccumulator::GetCollectedMetrics() const {
MutexLock lock(&mutex_);
std::vector<Metric> out;
out.reserve(metrics_.size());
for (const auto& [unused_key, metric_value] : metrics_) {
out.push_back(SetTimeseries(metric_value.metric, metric_value.counter));
}
return out;
}
MetricsAccumulator::MetricValue* MetricsAccumulator::GetOrCreateMetric(
absl::string_view metric_name,
absl::string_view test_case_name,
bool* created) {
MetricKey key(metric_name, test_case_name);
auto it = metrics_.find(key);
if (it != metrics_.end()) {
*created = false;
return &it->second;
}
*created = true;
Metric metric{
.name = key.metric_name,
.unit = Unit::kUnitless,
.improvement_direction = ImprovementDirection::kNeitherIsBetter,
.test_case = key.test_case_name,
};
return &metrics_.emplace(key, MetricValue{.metric = std::move(metric)})
.first->second;
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,99 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef API_TEST_METRICS_METRICS_ACCUMULATOR_H_
#define API_TEST_METRICS_METRICS_ACCUMULATOR_H_
#include <map>
#include <string>
#include <vector>
#include "absl/strings/string_view.h"
#include "api/numerics/samples_stats_counter.h"
#include "api/test/metrics/metric.h"
#include "api/units/timestamp.h"
#include "rtc_base/synchronization/mutex.h"
#include "rtc_base/thread_annotations.h"
namespace webrtc {
namespace test {
// Accumulates metrics' samples internally and provides API to get collected
// ones.
//
// This object is thread safe.
class MetricsAccumulator {
public:
MetricsAccumulator() = default;
// Adds sample for the specified `metric_name` within specified
// `test_case_name`. If it is the first time when this combination of
// `metric_name` and `test_case_name` is used, creates a new Metric to collect
// samples, otherwise adds a sample to the previously created Metric.
//
// By default metric will use `Unit::kUnitless` and
// `ImprovementDirection::kNeitherIsBetter`.
//
// `point_metadata` - the metadata to be added to the single data point that
// this method adds to the Metric (it is not a metric global metadata).
//
// Returns true if a new metric was created and false otherwise.
bool AddSample(absl::string_view metric_name,
absl::string_view test_case_name,
double value,
Timestamp timestamp,
std::map<std::string, std::string> point_metadata = {});
// Adds metadata to the metric specified by `metric_name` within specified
// `test_case_name`. If such a metric doesn't exist, creates a new one,
// otherwise overrides previously recorded values.
//
// Returns true if a new metric was created and false otherwise.
bool AddMetricMetadata(
absl::string_view metric_name,
absl::string_view test_case_name,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metric_metadata = {});
// Returns all metrics collected by this accumulator. No order guarantees
// provided.
std::vector<Metric> GetCollectedMetrics() const;
private:
struct MetricKey {
MetricKey(absl::string_view metric_name, absl::string_view test_case_name)
: metric_name(metric_name), test_case_name(test_case_name) {}
std::string metric_name;
std::string test_case_name;
};
friend bool operator<(const MetricKey& a, const MetricKey& b);
struct MetricValue {
SamplesStatsCounter counter;
Metric metric;
};
// Gets existing metrics or creates a new one. If metric was created `created`
// will be set to true.
MetricValue* GetOrCreateMetric(absl::string_view metric_name,
absl::string_view test_case_name,
bool* created)
RTC_EXCLUSIVE_LOCKS_REQUIRED(mutex_);
mutable Mutex mutex_;
std::map<MetricKey, MetricValue> metrics_ RTC_GUARDED_BY(mutex_);
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_METRICS_METRICS_ACCUMULATOR_H_

View file

@ -0,0 +1,315 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/metrics_accumulator.h"
#include <map>
#include <vector>
#include "api/test/metrics/metric.h"
#include "api/units/timestamp.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace test {
namespace {
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::SizeIs;
TEST(MetricsAccumulatorTest, AddSampleToTheNewMetricWillCreateOne) {
MetricsAccumulator accumulator;
ASSERT_TRUE(accumulator.AddSample(
"metric_name", "test_case_name",
/*value=*/10, Timestamp::Seconds(1),
/*point_metadata=*/std::map<std::string, std::string>{{"key", "value"}}));
std::vector<Metric> metrics = accumulator.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kUnitless));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kNeitherIsBetter));
EXPECT_THAT(metric.metric_metadata, IsEmpty());
ASSERT_THAT(metric.time_series.samples, SizeIs(1));
EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metric.time_series.samples[0].timestamp,
Eq(Timestamp::Seconds(1)));
EXPECT_THAT(metric.time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{{"key", "value"}}));
ASSERT_THAT(metric.stats.mean, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.stddev, absl::optional<double>(0.0));
ASSERT_THAT(metric.stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.max, absl::optional<double>(10.0));
}
TEST(MetricsAccumulatorTest, AddSamplesToExistingMetricWontCreateNewOne) {
MetricsAccumulator accumulator;
ASSERT_TRUE(accumulator.AddSample(
"metric_name", "test_case_name",
/*value=*/10, Timestamp::Seconds(1),
/*point_metadata=*/
std::map<std::string, std::string>{{"key1", "value1"}}));
ASSERT_FALSE(accumulator.AddSample(
"metric_name", "test_case_name",
/*value=*/20, Timestamp::Seconds(2),
/*point_metadata=*/
std::map<std::string, std::string>{{"key2", "value2"}}));
std::vector<Metric> metrics = accumulator.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kUnitless));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kNeitherIsBetter));
EXPECT_THAT(metric.metric_metadata, IsEmpty());
ASSERT_THAT(metric.time_series.samples, SizeIs(2));
EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metric.time_series.samples[0].timestamp,
Eq(Timestamp::Seconds(1)));
EXPECT_THAT(metric.time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{{"key1", "value1"}}));
EXPECT_THAT(metric.time_series.samples[1].value, Eq(20.0));
EXPECT_THAT(metric.time_series.samples[1].timestamp,
Eq(Timestamp::Seconds(2)));
EXPECT_THAT(metric.time_series.samples[1].sample_metadata,
Eq(std::map<std::string, std::string>{{"key2", "value2"}}));
ASSERT_THAT(metric.stats.mean, absl::optional<double>(15.0));
ASSERT_THAT(metric.stats.stddev, absl::optional<double>(5.0));
ASSERT_THAT(metric.stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.max, absl::optional<double>(20.0));
}
TEST(MetricsAccumulatorTest, AddSampleToDifferentMetricsWillCreateBoth) {
MetricsAccumulator accumulator;
ASSERT_TRUE(accumulator.AddSample(
"metric_name1", "test_case_name1",
/*value=*/10, Timestamp::Seconds(1),
/*point_metadata=*/
std::map<std::string, std::string>{{"key1", "value1"}}));
ASSERT_TRUE(accumulator.AddSample(
"metric_name2", "test_case_name2",
/*value=*/20, Timestamp::Seconds(2),
/*point_metadata=*/
std::map<std::string, std::string>{{"key2", "value2"}}));
std::vector<Metric> metrics = accumulator.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(2));
EXPECT_THAT(metrics[0].name, Eq("metric_name1"));
EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1"));
EXPECT_THAT(metrics[0].unit, Eq(Unit::kUnitless));
EXPECT_THAT(metrics[0].improvement_direction,
Eq(ImprovementDirection::kNeitherIsBetter));
EXPECT_THAT(metrics[0].metric_metadata, IsEmpty());
ASSERT_THAT(metrics[0].time_series.samples, SizeIs(1));
EXPECT_THAT(metrics[0].time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metrics[0].time_series.samples[0].timestamp,
Eq(Timestamp::Seconds(1)));
EXPECT_THAT(metrics[0].time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{{"key1", "value1"}}));
ASSERT_THAT(metrics[0].stats.mean, absl::optional<double>(10.0));
ASSERT_THAT(metrics[0].stats.stddev, absl::optional<double>(0.0));
ASSERT_THAT(metrics[0].stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metrics[0].stats.max, absl::optional<double>(10.0));
EXPECT_THAT(metrics[1].name, Eq("metric_name2"));
EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2"));
EXPECT_THAT(metrics[1].unit, Eq(Unit::kUnitless));
EXPECT_THAT(metrics[1].improvement_direction,
Eq(ImprovementDirection::kNeitherIsBetter));
EXPECT_THAT(metrics[1].metric_metadata, IsEmpty());
ASSERT_THAT(metrics[1].time_series.samples, SizeIs(1));
EXPECT_THAT(metrics[1].time_series.samples[0].value, Eq(20.0));
EXPECT_THAT(metrics[1].time_series.samples[0].timestamp,
Eq(Timestamp::Seconds(2)));
EXPECT_THAT(metrics[1].time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{{"key2", "value2"}}));
ASSERT_THAT(metrics[1].stats.mean, absl::optional<double>(20.0));
ASSERT_THAT(metrics[1].stats.stddev, absl::optional<double>(0.0));
ASSERT_THAT(metrics[1].stats.min, absl::optional<double>(20.0));
ASSERT_THAT(metrics[1].stats.max, absl::optional<double>(20.0));
}
TEST(MetricsAccumulatorTest, AddMetadataToTheNewMetricWillCreateOne) {
MetricsAccumulator accumulator;
ASSERT_TRUE(accumulator.AddMetricMetadata(
"metric_name", "test_case_name", Unit::kMilliseconds,
ImprovementDirection::kBiggerIsBetter,
/*metric_metadata=*/
std::map<std::string, std::string>{{"key", "value"}}));
std::vector<Metric> metrics = accumulator.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kBiggerIsBetter));
EXPECT_THAT(metric.metric_metadata,
Eq(std::map<std::string, std::string>{{"key", "value"}}));
ASSERT_THAT(metric.time_series.samples, IsEmpty());
ASSERT_THAT(metric.stats.mean, absl::nullopt);
ASSERT_THAT(metric.stats.stddev, absl::nullopt);
ASSERT_THAT(metric.stats.min, absl::nullopt);
ASSERT_THAT(metric.stats.max, absl::nullopt);
}
TEST(MetricsAccumulatorTest,
AddMetadataToTheExistingMetricWillOverwriteValues) {
MetricsAccumulator accumulator;
ASSERT_TRUE(accumulator.AddMetricMetadata(
"metric_name", "test_case_name", Unit::kMilliseconds,
ImprovementDirection::kBiggerIsBetter,
/*metric_metadata=*/
std::map<std::string, std::string>{{"key1", "value1"}}));
ASSERT_FALSE(accumulator.AddMetricMetadata(
"metric_name", "test_case_name", Unit::kBytes,
ImprovementDirection::kSmallerIsBetter,
/*metric_metadata=*/
std::map<std::string, std::string>{{"key2", "value2"}}));
std::vector<Metric> metrics = accumulator.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kBytes));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kSmallerIsBetter));
EXPECT_THAT(metric.metric_metadata,
Eq(std::map<std::string, std::string>{{"key2", "value2"}}));
ASSERT_THAT(metric.time_series.samples, IsEmpty());
ASSERT_THAT(metric.stats.mean, absl::nullopt);
ASSERT_THAT(metric.stats.stddev, absl::nullopt);
ASSERT_THAT(metric.stats.min, absl::nullopt);
ASSERT_THAT(metric.stats.max, absl::nullopt);
}
TEST(MetricsAccumulatorTest, AddMetadataToDifferentMetricsWillCreateBoth) {
MetricsAccumulator accumulator;
ASSERT_TRUE(accumulator.AddMetricMetadata(
"metric_name1", "test_case_name1", Unit::kMilliseconds,
ImprovementDirection::kBiggerIsBetter,
/*metric_metadata=*/
std::map<std::string, std::string>{{"key1", "value1"}}));
ASSERT_TRUE(accumulator.AddMetricMetadata(
"metric_name2", "test_case_name2", Unit::kBytes,
ImprovementDirection::kSmallerIsBetter,
/*metric_metadata=*/
std::map<std::string, std::string>{{"key2", "value2"}}));
std::vector<Metric> metrics = accumulator.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(2));
EXPECT_THAT(metrics[0].name, Eq("metric_name1"));
EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1"));
EXPECT_THAT(metrics[0].unit, Eq(Unit::kMilliseconds));
EXPECT_THAT(metrics[0].improvement_direction,
Eq(ImprovementDirection::kBiggerIsBetter));
EXPECT_THAT(metrics[0].metric_metadata,
Eq(std::map<std::string, std::string>{{"key1", "value1"}}));
ASSERT_THAT(metrics[0].time_series.samples, IsEmpty());
ASSERT_THAT(metrics[0].stats.mean, absl::nullopt);
ASSERT_THAT(metrics[0].stats.stddev, absl::nullopt);
ASSERT_THAT(metrics[0].stats.min, absl::nullopt);
ASSERT_THAT(metrics[0].stats.max, absl::nullopt);
EXPECT_THAT(metrics[1].name, Eq("metric_name2"));
EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2"));
EXPECT_THAT(metrics[1].unit, Eq(Unit::kBytes));
EXPECT_THAT(metrics[1].improvement_direction,
Eq(ImprovementDirection::kSmallerIsBetter));
EXPECT_THAT(metrics[1].metric_metadata,
Eq(std::map<std::string, std::string>{{"key2", "value2"}}));
ASSERT_THAT(metrics[1].time_series.samples, IsEmpty());
ASSERT_THAT(metrics[1].stats.mean, absl::nullopt);
ASSERT_THAT(metrics[1].stats.stddev, absl::nullopt);
ASSERT_THAT(metrics[1].stats.min, absl::nullopt);
ASSERT_THAT(metrics[1].stats.max, absl::nullopt);
}
TEST(MetricsAccumulatorTest, AddMetadataAfterAddingSampleWontCreateNewMetric) {
MetricsAccumulator accumulator;
ASSERT_TRUE(accumulator.AddSample(
"metric_name", "test_case_name",
/*value=*/10, Timestamp::Seconds(1),
/*point_metadata=*/
std::map<std::string, std::string>{{"key_s", "value_s"}}));
ASSERT_FALSE(accumulator.AddMetricMetadata(
"metric_name", "test_case_name", Unit::kMilliseconds,
ImprovementDirection::kBiggerIsBetter,
/*metric_metadata=*/
std::map<std::string, std::string>{{"key_m", "value_m"}}));
std::vector<Metric> metrics = accumulator.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kBiggerIsBetter));
EXPECT_THAT(metric.metric_metadata,
Eq(std::map<std::string, std::string>{{"key_m", "value_m"}}));
ASSERT_THAT(metric.time_series.samples, SizeIs(1));
EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metric.time_series.samples[0].timestamp,
Eq(Timestamp::Seconds(1)));
EXPECT_THAT(metric.time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{{"key_s", "value_s"}}));
ASSERT_THAT(metric.stats.mean, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.stddev, absl::optional<double>(0.0));
ASSERT_THAT(metric.stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.max, absl::optional<double>(10.0));
}
TEST(MetricsAccumulatorTest, AddSampleAfterAddingMetadataWontCreateNewMetric) {
MetricsAccumulator accumulator;
ASSERT_TRUE(accumulator.AddMetricMetadata(
"metric_name", "test_case_name", Unit::kMilliseconds,
ImprovementDirection::kBiggerIsBetter,
/*metric_metadata=*/
std::map<std::string, std::string>{{"key_m", "value_m"}}));
ASSERT_FALSE(accumulator.AddSample(
"metric_name", "test_case_name",
/*value=*/10, Timestamp::Seconds(1),
/*point_metadata=*/
std::map<std::string, std::string>{{"key_s", "value_s"}}));
std::vector<Metric> metrics = accumulator.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kBiggerIsBetter));
EXPECT_THAT(metric.metric_metadata,
Eq(std::map<std::string, std::string>{{"key_m", "value_m"}}));
ASSERT_THAT(metric.time_series.samples, SizeIs(1));
EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metric.time_series.samples[0].timestamp,
Eq(Timestamp::Seconds(1)));
EXPECT_THAT(metric.time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{{"key_s", "value_s"}}));
ASSERT_THAT(metric.stats.mean, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.stddev, absl::optional<double>(0.0));
ASSERT_THAT(metric.stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.max, absl::optional<double>(10.0));
}
} // namespace
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,33 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef API_TEST_METRICS_METRICS_EXPORTER_H_
#define API_TEST_METRICS_METRICS_EXPORTER_H_
#include "api/array_view.h"
#include "api/test/metrics/metric.h"
namespace webrtc {
namespace test {
// Exports metrics in the requested format.
class MetricsExporter {
public:
virtual ~MetricsExporter() = default;
// Exports specified metrics in a format that depends on the implementation.
// Returns true if export succeeded, false otherwise.
virtual bool Export(rtc::ArrayView<const Metric> metrics) = 0;
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_METRICS_METRICS_EXPORTER_H_

View file

@ -0,0 +1,114 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/metrics_logger.h"
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "api/numerics/samples_stats_counter.h"
#include "api/test/metrics/metric.h"
#include "rtc_base/synchronization/mutex.h"
namespace webrtc {
namespace test {
namespace {
Metric::Stats ToStats(const SamplesStatsCounter& values) {
if (values.IsEmpty()) {
return Metric::Stats();
}
return Metric::Stats{.mean = values.GetAverage(),
.stddev = values.GetStandardDeviation(),
.min = values.GetMin(),
.max = values.GetMax()};
}
} // namespace
void DefaultMetricsLogger::LogSingleValueMetric(
absl::string_view name,
absl::string_view test_case_name,
double value,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metadata) {
MutexLock lock(&mutex_);
metrics_.push_back(Metric{
.name = std::string(name),
.unit = unit,
.improvement_direction = improvement_direction,
.test_case = std::string(test_case_name),
.metric_metadata = std::move(metadata),
.time_series =
Metric::TimeSeries{.samples = std::vector{Metric::TimeSeries::Sample{
.timestamp = Now(), .value = value}}},
.stats = Metric::Stats{
.mean = value, .stddev = absl::nullopt, .min = value, .max = value}});
}
void DefaultMetricsLogger::LogMetric(
absl::string_view name,
absl::string_view test_case_name,
const SamplesStatsCounter& values,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metadata) {
MutexLock lock(&mutex_);
Metric::TimeSeries time_series;
for (const SamplesStatsCounter::StatsSample& sample :
values.GetTimedSamples()) {
time_series.samples.push_back(
Metric::TimeSeries::Sample{.timestamp = sample.time,
.value = sample.value,
.sample_metadata = sample.metadata});
}
metrics_.push_back(Metric{.name = std::string(name),
.unit = unit,
.improvement_direction = improvement_direction,
.test_case = std::string(test_case_name),
.metric_metadata = std::move(metadata),
.time_series = std::move(time_series),
.stats = ToStats(values)});
}
void DefaultMetricsLogger::LogMetric(
absl::string_view name,
absl::string_view test_case_name,
const Metric::Stats& metric_stats,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metadata) {
MutexLock lock(&mutex_);
metrics_.push_back(Metric{.name = std::string(name),
.unit = unit,
.improvement_direction = improvement_direction,
.test_case = std::string(test_case_name),
.metric_metadata = std::move(metadata),
.time_series = Metric::TimeSeries{.samples = {}},
.stats = std::move(metric_stats)});
}
std::vector<Metric> DefaultMetricsLogger::GetCollectedMetrics() const {
std::vector<Metric> out = metrics_accumulator_.GetCollectedMetrics();
MutexLock lock(&mutex_);
out.insert(out.end(), metrics_.begin(), metrics_.end());
return out;
}
Timestamp DefaultMetricsLogger::Now() {
return clock_->CurrentTime();
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,112 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef API_TEST_METRICS_METRICS_LOGGER_H_
#define API_TEST_METRICS_METRICS_LOGGER_H_
#include <map>
#include <string>
#include <utility>
#include <vector>
#include "absl/strings/string_view.h"
#include "api/numerics/samples_stats_counter.h"
#include "api/test/metrics/metric.h"
#include "api/test/metrics/metrics_accumulator.h"
#include "rtc_base/synchronization/mutex.h"
#include "system_wrappers/include/clock.h"
namespace webrtc {
namespace test {
// Provides API to log and collect performance metrics.
class MetricsLogger {
public:
virtual ~MetricsLogger() = default;
// Adds a metric with a single value.
// `metadata` - metric's level metadata to add.
virtual void LogSingleValueMetric(
absl::string_view name,
absl::string_view test_case_name,
double value,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metadata = {}) = 0;
// Adds metrics with a time series created based on the provided `values`.
// `metadata` - metric's level metadata to add.
virtual void LogMetric(absl::string_view name,
absl::string_view test_case_name,
const SamplesStatsCounter& values,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metadata = {}) = 0;
// Adds metric with a time series with only stats object and without actual
// collected values.
// `metadata` - metric's level metadata to add.
virtual void LogMetric(absl::string_view name,
absl::string_view test_case_name,
const Metric::Stats& metric_stats,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metadata = {}) = 0;
// Returns all metrics collected by this logger.
virtual std::vector<Metric> GetCollectedMetrics() const = 0;
};
class DefaultMetricsLogger : public MetricsLogger {
public:
explicit DefaultMetricsLogger(webrtc::Clock* clock) : clock_(clock) {}
~DefaultMetricsLogger() override = default;
void LogSingleValueMetric(
absl::string_view name,
absl::string_view test_case_name,
double value,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metadata = {}) override;
void LogMetric(absl::string_view name,
absl::string_view test_case_name,
const SamplesStatsCounter& values,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metadata = {}) override;
void LogMetric(absl::string_view name,
absl::string_view test_case_name,
const Metric::Stats& metric_stats,
Unit unit,
ImprovementDirection improvement_direction,
std::map<std::string, std::string> metadata = {}) override;
// Returns all metrics collected by this logger and its `MetricsAccumulator`.
std::vector<Metric> GetCollectedMetrics() const override;
MetricsAccumulator* GetMetricsAccumulator() { return &metrics_accumulator_; }
private:
webrtc::Timestamp Now();
webrtc::Clock* const clock_;
MetricsAccumulator metrics_accumulator_;
mutable Mutex mutex_;
std::vector<Metric> metrics_ RTC_GUARDED_BY(mutex_);
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_METRICS_METRICS_LOGGER_H_

View file

@ -0,0 +1,326 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/metrics_logger.h"
#include <map>
#include <memory>
#include <string>
#include <vector>
#include "absl/types/optional.h"
#include "api/numerics/samples_stats_counter.h"
#include "api/test/metrics/metric.h"
#include "system_wrappers/include/clock.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace test {
namespace {
using ::testing::Eq;
using ::testing::IsEmpty;
using ::testing::SizeIs;
std::map<std::string, std::string> DefaultMetadata() {
return std::map<std::string, std::string>{{"key", "value"}};
}
TEST(DefaultMetricsLoggerTest, LogSingleValueMetricRecordsMetric) {
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
logger.LogSingleValueMetric(
"metric_name", "test_case_name",
/*value=*/10, Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter,
std::map<std::string, std::string>{{"key", "value"}});
std::vector<Metric> metrics = logger.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kBiggerIsBetter));
EXPECT_THAT(metric.metric_metadata,
Eq(std::map<std::string, std::string>{{"key", "value"}}));
ASSERT_THAT(metric.time_series.samples, SizeIs(1));
EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metric.time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{}));
ASSERT_THAT(metric.stats.mean, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.stddev, absl::nullopt);
ASSERT_THAT(metric.stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.max, absl::optional<double>(10.0));
}
TEST(DefaultMetricsLoggerTest, LogMetricWithSamplesStatsCounterRecordsMetric) {
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
SamplesStatsCounter values;
values.AddSample(SamplesStatsCounter::StatsSample{
.value = 10,
.time = Clock::GetRealTimeClock()->CurrentTime(),
.metadata =
std::map<std::string, std::string>{{"point_key1", "value1"}}});
values.AddSample(SamplesStatsCounter::StatsSample{
.value = 20,
.time = Clock::GetRealTimeClock()->CurrentTime(),
.metadata =
std::map<std::string, std::string>{{"point_key2", "value2"}}});
logger.LogMetric("metric_name", "test_case_name", values, Unit::kMilliseconds,
ImprovementDirection::kBiggerIsBetter,
std::map<std::string, std::string>{{"key", "value"}});
std::vector<Metric> metrics = logger.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kBiggerIsBetter));
EXPECT_THAT(metric.metric_metadata,
Eq(std::map<std::string, std::string>{{"key", "value"}}));
ASSERT_THAT(metric.time_series.samples, SizeIs(2));
EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metric.time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{{"point_key1", "value1"}}));
EXPECT_THAT(metric.time_series.samples[1].value, Eq(20.0));
EXPECT_THAT(metric.time_series.samples[1].sample_metadata,
Eq(std::map<std::string, std::string>{{"point_key2", "value2"}}));
ASSERT_THAT(metric.stats.mean, absl::optional<double>(15.0));
ASSERT_THAT(metric.stats.stddev, absl::optional<double>(5.0));
ASSERT_THAT(metric.stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.max, absl::optional<double>(20.0));
}
TEST(DefaultMetricsLoggerTest,
LogMetricWithEmptySamplesStatsCounterRecordsEmptyMetric) {
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
SamplesStatsCounter values;
logger.LogMetric("metric_name", "test_case_name", values, Unit::kUnitless,
ImprovementDirection::kBiggerIsBetter, DefaultMetadata());
std::vector<Metric> metrics = logger.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
EXPECT_THAT(metrics[0].name, Eq("metric_name"));
EXPECT_THAT(metrics[0].test_case, Eq("test_case_name"));
EXPECT_THAT(metrics[0].time_series.samples, IsEmpty());
ASSERT_THAT(metrics[0].stats.mean, Eq(absl::nullopt));
ASSERT_THAT(metrics[0].stats.stddev, Eq(absl::nullopt));
ASSERT_THAT(metrics[0].stats.min, Eq(absl::nullopt));
ASSERT_THAT(metrics[0].stats.max, Eq(absl::nullopt));
}
TEST(DefaultMetricsLoggerTest, LogMetricWithStatsRecordsMetric) {
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
Metric::Stats metric_stats{.mean = 15, .stddev = 5, .min = 10, .max = 20};
logger.LogMetric("metric_name", "test_case_name", metric_stats,
Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter,
std::map<std::string, std::string>{{"key", "value"}});
std::vector<Metric> metrics = logger.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kMilliseconds));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kBiggerIsBetter));
EXPECT_THAT(metric.metric_metadata,
Eq(std::map<std::string, std::string>{{"key", "value"}}));
ASSERT_THAT(metric.time_series.samples, IsEmpty());
ASSERT_THAT(metric.stats.mean, absl::optional<double>(15.0));
ASSERT_THAT(metric.stats.stddev, absl::optional<double>(5.0));
ASSERT_THAT(metric.stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.max, absl::optional<double>(20.0));
}
TEST(DefaultMetricsLoggerTest, LogSingleValueMetricRecordsMultipleMetrics) {
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
logger.LogSingleValueMetric("metric_name1", "test_case_name1",
/*value=*/10, Unit::kMilliseconds,
ImprovementDirection::kBiggerIsBetter,
DefaultMetadata());
logger.LogSingleValueMetric("metric_name2", "test_case_name2",
/*value=*/10, Unit::kMilliseconds,
ImprovementDirection::kBiggerIsBetter,
DefaultMetadata());
std::vector<Metric> metrics = logger.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(2));
EXPECT_THAT(metrics[0].name, Eq("metric_name1"));
EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1"));
EXPECT_THAT(metrics[1].name, Eq("metric_name2"));
EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2"));
}
TEST(DefaultMetricsLoggerTest,
LogMetricWithSamplesStatsCounterRecordsMultipleMetrics) {
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
SamplesStatsCounter values;
values.AddSample(SamplesStatsCounter::StatsSample{
.value = 10,
.time = Clock::GetRealTimeClock()->CurrentTime(),
.metadata = DefaultMetadata()});
values.AddSample(SamplesStatsCounter::StatsSample{
.value = 20,
.time = Clock::GetRealTimeClock()->CurrentTime(),
.metadata = DefaultMetadata()});
logger.LogMetric("metric_name1", "test_case_name1", values,
Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter,
DefaultMetadata());
logger.LogMetric("metric_name2", "test_case_name2", values,
Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter,
DefaultMetadata());
std::vector<Metric> metrics = logger.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(2));
EXPECT_THAT(metrics[0].name, Eq("metric_name1"));
EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1"));
EXPECT_THAT(metrics[1].name, Eq("metric_name2"));
EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2"));
}
TEST(DefaultMetricsLoggerTest, LogMetricWithStatsRecordsMultipleMetrics) {
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
Metric::Stats metric_stats{.mean = 15, .stddev = 5, .min = 10, .max = 20};
logger.LogMetric("metric_name1", "test_case_name1", metric_stats,
Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter,
DefaultMetadata());
logger.LogMetric("metric_name2", "test_case_name2", metric_stats,
Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter,
DefaultMetadata());
std::vector<Metric> metrics = logger.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(2));
EXPECT_THAT(metrics[0].name, Eq("metric_name1"));
EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1"));
EXPECT_THAT(metrics[1].name, Eq("metric_name2"));
EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2"));
}
TEST(DefaultMetricsLoggerTest,
LogMetricThroughtAllMethodsAccumulateAllMetrics) {
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
SamplesStatsCounter values;
values.AddSample(SamplesStatsCounter::StatsSample{
.value = 10,
.time = Clock::GetRealTimeClock()->CurrentTime(),
.metadata = DefaultMetadata()});
values.AddSample(SamplesStatsCounter::StatsSample{
.value = 20,
.time = Clock::GetRealTimeClock()->CurrentTime(),
.metadata = DefaultMetadata()});
Metric::Stats metric_stats{.mean = 15, .stddev = 5, .min = 10, .max = 20};
logger.LogSingleValueMetric("metric_name1", "test_case_name1",
/*value=*/10, Unit::kMilliseconds,
ImprovementDirection::kBiggerIsBetter,
DefaultMetadata());
logger.LogMetric("metric_name2", "test_case_name2", values,
Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter,
DefaultMetadata());
logger.LogMetric("metric_name3", "test_case_name3", metric_stats,
Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter,
DefaultMetadata());
std::vector<Metric> metrics = logger.GetCollectedMetrics();
ASSERT_THAT(metrics.size(), Eq(3lu));
EXPECT_THAT(metrics[0].name, Eq("metric_name1"));
EXPECT_THAT(metrics[0].test_case, Eq("test_case_name1"));
EXPECT_THAT(metrics[1].name, Eq("metric_name2"));
EXPECT_THAT(metrics[1].test_case, Eq("test_case_name2"));
EXPECT_THAT(metrics[2].name, Eq("metric_name3"));
EXPECT_THAT(metrics[2].test_case, Eq("test_case_name3"));
}
TEST(DefaultMetricsLoggerTest, AccumulatedMetricsReturnedInCollectedMetrics) {
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
logger.GetMetricsAccumulator()->AddSample(
"metric_name", "test_case_name",
/*value=*/10, Timestamp::Seconds(1),
/*point_metadata=*/std::map<std::string, std::string>{{"key", "value"}});
std::vector<Metric> metrics = logger.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(1));
const Metric& metric = metrics[0];
EXPECT_THAT(metric.name, Eq("metric_name"));
EXPECT_THAT(metric.test_case, Eq("test_case_name"));
EXPECT_THAT(metric.unit, Eq(Unit::kUnitless));
EXPECT_THAT(metric.improvement_direction,
Eq(ImprovementDirection::kNeitherIsBetter));
EXPECT_THAT(metric.metric_metadata, IsEmpty());
ASSERT_THAT(metric.time_series.samples, SizeIs(1));
EXPECT_THAT(metric.time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metric.time_series.samples[0].timestamp,
Eq(Timestamp::Seconds(1)));
EXPECT_THAT(metric.time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{{"key", "value"}}));
ASSERT_THAT(metric.stats.mean, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.stddev, absl::optional<double>(0.0));
ASSERT_THAT(metric.stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metric.stats.max, absl::optional<double>(10.0));
}
TEST(DefaultMetricsLoggerTest,
AccumulatedMetricsReturnedTogetherWithLoggedMetrics) {
DefaultMetricsLogger logger(Clock::GetRealTimeClock());
logger.LogSingleValueMetric(
"metric_name1", "test_case_name1",
/*value=*/10, Unit::kMilliseconds, ImprovementDirection::kBiggerIsBetter,
std::map<std::string, std::string>{{"key_m", "value_m"}});
logger.GetMetricsAccumulator()->AddSample(
"metric_name2", "test_case_name2",
/*value=*/10, Timestamp::Seconds(1),
/*point_metadata=*/
std::map<std::string, std::string>{{"key_s", "value_s"}});
std::vector<Metric> metrics = logger.GetCollectedMetrics();
ASSERT_THAT(metrics, SizeIs(2));
EXPECT_THAT(metrics[0].name, Eq("metric_name2"));
EXPECT_THAT(metrics[0].test_case, Eq("test_case_name2"));
EXPECT_THAT(metrics[0].unit, Eq(Unit::kUnitless));
EXPECT_THAT(metrics[0].improvement_direction,
Eq(ImprovementDirection::kNeitherIsBetter));
EXPECT_THAT(metrics[0].metric_metadata, IsEmpty());
ASSERT_THAT(metrics[0].time_series.samples, SizeIs(1));
EXPECT_THAT(metrics[0].time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metrics[0].time_series.samples[0].timestamp,
Eq(Timestamp::Seconds(1)));
EXPECT_THAT(metrics[0].time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{{"key_s", "value_s"}}));
ASSERT_THAT(metrics[0].stats.mean, absl::optional<double>(10.0));
ASSERT_THAT(metrics[0].stats.stddev, absl::optional<double>(0.0));
ASSERT_THAT(metrics[0].stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metrics[0].stats.max, absl::optional<double>(10.0));
EXPECT_THAT(metrics[1].name, Eq("metric_name1"));
EXPECT_THAT(metrics[1].test_case, Eq("test_case_name1"));
EXPECT_THAT(metrics[1].unit, Eq(Unit::kMilliseconds));
EXPECT_THAT(metrics[1].improvement_direction,
Eq(ImprovementDirection::kBiggerIsBetter));
EXPECT_THAT(metrics[1].metric_metadata,
Eq(std::map<std::string, std::string>{{"key_m", "value_m"}}));
ASSERT_THAT(metrics[1].time_series.samples, SizeIs(1));
EXPECT_THAT(metrics[1].time_series.samples[0].value, Eq(10.0));
EXPECT_THAT(metrics[1].time_series.samples[0].sample_metadata,
Eq(std::map<std::string, std::string>{}));
ASSERT_THAT(metrics[1].stats.mean, absl::optional<double>(10.0));
ASSERT_THAT(metrics[1].stats.stddev, absl::nullopt);
ASSERT_THAT(metrics[1].stats.min, absl::optional<double>(10.0));
ASSERT_THAT(metrics[1].stats.max, absl::optional<double>(10.0));
}
} // namespace
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,166 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/metrics_set_proto_file_exporter.h"
#include <stdio.h>
#include <map>
#include <string>
#include <utility>
#include "api/test/metrics/metric.h"
#include "rtc_base/logging.h"
#include "test/testsupport/file_utils.h"
#if WEBRTC_ENABLE_PROTOBUF
#include "api/test/metrics/proto/metric.pb.h"
#endif
namespace webrtc {
namespace test {
namespace {
#if WEBRTC_ENABLE_PROTOBUF
webrtc::test_metrics::Unit ToProtoUnit(Unit unit) {
switch (unit) {
case Unit::kMilliseconds:
return webrtc::test_metrics::Unit::MILLISECONDS;
case Unit::kPercent:
return webrtc::test_metrics::Unit::PERCENT;
case Unit::kBytes:
return webrtc::test_metrics::Unit::BYTES;
case Unit::kKilobitsPerSecond:
return webrtc::test_metrics::Unit::KILOBITS_PER_SECOND;
case Unit::kHertz:
return webrtc::test_metrics::Unit::HERTZ;
case Unit::kUnitless:
return webrtc::test_metrics::Unit::UNITLESS;
case Unit::kCount:
return webrtc::test_metrics::Unit::COUNT;
}
}
webrtc::test_metrics::ImprovementDirection ToProtoImprovementDirection(
ImprovementDirection direction) {
switch (direction) {
case ImprovementDirection::kBiggerIsBetter:
return webrtc::test_metrics::ImprovementDirection::BIGGER_IS_BETTER;
case ImprovementDirection::kNeitherIsBetter:
return webrtc::test_metrics::ImprovementDirection::NEITHER_IS_BETTER;
case ImprovementDirection::kSmallerIsBetter:
return webrtc::test_metrics::ImprovementDirection::SMALLER_IS_BETTER;
}
}
void SetTimeSeries(
const Metric::TimeSeries& time_series,
webrtc::test_metrics::Metric::TimeSeries* proto_time_series) {
for (const Metric::TimeSeries::Sample& sample : time_series.samples) {
webrtc::test_metrics::Metric::TimeSeries::Sample* proto_sample =
proto_time_series->add_samples();
proto_sample->set_value(sample.value);
proto_sample->set_timestamp_us(sample.timestamp.us());
for (const auto& [key, value] : sample.sample_metadata) {
proto_sample->mutable_sample_metadata()->insert({key, value});
}
}
}
void SetStats(const Metric::Stats& stats,
webrtc::test_metrics::Metric::Stats* proto_stats) {
if (stats.mean.has_value()) {
proto_stats->set_mean(*stats.mean);
}
if (stats.stddev.has_value()) {
proto_stats->set_stddev(*stats.stddev);
}
if (stats.min.has_value()) {
proto_stats->set_min(*stats.min);
}
if (stats.max.has_value()) {
proto_stats->set_max(*stats.max);
}
}
bool WriteMetricsToFile(const std::string& path,
const webrtc::test_metrics::MetricsSet& metrics_set) {
std::string data;
bool ok = metrics_set.SerializeToString(&data);
if (!ok) {
RTC_LOG(LS_ERROR) << "Failed to serialize histogram set to string";
return false;
}
CreateDir(DirName(path));
FILE* output = fopen(path.c_str(), "wb");
if (output == NULL) {
RTC_LOG(LS_ERROR) << "Failed to write to " << path;
return false;
}
size_t written = fwrite(data.c_str(), sizeof(char), data.size(), output);
fclose(output);
if (written != data.size()) {
size_t expected = data.size();
RTC_LOG(LS_ERROR) << "Wrote " << written << ", tried to write " << expected;
return false;
}
return true;
}
#endif // WEBRTC_ENABLE_PROTOBUF
} // namespace
MetricsSetProtoFileExporter::Options::Options(
absl::string_view export_file_path)
: export_file_path(export_file_path) {}
MetricsSetProtoFileExporter::Options::Options(
absl::string_view export_file_path,
bool export_whole_time_series)
: export_file_path(export_file_path),
export_whole_time_series(export_whole_time_series) {}
MetricsSetProtoFileExporter::Options::Options(
absl::string_view export_file_path,
std::map<std::string, std::string> metadata)
: export_file_path(export_file_path), metadata(std::move(metadata)) {}
bool MetricsSetProtoFileExporter::Export(rtc::ArrayView<const Metric> metrics) {
#if WEBRTC_ENABLE_PROTOBUF
webrtc::test_metrics::MetricsSet metrics_set;
for (const auto& [key, value] : options_.metadata) {
metrics_set.mutable_metadata()->insert({key, value});
}
for (const Metric& metric : metrics) {
webrtc::test_metrics::Metric* metric_proto = metrics_set.add_metrics();
metric_proto->set_name(metric.name);
metric_proto->set_unit(ToProtoUnit(metric.unit));
metric_proto->set_improvement_direction(
ToProtoImprovementDirection(metric.improvement_direction));
metric_proto->set_test_case(metric.test_case);
for (const auto& [key, value] : metric.metric_metadata) {
metric_proto->mutable_metric_metadata()->insert({key, value});
}
if (options_.export_whole_time_series) {
SetTimeSeries(metric.time_series, metric_proto->mutable_time_series());
}
SetStats(metric.stats, metric_proto->mutable_stats());
}
return WriteMetricsToFile(options_.export_file_path, metrics_set);
#else
RTC_LOG(LS_ERROR)
<< "Compile with protobuf support to properly use this class";
return false;
#endif
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,59 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef API_TEST_METRICS_METRICS_SET_PROTO_FILE_EXPORTER_H_
#define API_TEST_METRICS_METRICS_SET_PROTO_FILE_EXPORTER_H_
#include <map>
#include <string>
#include "api/array_view.h"
#include "api/test/metrics/metric.h"
#include "api/test/metrics/metrics_exporter.h"
namespace webrtc {
namespace test {
// Exports all collected metrics to the proto file using
// `webrtc::test_metrics::MetricsSet` format.
class MetricsSetProtoFileExporter : public MetricsExporter {
public:
struct Options {
explicit Options(absl::string_view export_file_path);
Options(absl::string_view export_file_path, bool export_whole_time_series);
Options(absl::string_view export_file_path,
std::map<std::string, std::string> metadata);
// File to export proto.
std::string export_file_path;
// If true will write all time series values to the output proto file,
// otherwise will write stats only.
bool export_whole_time_series = true;
// Metadata associated to the whole MetricsSet.
std::map<std::string, std::string> metadata;
};
explicit MetricsSetProtoFileExporter(const Options& options)
: options_(options) {}
MetricsSetProtoFileExporter(const MetricsSetProtoFileExporter&) = delete;
MetricsSetProtoFileExporter& operator=(const MetricsSetProtoFileExporter&) =
delete;
bool Export(rtc::ArrayView<const Metric> metrics) override;
private:
const Options options_;
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_METRICS_METRICS_SET_PROTO_FILE_EXPORTER_H_

View file

@ -0,0 +1,172 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/metrics_set_proto_file_exporter.h"
#include <fstream>
#include <map>
#include <string>
#include <vector>
#include "api/test/metrics/metric.h"
#include "api/test/metrics/proto/metric.pb.h"
#include "api/units/timestamp.h"
#include "rtc_base/protobuf_utils.h"
#include "test/gmock.h"
#include "test/gtest.h"
#include "test/testsupport/file_utils.h"
namespace webrtc {
namespace test {
namespace {
using ::testing::Eq;
using ::testing::Test;
namespace proto = ::webrtc::test_metrics;
std::string ReadFileAsString(const std::string& filename) {
std::ifstream infile(filename, std::ios_base::binary);
auto buffer = std::vector<char>(std::istreambuf_iterator<char>(infile),
std::istreambuf_iterator<char>());
return std::string(buffer.begin(), buffer.end());
}
std::map<std::string, std::string> DefaultMetadata() {
return std::map<std::string, std::string>{{"key", "value"}};
}
Metric::TimeSeries::Sample Sample(double value) {
return Metric::TimeSeries::Sample{.timestamp = Timestamp::Seconds(1),
.value = value,
.sample_metadata = DefaultMetadata()};
}
void AssertSamplesEqual(const proto::Metric::TimeSeries::Sample& actual_sample,
const Metric::TimeSeries::Sample& expected_sample) {
EXPECT_THAT(actual_sample.value(), Eq(expected_sample.value));
EXPECT_THAT(actual_sample.timestamp_us(), Eq(expected_sample.timestamp.us()));
EXPECT_THAT(actual_sample.sample_metadata().size(),
Eq(expected_sample.sample_metadata.size()));
for (const auto& [key, value] : expected_sample.sample_metadata) {
EXPECT_THAT(actual_sample.sample_metadata().at(key), Eq(value));
}
}
class MetricsSetProtoFileExporterTest : public Test {
protected:
~MetricsSetProtoFileExporterTest() override = default;
void SetUp() override {
temp_filename_ = webrtc::test::TempFilename(
webrtc::test::OutputPath(), "metrics_set_proto_file_exporter_test");
}
void TearDown() override {
ASSERT_TRUE(webrtc::test::RemoveFile(temp_filename_));
}
std::string temp_filename_;
};
TEST_F(MetricsSetProtoFileExporterTest, MetricsAreExportedCorrectly) {
MetricsSetProtoFileExporter::Options options(temp_filename_);
MetricsSetProtoFileExporter exporter(options);
Metric metric1{
.name = "test_metric1",
.unit = Unit::kMilliseconds,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case_name1",
.metric_metadata = DefaultMetadata(),
.time_series =
Metric::TimeSeries{.samples = std::vector{Sample(10), Sample(20)}},
.stats =
Metric::Stats{.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}};
Metric metric2{
.name = "test_metric2",
.unit = Unit::kKilobitsPerSecond,
.improvement_direction = ImprovementDirection::kSmallerIsBetter,
.test_case = "test_case_name2",
.metric_metadata = DefaultMetadata(),
.time_series =
Metric::TimeSeries{.samples = std::vector{Sample(20), Sample(40)}},
.stats = Metric::Stats{
.mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}};
ASSERT_TRUE(exporter.Export(std::vector<Metric>{metric1, metric2}));
webrtc::test_metrics::MetricsSet actual_metrics_set;
actual_metrics_set.ParseFromString(ReadFileAsString(temp_filename_));
EXPECT_THAT(actual_metrics_set.metrics().size(), Eq(2));
EXPECT_THAT(actual_metrics_set.metrics(0).name(), Eq("test_metric1"));
EXPECT_THAT(actual_metrics_set.metrics(0).test_case(), Eq("test_case_name1"));
EXPECT_THAT(actual_metrics_set.metrics(0).unit(),
Eq(proto::Unit::MILLISECONDS));
EXPECT_THAT(actual_metrics_set.metrics(0).improvement_direction(),
Eq(proto::ImprovementDirection::BIGGER_IS_BETTER));
EXPECT_THAT(actual_metrics_set.metrics(0).metric_metadata().size(), Eq(1lu));
EXPECT_THAT(actual_metrics_set.metrics(0).metric_metadata().at("key"),
Eq("value"));
EXPECT_THAT(actual_metrics_set.metrics(0).time_series().samples().size(),
Eq(2));
AssertSamplesEqual(actual_metrics_set.metrics(0).time_series().samples(0),
Sample(10.0));
AssertSamplesEqual(actual_metrics_set.metrics(0).time_series().samples(1),
Sample(20.0));
EXPECT_THAT(actual_metrics_set.metrics(0).stats().mean(), Eq(15.0));
EXPECT_THAT(actual_metrics_set.metrics(0).stats().stddev(), Eq(5.0));
EXPECT_THAT(actual_metrics_set.metrics(0).stats().min(), Eq(10.0));
EXPECT_THAT(actual_metrics_set.metrics(0).stats().max(), Eq(20.0));
EXPECT_THAT(actual_metrics_set.metrics(1).name(), Eq("test_metric2"));
EXPECT_THAT(actual_metrics_set.metrics(1).test_case(), Eq("test_case_name2"));
EXPECT_THAT(actual_metrics_set.metrics(1).unit(),
Eq(proto::Unit::KILOBITS_PER_SECOND));
EXPECT_THAT(actual_metrics_set.metrics(1).improvement_direction(),
Eq(proto::ImprovementDirection::SMALLER_IS_BETTER));
EXPECT_THAT(actual_metrics_set.metrics(1).metric_metadata().size(), Eq(1lu));
EXPECT_THAT(actual_metrics_set.metrics(1).metric_metadata().at("key"),
Eq("value"));
EXPECT_THAT(actual_metrics_set.metrics(1).time_series().samples().size(),
Eq(2));
AssertSamplesEqual(actual_metrics_set.metrics(1).time_series().samples(0),
Sample(20.0));
AssertSamplesEqual(actual_metrics_set.metrics(1).time_series().samples(1),
Sample(40.0));
EXPECT_THAT(actual_metrics_set.metrics(1).stats().mean(), Eq(30.0));
EXPECT_THAT(actual_metrics_set.metrics(1).stats().stddev(), Eq(10.0));
EXPECT_THAT(actual_metrics_set.metrics(1).stats().min(), Eq(20.0));
EXPECT_THAT(actual_metrics_set.metrics(1).stats().max(), Eq(40.0));
}
TEST_F(MetricsSetProtoFileExporterTest, NoMetricsSetMetadata) {
MetricsSetProtoFileExporter::Options options(temp_filename_);
MetricsSetProtoFileExporter exporter(options);
ASSERT_TRUE(exporter.Export(std::vector<Metric>{}));
webrtc::test_metrics::MetricsSet actual_metrics_set;
actual_metrics_set.ParseFromString(ReadFileAsString(temp_filename_));
EXPECT_EQ(actual_metrics_set.metadata_size(), 0);
}
TEST_F(MetricsSetProtoFileExporterTest, MetricsSetMetadata) {
MetricsSetProtoFileExporter::Options options(
temp_filename_, {{"a_metadata_key", "a_metadata_value"}});
MetricsSetProtoFileExporter exporter(options);
ASSERT_TRUE(exporter.Export(std::vector<Metric>{}));
webrtc::test_metrics::MetricsSet actual_metrics_set;
actual_metrics_set.ParseFromString(ReadFileAsString(temp_filename_));
EXPECT_EQ(actual_metrics_set.metadata_size(), 1);
EXPECT_EQ(actual_metrics_set.metadata().at("a_metadata_key"),
"a_metadata_value");
}
} // namespace
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,157 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/print_result_proxy_metrics_exporter.h"
#include <string>
#include <unordered_set>
#include "api/array_view.h"
#include "api/test/metrics/metric.h"
#include "test/testsupport/perf_test.h"
namespace webrtc {
namespace test {
namespace {
std::string ToPrintResultUnit(Unit unit) {
switch (unit) {
case Unit::kMilliseconds:
return "msBestFitFormat";
case Unit::kPercent:
return "n%";
case Unit::kBytes:
return "sizeInBytes";
case Unit::kKilobitsPerSecond:
// PrintResults prefer Chrome Perf Dashboard units, which doesn't have
// kpbs units, so we change the unit and value accordingly.
return "bytesPerSecond";
case Unit::kHertz:
return "Hz";
case Unit::kUnitless:
return "unitless";
case Unit::kCount:
return "count";
}
}
double ToPrintResultValue(double value, Unit unit) {
switch (unit) {
case Unit::kKilobitsPerSecond:
// PrintResults prefer Chrome Perf Dashboard units, which doesn't have
// kpbs units, so we change the unit and value accordingly.
return value * 1000 / 8;
default:
return value;
}
}
ImproveDirection ToPrintResultImproveDirection(ImprovementDirection direction) {
switch (direction) {
case ImprovementDirection::kBiggerIsBetter:
return ImproveDirection::kBiggerIsBetter;
case ImprovementDirection::kNeitherIsBetter:
return ImproveDirection::kNone;
case ImprovementDirection::kSmallerIsBetter:
return ImproveDirection::kSmallerIsBetter;
}
}
bool IsEmpty(const Metric::Stats& stats) {
return !stats.mean.has_value() && !stats.stddev.has_value() &&
!stats.min.has_value() && !stats.max.has_value();
}
bool NameEndsWithConnected(const std::string& name) {
static const std::string suffix = "_connected";
return name.size() >= suffix.size() &&
0 == name.compare(name.size() - suffix.size(), suffix.size(), suffix);
}
} // namespace
bool PrintResultProxyMetricsExporter::Export(
rtc::ArrayView<const Metric> metrics) {
static const std::unordered_set<std::string> per_call_metrics{
"actual_encode_bitrate",
"encode_frame_rate",
"harmonic_framerate",
"max_skipped",
"min_psnr_dB",
"retransmission_bitrate",
"sent_packets_loss",
"transmission_bitrate",
"dropped_frames",
"frames_in_flight",
"rendered_frames",
"average_receive_rate",
"average_send_rate",
"bytes_discarded_no_receiver",
"bytes_received",
"bytes_sent",
"packets_discarded_no_receiver",
"packets_received",
"packets_sent",
"payload_bytes_received",
"payload_bytes_sent",
"cpu_usage"};
for (const Metric& metric : metrics) {
if (metric.time_series.samples.empty() && IsEmpty(metric.stats)) {
// If there were no data collected for the metric it is expected that 0
// will be exported, so add 0 to the samples.
PrintResult(metric.name, /*modifier=*/"", metric.test_case,
ToPrintResultValue(0, metric.unit),
ToPrintResultUnit(metric.unit), /*important=*/false,
ToPrintResultImproveDirection(metric.improvement_direction));
continue;
}
if (metric.time_series.samples.empty()) {
PrintResultMeanAndError(
metric.name, /*modifier=*/"", metric.test_case,
ToPrintResultValue(*metric.stats.mean, metric.unit),
ToPrintResultValue(*metric.stats.stddev, metric.unit),
ToPrintResultUnit(metric.unit),
/*important=*/false,
ToPrintResultImproveDirection(metric.improvement_direction));
continue;
}
if (metric.time_series.samples.size() == 1lu &&
(per_call_metrics.count(metric.name) > 0 ||
NameEndsWithConnected(metric.name))) {
// Increase backwards compatibility for 1 value use case.
PrintResult(
metric.name, /*modifier=*/"", metric.test_case,
ToPrintResultValue(metric.time_series.samples[0].value, metric.unit),
ToPrintResultUnit(metric.unit), /*important=*/false,
ToPrintResultImproveDirection(metric.improvement_direction));
continue;
}
SamplesStatsCounter counter;
for (size_t i = 0; i < metric.time_series.samples.size(); ++i) {
counter.AddSample(SamplesStatsCounter::StatsSample{
.value = ToPrintResultValue(metric.time_series.samples[i].value,
metric.unit),
.time = metric.time_series.samples[i].timestamp,
.metadata = metric.time_series.samples[i].sample_metadata});
}
PrintResult(metric.name, /*modifier=*/"", metric.test_case, counter,
ToPrintResultUnit(metric.unit),
/*important=*/false,
ToPrintResultImproveDirection(metric.improvement_direction));
}
return true;
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef API_TEST_METRICS_PRINT_RESULT_PROXY_METRICS_EXPORTER_H_
#define API_TEST_METRICS_PRINT_RESULT_PROXY_METRICS_EXPORTER_H_
#include "api/array_view.h"
#include "api/test/metrics/metric.h"
#include "api/test/metrics/metrics_exporter.h"
namespace webrtc {
namespace test {
// Proxies all exported metrics to the `webrtc::test::PrintResult` API.
class PrintResultProxyMetricsExporter : public MetricsExporter {
public:
~PrintResultProxyMetricsExporter() override = default;
bool Export(rtc::ArrayView<const Metric> metrics) override;
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_METRICS_PRINT_RESULT_PROXY_METRICS_EXPORTER_H_

View file

@ -0,0 +1,177 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/print_result_proxy_metrics_exporter.h"
#include <map>
#include <string>
#include <vector>
#include "api/test/metrics/metric.h"
#include "api/units/timestamp.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace test {
namespace {
using ::testing::TestWithParam;
std::map<std::string, std::string> DefaultMetadata() {
return std::map<std::string, std::string>{{"key", "value"}};
}
Metric::TimeSeries::Sample Sample(double value) {
return Metric::TimeSeries::Sample{.timestamp = Timestamp::Seconds(1),
.value = value,
.sample_metadata = DefaultMetadata()};
}
TEST(PrintResultProxyMetricsExporterTest,
ExportMetricsWithTimeSeriesFormatCorrect) {
Metric metric1{
.name = "test_metric1",
.unit = Unit::kMilliseconds,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case_name1",
.metric_metadata = DefaultMetadata(),
.time_series =
Metric::TimeSeries{.samples = std::vector{Sample(10), Sample(20)}},
.stats =
Metric::Stats{.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}};
Metric metric2{
.name = "test_metric2",
.unit = Unit::kKilobitsPerSecond,
.improvement_direction = ImprovementDirection::kSmallerIsBetter,
.test_case = "test_case_name2",
.metric_metadata = DefaultMetadata(),
.time_series =
Metric::TimeSeries{.samples = std::vector{Sample(20), Sample(40)}},
.stats = Metric::Stats{
.mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}};
testing::internal::CaptureStdout();
PrintResultProxyMetricsExporter exporter;
std::string expected =
"RESULT test_metric1: test_case_name1= {15,5} "
"msBestFitFormat_biggerIsBetter\n"
"RESULT test_metric2: test_case_name2= {3750,1250} "
"bytesPerSecond_smallerIsBetter\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric1, metric2}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(PrintResultProxyMetricsExporterTest,
ExportMetricsTimeSeriesOfSingleValueBackwardCompatibleFormat) {
// This should be printed as {mean, stddev} despite only being a single data
// point.
Metric metric1{
.name = "available_send_bandwidth",
.unit = Unit::kKilobitsPerSecond,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case/alice",
.metric_metadata = DefaultMetadata(),
.time_series = Metric::TimeSeries{.samples = std::vector{Sample(1000)}},
.stats = Metric::Stats{
.mean = 1000.0, .stddev = 0.0, .min = 1000.0, .max = 1000.0}};
// This is a per-call metric that shouldn't have a stddev estimate.
Metric metric2{
.name = "min_psnr_dB",
.unit = Unit::kUnitless,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case/alice-video",
.metric_metadata = DefaultMetadata(),
.time_series = Metric::TimeSeries{.samples = std::vector{Sample(10)}},
.stats =
Metric::Stats{.mean = 10.0, .stddev = 0.0, .min = 10.0, .max = 10.0}};
// This is a per-call metric that shouldn't have a stddev estimate.
Metric metric3{
.name = "alice_connected",
.unit = Unit::kUnitless,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case",
.metric_metadata = DefaultMetadata(),
.time_series = Metric::TimeSeries{.samples = std::vector{Sample(1)}},
.stats =
Metric::Stats{.mean = 1.0, .stddev = 0.0, .min = 1.0, .max = 1.0}};
testing::internal::CaptureStdout();
PrintResultProxyMetricsExporter exporter;
std::string expected =
"RESULT available_send_bandwidth: test_case/alice= {125000,0} "
"bytesPerSecond_biggerIsBetter\n"
"RESULT min_psnr_dB: test_case/alice-video= 10 "
"unitless_biggerIsBetter\n"
"RESULT alice_connected: test_case= 1 "
"unitless_biggerIsBetter\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric1, metric2, metric3}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(PrintResultProxyMetricsExporterTest,
ExportMetricsWithStatsOnlyFormatCorrect) {
Metric metric1{.name = "test_metric1",
.unit = Unit::kMilliseconds,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case_name1",
.metric_metadata = DefaultMetadata(),
.time_series = Metric::TimeSeries{.samples = {}},
.stats = Metric::Stats{
.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}};
Metric metric2{
.name = "test_metric2",
.unit = Unit::kKilobitsPerSecond,
.improvement_direction = ImprovementDirection::kSmallerIsBetter,
.test_case = "test_case_name2",
.metric_metadata = DefaultMetadata(),
.time_series = Metric::TimeSeries{.samples = {}},
.stats = Metric::Stats{
.mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}};
testing::internal::CaptureStdout();
PrintResultProxyMetricsExporter exporter;
std::string expected =
"RESULT test_metric1: test_case_name1= {15,5} "
"msBestFitFormat_biggerIsBetter\n"
"RESULT test_metric2: test_case_name2= {3750,1250} "
"bytesPerSecond_smallerIsBetter\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric1, metric2}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(PrintResultProxyMetricsExporterTest, ExportEmptyMetricOnlyFormatCorrect) {
Metric metric{.name = "test_metric",
.unit = Unit::kMilliseconds,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case_name",
.metric_metadata = DefaultMetadata(),
.time_series = Metric::TimeSeries{.samples = {}},
.stats = Metric::Stats{}};
testing::internal::CaptureStdout();
PrintResultProxyMetricsExporter exporter;
std::string expected =
"RESULT test_metric: test_case_name= 0 "
"msBestFitFormat_biggerIsBetter\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
} // namespace
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,89 @@
/*
* Copyright (c) 2022 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.
*/
syntax = "proto3";
package webrtc.test_metrics;
// Root message of the proto file. Contains collection of all the metrics.
message MetricsSet {
repeated Metric metrics = 1;
// Metadata associated with the whole metrics set.
map<string, string> metadata = 2;
}
enum Unit {
// Default value that has to be defined.
UNDEFINED_UNIT = 0;
// General unitless value. Can be used either for dimensionless quantities
// (ex ratio) or for units not presented in this enum and too specific to add
// to this enum.
UNITLESS = 1;
MILLISECONDS = 2;
PERCENT = 3;
BYTES = 4;
KILOBITS_PER_SECOND = 5;
HERTZ = 6;
COUNT = 7;
}
enum ImprovementDirection {
// Default value that has to be defined.
UNDEFINED_IMPROVEMENT_DIRECTION = 0;
BIGGER_IS_BETTER = 1;
NEITHER_IS_BETTER = 2;
SMALLER_IS_BETTER = 3;
}
// Single performance metric with all related metadata.
message Metric {
// Metric name, for example PSNR, SSIM, decode_time, etc.
string name = 1;
Unit unit = 2;
ImprovementDirection improvement_direction = 3;
// If the metric is generated by a test, this field can be used to specify
// this information.
string test_case = 4;
// Metadata associated with the whole metric.
map<string, string> metric_metadata = 5;
message TimeSeries {
message Sample {
// Timestamp in microseconds associated with a sample. For example,
// the timestamp when the sample was collected.
int64 timestamp_us = 1;
double value = 2;
// Metadata associated with this particular sample.
map<string, string> sample_metadata = 3;
}
// All samples collected for this metric. It can be empty if the Metric
// object only contains `stats`.
repeated Sample samples = 1;
}
// Contains all samples of the metric collected during test execution.
// It can be empty if the user only stores precomputed statistics into
// `stats`.
TimeSeries time_series = 6;
// Contains metric's precomputed statistics based on the `time_series` or if
// `time_series` is omitted (has 0 samples) contains precomputed statistics
// provided by the metric's calculator.
message Stats {
// Sample mean of the metric
// (https://en.wikipedia.org/wiki/Sample_mean_and_covariance).
optional double mean = 1;
// Standard deviation (https://en.wikipedia.org/wiki/Standard_deviation).
// Is undefined if `time_series` contains only a single sample.
optional double stddev = 2;
optional double min = 3;
optional double max = 4;
}
Stats stats = 7;
}

View file

@ -0,0 +1,101 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/stdout_metrics_exporter.h"
#include <stdio.h>
#include <cmath>
#include <string>
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/test/metrics/metric.h"
#include "rtc_base/strings/string_builder.h"
namespace webrtc {
namespace test {
namespace {
// Returns positive integral part of the number.
int64_t IntegralPart(double value) {
return std::lround(std::floor(std::abs(value)));
}
void AppendWithPrecision(double value,
int digits_after_comma,
rtc::StringBuilder& out) {
int64_t multiplier = std::lround(std::pow(10, digits_after_comma));
int64_t integral_part = IntegralPart(value);
double decimal_part = std::abs(value) - integral_part;
// If decimal part has leading zeros then when it will be multiplied on
// `multiplier`, leading zeros will be lost. To preserve them we add "1"
// so then leading digit will be greater than 0 and won't be removed.
//
// During conversion to the string leading digit has to be stripped.
//
// Also due to rounding it may happen that leading digit may be incremented,
// like with `digits_after_comma` 3 number 1.9995 will be rounded to 2. In
// such case this increment has to be propagated to the `integral_part`.
int64_t decimal_holder = std::lround((1 + decimal_part) * multiplier);
if (decimal_holder >= 2 * multiplier) {
// Rounding incremented added leading digit, so we need to transfer 1 to
// integral part.
integral_part++;
decimal_holder -= multiplier;
}
// Remove trailing zeros.
while (decimal_holder % 10 == 0) {
decimal_holder /= 10;
}
// Print serialized number to output.
if (value < 0) {
out << "-";
}
out << integral_part;
if (decimal_holder != 1) {
out << "." << std::to_string(decimal_holder).substr(1, digits_after_comma);
}
}
} // namespace
StdoutMetricsExporter::StdoutMetricsExporter() : output_(stdout) {}
bool StdoutMetricsExporter::Export(rtc::ArrayView<const Metric> metrics) {
for (const Metric& metric : metrics) {
PrintMetric(metric);
}
return true;
}
void StdoutMetricsExporter::PrintMetric(const Metric& metric) {
rtc::StringBuilder value_stream;
value_stream << metric.test_case << " / " << metric.name << "= {mean=";
if (metric.stats.mean.has_value()) {
AppendWithPrecision(*metric.stats.mean, 8, value_stream);
} else {
value_stream << "-";
}
value_stream << ", stddev=";
if (metric.stats.stddev.has_value()) {
AppendWithPrecision(*metric.stats.stddev, 8, value_stream);
} else {
value_stream << "-";
}
value_stream << "} " << ToString(metric.unit) << " ("
<< ToString(metric.improvement_direction) << ")";
fprintf(output_, "RESULT: %s\n", value_stream.str().c_str());
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,41 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef API_TEST_METRICS_STDOUT_METRICS_EXPORTER_H_
#define API_TEST_METRICS_STDOUT_METRICS_EXPORTER_H_
#include "api/array_view.h"
#include "api/test/metrics/metric.h"
#include "api/test/metrics/metrics_exporter.h"
namespace webrtc {
namespace test {
// Exports all collected metrics to stdout.
class StdoutMetricsExporter : public MetricsExporter {
public:
StdoutMetricsExporter();
~StdoutMetricsExporter() override = default;
StdoutMetricsExporter(const StdoutMetricsExporter&) = delete;
StdoutMetricsExporter& operator=(const StdoutMetricsExporter&) = delete;
bool Export(rtc::ArrayView<const Metric> metrics) override;
private:
void PrintMetric(const Metric& metric);
FILE* const output_;
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_METRICS_STDOUT_METRICS_EXPORTER_H_

View file

@ -0,0 +1,211 @@
/*
* Copyright (c) 2022 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 "api/test/metrics/stdout_metrics_exporter.h"
#include <map>
#include <string>
#include <vector>
#include "api/test/metrics/metric.h"
#include "api/units/timestamp.h"
#include "test/gmock.h"
#include "test/gtest.h"
namespace webrtc {
namespace test {
namespace {
using ::testing::TestWithParam;
std::map<std::string, std::string> DefaultMetadata() {
return std::map<std::string, std::string>{{"key", "value"}};
}
Metric::TimeSeries::Sample Sample(double value) {
return Metric::TimeSeries::Sample{.timestamp = Timestamp::Seconds(1),
.value = value,
.sample_metadata = DefaultMetadata()};
}
Metric PsnrForTestFoo(double mean, double stddev) {
return Metric{.name = "psnr",
.unit = Unit::kUnitless,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "foo",
.time_series = Metric::TimeSeries{},
.stats = Metric::Stats{.mean = mean, .stddev = stddev}};
}
TEST(StdoutMetricsExporterTest, ExportMetricFormatCorrect) {
Metric metric1{
.name = "test_metric1",
.unit = Unit::kMilliseconds,
.improvement_direction = ImprovementDirection::kBiggerIsBetter,
.test_case = "test_case_name1",
.metric_metadata = DefaultMetadata(),
.time_series =
Metric::TimeSeries{.samples = std::vector{Sample(10), Sample(20)}},
.stats =
Metric::Stats{.mean = 15.0, .stddev = 5.0, .min = 10.0, .max = 20.0}};
Metric metric2{
.name = "test_metric2",
.unit = Unit::kKilobitsPerSecond,
.improvement_direction = ImprovementDirection::kSmallerIsBetter,
.test_case = "test_case_name2",
.metric_metadata = DefaultMetadata(),
.time_series =
Metric::TimeSeries{.samples = std::vector{Sample(20), Sample(40)}},
.stats = Metric::Stats{
.mean = 30.0, .stddev = 10.0, .min = 20.0, .max = 40.0}};
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
std::string expected =
"RESULT: test_case_name1 / test_metric1= "
"{mean=15, stddev=5} Milliseconds (BiggerIsBetter)\n"
"RESULT: test_case_name2 / test_metric2= "
"{mean=30, stddev=10} KilobitsPerSecond (SmallerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric1, metric2}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(StdoutMetricsExporterNumberFormatTest, PositiveNumberMaxPrecision) {
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
Metric metric = PsnrForTestFoo(15.00000001, 0.00000001);
std::string expected =
"RESULT: foo / psnr= "
"{mean=15.00000001, stddev=0.00000001} Unitless (BiggerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(StdoutMetricsExporterNumberFormatTest,
PositiveNumberTrailingZeroNotAdded) {
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
Metric metric = PsnrForTestFoo(15.12345, 0.12);
std::string expected =
"RESULT: foo / psnr= "
"{mean=15.12345, stddev=0.12} Unitless (BiggerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(StdoutMetricsExporterNumberFormatTest,
PositiveNumberTrailingZeroAreRemoved) {
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
Metric metric = PsnrForTestFoo(15.123450000, 0.120000000);
std::string expected =
"RESULT: foo / psnr= "
"{mean=15.12345, stddev=0.12} Unitless (BiggerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(StdoutMetricsExporterNumberFormatTest,
PositiveNumberRoundsUpOnPrecisionCorrectly) {
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
Metric metric = PsnrForTestFoo(15.000000009, 0.999999999);
std::string expected =
"RESULT: foo / psnr= "
"{mean=15.00000001, stddev=1} Unitless (BiggerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(StdoutMetricsExporterNumberFormatTest,
PositiveNumberRoundsDownOnPrecisionCorrectly) {
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
Metric metric = PsnrForTestFoo(15.0000000049, 0.9999999949);
std::string expected =
"RESULT: foo / psnr= "
"{mean=15, stddev=0.99999999} Unitless (BiggerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(StdoutMetricsExporterNumberFormatTest, NegativeNumberMaxPrecision) {
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
Metric metric = PsnrForTestFoo(-15.00000001, -0.00000001);
std::string expected =
"RESULT: foo / psnr= "
"{mean=-15.00000001, stddev=-0.00000001} Unitless (BiggerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(StdoutMetricsExporterNumberFormatTest,
NegativeNumberTrailingZeroNotAdded) {
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
Metric metric = PsnrForTestFoo(-15.12345, -0.12);
std::string expected =
"RESULT: foo / psnr= "
"{mean=-15.12345, stddev=-0.12} Unitless (BiggerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(StdoutMetricsExporterNumberFormatTest,
NegativeNumberTrailingZeroAreRemoved) {
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
Metric metric = PsnrForTestFoo(-15.123450000, -0.120000000);
std::string expected =
"RESULT: foo / psnr= "
"{mean=-15.12345, stddev=-0.12} Unitless (BiggerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(StdoutMetricsExporterNumberFormatTest,
NegativeNumberRoundsUpOnPrecisionCorrectly) {
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
Metric metric = PsnrForTestFoo(-15.000000009, -0.999999999);
std::string expected =
"RESULT: foo / psnr= "
"{mean=-15.00000001, stddev=-1} Unitless (BiggerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
TEST(StdoutMetricsExporterNumberFormatTest,
NegativeNumberRoundsDownOnPrecisionCorrectly) {
testing::internal::CaptureStdout();
StdoutMetricsExporter exporter;
Metric metric = PsnrForTestFoo(-15.0000000049, -0.9999999949);
std::string expected =
"RESULT: foo / psnr= "
"{mean=-15, stddev=-0.99999999} Unitless (BiggerIsBetter)\n";
EXPECT_TRUE(exporter.Export(std::vector<Metric>{metric}));
EXPECT_EQ(expected, testing::internal::GetCapturedStdout());
}
} // namespace
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,64 @@
/*
* Copyright 2021 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.
*/
#ifndef API_TEST_MOCK_ASYNC_DNS_RESOLVER_H_
#define API_TEST_MOCK_ASYNC_DNS_RESOLVER_H_
#include <functional>
#include <memory>
#include "api/async_dns_resolver.h"
#include "test/gmock.h"
namespace webrtc {
class MockAsyncDnsResolverResult : public AsyncDnsResolverResult {
public:
MOCK_METHOD(bool,
GetResolvedAddress,
(int, rtc::SocketAddress*),
(const, override));
MOCK_METHOD(int, GetError, (), (const, override));
};
class MockAsyncDnsResolver : public AsyncDnsResolverInterface {
public:
MOCK_METHOD(void,
Start,
(const rtc::SocketAddress&, absl::AnyInvocable<void()>),
(override));
MOCK_METHOD(void,
Start,
(const rtc::SocketAddress&,
int family,
absl::AnyInvocable<void()>),
(override));
MOCK_METHOD(AsyncDnsResolverResult&, result, (), (const, override));
};
class MockAsyncDnsResolverFactory : public AsyncDnsResolverFactoryInterface {
public:
MOCK_METHOD(std::unique_ptr<webrtc::AsyncDnsResolverInterface>,
CreateAndResolve,
(const rtc::SocketAddress&, absl::AnyInvocable<void()>),
(override));
MOCK_METHOD(std::unique_ptr<webrtc::AsyncDnsResolverInterface>,
CreateAndResolve,
(const rtc::SocketAddress&, int, absl::AnyInvocable<void()>),
(override));
MOCK_METHOD(std::unique_ptr<webrtc::AsyncDnsResolverInterface>,
Create,
(),
(override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_ASYNC_DNS_RESOLVER_H_

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2016 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.
*/
#ifndef API_TEST_MOCK_AUDIO_MIXER_H_
#define API_TEST_MOCK_AUDIO_MIXER_H_
#include "api/audio/audio_mixer.h"
#include "test/gmock.h"
namespace webrtc {
namespace test {
class MockAudioMixer : public AudioMixer {
public:
MOCK_METHOD(bool, AddSource, (Source*), (override));
MOCK_METHOD(void, RemoveSource, (Source*), (override));
MOCK_METHOD(void, Mix, (size_t number_of_channels, AudioFrame*), (override));
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_MOCK_AUDIO_MIXER_H_

View file

@ -0,0 +1,44 @@
/*
* Copyright 2021 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.
*/
#ifndef API_TEST_MOCK_AUDIO_SINK_H_
#define API_TEST_MOCK_AUDIO_SINK_H_
#include "absl/types/optional.h"
#include "api/media_stream_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockAudioSink : public webrtc::AudioTrackSinkInterface {
public:
MOCK_METHOD(void,
OnData,
(const void* audio_data,
int bits_per_sample,
int sample_rate,
size_t number_of_channels,
size_t number_of_frames),
(override));
MOCK_METHOD(void,
OnData,
(const void* audio_data,
int bits_per_sample,
int sample_rate,
size_t number_of_channels,
size_t number_of_frames,
absl::optional<int64_t> absolute_capture_timestamp_ms),
(override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_AUDIO_SINK_H_

View file

@ -0,0 +1,66 @@
/*
* Copyright 2020 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.
*/
#ifndef API_TEST_MOCK_DATA_CHANNEL_H_
#define API_TEST_MOCK_DATA_CHANNEL_H_
#include <string>
#include "api/data_channel_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockDataChannelInterface
: public rtc::RefCountedObject<webrtc::DataChannelInterface> {
public:
static rtc::scoped_refptr<MockDataChannelInterface> Create() {
return rtc::scoped_refptr<MockDataChannelInterface>(
new MockDataChannelInterface());
}
MOCK_METHOD(void,
RegisterObserver,
(DataChannelObserver * observer),
(override));
MOCK_METHOD(void, UnregisterObserver, (), (override));
MOCK_METHOD(std::string, label, (), (const, override));
MOCK_METHOD(bool, reliable, (), (const, override));
MOCK_METHOD(bool, ordered, (), (const, override));
MOCK_METHOD(uint16_t, maxRetransmitTime, (), (const, override));
MOCK_METHOD(uint16_t, maxRetransmits, (), (const, override));
MOCK_METHOD(absl::optional<int>, maxRetransmitsOpt, (), (const, override));
MOCK_METHOD(absl::optional<int>, maxPacketLifeTime, (), (const, override));
MOCK_METHOD(std::string, protocol, (), (const, override));
MOCK_METHOD(bool, negotiated, (), (const, override));
MOCK_METHOD(int, id, (), (const, override));
MOCK_METHOD(Priority, priority, (), (const, override));
MOCK_METHOD(DataState, state, (), (const, override));
MOCK_METHOD(RTCError, error, (), (const, override));
MOCK_METHOD(uint32_t, messages_sent, (), (const, override));
MOCK_METHOD(uint64_t, bytes_sent, (), (const, override));
MOCK_METHOD(uint32_t, messages_received, (), (const, override));
MOCK_METHOD(uint64_t, bytes_received, (), (const, override));
MOCK_METHOD(uint64_t, buffered_amount, (), (const, override));
MOCK_METHOD(void, Close, (), (override));
MOCK_METHOD(bool, Send, (const DataBuffer& buffer), (override));
MOCK_METHOD(void,
SendAsync,
(DataBuffer buffer,
absl::AnyInvocable<void(RTCError) &&> on_complete),
(override));
protected:
MockDataChannelInterface() = default;
};
} // namespace webrtc
#endif // API_TEST_MOCK_DATA_CHANNEL_H_

View file

@ -0,0 +1,56 @@
/*
* Copyright 2022 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.
*/
#ifndef API_TEST_MOCK_DTMF_SENDER_H_
#define API_TEST_MOCK_DTMF_SENDER_H_
#include <string>
#include "api/dtmf_sender_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockDtmfSenderObserver : public DtmfSenderObserverInterface {
public:
MOCK_METHOD(void,
OnToneChange,
(const std::string&, const std::string&),
(override));
MOCK_METHOD(void, OnToneChange, (const std::string&), (override));
};
static_assert(!std::is_abstract_v<MockDtmfSenderObserver>, "");
class MockDtmfSender : public DtmfSenderInterface {
public:
static rtc::scoped_refptr<MockDtmfSender> Create() {
return rtc::make_ref_counted<MockDtmfSender>();
}
MOCK_METHOD(void,
RegisterObserver,
(DtmfSenderObserverInterface * observer),
(override));
MOCK_METHOD(void, UnregisterObserver, (), (override));
MOCK_METHOD(bool, CanInsertDtmf, (), (override));
MOCK_METHOD(std::string, tones, (), (const override));
MOCK_METHOD(int, duration, (), (const override));
MOCK_METHOD(int, inter_tone_gap, (), (const override));
protected:
MockDtmfSender() = default;
};
static_assert(!std::is_abstract_v<rtc::RefCountedObject<MockDtmfSender>>, "");
} // namespace webrtc
#endif // API_TEST_MOCK_DTMF_SENDER_H_

View file

@ -0,0 +1,42 @@
/*
* Copyright 2018 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.
*/
#ifndef API_TEST_MOCK_ENCODER_SELECTOR_H_
#define API_TEST_MOCK_ENCODER_SELECTOR_H_
#include "api/video_codecs/video_encoder_factory.h"
#include "test/gmock.h"
namespace webrtc {
class MockEncoderSelector
: public VideoEncoderFactory::EncoderSelectorInterface {
public:
MOCK_METHOD(void,
OnCurrentEncoder,
(const SdpVideoFormat& format),
(override));
MOCK_METHOD(absl::optional<SdpVideoFormat>,
OnAvailableBitrate,
(const DataRate& rate),
(override));
MOCK_METHOD(absl::optional<SdpVideoFormat>,
OnResolutionChange,
(const RenderResolution& resolution),
(override));
MOCK_METHOD(absl::optional<SdpVideoFormat>, OnEncoderBroken, (), (override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_ENCODER_SELECTOR_H_

View file

@ -0,0 +1,26 @@
/*
* Copyright 2019 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.
*/
#ifndef API_TEST_MOCK_FEC_CONTROLLER_OVERRIDE_H_
#define API_TEST_MOCK_FEC_CONTROLLER_OVERRIDE_H_
#include "api/fec_controller_override.h"
#include "test/gmock.h"
namespace webrtc {
class MockFecControllerOverride : public FecControllerOverride {
public:
MOCK_METHOD(void, SetFecAllowed, (bool fec_allowed), (override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_FEC_CONTROLLER_OVERRIDE_H_

View file

@ -0,0 +1,40 @@
/*
* Copyright 2018 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.
*/
#ifndef API_TEST_MOCK_FRAME_DECRYPTOR_H_
#define API_TEST_MOCK_FRAME_DECRYPTOR_H_
#include <vector>
#include "api/crypto/frame_decryptor_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockFrameDecryptor : public FrameDecryptorInterface {
public:
MOCK_METHOD(Result,
Decrypt,
(cricket::MediaType,
const std::vector<uint32_t>&,
rtc::ArrayView<const uint8_t>,
rtc::ArrayView<const uint8_t>,
rtc::ArrayView<uint8_t>),
(override));
MOCK_METHOD(size_t,
GetMaxPlaintextByteSize,
(cricket::MediaType, size_t encrypted_frame_size),
(override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_FRAME_DECRYPTOR_H_

View file

@ -0,0 +1,39 @@
/*
* Copyright 2018 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.
*/
#ifndef API_TEST_MOCK_FRAME_ENCRYPTOR_H_
#define API_TEST_MOCK_FRAME_ENCRYPTOR_H_
#include "api/crypto/frame_encryptor_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockFrameEncryptor : public FrameEncryptorInterface {
public:
MOCK_METHOD(int,
Encrypt,
(cricket::MediaType,
uint32_t,
rtc::ArrayView<const uint8_t>,
rtc::ArrayView<const uint8_t>,
rtc::ArrayView<uint8_t>,
size_t*),
(override));
MOCK_METHOD(size_t,
GetMaxCiphertextByteSize,
(cricket::MediaType media_type, size_t frame_size),
(override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_FRAME_ENCRYPTOR_H_

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef API_TEST_MOCK_FRAME_TRANSFORMER_H_
#define API_TEST_MOCK_FRAME_TRANSFORMER_H_
#include <memory>
#include <vector>
#include "api/frame_transformer_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockFrameTransformer : public FrameTransformerInterface {
public:
MOCK_METHOD(void,
Transform,
(std::unique_ptr<TransformableFrameInterface>),
(override));
MOCK_METHOD(void,
RegisterTransformedFrameCallback,
(rtc::scoped_refptr<TransformedFrameCallback>),
(override));
MOCK_METHOD(void,
RegisterTransformedFrameSinkCallback,
(rtc::scoped_refptr<TransformedFrameCallback>, uint32_t),
(override));
MOCK_METHOD(void, UnregisterTransformedFrameCallback, (), (override));
MOCK_METHOD(void,
UnregisterTransformedFrameSinkCallback,
(uint32_t),
(override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_FRAME_TRANSFORMER_H_

View file

@ -0,0 +1,134 @@
/*
* Copyright 2020 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.
*/
#ifndef API_TEST_MOCK_MEDIA_STREAM_INTERFACE_H_
#define API_TEST_MOCK_MEDIA_STREAM_INTERFACE_H_
#include <string>
#include "api/media_stream_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockAudioSource : public rtc::RefCountedObject<AudioSourceInterface> {
public:
static rtc::scoped_refptr<MockAudioSource> Create() {
return rtc::scoped_refptr<MockAudioSource>(new MockAudioSource());
}
MOCK_METHOD(void,
RegisterObserver,
(ObserverInterface * observer),
(override));
MOCK_METHOD(void,
UnregisterObserver,
(ObserverInterface * observer),
(override));
MOCK_METHOD(SourceState, state, (), (const, override));
MOCK_METHOD(bool, remote, (), (const, override));
MOCK_METHOD(void, SetVolume, (double volume), (override));
MOCK_METHOD(void,
RegisterAudioObserver,
(AudioObserver * observer),
(override));
MOCK_METHOD(void,
UnregisterAudioObserver,
(AudioObserver * observer),
(override));
MOCK_METHOD(void, AddSink, (AudioTrackSinkInterface * sink), (override));
MOCK_METHOD(void, RemoveSink, (AudioTrackSinkInterface * sink), (override));
MOCK_METHOD(const cricket::AudioOptions, options, (), (const, override));
private:
MockAudioSource() = default;
};
class MockAudioTrack : public rtc::RefCountedObject<AudioTrackInterface> {
public:
static rtc::scoped_refptr<MockAudioTrack> Create() {
return rtc::scoped_refptr<MockAudioTrack>(new MockAudioTrack());
}
MOCK_METHOD(void,
RegisterObserver,
(ObserverInterface * observer),
(override));
MOCK_METHOD(void,
UnregisterObserver,
(ObserverInterface * observer),
(override));
MOCK_METHOD(std::string, kind, (), (const, override));
MOCK_METHOD(std::string, id, (), (const, override));
MOCK_METHOD(bool, enabled, (), (const, override));
MOCK_METHOD(bool, set_enabled, (bool enable), (override));
MOCK_METHOD(TrackState, state, (), (const, override));
MOCK_METHOD(AudioSourceInterface*, GetSource, (), (const, override));
MOCK_METHOD(void, AddSink, (AudioTrackSinkInterface * sink), (override));
MOCK_METHOD(void, RemoveSink, (AudioTrackSinkInterface * sink), (override));
MOCK_METHOD(bool, GetSignalLevel, (int* level), (override));
MOCK_METHOD(rtc::scoped_refptr<AudioProcessorInterface>,
GetAudioProcessor,
(),
(override));
private:
MockAudioTrack() = default;
};
class MockMediaStream : public MediaStreamInterface {
public:
MOCK_METHOD(std::string, id, (), (const override));
MOCK_METHOD(AudioTrackVector, GetAudioTracks, (), (override));
MOCK_METHOD(VideoTrackVector, GetVideoTracks, (), (override));
MOCK_METHOD(rtc::scoped_refptr<AudioTrackInterface>,
FindAudioTrack,
(const std::string& track_id),
(override));
MOCK_METHOD(rtc::scoped_refptr<VideoTrackInterface>,
FindVideoTrack,
(const std::string& track_id),
(override));
MOCK_METHOD(bool,
AddTrack,
(rtc::scoped_refptr<AudioTrackInterface> track),
(override));
MOCK_METHOD(bool,
AddTrack,
(rtc::scoped_refptr<VideoTrackInterface> track),
(override));
MOCK_METHOD(bool,
RemoveTrack,
(rtc::scoped_refptr<AudioTrackInterface> track),
(override));
MOCK_METHOD(bool,
RemoveTrack,
(rtc::scoped_refptr<VideoTrackInterface> track),
(override));
// Old AddTrack/RemoveTrack methods - slated for removal
MOCK_METHOD(bool, AddTrack, (AudioTrackInterface * track), (override));
MOCK_METHOD(bool, AddTrack, (VideoTrackInterface * track), (override));
MOCK_METHOD(bool, RemoveTrack, (AudioTrackInterface * track), (override));
MOCK_METHOD(bool, RemoveTrack, (VideoTrackInterface * track), (override));
MOCK_METHOD(void,
RegisterObserver,
(ObserverInterface * observer),
(override));
MOCK_METHOD(void,
UnregisterObserver,
(ObserverInterface * observer),
(override));
};
static_assert(!std::is_abstract_v<rtc::RefCountedObject<MockMediaStream>>, "");
} // namespace webrtc
#endif // API_TEST_MOCK_MEDIA_STREAM_INTERFACE_H_

View file

@ -0,0 +1,49 @@
/*
* Copyright (c) 2022 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.
*/
#ifndef API_TEST_MOCK_PACKET_SOCKET_FACTORY_H_
#define API_TEST_MOCK_PACKET_SOCKET_FACTORY_H_
#include <memory>
#include <string>
#include "api/packet_socket_factory.h"
#include "test/gmock.h"
namespace rtc {
class MockPacketSocketFactory : public PacketSocketFactory {
public:
MOCK_METHOD(AsyncPacketSocket*,
CreateUdpSocket,
(const SocketAddress&, uint16_t, uint16_t),
(override));
MOCK_METHOD(AsyncListenSocket*,
CreateServerTcpSocket,
(const SocketAddress&, uint16_t, uint16_t, int opts),
(override));
MOCK_METHOD(AsyncPacketSocket*,
CreateClientTcpSocket,
(const SocketAddress& local_address,
const SocketAddress&,
const ProxyInfo&,
const std::string&,
const PacketSocketTcpOptions&),
(override));
MOCK_METHOD(std::unique_ptr<webrtc::AsyncDnsResolverInterface>,
CreateAsyncDnsResolver,
(),
(override));
};
static_assert(!std::is_abstract_v<MockPacketSocketFactory>, "");
} // namespace rtc
#endif // API_TEST_MOCK_PACKET_SOCKET_FACTORY_H_

View file

@ -0,0 +1,86 @@
/*
* Copyright 2020 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.
*/
#ifndef API_TEST_MOCK_PEER_CONNECTION_FACTORY_INTERFACE_H_
#define API_TEST_MOCK_PEER_CONNECTION_FACTORY_INTERFACE_H_
#include <memory>
#include <string>
#include "api/peer_connection_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockPeerConnectionFactoryInterface
: public rtc::RefCountedObject<webrtc::PeerConnectionFactoryInterface> {
public:
static rtc::scoped_refptr<MockPeerConnectionFactoryInterface> Create() {
return rtc::scoped_refptr<MockPeerConnectionFactoryInterface>(
new MockPeerConnectionFactoryInterface());
}
MOCK_METHOD(void, SetOptions, (const Options&), (override));
MOCK_METHOD(rtc::scoped_refptr<PeerConnectionInterface>,
CreatePeerConnection,
(const PeerConnectionInterface::RTCConfiguration&,
PeerConnectionDependencies),
(override));
MOCK_METHOD(RTCErrorOr<rtc::scoped_refptr<PeerConnectionInterface>>,
CreatePeerConnectionOrError,
(const PeerConnectionInterface::RTCConfiguration&,
PeerConnectionDependencies),
(override));
MOCK_METHOD(rtc::scoped_refptr<PeerConnectionInterface>,
CreatePeerConnection,
(const PeerConnectionInterface::RTCConfiguration&,
std::unique_ptr<cricket::PortAllocator>,
std::unique_ptr<rtc::RTCCertificateGeneratorInterface>,
PeerConnectionObserver*),
(override));
MOCK_METHOD(RtpCapabilities,
GetRtpSenderCapabilities,
(cricket::MediaType),
(const, override));
MOCK_METHOD(RtpCapabilities,
GetRtpReceiverCapabilities,
(cricket::MediaType),
(const, override));
MOCK_METHOD(rtc::scoped_refptr<MediaStreamInterface>,
CreateLocalMediaStream,
(const std::string&),
(override));
MOCK_METHOD(rtc::scoped_refptr<AudioSourceInterface>,
CreateAudioSource,
(const cricket::AudioOptions&),
(override));
MOCK_METHOD(rtc::scoped_refptr<VideoTrackInterface>,
CreateVideoTrack,
(const std::string&, VideoTrackSourceInterface*),
(override));
MOCK_METHOD(rtc::scoped_refptr<VideoTrackInterface>,
CreateVideoTrack,
(rtc::scoped_refptr<VideoTrackSourceInterface>,
absl::string_view),
(override));
MOCK_METHOD(rtc::scoped_refptr<AudioTrackInterface>,
CreateAudioTrack,
(const std::string&, AudioSourceInterface*),
(override));
MOCK_METHOD(bool, StartAecDump, (FILE*, int64_t), (override));
MOCK_METHOD(void, StopAecDump, (), (override));
protected:
MockPeerConnectionFactoryInterface() = default;
};
} // namespace webrtc
#endif // API_TEST_MOCK_PEER_CONNECTION_FACTORY_INTERFACE_H_

View file

@ -0,0 +1,217 @@
/*
* Copyright 2016 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.
*/
#ifndef API_TEST_MOCK_PEERCONNECTIONINTERFACE_H_
#define API_TEST_MOCK_PEERCONNECTIONINTERFACE_H_
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <vector>
#include "api/peer_connection_interface.h"
#include "api/scoped_refptr.h"
#include "api/sctp_transport_interface.h"
#include "rtc_base/ref_counted_object.h"
#include "test/gmock.h"
namespace webrtc {
class MockPeerConnectionInterface : public webrtc::PeerConnectionInterface {
public:
static rtc::scoped_refptr<MockPeerConnectionInterface> Create() {
return rtc::make_ref_counted<MockPeerConnectionInterface>();
}
// PeerConnectionInterface
MOCK_METHOD(rtc::scoped_refptr<StreamCollectionInterface>,
local_streams,
(),
(override));
MOCK_METHOD(rtc::scoped_refptr<StreamCollectionInterface>,
remote_streams,
(),
(override));
MOCK_METHOD(bool, AddStream, (MediaStreamInterface*), (override));
MOCK_METHOD(void, RemoveStream, (MediaStreamInterface*), (override));
MOCK_METHOD(RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>>,
AddTrack,
(rtc::scoped_refptr<MediaStreamTrackInterface>,
const std::vector<std::string>&),
(override));
MOCK_METHOD(RTCErrorOr<rtc::scoped_refptr<RtpSenderInterface>>,
AddTrack,
(rtc::scoped_refptr<MediaStreamTrackInterface>,
const std::vector<std::string>&,
const std::vector<RtpEncodingParameters>&),
(override));
MOCK_METHOD(RTCError,
RemoveTrackOrError,
(rtc::scoped_refptr<RtpSenderInterface>),
(override));
MOCK_METHOD(RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>,
AddTransceiver,
(rtc::scoped_refptr<MediaStreamTrackInterface>),
(override));
MOCK_METHOD(RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>,
AddTransceiver,
(rtc::scoped_refptr<MediaStreamTrackInterface>,
const RtpTransceiverInit&),
(override));
MOCK_METHOD(RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>,
AddTransceiver,
(cricket::MediaType),
(override));
MOCK_METHOD(RTCErrorOr<rtc::scoped_refptr<RtpTransceiverInterface>>,
AddTransceiver,
(cricket::MediaType, const RtpTransceiverInit&),
(override));
MOCK_METHOD(rtc::scoped_refptr<RtpSenderInterface>,
CreateSender,
(const std::string&, const std::string&),
(override));
MOCK_METHOD(std::vector<rtc::scoped_refptr<RtpSenderInterface>>,
GetSenders,
(),
(const, override));
MOCK_METHOD(std::vector<rtc::scoped_refptr<RtpReceiverInterface>>,
GetReceivers,
(),
(const, override));
MOCK_METHOD(std::vector<rtc::scoped_refptr<RtpTransceiverInterface>>,
GetTransceivers,
(),
(const, override));
MOCK_METHOD(bool,
GetStats,
(StatsObserver*, MediaStreamTrackInterface*, StatsOutputLevel),
(override));
MOCK_METHOD(void, GetStats, (RTCStatsCollectorCallback*), (override));
MOCK_METHOD(void,
GetStats,
(rtc::scoped_refptr<RtpSenderInterface>,
rtc::scoped_refptr<RTCStatsCollectorCallback>),
(override));
MOCK_METHOD(void,
GetStats,
(rtc::scoped_refptr<RtpReceiverInterface>,
rtc::scoped_refptr<RTCStatsCollectorCallback>),
(override));
MOCK_METHOD(void, ClearStatsCache, (), (override));
MOCK_METHOD(rtc::scoped_refptr<SctpTransportInterface>,
GetSctpTransport,
(),
(const, override));
MOCK_METHOD(RTCErrorOr<rtc::scoped_refptr<DataChannelInterface>>,
CreateDataChannelOrError,
(const std::string&, const DataChannelInit*),
(override));
MOCK_METHOD(const SessionDescriptionInterface*,
local_description,
(),
(const, override));
MOCK_METHOD(const SessionDescriptionInterface*,
remote_description,
(),
(const, override));
MOCK_METHOD(const SessionDescriptionInterface*,
current_local_description,
(),
(const, override));
MOCK_METHOD(const SessionDescriptionInterface*,
current_remote_description,
(),
(const, override));
MOCK_METHOD(const SessionDescriptionInterface*,
pending_local_description,
(),
(const, override));
MOCK_METHOD(const SessionDescriptionInterface*,
pending_remote_description,
(),
(const, override));
MOCK_METHOD(void, RestartIce, (), (override));
MOCK_METHOD(void,
CreateOffer,
(CreateSessionDescriptionObserver*, const RTCOfferAnswerOptions&),
(override));
MOCK_METHOD(void,
CreateAnswer,
(CreateSessionDescriptionObserver*, const RTCOfferAnswerOptions&),
(override));
MOCK_METHOD(void,
SetLocalDescription,
(SetSessionDescriptionObserver*, SessionDescriptionInterface*),
(override));
MOCK_METHOD(void,
SetRemoteDescription,
(SetSessionDescriptionObserver*, SessionDescriptionInterface*),
(override));
MOCK_METHOD(void,
SetRemoteDescription,
(std::unique_ptr<SessionDescriptionInterface>,
rtc::scoped_refptr<SetRemoteDescriptionObserverInterface>),
(override));
MOCK_METHOD(PeerConnectionInterface::RTCConfiguration,
GetConfiguration,
(),
(override));
MOCK_METHOD(RTCError,
SetConfiguration,
(const PeerConnectionInterface::RTCConfiguration&),
(override));
MOCK_METHOD(bool,
AddIceCandidate,
(const IceCandidateInterface*),
(override));
MOCK_METHOD(bool,
RemoveIceCandidates,
(const std::vector<cricket::Candidate>&),
(override));
MOCK_METHOD(RTCError, SetBitrate, (const BitrateSettings&), (override));
MOCK_METHOD(void,
ReconfigureBandwidthEstimation,
(const BandwidthEstimationSettings&),
(override));
MOCK_METHOD(void, SetAudioPlayout, (bool), (override));
MOCK_METHOD(void, SetAudioRecording, (bool), (override));
MOCK_METHOD(rtc::scoped_refptr<DtlsTransportInterface>,
LookupDtlsTransportByMid,
(const std::string&),
(override));
MOCK_METHOD(SignalingState, signaling_state, (), (override));
MOCK_METHOD(IceConnectionState, ice_connection_state, (), (override));
MOCK_METHOD(IceConnectionState,
standardized_ice_connection_state,
(),
(override));
MOCK_METHOD(PeerConnectionState, peer_connection_state, (), (override));
MOCK_METHOD(IceGatheringState, ice_gathering_state, (), (override));
MOCK_METHOD(absl::optional<bool>, can_trickle_ice_candidates, (), (override));
MOCK_METHOD(bool,
StartRtcEventLog,
(std::unique_ptr<RtcEventLogOutput>, int64_t),
(override));
MOCK_METHOD(bool,
StartRtcEventLog,
(std::unique_ptr<RtcEventLogOutput>),
(override));
MOCK_METHOD(void, StopRtcEventLog, (), (override));
MOCK_METHOD(void, Close, (), (override));
};
static_assert(
!std::is_abstract_v<rtc::RefCountedObject<MockPeerConnectionInterface>>,
"");
} // namespace webrtc
#endif // API_TEST_MOCK_PEERCONNECTIONINTERFACE_H_

View file

@ -0,0 +1,87 @@
/*
* Copyright 2020 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.
*/
#ifndef API_TEST_MOCK_RTP_TRANSCEIVER_H_
#define API_TEST_MOCK_RTP_TRANSCEIVER_H_
#include <string>
#include <vector>
#include "api/rtp_transceiver_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockRtpTransceiver : public RtpTransceiverInterface {
public:
MockRtpTransceiver() = default;
static rtc::scoped_refptr<MockRtpTransceiver> Create() {
return rtc::make_ref_counted<MockRtpTransceiver>();
}
MOCK_METHOD(cricket::MediaType, media_type, (), (const, override));
MOCK_METHOD(absl::optional<std::string>, mid, (), (const, override));
MOCK_METHOD(rtc::scoped_refptr<RtpSenderInterface>,
sender,
(),
(const, override));
MOCK_METHOD(rtc::scoped_refptr<RtpReceiverInterface>,
receiver,
(),
(const, override));
MOCK_METHOD(bool, stopped, (), (const, override));
MOCK_METHOD(bool, stopping, (), (const, override));
MOCK_METHOD(RtpTransceiverDirection, direction, (), (const, override));
MOCK_METHOD(void,
SetDirection,
(RtpTransceiverDirection new_direction),
(override));
MOCK_METHOD(RTCError,
SetDirectionWithError,
(RtpTransceiverDirection new_direction),
(override));
MOCK_METHOD(absl::optional<RtpTransceiverDirection>,
current_direction,
(),
(const, override));
MOCK_METHOD(absl::optional<RtpTransceiverDirection>,
fired_direction,
(),
(const, override));
MOCK_METHOD(RTCError, StopStandard, (), (override));
MOCK_METHOD(void, StopInternal, (), (override));
MOCK_METHOD(void, Stop, (), (override));
MOCK_METHOD(RTCError,
SetCodecPreferences,
(rtc::ArrayView<RtpCodecCapability> codecs),
(override));
MOCK_METHOD(std::vector<RtpCodecCapability>,
codec_preferences,
(),
(const, override));
MOCK_METHOD(std::vector<RtpHeaderExtensionCapability>,
GetHeaderExtensionsToNegotiate,
(),
(const, override));
MOCK_METHOD(std::vector<RtpHeaderExtensionCapability>,
GetNegotiatedHeaderExtensions,
(),
(const, override));
MOCK_METHOD(
webrtc::RTCError,
SetHeaderExtensionsToNegotiate,
(rtc::ArrayView<const RtpHeaderExtensionCapability> header_extensions),
(override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_RTP_TRANSCEIVER_H_

View file

@ -0,0 +1,58 @@
/*
* Copyright 2016 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.
*/
#ifndef API_TEST_MOCK_RTPRECEIVER_H_
#define API_TEST_MOCK_RTPRECEIVER_H_
#include <string>
#include <vector>
#include "api/crypto/frame_decryptor_interface.h"
#include "api/rtp_receiver_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockRtpReceiver : public rtc::RefCountedObject<RtpReceiverInterface> {
public:
MOCK_METHOD(rtc::scoped_refptr<MediaStreamTrackInterface>,
track,
(),
(const, override));
MOCK_METHOD(std::vector<rtc::scoped_refptr<MediaStreamInterface>>,
streams,
(),
(const, override));
MOCK_METHOD(cricket::MediaType, media_type, (), (const, override));
MOCK_METHOD(std::string, id, (), (const, override));
MOCK_METHOD(RtpParameters, GetParameters, (), (const, override));
MOCK_METHOD(bool,
SetParameters,
(const webrtc::RtpParameters& parameters),
(override));
MOCK_METHOD(void, SetObserver, (RtpReceiverObserverInterface*), (override));
MOCK_METHOD(void,
SetJitterBufferMinimumDelay,
(absl::optional<double>),
(override));
MOCK_METHOD(std::vector<RtpSource>, GetSources, (), (const, override));
MOCK_METHOD(void,
SetFrameDecryptor,
(rtc::scoped_refptr<webrtc::FrameDecryptorInterface>),
(override));
MOCK_METHOD(rtc::scoped_refptr<webrtc::FrameDecryptorInterface>,
GetFrameDecryptor,
(),
(const, override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_RTPRECEIVER_H_

View file

@ -0,0 +1,78 @@
/*
* Copyright 2016 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.
*/
#ifndef API_TEST_MOCK_RTPSENDER_H_
#define API_TEST_MOCK_RTPSENDER_H_
#include <memory>
#include <string>
#include <vector>
#include "api/rtp_sender_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockRtpSender : public RtpSenderInterface {
public:
static rtc::scoped_refptr<MockRtpSender> Create() {
return rtc::make_ref_counted<MockRtpSender>();
}
MOCK_METHOD(bool, SetTrack, (MediaStreamTrackInterface*), (override));
MOCK_METHOD(rtc::scoped_refptr<MediaStreamTrackInterface>,
track,
(),
(const, override));
MOCK_METHOD(rtc::scoped_refptr<DtlsTransportInterface>,
dtls_transport,
(),
(const override));
MOCK_METHOD(uint32_t, ssrc, (), (const, override));
MOCK_METHOD(cricket::MediaType, media_type, (), (const, override));
MOCK_METHOD(std::string, id, (), (const, override));
MOCK_METHOD(std::vector<std::string>, stream_ids, (), (const, override));
MOCK_METHOD(void, SetStreams, (const std::vector<std::string>&), (override));
MOCK_METHOD(std::vector<RtpEncodingParameters>,
init_send_encodings,
(),
(const, override));
MOCK_METHOD(RtpParameters, GetParameters, (), (const, override));
MOCK_METHOD(RTCError, SetParameters, (const RtpParameters&), (override));
MOCK_METHOD(void,
SetParametersAsync,
(const RtpParameters&, SetParametersCallback),
(override));
MOCK_METHOD(rtc::scoped_refptr<DtmfSenderInterface>,
GetDtmfSender,
(),
(const, override));
MOCK_METHOD(void,
SetFrameEncryptor,
(rtc::scoped_refptr<FrameEncryptorInterface>),
(override));
MOCK_METHOD(rtc::scoped_refptr<FrameEncryptorInterface>,
GetFrameEncryptor,
(),
(const, override));
MOCK_METHOD(void,
SetEncoderToPacketizerFrameTransformer,
(rtc::scoped_refptr<FrameTransformerInterface>),
(override));
MOCK_METHOD(void,
SetEncoderSelector,
(std::unique_ptr<VideoEncoderFactory::EncoderSelectorInterface>),
(override));
};
static_assert(!std::is_abstract_v<rtc::RefCountedObject<MockRtpSender>>, "");
} // namespace webrtc
#endif // API_TEST_MOCK_RTPSENDER_H_

View file

@ -0,0 +1,56 @@
/*
* Copyright 2022 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.
*/
#ifndef API_TEST_MOCK_SESSION_DESCRIPTION_INTERFACE_H_
#define API_TEST_MOCK_SESSION_DESCRIPTION_INTERFACE_H_
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "api/jsep.h"
#include "test/gmock.h"
namespace webrtc {
class MockSessionDescriptionInterface : public SessionDescriptionInterface {
public:
MOCK_METHOD(std::unique_ptr<SessionDescriptionInterface>,
Clone,
(),
(const, override));
MOCK_METHOD(cricket::SessionDescription*, description, (), (override));
MOCK_METHOD(const cricket::SessionDescription*,
description,
(),
(const, override));
MOCK_METHOD(std::string, session_id, (), (const, override));
MOCK_METHOD(std::string, session_version, (), (const, override));
MOCK_METHOD(SdpType, GetType, (), (const, override));
MOCK_METHOD(std::string, type, (), (const, override));
MOCK_METHOD(bool, AddCandidate, (const IceCandidateInterface*), (override));
MOCK_METHOD(size_t,
RemoveCandidates,
(const std::vector<cricket::Candidate>&),
(override));
MOCK_METHOD(size_t, number_of_mediasections, (), (const, override));
MOCK_METHOD(const IceCandidateCollection*,
candidates,
(size_t),
(const, override));
MOCK_METHOD(bool, ToString, (std::string*), (const, override));
};
static_assert(!std::is_abstract_v<MockSessionDescriptionInterface>);
} // namespace webrtc
#endif // API_TEST_MOCK_SESSION_DESCRIPTION_INTERFACE_H_

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef API_TEST_MOCK_TRANSFORMABLE_AUDIO_FRAME_H_
#define API_TEST_MOCK_TRANSFORMABLE_AUDIO_FRAME_H_
#include <string>
#include "api/frame_transformer_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockTransformableAudioFrame : public TransformableAudioFrameInterface {
public:
MOCK_METHOD(rtc::ArrayView<const uint8_t>, GetData, (), (const, override));
MOCK_METHOD(void, SetData, (rtc::ArrayView<const uint8_t>), (override));
MOCK_METHOD(void, SetRTPTimestamp, (uint32_t), (override));
MOCK_METHOD(uint8_t, GetPayloadType, (), (const, override));
MOCK_METHOD(uint32_t, GetSsrc, (), (const, override));
MOCK_METHOD(uint32_t, GetTimestamp, (), (const, override));
MOCK_METHOD(std::string, GetMimeType, (), (const, override));
MOCK_METHOD(rtc::ArrayView<const uint32_t>,
GetContributingSources,
(),
(const override));
MOCK_METHOD(const absl::optional<uint16_t>,
SequenceNumber,
(),
(const, override));
MOCK_METHOD(TransformableFrameInterface::Direction,
GetDirection,
(),
(const, override));
MOCK_METHOD(absl::optional<uint64_t>,
AbsoluteCaptureTimestamp,
(),
(const, override));
MOCK_METHOD(TransformableAudioFrameInterface::FrameType,
Type,
(),
(const, override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_TRANSFORMABLE_AUDIO_FRAME_H_

View file

@ -0,0 +1,45 @@
/*
* Copyright 2023 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.
*/
#ifndef API_TEST_MOCK_TRANSFORMABLE_FRAME_H_
#define API_TEST_MOCK_TRANSFORMABLE_FRAME_H_
#include <stdint.h>
#include <optional>
#include <string>
#include "api/array_view.h"
#include "api/frame_transformer_interface.h"
#include "api/units/timestamp.h"
#include "test/gmock.h"
namespace webrtc {
class MockTransformableFrame : public webrtc::TransformableFrameInterface {
public:
MOCK_METHOD(rtc::ArrayView<const uint8_t>, GetData, (), (const, override));
MOCK_METHOD(void, SetData, (rtc::ArrayView<const uint8_t>), (override));
MOCK_METHOD(uint8_t, GetPayloadType, (), (const, override));
MOCK_METHOD(uint32_t, GetSsrc, (), (const, override));
MOCK_METHOD(uint32_t, GetTimestamp, (), (const, override));
MOCK_METHOD(void, SetRTPTimestamp, (uint32_t), (override));
MOCK_METHOD(std::optional<webrtc::Timestamp>,
GetCaptureTimeIdentifier,
(),
(const, override));
MOCK_METHOD(std::string, GetMimeType, (), (const, override));
};
static_assert(!std::is_abstract_v<MockTransformableFrame>, "");
} // namespace webrtc
#endif // API_TEST_MOCK_TRANSFORMABLE_FRAME_H_

View file

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020 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.
*/
#ifndef API_TEST_MOCK_TRANSFORMABLE_VIDEO_FRAME_H_
#define API_TEST_MOCK_TRANSFORMABLE_VIDEO_FRAME_H_
#include <string>
#include <vector>
#include "api/frame_transformer_interface.h"
#include "test/gmock.h"
namespace webrtc {
class MockTransformableVideoFrame
: public webrtc::TransformableVideoFrameInterface {
public:
MOCK_METHOD(rtc::ArrayView<const uint8_t>, GetData, (), (const, override));
MOCK_METHOD(void, SetData, (rtc::ArrayView<const uint8_t> data), (override));
MOCK_METHOD(uint32_t, GetTimestamp, (), (const, override));
MOCK_METHOD(void, SetRTPTimestamp, (uint32_t), (override));
MOCK_METHOD(uint32_t, GetSsrc, (), (const, override));
MOCK_METHOD(bool, IsKeyFrame, (), (const, override));
MOCK_METHOD(void,
SetMetadata,
(const webrtc::VideoFrameMetadata&),
(override));
MOCK_METHOD(uint8_t, GetPayloadType, (), (const, override));
MOCK_METHOD(TransformableFrameInterface::Direction,
GetDirection,
(),
(const, override));
MOCK_METHOD(std::string, GetMimeType, (), (const, override));
MOCK_METHOD(VideoFrameMetadata, Metadata, (), (const, override));
MOCK_METHOD(absl::optional<Timestamp>,
GetCaptureTimeIdentifier,
(),
(const, override));
};
static_assert(!std::is_abstract_v<MockTransformableVideoFrame>, "");
} // namespace webrtc
#endif // API_TEST_MOCK_TRANSFORMABLE_VIDEO_FRAME_H_

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_MOCK_VIDEO_BITRATE_ALLOCATOR_H_
#define API_TEST_MOCK_VIDEO_BITRATE_ALLOCATOR_H_
#include "api/video/video_bitrate_allocator.h"
#include "test/gmock.h"
namespace webrtc {
class MockVideoBitrateAllocator : public webrtc::VideoBitrateAllocator {
MOCK_METHOD(VideoBitrateAllocation,
Allocate,
(VideoBitrateAllocationParameters parameters),
(override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_VIDEO_BITRATE_ALLOCATOR_H_

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_MOCK_VIDEO_BITRATE_ALLOCATOR_FACTORY_H_
#define API_TEST_MOCK_VIDEO_BITRATE_ALLOCATOR_FACTORY_H_
#include <memory>
#include "api/video/video_bitrate_allocator_factory.h"
#include "test/gmock.h"
namespace webrtc {
class MockVideoBitrateAllocatorFactory
: public webrtc::VideoBitrateAllocatorFactory {
public:
~MockVideoBitrateAllocatorFactory() override { Die(); }
MOCK_METHOD(std::unique_ptr<VideoBitrateAllocator>,
CreateVideoBitrateAllocator,
(const VideoCodec&),
(override));
MOCK_METHOD(void, Die, ());
};
} // namespace webrtc
#endif // API_TEST_MOCK_VIDEO_BITRATE_ALLOCATOR_FACTORY_H_

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_MOCK_VIDEO_DECODER_H_
#define API_TEST_MOCK_VIDEO_DECODER_H_
#include <utility>
#include "api/video_codecs/video_decoder.h"
#include "test/gmock.h"
namespace webrtc {
using testing::_;
using testing::Invoke;
class MockDecodedImageCallback : public DecodedImageCallback {
public:
MOCK_METHOD(int32_t,
Decoded,
(VideoFrame & decoded_image), // NOLINT
(override));
MOCK_METHOD(int32_t,
Decoded,
(VideoFrame & decoded_image, // NOLINT
int64_t decode_time_ms),
(override));
MOCK_METHOD(void,
Decoded,
(VideoFrame & decoded_image, // NOLINT
absl::optional<int32_t> decode_time_ms,
absl::optional<uint8_t> qp),
(override));
};
class MockVideoDecoder : public VideoDecoder {
public:
MockVideoDecoder() {
// Make `Configure` succeed by default, so that individual tests that
// verify other methods wouldn't need to stub `Configure`.
ON_CALL(*this, Configure).WillByDefault(testing::Return(true));
// TODO(bugs.webrtc.org/15444): Remove once all tests have been migrated to
// expecting calls Decode without a missing_frames param.
ON_CALL(*this, Decode(_, _))
.WillByDefault(Invoke([this](const EncodedImage& input_image,
int64_t render_time_ms) {
return Decode(input_image, /*missing_frames=*/false, render_time_ms);
}));
}
~MockVideoDecoder() override { Destruct(); }
MOCK_METHOD(bool, Configure, (const Settings& settings), (override));
MOCK_METHOD(int32_t,
Decode,
(const EncodedImage& input_image,
int64_t render_time_ms),
(override));
MOCK_METHOD(int32_t,
Decode,
(const EncodedImage& input_image,
bool missing_frames,
int64_t render_time_ms));
MOCK_METHOD(int32_t,
RegisterDecodeCompleteCallback,
(DecodedImageCallback * callback),
(override));
MOCK_METHOD(int32_t, Release, (), (override));
// Special utility method that allows a test to monitor/verify when
// destruction of the decoder instance occurs.
MOCK_METHOD(void, Destruct, (), ());
};
} // namespace webrtc
#endif // API_TEST_MOCK_VIDEO_DECODER_H_

View file

@ -0,0 +1,45 @@
/*
* Copyright (c) 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.
*/
#ifndef API_TEST_MOCK_VIDEO_DECODER_FACTORY_H_
#define API_TEST_MOCK_VIDEO_DECODER_FACTORY_H_
#include <memory>
#include <vector>
#include "api/environment/environment.h"
#include "api/video_codecs/sdp_video_format.h"
#include "api/video_codecs/video_decoder.h"
#include "api/video_codecs/video_decoder_factory.h"
#include "test/gmock.h"
namespace webrtc {
class MockVideoDecoderFactory : public VideoDecoderFactory {
public:
~MockVideoDecoderFactory() override { Die(); }
MOCK_METHOD(std::vector<SdpVideoFormat>,
GetSupportedFormats,
(),
(const, override));
MOCK_METHOD(std::unique_ptr<VideoDecoder>,
Create,
(const Environment&, const SdpVideoFormat&),
(override));
MOCK_METHOD(std::unique_ptr<VideoDecoder>,
CreateVideoDecoder,
(const SdpVideoFormat&),
(override));
MOCK_METHOD(void, Die, ());
};
} // namespace webrtc
#endif // API_TEST_MOCK_VIDEO_DECODER_FACTORY_H_

View file

@ -0,0 +1,73 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_MOCK_VIDEO_ENCODER_H_
#define API_TEST_MOCK_VIDEO_ENCODER_H_
#include <vector>
#include "api/video_codecs/video_encoder.h"
#include "test/gmock.h"
namespace webrtc {
class MockEncodedImageCallback : public EncodedImageCallback {
public:
MOCK_METHOD(Result,
OnEncodedImage,
(const EncodedImage&, const CodecSpecificInfo*),
(override));
MOCK_METHOD(void, OnDroppedFrame, (DropReason reason), (override));
};
class MockVideoEncoder : public VideoEncoder {
public:
MOCK_METHOD(void,
SetFecControllerOverride,
(FecControllerOverride*),
(override));
MOCK_METHOD(int32_t,
InitEncode,
(const VideoCodec*, int32_t numberOfCores, size_t maxPayloadSize),
(override));
MOCK_METHOD(int32_t,
InitEncode,
(const VideoCodec*, const VideoEncoder::Settings& settings),
(override));
MOCK_METHOD(int32_t,
Encode,
(const VideoFrame& inputImage,
const std::vector<VideoFrameType>*),
(override));
MOCK_METHOD(int32_t,
RegisterEncodeCompleteCallback,
(EncodedImageCallback*),
(override));
MOCK_METHOD(int32_t, Release, (), (override));
MOCK_METHOD(void,
SetRates,
(const RateControlParameters& parameters),
(override));
MOCK_METHOD(void,
OnPacketLossRateUpdate,
(float packet_loss_rate),
(override));
MOCK_METHOD(void, OnRttUpdate, (int64_t rtt_ms), (override));
MOCK_METHOD(void,
OnLossNotification,
(const LossNotification& loss_notification),
(override));
MOCK_METHOD(EncoderInfo, GetEncoderInfo, (), (const, override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_VIDEO_ENCODER_H_

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 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.
*/
#ifndef API_TEST_MOCK_VIDEO_ENCODER_FACTORY_H_
#define API_TEST_MOCK_VIDEO_ENCODER_FACTORY_H_
#include <memory>
#include <vector>
#include "api/video_codecs/sdp_video_format.h"
#include "api/video_codecs/video_encoder.h"
#include "api/video_codecs/video_encoder_factory.h"
#include "test/gmock.h"
namespace webrtc {
class MockVideoEncoderFactory : public webrtc::VideoEncoderFactory {
public:
~MockVideoEncoderFactory() override { Die(); }
MOCK_METHOD(std::vector<SdpVideoFormat>,
GetSupportedFormats,
(),
(const, override));
MOCK_METHOD(std::unique_ptr<VideoEncoder>,
CreateVideoEncoder,
(const SdpVideoFormat&),
(override));
MOCK_METHOD(void, Die, ());
};
} // namespace webrtc
#endif // API_TEST_MOCK_VIDEO_ENCODER_FACTORY_H_

View file

@ -0,0 +1,69 @@
/*
* Copyright 2021 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.
*/
#ifndef API_TEST_MOCK_VIDEO_TRACK_H_
#define API_TEST_MOCK_VIDEO_TRACK_H_
#include <string>
#include "api/media_stream_interface.h"
#include "api/scoped_refptr.h"
#include "rtc_base/ref_counted_object.h"
#include "test/gmock.h"
namespace webrtc {
class MockVideoTrack
: public rtc::RefCountedObject<webrtc::VideoTrackInterface> {
public:
static rtc::scoped_refptr<MockVideoTrack> Create() {
return rtc::scoped_refptr<MockVideoTrack>(new MockVideoTrack());
}
// NotifierInterface
MOCK_METHOD(void,
RegisterObserver,
(ObserverInterface * observer),
(override));
MOCK_METHOD(void,
UnregisterObserver,
(ObserverInterface * observer),
(override));
// MediaStreamTrackInterface
MOCK_METHOD(std::string, kind, (), (const, override));
MOCK_METHOD(std::string, id, (), (const, override));
MOCK_METHOD(bool, enabled, (), (const, override));
MOCK_METHOD(bool, set_enabled, (bool enable), (override));
MOCK_METHOD(TrackState, state, (), (const, override));
// VideoSourceInterface
MOCK_METHOD(void,
AddOrUpdateSink,
(rtc::VideoSinkInterface<VideoFrame> * sink,
const rtc::VideoSinkWants& wants),
(override));
// RemoveSink must guarantee that at the time the method returns,
// there is no current and no future calls to VideoSinkInterface::OnFrame.
MOCK_METHOD(void,
RemoveSink,
(rtc::VideoSinkInterface<VideoFrame> * sink),
(override));
// VideoTrackInterface
MOCK_METHOD(VideoTrackSourceInterface*, GetSource, (), (const, override));
MOCK_METHOD(ContentHint, content_hint, (), (const, override));
MOCK_METHOD(void, set_content_hint, (ContentHint hint), (override));
};
} // namespace webrtc
#endif // API_TEST_MOCK_VIDEO_TRACK_H_

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2018 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 "api/test/neteq_simulator.h"
namespace webrtc {
namespace test {
NetEqSimulator::SimulationStepResult::SimulationStepResult() = default;
NetEqSimulator::SimulationStepResult::SimulationStepResult(
const NetEqSimulator::SimulationStepResult& other) = default;
NetEqSimulator::SimulationStepResult::~SimulationStepResult() = default;
NetEqSimulator::NetEqState::NetEqState() = default;
NetEqSimulator::NetEqState::NetEqState(const NetEqState& other) = default;
NetEqSimulator::NetEqState::~NetEqState() = default;
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,82 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_NETEQ_SIMULATOR_H_
#define API_TEST_NETEQ_SIMULATOR_H_
#include <stdint.h>
#include <map>
#include <vector>
namespace webrtc {
namespace test {
class NetEqSimulator {
public:
virtual ~NetEqSimulator() = default;
enum class Action { kNormal, kExpand, kAccelerate, kPreemptiveExpand };
// The results of one simulation step.
struct SimulationStepResult {
SimulationStepResult();
SimulationStepResult(const SimulationStepResult& other);
~SimulationStepResult();
bool is_simulation_finished = false;
// The amount of audio produced (in ms) with the actions in this time step.
std::map<Action, int> action_times_ms;
// The amount of wall clock time (in ms) that elapsed since the previous
// event. This is not necessarily equal to the sum of the values in
// action_times_ms.
int64_t simulation_step_ms = 0;
};
struct NetEqState {
NetEqState();
NetEqState(const NetEqState& other);
~NetEqState();
// The sum of the packet buffer and sync buffer delay.
int current_delay_ms = 0;
// An indicator that packet loss occurred since the last GetAudio event.
bool packet_loss_occurred = false;
// An indicator that the packet buffer has been flushed since the last
// GetAudio event.
bool packet_buffer_flushed = false;
// Indicates if the next needed packet is available in the buffer.
bool next_packet_available = false;
// The inter-arrival times in ms of the packets that have arrived since the
// last GetAudio event.
std::vector<int> packet_iat_ms;
// The current packet size in ms.
int packet_size_ms = 0;
};
// Runs the simulation until the end. Returns the duration of the produced
// audio in ms.
virtual int64_t Run() = 0;
// Runs the simulation until we hit the next GetAudio event. If the simulation
// is finished, is_simulation_finished will be set to true in the returned
// SimulationStepResult.
virtual SimulationStepResult RunToNextGetAudio() = 0;
// Set the next action to be taken by NetEq. This will override any action
// that NetEq would normally decide to take.
virtual void SetNextAction(Action next_operation) = 0;
// Get the current state of NetEq.
virtual NetEqState GetNetEqState() = 0;
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_NETEQ_SIMULATOR_H_

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2018 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 "api/test/neteq_simulator_factory.h"
#include <memory>
#include <string>
#include <vector>
#include "absl/flags/flag.h"
#include "absl/flags/parse.h"
#include "modules/audio_coding/neteq/tools/neteq_test_factory.h"
#include "rtc_base/checks.h"
namespace webrtc {
namespace test {
namespace {
NetEqTestFactory::Config convertConfig(
const NetEqSimulatorFactory::Config& simulation_config,
absl::string_view replacement_audio_filename) {
NetEqTestFactory::Config config;
config.replacement_audio_file = std::string(replacement_audio_filename);
config.max_nr_packets_in_buffer = simulation_config.max_nr_packets_in_buffer;
config.initial_dummy_packets = simulation_config.initial_dummy_packets;
config.skip_get_audio_events = simulation_config.skip_get_audio_events;
config.field_trial_string = simulation_config.field_trial_string;
config.output_audio_filename = simulation_config.output_audio_filename;
config.pythonplot = simulation_config.python_plot_filename.has_value();
config.plot_scripts_basename = simulation_config.python_plot_filename;
config.textlog = simulation_config.text_log_filename.has_value();
config.textlog_filename = simulation_config.text_log_filename;
return config;
}
} // namespace
NetEqSimulatorFactory::NetEqSimulatorFactory()
: factory_(std::make_unique<NetEqTestFactory>()) {}
NetEqSimulatorFactory::~NetEqSimulatorFactory() = default;
std::unique_ptr<NetEqSimulator> NetEqSimulatorFactory::CreateSimulatorFromFile(
absl::string_view event_log_filename,
absl::string_view replacement_audio_filename,
Config simulation_config) {
NetEqTestFactory::Config config =
convertConfig(simulation_config, replacement_audio_filename);
return factory_->InitializeTestFromFile(
std::string(event_log_filename), simulation_config.neteq_factory, config);
}
std::unique_ptr<NetEqSimulator>
NetEqSimulatorFactory::CreateSimulatorFromString(
absl::string_view event_log_file_contents,
absl::string_view replacement_audio_filename,
Config simulation_config) {
NetEqTestFactory::Config config =
convertConfig(simulation_config, replacement_audio_filename);
return factory_->InitializeTestFromString(
std::string(event_log_file_contents), simulation_config.neteq_factory,
config);
}
} // namespace test
} // namespace webrtc

View file

@ -0,0 +1,71 @@
/*
* Copyright (c) 2018 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.
*/
#ifndef API_TEST_NETEQ_SIMULATOR_FACTORY_H_
#define API_TEST_NETEQ_SIMULATOR_FACTORY_H_
#include <memory>
#include <string>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/neteq/neteq_factory.h"
#include "api/test/neteq_simulator.h"
namespace webrtc {
namespace test {
class NetEqTestFactory;
class NetEqSimulatorFactory {
public:
NetEqSimulatorFactory();
~NetEqSimulatorFactory();
struct Config {
// The maximum allowed number of packets in the jitter buffer.
int max_nr_packets_in_buffer = 0;
// The number of audio packets to insert at the start of the simulation.
// Since the simulation is done with a replacement audio file, these
// artificial packets will take a small piece of that replacement audio.
int initial_dummy_packets = 0;
// The number of simulation steps to skip at the start of the simulation.
// This removes incoming packets and GetAudio events from the start of the
// simulation, until the requested number of GetAudio events has been
// removed.
int skip_get_audio_events = 0;
// A WebRTC field trial string to be used during the simulation.
std::string field_trial_string;
// A filename for the generated output audio file.
absl::optional<std::string> output_audio_filename;
// A filename for the python plot.
absl::optional<std::string> python_plot_filename;
// A filename for the text log.
absl::optional<std::string> text_log_filename;
// A custom NetEqFactory can be used.
NetEqFactory* neteq_factory = nullptr;
};
std::unique_ptr<NetEqSimulator> CreateSimulatorFromFile(
absl::string_view event_log_filename,
absl::string_view replacement_audio_filename,
Config simulation_config);
// The same as above, but pass the file contents as a string.
std::unique_ptr<NetEqSimulator> CreateSimulatorFromString(
absl::string_view event_log_file_contents,
absl::string_view replacement_audio_file,
Config simulation_config);
private:
std::unique_ptr<NetEqTestFactory> factory_;
};
} // namespace test
} // namespace webrtc
#endif // API_TEST_NETEQ_SIMULATOR_FACTORY_H_

View file

@ -0,0 +1,52 @@
# Copyright (c) 2019 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.
import("../../../webrtc.gni")
rtc_library("network_emulation") {
visibility = [ "*" ]
sources = [
"cross_traffic.h",
"network_emulation_interfaces.cc",
"network_emulation_interfaces.h",
]
deps = [
"../..:array_view",
"../../../rtc_base:checks",
"../../../rtc_base:copy_on_write_buffer",
"../../../rtc_base:ip_address",
"../../../rtc_base:net_helper",
"../../../rtc_base:socket_address",
"../../numerics",
"../../task_queue",
"../../units:data_rate",
"../../units:data_size",
"../../units:time_delta",
"../../units:timestamp",
]
absl_deps = [ "//third_party/abseil-cpp/absl/types:optional" ]
}
rtc_library("create_cross_traffic") {
visibility = [ "*" ]
testonly = true
sources = [
"create_cross_traffic.cc",
"create_cross_traffic.h",
]
deps = [
":network_emulation",
"../..:network_emulation_manager_api",
"../../../rtc_base/task_utils:repeating_task",
"../../../test/network:emulated_network",
]
}

View file

@ -0,0 +1,7 @@
specific_include_rules = {
".*": [
"+rtc_base/socket_address.h",
"+rtc_base/ip_address.h",
"+rtc_base/copy_on_write_buffer.h",
],
}

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2021 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 "api/test/network_emulation/create_cross_traffic.h"
#include <memory>
#include "rtc_base/task_utils/repeating_task.h"
#include "test/network/cross_traffic.h"
namespace webrtc {
std::unique_ptr<CrossTrafficGenerator> CreateRandomWalkCrossTraffic(
CrossTrafficRoute* traffic_route,
RandomWalkConfig config) {
return std::make_unique<test::RandomWalkCrossTraffic>(config, traffic_route);
}
std::unique_ptr<CrossTrafficGenerator> CreatePulsedPeaksCrossTraffic(
CrossTrafficRoute* traffic_route,
PulsedPeaksConfig config) {
return std::make_unique<test::PulsedPeaksCrossTraffic>(config, traffic_route);
}
std::unique_ptr<CrossTrafficGenerator> CreateFakeTcpCrossTraffic(
EmulatedRoute* send_route,
EmulatedRoute* ret_route,
FakeTcpConfig config) {
return std::make_unique<test::FakeTcpCrossTraffic>(config, send_route,
ret_route);
}
} // namespace webrtc

View file

@ -0,0 +1,37 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef API_TEST_NETWORK_EMULATION_CREATE_CROSS_TRAFFIC_H_
#define API_TEST_NETWORK_EMULATION_CREATE_CROSS_TRAFFIC_H_
#include <memory>
#include "api/test/network_emulation/cross_traffic.h"
#include "api/test/network_emulation_manager.h"
namespace webrtc {
// This API is still in development and can be changed without prior notice.
std::unique_ptr<CrossTrafficGenerator> CreateRandomWalkCrossTraffic(
CrossTrafficRoute* traffic_route,
RandomWalkConfig config);
std::unique_ptr<CrossTrafficGenerator> CreatePulsedPeaksCrossTraffic(
CrossTrafficRoute* traffic_route,
PulsedPeaksConfig config);
std::unique_ptr<CrossTrafficGenerator> CreateFakeTcpCrossTraffic(
EmulatedRoute* send_route,
EmulatedRoute* ret_route,
FakeTcpConfig config);
} // namespace webrtc
#endif // API_TEST_NETWORK_EMULATION_CREATE_CROSS_TRAFFIC_H_

View file

@ -0,0 +1,85 @@
/*
* Copyright (c) 2021 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.
*/
#ifndef API_TEST_NETWORK_EMULATION_CROSS_TRAFFIC_H_
#define API_TEST_NETWORK_EMULATION_CROSS_TRAFFIC_H_
#include "api/task_queue/task_queue_base.h"
#include "api/test/network_emulation/network_emulation_interfaces.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/time_delta.h"
#include "api/units/timestamp.h"
namespace webrtc {
// This API is still in development and can be changed without prior notice.
// Represents the endpoint for cross traffic that is going through the network.
// It can be used to emulate unexpected network load.
class CrossTrafficRoute {
public:
virtual ~CrossTrafficRoute() = default;
// Triggers sending of dummy packets with size `packet_size` bytes.
virtual void TriggerPacketBurst(size_t num_packets, size_t packet_size) = 0;
// Sends a packet over the nodes. The content of the packet is unspecified;
// only the size metter for the emulation purposes.
virtual void SendPacket(size_t packet_size) = 0;
// Sends a packet over the nodes and runs `action` when it has been delivered.
virtual void NetworkDelayedAction(size_t packet_size,
std::function<void()> action) = 0;
};
// Describes a way of generating cross traffic on some route. Used by
// NetworkEmulationManager to produce cross traffic during some period of time.
class CrossTrafficGenerator {
public:
virtual ~CrossTrafficGenerator() = default;
// Time between Process calls.
virtual TimeDelta GetProcessInterval() const = 0;
// Called periodically by NetworkEmulationManager. Generates traffic on the
// route.
virtual void Process(Timestamp at_time) = 0;
};
// Config of a cross traffic generator. Generated traffic rises and falls
// randomly.
struct RandomWalkConfig {
int random_seed = 1;
DataRate peak_rate = DataRate::KilobitsPerSec(100);
DataSize min_packet_size = DataSize::Bytes(200);
TimeDelta min_packet_interval = TimeDelta::Millis(1);
TimeDelta update_interval = TimeDelta::Millis(200);
double variance = 0.6;
double bias = -0.1;
};
// Config of a cross traffic generator. Generated traffic has form of periodic
// peaks alternating with periods of silence.
struct PulsedPeaksConfig {
DataRate peak_rate = DataRate::KilobitsPerSec(100);
DataSize min_packet_size = DataSize::Bytes(200);
TimeDelta min_packet_interval = TimeDelta::Millis(1);
TimeDelta send_duration = TimeDelta::Millis(100);
TimeDelta hold_duration = TimeDelta::Millis(2000);
};
struct FakeTcpConfig {
DataSize packet_size = DataSize::Bytes(1200);
DataSize send_limit = DataSize::PlusInfinity();
TimeDelta process_interval = TimeDelta::Millis(200);
TimeDelta packet_timeout = TimeDelta::Seconds(1);
};
} // namespace webrtc
#endif // API_TEST_NETWORK_EMULATION_CROSS_TRAFFIC_H_

View file

@ -0,0 +1,46 @@
/*
* Copyright (c) 2019 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 "api/test/network_emulation/network_emulation_interfaces.h"
#include "rtc_base/net_helper.h"
namespace webrtc {
EmulatedIpPacket::EmulatedIpPacket(const rtc::SocketAddress& from,
const rtc::SocketAddress& to,
rtc::CopyOnWriteBuffer data,
Timestamp arrival_time,
uint16_t application_overhead)
: from(from),
to(to),
data(data),
headers_size(to.ipaddr().overhead() + application_overhead +
cricket::kUdpHeaderSize),
arrival_time(arrival_time) {
RTC_DCHECK(to.family() == AF_INET || to.family() == AF_INET6);
}
DataRate EmulatedNetworkOutgoingStats::AverageSendRate() const {
RTC_DCHECK_GE(packets_sent, 2);
RTC_DCHECK(first_packet_sent_time.IsFinite());
RTC_DCHECK(last_packet_sent_time.IsFinite());
return (bytes_sent - first_sent_packet_size) /
(last_packet_sent_time - first_packet_sent_time);
}
DataRate EmulatedNetworkIncomingStats::AverageReceiveRate() const {
RTC_DCHECK_GE(packets_received, 2);
RTC_DCHECK(first_packet_received_time.IsFinite());
RTC_DCHECK(last_packet_received_time.IsFinite());
return (bytes_received - first_received_packet_size) /
(last_packet_received_time - first_packet_received_time);
}
} // namespace webrtc

View file

@ -0,0 +1,311 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef API_TEST_NETWORK_EMULATION_NETWORK_EMULATION_INTERFACES_H_
#define API_TEST_NETWORK_EMULATION_NETWORK_EMULATION_INTERFACES_H_
#include <map>
#include <memory>
#include <vector>
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/numerics/samples_stats_counter.h"
#include "api/units/data_rate.h"
#include "api/units/data_size.h"
#include "api/units/timestamp.h"
#include "rtc_base/copy_on_write_buffer.h"
#include "rtc_base/ip_address.h"
#include "rtc_base/socket_address.h"
namespace webrtc {
struct EmulatedIpPacket {
public:
EmulatedIpPacket(const rtc::SocketAddress& from,
const rtc::SocketAddress& to,
rtc::CopyOnWriteBuffer data,
Timestamp arrival_time,
uint16_t application_overhead = 0);
~EmulatedIpPacket() = default;
// This object is not copyable or assignable.
EmulatedIpPacket(const EmulatedIpPacket&) = delete;
EmulatedIpPacket& operator=(const EmulatedIpPacket&) = delete;
// This object is only moveable.
EmulatedIpPacket(EmulatedIpPacket&&) = default;
EmulatedIpPacket& operator=(EmulatedIpPacket&&) = default;
size_t size() const { return data.size(); }
const uint8_t* cdata() const { return data.cdata(); }
size_t ip_packet_size() const { return size() + headers_size; }
rtc::SocketAddress from;
rtc::SocketAddress to;
// Holds the UDP payload.
rtc::CopyOnWriteBuffer data;
uint16_t headers_size;
Timestamp arrival_time;
};
// Interface for handling IP packets from an emulated network. This is used with
// EmulatedEndpoint to receive packets on a specific port.
class EmulatedNetworkReceiverInterface {
public:
virtual ~EmulatedNetworkReceiverInterface() = default;
virtual void OnPacketReceived(EmulatedIpPacket packet) = 0;
};
struct EmulatedNetworkOutgoingStats {
int64_t packets_sent = 0;
DataSize bytes_sent = DataSize::Zero();
// Sizes of all sent packets.
// Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled.
SamplesStatsCounter sent_packets_size;
DataSize first_sent_packet_size = DataSize::Zero();
// Time of the first packet sent or infinite value if no packets were sent.
Timestamp first_packet_sent_time = Timestamp::PlusInfinity();
// Time of the last packet sent or infinite value if no packets were sent.
Timestamp last_packet_sent_time = Timestamp::MinusInfinity();
// Returns average send rate. Requires that at least 2 packets were sent.
DataRate AverageSendRate() const;
};
struct EmulatedNetworkIncomingStats {
// Total amount of packets received with or without destination.
int64_t packets_received = 0;
// Total amount of bytes in received packets.
DataSize bytes_received = DataSize::Zero();
// Sizes of all received packets.
// Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled.
SamplesStatsCounter received_packets_size;
// Total amount of packets that were received, but no destination was found.
int64_t packets_discarded_no_receiver = 0;
// Total amount of bytes in discarded packets.
DataSize bytes_discarded_no_receiver = DataSize::Zero();
// Sizes of all packets that were received, but no destination was found.
// Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled.
SamplesStatsCounter packets_discarded_no_receiver_size;
DataSize first_received_packet_size = DataSize::Zero();
// Time of the first packet received or infinite value if no packets were
// received.
Timestamp first_packet_received_time = Timestamp::PlusInfinity();
// Time of the last packet received or infinite value if no packets were
// received.
Timestamp last_packet_received_time = Timestamp::MinusInfinity();
DataRate AverageReceiveRate() const;
};
struct EmulatedNetworkStats {
int64_t PacketsSent() const { return overall_outgoing_stats.packets_sent; }
DataSize BytesSent() const { return overall_outgoing_stats.bytes_sent; }
// Returns the timestamped sizes of all sent packets.
// Returned reference is valid until the next call to a non-const method.
// Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled.
const SamplesStatsCounter& SentPacketsSizeCounter() const {
return overall_outgoing_stats.sent_packets_size;
}
DataSize FirstSentPacketSize() const {
return overall_outgoing_stats.first_sent_packet_size;
}
// Returns time of the first packet sent or infinite value if no packets were
// sent.
Timestamp FirstPacketSentTime() const {
return overall_outgoing_stats.first_packet_sent_time;
}
// Returns time of the last packet sent or infinite value if no packets were
// sent.
Timestamp LastPacketSentTime() const {
return overall_outgoing_stats.last_packet_sent_time;
}
DataRate AverageSendRate() const {
return overall_outgoing_stats.AverageSendRate();
}
// Total amount of packets received regardless of the destination address.
int64_t PacketsReceived() const {
return overall_incoming_stats.packets_received;
}
// Total amount of bytes in received packets.
DataSize BytesReceived() const {
return overall_incoming_stats.bytes_received;
}
// Returns the timestamped sizes of all received packets.
// Returned reference is valid until the next call to a non-const method.
// Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled.
const SamplesStatsCounter& ReceivedPacketsSizeCounter() const {
return overall_incoming_stats.received_packets_size;
}
// Total amount of packets that were received, but no destination was found.
int64_t PacketsDiscardedNoReceiver() const {
return overall_incoming_stats.packets_discarded_no_receiver;
}
// Total amount of bytes in dropped packets.
DataSize BytesDiscardedNoReceiver() const {
return overall_incoming_stats.bytes_discarded_no_receiver;
}
// Returns counter with timestamped sizes of all packets that were received,
// but no destination was found.
// Returned reference is valid until the next call to a non-const method.
// Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled.
const SamplesStatsCounter& PacketsDiscardedNoReceiverSizeCounter() const {
return overall_incoming_stats.packets_discarded_no_receiver_size;
}
DataSize FirstReceivedPacketSize() const {
return overall_incoming_stats.first_received_packet_size;
}
// Returns time of the first packet received or infinite value if no packets
// were received.
Timestamp FirstPacketReceivedTime() const {
return overall_incoming_stats.first_packet_received_time;
}
// Returns time of the last packet received or infinite value if no packets
// were received.
Timestamp LastPacketReceivedTime() const {
return overall_incoming_stats.last_packet_received_time;
}
DataRate AverageReceiveRate() const {
return overall_incoming_stats.AverageReceiveRate();
}
// List of IP addresses that were used to send data considered in this stats
// object.
std::vector<rtc::IPAddress> local_addresses;
// Overall outgoing stats for all IP addresses which were requested.
EmulatedNetworkOutgoingStats overall_outgoing_stats;
// Overall incoming stats for all IP addresses from which data was received
// on requested interfaces.
EmulatedNetworkIncomingStats overall_incoming_stats;
std::map<rtc::IPAddress, EmulatedNetworkOutgoingStats>
outgoing_stats_per_destination;
std::map<rtc::IPAddress, EmulatedNetworkIncomingStats>
incoming_stats_per_source;
// Duration between packet was received on network interface and was
// dispatched to the network in microseconds.
// Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled.
SamplesStatsCounter sent_packets_queue_wait_time_us;
};
struct EmulatedNetworkNodeStats {
// Amount of time each packet spent in the emulated network node for which
// stats were collected.
//
// Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled.
SamplesStatsCounter packet_transport_time;
// For each packet contains its size divided on the amount of time which it
// spent in the emulated network node for which stats were collected.
//
// Collected iff EmulatedNetworkStatsGatheringMode::kDebug is enabled.
SamplesStatsCounter size_to_packet_transport_time;
};
// EmulatedEndpoint is an abstraction for network interface on device. Instances
// of this are created by NetworkEmulationManager::CreateEndpoint and
// thread safe.
class EmulatedEndpoint : public EmulatedNetworkReceiverInterface {
public:
// Send packet into network.
// `from` will be used to set source address for the packet in destination
// socket.
// `to` will be used for routing verification and picking right socket by port
// on destination endpoint.
virtual void SendPacket(const rtc::SocketAddress& from,
const rtc::SocketAddress& to,
rtc::CopyOnWriteBuffer packet_data,
uint16_t application_overhead = 0) = 0;
// Binds receiver to this endpoint to send and receive data.
// `desired_port` is a port that should be used. If it is equal to 0,
// endpoint will pick the first available port starting from
// `kFirstEphemeralPort`.
//
// Returns the port, that should be used (it will be equals to desired, if
// `desired_port` != 0 and is free or will be the one, selected by endpoint)
// or absl::nullopt if desired_port in used. Also fails if there are no more
// free ports to bind to.
//
// The Bind- and Unbind-methods must not be called from within a bound
// receiver's OnPacketReceived method.
virtual absl::optional<uint16_t> BindReceiver(
uint16_t desired_port,
EmulatedNetworkReceiverInterface* receiver) = 0;
// Unbinds receiver from the specified port. Do nothing if no receiver was
// bound before. After this method returns, no more packets can be delivered
// to the receiver, and it is safe to destroy it.
virtual void UnbindReceiver(uint16_t port) = 0;
// Binds receiver that will accept all packets which arrived on any port
// for which there are no bound receiver.
virtual void BindDefaultReceiver(
EmulatedNetworkReceiverInterface* receiver) = 0;
// Unbinds default receiver. Do nothing if no default receiver was bound
// before.
virtual void UnbindDefaultReceiver() = 0;
virtual rtc::IPAddress GetPeerLocalAddress() const = 0;
private:
// Ensure that there can be no other subclass than EmulatedEndpointImpl. This
// means that it's always safe to downcast EmulatedEndpoint instances to
// EmulatedEndpointImpl.
friend class EmulatedEndpointImpl;
EmulatedEndpoint() = default;
};
// Simulates a TCP connection, this roughly implements the Reno algorithm. In
// difference from TCP this only support sending messages with a fixed length,
// no streaming. This is useful to simulate signaling and cross traffic using
// message based protocols such as HTTP. It differs from UDP messages in that
// they are guranteed to be delivered eventually, even on lossy networks.
class TcpMessageRoute {
public:
// Sends a TCP message of the given `size` over the route, `on_received` is
// called when the message has been delivered. Note that the connection
// parameters are reset iff there's no currently pending message on the route.
virtual void SendMessage(size_t size, std::function<void()> on_received) = 0;
protected:
~TcpMessageRoute() = default;
};
} // namespace webrtc
#endif // API_TEST_NETWORK_EMULATION_NETWORK_EMULATION_INTERFACES_H_

View file

@ -0,0 +1,149 @@
/*
* Copyright (c) 2019 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 "api/test/network_emulation_manager.h"
#include <utility>
#include "call/simulated_network.h"
#include "rtc_base/checks.h"
namespace webrtc {
bool AbslParseFlag(absl::string_view text, TimeMode* mode, std::string* error) {
if (text == "realtime") {
*mode = TimeMode::kRealTime;
return true;
}
if (text == "simulated") {
*mode = TimeMode::kSimulated;
return true;
}
*error =
"Unknown value for TimeMode enum. Options are 'realtime' or 'simulated'";
return false;
}
std::string AbslUnparseFlag(TimeMode mode) {
switch (mode) {
case TimeMode::kRealTime:
return "realtime";
case TimeMode::kSimulated:
return "simulated";
}
RTC_CHECK_NOTREACHED();
return "unknown";
}
NetworkEmulationManager::SimulatedNetworkNode::Builder&
NetworkEmulationManager::SimulatedNetworkNode::Builder::config(
BuiltInNetworkBehaviorConfig config) {
config_ = config;
return *this;
}
NetworkEmulationManager::SimulatedNetworkNode::Builder&
NetworkEmulationManager::SimulatedNetworkNode::Builder::delay_ms(
int queue_delay_ms) {
config_.queue_delay_ms = queue_delay_ms;
return *this;
}
NetworkEmulationManager::SimulatedNetworkNode::Builder&
NetworkEmulationManager::SimulatedNetworkNode::Builder::capacity_kbps(
int link_capacity_kbps) {
config_.link_capacity_kbps = link_capacity_kbps;
return *this;
}
NetworkEmulationManager::SimulatedNetworkNode::Builder&
NetworkEmulationManager::SimulatedNetworkNode::Builder::capacity_Mbps(
int link_capacity_Mbps) {
config_.link_capacity_kbps = link_capacity_Mbps * 1000;
return *this;
}
NetworkEmulationManager::SimulatedNetworkNode::Builder&
NetworkEmulationManager::SimulatedNetworkNode::Builder::loss(double loss_rate) {
config_.loss_percent = std::round(loss_rate * 100);
return *this;
}
NetworkEmulationManager::SimulatedNetworkNode::Builder&
NetworkEmulationManager::SimulatedNetworkNode::Builder::packet_queue_length(
int max_queue_length_in_packets) {
config_.queue_length_packets = max_queue_length_in_packets;
return *this;
}
NetworkEmulationManager::SimulatedNetworkNode::Builder&
NetworkEmulationManager::SimulatedNetworkNode::Builder::
delay_standard_deviation_ms(int delay_standard_deviation_ms) {
config_.delay_standard_deviation_ms = delay_standard_deviation_ms;
return *this;
}
NetworkEmulationManager::SimulatedNetworkNode::Builder&
NetworkEmulationManager::SimulatedNetworkNode::Builder::allow_reordering() {
config_.allow_reordering = true;
return *this;
}
NetworkEmulationManager::SimulatedNetworkNode::Builder&
NetworkEmulationManager::SimulatedNetworkNode::Builder::avg_burst_loss_length(
int avg_burst_loss_length) {
config_.avg_burst_loss_length = avg_burst_loss_length;
return *this;
}
NetworkEmulationManager::SimulatedNetworkNode::Builder&
NetworkEmulationManager::SimulatedNetworkNode::Builder::packet_overhead(
int packet_overhead) {
config_.packet_overhead = packet_overhead;
return *this;
}
NetworkEmulationManager::SimulatedNetworkNode
NetworkEmulationManager::SimulatedNetworkNode::Builder::Build(
uint64_t random_seed) const {
RTC_CHECK(net_);
return Build(net_, random_seed);
}
NetworkEmulationManager::SimulatedNetworkNode
NetworkEmulationManager::SimulatedNetworkNode::Builder::Build(
NetworkEmulationManager* net,
uint64_t random_seed) const {
RTC_CHECK(net);
RTC_CHECK(net_ == nullptr || net_ == net);
SimulatedNetworkNode res;
auto behavior = std::make_unique<SimulatedNetwork>(config_, random_seed);
res.simulation = behavior.get();
res.node = net->CreateEmulatedNode(std::move(behavior));
return res;
}
std::pair<EmulatedNetworkManagerInterface*, EmulatedNetworkManagerInterface*>
NetworkEmulationManager::CreateEndpointPairWithTwoWayRoutes(
const BuiltInNetworkBehaviorConfig& config) {
auto* alice_node = CreateEmulatedNode(config);
auto* bob_node = CreateEmulatedNode(config);
auto* alice_endpoint = CreateEndpoint(EmulatedEndpointConfig());
auto* bob_endpoint = CreateEndpoint(EmulatedEndpointConfig());
CreateRoute(alice_endpoint, {alice_node}, bob_endpoint);
CreateRoute(bob_endpoint, {bob_node}, alice_endpoint);
return {
CreateEmulatedNetworkManagerInterface({alice_endpoint}),
CreateEmulatedNetworkManagerInterface({bob_endpoint}),
};
}
} // namespace webrtc

View file

@ -0,0 +1,361 @@
/*
* Copyright (c) 2019 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.
*/
#ifndef API_TEST_NETWORK_EMULATION_MANAGER_H_
#define API_TEST_NETWORK_EMULATION_MANAGER_H_
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "api/array_view.h"
#include "api/packet_socket_factory.h"
#include "api/test/network_emulation/cross_traffic.h"
#include "api/test/network_emulation/network_emulation_interfaces.h"
#include "api/test/peer_network_dependencies.h"
#include "api/test/simulated_network.h"
#include "api/test/time_controller.h"
#include "api/units/timestamp.h"
#include "rtc_base/network.h"
#include "rtc_base/network_constants.h"
#include "rtc_base/thread.h"
namespace webrtc {
// This API is still in development and can be changed without prior notice.
// These classes are forward declared here, because they used as handles, to
// make it possible for client code to operate with these abstractions and build
// required network configuration. With forward declaration here implementation
// is more readable, than with interfaces approach and cause user needn't any
// API methods on these abstractions it is acceptable here.
// EmulatedNetworkNode is an abstraction for some network in the real world,
// like 3G network between peers, or Wi-Fi for one peer and LTE for another.
// Multiple networks can be joined into chain emulating a network path from
// one peer to another.
class EmulatedNetworkNode;
// EmulatedRoute is handle for single route from one network interface on one
// peer device to another network interface on another peer device.
class EmulatedRoute;
enum class EmulatedNetworkStatsGatheringMode {
// Gather main network stats counters. See more details on which particular
// metrics are collected in the `EmulatedNetworkStats` and
// `EmulatedNetworkNodeStats` documentation.
kDefault,
// kDefault + also gather per packet statistics. In this mode more memory
// will be used.
kDebug
};
struct EmulatedEndpointConfig {
enum class IpAddressFamily { kIpv4, kIpv6 };
// If specified will be used to name endpoint for logging purposes.
absl::optional<std::string> name = absl::nullopt;
IpAddressFamily generated_ip_family = IpAddressFamily::kIpv4;
// If specified will be used as IP address for endpoint node. Must be unique
// among all created nodes.
absl::optional<rtc::IPAddress> ip;
// Should endpoint be enabled or not, when it will be created.
// Enabled endpoints will be available for webrtc to send packets.
bool start_as_enabled = true;
// Network type which will be used to represent endpoint to WebRTC.
rtc::AdapterType type = rtc::AdapterType::ADAPTER_TYPE_UNKNOWN;
// Allow endpoint to send packets specifying source IP address different to
// the current endpoint IP address. If false endpoint will crash if attempt
// to send such packet will be done.
bool allow_send_packet_with_different_source_ip = false;
// Allow endpoint to receive packet with destination IP address different to
// the current endpoint IP address. If false endpoint will crash if such
// packet will arrive.
bool allow_receive_packets_with_different_dest_ip = false;
};
struct EmulatedTURNServerConfig {
EmulatedEndpointConfig client_config;
EmulatedEndpointConfig peer_config;
};
// EmulatedTURNServer is an abstraction for a TURN server.
class EmulatedTURNServerInterface {
public:
struct IceServerConfig {
std::string username;
std::string password;
std::string url;
};
virtual ~EmulatedTURNServerInterface() {}
// Get an IceServer configuration suitable to add to a PeerConnection.
virtual IceServerConfig GetIceServerConfig() const = 0;
// Get non-null client endpoint, an endpoint that accepts TURN allocations.
// This shall typically be connected to one or more webrtc endpoint.
virtual EmulatedEndpoint* GetClientEndpoint() const = 0;
// Returns socket address, which client should use to connect to TURN server
// and do TURN allocation.
virtual rtc::SocketAddress GetClientEndpointAddress() const = 0;
// Get non-null peer endpoint, that is "connected to the internet".
// This shall typically be connected to another TURN server.
virtual EmulatedEndpoint* GetPeerEndpoint() const = 0;
};
// Provide interface to obtain all required objects to inject network emulation
// layer into PeerConnection. Also contains information about network interfaces
// accessible by PeerConnection.
class EmulatedNetworkManagerInterface {
public:
virtual ~EmulatedNetworkManagerInterface() = default;
// Returns non-null pointer to thread that have to be used as network thread
// for WebRTC to properly setup network emulation. Returned thread is owned
// by EmulatedNetworkManagerInterface implementation.
virtual rtc::Thread* network_thread() = 0;
// Returns non-null pointer to network manager that have to be injected into
// WebRTC to properly setup network emulation. Returned manager is owned by
// EmulatedNetworkManagerInterface implementation.
virtual rtc::NetworkManager* network_manager() = 0;
// Returns non-null pointer to packet socket factory that have to be injected
// into WebRTC to properly setup network emulation. Returned factory is owned
// by EmulatedNetworkManagerInterface implementation.
virtual rtc::PacketSocketFactory* packet_socket_factory() = 0;
webrtc::webrtc_pc_e2e::PeerNetworkDependencies network_dependencies() {
return {network_thread(), network_manager(), packet_socket_factory()};
}
// Returns list of endpoints that are associated with this instance. Pointers
// are guaranteed to be non-null and are owned by NetworkEmulationManager.
virtual std::vector<EmulatedEndpoint*> endpoints() const = 0;
// Passes summarized network stats for endpoints for this manager into
// specified `stats_callback`. Callback will be executed on network emulation
// internal task queue.
virtual void GetStats(
std::function<void(EmulatedNetworkStats)> stats_callback) const = 0;
};
enum class TimeMode { kRealTime, kSimulated };
// Called implicitly when parsing an ABSL_FLAG of type TimeMode.
// from the command line flag value `text`.
// Returns `true` and sets `*mode` on success;
// returns `false` and sets `*error` on failure.
bool AbslParseFlag(absl::string_view text, TimeMode* mode, std::string* error);
// AbslUnparseFlag returns a textual flag value corresponding to the TimeMode
// `mode`.
std::string AbslUnparseFlag(TimeMode mode);
// Provides an API for creating and configuring emulated network layer.
// All objects returned by this API are owned by NetworkEmulationManager itself
// and will be deleted when manager will be deleted.
class NetworkEmulationManager {
public:
// Helper struct to simplify creation of simulated network behaviors. Contains
// non-owning pointers as the underlying instances are owned by the manager.
struct SimulatedNetworkNode {
SimulatedNetworkInterface* simulation;
EmulatedNetworkNode* node;
class Builder {
public:
explicit Builder(NetworkEmulationManager* net) : net_(net) {}
Builder() : net_(nullptr) {}
Builder(const Builder&) = default;
// Sets the config state, note that this will replace any previously set
// values.
Builder& config(BuiltInNetworkBehaviorConfig config);
Builder& delay_ms(int queue_delay_ms);
Builder& capacity_kbps(int link_capacity_kbps);
Builder& capacity_Mbps(int link_capacity_Mbps);
Builder& loss(double loss_rate);
Builder& packet_queue_length(int max_queue_length_in_packets);
Builder& delay_standard_deviation_ms(int delay_standard_deviation_ms);
Builder& allow_reordering();
Builder& avg_burst_loss_length(int avg_burst_loss_length);
Builder& packet_overhead(int packet_overhead);
SimulatedNetworkNode Build(uint64_t random_seed = 1) const;
SimulatedNetworkNode Build(NetworkEmulationManager* net,
uint64_t random_seed = 1) const;
private:
NetworkEmulationManager* const net_;
BuiltInNetworkBehaviorConfig config_;
};
};
virtual ~NetworkEmulationManager() = default;
virtual TimeController* time_controller() = 0;
// Returns a mode in which underlying time controller operates.
virtual TimeMode time_mode() const = 0;
// Creates an emulated network node, which represents ideal network with
// unlimited capacity, no delay and no packet loss.
EmulatedNetworkNode* CreateUnconstrainedEmulatedNode() {
return CreateEmulatedNode(BuiltInNetworkBehaviorConfig());
}
// Creates an emulated network node, which represents single network in
// the emulated network layer. Uses default implementation on network behavior
// which can be configured with `config`. `random_seed` can be provided to
// alter randomization behavior.
virtual EmulatedNetworkNode* CreateEmulatedNode(
BuiltInNetworkBehaviorConfig config,
uint64_t random_seed = 1) = 0;
// Creates an emulated network node, which represents single network in
// the emulated network layer. `network_behavior` determines how created node
// will forward incoming packets to the next receiver.
virtual EmulatedNetworkNode* CreateEmulatedNode(
std::unique_ptr<NetworkBehaviorInterface> network_behavior) = 0;
virtual SimulatedNetworkNode::Builder NodeBuilder() = 0;
// Creates an emulated endpoint, which represents single network interface on
// the peer's device.
virtual EmulatedEndpoint* CreateEndpoint(EmulatedEndpointConfig config) = 0;
// Enable emulated endpoint to make it available for webrtc.
// Caller mustn't enable currently enabled endpoint.
virtual void EnableEndpoint(EmulatedEndpoint* endpoint) = 0;
// Disable emulated endpoint to make it unavailable for webrtc.
// Caller mustn't disable currently disabled endpoint.
virtual void DisableEndpoint(EmulatedEndpoint* endpoint) = 0;
// Creates a route between endpoints going through specified network nodes.
// This route is single direction only and describe how traffic that was
// sent by network interface `from` have to be delivered to the network
// interface `to`. Return object can be used to remove created route. The
// route must contains at least one network node inside it.
//
// Assume that E{0-9} are endpoints and N{0-9} are network nodes, then
// creation of the route have to follow these rules:
// 1. A route consists of a source endpoint, an ordered list of one or
// more network nodes, and a destination endpoint.
// 2. If (E1, ..., E2) is a route, then E1 != E2.
// In other words, the source and the destination may not be the same.
// 3. Given two simultaneously existing routes (E1, ..., E2) and
// (E3, ..., E4), either E1 != E3 or E2 != E4.
// In other words, there may be at most one route from any given source
// endpoint to any given destination endpoint.
// 4. Given two simultaneously existing routes (E1, ..., N1, ..., E2)
// and (E3, ..., N2, ..., E4), either N1 != N2 or E2 != E4.
// In other words, a network node may not belong to two routes that lead
// to the same destination endpoint.
virtual EmulatedRoute* CreateRoute(
EmulatedEndpoint* from,
const std::vector<EmulatedNetworkNode*>& via_nodes,
EmulatedEndpoint* to) = 0;
// Creates a route over the given `via_nodes` creating the required endpoints
// in the process. The returned EmulatedRoute pointer can be used in other
// calls as a transport route for message or cross traffic.
virtual EmulatedRoute* CreateRoute(
const std::vector<EmulatedNetworkNode*>& via_nodes) = 0;
// Creates a default route between endpoints going through specified network
// nodes. Default route is used for packet when there is no known route for
// packet's destination IP.
//
// This route is single direction only and describe how traffic that was
// sent by network interface `from` have to be delivered in case if routing
// was unspecified. Return object can be used to remove created route. The
// route must contains at least one network node inside it.
//
// Assume that E{0-9} are endpoints and N{0-9} are network nodes, then
// creation of the route have to follow these rules:
// 1. A route consists of a source endpoint, an ordered list of one or
// more network nodes, and a destination endpoint.
// 2. If (E1, ..., E2) is a route, then E1 != E2.
// In other words, the source and the destination may not be the same.
// 3. Given two simultaneously existing routes (E1, ..., E2) and
// (E3, ..., E4), either E1 != E3 or E2 != E4.
// In other words, there may be at most one route from any given source
// endpoint to any given destination endpoint.
// 4. Given two simultaneously existing routes (E1, ..., N1, ..., E2)
// and (E3, ..., N2, ..., E4), either N1 != N2 or E2 != E4.
// In other words, a network node may not belong to two routes that lead
// to the same destination endpoint.
// 5. Any node N can belong to only one default route.
virtual EmulatedRoute* CreateDefaultRoute(
EmulatedEndpoint* from,
const std::vector<EmulatedNetworkNode*>& via_nodes,
EmulatedEndpoint* to) = 0;
// Removes route previously created by CreateRoute(...).
// Caller mustn't call this function with route, that have been already
// removed earlier. Removing a route that is currently in use will lead to
// packets being dropped.
virtual void ClearRoute(EmulatedRoute* route) = 0;
// Creates a simulated TCP connection using `send_route` for traffic and
// `ret_route` for feedback. This can be used to emulate HTTP cross traffic
// and to implement realistic reliable signaling over lossy networks.
// TODO(srte): Handle clearing of the routes involved.
virtual TcpMessageRoute* CreateTcpRoute(EmulatedRoute* send_route,
EmulatedRoute* ret_route) = 0;
// Creates a route over the given `via_nodes`. Returns an object that can be
// used to emulate network load with cross traffic over the created route.
virtual CrossTrafficRoute* CreateCrossTrafficRoute(
const std::vector<EmulatedNetworkNode*>& via_nodes) = 0;
// Starts generating cross traffic using given `generator`. Takes ownership
// over the generator.
virtual CrossTrafficGenerator* StartCrossTraffic(
std::unique_ptr<CrossTrafficGenerator> generator) = 0;
// Stops generating cross traffic that was started using given `generator`.
// The `generator` shouldn't be used after and the reference may be invalid.
virtual void StopCrossTraffic(CrossTrafficGenerator* generator) = 0;
// Creates EmulatedNetworkManagerInterface which can be used then to inject
// network emulation layer into PeerConnection. `endpoints` - are available
// network interfaces for PeerConnection. If endpoint is enabled, it will be
// immediately available for PeerConnection, otherwise user will be able to
// enable endpoint later to make it available for PeerConnection.
virtual EmulatedNetworkManagerInterface*
CreateEmulatedNetworkManagerInterface(
const std::vector<EmulatedEndpoint*>& endpoints) = 0;
// Passes combined network stats for all specified `endpoints` into specified
// `stats_callback`. Callback will be executed on network emulation
// internal task queue.
virtual void GetStats(
rtc::ArrayView<EmulatedEndpoint* const> endpoints,
std::function<void(EmulatedNetworkStats)> stats_callback) = 0;
// Passes combined network stats for all specified `nodes` into specified
// `stats_callback`. Callback will be executed on network emulation
// internal task queue.
virtual void GetStats(
rtc::ArrayView<EmulatedNetworkNode* const> nodes,
std::function<void(EmulatedNetworkNodeStats)> stats_callback) = 0;
// Create a EmulatedTURNServer.
// The TURN server has 2 endpoints that need to be connected with routes,
// - GetClientEndpoint() - the endpoint that accepts TURN allocations.
// - GetPeerEndpoint() - the endpoint that is "connected to the internet".
virtual EmulatedTURNServerInterface* CreateTURNServer(
EmulatedTURNServerConfig config) = 0;
// Create a pair of EmulatedNetworkManagerInterfaces connected to each other.
std::pair<EmulatedNetworkManagerInterface*, EmulatedNetworkManagerInterface*>
CreateEndpointPairWithTwoWayRoutes(
const BuiltInNetworkBehaviorConfig& config);
};
} // namespace webrtc
#endif // API_TEST_NETWORK_EMULATION_MANAGER_H_

View file

@ -0,0 +1,123 @@
# Copyright (c) 2022 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.
import("../../../webrtc.gni")
rtc_source_set("media_configuration") {
visibility = [ "*" ]
testonly = true
sources = [
"media_configuration.cc",
"media_configuration.h",
]
deps = [
"../..:array_view",
"../..:audio_options_api",
"../..:audio_quality_analyzer_api",
"../..:fec_controller_api",
"../..:frame_generator_api",
"../..:function_view",
"../..:libjingle_peerconnection_api",
"../..:media_stream_interface",
"../..:packet_socket_factory",
"../..:peer_network_dependencies",
"../..:rtp_parameters",
"../..:simulated_network_api",
"../..:stats_observer_interface",
"../..:track_id_stream_info_map",
"../..:video_quality_analyzer_api",
"../../../modules/audio_processing:api",
"../../../rtc_base:checks",
"../../../rtc_base:network",
"../../../rtc_base:rtc_certificate_generator",
"../../../rtc_base:ssl",
"../../../rtc_base:stringutils",
"../../../rtc_base:threading",
"../../../test:fileutils",
"../../../test:video_test_support",
"../../../test/pc/e2e/analyzer/video:video_dumping",
"../../audio:audio_mixer_api",
"../../rtc_event_log",
"../../task_queue",
"../../transport:network_control",
"../../units:time_delta",
"../../video_codecs:video_codecs_api",
"../video:video_frame_writer",
]
absl_deps = [
"//third_party/abseil-cpp/absl/memory",
"//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/types:optional",
]
}
rtc_library("media_quality_test_params") {
visibility = [ "*" ]
testonly = true
sources = [ "media_quality_test_params.h" ]
deps = [
":media_configuration",
"../..:async_dns_resolver",
"../../../api:fec_controller_api",
"../../../api:field_trials_view",
"../../../api:libjingle_peerconnection_api",
"../../../api:packet_socket_factory",
"../../../api/audio:audio_mixer_api",
"../../../api/rtc_event_log",
"../../../api/transport:network_control",
"../../../api/video_codecs:video_codecs_api",
"../../../modules/audio_processing:api",
"../../../p2p:connection",
"../../../p2p:port_allocator",
"../../../p2p:rtc_p2p",
"../../../rtc_base:network",
"../../../rtc_base:rtc_certificate_generator",
"../../../rtc_base:ssl",
"../../../rtc_base:threading",
]
}
rtc_library("peer_configurer") {
visibility = [ "*" ]
testonly = true
sources = [
"peer_configurer.cc",
"peer_configurer.h",
]
deps = [
":media_configuration",
":media_quality_test_params",
"../..:async_dns_resolver",
"../../../api:create_peer_connection_quality_test_frame_generator",
"../../../api:fec_controller_api",
"../../../api:field_trials_view",
"../../../api:frame_generator_api",
"../../../api:ice_transport_interface",
"../../../api:libjingle_peerconnection_api",
"../../../api:peer_network_dependencies",
"../../../api:scoped_refptr",
"../../../api/audio:audio_mixer_api",
"../../../api/audio_codecs:audio_codecs_api",
"../../../api/neteq:neteq_api",
"../../../api/rtc_event_log",
"../../../api/transport:bitrate_settings",
"../../../api/transport:network_control",
"../../../api/video_codecs:video_codecs_api",
"../../../modules/audio_processing:api",
"../../../rtc_base:checks",
"../../../rtc_base:rtc_certificate_generator",
"../../../rtc_base:ssl",
]
absl_deps = [
"//third_party/abseil-cpp/absl/strings",
"//third_party/abseil-cpp/absl/types:optional",
"//third_party/abseil-cpp/absl/types:variant",
]
}

View file

@ -0,0 +1,13 @@
specific_include_rules = {
".*": [
"+modules/audio_processing/include/audio_processing.h",
"+rtc_base/checks.h",
"+rtc_base/network.h",
"+rtc_base/rtc_certificate_generator.h",
"+rtc_base/ssl_certificate.h",
"+rtc_base/thread.h",
],
"media_quality_test_params\.h": [
"+p2p/base/port_allocator.h",
],
}

View file

@ -0,0 +1,312 @@
/*
* Copyright 2022 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 "api/test/pclf/media_configuration.h"
#include <string>
#include <utility>
#include "absl/strings/string_view.h"
#include "absl/types/optional.h"
#include "api/array_view.h"
#include "api/test/video/video_frame_writer.h"
#include "rtc_base/checks.h"
#include "rtc_base/strings/string_builder.h"
#include "test/pc/e2e/analyzer/video/video_dumping.h"
#include "test/testsupport/file_utils.h"
#include "test/testsupport/video_frame_writer.h"
namespace webrtc {
namespace webrtc_pc_e2e {
namespace {
absl::string_view SpecToString(VideoResolution::Spec spec) {
switch (spec) {
case VideoResolution::Spec::kNone:
return "None";
case VideoResolution::Spec::kMaxFromSender:
return "MaxFromSender";
}
}
void AppendResolution(const VideoResolution& resolution,
rtc::StringBuilder& builder) {
builder << "_" << resolution.width() << "x" << resolution.height() << "_"
<< resolution.fps();
}
} // namespace
ScreenShareConfig::ScreenShareConfig(TimeDelta slide_change_interval)
: slide_change_interval(slide_change_interval) {
RTC_CHECK_GT(slide_change_interval.ms(), 0);
}
VideoSimulcastConfig::VideoSimulcastConfig(int simulcast_streams_count)
: simulcast_streams_count(simulcast_streams_count) {
RTC_CHECK_GT(simulcast_streams_count, 1);
}
EmulatedSFUConfig::EmulatedSFUConfig(int target_layer_index)
: target_layer_index(target_layer_index) {
RTC_CHECK_GE(target_layer_index, 0);
}
EmulatedSFUConfig::EmulatedSFUConfig(absl::optional<int> target_layer_index,
absl::optional<int> target_temporal_index)
: target_layer_index(target_layer_index),
target_temporal_index(target_temporal_index) {
RTC_CHECK_GE(target_temporal_index.value_or(0), 0);
if (target_temporal_index)
RTC_CHECK_GE(*target_temporal_index, 0);
}
VideoResolution::VideoResolution(size_t width, size_t height, int32_t fps)
: width_(width), height_(height), fps_(fps), spec_(Spec::kNone) {}
VideoResolution::VideoResolution(Spec spec)
: width_(0), height_(0), fps_(0), spec_(spec) {}
bool VideoResolution::operator==(const VideoResolution& other) const {
if (spec_ != Spec::kNone && spec_ == other.spec_) {
// If there is some particular spec set, then it doesn't matter what
// values we have in other fields.
return true;
}
return width_ == other.width_ && height_ == other.height_ &&
fps_ == other.fps_ && spec_ == other.spec_;
}
bool VideoResolution::operator!=(const VideoResolution& other) const {
return !(*this == other);
}
bool VideoResolution::IsRegular() const {
return spec_ == Spec::kNone;
}
std::string VideoResolution::ToString() const {
rtc::StringBuilder out;
out << "{ width=" << width_ << ", height=" << height_ << ", fps=" << fps_
<< ", spec=" << SpecToString(spec_) << " }";
return out.Release();
}
VideoDumpOptions::VideoDumpOptions(
absl::string_view output_directory,
int sampling_modulo,
bool export_frame_ids,
std::function<std::unique_ptr<test::VideoFrameWriter>(
absl::string_view file_name_prefix,
const VideoResolution& resolution)> video_frame_writer_factory)
: output_directory_(output_directory),
sampling_modulo_(sampling_modulo),
export_frame_ids_(export_frame_ids),
video_frame_writer_factory_(video_frame_writer_factory) {
RTC_CHECK_GT(sampling_modulo, 0);
}
VideoDumpOptions::VideoDumpOptions(absl::string_view output_directory,
bool export_frame_ids)
: VideoDumpOptions(output_directory,
kDefaultSamplingModulo,
export_frame_ids) {}
std::unique_ptr<test::VideoFrameWriter>
VideoDumpOptions::CreateInputDumpVideoFrameWriter(
absl::string_view stream_label,
const VideoResolution& resolution) const {
std::unique_ptr<test::VideoFrameWriter> writer = video_frame_writer_factory_(
GetInputDumpFileName(stream_label, resolution), resolution);
absl::optional<std::string> frame_ids_file =
GetInputFrameIdsDumpFileName(stream_label, resolution);
if (frame_ids_file.has_value()) {
writer = CreateVideoFrameWithIdsWriter(std::move(writer), *frame_ids_file);
}
return writer;
}
std::unique_ptr<test::VideoFrameWriter>
VideoDumpOptions::CreateOutputDumpVideoFrameWriter(
absl::string_view stream_label,
absl::string_view receiver,
const VideoResolution& resolution) const {
std::unique_ptr<test::VideoFrameWriter> writer = video_frame_writer_factory_(
GetOutputDumpFileName(stream_label, receiver, resolution), resolution);
absl::optional<std::string> frame_ids_file =
GetOutputFrameIdsDumpFileName(stream_label, receiver, resolution);
if (frame_ids_file.has_value()) {
writer = CreateVideoFrameWithIdsWriter(std::move(writer), *frame_ids_file);
}
return writer;
}
std::unique_ptr<test::VideoFrameWriter>
VideoDumpOptions::Y4mVideoFrameWriterFactory(
absl::string_view file_name_prefix,
const VideoResolution& resolution) {
return std::make_unique<test::Y4mVideoFrameWriterImpl>(
std::string(file_name_prefix) + ".y4m", resolution.width(),
resolution.height(), resolution.fps());
}
std::string VideoDumpOptions::GetInputDumpFileName(
absl::string_view stream_label,
const VideoResolution& resolution) const {
rtc::StringBuilder file_name;
file_name << stream_label;
AppendResolution(resolution, file_name);
return test::JoinFilename(output_directory_, file_name.Release());
}
absl::optional<std::string> VideoDumpOptions::GetInputFrameIdsDumpFileName(
absl::string_view stream_label,
const VideoResolution& resolution) const {
if (!export_frame_ids_) {
return absl::nullopt;
}
return GetInputDumpFileName(stream_label, resolution) + ".frame_ids.txt";
}
std::string VideoDumpOptions::GetOutputDumpFileName(
absl::string_view stream_label,
absl::string_view receiver,
const VideoResolution& resolution) const {
rtc::StringBuilder file_name;
file_name << stream_label << "_" << receiver;
AppendResolution(resolution, file_name);
return test::JoinFilename(output_directory_, file_name.Release());
}
absl::optional<std::string> VideoDumpOptions::GetOutputFrameIdsDumpFileName(
absl::string_view stream_label,
absl::string_view receiver,
const VideoResolution& resolution) const {
if (!export_frame_ids_) {
return absl::nullopt;
}
return GetOutputDumpFileName(stream_label, receiver, resolution) +
".frame_ids.txt";
}
std::string VideoDumpOptions::ToString() const {
rtc::StringBuilder out;
out << "{ output_directory_=" << output_directory_
<< ", sampling_modulo_=" << sampling_modulo_
<< ", export_frame_ids_=" << export_frame_ids_ << " }";
return out.Release();
}
VideoConfig::VideoConfig(const VideoResolution& resolution)
: width(resolution.width()),
height(resolution.height()),
fps(resolution.fps()) {
RTC_CHECK(resolution.IsRegular());
}
VideoConfig::VideoConfig(size_t width, size_t height, int32_t fps)
: width(width), height(height), fps(fps) {}
VideoConfig::VideoConfig(absl::string_view stream_label,
size_t width,
size_t height,
int32_t fps)
: width(width), height(height), fps(fps), stream_label(stream_label) {}
AudioConfig::AudioConfig(absl::string_view stream_label)
: stream_label(stream_label) {}
VideoCodecConfig::VideoCodecConfig(absl::string_view name)
: name(name), required_params() {}
VideoCodecConfig::VideoCodecConfig(
absl::string_view name,
std::map<std::string, std::string> required_params)
: name(name), required_params(std::move(required_params)) {}
absl::optional<VideoResolution> VideoSubscription::GetMaxResolution(
rtc::ArrayView<const VideoConfig> video_configs) {
std::vector<VideoResolution> resolutions;
for (const auto& video_config : video_configs) {
resolutions.push_back(video_config.GetResolution());
}
return GetMaxResolution(resolutions);
}
absl::optional<VideoResolution> VideoSubscription::GetMaxResolution(
rtc::ArrayView<const VideoResolution> resolutions) {
if (resolutions.empty()) {
return absl::nullopt;
}
VideoResolution max_resolution;
for (const VideoResolution& resolution : resolutions) {
if (max_resolution.width() < resolution.width()) {
max_resolution.set_width(resolution.width());
}
if (max_resolution.height() < resolution.height()) {
max_resolution.set_height(resolution.height());
}
if (max_resolution.fps() < resolution.fps()) {
max_resolution.set_fps(resolution.fps());
}
}
return max_resolution;
}
bool VideoSubscription::operator==(const VideoSubscription& other) const {
return default_resolution_ == other.default_resolution_ &&
peers_resolution_ == other.peers_resolution_;
}
bool VideoSubscription::operator!=(const VideoSubscription& other) const {
return !(*this == other);
}
VideoSubscription& VideoSubscription::SubscribeToPeer(
absl::string_view peer_name,
VideoResolution resolution) {
peers_resolution_[std::string(peer_name)] = resolution;
return *this;
}
VideoSubscription& VideoSubscription::SubscribeToAllPeers(
VideoResolution resolution) {
default_resolution_ = resolution;
return *this;
}
absl::optional<VideoResolution> VideoSubscription::GetResolutionForPeer(
absl::string_view peer_name) const {
auto it = peers_resolution_.find(std::string(peer_name));
if (it == peers_resolution_.end()) {
return default_resolution_;
}
return it->second;
}
std::vector<std::string> VideoSubscription::GetSubscribedPeers() const {
std::vector<std::string> subscribed_streams;
subscribed_streams.reserve(peers_resolution_.size());
for (const auto& entry : peers_resolution_) {
subscribed_streams.push_back(entry.first);
}
return subscribed_streams;
}
std::string VideoSubscription::ToString() const {
rtc::StringBuilder out;
out << "{ default_resolution_=[";
if (default_resolution_.has_value()) {
out << default_resolution_->ToString();
} else {
out << "undefined";
}
out << "], {";
for (const auto& [peer_name, resolution] : peers_resolution_) {
out << "[" << peer_name << ": " << resolution.ToString() << "], ";
}
out << "} }";
return out.Release();
}
} // namespace webrtc_pc_e2e
} // namespace webrtc

Some files were not shown because too many files have changed in this diff Show more