diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-26 09:12:21 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-26 09:12:21 +0200 |
| commit | 1eb967082ac29d6833a87733ac5bbafd41399468 (patch) | |
| tree | b856ce0cc3d93742ae3e595f4e74ceba6d611635 /cmd | |
| parent | dd7fb519b25b75a8a33868c835ddf0b991ef8c24 (diff) | |
feat: Add integration tests for variable assignments and fix RPN parser bugs
Diffstat (limited to 'cmd')
| -rw-r--r-- | cmd/gt/cli_test.go | 194 | ||||
| -rw-r--r-- | cmd/gt/main_test.go | 47 |
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) + } + }) + } +} |
