Browse Source

Cleanup branches and add more unit tests

fixes
Antonio SJ Musumeci 4 days ago
parent
commit
1cf52cae1f
  1. 10
      src/branch.cpp
  2. 7
      src/branch.hpp
  3. 11
      src/branches.cpp
  4. 957
      tests/tests.cpp

10
src/branch.cpp

@ -17,11 +17,10 @@
*/
#include "branch.hpp"
#include "ef.hpp"
#include "errno.hpp"
#include "num.hpp"
Branch::Branch()
: mode(Branch::Mode::RW)
{
}
@ -37,12 +36,6 @@ Branch::Branch(const u64 &default_minfreespace_)
{
}
int
Branch::from_string(const std::string_view str_)
{
return -EINVAL;
}
std::string
Branch::to_string(void) const
{
@ -64,7 +57,6 @@ Branch::to_string(void) const
break;
}
if(std::holds_alternative<u64>(_minfreespace))
{
rv += ',';

7
src/branch.hpp

@ -20,7 +20,6 @@
#include "base_types.h"
#include "strvec.hpp"
#include "tofrom_string.hpp"
#include "fs_path.hpp"
#include <cstdint>
@ -31,12 +30,11 @@
#include <variant>
class Branch final : public ToFromString
class Branch final
{
public:
enum class Mode
{
INVALID,
RO,
RW,
NC
@ -61,8 +59,7 @@ public:
bool ro_or_nc(void) const;
public:
int from_string(const std::string_view str) final;
std::string to_string(void) const final;
std::string to_string(void) const;
public:
u64 minfreespace() const;

11
src/branches.cpp

@ -82,7 +82,10 @@ namespace l
offset = s_.find_first_not_of("+<>-=");
if(offset == std::string::npos)
return;
{
*instr_ = s_.substr(0, std::min(s_.size(), (size_t)2));
return;
}
if(offset > 1)
offset = 2;
@ -293,6 +296,9 @@ namespace l
int
erase_begin(Branches::Impl *branches_)
{
if(branches_->empty())
return -ENOENT;
branches_->erase(branches_->begin());
return 0;
@ -302,6 +308,9 @@ namespace l
int
erase_end(Branches::Impl *branches_)
{
if(branches_->empty())
return -ENOENT;
branches_->pop_back();
return 0;

957
tests/tests.cpp

@ -367,6 +367,905 @@ test_config_branches()
TEST_CHECK(b.from_string("./foo/bar:/bar/baz:blah/asdf") == 0);
}
// ---------------------------------------------------------------------------
// Branch unit tests
// ---------------------------------------------------------------------------
void
test_branch_default_ctor_mode()
{
Branch b;
TEST_CHECK(b.mode == Branch::Mode::RW);
}
void
test_branch_to_string_modes()
{
uint64_t global = 0;
Branch b(global);
b.path = "/mnt/disk1";
b.mode = Branch::Mode::RW;
TEST_CHECK(b.to_string() == "/mnt/disk1=RW");
b.mode = Branch::Mode::RO;
TEST_CHECK(b.to_string() == "/mnt/disk1=RO");
b.mode = Branch::Mode::NC;
TEST_CHECK(b.to_string() == "/mnt/disk1=NC");
}
void
test_branch_to_string_with_minfreespace()
{
uint64_t global = 0;
Branch b(global);
b.path = "/mnt/disk1";
b.mode = Branch::Mode::RW;
// While minfreespace is held by pointer (global default), it is NOT emitted
TEST_CHECK(b.to_string() == "/mnt/disk1=RW");
// Once set_minfreespace is called the value is stored locally and IS emitted
b.set_minfreespace(1024);
TEST_CHECK(b.to_string() == "/mnt/disk1=RW,1K");
b.set_minfreespace(1024 * 1024);
TEST_CHECK(b.to_string() == "/mnt/disk1=RW,1M");
b.set_minfreespace(1024 * 1024 * 1024);
TEST_CHECK(b.to_string() == "/mnt/disk1=RW,1G");
b.set_minfreespace(500);
TEST_CHECK(b.to_string() == "/mnt/disk1=RW,500");
}
void
test_branch_mode_predicates()
{
Branch b;
b.mode = Branch::Mode::RW;
TEST_CHECK(b.ro() == false);
TEST_CHECK(b.nc() == false);
TEST_CHECK(b.ro_or_nc() == false);
b.mode = Branch::Mode::RO;
TEST_CHECK(b.ro() == true);
TEST_CHECK(b.nc() == false);
TEST_CHECK(b.ro_or_nc() == true);
b.mode = Branch::Mode::NC;
TEST_CHECK(b.ro() == false);
TEST_CHECK(b.nc() == true);
TEST_CHECK(b.ro_or_nc() == true);
}
void
test_branch_minfreespace_pointer_vs_value()
{
uint64_t global = 9999;
Branch b(global);
// Reads through the pointer
TEST_CHECK(b.minfreespace() == 9999);
// Pointer tracks changes to the referenced global
global = 1234;
TEST_CHECK(b.minfreespace() == 1234);
// set_minfreespace switches variant to owned u64
b.set_minfreespace(42);
TEST_CHECK(b.minfreespace() == 42);
// Global changes no longer affect it
global = 9999;
TEST_CHECK(b.minfreespace() == 42);
}
void
test_branch_copy_constructor()
{
uint64_t global = 100;
Branch orig(global);
orig.path = "/orig";
orig.mode = Branch::Mode::RO;
Branch copy(orig);
TEST_CHECK(copy.path == "/orig");
TEST_CHECK(copy.mode == Branch::Mode::RO);
TEST_CHECK(copy.minfreespace() == 100);
}
void
test_branch_copy_assignment()
{
uint64_t global = 77;
Branch a(global);
a.path = "/a";
a.mode = Branch::Mode::NC;
a.set_minfreespace(555);
Branch b;
b = a;
TEST_CHECK(b.path == "/a");
TEST_CHECK(b.mode == Branch::Mode::NC);
TEST_CHECK(b.minfreespace() == 555);
}
// ---------------------------------------------------------------------------
// Branches unit tests
// ---------------------------------------------------------------------------
void
test_branches_default_minfreespace()
{
Branches b;
// Default minfreespace propagates into each parsed branch
b.minfreespace = 8192;
TEST_CHECK(b.from_string("/tmp/a") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p)[0].minfreespace() == 8192);
}
void
test_branches_per_branch_minfreespace_overrides_default()
{
Branches b;
b.minfreespace = 1000;
TEST_CHECK(b.from_string("/tmp/a=RW,2000") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p)[0].minfreespace() == 2000);
TEST_MSG("expected 2000, got %lu", (unsigned long)(*p)[0].minfreespace());
}
void
test_branches_add_end_operator()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a") == 0);
TEST_CHECK(b->size() == 1);
// + appends to end
TEST_CHECK(b.from_string("+/tmp/b") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 2);
TEST_CHECK((*p)[0].path == "/tmp/a");
TEST_CHECK((*p)[1].path == "/tmp/b");
// +> also appends to end
TEST_CHECK(b.from_string("+>/tmp/c") == 0);
p = b;
TEST_CHECK((*p).size() == 3);
TEST_CHECK((*p)[2].path == "/tmp/c");
}
void
test_branches_add_begin_operator()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a") == 0);
TEST_CHECK(b.from_string("+</tmp/b") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 2);
TEST_CHECK((*p)[0].path == "/tmp/b");
TEST_CHECK((*p)[1].path == "/tmp/a");
}
void
test_branches_set_operator()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b") == 0);
TEST_CHECK(b->size() == 2);
// = replaces the whole list
TEST_CHECK(b.from_string("=/tmp/c") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 1);
TEST_CHECK((*p)[0].path == "/tmp/c");
}
void
test_branches_erase_end_operator()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b:/tmp/c") == 0);
TEST_CHECK(b.from_string("->") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 2);
TEST_CHECK((*p).back().path == "/tmp/b");
}
void
test_branches_erase_begin_operator()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b:/tmp/c") == 0);
TEST_CHECK(b.from_string("-<") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 2);
TEST_CHECK((*p).front().path == "/tmp/b");
}
void
test_branches_erase_fnmatch_operator()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/disk1:/tmp/disk2:/mnt/ssd") == 0);
// Remove branches matching /tmp/*
TEST_CHECK(b.from_string("-/tmp/*") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 1);
TEST_CHECK((*p)[0].path == "/mnt/ssd");
}
void
test_branches_erase_fnmatch_multiple_patterns()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b:/mnt/c:/mnt/d") == 0);
TEST_CHECK(b.from_string("-/tmp/a:/mnt/d") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 2);
TEST_CHECK((*p)[0].path == "/tmp/b");
TEST_CHECK((*p)[1].path == "/mnt/c");
}
void
test_branches_invalid_mode()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a=XX") == -EINVAL);
}
void
test_branches_invalid_minfreespace()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a=RW,notanumber") == -EINVAL);
}
void
test_branches_to_paths()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b:/tmp/c") == 0);
Branches::Ptr p = b;
std::vector<fs::path> paths = (*p).to_paths();
TEST_CHECK(paths.size() == 3);
TEST_CHECK(paths[0] == "/tmp/a");
TEST_CHECK(paths[1] == "/tmp/b");
TEST_CHECK(paths[2] == "/tmp/c");
StrVec sv;
(*p).to_paths(sv);
TEST_CHECK(sv.size() == 3);
TEST_CHECK(sv[0] == "/tmp/a");
TEST_CHECK(sv[1] == "/tmp/b");
TEST_CHECK(sv[2] == "/tmp/c");
}
void
test_branches_cow_semantics()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a") == 0);
Branches::Ptr snap0 = b;
TEST_CHECK(b.from_string("+/tmp/b") == 0);
Branches::Ptr snap1 = b;
// Each mutation produces a new Impl pointer
TEST_CHECK(snap0.get() != snap1.get());
// snap0 is unchanged
TEST_CHECK((*snap0).size() == 1);
TEST_CHECK((*snap1).size() == 2);
}
void
test_branches_mode_round_trip()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a=RO:/tmp/b=NC:/tmp/c=RW") == 0);
TEST_CHECK(b.to_string() == "/tmp/a=RO:/tmp/b=NC:/tmp/c=RW");
Branches::Ptr p = b;
TEST_CHECK((*p)[0].mode == Branch::Mode::RO);
TEST_CHECK((*p)[1].mode == Branch::Mode::NC);
TEST_CHECK((*p)[2].mode == Branch::Mode::RW);
}
void
test_branches_srcmounts_to_string()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a=RO,1234:/tmp/b=NC") == 0);
SrcMounts sm(b);
// SrcMounts::to_string emits only paths, no mode or minfreespace
TEST_CHECK(sm.to_string() == "/tmp/a:/tmp/b");
}
void
test_branches_srcmounts_empty()
{
Branches b;
SrcMounts sm(b);
TEST_CHECK(sm.to_string() == "");
}
// ---------------------------------------------------------------------------
// Branch::to_string humanization edge cases
// ---------------------------------------------------------------------------
void
test_branch_to_string_1t()
{
uint64_t global = 0;
Branch b(global);
b.path = "/mnt/disk1";
b.mode = Branch::Mode::RW;
b.set_minfreespace(1024ULL * 1024 * 1024 * 1024);
TEST_CHECK(b.to_string() == "/mnt/disk1=RW,1T");
b.set_minfreespace(2ULL * 1024 * 1024 * 1024 * 1024);
TEST_CHECK(b.to_string() == "/mnt/disk1=RW,2T");
}
void
test_branch_to_string_non_exact_multiple()
{
// 1536 = 1.5K: not divisible by any tier, emitted as raw decimal
uint64_t global = 0;
Branch b(global);
b.path = "/mnt/disk1";
b.mode = Branch::Mode::RW;
b.set_minfreespace(1536);
TEST_CHECK(b.to_string() == "/mnt/disk1=RW,1536");
// 1537 also not exact
b.set_minfreespace(1537);
TEST_CHECK(b.to_string() == "/mnt/disk1=RW,1537");
}
void
test_branch_to_string_zero_owned_minfreespace()
{
// Default Branch() holds u64(0) in variant — to_string WILL emit ",0"
Branch b;
b.path = "/mnt/disk1";
b.mode = Branch::Mode::RW;
TEST_CHECK(b.to_string() == "/mnt/disk1=RW,0");
}
void
test_branch_copy_pointer_aliasing()
{
// A copy of a pointer-backed Branch shares the same pointer —
// mutating the original global value is seen by both.
uint64_t global = 1024;
Branch orig(global);
orig.path = "/mnt/a";
orig.mode = Branch::Mode::RW;
Branch copy = orig;
TEST_CHECK(orig.minfreespace() == 1024);
TEST_CHECK(copy.minfreespace() == 1024);
// Both see the updated value through the shared pointer
global = 2048;
TEST_CHECK(orig.minfreespace() == 2048);
TEST_CHECK(copy.minfreespace() == 2048);
}
// ---------------------------------------------------------------------------
// parse_mode: case-sensitivity
// ---------------------------------------------------------------------------
void
test_branches_mode_case_sensitivity()
{
Branches b;
// Only exact uppercase is accepted
TEST_CHECK(b.from_string("/tmp/a=rw") == -EINVAL);
TEST_CHECK(b.from_string("/tmp/a=ro") == -EINVAL);
TEST_CHECK(b.from_string("/tmp/a=nc") == -EINVAL);
TEST_CHECK(b.from_string("/tmp/a=Rw") == -EINVAL);
TEST_CHECK(b.from_string("/tmp/a=RW") == 0);
}
// ---------------------------------------------------------------------------
// parse_minfreespace: suffix variants
// ---------------------------------------------------------------------------
void
test_branches_minfreespace_lowercase_suffixes()
{
Branches b;
Branches::Ptr p;
// Lowercase k/m/g/t must be accepted and multiply correctly
TEST_CHECK(b.from_string("/tmp/a=RW,1k") == 0);
p = b;
TEST_CHECK((*p)[0].minfreespace() == 1024ULL);
TEST_CHECK(b.from_string("/tmp/a=RW,1m") == 0);
p = b;
TEST_CHECK((*p)[0].minfreespace() == 1024ULL * 1024);
TEST_CHECK(b.from_string("/tmp/a=RW,1g") == 0);
p = b;
TEST_CHECK((*p)[0].minfreespace() == 1024ULL * 1024 * 1024);
TEST_CHECK(b.from_string("/tmp/a=RW,1t") == 0);
p = b;
TEST_CHECK((*p)[0].minfreespace() == 1024ULL * 1024 * 1024 * 1024);
}
void
test_branches_minfreespace_bytes_suffix()
{
Branches b;
Branches::Ptr p;
// 'B' / 'b' suffix means bytes (no multiplication)
TEST_CHECK(b.from_string("/tmp/a=RW,1024B") == 0);
p = b;
TEST_CHECK((*p)[0].minfreespace() == 1024ULL);
TEST_CHECK(b.from_string("/tmp/a=RW,1024b") == 0);
p = b;
TEST_CHECK((*p)[0].minfreespace() == 1024ULL);
}
void
test_branches_minfreespace_zero()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a=RW,0") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p)[0].minfreespace() == 0ULL);
// to_string should reproduce the zero
TEST_CHECK(p->to_string() == "/tmp/a=RW,0");
}
void
test_branches_minfreespace_negative_invalid()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a=RW,-1") == -EINVAL);
}
void
test_branches_minfreespace_overflow()
{
Branches b;
// 16777216T overflows u64 (16777216 * 2^40 > 2^64-1)
TEST_CHECK(b.from_string("/tmp/a=RW,16777216T") == -EOVERFLOW);
}
// ---------------------------------------------------------------------------
// Minfreespace round-trip through Branches
// ---------------------------------------------------------------------------
void
test_branches_minfreespace_round_trip()
{
Branches b;
// Parse 1K — should store 1024, to_string should emit "1K"
TEST_CHECK(b.from_string("/tmp/a=RW,1K") == 0);
{
Branches::Ptr p = b;
TEST_CHECK((*p)[0].minfreespace() == 1024ULL);
TEST_CHECK(p->to_string() == "/tmp/a=RW,1K");
// Re-parsing the output should produce the same state
TEST_CHECK(b.from_string(p->to_string()) == 0);
}
{
Branches::Ptr p = b;
TEST_CHECK((*p)[0].minfreespace() == 1024ULL);
}
}
void
test_branches_minfreespace_round_trip_1t()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a=RW,1T") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p)[0].minfreespace() == 1024ULL * 1024 * 1024 * 1024);
TEST_CHECK(p->to_string() == "/tmp/a=RW,1T");
}
// ---------------------------------------------------------------------------
// Instruction dispatch edge cases
// ---------------------------------------------------------------------------
void
test_branches_clear_with_equals_alone()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b") == 0);
TEST_CHECK(b->size() == 2);
// "=" alone clears the list
TEST_CHECK(b.from_string("=") == 0);
TEST_CHECK(b->empty());
}
void
test_branches_empty_string_clears_same_as_equals()
{
// "" and "=" both dispatch to l::set — should produce identical result
Branches b1, b2;
TEST_CHECK(b1.from_string("/tmp/a") == 0);
TEST_CHECK(b2.from_string("/tmp/a") == 0);
TEST_CHECK(b1.from_string("=") == 0);
TEST_CHECK(b2.from_string("") == 0);
TEST_CHECK(b1->empty());
TEST_CHECK(b2->empty());
}
void
test_branches_plus_and_plus_greater_equivalent()
{
// "+" and "+>" both dispatch to l::add_end
Branches b1, b2;
TEST_CHECK(b1.from_string("/tmp/a") == 0);
TEST_CHECK(b2.from_string("/tmp/a") == 0);
TEST_CHECK(b1.from_string("+/tmp/b") == 0);
TEST_CHECK(b2.from_string("+>/tmp/b") == 0);
TEST_CHECK(b1->to_string() == b2->to_string());
}
void
test_branches_unknown_instruction_einval()
{
Branches b;
// "++" is not a recognized instruction
TEST_CHECK(b.from_string("++-/tmp/a") == -EINVAL);
// "+-" is not recognized either
TEST_CHECK(b.from_string("+-/tmp/a") == -EINVAL);
}
// ---------------------------------------------------------------------------
// parse_branch edge cases
// ---------------------------------------------------------------------------
void
test_branches_empty_options_after_equals_einval()
{
Branches b;
// "/tmp/a=" — empty options field (no mode) is invalid
TEST_CHECK(b.from_string("/tmp/a=") == -EINVAL);
}
void
test_branches_path_with_equals_in_name()
{
Branches b;
// rsplit1 splits on the LAST '=' so "/path/with=sign=RW" works
TEST_CHECK(b.from_string("/tmp/with=equals=RW") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p)[0].path == "/tmp/with=equals");
TEST_CHECK((*p)[0].mode == Branch::Mode::RW);
}
// ---------------------------------------------------------------------------
// add_begin / add_end with multiple colon-separated paths
// ---------------------------------------------------------------------------
void
test_branches_add_end_multiple_paths()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a") == 0);
// "+>" with two paths should append both in order
TEST_CHECK(b.from_string("+>/tmp/b:/tmp/c") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 3);
TEST_CHECK((*p)[0].path == "/tmp/a");
TEST_CHECK((*p)[1].path == "/tmp/b");
TEST_CHECK((*p)[2].path == "/tmp/c");
}
void
test_branches_add_begin_multiple_paths()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a") == 0);
// "+<" with two paths should prepend both in order
TEST_CHECK(b.from_string("+</tmp/b:/tmp/c") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 3);
TEST_CHECK((*p)[0].path == "/tmp/b");
TEST_CHECK((*p)[1].path == "/tmp/c");
TEST_CHECK((*p)[2].path == "/tmp/a");
}
// ---------------------------------------------------------------------------
// erase_fnmatch edge cases
// ---------------------------------------------------------------------------
void
test_branches_erase_empty_pattern_noop()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b") == 0);
// "-" alone (empty pattern) — no-op, list unchanged
TEST_CHECK(b.from_string("-") == 0);
TEST_CHECK(b->size() == 2);
}
void
test_branches_erase_wildcard_removes_all()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b:/tmp/c") == 0);
// "-*" — '*' matches everything (no FNM_PATHNAME, so '*' matches '/')
TEST_CHECK(b.from_string("-*") == 0);
TEST_CHECK(b->empty());
}
void
test_branches_erase_nonmatching_pattern_noop()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b") == 0);
// Pattern that matches nothing — list unchanged
TEST_CHECK(b.from_string("-/mnt/*") == 0);
TEST_CHECK(b->size() == 2);
}
void
test_branches_erase_multi_pattern_first_miss_second_hit()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/mnt/b") == 0);
// First pattern matches nothing, second matches /mnt/b
TEST_CHECK(b.from_string("-/nonexist/*:/mnt/*") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 1);
TEST_CHECK((*p)[0].path == "/tmp/a");
}
// ---------------------------------------------------------------------------
// erase_begin / erase_end on single-element list
// ---------------------------------------------------------------------------
void
test_branches_erase_end_single_element()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a") == 0);
TEST_CHECK(b.from_string("->") == 0);
TEST_CHECK(b->empty());
}
void
test_branches_erase_begin_single_element()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a") == 0);
TEST_CHECK(b.from_string("-<") == 0);
TEST_CHECK(b->empty());
}
void
test_branches_erase_end_empty_returns_enoent()
{
Branches b;
TEST_CHECK(b.from_string("->") == -ENOENT);
TEST_CHECK(b->empty());
}
void
test_branches_erase_begin_empty_returns_enoent()
{
Branches b;
TEST_CHECK(b.from_string("-<") == -ENOENT);
TEST_CHECK(b->empty());
}
// ---------------------------------------------------------------------------
// Operation sequences
// ---------------------------------------------------------------------------
void
test_branches_clear_then_add()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b") == 0);
TEST_CHECK(b.from_string("=") == 0);
TEST_CHECK(b->empty());
TEST_CHECK(b.from_string("+/tmp/c") == 0);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 1);
TEST_CHECK((*p)[0].path == "/tmp/c");
}
void
test_branches_add_begin_then_end_then_erase_both()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/b") == 0);
TEST_CHECK(b.from_string("+</tmp/a") == 0); // prepend
TEST_CHECK(b.from_string("+>/tmp/c") == 0); // append
{
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 3);
TEST_CHECK((*p)[0].path == "/tmp/a");
TEST_CHECK((*p)[1].path == "/tmp/b");
TEST_CHECK((*p)[2].path == "/tmp/c");
}
TEST_CHECK(b.from_string("-<") == 0); // remove first
TEST_CHECK(b.from_string("->") == 0); // remove last
{
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 1);
TEST_CHECK((*p)[0].path == "/tmp/b");
}
}
void
test_branches_error_in_multipath_add_leaves_list_unchanged()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a") == 0);
// Second path has invalid mode — the whole add should fail atomically
TEST_CHECK(b.from_string("+/tmp/b:/tmp/c=XX") == -EINVAL);
// Original list unchanged
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 1);
TEST_CHECK((*p)[0].path == "/tmp/a");
}
void
test_branches_error_in_set_leaves_list_unchanged()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a") == 0);
// Invalid set — list should be unchanged
TEST_CHECK(b.from_string("=/tmp/b=BADMODE") == -EINVAL);
Branches::Ptr p = b;
TEST_CHECK((*p).size() == 1);
TEST_CHECK((*p)[0].path == "/tmp/a");
}
// ---------------------------------------------------------------------------
// Branches::minfreespace live tracking
// ---------------------------------------------------------------------------
void
test_branches_global_minfreespace_live_tracking()
{
Branches b;
b.from_string("/tmp/a");
// Branch uses pointer to Branches::minfreespace — tracks it live
uint64_t initial = b.minfreespace;
{
Branches::Ptr p = b;
TEST_CHECK((*p)[0].minfreespace() == initial);
}
b.minfreespace = 999999;
{
Branches::Ptr p = b;
TEST_CHECK((*p)[0].minfreespace() == 999999);
}
}
// ---------------------------------------------------------------------------
// SrcMounts: from_string stub and live reflection of Branches mutations
// ---------------------------------------------------------------------------
void
test_branches_srcmounts_from_string_noop()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a:/tmp/b") == 0);
SrcMounts sm(b);
// from_string on SrcMounts is a no-op stub — always returns 0, no change
TEST_CHECK(sm.from_string("/tmp/c:/tmp/d") == 0);
// Branches should be unchanged (still /tmp/a:/tmp/b)
TEST_CHECK(sm.to_string() == "/tmp/a:/tmp/b");
}
void
test_branches_srcmounts_reflects_mutations()
{
Branches b;
TEST_CHECK(b.from_string("/tmp/a") == 0);
SrcMounts sm(b);
TEST_CHECK(sm.to_string() == "/tmp/a");
// Mutate Branches; SrcMounts::to_string should reflect the new state
TEST_CHECK(b.from_string("+/tmp/b") == 0);
TEST_CHECK(sm.to_string() == "/tmp/a:/tmp/b");
TEST_CHECK(b.from_string("->") == 0);
TEST_CHECK(sm.to_string() == "/tmp/a");
}
void
test_config_cachefiles()
{
@ -1943,6 +2842,64 @@ TEST_LIST =
{"config_int",test_config_int},
{"config_str",test_config_str},
{"config_branches",test_config_branches},
{"branch_default_ctor_mode",test_branch_default_ctor_mode},
{"branch_to_string_modes",test_branch_to_string_modes},
{"branch_to_string_with_minfreespace",test_branch_to_string_with_minfreespace},
{"branch_mode_predicates",test_branch_mode_predicates},
{"branch_minfreespace_pointer_vs_value",test_branch_minfreespace_pointer_vs_value},
{"branch_copy_constructor",test_branch_copy_constructor},
{"branch_copy_assignment",test_branch_copy_assignment},
{"branches_default_minfreespace",test_branches_default_minfreespace},
{"branches_per_branch_minfreespace_overrides_default",test_branches_per_branch_minfreespace_overrides_default},
{"branches_add_end_operator",test_branches_add_end_operator},
{"branches_add_begin_operator",test_branches_add_begin_operator},
{"branches_set_operator",test_branches_set_operator},
{"branches_erase_end_operator",test_branches_erase_end_operator},
{"branches_erase_begin_operator",test_branches_erase_begin_operator},
{"branches_erase_fnmatch_operator",test_branches_erase_fnmatch_operator},
{"branches_erase_fnmatch_multiple_patterns",test_branches_erase_fnmatch_multiple_patterns},
{"branches_invalid_mode",test_branches_invalid_mode},
{"branches_invalid_minfreespace",test_branches_invalid_minfreespace},
{"branches_to_paths",test_branches_to_paths},
{"branches_cow_semantics",test_branches_cow_semantics},
{"branches_mode_round_trip",test_branches_mode_round_trip},
{"branches_srcmounts_to_string",test_branches_srcmounts_to_string},
{"branches_srcmounts_empty",test_branches_srcmounts_empty},
{"branch_to_string_1t",test_branch_to_string_1t},
{"branch_to_string_non_exact_multiple",test_branch_to_string_non_exact_multiple},
{"branch_to_string_zero_owned_minfreespace",test_branch_to_string_zero_owned_minfreespace},
{"branch_copy_pointer_aliasing",test_branch_copy_pointer_aliasing},
{"branches_mode_case_sensitivity",test_branches_mode_case_sensitivity},
{"branches_minfreespace_lowercase_suffixes",test_branches_minfreespace_lowercase_suffixes},
{"branches_minfreespace_bytes_suffix",test_branches_minfreespace_bytes_suffix},
{"branches_minfreespace_zero",test_branches_minfreespace_zero},
{"branches_minfreespace_negative_invalid",test_branches_minfreespace_negative_invalid},
{"branches_minfreespace_overflow",test_branches_minfreespace_overflow},
{"branches_minfreespace_round_trip",test_branches_minfreespace_round_trip},
{"branches_minfreespace_round_trip_1t",test_branches_minfreespace_round_trip_1t},
{"branches_clear_with_equals_alone",test_branches_clear_with_equals_alone},
{"branches_empty_string_clears_same_as_equals",test_branches_empty_string_clears_same_as_equals},
{"branches_plus_and_plus_greater_equivalent",test_branches_plus_and_plus_greater_equivalent},
{"branches_unknown_instruction_einval",test_branches_unknown_instruction_einval},
{"branches_empty_options_after_equals_einval",test_branches_empty_options_after_equals_einval},
{"branches_path_with_equals_in_name",test_branches_path_with_equals_in_name},
{"branches_add_end_multiple_paths",test_branches_add_end_multiple_paths},
{"branches_add_begin_multiple_paths",test_branches_add_begin_multiple_paths},
{"branches_erase_empty_pattern_noop",test_branches_erase_empty_pattern_noop},
{"branches_erase_wildcard_removes_all",test_branches_erase_wildcard_removes_all},
{"branches_erase_nonmatching_pattern_noop",test_branches_erase_nonmatching_pattern_noop},
{"branches_erase_multi_pattern_first_miss_second_hit",test_branches_erase_multi_pattern_first_miss_second_hit},
{"branches_erase_end_single_element",test_branches_erase_end_single_element},
{"branches_erase_begin_single_element",test_branches_erase_begin_single_element},
{"branches_erase_end_empty_returns_enoent",test_branches_erase_end_empty_returns_enoent},
{"branches_erase_begin_empty_returns_enoent",test_branches_erase_begin_empty_returns_enoent},
{"branches_clear_then_add",test_branches_clear_then_add},
{"branches_add_begin_then_end_then_erase_both",test_branches_add_begin_then_end_then_erase_both},
{"branches_error_in_multipath_add_leaves_list_unchanged",test_branches_error_in_multipath_add_leaves_list_unchanged},
{"branches_error_in_set_leaves_list_unchanged",test_branches_error_in_set_leaves_list_unchanged},
{"branches_global_minfreespace_live_tracking",test_branches_global_minfreespace_live_tracking},
{"branches_srcmounts_from_string_noop",test_branches_srcmounts_from_string_noop},
{"branches_srcmounts_reflects_mutations",test_branches_srcmounts_reflects_mutations},
{"config_cachefiles",test_config_cachefiles},
{"config_inodecalc",test_config_inodecalc},
{"config_moveonenospc",test_config_moveonenospc},

Loading…
Cancel
Save