272 lines
6.2 KiB
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)
|
|
}
|
|
})
|
|
}
|
|
}
|