import { test } from "node:test"; import assert from "node:assert/strict"; import { formatBytes, formatRate, formatPercent, formatCount, toFahrenheit, formatSeconds, toFixedHalfEven, formatNetworkRate, formatFixed0, formatAutoUnit, formatAutoHz } from "../../glances/outputs/static/js/v5/format.js"; test("formatBytes uses binary units", () => { assert.equal(formatBytes(0), "0B"); assert.equal(formatBytes(1023), "1023B"); assert.equal(formatBytes(1024), "1.0K"); assert.equal(formatBytes(16417853440), "15.3G"); }); test("formatBytes survives what the API can actually send", () => { // A rate field is null until its second cycle -- see the v5 base plugin. assert.equal(formatBytes(null), "-"); assert.equal(formatBytes(undefined), "-"); }); test("formatRate marks per-second values", () => { assert.equal(formatRate(1024), "1.0K/s"); assert.equal(formatRate(null), "-"); }); test("formatPercent keeps one decimal", () => { assert.equal(formatPercent(52.5), "52.5%"); assert.equal(formatPercent(0), "0.0%"); assert.equal(formatPercent(null), "-"); }); test("formatCount is a plain integer below 1024 and K-scaled at or above it", () => { // The TUI is the authority here: _ctx_sw_value_cell() // (glances/plugins/cpu/render_curses_v5.py:52-70) scales at >= 1024, not // >= 1000, matching v4's auto_unit. Counting in powers of two is odd, but // the two outputs agreeing matters more than either being tidy. assert.equal(formatCount(0), "0"); assert.equal(formatCount(42), "42"); assert.equal(formatCount(1023), "1023"); assert.equal(formatCount(1024), "1.0K"); assert.equal(formatCount(6860), "6.7K"); assert.equal(formatCount(1048576), "1.0M"); }); test("formatCount truncates below 1024, like the TUI's int()", () => { // These fields are `rate`s, so the values are floats, and an idle // machine's interrupts/s is routinely under 1024 -- i.e. this is the // common path, not an edge case. `_ctx_sw_value_cell()` // (glances/plugins/cpu/render_curses_v5.py) formats an unscaled counter // with `f"{int(value)}"`, which truncates: 855.6 reads 855 there, so // Math.round would have shown 856 in the browser for the same tick. assert.equal(formatCount(855.6), "855"); assert.equal(formatCount(1023.9), "1023"); // At or above 1024 both sides go through one decimal and already agree. assert.equal(formatCount(1024.9), "1.0K"); }); test("formatCount returns the missing marker for a non-number", () => { // `rate` fields are null until their second cycle -- null IS a value the // API sends, so this is a live path, not defensive padding. assert.equal(formatCount(null), "-"); assert.equal(formatCount(undefined), "-"); assert.equal(formatCount("12"), "-"); }); test("toFahrenheit mirrors glances.globals.to_fahrenheit", () => { // celsius * 1.8 + 32, and nothing else -- no rounding, no unit. The // caller decides how to render, because gpu's missing marker is "N/A" // while every other v5 formatter uses "-". assert.equal(toFahrenheit(0), 32); assert.equal(toFahrenheit(100), 212); assert.equal(toFahrenheit(55), 131); }); test("formatSeconds mirrors the TUI's format_seconds exactly", () => { // glances/outputs/curses_formatters_v5.py:66-80. Each boundary on both // sides: the unit changes at 60, 3600 and 86400. assert.equal(formatSeconds(0), "0s"); assert.equal(formatSeconds(59), "59s"); assert.equal(formatSeconds(60), "1m00s"); assert.equal(formatSeconds(3599), "59m59s"); assert.equal(formatSeconds(3600), "1h00m"); assert.equal(formatSeconds(86399), "23h59m"); assert.equal(formatSeconds(86400), "1d00h"); assert.equal(formatSeconds(273600), "3d04h"); }); test("formatSeconds truncates like int(float(value))", () => { assert.equal(formatSeconds(61.9), "1m01s"); assert.equal(formatSeconds("61.9"), "1m01s"); }); test("formatSeconds returns the TUI's empty string for what it cannot parse", () => { // NOT this module's "-": format_seconds() catches TypeError/ValueError and // returns "", and the TUI then renders nothing. assert.equal(formatSeconds(null), ""); assert.equal(formatSeconds(undefined), ""); assert.equal(formatSeconds(""), ""); assert.equal(formatSeconds("abc"), ""); }); test("toFixedHalfEven matches CPython's float formatting, exact ties included", () => { // Expected strings are CPython's f"{value:.{digits}f}". JS toFixed agrees // everywhere except on an EXACT tie, which it rounds away from zero. const cases = [ [0.15, 1, "0.1"], // stored as 0.1499999..., no tie -- a detector using value * 10 would call it one [1.25, 1, "1.2"], [1.35, 1, "1.4"], // stored above 1.35, no tie [0.75, 1, "0.8"], [42.5, 0, "42"], [43.5, 0, "44"], [0.5, 0, "0"], [-54.5, 0, "-54"], [-54.4, 0, "-54"], [108.5, 0, "108"], // 42.5 C in Fahrenheit: 42.5 * 1.8 + 32 is exactly 108.5 ]; for (const [value, digits, expected] of cases) { assert.equal(toFixedHalfEven(value, digits), expected, `${value} at ${digits} digit(s)`); } }); test("formatBytes truncates below 1K like the TUI's int()", () => { // _auto_unit() in curses_formatters_v5.py prints int(value) below 1024. assert.equal(formatBytes(855.6), "855B"); assert.equal(formatBytes(1023.9), "1023B"); }); test("formatBytes breaks an exact one-decimal tie to even, like the TUI", () => { assert.equal(formatBytes(1280), "1.2K"); // 1.25K exactly assert.equal(formatBytes(1792), "1.8K"); // 1.75K exactly }); test("formatRate inherits the TUI's sub-K truncation", () => { assert.equal(formatRate(855.6), "855B/s"); // format_bytespers(855.6) }); test("formatNetworkRate mirrors network's _format_rate: bits, or bytes under --byte", () => { assert.equal(formatNetworkRate(100, false), "800b"); assert.equal(formatNetworkRate(1048576, false), "8.0Mb"); assert.equal(formatNetworkRate(524288, false), "4.0Mb"); assert.equal(formatNetworkRate(100, true), "100"); assert.equal(formatNetworkRate(1048576, true), "1.0M"); assert.equal(formatNetworkRate(524288, true), "512.0K"); assert.equal(formatNetworkRate(null, false), "-"); assert.equal(formatNetworkRate(null, true), "-"); }); test("formatFixed0 is Python's :.0f, and the usual missing marker", () => { assert.equal(formatFixed0(42.5), "42"); assert.equal(formatFixed0(-71.2), "-71"); assert.equal(formatFixed0(1200), "1200"); assert.equal(formatFixed0(null), "-"); assert.equal(formatFixed0("ERR"), "-"); }); // The cases are auto_unit()'s own docstring (glances/globals.py:431-447) -- // the one place the v4 algorithm is specified by example. test("formatAutoUnit mirrors v4 auto_unit", () => { assert.equal(formatAutoUnit(613421788), "585M"); assert.equal(formatAutoUnit(5307033647), "4.94G"); assert.equal(formatAutoUnit(44968414685), "41.9G"); assert.equal(formatAutoUnit(838471403472), "781G"); assert.equal(formatAutoUnit(9683209690677), "8.81T"); // A quotient of exactly 1024 stays in the smaller unit: the loop takes the // largest prefix whose quotient is > 1, so 1G is "1024M", not "1.0G". assert.equal(formatAutoUnit(1073741824), "1024M"); // The trailing zero is part of the contract: a fixed-decimal string, never // a Number round-trip that would print "1.1G". assert.equal(formatAutoUnit(1181116006), "1.10G"); }); test("formatAutoUnit below 1K, at zero and on a missing value", () => { // Python: `if number == 0: return '0'` -- before any division. assert.equal(formatAutoUnit(0), "0"); // No prefix quotient is > 1, so the fallthrough formats the number itself: // 0 decimals for an integer, 2 for a float (Python's isinstance check). assert.equal(formatAutoUnit(500), "500"); assert.equal(formatAutoUnit(500.5), "500.50"); assert.equal(formatAutoUnit(1024), "1024"); assert.equal(formatAutoUnit(null), "-"); assert.equal(formatAutoUnit(undefined), "-"); }); // Mirrors npu/render_curses_v5.py::_auto_hz() -- base 1000, one decimal once // scaled, a plain truncated integer below 1K. test("formatAutoHz mirrors npu's _auto_hz: base 1000, one decimal", () => { assert.equal(formatAutoHz(1000000000), "1.0G"); assert.equal(formatAutoHz(2000000000), "2.0G"); assert.equal(formatAutoHz(1500000000), "1.5G"); assert.equal(formatAutoHz(1000000), "1.0M"); assert.equal(formatAutoHz(1000), "1.0K"); assert.equal(formatAutoHz(999), "999"); assert.equal(formatAutoHz(0), "0"); }); test("formatAutoHz accepts a numeric string, like Python's float()", () => { assert.equal(formatAutoHz("1"), "1"); assert.equal(formatAutoHz("2000000000"), "2.0G"); }); test('formatAutoHz returns "?" for whatever it cannot parse', () => { // _auto_hz() catches TypeError/ValueError from float(hz) and returns "?" -- // NOT this module's usual "-" missing marker. assert.equal(formatAutoHz(null), "?"); assert.equal(formatAutoHz(undefined), "?"); assert.equal(formatAutoHz("abc"), "?"); assert.equal(formatAutoHz(""), "?"); assert.equal(formatAutoHz(NaN), "?"); });