Test internal/datafiles, internal/datatype, internal/proven and internal/builtinfunc, and allow the root to import them
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
package builtinfunc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/larvit/fejkdata/internal/grammar"
|
||||
)
|
||||
|
||||
// TestRegistryShapes pins the builtin contract Prep relies on: every entry supplies prep,
|
||||
// and args parsed at compile only behind a check.
|
||||
func TestRegistryShapes(t *testing.T) {
|
||||
for name, b := range builtins {
|
||||
if b.prep == nil {
|
||||
t.Errorf("builtin %q has no prep: Prep would call a nil func", name)
|
||||
}
|
||||
if b.arity != 0 && b.checkArgs == nil {
|
||||
t.Errorf("builtin %q parses args in prep with no check", name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoRefOperandsMatchesTheOperandGrammar(t *testing.T) {
|
||||
for name, b := range builtins {
|
||||
if b.operands == nil {
|
||||
continue
|
||||
}
|
||||
if takesRef := slices.ContainsFunc(b.operands([]string{"/x"}), grammar.IsRef); b.noRefOperands == takesRef {
|
||||
t.Errorf("%s: noRefOperands = %v, but its operands func reads a reference: %v", name, b.noRefOperands, takesRef)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCheckNamesWhatACallGetsWrong(t *testing.T) {
|
||||
for _, c := range []struct {
|
||||
name string
|
||||
args []string
|
||||
want string
|
||||
}{
|
||||
{"nope", nil, `unknown function "nope"`},
|
||||
{"luhn", []string{"1"}, "luhn takes 0 arguments, got 1"},
|
||||
{"hex", nil, "hex takes 1 argument, got 0"},
|
||||
{"calc", []string{"a +"}, `calc("a +"): `},
|
||||
{"calc", []string{"a", "-1"}, "calc decimals "},
|
||||
{"calc", nil, "calc takes an expression and an optional decimals count, got 0 args"},
|
||||
} {
|
||||
if err := Check(c.name, c.args); err == nil || !strings.HasPrefix(err.Error(), c.want) {
|
||||
t.Errorf("Check(%s, %q) = %v, want it to start %q", c.name, c.args, err, c.want)
|
||||
}
|
||||
}
|
||||
if err := Check("calc", []string{"a / b", "2"}); err != nil {
|
||||
t.Errorf("Check(calc) = %v, want an expression over any operand accepted", err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestArgGuardsPanic pins the guards that report a builtin arg its check should have
|
||||
// rejected. No data reaches them, since Check runs before Prep, so they are called directly.
|
||||
func TestArgGuardsPanic(t *testing.T) {
|
||||
for name, call := range map[string]func(){
|
||||
"atoi on an unvalidated arg": func() { atoi("nope") },
|
||||
"atof on an unvalidated arg": func() { atof("nope") },
|
||||
"prep on an expression that won't parse": func() { calcPrep([]string{"1 +"}) },
|
||||
} {
|
||||
mustPanic(t, name, call)
|
||||
}
|
||||
}
|
||||
|
||||
// mustPanic fails unless call panics with internalError's phrase, which is what
|
||||
// separates a reported invariant break from a silently wrong value.
|
||||
func mustPanic(t *testing.T, name string, call func()) {
|
||||
t.Helper()
|
||||
defer func() {
|
||||
if r := recover(); r == nil || !strings.HasPrefix(fmt.Sprint(r), "fejkdata: internal error: ") {
|
||||
t.Errorf("%s: recovered %v, want the invariant reported as an internal error", name, r)
|
||||
}
|
||||
}()
|
||||
call()
|
||||
}
|
||||
Reference in New Issue
Block a user