refactor: clarify C++ declarations across 13 Sonar rules (#5934)

This commit is contained in:
Dave Lane authored and GitHub committed 2026-10-14 23:22:57 -04:00
1 parent 2e4d4f5ac0
commit a36b9f8a9c
44 files changed
+342 -162

No files matched your search

+1 -1
View File
@@ -237,7 +237,7 @@ namespace audio {
auto samples = std::make_shared<sample_queue_t::element_type>(30);
std::jthread thread {encodeThread, samples, config, channel_data};
auto fg = util::fail_guard([&]() {
auto fg = util::fail_guard([&samples, &thread, &shutdown_event]() {
samples->stop();
thread.join();
+6 -2
View File
@@ -224,7 +224,9 @@ namespace config {
bool config_revert_on_disconnect; ///< Specify whether to revert display configuration on client disconnect.
mode_remapping_t mode_remapping; ///< Display mode remapping rules grouped by automatic selection mode.
workarounds_t wa; ///< Display-device compatibility workarounds.
} dd; ///< Display-device integration settings.
};
dd_t dd; ///< Display-device integration settings.
int max_bitrate; ///< Maximum bitrate ceiling in kbps for bitrate requested from the client.
double minimum_fps_target; ///< Lowest framerate that will be used when streaming. Range 0-1000, 0 = half of client's requested framerate.
@@ -387,7 +389,9 @@ namespace config {
std::string name; ///< Executable name from the command line.
int argc; ///< Number of command-line arguments.
char **argv; ///< Command-line argument vector.
} cmd; ///< Command line used to launch the application.
};
cmd_t cmd; ///< Command line used to launch the application.
std::uint16_t port; ///< TCP port used by Sunshine services.
std::string address_family; ///< Address family requested for listening sockets.
+1 -1
View File
@@ -947,7 +947,7 @@ namespace input {
* when the last mouse coordinates were absolute
*/
if (button == BUTTON_LEFT && release && !input->mouse_left_button_timeout) {
auto f = [=]() {
auto f = [input, release]() {
if (auto left_released = mouse_press[BUTTON_LEFT]; left_released) {
// Already released left button
return;
+1 -1
View File
@@ -96,7 +96,7 @@ namespace logging {
log_type = "Tests: "sv;
break;
#endif
};
}
auto now = std::chrono::system_clock::now();
auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
+2 -2
View File
@@ -179,13 +179,13 @@ namespace logging {
}
/**
* @brief Collect a metric sample and write it to the periodic log when due.
* @brief Collect a metric sample and report it with this logger's configured message and units when due.
*
* @param value Numeric sample to include in the next periodic aggregate.
*/
void collect_and_log(const T &value) {
if (enabled) {
auto print_info = [&](const T &min_value, const T &max_value, double avg_value) {
auto print_info = [this](const T &min_value, const T &max_value, double avg_value) {
auto f = stat_trackers::two_digits_after_decimal();
if constexpr (std::is_floating_point_v<T>) {
BOOST_LOG(severity.get()) << message << " (min/max/avg): " << f % min_value << units << "/" << f % max_value << units << "/" << f % avg_value << units;
+5 -5
View File
@@ -63,29 +63,29 @@ namespace net {
}
/**
* @brief Convert a Boost address family to Sunshine network enum value.
* @brief Classify an address using the configured loopback and LAN ranges without modifying them.
*/
net_e from_address(const std::string_view &view) {
if (auto addr = normalize_address(ip::make_address(view)); addr.is_v6()) {
for (auto &range : pc_ips_v6) {
for (const auto &range : pc_ips_v6) {
if (range.hosts().find(addr.to_v6()) != range.hosts().end()) {
return PC;
}
}
for (auto &range : lan_ips_v6) {
for (const auto &range : lan_ips_v6) {
if (range.hosts().find(addr.to_v6()) != range.hosts().end()) {
return LAN;
}
}
} else {
for (auto &range : pc_ips_v4) {
for (const auto &range : pc_ips_v4) {
if (range.hosts().find(addr.to_v4()) != range.hosts().end()) {
return PC;
}
}
for (auto &range : lan_ips_v4) {
for (const auto &range : lan_ips_v4) {
if (range.hosts().find(addr.to_v4()) != range.hosts().end()) {
return LAN;
}
+1 -3
View File
@@ -188,9 +188,7 @@ namespace NVENC_NAMESPACE {
device_type(device_type) {
}
nvenc_base::~nvenc_base() {
// Use destroy_encoder() instead
}
nvenc_base::~nvenc_base() = default;
int nvenc_base::get_encoder_cap(const GUID &encode_guid, NV_ENC_CAPS cap) const {
NV_ENC_CAPS_PARAM param = {.version = NV_ENC_CAPS_PARAM_VER};
+3
View File
@@ -44,6 +44,9 @@ namespace NVENC_NAMESPACE {
* @param device_type Underlying device type used by derived class.
*/
explicit nvenc_base(NV_ENC_DEVICE_TYPE device_type);
/**
* @brief Destroy the encoder wrapper; callers release the encoder session with destroy_encoder().
*/
~nvenc_base() override;
nvenc_base(const nvenc_base &) = delete;
+1 -1
View File
@@ -178,7 +178,7 @@ namespace NVENC_NAMESPACE {
return false;
}
auto unmap = [&]() -> bool {
auto unmap = [&]() {
if (cuda_failed(cuda_functions.cuGraphicsUnmapResources(1, &cuda_d3d_input_texture, 0))) {
BOOST_LOG(error) << "NvEnc: cuGraphicsUnmapResources() failed: error " << last_cuda_error;
return false;
+8 -6
View File
@@ -172,7 +172,9 @@ namespace nvhttp {
struct conf_intern_t {
std::string servercert; ///< Server certificate PEM string.
std::string pkey; ///< Private key PEM string or path.
} conf_intern; ///< TLS credential paths loaded from Sunshine's runtime configuration.
};
conf_intern_t conf_intern; ///< TLS credential paths loaded from Sunshine's runtime configuration.
/**
* @brief Certificate entry associated with a client name and UUID.
@@ -491,7 +493,7 @@ namespace nvhttp {
std::copy(rikey.cbegin(), rikey.cend(), std::back_inserter(launch_session->gcm_key));
launch_session->host_audio = host_audio;
std::stringstream mode = std::stringstream(get_arg(args, "mode", "0x0x0"));
auto mode = std::stringstream(get_arg(args, "mode", "0x0x0"));
// Split mode by the char "x", to populate width/height/fps
int x = 0;
std::string segment;
@@ -1082,7 +1084,7 @@ namespace nvhttp {
pt::ptree tree;
auto fg = util::fail_guard([&]() {
auto fg = util::fail_guard([&tree, &response]() {
std::ostringstream data;
pt::write_xml(data, tree);
@@ -1339,7 +1341,7 @@ namespace nvhttp {
pt::ptree tree;
auto g = util::fail_guard([&]() {
auto g = util::fail_guard([&tree, &response]() {
std::ostringstream data;
pt::write_xml(data, tree);
@@ -1588,7 +1590,7 @@ namespace nvhttp {
print_req<SunshineHTTPS>(request);
pt::ptree tree;
auto g = util::fail_guard([&]() {
auto g = util::fail_guard([&tree, &response]() {
std::ostringstream data;
pt::write_xml(data, tree);
@@ -1716,7 +1718,7 @@ namespace nvhttp {
https_server.on_verify_failed = [](resp_https_t resp, req_https_t req) {
pt::ptree tree;
auto g = util::fail_guard([&]() {
auto g = util::fail_guard([&tree, &resp]() {
std::ostringstream data;
pt::write_xml(data, tree);
+4 -4
View File
@@ -1050,11 +1050,11 @@ namespace platf {
boost::asio::ip::address &source_address; ///< Local source IP address for outgoing packets.
/**
* @brief Returns a payload buffer descriptor for the given payload offset.
* @brief Locate a payload region without changing the batch descriptor or its buffers.
* @param offset The offset in the total payload data (bytes).
* @return Buffer descriptor describing the region at the given offset.
*/
buffer_descriptor_t buffer_for_payload_offset(ptrdiff_t offset) {
buffer_descriptor_t buffer_for_payload_offset(ptrdiff_t offset) const {
for (const auto &desc : payload_buffers) {
if (offset < desc.size) {
return {
@@ -1070,12 +1070,12 @@ namespace platf {
};
/**
* @brief Send multiple fixed-size UDP payload blocks using the platform backend.
* @brief Send fixed-size UDP payload blocks without modifying the batch descriptor.
*
* @param send_info Socket addresses, buffers, and sizes for the send operation.
* @return True when all requested packet blocks are submitted to the socket.
*/
bool send_batch(batched_send_info_t &send_info);
bool send_batch(const batched_send_info_t &send_info);
/**
* @brief Destination address and payload data for one UDP send.
+1 -1
View File
@@ -299,7 +299,7 @@ namespace platf {
* @param userdata Caller-provided pointer passed through the callback.
*/
void ctx_state_cb(ctx_t::pointer ctx, void *userdata) {
auto &f = *(std::function<void(ctx_t::pointer)> *) userdata;
const auto &f = *(std::function<void(ctx_t::pointer)> *) userdata;
f(ctx);
}
+6 -1
View File
@@ -698,7 +698,12 @@ namespace cuda {
static PNVFBCCREATEINSTANCE createInstance {};
static NVFBC_API_FUNCTION_LIST func {.dwVersion = NVFBC_VERSION};
static constexpr inline NVFBC_BOOL nv_bool(bool b) {
/**
* @brief Convert a C++ boolean to the corresponding NvFBC flag.
* @param b Boolean state to convert.
* @return NVFBC_TRUE when b is true, otherwise NVFBC_FALSE.
*/
static constexpr NVFBC_BOOL nv_bool(bool b) {
return b ? NVFBC_TRUE : NVFBC_FALSE;
}
+19 -5
View File
@@ -41,11 +41,20 @@ namespace cuda {
int init();
} // namespace cuda
typedef struct cudaArray *cudaArray_t;
/**
* @brief Opaque CUDA array handle shared by host and CUDA compiler declarations.
*/
using cudaArray_t = struct cudaArray *;
#if !defined(__CUDACC__)
typedef struct CUstream_st *cudaStream_t;
typedef unsigned long long cudaTextureObject_t;
/**
* @brief Opaque CUDA stream handle for host compilation.
*/
using cudaStream_t = struct CUstream_st *;
/**
* @brief CUDA texture object identifier for host compilation.
*/
using cudaTextureObject_t = unsigned long long;
#else /* defined(__CUDACC__) */
typedef __location__(device_builtin) struct CUstream_st *cudaStream_t;
typedef __location__(device_builtin) unsigned long long cudaTextureObject_t;
@@ -90,10 +99,15 @@ namespace cuda {
cudaArray_t array;
struct texture {
/**
* @brief CUDA texture objects for point and linear sampling of the same image.
*/
struct texture_t {
cudaTextureObject_t point;
cudaTextureObject_t linear;
} texture;
};
texture_t texture; ///< Point and linear sampling textures for the CUDA array.
};
class sws_t {
+1 -1
View File
@@ -1342,7 +1342,7 @@ namespace egl {
}
int sws_t::blank(gl::frame_buf_t &fb, int offsetX_, int offsetY_, int width, int height, bool is_yuv444) {
auto f = [&]() {
auto f = [this, &offsetX_, &offsetY_, &width, &height]() {
std::swap(offsetX_, this->offsetX);
std::swap(offsetY_, this->offsetY);
std::swap(width, this->out_width);
+7 -7
View File
@@ -1367,7 +1367,7 @@ namespace platf {
*
* @return True when the active display mode is HDR.
*/
bool is_hdr() {
bool is_hdr() override {
if (!hdr_metadata_blob_id || *hdr_metadata_blob_id == 0) {
return false;
}
@@ -1419,7 +1419,7 @@ namespace platf {
* @param metadata Output structure populated with HDR metadata.
* @return True when HDR metadata was written to the output structure.
*/
bool get_hdr_metadata(SS_HDR_METADATA &metadata) {
bool get_hdr_metadata(SS_HDR_METADATA &metadata) override {
// This performs all the metadata validation
if (!is_hdr()) {
return false;
@@ -2080,7 +2080,7 @@ namespace platf {
* @param cursor Whether the cursor should be included in the captured image.
* @return Final capture status reported to the streaming pipeline.
*/
capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) {
capture_e capture(const push_captured_image_cb_t &push_captured_image_cb, const pull_free_image_cb_t &pull_free_image_cb, bool *cursor) override {
using enum platf::capture_e;
auto next_frame = std::chrono::steady_clock::now();
@@ -2429,11 +2429,11 @@ namespace platf {
BOOST_LOG(debug) << "Resolution: "sv << monitor_descriptor.viewport.width << 'x' << monitor_descriptor.viewport.height;
BOOST_LOG(debug) << "Offset: "sv << monitor_descriptor.viewport.offset_x << 'x' << monitor_descriptor.viewport.offset_y;
kms::env_width = std::max(kms::env_width, (int) (monitor_descriptor.viewport.offset_x + monitor_descriptor.viewport.width));
kms::env_height = std::max(kms::env_height, (int) (monitor_descriptor.viewport.offset_y + monitor_descriptor.viewport.height));
kms::env_width = std::max(kms::env_width, monitor_descriptor.viewport.offset_x + monitor_descriptor.viewport.width);
kms::env_height = std::max(kms::env_height, monitor_descriptor.viewport.offset_y + monitor_descriptor.viewport.height);
kms::env_logical_height = std::max(kms::env_logical_height, (int) (monitor_descriptor.viewport.offset_y + monitor_descriptor.viewport.logical_height));
kms::env_logical_width = std::max(kms::env_logical_width, (int) (monitor_descriptor.viewport.offset_x + monitor_descriptor.viewport.logical_width));
kms::env_logical_height = std::max(kms::env_logical_height, monitor_descriptor.viewport.offset_y + monitor_descriptor.viewport.logical_height);
kms::env_logical_width = std::max(kms::env_logical_width, monitor_descriptor.viewport.offset_x + monitor_descriptor.viewport.logical_width);
}
}
+3 -3
View File
@@ -625,7 +625,7 @@ namespace platf {
executable[len] = '\0';
// ASIO doesn't use O_CLOEXEC, so we have to close all fds ourselves
int openmax = (int) sysconf(_SC_OPEN_MAX);
auto openmax = (int) sysconf(_SC_OPEN_MAX);
for (int fd = STDERR_FILENO + 1; fd < openmax; fd++) {
close(fd);
}
@@ -756,7 +756,7 @@ namespace platf {
/**
* @brief Send multiple fixed-size UDP payload blocks using the platform backend.
*/
bool send_batch(batched_send_info_t &send_info) {
bool send_batch(const batched_send_info_t &send_info) {
auto sockfd = (int) send_info.native_socket;
struct msghdr msg = {};
@@ -1059,7 +1059,7 @@ namespace platf {
* @brief Enables QoS on the given socket for traffic to the specified destination.
*/
std::unique_ptr<deinit_t> enable_socket_qos(uintptr_t native_socket, boost::asio::ip::address &address, uint16_t port, qos_data_type_e data_type, bool dscp_tagging) {
int sockfd = (int) native_socket;
auto sockfd = (int) native_socket;
std::vector<std::tuple<int, int, int>> reset_options;
if (dscp_tagging) {
+3 -3
View File
@@ -477,7 +477,7 @@ namespace platf {
auto pixels_begin = &pixels[(y + overlay->y) * (img.row_pitch / img.pixel_pitch) + overlay->x];
std::for_each(overlay_begin, overlay_end, [&](long pixel) {
int *pixel_p = (int *) &pixel;
auto pixel_p = (int *) &pixel;
auto colors_in = (uint8_t *) pixels_begin;
@@ -720,8 +720,8 @@ namespace platf {
// TODO: stop cheating and give black image
if (!img) {
return -1;
};
auto pull_dummy_img_callback = [&img](std::shared_ptr<platf::img_t> &img_out) -> bool {
}
auto pull_dummy_img_callback = [&img](std::shared_ptr<platf::img_t> &img_out) {
img_out = img->shared_from_this();
return true;
};
+1 -1
View File
@@ -654,7 +654,7 @@ namespace platf {
return saddr_v6;
}
bool send_batch(batched_send_info_t &send_info) {
bool send_batch(const batched_send_info_t &send_info) {
// Fall back to unbatched send calls
return false;
}
+17 -15
View File
@@ -441,8 +441,7 @@ namespace platf::audio {
*/
class audio_notification_t: public ::IMMNotificationClient {
public:
audio_notification_t() {
}
audio_notification_t() = default;
// IUnknown implementation (unused by IMMDeviceEnumerator)
/**
@@ -450,7 +449,7 @@ namespace platf::audio {
*
* @return Static reference count because the callback lifetime is externally owned.
*/
ULONG STDMETHODCALLTYPE AddRef() {
ULONG STDMETHODCALLTYPE AddRef() override {
return 1;
}
@@ -459,18 +458,18 @@ namespace platf::audio {
*
* @return Reference count or status returned after releasing the object.
*/
ULONG STDMETHODCALLTYPE Release() {
ULONG STDMETHODCALLTYPE Release() override {
return 1;
}
/**
* @brief Return the supported COM interface for the notification callback.
* @brief Implement IUnknown's interface query for the endpoint notification callback.
*
* @param riid COM interface identifier requested by QueryInterface.
* @param ppvInterface Output pointer receiving the requested interface.
* @return S_OK when the interface is supported; E_NOINTERFACE otherwise.
*/
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) { // NOSONAR(cpp:S5008): IUnknown::QueryInterface requires a void double pointer in the COM ABI.
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, VOID **ppvInterface) override { // NOSONAR(cpp:S5008): IUnknown::QueryInterface requires a void double pointer in the COM ABI.
if (IID_IUnknown == riid) {
AddRef();
*ppvInterface = (IUnknown *) this;
@@ -494,7 +493,7 @@ namespace platf::audio {
* @param pwstrDeviceId Windows endpoint ID for the new default device.
* @return S_OK after recording the render-device change notification.
*/
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) {
HRESULT STDMETHODCALLTYPE OnDefaultDeviceChanged(EDataFlow flow, ERole role, LPCWSTR pwstrDeviceId) override {
if (flow == eRender) {
default_render_device_changed_flag.store(true);
}
@@ -507,7 +506,7 @@ namespace platf::audio {
* @param pwstrDeviceId Windows endpoint ID for the added device.
* @return S_OK because Sunshine does not act on this notification.
*/
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) {
HRESULT STDMETHODCALLTYPE OnDeviceAdded(LPCWSTR pwstrDeviceId) override {
return S_OK;
}
@@ -517,7 +516,7 @@ namespace platf::audio {
* @param pwstrDeviceId Windows endpoint ID for the removed device.
* @return S_OK because Sunshine does not act on this notification.
*/
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) {
HRESULT STDMETHODCALLTYPE OnDeviceRemoved(LPCWSTR pwstrDeviceId) override {
return S_OK;
}
@@ -531,7 +530,7 @@ namespace platf::audio {
HRESULT STDMETHODCALLTYPE OnDeviceStateChanged(
LPCWSTR pwstrDeviceId,
DWORD dwNewState
) {
) override {
return S_OK;
}
@@ -545,7 +544,7 @@ namespace platf::audio {
HRESULT STDMETHODCALLTYPE OnPropertyValueChanged(
LPCWSTR pwstrDeviceId,
const PROPERTYKEY key
) {
) override {
return S_OK;
}
@@ -751,12 +750,16 @@ namespace platf::audio {
struct sample_aligned_t {
std::uint32_t uninitialized;
float *samples;
} sample_aligned;
};
sample_aligned_t sample_aligned;
// number of samples / number of channels
struct block_aligned_t {
std::uint32_t audio_sample_size;
} block_aligned;
};
block_aligned_t block_aligned;
// Check if the default audio device has changed
if (endpt_notification.check_default_render_device_changed()) {
@@ -1448,8 +1451,7 @@ namespace platf::audio {
/**
* @brief Destroy the Windows audio control.
*/
~audio_control_t() override {
}
~audio_control_t() override = default;
policy_t policy; ///< Windows policy configuration interface used to switch default audio devices.
audio::device_enum_t device_enum; ///< Device enumerator used to query and watch audio endpoints.
+2 -2
View File
@@ -452,14 +452,14 @@ namespace platf::dxgi {
*
* @return True when the active display mode is HDR.
*/
virtual bool is_hdr() override;
bool is_hdr() override;
/**
* @brief Read HDR metadata for the active display mode.
*
* @param metadata Output structure populated with HDR metadata.
* @return True when HDR metadata was written to `metadata`.
*/
virtual bool get_hdr_metadata(SS_HDR_METADATA &metadata) override;
bool get_hdr_metadata(SS_HDR_METADATA &metadata) override;
/**
* @brief Convert a DXGI format enum to a diagnostic string.
+1 -1
View File
@@ -647,7 +647,7 @@ namespace platf::dxgi {
HMODULE gdi32 = GetModuleHandleA("GDI32");
if (gdi32) {
auto check_hags = [&](const LUID &adapter_luid) -> bool {
auto check_hags = [&](const LUID &adapter_luid) {
auto d3dkmt_open_adapter = (PD3DKMTOpenAdapterFromLuid) GetProcAddress(gdi32, "D3DKMTOpenAdapterFromLuid");
auto d3dkmt_query_adapter_info = (PD3DKMTQueryAdapterInfo) GetProcAddress(gdi32, "D3DKMTQueryAdapterInfo");
auto d3dkmt_close_adapter = (PD3DKMTCloseAdapter) GetProcAddress(gdi32, "D3DKMTCloseAdapter");
+3 -3
View File
@@ -603,7 +603,7 @@ namespace platf::dxgi {
frame_texture->AddRef();
output_texture.reset(frame_texture);
HRESULT status = S_OK;
auto status = S_OK;
#ifndef DOXYGEN
#define create_vertex_shader_helper(x, y) \
@@ -785,7 +785,7 @@ namespace platf::dxgi {
return -1;
}
auto create_rtv = [&](auto &rt, DXGI_FORMAT rt_format) -> bool {
auto create_rtv = [&](auto &rt, DXGI_FORMAT rt_format) {
D3D11_RENDER_TARGET_VIEW_DESC rtv_desc = {};
rtv_desc.Format = rt_format;
rtv_desc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
@@ -1520,7 +1520,7 @@ namespace platf::dxgi {
}
}
auto create_surface = [&](texture2d_t &surface) -> bool {
auto create_surface = [&](texture2d_t &surface) {
// Try to reuse the old surface if it hasn't been destroyed yet.
if (old_surface_delayed_destruction) {
surface.reset(old_surface_delayed_destruction.release());
+3 -3
View File
@@ -632,7 +632,7 @@ namespace platf {
if (file) {
// If a file was provided, get its handle and use it as the standard output and error for the new process
HANDLE log_file_handle = (HANDLE) _get_osfhandle(_fileno(file));
auto log_file_handle = (HANDLE) _get_osfhandle(_fileno(file));
// Populate std handles if the caller gave us a log file to use
startup_info.StartupInfo.dwFlags |= STARTF_USESTDHANDLES;
@@ -1045,7 +1045,7 @@ namespace platf {
sunshine_wenv[path_var_name].assign(start_dir + L";" + old_path_val);
// Restore the old PATH value for our process when we're done here
auto restore_path = util::fail_guard([&]() {
auto restore_path = util::fail_guard([&old_path_val, &sunshine_wenv, &path_var_name]() {
if (old_path_val.empty()) {
sunshine_wenv[path_var_name].clear();
} else {
@@ -1505,7 +1505,7 @@ namespace platf {
// Use UDP segmentation offload if it is supported by the OS. If the NIC is capable, this will use
// hardware acceleration to reduce CPU usage. Support for USO was introduced in Windows 10 20H1.
bool send_batch(batched_send_info_t &send_info) {
bool send_batch(const batched_send_info_t &send_info) {
WSAMSG msg;
// Convert the target address into a SOCKADDR
@@ -42,7 +42,7 @@ namespace nvprefs {
* @param p LocalAlloc-compatible pointer to release.
*/
template<class T>
void operator()(T *p) {
void operator()(T *p) const {
LocalFree(p);
}
};
@@ -141,7 +141,7 @@ namespace nvprefs {
return true;
}
auto make_undo_and_commit = [&]() -> bool {
auto make_undo_and_commit = [&]() {
// Create and lock undo file if it hasn't been done yet
if (!pimpl->undo_file) {
// Prepare Sunshine folder in ProgramData if it doesn't exist
@@ -193,7 +193,7 @@ namespace nvprefs {
return true;
}
bool nvprefs_interface::owning_undo_file() {
bool nvprefs_interface::owning_undo_file() const {
return pimpl->undo_file.has_value();
}
@@ -53,9 +53,9 @@ namespace nvprefs {
/**
* @brief Check whether this interface owns the active undo file.
*
* @return True when the NVIDIA preferences operation succeeds.
* @return True when this interface owns the undo file; querying does not load NVIDIA APIs.
*/
bool owning_undo_file();
bool owning_undo_file() const;
/**
* @brief Restore NVIDIA global profile settings from saved undo data.
+1 -1
View File
@@ -230,7 +230,7 @@ namespace proc {
std::error_code ec;
// Executed when returning from function
auto fg = util::fail_guard([&]() {
auto fg = util::fail_guard([this]() {
terminate();
});
+1 -53
View File
@@ -51,58 +51,6 @@ namespace rtsp_stream {
delete msg;
}
#pragma pack(push, 1)
/**
* @brief Encrypted RTSP message header used by GameStream.
*/
struct encrypted_rtsp_header_t {
// We set the MSB in encrypted RTSP messages to allow format-agnostic
// parsing code to be able to tell encrypted from plaintext messages.
static constexpr std::uint32_t ENCRYPTED_MESSAGE_TYPE_BIT = 0x80000000; ///< Protocol or platform constant for encrypted message type bit.
/**
* @brief Return a pointer to the protocol payload following the packet header.
*
* @return Parsed or serialized payload data.
*/
uint8_t *payload() {
return (uint8_t *) (this + 1);
}
/**
* @brief Decode the RTSP interleaved-frame payload length.
*
* @return Payload byte count with the encrypted-message flag masked out.
*/
std::uint32_t payload_length() {
return util::endian::big<std::uint32_t>(typeAndLength) & ~ENCRYPTED_MESSAGE_TYPE_BIT;
}
/**
* @brief Check whether encrypted.
*
* @return True when the RTSP connection is using TLS.
*/
bool is_encrypted() {
return !!(util::endian::big<std::uint32_t>(typeAndLength) & ENCRYPTED_MESSAGE_TYPE_BIT);
}
// This field is the length of the payload + ENCRYPTED_MESSAGE_TYPE_BIT in big-endian
std::uint32_t typeAndLength; ///< Type and length.
// This field is the number used to initialize the bottom 4 bytes of the AES IV in big-endian
std::uint32_t sequenceNumber; ///< Sequence number.
// This field is the AES GCM authentication tag
std::array<std::uint8_t, 16> tag; ///< Authentication tag appended to the encrypted payload.
};
#pragma pack(pop)
static_assert(sizeof(encrypted_rtsp_header_t) == 24);
static_assert(offsetof(encrypted_rtsp_header_t, tag) == 8);
class rtsp_server_t;
/**
@@ -806,7 +754,7 @@ namespace rtsp_stream {
auto [payload, original_payload_length] = std::make_pair(resp->payload, resp->payloadLength);
// Restore response message for proper destruction
auto lg = util::fail_guard([&]() {
auto lg = util::fail_guard([&resp, &payload, &original_payload_length]() {
resp->payload = payload;
resp->payloadLength = original_payload_length;
});
+55
View File
@@ -5,7 +5,10 @@
#pragma once
// standard includes
#include <array>
#include <atomic>
#include <cstddef>
#include <cstdint>
// local includes
#include "crypto.h"
@@ -14,6 +17,58 @@
namespace rtsp_stream {
constexpr auto RTSP_SETUP_PORT = 21; ///< GameStream base-port offset used for the RTSP setup listener.
#pragma pack(push, 1)
/**
* @brief Encrypted RTSP message header used by GameStream.
*/
struct encrypted_rtsp_header_t {
// We set the MSB in encrypted RTSP messages to allow format-agnostic
// parsing code to be able to tell encrypted from plaintext messages.
static constexpr std::uint32_t ENCRYPTED_MESSAGE_TYPE_BIT = 0x80000000; ///< Protocol or platform constant for encrypted message type bit.
/**
* @brief Return a pointer to the protocol payload following the packet header.
*
* @return Parsed or serialized payload data.
*/
uint8_t *payload() {
return (uint8_t *) (this + 1);
}
/**
* @brief Decode the RTSP interleaved-frame payload length.
*
* @return Payload byte count with the encrypted-message flag masked out.
*/
std::uint32_t payload_length() const {
return util::endian::big<std::uint32_t>(typeAndLength) & ~ENCRYPTED_MESSAGE_TYPE_BIT;
}
/**
* @brief Inspect the encrypted-message flag without modifying the wire header.
*
* @return True when the encrypted-message flag is set.
*/
bool is_encrypted() const {
return !!(util::endian::big<std::uint32_t>(typeAndLength) & ENCRYPTED_MESSAGE_TYPE_BIT);
}
// This field is the length of the payload + ENCRYPTED_MESSAGE_TYPE_BIT in big-endian
std::uint32_t typeAndLength; ///< Type and length.
// This field is the number used to initialize the bottom 4 bytes of the AES IV in big-endian
std::uint32_t sequenceNumber; ///< Sequence number.
// This field is the AES GCM authentication tag
std::array<std::uint8_t, 16> tag; ///< Authentication tag appended to the encrypted payload.
};
#pragma pack(pop)
static_assert(sizeof(encrypted_rtsp_header_t) == 24);
static_assert(offsetof(encrypted_rtsp_header_t, tag) == 8);
/**
* @brief RTSP launch session state shared with stream setup.
*/
+2 -2
View File
@@ -1189,7 +1189,7 @@ namespace stream {
});
server->map(packetTypes[IDX_LOSS_STATS], [&](session_t *session, const std::string_view &payload) {
int32_t *stats = (int32_t *) payload.data();
auto stats = (int32_t *) payload.data();
auto count = stats[0];
std::chrono::milliseconds t {stats[1]};
@@ -1479,7 +1479,7 @@ namespace stream {
auto recv_func_init = [&](udp::socket &sock, int buf_elem, std::map<av_session_id_t, message_queue_t> &peer_to_session) {
recv_func[buf_elem] = [&, buf_elem](const boost::system::error_code &ec, size_t bytes) {
auto fg = util::fail_guard([&]() {
auto fg = util::fail_guard([&sock, &buf, &peer, &recv_func, &buf_elem]() {
sock.async_receive_from(asio::buffer(buf[buf_elem]), peer, 0, recv_func[buf_elem]);
});
+4 -1
View File
@@ -28,7 +28,10 @@ namespace task_pool_util {
public:
// _unique_base_type _this_ptr;
inline virtual ~_ImplBase() = default;
/**
* @brief Destroy a queued callable through its polymorphic base.
*/
virtual ~_ImplBase() = default;
/**
* @brief Execute the queued task body.
+3 -3
View File
@@ -162,11 +162,11 @@ namespace safe {
}
/**
* @brief Inspect the next queued value without popping it.
* @brief Inspect whether this event has a value without modifying or consuming it.
*
* @return True when a value is available to inspect.
*/
bool peek() {
bool peek() const {
return _continue && (bool) _status;
}
@@ -859,7 +859,7 @@ namespace safe {
std::lock_guard lg {mutex};
for (auto it = std::begin(id_to_post); it != std::end(id_to_post); ++it) {
auto &weak = it->second;
const auto &weak = it->second;
if (weak.expired()) {
id_to_post.erase(it);
+3 -3
View File
@@ -632,7 +632,7 @@ namespace util {
T from_hex(const std::string_view &hex, bool rev = false) {
std::array<std::byte, sizeof(T)> buf;
auto is_convertable = [](char ch) -> bool {
auto is_convertable = [](char ch) {
if (isdigit(ch)) {
return true;
}
@@ -687,7 +687,7 @@ namespace util {
inline std::string from_hex_vec(const std::string &hex, bool rev = false) {
std::string buf;
auto is_convertable = [](char ch) -> bool {
auto is_convertable = [](char ch) {
if (isdigit(ch)) {
return true;
}
@@ -1643,7 +1643,7 @@ namespace util {
*
* @param p Pointer to release.
*/
void operator()(pointer p) {
void operator()(pointer p) const {
function(p);
}
};
+19 -18
View File
@@ -1552,6 +1552,7 @@ namespace video {
/**
* @brief Run the shared display capture thread for asynchronous encoding.
* @details Capture callbacks share this thread's session queues and reusable image pool.
*
* @param capture_ctx_queue Capture context queue.
* @param display_wp Weak pointer holder for the active display.
@@ -1566,7 +1567,7 @@ namespace video {
) {
std::vector<capture_ctx_t> capture_ctxs;
auto fg = util::fail_guard([&]() {
auto fg = util::fail_guard([&capture_ctx_queue, &capture_ctxs]() {
capture_ctx_queue->stop();
// Stop all sessions listening to this thread
@@ -1603,7 +1604,7 @@ namespace video {
std::vector<std::optional<std::chrono::steady_clock::time_point>> imgs_used_timestamps;
const std::chrono::seconds trim_timeot = 3s;
auto trim_imgs = [&]() {
auto trim_imgs = [&imgs, &imgs_used_timestamps, &trim_timeot]() {
// count allocated and used within current pool
size_t allocated_count = 0;
size_t used_count = 0;
@@ -1651,7 +1652,7 @@ namespace video {
}
};
auto pull_free_image_callback = [&](std::shared_ptr<platf::img_t> &img_out) -> bool {
auto pull_free_image_callback = [&capture_ctx_queue, &imgs, &disp, &trim_imgs](std::shared_ptr<platf::img_t> &img_out) {
img_out.reset();
while (capture_ctx_queue->running()) {
// pick first allocated but unused
@@ -1702,7 +1703,7 @@ namespace video {
while (capture_ctx_queue->running()) {
bool artificial_reinit = false;
auto push_captured_image_callback = [&](std::shared_ptr<platf::img_t> &&img, bool frame_captured) -> bool {
auto push_captured_image_callback = [&capture_ctxs, &capture_ctx_queue, &switch_display_event, &artificial_reinit](std::shared_ptr<platf::img_t> &&img, bool frame_captured) {
KITTY_WHILE_LOOP(auto capture_ctx = std::begin(capture_ctxs), capture_ctx != std::end(capture_ctxs), {
if (!capture_ctx->images->running()) {
capture_ctx = capture_ctxs.erase(capture_ctx);
@@ -2164,29 +2165,29 @@ namespace video {
auto handle_option = [&options, &config](const encoder_t::option_t &option) {
std::visit(
util::overloaded {
[&](int v) {
[&options, &option](int v) {
av_dict_set_int(&options, option.name.c_str(), v, 0);
},
[&](int *v) {
[&options, &option](int *v) {
av_dict_set_int(&options, option.name.c_str(), *v, 0);
},
[&](std::optional<int> *v) {
[&options, &option](std::optional<int> *v) {
if (*v) {
av_dict_set_int(&options, option.name.c_str(), **v, 0);
}
},
[&](const std::function<int()> &v) {
[&options, &option](const std::function<int()> &v) {
av_dict_set_int(&options, option.name.c_str(), v(), 0);
},
[&](const std::string &v) {
[&options, &option](const std::string &v) {
av_dict_set(&options, option.name.c_str(), v.c_str(), 0);
},
[&](std::string *v) {
[&options, &option](std::string *v) {
if (!v->empty()) {
av_dict_set(&options, option.name.c_str(), v->c_str(), 0);
}
},
[&](const std::function<const std::string(const config_t &cfg)> &v) {
[&options, &option, &config](const std::function<const std::string(const config_t &cfg)> &v) {
av_dict_set(&options, option.name.c_str(), v(config).c_str(), 0);
}
},
@@ -2644,7 +2645,7 @@ namespace video {
ctx.touch_port_events->raise(make_port(disp, ctx.config));
// Update client with our current HDR display state
hdr_info_t hdr_info = std::make_unique<hdr_info_raw_t>(false);
auto hdr_info = std::make_unique<hdr_info_raw_t>(false);
if (colorspace_is_hdr(encode_device->colorspace)) {
if (disp->get_hdr_metadata(hdr_info->metadata)) {
hdr_info->enabled = true;
@@ -2737,7 +2738,7 @@ namespace video {
auto ec = platf::capture_e::ok;
while (encode_session_ctx_queue.running()) {
auto push_captured_image_callback = [&](std::shared_ptr<platf::img_t> &&captured_image, bool frame_captured) -> bool {
auto push_captured_image_callback = [&](std::shared_ptr<platf::img_t> &&captured_image, bool frame_captured) {
while (encode_session_ctx_queue.peek()) {
auto encode_session_ctx = encode_session_ctx_queue.pop();
if (!encode_session_ctx) {
@@ -2810,7 +2811,7 @@ namespace video {
return true;
};
auto pull_free_image_callback = [&img](std::shared_ptr<platf::img_t> &img_out) -> bool {
auto pull_free_image_callback = [&img](std::shared_ptr<platf::img_t> &img_out) {
img_out = img;
img_out->frame_timestamp.reset();
return true;
@@ -2839,10 +2840,10 @@ namespace video {
std::vector<std::unique_ptr<sync_session_ctx_t>> synced_session_ctxs;
auto &ctx = ref->encode_session_ctx_queue;
auto lg = util::fail_guard([&]() {
auto lg = util::fail_guard([&ctx, &synced_session_ctxs]() {
ctx.stop();
for (auto &session_ctx : synced_session_ctxs) {
for (const auto &session_ctx : synced_session_ctxs) {
session_ctx->shutdown_event->raise(true);
session_ctx->join_event->raise(true);
}
@@ -2877,7 +2878,7 @@ namespace video {
auto shutdown_event = mail->event<bool>(mail::shutdown);
auto images = std::make_shared<img_event_t::element_type>();
auto lg = util::fail_guard([&]() {
auto lg = util::fail_guard([&images, &shutdown_event]() {
images->stop();
shutdown_event->raise(true);
});
@@ -2929,7 +2930,7 @@ namespace video {
touch_port_event->raise(make_port(display.get(), config));
// Update client with our current HDR display state
hdr_info_t hdr_info = std::make_unique<hdr_info_raw_t>(false);
auto hdr_info = std::make_unique<hdr_info_raw_t>(false);
if (colorspace_is_hdr(encode_device->colorspace)) {
if (display->get_hdr_metadata(hdr_info->metadata)) {
hdr_info->enabled = true;
+1 -1
View File
@@ -157,7 +157,7 @@ namespace video {
*/
const color_t *color_vectors_from_colorspace(const sunshine_colorspace_t &colorspace, bool unorm_output) {
using enum colorspace_e;
constexpr auto generate_color_vectors = [](const sunshine_colorspace_t &colorspace, bool unorm_output) -> color_t {
constexpr auto generate_color_vectors = [](const sunshine_colorspace_t &colorspace, bool unorm_output) {
// "Table 4 – Interpretation of matrix coefficients (MatrixCoefficients) value" section of ITU-T H.273
double Kr;
double Kb;
+29 -1
View File
@@ -43,6 +43,34 @@ TEST(SocketAddressTest, UnsupportedAddressFamiliesProduceEmptyAddressesAndZeroPo
EXPECT_EQ(platf::from_sockaddr_ex(&address), (std::pair<std::uint16_t, std::string> {0, {}}));
}
/**
* @brief Const batch descriptors locate payload boundaries and reject offsets past all buffers.
*/
TEST(BatchedSendInfoTest, ConstLookupPreservesBuffersAndFindsEveryBoundary) {
const std::string_view first = "abc";
const std::string_view second = "defg";
std::vector<platf::buffer_descriptor_t> buffers {{nullptr, 0}, {first.data(), first.size()}, {second.data(), second.size()}};
auto address = boost::asio::ip::address_v4::loopback();
boost::asio::ip::address endpoint = address;
const platf::batched_send_info_t batch {nullptr, 0, buffers, 1, 0, 7, 0, endpoint, 0, endpoint};
EXPECT_EQ(batch.buffer_for_payload_offset(0).buffer, first.data());
EXPECT_EQ(batch.buffer_for_payload_offset(0).size, first.size());
EXPECT_EQ(batch.buffer_for_payload_offset(2).buffer, first.data() + 2);
EXPECT_EQ(batch.buffer_for_payload_offset(2).size, 1);
EXPECT_EQ(batch.buffer_for_payload_offset(3).buffer, second.data());
EXPECT_EQ(batch.buffer_for_payload_offset(3).size, second.size());
EXPECT_EQ(batch.buffer_for_payload_offset(6).buffer, second.data() + 3);
EXPECT_EQ(batch.buffer_for_payload_offset(6).size, 1);
for (const auto offset : {7, 8}) {
EXPECT_EQ(batch.buffer_for_payload_offset(offset).buffer, nullptr);
EXPECT_EQ(batch.buffer_for_payload_offset(offset).size, 0);
}
EXPECT_EQ(buffers.size(), 3);
EXPECT_EQ(buffers[1].buffer, first.data());
EXPECT_EQ(buffers[1].size, first.size());
}
#if defined(_WIN32) || defined(__linux__) || defined(__FreeBSD__)
/**
* @brief Exercise source-address control messages on bound UDP sockets with optional headers.
@@ -64,7 +92,7 @@ TEST_P(UdpSourceAddressTest, SendsTheExpectedDatagramFromTheRequestedAddress) {
const std::string_view headers = "headerheader";
const std::string_view payloads = "payloadpayload";
std::vector<platf::buffer_descriptor_t> buffers {{payloads.data(), payloads.size()}};
platf::batched_send_info_t batch_info {
const platf::batched_send_info_t batch_info {
with_header ? headers.data() : nullptr,
with_header ? header.size() : 0,
buffers,
+2 -2
View File
@@ -89,7 +89,7 @@ INSTANTIATE_TEST_SUITE_P(
);
TEST_P(AudioTest, TestEncode) {
std::jthread timer([&] {
std::jthread timer([this] {
// Terminate the audio capture after 100 ms
std::this_thread::sleep_for(100ms);
const auto shutdown_event = m_mail->event<bool>(mail::shutdown);
@@ -97,7 +97,7 @@ TEST_P(AudioTest, TestEncode) {
shutdown_event->raise(true);
audio_packets->stop();
});
std::jthread capture([&] {
std::jthread capture([this] {
const auto packets = m_mail->queue<packet_t>(mail::audio_packets);
const auto shutdown_event = m_mail->event<bool>(mail::shutdown);
while (const auto packet = packets->pop()) {
+16
View File
@@ -141,6 +141,22 @@ TEST_F(LogRotationTest, FindsSubstringsAtBoundariesAndWithCaseFolding) {
struct LogLevelsTest: BaseTest, testing::WithParamInterface<decltype(log_levels)::value_type> {};
/**
* @brief Periodic logger callbacks preserve their owner's message and numeric formatting.
*/
TEST(PeriodicLoggerTest, ReportsIntegralAndFloatingSamplesWithConfiguredUnits) {
logging::min_max_avg_periodic_logger<int> integral {info, "Integral capture", "ms", std::chrono::seconds {-1}};
logging::min_max_avg_periodic_logger<double> floating {info, "Floating capture", "s", std::chrono::seconds {-1}};
integral.collect_and_log(3);
floating.collect_and_log(1.25);
integral.collect_and_log(7);
floating.collect_and_log(2.5);
EXPECT_TRUE(log_checker::line_contains(log_file, "Integral capture (min/max/avg): 3ms/3ms/3.00ms"));
EXPECT_TRUE(log_checker::line_contains(log_file, "Floating capture (min/max/avg): 1.25s/1.25s/1.25s"));
}
INSTANTIATE_TEST_SUITE_P(
Logging,
LogLevelsTest,
+11
View File
@@ -21,6 +21,17 @@ TEST(NetworkClassificationTest, DeducedNetworkListsPreserveAddressScopes) {
EXPECT_EQ(net::from_address("2001:4860:4860::8888"), net::WAN);
}
/**
* @brief Loopback and documentation addresses retain their classification when mapped to IPv6.
*/
TEST(NetworkClassificationTest, ClassifiesMappedIpv4AddressesWithoutChangingSubsequentLookups) {
for (const auto *address : {"127.0.0.1", "192.0.2.1", "198.51.100.2", "203.0.113.3"}) {
const auto expected = net::from_address(address);
EXPECT_EQ(net::from_address(std::string {"::ffff:"} + address), expected);
EXPECT_EQ(net::from_address(address), expected);
}
}
struct MdnsInstanceNameTest: BaseTest, testing::WithParamInterface<std::tuple<std::string, std::string>> {};
TEST_P(MdnsInstanceNameTest, Run) {
+23
View File
@@ -31,6 +31,7 @@
#include <src/video.h>
#ifdef _WIN32
#include <src/platform/windows/nvprefs/nvprefs_common.h>
#include <src/platform/windows/nvprefs/nvprefs_interface.h>
#endif
namespace {
@@ -163,6 +164,28 @@ TEST(ResourceOwnershipTest, FileResetClosesUnderlyingDescriptor) {
}
#ifdef _WIN32
/**
* @brief Const NVIDIA preference inspection does not claim ownership before the interface is loaded.
*/
TEST(ResourceOwnershipTest, ConstNvPrefsInterfaceStartsWithoutAnUndoFile) {
const nvprefs::nvprefs_interface preferences;
EXPECT_FALSE(preferences.owning_undo_file());
EXPECT_FALSE(preferences.owning_undo_file());
}
/**
* @brief A const LocalAlloc deleter releases a movable allocation and accepts null pointers.
*/
TEST(ResourceOwnershipTest, ConstLocalAllocDeleterReleasesTheAllocation) {
const auto allocation = LocalAlloc(LMEM_MOVEABLE, 16);
ASSERT_NE(allocation, nullptr);
ASSERT_NE(LocalFlags(allocation), LMEM_INVALID_HANDLE);
const nvprefs::safe_hlocal_deleter destroy;
destroy(allocation);
EXPECT_EQ(LocalFlags(allocation), LMEM_INVALID_HANDLE);
destroy(static_cast<void *>(nullptr));
}
/**
* @brief Moving an NVPrefs handle transfers responsibility for closing it.
*/
+16
View File
@@ -20,6 +20,22 @@ extern "C" {
// local includes
#include <src/rtsp.h>
/**
* @brief Const RTSP wire headers decode network-order lengths independently of encryption flags.
*/
TEST(RtspHeaderTest, ConstAccessorsPreserveTheSerializedHeader) {
using rtsp_stream::encrypted_rtsp_header_t;
for (const auto length : {0U, 1U, 0x7fffffffU}) {
for (const bool encrypted : {false, true}) {
const auto flags = encrypted ? encrypted_rtsp_header_t::ENCRYPTED_MESSAGE_TYPE_BIT : 0U;
const encrypted_rtsp_header_t header {util::endian::big(length | flags), 0, {}};
EXPECT_EQ(header.payload_length(), length);
EXPECT_EQ(header.is_encrypted(), encrypted);
EXPECT_EQ(header.typeAndLength, util::endian::big(length | flags));
}
}
}
namespace rtsp_stream {
class rtsp_server_t;
+38
View File
@@ -15,6 +15,44 @@
static_assert(!std::is_convertible_v<std::uint32_t, safe::queue_t<int>>);
/**
* @brief Const event inspection observes state transitions without consuming the pending value.
*/
TEST(ThreadSafeEvent, ConstPeekPreservesPendingValuesAcrossStateChanges) {
safe::event_t<int> event;
const auto &view = event;
EXPECT_FALSE(view.peek());
event.raise(42);
EXPECT_TRUE(view.peek());
EXPECT_TRUE(view.peek());
EXPECT_EQ(event.try_pop(), 42);
EXPECT_FALSE(view.peek());
event.raise(7);
event.stop();
EXPECT_FALSE(view.peek());
event.reset();
EXPECT_FALSE(view.peek());
event.raise(9);
EXPECT_TRUE(view.peek());
}
/**
* @brief Mail cleanup removes an expired post while preserving an active post's owner.
*/
TEST(ThreadSafeMail, CleanupPreservesLivePostsAndRemovesExpiredPosts) {
safe::mail_raw_t mailbox;
const auto live = std::make_shared<int>(7);
mailbox.id_to_post.try_emplace("live", live);
mailbox.id_to_post.try_emplace("expired", std::weak_ptr<void> {});
mailbox.cleanup();
EXPECT_EQ(mailbox.id_to_post.size(), 1);
EXPECT_EQ(mailbox.id_to_post.at("live").lock(), live);
mailbox.cleanup();
EXPECT_EQ(mailbox.id_to_post.size(), 1);
}
TEST(ThreadSafeQueue, RejectsNewestItemAtCapacity) {
safe::queue_t<int> queue {1, safe::queue_t<int>::overflow_policy_e::reject};
+13
View File
@@ -21,6 +21,19 @@
// local includes
#include <src/utility.h>
/**
* @brief A const resource deleter invokes its release function exactly once per call.
*/
TEST(UtilityDestroyTest, ConstDeleterInvokesTheConfiguredReleaseFunction) {
const util::Destroy<int *, void, +[](int *value) {
++*value;
}>
destroy;
int releases = 0;
destroy(&releases);
EXPECT_EQ(releases, 1);
}
/**
* @brief Simulate the opposite byte order for a byte-array fixture.
*/