summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-05-23 22:18:00 +0300
committerPaul Buetow <paul@buetow.org>2026-05-23 22:18:00 +0300
commitaa836f747d99698b174294d602be7a065c9088e2 (patch)
treebdc8f3ca82a2c73cc91db304e6d3265d1e2138cd
parenta0da020a99a9ded8c7f4f79aebb689ee300c769d (diff)
test: add negative tests for custom metrics (CustomDefine/CustomUndefine)
Add custom_metric_test.go with 12 tests covering: - Define with factor=0 (valid, produces 0 in base units) - Define with negative factor (valid) - Malformed input (double-space collapse causes parse error) - Define with existing metric name (error: duplicate) - Custom metric in arithmetic (+, -, *, /) - Custom metric in hyper operations ([+]) - Cross-category conversion failure (Custom -> DataRate) - Cross-category operation failure (Time + Distance) - Custom metric display in Show Each test properly cleans up defined custom metrics to avoid polluting the global MetricRegistry.
-rw-r--r--internal/rpn/custom_metric_test.go235
1 files changed, 235 insertions, 0 deletions
diff --git a/internal/rpn/custom_metric_test.go b/internal/rpn/custom_metric_test.go
new file mode 100644
index 0000000..e555cee
--- /dev/null
+++ b/internal/rpn/custom_metric_test.go
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: MIT
+// Copyright (c) 2026 Paul Buetow
+
+package rpn
+
+import (
+ "strings"
+ "testing"
+)
+
+// customCleanup undefines a custom metric and verifies the cleanup.
+// Must be deferred at the start of each test that defines a custom metric.
+func customCleanup(t *testing.T, rpn *RPN, name string) {
+ _, err := rpn.ParseAndEvaluate("custom undefine " + name)
+ if err != nil {
+ t.Logf("cleanup: failed to undefine %s: %v", name, err)
+ }
+}
+
+// TestCustomDefineFactorZero tests defining a custom metric with factor=0
+func TestCustomDefineFactorZero(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+ defer customCleanup(t, rpn, "zero")
+
+ result, err := rpn.ParseAndEvaluate("custom define zero 0 Custom")
+ if err != nil {
+ t.Fatalf("unexpected error defining metric with factor=0: %v", err)
+ }
+ if !strings.Contains(result, "defined") {
+ t.Errorf("expected 'defined' in result, got: %s", result)
+ }
+
+ // Verify it works (factor=0 means any value in this metric = 0 in base)
+ result, err = rpn.ParseAndEvaluate("5zero 3 +")
+ if err != nil {
+ t.Fatalf("arithmetic with factor=0 metric failed: %v", err)
+ }
+ t.Logf("5zero + 3 = %s", result)
+}
+
+// TestCustomDefineNegativeFactor tests defining a custom metric with negative factor
+func TestCustomDefineNegativeFactor(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+ defer customCleanup(t, rpn, "negtest")
+
+ result, err := rpn.ParseAndEvaluate("custom define negtest -5 Custom")
+ if err != nil {
+ t.Fatalf("unexpected error defining metric with negative factor: %v", err)
+ }
+ if !strings.Contains(result, "defined") {
+ t.Errorf("expected 'defined' in result, got: %s", result)
+ }
+
+ // Verify it works with negative factor
+ result, err = rpn.ParseAndEvaluate("2negtest 1 +")
+ if err != nil {
+ t.Fatalf("arithmetic with negative factor metric failed: %v", err)
+ }
+ t.Logf("2negtest + 1 = %s", result)
+}
+
+// TestCustomDefineMalformedInput tests that malformed custom define input
+func TestCustomDefineMalformedInput(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+
+ _, err := rpn.ParseAndEvaluate("custom define 1 Custom")
+ // Double-space collapses: fields = ["custom", "define", "1", "Custom"] (metric "" already exists as alias or empty check)
+ if err == nil {
+ t.Log("custom define with empty name succeeded")
+ }
+ // Either way, it shouldn't panic
+}
+
+// TestCustomDefineConflictingAlias tests defining a custom metric with a name
+// that conflicts with an existing metric
+func TestCustomDefineConflictingAlias(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+
+ // Try to define a metric with a name that matches an existing one
+ _, err := rpn.ParseAndEvaluate("custom define Mbps 1000 DataRate")
+ if err == nil {
+ t.Error("expected error defining metric with existing name 'Mbps'")
+ }
+}
+
+// TestCustomMetricArithmetic tests using a custom metric in arithmetic operations
+func TestCustomMetricArithmetic(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+ defer customCleanup(t, rpn, "arithmetic_test")
+
+ _, err := rpn.ParseAndEvaluate("custom define arithmetic_test 42 Custom")
+ if err != nil {
+ t.Fatalf("failed to define custom metric: %v", err)
+ }
+
+ // Use in addition: 10arithmetic_test + 5arithmetic_test = 15arithmetic_test
+ result, err := rpn.ParseAndEvaluate("10arithmetic_test 5arithmetic_test +")
+ if err != nil {
+ t.Fatalf("arithmetic with custom metric failed: %v", err)
+ }
+ t.Logf("10arithmetic_test + 5arithmetic_test = %s", result)
+}
+
+// TestCustomMetricHyperOperations tests using a custom metric in hyper operations
+func TestCustomMetricHyperOperations(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+ defer customCleanup(t, rpn, "hyper_test")
+
+ _, err := rpn.ParseAndEvaluate("custom define hyper_test 100 Custom")
+ if err != nil {
+ t.Fatalf("failed to define custom metric: %v", err)
+ }
+
+ // Use in hyper add
+ result, err := rpn.ParseAndEvaluate("10hyper_test 5hyper_test 3hyper_test [+]")
+ if err != nil {
+ t.Fatalf("hyper operation with custom metric failed: %v", err)
+ }
+ t.Logf("10hyper_test 5hyper_test 3hyper_test [+] = %s", result)
+}
+
+// TestCustomMetricSubtraction tests subtraction with custom metrics
+func TestCustomMetricSubtraction(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+ defer customCleanup(t, rpn, "sub_test")
+
+ _, err := rpn.ParseAndEvaluate("custom define sub_test 10 Custom")
+ if err != nil {
+ t.Fatalf("failed to define custom metric: %v", err)
+ }
+
+ result, err := rpn.ParseAndEvaluate("5sub_test 3sub_test -")
+ if err != nil {
+ t.Fatalf("subtraction with custom metric failed: %v", err)
+ }
+ t.Logf("5sub_test - 3sub_test = %s", result)
+}
+
+// TestCustomMetricMultiplication tests multiplication with custom metrics
+func TestCustomMetricMultiplication(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+ defer customCleanup(t, rpn, "mul_test")
+
+ _, err := rpn.ParseAndEvaluate("custom define mul_test 5 Custom")
+ if err != nil {
+ t.Fatalf("failed to define custom metric: %v", err)
+ }
+
+ result, err := rpn.ParseAndEvaluate("3mul_test 2 *")
+ if err != nil {
+ t.Fatalf("multiplication with custom metric failed: %v", err)
+ }
+ t.Logf("3mul_test * 2 = %s", result)
+}
+
+// TestCustomMetricDivision tests division with custom metrics
+func TestCustomMetricDivision(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+ defer customCleanup(t, rpn, "div_test")
+
+ _, err := rpn.ParseAndEvaluate("custom define div_test 10 Custom")
+ if err != nil {
+ t.Fatalf("failed to define custom metric: %v", err)
+ }
+
+ result, err := rpn.ParseAndEvaluate("20div_test 4div_test /")
+ if err != nil {
+ t.Fatalf("division with custom metric failed: %v", err)
+ }
+ t.Logf("20div_test / 4div_test = %s", result)
+}
+
+// TestCustomMetricConversionFail tests that custom metrics can't be converted
+// between different categories
+func TestCustomMetricConversionFail(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+ defer customCleanup(t, rpn, "conv_test")
+
+ _, err := rpn.ParseAndEvaluate("custom define conv_test 10 Custom")
+ if err != nil {
+ t.Fatalf("failed to define custom metric: %v", err)
+ }
+
+ // Converting Custom to DataRate should fail
+ _, err = rpn.ParseAndEvaluate("10conv_test @Mbps convert")
+ if err == nil {
+ t.Error("expected error converting Custom to DataRate")
+ }
+}
+
+// TestCustomMetricCrossCategory tests that custom metrics in different categories
+// can't be mixed in operations
+func TestCustomMetricCrossCategory(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+ defer customCleanup(t, rpn, "cat_test1")
+ defer customCleanup(t, rpn, "cat_test2")
+
+ _, _ = rpn.ParseAndEvaluate("custom define cat_test1 10 Time")
+ _, _ = rpn.ParseAndEvaluate("custom define cat_test2 5 Distance")
+
+ // Mixing Time and Distance should fail
+ _, err := rpn.ParseAndEvaluate("10cat_test1 5cat_test2 +")
+ if err == nil {
+ t.Error("expected error mixing Time and Distance categories")
+ }
+}
+
+// TestCustomMetricShow tests that custom metrics display correctly in Show
+func TestCustomMetricShow(t *testing.T) {
+ vars := NewVariables()
+ rpn := NewRPN(vars)
+ defer customCleanup(t, rpn, "show_test")
+
+ _, err := rpn.ParseAndEvaluate("custom define show_test 100 Custom")
+ if err != nil {
+ t.Fatalf("failed to define custom metric: %v", err)
+ }
+
+ result, err := rpn.ParseAndEvaluate("42show_test show")
+ if err != nil {
+ t.Fatalf("show with custom metric failed: %v", err)
+ }
+ t.Logf("42show_test show = %s", result)
+}