107 lines
4.5 KiB
C++
107 lines
4.5 KiB
C++
#include <doctest/doctest.h>
|
|
|
|
#include "fgc/IniEdit.h"
|
|
|
|
#include <stdexcept>
|
|
#include <string>
|
|
|
|
using namespace fgc;
|
|
|
|
TEST_CASE("a Section.key splits on its LAST dot, since sections carry dots") {
|
|
auto k = splitIniKey("Camera.rgb.id");
|
|
REQUIRE(k);
|
|
CHECK(k->first == "Camera.rgb");
|
|
CHECK(k->second == "id");
|
|
CHECK_FALSE(splitIniKey("nodot"));
|
|
CHECK_FALSE(splitIniKey(".key"));
|
|
CHECK_FALSE(splitIniKey("Section."));
|
|
}
|
|
|
|
TEST_CASE("the line limit is the one inih actually reads whole") {
|
|
// 200-byte buffer, minus the newline and the terminator.
|
|
CHECK(iniMaxLineContent() == 198);
|
|
const std::string ok(198, 'x');
|
|
const std::string too_long(199, 'x');
|
|
CHECK(firstOverlongIniLine("a = 1\n" + ok + "\n") == 0);
|
|
CHECK(firstOverlongIniLine("a = 1\n" + too_long + "\nb = 2\n") == 2);
|
|
// A CRLF file is measured without its \r.
|
|
CHECK(firstOverlongIniLine(ok + "\r\n") == 0);
|
|
}
|
|
|
|
TEST_CASE("parseIniString reads keys as the program does, and names a bad line") {
|
|
auto kv = parseIniString("[General]\ntower_name = t1 ; note\n[Camera.rgb]\nid = D\n");
|
|
CHECK(kv.at("General.tower_name") == "t1");
|
|
CHECK(kv.at("Camera.rgb.id") == "D");
|
|
CHECK_THROWS_WITH(parseIniString("[General]\nno equals sign here\n"),
|
|
doctest::Contains("line 2"));
|
|
}
|
|
|
|
TEST_CASE("applyIniSets edits in place, appends what is missing, keeps comments") {
|
|
const std::string in =
|
|
"; header\n"
|
|
"[General]\n"
|
|
"; the name\n"
|
|
"tower_name = ExampleTower\n"
|
|
"[Network]\n"
|
|
"zkms_server_ip = 127.0.0.1\n";
|
|
const std::string out = applyIniSets(
|
|
in, {{"General.tower_name", "north"}, {"Motor.yaw_center_count", "1000000"}}, "");
|
|
CHECK(out.find("; header\n") == 0);
|
|
CHECK(out.find("; the name\ntower_name = north\n") != std::string::npos);
|
|
CHECK(out.find("ExampleTower") == std::string::npos);
|
|
CHECK(out.find("[Motor]\nyaw_center_count = 1000000\n") != std::string::npos);
|
|
CHECK(parseIniString(out).at("Network.zkms_server_ip") == "127.0.0.1");
|
|
}
|
|
|
|
TEST_CASE("applyIniSets keeps one ;prev generation when given a provenance") {
|
|
const std::string in = "[Serial]\ndevice = /dev/serial/by-id/old\n";
|
|
const std::string out =
|
|
applyIniSets(in, {{"Serial.device", "/dev/serial/by-id/new"}}, "install-tower");
|
|
CHECK(out.find(";prev device = /dev/serial/by-id/old") != std::string::npos);
|
|
CHECK(parseIniString(out).at("Serial.device") == "/dev/serial/by-id/new");
|
|
}
|
|
|
|
TEST_CASE("applyIniSets refuses a malformed key or a multi-line value") {
|
|
CHECK_THROWS_AS(applyIniSets("", {{"nodot", "1"}}, ""), std::invalid_argument);
|
|
CHECK_THROWS_AS(applyIniSets("", {{"A.b", "1\nc = 2"}}, ""), std::invalid_argument);
|
|
}
|
|
|
|
TEST_CASE("dropIniKeys comments a key out WITHOUT its value") {
|
|
// The placeholder must not survive even as a comment: deploy.sh's guard greps
|
|
// the raw file for DEV_0000000000 and would refuse to run the tower.
|
|
const std::string in =
|
|
"[Camera.rgb]\n"
|
|
"enabled = true\n"
|
|
"id = DEV_0000000000 ; USB DEV_ id\n"
|
|
"[Serial]\n"
|
|
"device = /dev/ttyACM0\n";
|
|
std::vector<std::string> dropped;
|
|
const std::string out = dropIniKeys(
|
|
in, {{"Camera.rgb.id", "is set in hardware.ini"}, {"Serial.device", "is set elsewhere"}},
|
|
&dropped);
|
|
CHECK(out.find("DEV_0000000000") == std::string::npos);
|
|
CHECK(out.find("; id is set in hardware.ini\n") != std::string::npos);
|
|
CHECK(out.find("; device is set elsewhere\n") != std::string::npos);
|
|
CHECK(dropped == std::vector<std::string>{"Camera.rgb.id", "Serial.device"});
|
|
const auto kv = parseIniString(out);
|
|
CHECK(kv.count("Camera.rgb.id") == 0);
|
|
CHECK(kv.at("Camera.rgb.enabled") == "true");
|
|
}
|
|
|
|
TEST_CASE("dropIniKeys matches the section too, not just the key name") {
|
|
const std::string in = "[IMU]\ndevice = /dev/ttyS0\n[Serial]\ndevice = /dev/ttyACM0\n";
|
|
const auto kv = parseIniString(dropIniKeys(in, {{"Serial.device", "gone"}}));
|
|
CHECK(kv.at("IMU.device") == "/dev/ttyS0");
|
|
CHECK(kv.count("Serial.device") == 0);
|
|
}
|
|
|
|
TEST_CASE("dropIniKeys takes a dropped key's continuation lines with it") {
|
|
// inih joins an indented line onto the value above it. Left behind, it would
|
|
// attach itself to whatever key now precedes it.
|
|
const std::string in = "[A]\nkeep = 1\ngone = first\n second\nafter = 2\n";
|
|
const auto kv = parseIniString(dropIniKeys(in, {{"A.gone", "x"}}));
|
|
CHECK(kv.at("A.keep") == "1");
|
|
CHECK(kv.at("A.after") == "2");
|
|
CHECK(kv.count("A.gone") == 0);
|
|
}
|