mirror of
https://github.com/zyedidia/micro
synced 2024-11-05 17:41:24 +00:00
255 lines
5.9 KiB
Go
255 lines
5.9 KiB
Go
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{}
|
|
|
|
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))
|
|
}
|
|
}
|