Declare scanner.scan's apart argument, so the console lets it through to the tool (issue 1528)
mesh/merge-gate head c4913dbbcb35 merged into trunk 9daeab209ba4 — pass: builds brother → shanks; no bus step; every machine composes with the change as it…
mesh/repo-check head c4913dbbcb35 merged into trunk 9daeab209ba4 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
mesh/merge-gate head c4913dbbcb35 merged into trunk 9daeab209ba4 — pass: builds brother → shanks; no bus step; every machine composes with the change as it…
mesh/repo-check head c4913dbbcb35 merged into trunk 9daeab209ba4 — pass: its merge-check.sh passed, each part in its toolchain: merge-check.sh (go), merge-…
mesh/delivery delivered
This commit is contained in:
@@ -0,0 +1,95 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"go/ast"
|
||||||
|
"go/parser"
|
||||||
|
"go/token"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 1528: a tool reads only arguments its input schema declares; the console refuses an undeclared
|
||||||
|
// one before it reaches the tool (scanner.scan read apart, which its schema never named).
|
||||||
|
func TestEveryArgumentAToolReadsIsDeclared(t *testing.T) {
|
||||||
|
fs := token.NewFileSet()
|
||||||
|
f, err := parser.ParseFile(fs, "main.go", nil, 0)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
checked := 0
|
||||||
|
ast.Inspect(f, func(n ast.Node) bool {
|
||||||
|
lit, ok := n.(*ast.CompositeLit)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
var name string
|
||||||
|
var input, run ast.Expr
|
||||||
|
for _, e := range lit.Elts {
|
||||||
|
kv, ok := e.(*ast.KeyValueExpr)
|
||||||
|
if !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch k, _ := kv.Key.(*ast.Ident); {
|
||||||
|
case k == nil:
|
||||||
|
case k.Name == "Name":
|
||||||
|
if b, ok := kv.Value.(*ast.BasicLit); ok {
|
||||||
|
name, _ = strconv.Unquote(b.Value)
|
||||||
|
}
|
||||||
|
case k.Name == "Input":
|
||||||
|
input = kv.Value
|
||||||
|
case k.Name == "Run":
|
||||||
|
run = kv.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if name == "" || input == nil || run == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
declared := map[string]bool{}
|
||||||
|
ast.Inspect(input, func(m ast.Node) bool {
|
||||||
|
kv, ok := m.(*ast.KeyValueExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if k, ok := kv.Key.(*ast.BasicLit); ok && k.Value == `"properties"` {
|
||||||
|
if props, ok := kv.Value.(*ast.CompositeLit); ok {
|
||||||
|
for _, p := range props.Elts {
|
||||||
|
if pkv, ok := p.(*ast.KeyValueExpr); ok {
|
||||||
|
if b, ok := pkv.Key.(*ast.BasicLit); ok {
|
||||||
|
s, _ := strconv.Unquote(b.Value)
|
||||||
|
declared[s] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
var missing []string
|
||||||
|
ast.Inspect(run, func(m ast.Node) bool {
|
||||||
|
ix, ok := m.(*ast.IndexExpr)
|
||||||
|
if !ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if id, ok := ix.X.(*ast.Ident); ok && id.Name == "args" {
|
||||||
|
if b, ok := ix.Index.(*ast.BasicLit); ok && b.Kind == token.STRING {
|
||||||
|
s, _ := strconv.Unquote(b.Value)
|
||||||
|
if !declared[s] {
|
||||||
|
missing = append(missing, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
checked++
|
||||||
|
if len(missing) > 0 {
|
||||||
|
sort.Strings(missing)
|
||||||
|
t.Errorf("%s reads %v, which its input schema does not declare", name, missing)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
})
|
||||||
|
if checked < 5 {
|
||||||
|
t.Fatalf("checked %d tools; the test no longer finds the tool literals", checked)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -277,6 +277,7 @@ func tools(settingsFile string, now func() time.Time) []stdio.Tool {
|
|||||||
"document": map[string]any{"type": "string", "description": "glass only, a document of several pages in one PDF: start opens one and scans its first page; the id of an open document scans the next page into it"},
|
"document": map[string]any{"type": "string", "description": "glass only, a document of several pages in one PDF: start opens one and scans its first page; the id of an open document scans the next page into it"},
|
||||||
"finish": map[string]any{"type": "boolean", "description": "with document: the id of an open document: close it into one PDF, filed once (nothing is scanned)"},
|
"finish": map[string]any{"type": "boolean", "description": "with document: the id of an open document: close it into one PDF, filed once (nothing is scanned)"},
|
||||||
"duplex": map[string]any{"type": "boolean", "description": "feeder only: scan both sides of each sheet (default false)"},
|
"duplex": map[string]any{"type": "boolean", "description": "feeder only: scan both sides of each sheet (default false)"},
|
||||||
|
"apart": map[string]any{"type": "boolean", "description": "glass only: several documents lying apart on the glass are each filed as their own PDF (default false: one scan of the glass)"},
|
||||||
}},
|
}},
|
||||||
Run: func(args map[string]any) (any, error) {
|
Run: func(args map[string]any) (any, error) {
|
||||||
s, err := service()
|
s, err := service()
|
||||||
|
|||||||
Reference in New Issue
Block a user