summaryrefslogtreecommitdiff
path: root/cmd
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2026-03-26 09:12:21 +0200
committerPaul Buetow <paul@buetow.org>2026-03-26 09:12:21 +0200
commit1eb967082ac29d6833a87733ac5bbafd41399468 (patch)
treeb856ce0cc3d93742ae3e595f4e74ceba6d611635 /cmd
parentdd7fb519b25b75a8a33868c835ddf0b991ef8c24 (diff)
feat: Add integration tests for variable assignments and fix RPN parser bugs
Diffstat (limited to 'cmd')
-rw-r--r--cmd/gt/cli_test.go194
-rw-r--r--cmd/gt/main_test.go47
2 files changed, 241 insertions, 0 deletions
diff --git a/cmd/gt/cli_test.go b/cmd/gt/cli_test.go
index 9639151..916655f 100644
--- a/cmd/gt/cli_test.go
+++ b/cmd/gt/cli_test.go
@@ -209,3 +209,197 @@ func TestCLIInvalidRPN(t *testing.T) {
t.Errorf("error output should contain 'Error:', got: %s", string(output))
}
}
+
+// TestCLIVariableAssignment tests all variable assignment syntaxes.
+func TestCLIVariableAssignment(t *testing.T) {
+ binaryPath := buildBinary(t)
+
+ tests := []struct {
+ name string
+ args []string
+ expected string
+ }{
+ {
+ name: "Standard assignment x = 2",
+ args: []string{"x", "2", "=", "x", "2", "+"},
+ expected: "4",
+ },
+ {
+ name: "Right assignment x 2 := (value on stack, right)",
+ args: []string{"x", "2", ":=", "x", "2", "+"},
+ expected: "4",
+ },
+ {
+ name: "Left assignment 2 x =: (value on stack, left)",
+ args: []string{"2", "x", "=: ", "x", "2", "+"},
+ expected: "4",
+ },
+ {
+ name: "Stack variant with =: (value on stack, left)",
+ args: []string{"2", "x", "=: ", "x", "2", "+"},
+ expected: "4",
+ },
+ {
+ name: "Assignment with existing variable",
+ args: []string{"x", "5", "=", "x", "3", "+"},
+ expected: "8",
+ },
+ {
+ name: "Assignment with complex expression",
+ args: []string{"x", "2", "=", "x", "x", "*", "x", "+"},
+ expected: "6",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cmd := exec.Command(binaryPath, tt.args...)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("command failed: %v\nOutput: %s", err, string(output))
+ }
+
+ outputStr := strings.TrimSpace(string(output))
+ if !strings.Contains(outputStr, tt.expected) {
+ t.Errorf("output should contain '%s', got: %s", tt.expected, outputStr)
+ }
+ })
+ }
+}
+
+// TestCLIVariableAssignmentSyntaxes tests each assignment syntax individually
+// and verifies the variable can be used in subsequent expressions.
+func TestCLIVariableAssignmentSyntaxes(t *testing.T) {
+ binaryPath := buildBinary(t)
+
+ tests := []struct {
+ name string
+ args []string
+ expected string
+ }{
+ {
+ name: "Assignment: x = 2, then x 2 +",
+ args: []string{"x", "2", "=", "x", "2", "+"},
+ expected: "4",
+ },
+ {
+ name: "Right assignment: x 2 :=, then x 2 +",
+ args: []string{"x", "2", ":=", "x", "2", "+"},
+ expected: "4",
+ },
+ {
+ name: "Left assignment: 2 x =:, then x 2 +",
+ args: []string{"2", "x", "=: ", "x", "2", "+"},
+ expected: "4",
+ },
+ {
+ name: "Assignment: y = 10, then y 5 *",
+ args: []string{"y", "10", "=", "y", "5", "*"},
+ expected: "50",
+ },
+ {
+ name: "Assignment: pi = 3.14159, then pi 2 *",
+ args: []string{"pi", "3.14159", "=", "pi", "2", "*"},
+ expected: "6.28318",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cmd := exec.Command(binaryPath, tt.args...)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("command failed: %v\nOutput: %s", err, string(output))
+ }
+
+ outputStr := strings.TrimSpace(string(output))
+ if !strings.Contains(outputStr, tt.expected) {
+ t.Errorf("output should contain '%s', got: %s", tt.expected, outputStr)
+ }
+ })
+ }
+}
+
+// TestCLIVariableAssignmentPreservation tests that variables persist within a single expression.
+func TestCLIVariableAssignmentPreservation(t *testing.T) {
+ binaryPath := buildBinary(t)
+
+ tests := []struct {
+ name string
+ args []string
+ expected string
+ }{
+ {
+ name: "Single assignment, multiple uses",
+ args: []string{"x", "5", "=", "x", "x", "+"},
+ expected: "10", // x=5, then x+x=10
+ },
+ {
+ name: "Assignment with calculation as value (stack-variant)",
+ args: []string{"2", "3", "+", "x", "=: ", "x", "4", "*"},
+ expected: "20", // 2+3=5, x=: assigns 5 to x, x*4=20
+ },
+ {
+ name: "Stack-variant with := (value on stack)",
+ args: []string{"x", "2", "3", "+", ":=", "x", "1", "+"},
+ expected: "6", // x=2+3=5, x+1=6
+ },
+ {
+ name: "Stack-variant with =: (value first)",
+ args: []string{"2", "3", "+", "x", "=: ", "x", "1", "+"},
+ expected: "6", // 2+3=5, x=: assigns 5 to x, x+1=6
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cmd := exec.Command(binaryPath, tt.args...)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("command failed: %v\nOutput: %s", err, string(output))
+ }
+
+ outputStr := strings.TrimSpace(string(output))
+ if !strings.Contains(outputStr, tt.expected) {
+ t.Errorf("output should contain '%s', got: %s", tt.expected, outputStr)
+ }
+ })
+ }
+}
+
+// TestCLIVariableAssignmentRepetition tests that repeated assignment works correctly.
+func TestCLIVariableAssignmentRepetition(t *testing.T) {
+ binaryPath := buildBinary(t)
+
+ tests := []struct {
+ name string
+ args []string
+ expected string
+ }{
+ {
+ name: "Reassign same variable",
+ args: []string{"x", "5", ":=", "x", "10", ":=", "x", "2", "+"},
+ expected: "12", // x=5, x=10, x+2=12
+ },
+ {
+ name: "Stack-variant reassignment",
+ args: []string{"x", "1", ":=", "x", "2", ":=", "x", "3", "+"},
+ expected: "5", // x=1 then x=2, x+3=5
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ cmd := exec.Command(binaryPath, tt.args...)
+ output, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("command failed: %v\nOutput: %s", err, string(output))
+ }
+
+ outputStr := strings.TrimSpace(string(output))
+ if !strings.Contains(outputStr, tt.expected) {
+ t.Errorf("output should contain '%s', got: %s", tt.expected, outputStr)
+ }
+ })
+ }
+}
diff --git a/cmd/gt/main_test.go b/cmd/gt/main_test.go
index 3ad91ab..9146d6d 100644
--- a/cmd/gt/main_test.go
+++ b/cmd/gt/main_test.go
@@ -174,3 +174,50 @@ func TestRunCommandNoArgs(t *testing.T) {
// - TestRunCommandCalcWithClear (calc with clear)
// These commands are now only available in REPL mode, not in command-line mode.
+
+// TestRunCommandAssignmentSyntaxes tests all variable assignment syntaxes
+func TestRunCommandAssignmentSyntaxes(t *testing.T) {
+ tests := []struct {
+ name string
+ input string
+ expectedVar string
+ expectedVal float64
+ expectedOut string
+ }{
+ {
+ name: "x 5 = x x + (standard assignment)",
+ input: "x 5 = x x +",
+ expectedVar: "x",
+ expectedVal: 5,
+ expectedOut: "10",
+ },
+ {
+ name: "x 5 =: x x + (left assignment)",
+ input: "5 x =: x x +",
+ expectedVar: "x",
+ expectedVal: 5,
+ expectedOut: "10",
+ },
+ {
+ name: "x 5 := x x + (right assignment)",
+ input: "x 5 := x x +",
+ expectedVar: "x",
+ expectedVal: 5,
+ expectedOut: "10",
+ },
+ }
+
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ args := []string{"gt", tt.input}
+ result, err := runCommand(args)
+ if err != nil {
+ t.Fatalf("runCommand(%q) returned error: %v", tt.input, err)
+ }
+
+ if result != tt.expectedOut {
+ t.Errorf("runCommand(%q) = %q, want %q", tt.input, result, tt.expectedOut)
+ }
+ })
+ }
+}