Skip to content

Commit

Permalink
outline: Add support for nodot and alias import of the ginkgo package
Browse files Browse the repository at this point in the history
  • Loading branch information
dlipovetsky committed Dec 26, 2020
1 parent c386859 commit 5ff68c9
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 6 deletions.
47 changes: 47 additions & 0 deletions ginkgo/outline/_testdata/alias_test.go
@@ -0,0 +1,47 @@
package example_test

import (
fooginkgo "github.com/onsi/ginkgo"
)

var _ = fooginkgo.Describe("NodotFixture", func() {
fooginkgo.Describe("normal", func() {
fooginkgo.It("normal", func() {

})
})

fooginkgo.Context("normal", func() {
fooginkgo.It("normal", func() {

})
})

fooginkgo.When("normal", func() {
fooginkgo.It("normal", func() {

})
})

fooginkgo.It("normal", func() {

})

fooginkgo.Specify("normal", func() {

})

fooginkgo.Measure("normal", func(b Benchmarker) {

}, 2)

fooginkgo.DescribeTable("normal",
func() {},
fooginkgo.Entry("normal"),
)

fooginkgo.DescribeTable("normal",
func() {},
fooginkgo.Entry("normal"),
)
})
11 changes: 11 additions & 0 deletions ginkgo/outline/_testdata/alias_test_outline.csv
@@ -0,0 +1,11 @@
Name,Text,Start,End,Spec,Focused,Pending
Describe,NodotFixture,79,676,false,false,false
Describe,normal,124,205,false,false,false
It,normal,164,201,true,false,false
Context,normal,208,288,false,false,false
It,normal,247,284,true,false,false
When,normal,291,368,false,false,false
It,normal,327,364,true,false,false
It,normal,371,407,true,false,false
Specify,normal,410,451,true,false,false
Measure,normal,454,511,true,false,false
1 change: 1 addition & 0 deletions ginkgo/outline/_testdata/alias_test_outline.json
@@ -0,0 +1 @@
[{"name":"Describe","text":"NodotFixture","start":79,"end":676,"spec":false,"focused":false,"pending":false,"nodes":[{"name":"Describe","text":"normal","start":124,"end":205,"spec":false,"focused":false,"pending":false,"nodes":[{"name":"It","text":"normal","start":164,"end":201,"spec":true,"focused":false,"pending":false}]},{"name":"Context","text":"normal","start":208,"end":288,"spec":false,"focused":false,"pending":false,"nodes":[{"name":"It","text":"normal","start":247,"end":284,"spec":true,"focused":false,"pending":false}]},{"name":"When","text":"normal","start":291,"end":368,"spec":false,"focused":false,"pending":false,"nodes":[{"name":"It","text":"normal","start":327,"end":364,"spec":true,"focused":false,"pending":false}]},{"name":"It","text":"normal","start":371,"end":407,"spec":true,"focused":false,"pending":false},{"name":"Specify","text":"normal","start":410,"end":451,"spec":true,"focused":false,"pending":false},{"name":"Measure","text":"normal","start":454,"end":511,"spec":true,"focused":false,"pending":false}]}]
47 changes: 47 additions & 0 deletions ginkgo/outline/_testdata/nodot_test.go
@@ -0,0 +1,47 @@
package example_test

import (
"github.com/onsi/ginkgo"
)

var _ = ginkgo.Describe("NodotFixture", func() {
ginkgo.Describe("normal", func() {
ginkgo.It("normal", func() {

})
})

ginkgo.Context("normal", func() {
ginkgo.It("normal", func() {

})
})

ginkgo.When("normal", func() {
ginkgo.It("normal", func() {

})
})

ginkgo.It("normal", func() {

})

ginkgo.Specify("normal", func() {

})

ginkgo.Measure("normal", func(b Benchmarker) {

}, 2)

ginkgo.DescribeTable("normal",
func() {},
ginkgo.Entry("normal"),
)

ginkgo.DescribeTable("normal",
func() {},
ginkgo.Entry("normal"),
)
})
11 changes: 11 additions & 0 deletions ginkgo/outline/_testdata/nodot_test_outline.csv
@@ -0,0 +1,11 @@
Name,Text,Start,End,Spec,Focused,Pending
Describe,NodotFixture,69,624,false,false,false
Describe,normal,111,186,false,false,false
It,normal,148,182,true,false,false
Context,normal,189,263,false,false,false
It,normal,225,259,true,false,false
When,normal,266,337,false,false,false
It,normal,299,333,true,false,false
It,normal,340,373,true,false,false
Specify,normal,376,414,true,false,false
Measure,normal,417,471,true,false,false
1 change: 1 addition & 0 deletions ginkgo/outline/_testdata/nodot_test_outline.json
@@ -0,0 +1 @@
[{"name":"Describe","text":"NodotFixture","start":69,"end":624,"spec":false,"focused":false,"pending":false,"nodes":[{"name":"Describe","text":"normal","start":111,"end":186,"spec":false,"focused":false,"pending":false,"nodes":[{"name":"It","text":"normal","start":148,"end":182,"spec":true,"focused":false,"pending":false}]},{"name":"Context","text":"normal","start":189,"end":263,"spec":false,"focused":false,"pending":false,"nodes":[{"name":"It","text":"normal","start":225,"end":259,"spec":true,"focused":false,"pending":false}]},{"name":"When","text":"normal","start":266,"end":337,"spec":false,"focused":false,"pending":false,"nodes":[{"name":"It","text":"normal","start":299,"end":333,"spec":true,"focused":false,"pending":false}]},{"name":"It","text":"normal","start":340,"end":373,"spec":true,"focused":false,"pending":false},{"name":"Specify","text":"normal","start":376,"end":414,"spec":true,"focused":false,"pending":false},{"name":"Measure","text":"normal","start":417,"end":471,"spec":true,"focused":false,"pending":false}]}]
61 changes: 61 additions & 0 deletions ginkgo/outline/import.go
@@ -0,0 +1,61 @@
// Copyright 2013 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Most of the required functions were available in the
// "golang.org/x/tools/go/ast/astutil" package, but not exported.
// They were copied from https://github.com/golang/tools/blob/2b0845dc783e36ae26d683f4915a5840ef01ab0f/go/ast/astutil/imports.go

package outline

import (
"go/ast"
"strconv"
"strings"
)

// importNameForPackage returns the import name for the package. If the package
// is not imported, it returns false. Examples:
// How package is imported -> Import Name
// "import example.com/pkg/foo" -> "foo"
// "import fooalias example.com/pkg/foo" -> "fooalias"
// "import . example.com/pkg/foo" -> "."
func importNameForPackage(f *ast.File, path string) (string, bool) {
spec := importSpec(f, path)
if spec == nil {
return "", false
}
name := spec.Name.String()
if name == "<nil>" {
// If the package name is not explicitly specified,
// make an educated guess. This is not guaranteed to be correct.
lastSlash := strings.LastIndex(path, "/")
if lastSlash == -1 {
name = path
} else {
name = path[lastSlash+1:]
}
}
return name, true
}

// importSpec returns the import spec if f imports path,
// or nil otherwise.
func importSpec(f *ast.File, path string) *ast.ImportSpec {
for _, s := range f.Imports {
if importPath(s) == path {
return s
}
}
return nil
}

// importPath returns the unquoted import path of s,
// or "" if the path is not properly quoted.
func importPath(s *ast.ImportSpec) string {
t, err := strconv.Unquote(s.Path.Value)
if err != nil {
return ""
}
return t
}
35 changes: 29 additions & 6 deletions ginkgo/outline/outline.go
Expand Up @@ -12,6 +12,8 @@ import (
)

const (
// ginkgoImportPath is the well-known ginkgo import path
ginkgoImportPath = "github.com/onsi/ginkgo"
// undefinedTextAlt is used if the spec/container text cannot be derived
undefinedTextAlt = "undefined"
)
Expand Down Expand Up @@ -52,18 +54,35 @@ func (n *ginkgoNode) Walk(f walkFunc) {

// ginkgoNodeFromCallExpr derives an outline entry from a go AST subtree
// corresponding to a Ginkgo container or spec.
func ginkgoNodeFromCallExpr(ce *ast.CallExpr) (*ginkgoNode, bool) {
id, ok := ce.Fun.(*ast.Ident)
if !ok {
func ginkgoNodeFromCallExpr(ce *ast.CallExpr, ginkgoImportName string) (*ginkgoNode, bool) {
var id *ast.Ident
switch ex := ce.Fun.(type) {
case *ast.Ident:
if ginkgoImportName != "." {
return nil, false
}
id = ex
case *ast.SelectorExpr:
pkgID, ok := ex.X.(*ast.Ident)
if !ok {
return nil, false
}
// A package identifier is top-level, so Obj must be nil
if pkgID.Obj != nil {
return nil, false
}
if ginkgoImportName != pkgID.Name {
return nil, false
}
id = ex.Sel
default:
return nil, false
}

n := ginkgoNode{}
n.Name = id.Name
n.Start = ce.Pos()
n.End = ce.End()
// TODO: Handle nodot and alias imports of the ginkgo package.
// The below assumes dot imports .
switch id.Name {
case "It", "Measure", "Specify":
n.Spec = true
Expand Down Expand Up @@ -131,6 +150,10 @@ func textFromCallExpr(ce *ast.CallExpr) (string, bool) {

// FromASTFile returns an outline for a Ginkgo test source file
func FromASTFile(src *ast.File) (*outline, error) {
ginkgoImportName, ok := importNameForPackage(src, ginkgoImportPath)
if !ok {
return nil, fmt.Errorf("file does not import %s", ginkgoImportPath)
}
root := ginkgoNode{
Nodes: []*ginkgoNode{},
}
Expand All @@ -144,7 +167,7 @@ func FromASTFile(src *ast.File) (*outline, error) {
// ast.CallExpr, this should never happen
panic(fmt.Errorf("node starting at %d, ending at %d is not an *ast.CallExpr", node.Pos(), node.End()))
}
gn, ok := ginkgoNodeFromCallExpr(ce)
gn, ok := ginkgoNodeFromCallExpr(ce, ginkgoImportName)
if !ok {
// Not a Ginkgo call, continue
return true
Expand Down
2 changes: 2 additions & 0 deletions ginkgo/outline/outline_test.go
Expand Up @@ -40,6 +40,8 @@ var _ = DescribeTable("Validate outline from file with",

Expect(gotCSV).To(Equal(string(wantCSV)))
},
Entry("normal import of ginkgo package (no dot, no alias), normal container and specs", "nodot_test.go", "nodot_test_outline.json", "nodot_test_outline.csv"),
Entry("aliased import of ginkgo package", "alias_test.go, normal containers and specs", "alias_test_outline.json", "alias_test_outline.csv"),
Entry("normal containers and specs", "normal_test.go", "normal_test_outline.json", "normal_test_outline.csv"),
Entry("focused containers and specs", "focused_test.go", "focused_test_outline.json", "focused_test_outline.csv"),
Entry("pending containers and specs", "pending_test.go", "pending_test_outline.json", "pending_test_outline.csv"),
Expand Down

0 comments on commit 5ff68c9

Please sign in to comment.