Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 25 additions & 13 deletions src/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -693,13 +693,19 @@ namespace config {
std::stringstream json_stream;
json_stream << "{\"dd_mode_remapping\":" << value << "}";

boost::property_tree::ptree json_tree;
boost::property_tree::read_json(json_stream, json_tree);

video_t::dd_t::mode_remapping_t output;
parse_entry_list(json_tree.get_child("dd_mode_remapping.mixed"), output.mixed);
parse_entry_list(json_tree.get_child("dd_mode_remapping.resolution_only"), output.resolution_only);
parse_entry_list(json_tree.get_child("dd_mode_remapping.refresh_rate_only"), output.refresh_rate_only);
try {
boost::property_tree::ptree json_tree;
boost::property_tree::read_json(json_stream, json_tree);

parse_entry_list(json_tree.get_child("dd_mode_remapping.mixed"), output.mixed);
parse_entry_list(json_tree.get_child("dd_mode_remapping.resolution_only"), output.resolution_only);
parse_entry_list(json_tree.get_child("dd_mode_remapping.refresh_rate_only"), output.refresh_rate_only);
} catch (const boost::property_tree::ptree_error &err) {
// don't let bad json kill startup, skip remapping
BOOST_LOG(warning) << "Ignoring invalid dd_mode_remapping configuration: "sv << err.what();
return {};
}

return output;
}
Expand Down Expand Up @@ -1426,15 +1432,21 @@ namespace config {
// We need to add a wrapping object to make it valid JSON, otherwise ptree cannot parse it.
jsonStream << "{\"prep_cmd\":" << string << "}";

boost::property_tree::ptree jsonTree;
boost::property_tree::read_json(jsonStream, jsonTree);
try {
boost::property_tree::ptree jsonTree;
boost::property_tree::read_json(jsonStream, jsonTree);

for (auto &[_, prep_cmd] : jsonTree.get_child("prep_cmd"s)) {
auto do_cmd = prep_cmd.get_optional<std::string>("do"s);
auto undo_cmd = prep_cmd.get_optional<std::string>("undo"s);
auto elevated = prep_cmd.get_optional<bool>("elevated"s);
for (auto &[_, prep_cmd] : jsonTree.get_child("prep_cmd"s)) {
auto do_cmd = prep_cmd.get_optional<std::string>("do"s);
auto undo_cmd = prep_cmd.get_optional<std::string>("undo"s);
auto elevated = prep_cmd.get_optional<bool>("elevated"s);

input.emplace_back(do_cmd.value_or(""), undo_cmd.value_or(""), elevated.value_or(false));
input.emplace_back(do_cmd.value_or(""), undo_cmd.value_or(""), elevated.value_or(false));
}
} catch (const boost::property_tree::ptree_error &err) {
// don't let bad json kill startup, skip this setting
BOOST_LOG(warning) << "Ignoring invalid "sv << name << " configuration: "sv << err.what();
input.clear();
}
}

Expand Down
81 changes: 81 additions & 0 deletions tests/unit/test_config.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
/**
* @file tests/unit/test_config.cpp
* @brief Test src/config.* configuration parsing helpers.
*/
// test imports
#include "../tests_common.h"

// standard imports
#include <string>
#include <string_view>
#include <unordered_map>
#include <vector>

// local imports
#include <src/config.h>

// internal helpers, not exposed in config.h
namespace config {
void list_prep_cmd_f(std::unordered_map<std::string, std::string> &vars, const std::string &name, std::vector<prep_cmd_t> &input);

Check warning on line 19 in tests/unit/test_config.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the transparent equality "std::equal_to<>" and a custom transparent heterogeneous hasher with this associative string container.

See more on https://sonarcloud.io/project/issues?id=LizardByte_Sunshine&issues=AZ_0Wo_9liFyBlXgiXbg&open=AZ_0Wo_9liFyBlXgiXbg&pullRequest=5500

namespace dd {
video_t::dd_t::mode_remapping_t mode_remapping_from_view(std::string_view value);
} // namespace dd
} // namespace config

struct ConfigParsingTest: BaseTest {};

TEST_F(ConfigParsingTest, PrepCmdMalformedJsonDoesNotThrow) {
// bad json here must not kill startup
std::unordered_map<std::string, std::string> vars {{"global_prep_cmd", "["}};

Check warning on line 30 in tests/unit/test_config.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the transparent equality "std::equal_to<>" and a custom transparent heterogeneous hasher with this associative string container.

See more on https://sonarcloud.io/project/issues?id=LizardByte_Sunshine&issues=AZ_0Wo_9liFyBlXgiXbh&open=AZ_0Wo_9liFyBlXgiXbh&pullRequest=5500
std::vector<config::prep_cmd_t> input;

EXPECT_NO_THROW(config::list_prep_cmd_f(vars, "global_prep_cmd", input));
EXPECT_TRUE(input.empty());
}

TEST_F(ConfigParsingTest, PrepCmdValidJsonIsParsed) {
std::unordered_map<std::string, std::string> vars {

Check warning on line 38 in tests/unit/test_config.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use the transparent equality "std::equal_to<>" and a custom transparent heterogeneous hasher with this associative string container.

See more on https://sonarcloud.io/project/issues?id=LizardByte_Sunshine&issues=AZ_0Wo_9liFyBlXgiXbi&open=AZ_0Wo_9liFyBlXgiXbi&pullRequest=5500
{"global_prep_cmd", R"([{"do":"echo start","undo":"echo stop","elevated":false}])"}
};
std::vector<config::prep_cmd_t> input;

ASSERT_NO_THROW(config::list_prep_cmd_f(vars, "global_prep_cmd", input));
ASSERT_EQ(input.size(), 1u);
EXPECT_EQ(input[0].do_cmd, "echo start");
EXPECT_EQ(input[0].undo_cmd, "echo stop");
EXPECT_FALSE(input[0].elevated);
}

TEST_F(ConfigParsingTest, ModeRemappingMalformedJsonReturnsEmpty) {
config::video_t::dd_t::mode_remapping_t result;
EXPECT_NO_THROW(result = config::dd::mode_remapping_from_view("{ not valid json"));
EXPECT_TRUE(result.mixed.empty());
EXPECT_TRUE(result.resolution_only.empty());
EXPECT_TRUE(result.refresh_rate_only.empty());
}

TEST_F(ConfigParsingTest, ModeRemappingValidJsonMissingChildKeysReturnsEmpty) {
// valid json but missing keys also throws (ptree_bad_path)
config::video_t::dd_t::mode_remapping_t result;
EXPECT_NO_THROW(result = config::dd::mode_remapping_from_view("[]"));
EXPECT_TRUE(result.mixed.empty());
EXPECT_TRUE(result.resolution_only.empty());
EXPECT_TRUE(result.refresh_rate_only.empty());
}

TEST_F(ConfigParsingTest, ModeRemappingValidJsonIsParsed) {
constexpr std::string_view value = R"({
"mixed": [{"requested_resolution":"1920x1080","requested_fps":"60","final_resolution":"2560x1440","final_refresh_rate":"120"}],
"resolution_only": [],
"refresh_rate_only": []
})";

config::video_t::dd_t::mode_remapping_t result;
ASSERT_NO_THROW(result = config::dd::mode_remapping_from_view(value));
ASSERT_EQ(result.mixed.size(), 1u);
EXPECT_EQ(result.mixed[0].requested_resolution, "1920x1080");
EXPECT_EQ(result.mixed[0].requested_fps, "60");
EXPECT_EQ(result.mixed[0].final_resolution, "2560x1440");
EXPECT_EQ(result.mixed[0].final_refresh_rate, "120");
}