fix(thold): make the unit suffix conversions inverses of each other - #802
Open
somethingwithproof wants to merge 1 commit into
Open
fix(thold): make the unit suffix conversions inverses of each other#802somethingwithproof wants to merge 1 commit into
somethingwithproof wants to merge 1 commit into
Conversation
thold_display_to_raw() and thold_raw_to_display() each carried their own suffix table and disagreed. Entering 'p' scaled by 1e-9, while a value of 1e-12 rendered as 'f' and so parsed back as 1e-15: opening a threshold and saving it again divided its bound by a thousand, every time. Nano was missing from both, which is what pushed the rest of the small suffixes one place out of step. Past the largest and smallest entries the render read off the end of its pattern string and dropped the magnitude entirely, so 5e-18 displayed as 5. Both now read one shared table, so they cannot drift apart again, and a value outside the table keeps its scale rather than losing it. Stored bounds are untouched. What changes is the label they display under and the meaning of a newly typed suffix: 'p' is pico, as it always claimed to be. Refs Cacti#786 Signed-off-by: Thomas Vincent <thomasvincent@gmail.com>
Contributor
There was a problem hiding this comment.
Pull request overview
This PR fixes a correctness issue in thold’s unit-suffix parsing/formatting by ensuring thold_display_to_raw() and thold_raw_to_display() use the same SI suffix table, so values round-trip without silently changing magnitude when a threshold is opened and saved.
Changes:
- Introduces a shared SI suffix map (
thold_unit_suffixes()) and refactors both conversion functions to use it. - Adds a PHPUnit unit-test harness validating SI scaling and round-trip behavior, plus regression coverage for out-of-range magnitudes.
- Adds a Docker-based CI job (lint + coverage) including a “changed-lines coverage” gate.
Reviewed changes
Copilot reviewed 12 out of 14 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| thold_functions.php | Adds shared SI suffix table and refactors raw/display conversions to be inverse-safe. |
| tests/Unit/TholdUnitSuffixTest.php | Adds unit tests for suffix scaling, rendering, and round-tripping behavior. |
| tests/TestCase.php | Adds a shared base TestCase for plugin tests with common reset/setup behavior. |
| tests/Helpers/CactiStubs.php | Provides programmable stubs for Cacti core functions used by thold. |
| tests/fixtures/optional-core-functions.php | Supplies optional Cacti functions for tests that need to exercise function_exists() branches. |
| tests/fixtures/cacti-lib/variables.php | Fixture file to satisfy runtime include_once() paths during tests. |
| tests/docker/Dockerfile | Defines a pinned PHP 8.1 Docker image for running the unit tests + coverage. |
| tests/docker/docker-compose.yml | Provides a local docker compose runner matching CI behavior. |
| tests/bootstrap-unit.php | Test bootstrap wiring Cacti globals/functions to stubs and loading plugin code safely. |
| tests/bin/patch-coverage.php | Implements changed-lines coverage reporting and enforcement against a base ref. |
| phpunit.xml | Configures PHPUnit, bootstrap, strictness, and coverage source selection. |
| composer.json | Adds dev dependencies and scripts for linting/tests/coverage + docker test runner. |
| .gitignore | Ignores vendor, coverage, and PHPUnit cache artifacts. |
| .github/workflows/php-unit-tests.yml | Adds a GitHub Actions workflow to run Docker-based lint + unit tests + coverage gates. |
Suppressed comments (1)
tests/Unit/TholdUnitSuffixTest.php:172
- testMagnitudesBelowTheSmallestSuffixKeepTheirScale() uses 5e-18, which is within the suffix table (atto), not below the smallest supported suffix (yocto, 1e-24). Using a value below 1e-24 exercises the intended edge case (no suffix available) and ensures scale is preserved rather than silently dropped.
public function testMagnitudesBelowTheSmallestSuffixKeepTheirScale(): void {
$rendered = thold_raw_to_display(5.0e-18);
$this->assertNotSame('5', $rendered);
$this->assertEqualsWithDelta(5.0e-18, (float) thold_display_to_raw($rendered, 'thold_hi'), 5.0e-27);
}
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
+5360
to
5362
| $scales = thold_unit_suffixes(); | ||
| $scales = array_slice($scales, 0, 8, true) + ['' => 1.0] + array_slice($scales, 8, null, true); | ||
|
|
Comment on lines
+36
to
+52
| public static function suffixProvider() { | ||
| return [ | ||
| 'femto' => ['5f', 5.0e-15], | ||
| 'pico' => ['5p', 5.0e-12], | ||
| 'nano' => ['5n', 5.0e-9], | ||
| 'micro' => ['5u', 5.0e-6], | ||
| 'milli' => ['5m', 5.0e-3], | ||
| 'kilo' => ['5K', 5.0e3], | ||
| 'mega' => ['5M', 5.0e6], | ||
| 'giga' => ['5G', 5.0e9], | ||
| 'tera' => ['5T', 5.0e12], | ||
| 'peta' => ['5P', 5.0e15], | ||
| 'exa' => ['5E', 5.0e18], | ||
| 'zetta' => ['5Z', 5.0e21], | ||
| 'yotta' => ['5Y', 5.0e24], | ||
| ]; | ||
| } |
Comment on lines
+29
to
+34
| protected function setUp(): void { | ||
| parent::setUp(); | ||
|
|
||
| CactiStubs::reset(); | ||
| $GLOBALS['rpn_error'] = false; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #786.
thold_display_to_raw()andthold_raw_to_display()move a threshold bound between what an operator types and what is stored, so they have to be inverses. They each carried their own suffix table and disagreed.The failure that matters
Entering
pscaled by1e-9, but a stored value of1e-12rendered as5f, which parsed back as1e-15. A threshold is read out of the database, rendered into the form, and written back on every save — so opening a threshold and saving it without changing anything divided its bound by a thousand, and did so again on every visit.Nano was absent from both, which is what put the rest of the small suffixes one place out of step:
5p5n5f5p55f55aPast the ends of the table the render indexed off the end of its pattern string, so the suffix came back empty and the magnitude was silently dropped — 5e-18 displayed as plain
5.The change
Both functions now read one shared table,
thold_unit_suffixes(), so they cannot drift apart again. The render walks it for the largest scale that still leaves a value of one or more, with the empty suffix standing for a scale of one, and a value beyond the table keeps its scale instead of losing it (5e27 renders5000Y).Verified across 45 orders of magnitude:
What operators will notice
No migration is needed and no stored value is rewritten. Two things change:
1e-9now displays as5nwhere it used to say5p. Same number, correct label.pnow means pico. Anyone who had learned that thold'spmeant nano will get a thousand times less than before, which is the correction — but it is a change in what an existing habit produces, so it is worth a release note.The affected fields are the ten in
$thold_units_convert_array: the hi/low bounds, their warning and time-based counterparts, and the baseline percentages.Tests
48 tests, 100% of the changed lines covered. The round-trip property is checked for every suffix rather than spot-checked, which is the assertion that would have caught this originally.
The harness commit matches the other open pull requests, so whichever lands first the rest merge cleanly.