From d2975303094669ba4a00de2f3fbe098ae4b2eef6 Mon Sep 17 00:00:00 2001 From: Celestino Amoroso Date: Sat, 24 Feb 2024 11:38:21 +0100 Subject: [PATCH] Added test source files --- dict-set-context_test.go | 137 +++++++++++++++++++++++++++++++++++++++ expander-context_test.go | 2 + expander_test.go | 114 ++++++++++++++++++++++++++++++++ scanner_test.go | 120 ++++++++++++++++++++++++++++++++++ tty_test.go | 27 ++++++++ 5 files changed, 400 insertions(+) create mode 100644 dict-set-context_test.go create mode 100644 expander-context_test.go create mode 100644 expander_test.go create mode 100644 scanner_test.go create mode 100644 tty_test.go diff --git a/dict-set-context_test.go b/dict-set-context_test.go new file mode 100644 index 0000000..f299ba2 --- /dev/null +++ b/dict-set-context_test.go @@ -0,0 +1,137 @@ +// dict-set-context_test.go +package text + +import ( + "crypto/sha1" + "encoding/hex" + "errors" + "fmt" + "strings" + "testing" + "time" +) + +func TestNewDictSetContext(t *testing.T) { + type inputType struct { + ctx ExpanderContext + source string + wantResult string + wantErr error + } + + funcName := "scan" + + flags := DictSetFlag(0) + vars := map[string]string{"one": "1", "two": "2", "three": "3"} + ctx1 := NewDictSetContext(flags, vars) + ctx2 := NewDictSetContext(KeepVar, vars) + AddTimeFuncs(ctx2) + /* + err = fmt.Errorf("unsupported '%%%c' time specifier at %d", ch, i) + */ + now := time.Now() + yday := now.Add(-24*time.Hour) + inputs := []inputType{ + {ctx2, `form: ${|__time-fmt now ""}`, fmt.Sprintf("form: %d-%02d-%02d %02d:%02d:%02d", now.Year(), now.Month(), now.Day(), now.Hour(), now.Minute(), now.Second()), nil}, + {ctx2, `form: ${|__time-fmt now "%-D"}`, fmt.Sprintf("form: %d-%d-%d", now.Year(), now.Month(), now.Day()), nil}, + {ctx2, `form: ${|__time-fmt now "%:T"}`, fmt.Sprintf("form: %d:%d:%d", now.Hour(), now.Minute(), now.Second()), nil}, + {ctx2, `form: ${|__time-fmt now "%02:T"}`,"", errors.New("invalid time specifier format at 3")}, + {ctx2, `form: ${|__time-fmt ""}`,"", errors.New("time value is empty")}, + {ctx2, `form: ${|__time-fmt yesterday "%-D"}`, fmt.Sprintf("form: %d-%d-%d", yday.Year(), yday.Month(), yday.Day()), nil}, + {ctx1, "${one|pippo}", "1", nil}, + {ctx1, "${one|__default pluto}", "1", nil}, + {ctx1, "${six|__default pluto}", "pluto", nil}, + {ctx1, "${six|__default - pluto paperino|__lower|__upper}", "PLUTO-PAPERINO", nil}, + {ctx1, "${two|__join + 1 ${.} ${|__get three}}", "1+2+3", nil}, + {ctx1, "${=one|__get}", "1", nil}, + {ctx1, "${|__get unknown default}", "default", nil}, + {ctx1, "${one|__set A ${.}}", "1", nil}, + {ctx1, "${one|__set B }", "1", nil}, + {ctx1, "${one|__set}", "1", nil}, + {ctx1, "${=archive.tar.gz|__trim-suffix}", "archive.tar", nil}, + {ctx1, "${=prog.exe|__trim-suffix .zip .png .exe .com}", "prog", nil}, + {ctx1, "${=prog.exe|__trim-suffix .zip .png .jpeg .com}", "prog.exe", nil}, + {ctx1, "${=ciccio}", "ciccio", nil}, + {ctx1, "${=ciccio{}", "", errors.New("unbalanced open braces at offset 9")}, + {ctx1, "${|__get two}", "2", nil}, + {ctx1, "${one} + ${two} = ${three}", "1 + 2 = 3", nil}, + {ctx1, "${one} + ${two} = ${four}", "1 + 2 = ", nil}, + {ctx2, "${one} + ${two} = ${four}", "1 + 2 = ${four}", nil}, + {ctx2, `now: ${|__time-fmt now "%:02T"}`, fmt.Sprintf("now: %02d:%02d:%02d", now.Hour(), now.Minute(), now.Second()), nil}, + {ctx2, `hour: ${|__time-fmt now "%02H"}`, fmt.Sprintf("hour: %02d", now.Hour()), nil}, + {ctx2, `minute: ${|__time-fmt now "%02M"}`, fmt.Sprintf("minute: %02d", now.Minute()), nil}, + {ctx2, `second: ${|__time-fmt now "%02S" }`, fmt.Sprintf("second: %02d", now.Second()), nil}, + {ctx2, `today: ${|__time-fmt now "%-02D"}`, fmt.Sprintf("today: %d-%02d-%02d", now.Year(), now.Month(), now.Day()), nil}, + {ctx2, `year: ${|__time-fmt now "%Y"}`, fmt.Sprintf("year: %d", now.Year()), nil}, + {ctx2, `month: ${|__time-fmt now "%m"}`, fmt.Sprintf("month: %02d", now.Month()), nil}, + {ctx2, `weird: ${|__time-fmt now "%c"}`, "", errors.New(`unsupported 'c' time specifier at "%c"/2`)}, + {ctx2, `perc: ${|__time-fmt now "%%"}`, "perc: %", nil}, + {ctx1, `unknown var ${!unknown}`, ``, errors.New(`the variable specification "unknown" requires a value`)}, + {ctx1, `${hello-word|}!`, `!`, nil}, + {ctx1, `${hello-word|ciao}!`, `ciao!`, nil}, + } + + for i, input := range inputs { + gotResult, gotErr := ExpandStringTemplate(input.ctx, input.source) + + if gotResult != input.wantResult { + t.Errorf("%d: %s(%q)/result = %q, want %q", i, funcName, input.source, gotResult, input.wantResult) + } + if gotErr == nil && input.wantErr == nil { + continue + } + if (gotErr != nil && input.wantErr == nil) || (gotErr == nil && input.wantErr != nil) || (gotErr.Error() != input.wantErr.Error()) { + t.Errorf("%d: %s(%q)/err = <%v>, want <%v>", i, funcName, input.source, gotErr, input.wantErr) + } + + } +} + +func TestCheckNumArgs(t *testing.T) { + type inputType struct { + info ExpanderFuncBlock + n int // number of args + want bool + } + funcName := "CheckNumArgs" + + inputs := []inputType{ + {ExpanderFuncBlock{"", nil, -1, -1, ""}, 0, true}, + {ExpanderFuncBlock{"", nil, 0, -1, ""}, 10, true}, + {ExpanderFuncBlock{"", nil, 2, 4, ""}, 1, false}, + } + for i, input := range inputs { + got := input.info.CheckNumArgs(input.n) + + if got != input.want { + t.Errorf("%d: %s(%d)/result = %v, want %v", i, funcName, input.n, got, input.want) + } + } +} + +func TestIterateFunc(t *testing.T) { + var sb strings.Builder + + funcName := "IterateFunc" + + want := "f141c5fa802ed509c08ec057eea5ba0086a84b21" + vars := map[string]string{"one": "1", "two": "2", "three": "3"} + ctx := NewDictSetContext(KeepVar, vars) + ctx.AddFunc("dumm1", nil, 1, 2, "bla-bla") + ctx.AddFunc("dumm2", nil, 0, 5, strings.Repeat(".", 81)) + ctx.AddFunc("dumm3", nil, -1, -1, "bla-bla") + ctx.AddFunc("dumm4", nil, 2, -1, "bla-bla") + + ctx.IterateFunc(func(info *ExpanderFuncBlock) error { + sb.WriteString(info.String()) + return nil + }) + hasher := sha1.New() + hasher.Write([]byte(sb.String())) + got := hex.EncodeToString(hasher.Sum(nil)) + //fmt.Println("sha:", got) + //fmt.Println(sb.String()) + if got != want { + t.Errorf("%s()/result = %q, want %q", funcName, got, want) + } +} diff --git a/expander-context_test.go b/expander-context_test.go new file mode 100644 index 0000000..c35df66 --- /dev/null +++ b/expander-context_test.go @@ -0,0 +1,2 @@ +// expander-context_test.go +package text diff --git a/expander_test.go b/expander_test.go new file mode 100644 index 0000000..dcbf15e --- /dev/null +++ b/expander_test.go @@ -0,0 +1,114 @@ +// scanner_test.go +package text + +import ( + "errors" + "fmt" + "strconv" + "testing" +) + +type ctxExpType struct { + BaseExpander + funcs map[string]ExpanderFunc +} + +func newCtxExp() *ctxExpType { + return &ctxExpType{funcs: make(map[string]ExpanderFunc)} +} + +func (ctx *ctxExpType) Handle(spec string, flags ScannerFlag) (value string, err error) { + var f ExpanderFunc + + //fmt.Println("Spec:", spec) + switch spec { + case "value": + value = "right-value" + case "|ciao": + value = "ciao" + case `|__sum(1,2,3)`: + if f, err = ctx.GetFunc("__sum"); err == nil { + value, err = f(ctx, "", []string{"1", "2", "3"}) + } + case `|__cat(1,"2",3)`: + if f, err = ctx.GetFunc("__cat"); err == nil { + value, err = f(ctx, "", []string{"1", "2", "3"}) + } + case `uno|due`: + value = "uno" + case "|__xyz()": + err = errors.New("unknown function __xyz") + default: + value = "" + } + return +} +func (ctx *ctxExpType) GetFunc(name string) (f ExpanderFunc, err error) { + var ok bool + if f, ok = ctx.funcs[name]; !ok { + err = fmt.Errorf("function %s not found", name) + } + return +} + +func (ctx *ctxExpType) AddFunc(name string, f ExpanderFunc, minArgs, maxArgs int, description string) { + ctx.funcs[name] = f +} + +func sum(ctx ExpanderContext, varValue string, args []string) (result string, err error) { + if len(varValue) > 0 { + result = varValue + } else { + var v int + s := 0 + for _, arg := range args { + if v, err = strconv.Atoi(arg); err != nil { + return + } else { + s += v + } + } + result = strconv.Itoa(s) + } + return +} + +func TestExpandStringTemplate(t *testing.T) { + type inputType struct { + ctx ExpanderContext + source string + wantResult string + wantErr error + } + + funcName := "scan" + ctx := newCtxExp() + ctx.AddFunc("__sum", sum, 1, 1, "fake function") + inputs := []inputType{ + {ctx, "this is the ${value}!", "this is the right-value!", nil}, + {ctx, `pay \$31`, `pay $31`, nil}, + {ctx, `\\regex\\`, `\regex\`, nil}, + {ctx, `fake \x.`, `fake x.`, nil}, + {ctx, ">${|ciao}<", ">ciao<", nil}, + {ctx, `>${|__sum(1,2,3)}<`, ">6<", nil}, + {ctx, `>${|__cat(1,"2",3)}<`, "", errors.New("function __cat not found")}, + {ctx, `>${|__cat(1,"2",3)<`, "", errors.New("unbalanced open braces at offset 2")}, + {ctx, `>${|__xyz()}<`, "", errors.New("unknown function __xyz")}, + {ctx, `>${uno|due}<`, ">uno<", nil}, + } + + for i, input := range inputs { + gotResult, gotErr := ExpandStringTemplate(input.ctx, input.source) + + if gotResult != input.wantResult { + t.Errorf("%d: %s(%q)/result = %q, want %q", i, funcName, input.source, gotResult, input.wantResult) + } + + if gotErr == nil && input.wantErr == nil { + continue + } + if (gotErr != nil && input.wantErr == nil) || (gotErr == nil && input.wantErr != nil) || (gotErr.Error() != input.wantErr.Error()) { + t.Errorf("%d: %s(%q)/err = %v, want %v", i, funcName, input.source, gotErr, input.wantErr) + } + } +} diff --git a/scanner_test.go b/scanner_test.go new file mode 100644 index 0000000..45dc245 --- /dev/null +++ b/scanner_test.go @@ -0,0 +1,120 @@ +// scanner_test.go +package text + +import ( + "errors" + "testing" +) + +func TestGetVarSpec(t *testing.T) { + type inputGetVarSpec struct { + source string + offset int + wantSpec string + wantFlags ScannerFlag + wantOffset int + wantErr error + } + + funcName := "getVarSpec" + + inputs := []inputGetVarSpec{ + {"Hello ${name}.", 7, "name", ScannerFlag(0), 13, nil}, + {"one ${!varspec}", 5, "varspec", ValueRequired, 15, nil}, + {"one ${*varspec}", 5, "varspec", EncryptValue, 15, nil}, + {"one ${*!varspec}", 5, "varspec", ValueRequired | EncryptValue, 16, nil}, + {"one ${*!var{spec}", 5, "", ValueRequired | EncryptValue, 17, errors.New("unbalanced open braces at offset 11")}, + {"one ${two}${three}", 11, "three", ScannerFlag(0), 18, nil}, + {"one $2${three}", 5, "2", ScannerFlag(0), 6, nil}, + {"one $!2${three}", 5, "", ScannerFlag(0), 5, nil}, + {"one $2{3}${three}", 5, "2", ScannerFlag(0), 6, nil}, + {"one ${{2}}{3}${three}", 5, "{2}", ScannerFlag(0), 10, nil}, + {">${|ciao}<", 2, "|ciao", ScannerFlag(0), 9, nil}, + {">${|{ciao}<", 2, "", ScannerFlag(0), 11, errors.New("unbalanced open braces at offset 4")}, + } + + for i, input := range inputs { + gotSpec, gotFlags, gotOffset, gotErr := getVarSpec(input.source, input.offset) + + if gotSpec != input.wantSpec { + t.Errorf("%d: %s(%q, %d)/spec = %q, want %q", i, funcName, input.source, input.offset, gotSpec, input.wantSpec) + } + + if gotFlags != input.wantFlags { + t.Errorf("%d: %s(%q, %d)/flags = %d, want %d", i, funcName, input.source, input.offset, gotFlags, input.wantFlags) + } + + if gotOffset != input.wantOffset { + t.Errorf("%d: %s(%q, %d)/offset = %d, want %d", i, funcName, input.source, input.offset, gotOffset, input.wantOffset) + } + + if gotErr == nil && input.wantErr == nil { + continue + } + if (gotErr != nil && input.wantErr == nil) || (gotErr == nil && input.wantErr != nil) || (gotErr.Error() != input.wantErr.Error()) { + t.Errorf("%d: %s(%q, %d)/err = %v, want %v", i, funcName, input.source, input.offset, gotErr, input.wantErr) + } + } +} + +type ctxType struct{} + +func (ctx *ctxType) Handle(spec string, flags ScannerFlag) (value string, err error) { + //fmt.Println("Spec:", spec) + switch spec { + case "value": + value = "right-value" + case "|ciao": + value = "ciao" + case `|__sum(1,2,3)`: + value = "6" + case `|__cat(1,"2",3)`: + value = "123" + case `uno|due`: + value = "uno" + case "|__xyz()": + err = errors.New("unknown function __xyz") + default: + value = "" + } + return +} + +func TestScan(t *testing.T) { + type inputType struct { + ctx VariableHandler + source string + wantResult string + wantErr error + } + + funcName := "scan" + + inputs := []inputType{ + {&ctxType{}, "this is the ${value}!", "this is the right-value!", nil}, + {&ctxType{}, `pay \$31`, `pay $31`, nil}, + {&ctxType{}, `\\regex\\`, `\regex\`, nil}, + {&ctxType{}, `fake \x.`, `fake x.`, nil}, + {&ctxType{}, ">${|ciao}<", ">ciao<", nil}, + {&ctxType{}, `>${|__sum(1,2,3)}<`, ">6<", nil}, + {&ctxType{}, `>${|__cat(1,"2",3)}<`, ">123<", nil}, + {&ctxType{}, `>${|__cat(1,"2",3)<`, "", errors.New("unbalanced open braces at offset 2")}, + {&ctxType{}, `>${|__xyz()}<`, "", errors.New("unknown function __xyz")}, + {&ctxType{}, `>${uno|due}<`, ">uno<", nil}, + } + + for i, input := range inputs { + gotResult, gotErr := Scan(input.ctx, input.source) + + if gotResult != input.wantResult { + t.Errorf("%d: %s(%q)/result = %q, want %q", i, funcName, input.source, gotResult, input.wantResult) + } + + if gotErr == nil && input.wantErr == nil { + continue + } + if (gotErr != nil && input.wantErr == nil) || (gotErr == nil && input.wantErr != nil) || (gotErr.Error() != input.wantErr.Error()) { + t.Errorf("%d: %s(%q)/err = %v, want %v", i, funcName, input.source, gotErr, input.wantErr) + } + } +} diff --git a/tty_test.go b/tty_test.go new file mode 100644 index 0000000..2e67892 --- /dev/null +++ b/tty_test.go @@ -0,0 +1,27 @@ +// expander-context_test.go +package text + +import ( + "fmt" + "os" + "testing" +) + +func TestSprintf(t *testing.T) { + list := []string{ + "ciao #{c(red);i}Mario#. #{u;b}Come# #{GREEN;b}Stai#{.}?", + "ciao #{c(red)i-Mario}. #{ub-Come} #{GREEN;b-Stai#}?", + } + //s := Sprintf("ciao #{fg(139);i}Mario#. #{u;b}Come# #{GREEN;b}%s#{.}?", "Stai") + fmt.Println("--- Sprintf() ---") + for i, s := range list { + x := Sprintf(s) + fmt.Printf("--- Test nr %d: %q\n", i+1, s) + fmt.Println(x) + } + fmt.Println("\n--- Sprintf() ---") + for i, s := range list { + fmt.Printf("--- Test nr %d: %q\n", i+1, s) + Fprintf(os.Stdout, s) + } +}