Skip to content

Commit

Permalink
Add unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
momotaro98 committed May 30, 2020
1 parent c0e5c31 commit 1d2fc04
Show file tree
Hide file tree
Showing 7 changed files with 248 additions and 93 deletions.
36 changes: 34 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,43 @@
## Install

```
$ go get -u github.com/momotaro98/strictimportsort
$ go get -u github.com/momotaro98/strictimportsort/cmd/strictimportsort
```

## Usage

```shell script
$ strictimportsort -exclude "*_mock.go,*.pb.go" -exclude-dir "testmock" -local "github.com/momotaro98/my-project" $HOME/.ghq/github.com/momotaro98/my-project
$ strictimportsort -exclude "*_mock.go,*.pb.go" -exclude-dir "testmock" -local "github.com/momotaro98/mixlunch-service-api" $HOME/.ghq/github.com/momotaro98/mixlunch-service-api
```

You'll see instructions like below.

```
/Users/shintaro/.ghq/github.com/momotaro98/mixlunch-service-api/partyservice/domain_test.go:8:2: import not sorted correctly. should be replace to
import (
"errors"
"testing"
"time"
"github.com/golang/mock/gomock"
"github.com/momotaro98/mixlunch-service-api/userservice"
"github.com/momotaro98/mixlunch-service-api/utils"
)
/Users/shintaro/.ghq/github.com/momotaro98/mixlunch-service-api/userservice/provider.go:5:2: import not sorted correctly. should be replace to
import (
"github.com/google/wire"
"github.com/momotaro98/mixlunch-service-api/tagservice"
)
/Users/shintaro/.ghq/github.com/momotaro98/mixlunch-service-api/wire.go:8:2: import not sorted correctly. should be replace to
import (
"github.com/google/wire"
"github.com/momotaro98/mixlunch-service-api/logger"
"github.com/momotaro98/mixlunch-service-api/partyservice"
"github.com/momotaro98/mixlunch-service-api/tagservice"
usService "github.com/momotaro98/mixlunch-service-api/userscheduleservice"
"github.com/momotaro98/mixlunch-service-api/userservice"
)
```
97 changes: 97 additions & 0 deletions cmd/strictimportsort/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
package main

import (
"flag"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/minio/minio/pkg/wildcard"

"github.com/momotaro98/strictimportsort"
)

const (
invalidArgumentExitCode = 3
)

var (
localPaths = flag.String("local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list")
excludes = flag.String("exclude", "", "file names you wanna exclude; wile card is welcome; comma-separated list")
excludeDirs = flag.String("exclude-dir", "", "directory names you wanna exclude; wile card is welcome; comma-separated list")
dontRecurseFlag = flag.Bool("n", false, "don't recursively check paths")
)

func main() {
flag.Parse()

if len(flag.Args()) == 0 {
fmt.Println("missing argument: filepath")
os.Exit(invalidArgumentExitCode)
}

var lintFailed bool
for _, path := range flag.Args() {
rootPath, err := filepath.Abs(path)
if err != nil {
fmt.Printf("Error finding absolute path: %s", err)
os.Exit(invalidArgumentExitCode)
}
if walk(rootPath) {
lintFailed = true
}
}

if lintFailed {
os.Exit(1)
}
}

func walk(rootPath string) bool {
var lintFailed bool
filepath.Walk(rootPath, func(filePath string, fi os.FileInfo, err error) error {
if err != nil {
fmt.Printf("Error during filesystem walk: %v\n", err)
return nil
}
if fi.IsDir() {
if *excludeDirs != "" {
patternDirs := strings.Split(*excludeDirs, ",")
p := strings.Split(filePath, "/")
dirName := p[len(p)-1]
for _, pattern := range patternDirs {
if wildcard.MatchSimple(pattern, dirName) {
return filepath.SkipDir
}
}
}
if filePath != rootPath && (*dontRecurseFlag ||
filepath.Base(filePath) == "testdata" ||
filepath.Base(filePath) == "vendor") {
return filepath.SkipDir
}
return nil
}
if *excludes != "" {
patternFiles := strings.Split(*excludes, ",")
p := strings.Split(filePath, "/")
fileName := p[len(p)-1]
for _, pattern := range patternFiles {
if wildcard.MatchSimple(pattern, fileName) {
return nil
}
}
}
if !strings.HasSuffix(filePath, ".go") {
return nil
}
fset, poses, correctImport := strictimportsort.Run(filePath, *localPaths)
for _, pos := range poses {
fmt.Printf("%s: import not sorted correctly. should be replace to\n%s\n", fset.Position(pos), correctImport)
lintFailed = true
}
return nil
})
return lintFailed
}
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zV
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.1.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
Expand Down
99 changes: 8 additions & 91 deletions main.go → strictimportsort.go
Original file line number Diff line number Diff line change
@@ -1,106 +1,17 @@
package main
package strictimportsort

import (
"bufio"
"bytes"
"flag"
"fmt"
"go/ast"
"go/parser"
"go/token"
"io/ioutil"
"os"
"os/exec"
"path/filepath"
"strings"

"github.com/minio/minio/pkg/wildcard"
)

const (
invalidArgumentExitCode = 3
)

var (
localPaths = flag.String("local", "", "put imports beginning with this string after 3rd-party packages; comma-separated list")
excludes = flag.String("exclude", "", "file names you wanna exclude; wile card is welcome; comma-separated list")
excludeDirs = flag.String("exclude-dir", "", "directory names you wanna exclude; wile card is welcome; comma-separated list")
dontRecurseFlag = flag.Bool("n", false, "don't recursively check paths")
)

func main() {
flag.Parse()

if len(flag.Args()) == 0 {
fmt.Println("missing argument: filepath")
os.Exit(invalidArgumentExitCode)
}

var lintFailed bool
for _, path := range flag.Args() {
rootPath, err := filepath.Abs(path)
if err != nil {
fmt.Printf("Error finding absolute path: %s", err)
os.Exit(invalidArgumentExitCode)
}
if walk(rootPath) {
lintFailed = true
}
}

if lintFailed {
os.Exit(1)
}
}

func walk(rootPath string) bool {
var lintFailed bool
filepath.Walk(rootPath, func(filePath string, fi os.FileInfo, err error) error {
if err != nil {
fmt.Printf("Error during filesystem walk: %v\n", err)
return nil
}
if fi.IsDir() {
if *excludeDirs != "" {
patternDirs := strings.Split(*excludeDirs, ",")
p := strings.Split(filePath, "/")
dirName := p[len(p)-1]
for _, pattern := range patternDirs {
if wildcard.MatchSimple(pattern, dirName) {
return filepath.SkipDir
}
}
}
if filePath != rootPath && (*dontRecurseFlag ||
filepath.Base(filePath) == "testdata" ||
filepath.Base(filePath) == "vendor") {
return filepath.SkipDir
}
return nil
}
if *excludes != "" {
patternFiles := strings.Split(*excludes, ",")
p := strings.Split(filePath, "/")
fileName := p[len(p)-1]
for _, pattern := range patternFiles {
if wildcard.MatchSimple(pattern, fileName) {
return nil
}
}
}
if !strings.HasSuffix(filePath, ".go") {
return nil
}
fset, poses, correctImport := Run(filePath, *localPaths)
for _, pos := range poses {
fmt.Printf("%s: import not sorted correctly. should be replace to\n%s\n", fset.Position(pos), correctImport)
lintFailed = true
}
return nil
})
return lintFailed
}

func Run(filePath, localPaths string) (fileSet *token.FileSet, pos []token.Pos, correctImport string) {
fileSet = token.NewFileSet()
f, err := parser.ParseFile(fileSet, filePath, nil, parser.ImportsOnly)
Expand Down Expand Up @@ -203,7 +114,9 @@ func (ils ImportLines) String() string {
buf := bytes.NewBuffer(make([]byte, 0, len(ils)*50))
buf.WriteString("import (\n")
for i := range ils {
buf.WriteString("\t")
if !ils[i].isWhiteline() {
buf.WriteString("\t")
}
if nm := ils[i].name; nm != "" {
buf.WriteString(nm)
buf.WriteString(" ")
Expand All @@ -227,6 +140,10 @@ type imptLine struct {
pos token.Pos // File offset in the file. first char of the line
}

func (l *imptLine) isWhiteline() bool {
return l.name == "" && l.path == ""
}

// buildImportLines returns empty length list when
// the target code has no `import` or single path `import "path"`
func buildImportLines(filePath string, f *ast.File) ImportLines {
Expand Down
91 changes: 91 additions & 0 deletions strictimportsort_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package strictimportsort

import (
"fmt"
"testing"
)

func Test(t *testing.T) {
const (
localPath = "github.com/momotaro98"
data01 = "testdata/src/testdata01.go"
data02 = "testdata/src/testdata02.go"
)

passCases := []struct {
name string
filePath string
localPath string
}{
{
name: "pass case without local path",
filePath: data01,
localPath: "",
},
{
name: "pass case with local path",
filePath: data02,
localPath: localPath,
},
}
for _, tt := range passCases {
t.Run(tt.name, func(t *testing.T) {
_, poses, actCorrect := Run(tt.filePath, tt.localPath)
if len(poses) != 0 {
t.Error("unexpected:", poses)
}
if actCorrect != "" {
t.Error("unexpected:", actCorrect)
}
})
}

failCases := []struct {
name string
filePath string
localPath string
expectedLineColumn string
expectedCorrect string
}{
{
name: "fail with local path",
filePath: data01,
localPath: localPath,
expectedLineColumn: "7:2",
expectedCorrect: `import (
_ "fmt"
_ "github.com/golang/mock/gomock"
_ "github.com/momotaro98/strictimportsort"
)`,
},
{
name: "fail without local path",
filePath: data02,
localPath: "",
expectedLineColumn: "7:1",
expectedCorrect: `import (
_ "fmt"
_ "github.com/golang/mock/gomock"
_ "github.com/momotaro98/strictimportsort"
)`,
},
}

for _, tt := range failCases {
t.Run(tt.name, func(t *testing.T) {
fset, poses, actCorrect := Run(tt.filePath, tt.localPath)
for _, pos := range poses {
if p := fset.Position(pos).String(); p != fmt.Sprintf("%s:%s", tt.filePath, tt.expectedLineColumn) {
t.Error("unexpected:", p)
}
if actCorrect != tt.expectedCorrect {
t.Error("unexpected:", actCorrect)
}
}
})
}

}
8 changes: 8 additions & 0 deletions testdata/src/testdata01.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package src

import (
_ "fmt"

_ "github.com/golang/mock/gomock"
_ "github.com/momotaro98/strictimportsort"
)
9 changes: 9 additions & 0 deletions testdata/src/testdata02.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package src

import (
_ "fmt"

_ "github.com/golang/mock/gomock"

_ "github.com/momotaro98/strictimportsort"
)

0 comments on commit 1d2fc04

Please sign in to comment.