Forráskód Böngészése

add rm-gocheck.go script and eg templates

The following "rm-gocheck:"-prefixed commits were generated by
go run rm-gocheck.go --commit

Signed-off-by: Tibor Vass <tibor@docker.com>
Tibor Vass 5 éve
szülő
commit
8f64611c83
4 módosított fájl, 579 hozzáadás és 0 törlés
  1. 472 0
      rm-gocheck.go
  2. 35 0
      template.contains.go
  3. 37 0
      template.matches.go
  4. 35 0
      template.not_contains.go

+ 472 - 0
rm-gocheck.go

@@ -0,0 +1,472 @@
+// +build ignore
+
+package main
+
+import (
+	"bufio"
+	"bytes"
+	"errors"
+	"flag"
+	"fmt"
+	"go/format"
+	"io/ioutil"
+	"os"
+	"os/exec"
+	"path/filepath"
+	"regexp"
+	"strings"
+	"sync"
+)
+
+var (
+	shouldCommit = flag.Bool("commit", false, "if set, each step will result in a commit")
+	filter       = flag.String("filter", "", "only run on files matching filter")
+	titlePrefix  = flag.String("prefix", "rm-gocheck: ", "commit title prefix")
+	allFiles     []string
+	fileToCmp    = map[string]string{}
+	cmps         = map[string][]string{}
+)
+
+type action func(*step) string
+
+type step struct {
+	files []string
+	pkgs  map[string]string
+
+	title   string
+	pattern string
+	action  action
+	comment string
+}
+
+func mustSh(format string, args ...interface{}) (output []string) {
+	var err error
+	output, err = sh(format, args...)
+	if err != nil {
+		panic(err)
+	}
+	return
+}
+
+func sh(format string, args ...interface{}) (output []string, err error) {
+	cmdargs := fmt.Sprintf(format, args...)
+	out, err := exec.Command("sh", "-c", cmdargs).CombinedOutput()
+	if err != nil {
+		return nil, fmt.Errorf("cmd=%s\nout=%s\n", cmdargs, out)
+	}
+	l := strings.Split(string(out), "\n")
+	// remove last element if empty
+	if len(l[len(l)-1]) == 0 {
+		l = l[:len(l)-1]
+	}
+	return l, nil
+}
+
+func listToArgs(l []string) string {
+	s := fmt.Sprintf("%q", l)
+	s = s[1 : len(s)-1]
+	return s
+}
+
+func Replace(subst string) action {
+	return func(s *step) string {
+		return fmt.Sprintf("sed -E -i 's#%s#%s#g' \\\n-- %s", s.pattern, subst, listToArgs(s.files))
+	}
+}
+
+func CmpReplace(subst string) action {
+	return func(s *step) string {
+		var allCmdArgs, filesNeedingCmpImport []string
+		for _, file := range s.files {
+			cmp, ok := fileToCmp[file]
+			if !ok {
+				cmp = "cmp"
+				l := mustSh(`grep -m 1 -F '"gotest.tools/assert/cmp"' %s | awk '{print $1}'`, file)
+				if len(l) > 0 {
+					cmp = l[0]
+				} else {
+					filesNeedingCmpImport = append(filesNeedingCmpImport, file)
+				}
+				fileToCmp[file] = cmp
+				cmps[cmp] = append(cmps[cmp], file)
+			}
+		}
+
+		if len(filesNeedingCmpImport) > 0 {
+			linesep := " \\\n"
+			importCmd := fmt.Sprintf(`sed -E -i '0,/^import "github\.com/ s/^(import "github\.com.*)/\1\nimport "gotest.tools\/assert\/cmp")/'%s-- %s`, linesep, listToArgs(filesNeedingCmpImport))
+			allCmdArgs = append(allCmdArgs, importCmd)
+			importCmd = fmt.Sprintf(`sed -E -i '0,/^\t+"github\.com/ s/(^\t+"github\.com.*)/\1\n"gotest.tools\/assert\/cmp"/'%s-- %s`, linesep, listToArgs(filesNeedingCmpImport))
+			allCmdArgs = append(allCmdArgs, importCmd)
+		}
+
+		for cmp, files := range cmps {
+			cmdargs := fmt.Sprintf("sed -E -i 's#%s#%s#g' \\\n-- %s", s.pattern, strings.ReplaceAll(subst, "${cmp}", cmp), listToArgs(files))
+			allCmdArgs = append(allCmdArgs, cmdargs)
+		}
+		return strings.Join(allCmdArgs, " \\\n&& \\\n")
+	}
+}
+
+func redress(pattern string, files ...string) error {
+	rgx, err := regexp.Compile(pattern)
+	if err != nil {
+		return err
+	}
+	if len(files) == 0 {
+		return errors.New("no files provided")
+	}
+	fn := func(file string) error {
+		f, err := os.Open(file)
+		if err != nil {
+			return err
+		}
+		defer f.Close()
+
+		tmpName := file + ".tmp"
+		fixed, err := os.Create(tmpName)
+		if err != nil {
+			return err
+		}
+		defer fixed.Close()
+
+		const (
+			searching = iota
+			found
+			line_done
+		)
+		state := searching
+		s := bufio.NewScanner(f)
+		for s.Scan() {
+			b := s.Bytes()
+			if state != found {
+				bb := bytes.TrimRight(b, " \t")
+				if state == line_done && len(bb) == 0 {
+					continue
+				}
+				state = searching
+				if !rgx.Match(b) {
+					fixed.Write(b)
+					fixed.Write([]byte{'\n'})
+				} else {
+					fixed.Write(bb)
+					fixed.Write([]byte{' '})
+					state = found
+				}
+				continue
+			}
+			b = bytes.TrimRight(b, " \t")
+			fixed.Write(b)
+			if len(b) > 0 {
+				switch b[len(b)-1] {
+				case ',', '(':
+					fixed.Write([]byte{' '})
+					continue
+				case ')':
+					fixed.Write([]byte{'\n'})
+					state = line_done
+				}
+			}
+		}
+		if err := s.Err(); err != nil {
+			return err
+		}
+
+		fixed.Close()
+		f.Close()
+		src, err := ioutil.ReadFile(tmpName)
+		if err != nil {
+			return err
+		}
+		src, err = format.Source(src)
+		if err != nil {
+			return err
+		}
+		os.Remove(tmpName)
+		return ioutil.WriteFile(file, src, 0644)
+	}
+
+	var wg sync.WaitGroup
+	wg.Add(len(files))
+	for _, file := range files {
+		go func(file string) {
+			defer wg.Done()
+			if err := fn(file); err != nil {
+				panic(fmt.Sprintf("redress %s: %v", file, err))
+			}
+		}(file)
+	}
+	wg.Wait()
+	return nil
+}
+
+func Redress(s *step) string {
+	return fmt.Sprintf("go run rm-gocheck.go redress '%s' \\\n %s", s.pattern, listToArgs(s.files))
+}
+
+func Format(s *step) string {
+	pkgs := make([]string, 0, len(s.pkgs))
+	for dir := range s.pkgs {
+		pkgs = append(pkgs, "./"+dir)
+	}
+	files := listToArgs(pkgs)
+	return fmt.Sprintf("goimports -w \\\n-- %s \\\n&& \\\n gofmt -w -s \\\n-- %s", files, files)
+}
+
+func CommentInterface(s *step) string {
+	cmds := make([]string, 0, len(s.pkgs))
+	for dir := range s.pkgs {
+		cmd := fmt.Sprintf(`while :; do \
+	out=$(go test -c ./%s 2>&1 | grep 'cannot use nil as type string in return argument') || break
+	echo "$out" | while read line; do
+		file=$(echo "$line" | cut -d: -f1)
+		n=$(echo "$line" | cut -d: -f2)
+		sed -E -i "${n}"'s#\b(return .*, )nil#\1""#g' "$file"
+	done
+done`, dir)
+		cmds = append(cmds, cmd)
+	}
+	return strings.Join(cmds, " \\\n&& \\\n")
+}
+
+func Eg(template string, prehook action, helperTypes string) action {
+	return func(s *step) string {
+		cmds := make([]string, 0, 3+4*len(s.pkgs))
+
+		if prehook != nil {
+			cmds = append(cmds, prehook(s))
+		}
+
+		cmdstr := fmt.Sprintf(`go get -d golang.org/x/tools/cmd/eg && dir=$(go env GOPATH)/src/golang.org/x/tools && git -C "$dir" fetch https://github.com/tiborvass/tools handle-variadic && git -C "$dir" checkout 61a94b82347c29b3289e83190aa3dda74d47abbb && go install golang.org/x/tools/cmd/eg`)
+		cmds = append(cmds, cmdstr)
+
+		for dir, pkg := range s.pkgs {
+			cmds = append(cmds, fmt.Sprintf(`/bin/echo -e 'package %s\n%s' > ./%s/eg_helper.go`, pkg, helperTypes, dir))
+			cmds = append(cmds, fmt.Sprintf(`goimports -w ./%s`, dir))
+			cmds = append(cmds, fmt.Sprintf(`eg -w -t %s -- ./%s`, template, dir))
+			cmds = append(cmds, fmt.Sprintf(`rm -f ./%s/eg_helper.go`, dir))
+		}
+		cmds = append(cmds, fmt.Sprintf("go run rm-gocheck.go redress '%s' \\\n %s", `\bassert\.Assert\b.*(\(|,)\s*$`, listToArgs(s.files)))
+		return strings.Join(cmds, " \\\n&& \\\n")
+	}
+}
+
+func do(steps []step) {
+	fileArgs := listToArgs(allFiles)
+	for _, s := range steps {
+		fmt.Print(s.title, "... ")
+		s.files, _ = sh(`git grep --name-only -E '%s' -- %s`, s.pattern, fileArgs)
+		if len(s.files) == 0 {
+			fmt.Println("no files match")
+			continue
+		}
+		s.pkgs = map[string]string{}
+		pkg := ""
+		if len(s.files) > 0 {
+			x := mustSh(`grep -m1 '^package ' -- %s | cut -d' ' -f2`, s.files[0])
+			pkg = x[0]
+		}
+		for _, file := range s.files {
+			s.pkgs[filepath.Dir(file)] = pkg
+		}
+		cmdstr := s.action(&s)
+		mustSh(cmdstr)
+		if *shouldCommit {
+			if len(s.comment) > 0 {
+				s.comment = "\n\n" + s.comment
+			}
+			msg := fmt.Sprintf("%s%s\n\n%s%s", *titlePrefix, s.title, cmdstr, s.comment)
+			sh(`git add %s`, listToArgs(s.files))
+			cmd := exec.Command("git", "commit", "-s", "-F-")
+			cmd.Stdin = strings.NewReader(msg)
+			out, err := cmd.CombinedOutput()
+			if err != nil {
+				panic(string(out))
+			}
+			fmt.Println("committed")
+		} else {
+			fmt.Println("done")
+		}
+	}
+}
+
+func main() {
+	flag.Parse()
+
+	args := flag.Args()
+	if len(args) > 0 {
+		switch cmd := args[0]; cmd {
+		case "redress":
+			if len(args) < 3 {
+				panic(fmt.Sprintf("usage: %s [flags] redress <pattern> <files...>", os.Args[0]))
+			}
+			if err := redress(args[1], args[2:]...); err != nil {
+				panic(fmt.Sprintf("redress: %v", err))
+			}
+			return
+		default:
+			panic(fmt.Sprintf("unknown command %s", cmd))
+		}
+	}
+
+	allFiles, _ = sh(`git grep --name-only '"github.com/go-check/check"' :**.go | grep -vE '^(vendor/|integration-cli/checker|rm-gocheck\.go|template\..*\.go)' | grep -E '%s'`, *filter)
+	if len(allFiles) == 0 {
+		return
+	}
+
+	do([]step{
+		{
+			title:   "normalize c.Check to c.Assert",
+			pattern: `\bc\.Check\(`,
+			action:  Replace(`c.Assert(`),
+		},
+		{
+			title:   "redress multiline c.Assert calls",
+			pattern: `\bc\.Assert\b.*(,|\()\s*$`,
+			action:  Redress,
+		},
+		{
+			title:   "c.Assert(...) -> assert.Assert(c, ...)",
+			pattern: `\bc\.Assert\(`,
+			action:  Replace(`assert.Assert(c, `),
+		},
+		{
+			title:   "check.C -> testing.B for BenchmarkXXX",
+			pattern: `( Benchmark[^\(]+\([^ ]+ \*)check\.C\b`,
+			action:  Replace(`\1testing.B`),
+		},
+		{
+			title:   "check.C -> testing.T",
+			pattern: `\bcheck\.C\b`,
+			action:  Replace(`testing.T`),
+		},
+		{
+			title:   "ErrorMatches -> assert.ErrorContains",
+			pattern: `\bassert\.Assert\(c, (.*), check\.ErrorMatches,`,
+			action:  Replace(`assert.ErrorContains(c, \1,`),
+		},
+		{
+			title:   "normalize to use checker",
+			pattern: `\bcheck\.(Equals|DeepEquals|HasLen|IsNil|Matches|Not|NotNil)\b`,
+			action:  Replace(`checker.\1`),
+		},
+		{
+			title:   "Not(IsNil) -> != nil",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.Not\(checker\.IsNil\)`,
+			action:  Replace(`assert.Assert(c, \1 != nil`),
+		},
+		{
+			title:   "Not(Equals) -> a != b",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.Not\(checker\.Equals\), (.*)`,
+			action:  Replace(`assert.Assert(c, \1 != \2`),
+		},
+		{
+			title:   "Not(Matches) -> !cmp.Regexp",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.Not\(checker\.Matches\), (.*)\)`,
+			action:  CmpReplace(`assert.Assert(c, !${cmp}.Regexp("^"+\2+"$", \1)().Success())`),
+		},
+		{
+			title:   "Equals -> assert.Equal",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.Equals, (.*)`,
+			action:  Replace(`assert.Equal(c, \1, \2`),
+		},
+		{
+			title:   "DeepEquals -> assert.DeepEqual",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.DeepEquals, (.*)`,
+			action:  Replace(`assert.DeepEqual(c, \1, \2`),
+		},
+		{
+			title:   "HasLen -> assert.Equal + len()",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.HasLen, (.*)`,
+			action:  Replace(`assert.Equal(c, len(\1), \2`),
+		},
+		{
+			title:   "IsNil",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.IsNil\b`,
+			action:  Replace(`assert.Assert(c, \1 == nil`),
+		},
+		{
+			title:   "NotNil",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.NotNil\b`,
+			action:  Replace(`assert.Assert(c, \1 != nil`),
+		},
+		{
+			title:   "False",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.False\b`,
+			action:  Replace(`assert.Assert(c, !\1`),
+		},
+		{
+			title:   "True",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.True`,
+			action:  Replace(`assert.Assert(c, \1`),
+		},
+		{
+			title:   "redress check.Suite calls",
+			pattern: `[^/]\bcheck\.Suite\(.*\{\s*$`,
+			action:  Redress,
+		},
+		{
+			title:   "comment out check.Suite calls",
+			pattern: `^([^*])+?((var .*)?check\.Suite\(.*\))`,
+			action:  Replace(`\1/*\2*/`),
+		},
+		{
+			title:   "comment out check.TestingT",
+			pattern: `([^*])(check\.TestingT\([^\)]+\))`,
+			action:  Replace(`\1/*\2*/`),
+		},
+		{
+			title:  "run goimports to compile successfully",
+			action: Format,
+		},
+		{
+			title:   "Matches -> cmp.Regexp",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.Matches, (.*)\)$`,
+			action: Eg("template.matches.go",
+				CmpReplace(`assert.Assert(c, eg_matches(${cmp}.Regexp, \1, \2))`),
+				`var eg_matches func(func(cmp.RegexOrPattern, string) cmp.Comparison, interface{}, string, ...interface{}) bool`),
+		},
+		{
+			title:   "Not(Contains) -> !strings.Contains",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.Not\(checker\.Contains\), (.*)\)$`,
+			action: Eg("template.not_contains.go",
+				Replace(`assert.Assert(c, !eg_contains(\1, \2))`),
+				`var eg_contains func(arg1, arg2 string, extra ...interface{}) bool`),
+		},
+		{
+			title:   "Contains -> strings.Contains",
+			pattern: `\bassert\.Assert\(c, (.*), checker\.Contains, (.*)\)$`,
+			action: Eg("template.contains.go",
+				Replace(`assert.Assert(c, eg_contains(\1, \2))`),
+				`var eg_contains func(arg1, arg2 string, extra ...interface{}) bool`),
+		},
+		{
+			title:   "convert check.Commentf to string - with multiple args",
+			pattern: `\bcheck.Commentf\(([^,]+),(.*)\)`,
+			action:  Replace(`fmt.Sprintf(\1,\2)`),
+		},
+		{
+			title:   "convert check.Commentf to string - with just one string",
+			pattern: `\bcheck.Commentf\(("[^"]+")\)`,
+			action:  Replace(`\1`),
+		},
+		{
+			title:   "convert check.Commentf to string - other",
+			pattern: `\bcheck.Commentf\(([^\)]+)\)`,
+			action:  Replace(`\1`),
+		},
+		{
+			title:   "check.CommentInterface -> string",
+			pattern: `(\*testing\.T\b.*)check\.CommentInterface\b`,
+			action:  Replace(`\1string`),
+		},
+		{
+			title:  "goimports",
+			action: Format,
+		},
+		{
+			title:  "fix compile errors from converting check.CommentInterface to string",
+			action: CommentInterface,
+		},
+	})
+}

+ 35 - 0
template.contains.go

@@ -0,0 +1,35 @@
+// +build ignore
+
+package main
+
+import (
+	"strings"
+	"testing"
+
+	"gotest.tools/assert"
+)
+
+type fn func(arg1, arg2 string, extra ...interface{}) bool
+type assertfn func(t assert.TestingT, comparison assert.BoolOrComparison, msgAndArgs ...interface{})
+
+func before(
+	t *testing.T,
+	a assertfn,
+	eg_contains fn,
+	arg1 string,
+	arg2 string,
+	extra ...interface{}) {
+
+	a(t, eg_contains(arg1, arg2, extra...))
+}
+
+func after(
+	t *testing.T,
+	a assertfn,
+	eg_contains fn,
+	arg1 string,
+	arg2 string,
+	extra ...interface{}) {
+
+	a(t, strings.Contains(arg1, arg2), extra...)
+}

+ 37 - 0
template.matches.go

@@ -0,0 +1,37 @@
+// +build ignore
+
+package main
+
+import (
+	"testing"
+
+	"gotest.tools/assert"
+	"gotest.tools/assert/cmp"
+)
+
+type fn func(re func(cmp.RegexOrPattern, string) cmp.Comparison, r interface{}, v string, extra ...interface{}) bool
+type assertfn func(t assert.TestingT, comparison assert.BoolOrComparison, msgAndArgs ...interface{})
+
+func before(
+	t *testing.T,
+	a assertfn,
+	eg_matches fn,
+	re func(cmp.RegexOrPattern, string) cmp.Comparison,
+	r string,
+	v string,
+	extra ...interface{}) {
+
+	a(t, eg_matches(re, v, r, extra...))
+}
+
+func after(
+	t *testing.T,
+	a assertfn,
+	eg_matches fn,
+	re func(cmp.RegexOrPattern, string) cmp.Comparison,
+	r string,
+	v string,
+	extra ...interface{}) {
+
+	a(t, re("^"+r+"$", v), extra...)
+}

+ 35 - 0
template.not_contains.go

@@ -0,0 +1,35 @@
+// +build ignore
+
+package main
+
+import (
+	"strings"
+	"testing"
+
+	"gotest.tools/assert"
+)
+
+type fn func(arg1, arg2 string, extra ...interface{}) bool
+type assertfn func(t assert.TestingT, comparison assert.BoolOrComparison, msgAndArgs ...interface{})
+
+func before(
+	t *testing.T,
+	a assertfn,
+	eg_contains fn,
+	arg1 string,
+	arg2 string,
+	extra ...interface{}) {
+
+	a(t, !eg_contains(arg1, arg2, extra...))
+}
+
+func after(
+	t *testing.T,
+	a assertfn,
+	eg_contains fn,
+	arg1 string,
+	arg2 string,
+	extra ...interface{}) {
+
+	a(t, !strings.Contains(arg1, arg2), extra...)
+}