mirror of
https://github.com/zyedidia/micro.git
synced 2026-03-25 18:07:07 +09:00
Add buffer test and benchmark suite (and tool to generate it)
This commit is contained in:
267
tools/testgen.go
Normal file
267
tools/testgen.go
Normal file
@@ -0,0 +1,267 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/robertkrimen/otto/ast"
|
||||
"github.com/robertkrimen/otto/parser"
|
||||
)
|
||||
|
||||
type walker struct {
|
||||
nodes []ast.Node
|
||||
}
|
||||
|
||||
func (w *walker) Enter(node ast.Node) ast.Visitor {
|
||||
w.nodes = append(w.nodes, node)
|
||||
return w
|
||||
}
|
||||
|
||||
func (w *walker) Exit(node ast.Node) {
|
||||
}
|
||||
|
||||
func getAllNodes(node ast.Node) []ast.Node {
|
||||
w := &walker{}
|
||||
ast.Walk(w, node)
|
||||
return w.nodes
|
||||
}
|
||||
|
||||
func getCalls(node ast.Node, name string) []*ast.CallExpression {
|
||||
nodes := []*ast.CallExpression{}
|
||||
for _, n := range getAllNodes(node) {
|
||||
if ce, ok := n.(*ast.CallExpression); ok {
|
||||
var calleeName string
|
||||
switch callee := ce.Callee.(type) {
|
||||
case *ast.Identifier:
|
||||
calleeName = callee.Name
|
||||
case *ast.DotExpression:
|
||||
calleeName = callee.Identifier.Name
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if calleeName == name {
|
||||
nodes = append(nodes, ce)
|
||||
}
|
||||
}
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
func getPropertyValue(node ast.Node, key string) ast.Expression {
|
||||
for _, p := range node.(*ast.ObjectLiteral).Value {
|
||||
if p.Key == key {
|
||||
return p.Value
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type operation struct {
|
||||
startLine int
|
||||
startColumn int
|
||||
endLine int
|
||||
endColumn int
|
||||
text []string
|
||||
}
|
||||
|
||||
type check struct {
|
||||
before []string
|
||||
operations []operation
|
||||
after []string
|
||||
}
|
||||
|
||||
type test struct {
|
||||
description string
|
||||
checks []check
|
||||
}
|
||||
|
||||
func stringSliceToGoSource(slice []string) string {
|
||||
var b strings.Builder
|
||||
b.WriteString("[]string{\n")
|
||||
for _, s := range slice {
|
||||
b.WriteString(fmt.Sprintf("%#v,\n", s))
|
||||
}
|
||||
b.WriteString("}")
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func testToGoTest(test test, name string) string {
|
||||
var b strings.Builder
|
||||
|
||||
b.WriteString("func Test")
|
||||
b.WriteString(name)
|
||||
b.WriteString("(t *testing.T) {\n")
|
||||
|
||||
for _, c := range test.checks {
|
||||
b.WriteString("check(\n")
|
||||
b.WriteString("t,\n")
|
||||
b.WriteString(fmt.Sprintf("%v,\n", stringSliceToGoSource(c.before)))
|
||||
b.WriteString("[]operation{\n")
|
||||
for _, op := range c.operations {
|
||||
b.WriteString("operation{\n")
|
||||
b.WriteString(fmt.Sprintf("start: Loc{%v, %v},\n", op.startColumn, op.startLine))
|
||||
b.WriteString(fmt.Sprintf("end: Loc{%v, %v},\n", op.endColumn, op.endLine))
|
||||
b.WriteString(fmt.Sprintf("text: %v,\n", stringSliceToGoSource(op.text)))
|
||||
b.WriteString("},\n")
|
||||
}
|
||||
b.WriteString("},\n")
|
||||
b.WriteString(fmt.Sprintf("%v,\n", stringSliceToGoSource(c.after)))
|
||||
b.WriteString(")\n")
|
||||
}
|
||||
|
||||
b.WriteString("}\n")
|
||||
|
||||
return b.String()
|
||||
}
|
||||
|
||||
func nodeToStringSlice(node ast.Node) []string {
|
||||
var result []string
|
||||
for _, s := range node.(*ast.ArrayLiteral).Value {
|
||||
result = append(result, s.(*ast.StringLiteral).Value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func nodeToStringSlice2(node ast.Node) []string {
|
||||
var result []string
|
||||
for _, o := range node.(*ast.ArrayLiteral).Value {
|
||||
result = append(result, getPropertyValue(o, "text").(*ast.StringLiteral).Value)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func nodeToInt(node ast.Node) int {
|
||||
return int(node.(*ast.NumberLiteral).Value.(int64))
|
||||
}
|
||||
|
||||
func getChecks(node ast.Node) []check {
|
||||
checks := []check{}
|
||||
|
||||
for _, ce := range getCalls(node, "testApplyEdits") {
|
||||
if len(ce.ArgumentList) != 3 {
|
||||
// Wrong function
|
||||
continue
|
||||
}
|
||||
|
||||
before := nodeToStringSlice2(ce.ArgumentList[0])
|
||||
after := nodeToStringSlice2(ce.ArgumentList[2])
|
||||
|
||||
var operations []operation
|
||||
for _, op := range ce.ArgumentList[1].(*ast.ArrayLiteral).Value {
|
||||
args := getPropertyValue(op, "range").(*ast.NewExpression).ArgumentList
|
||||
operations = append(operations, operation{
|
||||
startLine: nodeToInt(args[0]) - 1,
|
||||
startColumn: nodeToInt(args[1]) - 1,
|
||||
endLine: nodeToInt(args[2]) - 1,
|
||||
endColumn: nodeToInt(args[3]) - 1,
|
||||
text: []string{getPropertyValue(op, "text").(*ast.StringLiteral).Value},
|
||||
})
|
||||
}
|
||||
|
||||
checks = append(checks, check{before, operations, after})
|
||||
}
|
||||
|
||||
for _, ce := range getCalls(node, "testApplyEditsWithSyncedModels") {
|
||||
if len(ce.ArgumentList) > 3 && ce.ArgumentList[3].(*ast.BooleanLiteral).Value {
|
||||
// inputEditsAreInvalid == true
|
||||
continue
|
||||
}
|
||||
|
||||
before := nodeToStringSlice(ce.ArgumentList[0])
|
||||
after := nodeToStringSlice(ce.ArgumentList[2])
|
||||
|
||||
var operations []operation
|
||||
for _, op := range getCalls(ce.ArgumentList[1], "editOp") {
|
||||
operations = append(operations, operation{
|
||||
startLine: nodeToInt(op.ArgumentList[0]) - 1,
|
||||
startColumn: nodeToInt(op.ArgumentList[1]) - 1,
|
||||
endLine: nodeToInt(op.ArgumentList[2]) - 1,
|
||||
endColumn: nodeToInt(op.ArgumentList[3]) - 1,
|
||||
text: nodeToStringSlice(op.ArgumentList[4]),
|
||||
})
|
||||
}
|
||||
|
||||
checks = append(checks, check{before, operations, after})
|
||||
}
|
||||
|
||||
return checks
|
||||
}
|
||||
|
||||
func getTests(node ast.Node) []test {
|
||||
tests := []test{}
|
||||
for _, ce := range getCalls(node, "test") {
|
||||
description := ce.ArgumentList[0].(*ast.StringLiteral).Value
|
||||
body := ce.ArgumentList[1].(*ast.FunctionLiteral).Body
|
||||
checks := getChecks(body)
|
||||
if len(checks) > 0 {
|
||||
tests = append(tests, test{description, checks})
|
||||
}
|
||||
}
|
||||
return tests
|
||||
}
|
||||
|
||||
func main() {
|
||||
var tests []test
|
||||
|
||||
for _, filename := range os.Args[1:] {
|
||||
source, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
program, err := parser.ParseFile(nil, "", source, parser.IgnoreRegExpErrors)
|
||||
if err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
tests = append(tests, getTests(program)...)
|
||||
}
|
||||
|
||||
if len(tests) == 0 {
|
||||
log.Fatalln("no tests found!")
|
||||
}
|
||||
|
||||
fmt.Println("// This file is generated from VSCode model tests by the testgen tool.")
|
||||
fmt.Println("// DO NOT EDIT THIS FILE BY HAND; your changes will be overwritten!\n")
|
||||
fmt.Println("package buffer")
|
||||
fmt.Println(`import "testing"`)
|
||||
|
||||
re := regexp.MustCompile(`[^\w]`)
|
||||
usedNames := map[string]bool{}
|
||||
|
||||
var b strings.Builder
|
||||
|
||||
for _, test := range tests {
|
||||
name := strings.Title(strings.ToLower(test.description))
|
||||
name = re.ReplaceAllLiteralString(name, "")
|
||||
if name == "" {
|
||||
name = "Unnamed"
|
||||
}
|
||||
if usedNames[name] {
|
||||
for i := 2; ; i++ {
|
||||
newName := fmt.Sprintf("%v_%v", name, i)
|
||||
if !usedNames[newName] {
|
||||
name = newName
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
usedNames[name] = true
|
||||
|
||||
fmt.Println(testToGoTest(test, name))
|
||||
|
||||
b.WriteString("Test")
|
||||
b.WriteString(name)
|
||||
b.WriteString("(nil)\n")
|
||||
}
|
||||
|
||||
fmt.Println("func BenchmarkBuffer(b *testing.B) {")
|
||||
fmt.Println("for i := 0; i < b.N; i++ {")
|
||||
fmt.Print(b.String())
|
||||
fmt.Println("}")
|
||||
fmt.Println("}")
|
||||
}
|
||||
Reference in New Issue
Block a user