Files
Waybar/test/utils/format.cpp
T
Guillaume Maudoux a3dc55f9ee feat(format): expand pow_format modifiers
Extend the pow_format spec parser beyond alignment so configs can shape the
rendered number, not just pad it. A forced scale (#, k, M, G, T, P) pins the SI
prefix instead of auto-selecting it and, since the author then knows both scale
and unit, hides the prefix and unit by default; U brings the unit back, u hides
it in auto mode, and i forces integer display. b and B override the decimal or
binary base independently of the call site, and a trailing width now fixes the
coefficient field when a scale is forced, overflowing to '#' when it does not
fit.

The old '>' and '<' branches rendered via fmt::format("{}", s), which discarded
every new field on the recursive call; they now build from a single render path
that reads the current formatter, so modifiers survive alignment. The auto path
is byte-identical to before, verified against the previous implementation.

Adds a Catch2 suite covering each modifier alone and combined, plus the
backward-compatible alignment cases, and documents the modifiers in the network
and disk man pages.
2026-07-04 23:08:42 +02:00

106 lines
4.2 KiB
C++

#include "util/format.hpp"
#if __has_include(<catch2/catch_test_macros.hpp>)
#include <catch2/catch_test_macros.hpp>
#else
#include <catch2/catch.hpp>
#endif
#include <fmt/format.h>
// Helpers to build the two representative sample values used throughout the
// pow_format modifier tests.
static std::string fmtA(const char* spec) {
// 1536 bytes, binary (base 1024) -> auto "1.5kiB"
return fmt::format(fmt::runtime(std::string("{:") + spec + "}"), pow_format(1536, "B", true));
}
static std::string fmtB(const char* spec) {
// 1500000 b/s, decimal (base 1000) -> auto "1.5Mb/s"
return fmt::format(fmt::runtime(std::string("{:") + spec + "}"), pow_format(1500000, "b/s"));
}
TEST_CASE("pow_format default/auto rendering", "[format][pow_format]") {
REQUIRE(fmt::format("{}", pow_format(1536, "B", true)) == "1.5kiB");
REQUIRE(fmt::format("{}", pow_format(1500000, "b/s")) == "1.5Mb/s");
// Sub-scale values and unit-only rendering.
REQUIRE(fmt::format("{}", pow_format(500, "B")) == "500.0B");
}
TEST_CASE("pow_format hide/show unit (u/U)", "[format][pow_format]") {
REQUIRE(fmtA("u") == "1.5ki"); // unit hidden, scale prefix (ki) kept
REQUIRE(fmtA("U") == "1.5kiB"); // explicit show = auto default
REQUIRE(fmtB("u") == "1.5M");
REQUIRE(fmtB("U") == "1.5Mb/s");
}
TEST_CASE("pow_format force integer (i)", "[format][pow_format]") {
REQUIRE(fmtA("i") == "2kiB"); // 1.5 -> 2
REQUIRE(fmtA("iu") == "2ki");
REQUIRE(fmtB("i") == "2Mb/s");
}
TEST_CASE("pow_format forced scale hides prefix and unit", "[format][pow_format]") {
// Base scale (#): no prefix; unit off by default, back on with U.
REQUIRE(fmtA("#") == "1536.0");
REQUIRE(fmtA("#U") == "1536.0B");
REQUIRE(fmtA("#i") == "1536");
REQUIRE(fmtB("#") == "1500000.0");
REQUIRE(fmtB("#U") == "1500000.0b/s");
// Force kilo: whole prefix (ki) suppressed, unit off by default.
REQUIRE(fmtA("k") == "1.5");
REQUIRE(fmtA("kU") == "1.5B"); // unit shown, no 'i' (it lives on the prefix)
REQUIRE(fmtA("ki") == "2");
REQUIRE(fmtB("k") == "1500.0");
REQUIRE(fmtB("kU") == "1500.0b/s");
REQUIRE(fmtB("ki") == "1500");
// Force a scale far above the value's magnitude -> underflow to 0, no bump.
REQUIRE(fmtA("M") == "0.0");
REQUIRE(fmtA("MU") == "0.0B");
REQUIRE(fmtB("G") == "0.0");
}
TEST_CASE("pow_format force base (b/B)", "[format][pow_format]") {
// Force decimal on a binary call-site value.
REQUIRE(fmtA("b") == "1.5kB"); // base 1000, prefix 'k', no 'i'
REQUIRE(fmtA("bu") == "1.5k");
REQUIRE(fmtA("b#") == "1536.0");
// Force binary on a decimal call-site value.
REQUIRE(fmtB("B") == "1.4Mib/s"); // base 1024, prefix 'Mi'
REQUIRE(fmtB("Bu") == "1.4Mi");
REQUIRE(fmtB("Bk") == "1464.8");
REQUIRE(fmtB("BkU") == "1464.8b/s");
}
TEST_CASE("pow_format fixed width with # overflow", "[format][pow_format]") {
// Base scale, numeric field width 3: 1536.0 (6 chars) overflows -> ###.
REQUIRE(fmtA("=3#") == "###");
REQUIRE(fmtA("=6#") == "1536.0");
REQUIRE(fmtA("=3#U") == "###B");
REQUIRE(fmtA("=6#U") == "1536.0B");
REQUIRE(fmtB("=4k#") == "####");
// Width digit position is flexible: {:=3#} and {:=#3} are equivalent.
REQUIRE(fmtA("=#3") == "###");
// Force kilo, width 6, coefficient "1500" fits, shown with unit; the '='
// column left-justifies the coefficient in its field.
REQUIRE(fmtB("=6kiU") == "1500 b/s");
}
TEST_CASE("pow_format modifiers compose with alignment", "[format][pow_format]") {
REQUIRE(fmtA(">u") == " 1.5ki"); // right-align, unit hidden (prefix kept)
REQUIRE(fmtB(">ki") == "1500"); // force kilo+int (unit off), no scale width
REQUIRE(fmtB("Bu") == "1.4Mi"); // force binary, unit hidden
}
TEST_CASE("pow_format backward compatible alignment", "[format][pow_format]") {
// These specs predate the new modifiers and must render as before.
REQUIRE(fmt::format("{:>}", pow_format(1536, "B", true)) == " 1.5kiB");
REQUIRE(fmt::format("{:<}", pow_format(1536, "B", true)) == "1.5kiB ");
REQUIRE(fmt::format("{:=}", pow_format(1536, "B", true)) == "1.5 kiB");
// Width without a forced scale is still ignored (only used for compat).
REQUIRE(fmt::format("{:>9}", pow_format(1536, "B", true)) == " 1.5kiB");
REQUIRE(fmt::format("{}", pow_format(1500000, "b/s")) == "1.5Mb/s");
}