c65gm/internal/utils/utils_test.go

272 lines
6.2 KiB
Go

package utils
import (
"reflect"
"testing"
)
func TestParseParams(t *testing.T) {
tests := []struct {
name string
input string
want []string
wantErr bool
}{
{
name: "simple params",
input: "BYTE x 10",
want: []string{"BYTE", "x", "10"},
},
{
name: "with quoted string",
input: `PRINT "hello world"`,
want: []string{"PRINT", `"hello world"`},
},
{
name: "multiple spaces",
input: "WORD y 20",
want: []string{"WORD", "y", "20"},
},
{
name: "empty string",
input: "",
want: []string{},
},
{
name: "only spaces",
input: " ",
want: []string{},
},
{
name: "unterminated string",
input: `PRINT "hello`,
wantErr: true,
},
{
name: "string with spaces inside",
input: `CALL print ( "hello world" )`,
want: []string{"CALL", "print", "(", `"hello world"`, ")"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := ParseParams(tt.input)
if (err != nil) != tt.wantErr {
t.Errorf("ParseParams() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && !reflect.DeepEqual(got, tt.want) {
t.Errorf("ParseParams() = %v, want %v", got, tt.want)
}
})
}
}
func TestNormalizeSpaces(t *testing.T) {
tests := []struct {
name string
input string
want string
}{
{
name: "multiple spaces",
input: "BYTE x 10",
want: "BYTE x 10",
},
{
name: "preserves string spaces",
input: `PRINT "hello world"`,
want: `PRINT "hello world"`,
},
{
name: "leading trailing spaces",
input: " BYTE x ",
want: "BYTE x",
},
{
name: "tabs",
input: "BYTE\t\tx",
want: "BYTE x",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := NormalizeSpaces(tt.input)
if got != tt.want {
t.Errorf("NormalizeSpaces() = %q, want %q", got, tt.want)
}
})
}
}
func TestIsStringLiteral(t *testing.T) {
tests := []struct {
input string
want bool
}{
{`"hello"`, true},
{`"hello world"`, true},
{`""`, true},
{`"`, false},
{`hello`, false},
{`'hello'`, false},
{`hello"`, false},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got := IsStringLiteral(tt.input)
if got != tt.want {
t.Errorf("IsStringLiteral(%q) = %v, want %v", tt.input, got, tt.want)
}
})
}
}
func TestStripQuotes(t *testing.T) {
tests := []struct {
input string
want string
}{
{`"hello"`, "hello"},
{`"hello world"`, "hello world"},
{`""`, ""},
{`hello`, "hello"},
{`"`, `"`},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got := StripQuotes(tt.input)
if got != tt.want {
t.Errorf("StripQuotes(%q) = %q, want %q", tt.input, got, tt.want)
}
})
}
}
func TestValidateIdentifier(t *testing.T) {
tests := []struct {
input string
want bool
}{
{"x", true},
{"_temp", true},
{"myVar123", true},
{"_", true},
{"var_name", true},
{"123abc", false},
{"", false},
{"my-var", false},
{"my var", false},
{"my.var", false},
}
for _, tt := range tests {
t.Run(tt.input, func(t *testing.T) {
got := ValidateIdentifier(tt.input)
if got != tt.want {
t.Errorf("ValidateIdentifier(%q) = %v, want %v", tt.input, got, tt.want)
}
})
}
}
func TestEvaluateExpression(t *testing.T) {
// Simple constant lookup for testing
constLookup := func(name string) (int64, bool) {
constants := map[string]int64{
"MAXVAL": 255,
"BASE": 100,
"OFFSET": 10,
}
val, ok := constants[name]
return val, ok
}
tests := []struct {
name string
expr string
lookup ConstantLookup
want int64
wantErr bool
}{
// Decimal
{name: "simple decimal", expr: "42", want: 42},
{name: "decimal zero", expr: "0", want: 0},
// Hex
{name: "hex lowercase", expr: "$ff", want: 255},
{name: "hex uppercase", expr: "$FF", want: 255},
{name: "hex zero", expr: "$00", want: 0},
{name: "hex word", expr: "$C000", want: 0xC000},
// Binary
{name: "binary 8bit", expr: "%11111111", want: 255},
{name: "binary zero", expr: "%00000000", want: 0},
{name: "binary mixed", expr: "%10101010", want: 0xAA},
// Addition
{name: "add decimal", expr: "10+20", want: 30},
{name: "add hex", expr: "$10+$20", want: 0x30},
{name: "add binary", expr: "%1010+%0101", want: 15},
{name: "add multiple", expr: "10+20+30", want: 60},
// Subtraction
{name: "subtract", expr: "100-25", want: 75},
{name: "subtract chain", expr: "100-20-10", want: 70},
// Multiplication
{name: "multiply", expr: "10*5", want: 50},
{name: "multiply chain", expr: "2*3*4", want: 24},
// Division
{name: "divide", expr: "100/5", want: 20},
{name: "divide chain", expr: "100/5/2", want: 10},
// Binary OR
{name: "binary or", expr: "$F0|$0F", want: 0xFF},
{name: "binary or multiple", expr: "1|2|4", want: 7},
// Binary AND
{name: "binary and", expr: "$FF&$0F", want: 0x0F},
{name: "binary and multiple", expr: "255&127&63", want: 63},
// Mixed operators
{name: "mixed math", expr: "10+20*2", want: 60}, // left-to-right: (10+20)*2
{name: "mixed bitwise", expr: "255&$F0|$0F", want: 0xFF},
// Constants
{name: "constant", expr: "MAXVAL", lookup: constLookup, want: 255},
{name: "constant add", expr: "BASE+OFFSET", lookup: constLookup, want: 110},
{name: "constant unknown", expr: "UNKNOWN", lookup: constLookup, wantErr: true},
{name: "constant no lookup", expr: "MAXVAL", wantErr: true},
// Spaces
{name: "spaces around ops", expr: "10 + 20", want: 30},
{name: "spaces everywhere", expr: " 100 - 20 ", want: 80},
// Errors
{name: "empty", expr: "", wantErr: true},
{name: "invalid hex", expr: "$ZZ", wantErr: true},
{name: "invalid binary", expr: "!22", wantErr: true},
{name: "division by zero", expr: "10/0", wantErr: true},
{name: "trailing operator", expr: "10+", wantErr: true},
{name: "leading operator", expr: "+10", wantErr: true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := EvaluateExpression(tt.expr, tt.lookup)
if (err != nil) != tt.wantErr {
t.Errorf("EvaluateExpression() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && got != tt.want {
t.Errorf("EvaluateExpression(%q) = %v, want %v", tt.expr, got, tt.want)
}
})
}
}