1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207
|
package outline
import (
"encoding/csv"
"encoding/json"
"fmt"
"go/parser"
"go/token"
"log"
"os"
"path/filepath"
"strconv"
"strings"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
var _ = DescribeTable("Validate outline from file with",
func(srcFilename, jsonOutlineFilename, csvOutlineFilename string) {
fset := token.NewFileSet()
astFile, err := parser.ParseFile(fset, filepath.Join("_testdata", srcFilename), nil, 0)
Expect(err).To(BeNil(), "error parsing source: %s", err)
if err != nil {
log.Fatalf("error parsing source: %s", err)
}
o, err := FromASTFile(fset, astFile)
Expect(err).To(BeNil(), "error creating outline: %s", err)
gotJSON, err := json.MarshalIndent(o, "", " ")
Expect(err).To(BeNil(), "error marshalling outline to json: %s", err)
wantJSON, err := os.ReadFile(filepath.Join("_testdata", jsonOutlineFilename))
Expect(err).To(BeNil(), "error reading JSON outline fixture: %s", err)
Expect(gotJSON).To(MatchJSON(wantJSON))
gotCSV := o.String()
wantCSV, err := os.ReadFile(filepath.Join("_testdata", csvOutlineFilename))
Expect(err).To(BeNil(), "error reading CSV outline fixture: %s", err)
Expect(gotCSV).To(Equal(string(wantCSV)))
ensureRecordsAreIdentical(csvOutlineFilename, jsonOutlineFilename)
},
// To add a test:
// 1. Create the input, e.g., `myspecialcase_test.go`
// 2. Create the sample CSV and JSON results: Run `bash ./_testdata/create_result.sh ./_testdata/myspecialcase_test.go`
// 3. Add an Entry below, by copying an existing one, and substituting `myspecialcase` where needed.
// To re-create the sample results for a test:
// 1. Run `bash ./_testdata/create_result.sh ./testdata/myspecialcase_test.go`
// To re-create the sample results for all tests:
// 1. Run `for name in ./_testdata/*_test.go; do bash ./_testdata/create_result.sh $name; done`
Entry("normal import of ginkgo package (no dot, no alias), normal container and specs", "nodot_test.go", "nodot_test.go.json", "nodot_test.go.csv"),
Entry("aliased import of ginkgo package, normal container and specs", "alias_test.go", "alias_test.go.json", "alias_test.go.csv"),
Entry("normal containers and specs", "normal_test.go", "normal_test.go.json", "normal_test.go.csv"),
Entry("focused containers and specs", "focused_test.go", "focused_test.go.json", "focused_test.go.csv"),
Entry("pending containers and specs", "pending_test.go", "pending_test.go.json", "pending_test.go.csv"),
Entry("nested focused containers and specs", "nestedfocused_test.go", "nestedfocused_test.go.json", "nestedfocused_test.go.csv"),
Entry("mixed focused containers and specs", "mixed_test.go", "mixed_test.go.json", "mixed_test.go.csv"),
Entry("specs used to verify position", "position_test.go", "position_test.go.json", "position_test.go.csv"),
Entry("suite setup", "suite_test.go", "suite_test.go.json", "suite_test.go.csv"),
Entry("core dsl import", "dsl_core_test.go", "dsl_core_test.go.json", "dsl_core_test.go.csv"),
Entry("labels decorator on containers and specs", "labels_test.go", "labels_test.go.json", "labels_test.go.csv"),
Entry("pending decorator on containers and specs", "pending_decorator_test.go", "pending_decorator_test.go.json", "pending_decorator_test.go.csv"),
Entry("proper csv escaping of all fields", "csv_proper_escaping_test.go", "csv_proper_escaping_test.go.json", "csv_proper_escaping_test.go.csv"),
)
var _ = Describe("Validate position", func() {
It("should report the correct start and end byte offsets of the ginkgo container or spec", func() {
fset := token.NewFileSet()
astFile, err := parser.ParseFile(fset, filepath.Join("_testdata", "position_test.go"), nil, 0)
Expect(err).To(BeNil(), "error parsing source: %s", err)
if err != nil {
log.Fatalf("error parsing source: %s", err)
}
o, err := FromASTFile(fset, astFile)
Expect(err).To(BeNil(), "error creating outline: %s", err)
for _, n := range o.Nodes {
n.PreOrder(func(n *ginkgoNode) {
wantPositions := strings.Split(n.Text, ",")
Expect(len(wantPositions)).To(Equal(2), "test fixture node text should be \"start position,end position")
wantStart, err := strconv.Atoi(wantPositions[0])
Expect(err).To(BeNil(), "could not parse start offset")
wantEnd, err := strconv.Atoi(wantPositions[1])
Expect(err).To(BeNil(), "could not parse end offset")
Expect(int(n.Start)).To(Equal(wantStart), fmt.Sprintf("test fixture node text says the node should start at %d, but it starts at %d", wantStart, n.Start))
Expect(int(n.End)).To(Equal(wantEnd), fmt.Sprintf("test fixture node text says the node should end at %d, but it ends at %d", wantEnd, n.End))
})
}
})
})
func ensureRecordsAreIdentical(csvOutlineFilename, jsonOutlineFilename string) {
csvFile, err := os.Open(filepath.Join("_testdata", csvOutlineFilename))
Expect(err).To(BeNil(), "error opening CSV outline fixture: %s", err)
defer csvFile.Close()
csvReader := csv.NewReader(csvFile)
csvRows, err := csvReader.ReadAll()
Expect(err).To(BeNil(), "error reading CSV outline fixture: %s", err)
// marshal csvRows into some comparable shape
csvFields := csvRows[0]
var csvRecords []ginkgoMetadata
for i := 1; i < len(csvRows); i++ {
var record ginkgoMetadata
for j, field := range csvFields {
field = strings.ToLower(field)
value := csvRows[i][j]
switch field {
case "name":
record.Name = value
case "text":
record.Text = value
case "start":
start, err := strconv.Atoi(value)
Expect(err).To(BeNil(), "error converting start to int: %s", err)
record.Start = start
case "end":
end, err := strconv.Atoi(value)
Expect(err).To(BeNil(), "error converting end to int: %s", err)
record.End = end
case "spec":
spec, err := strconv.ParseBool(value)
Expect(err).To(BeNil(), "error converting spec to bool: %s", err)
record.Spec = spec
case "focused":
focused, err := strconv.ParseBool(value)
Expect(err).To(BeNil(), "error converting focused to bool: %s", err)
record.Focused = focused
case "pending":
pending, err := strconv.ParseBool(value)
Expect(err).To(BeNil(), "error converting pending to bool: %s", err)
record.Pending = pending
case "labels":
// strings.Split will return [""] for an empty string, we want []
if value == "" {
record.Labels = []string{}
} else {
record.Labels = strings.Split(value, ", ")
}
default:
Fail(fmt.Sprintf("unexpected field: %s", field))
}
}
// "By" is a special case; nil out its labels so we can compare to the parsed JSON
if record.Name == "By" {
record.Labels = nil
}
csvRecords = append(csvRecords, record)
}
jsonFile, err := os.Open(filepath.Join("_testdata", jsonOutlineFilename))
Expect(err).To(BeNil(), "error opening JSON outline fixture: %s", err)
defer jsonFile.Close()
jsonDecoder := json.NewDecoder(jsonFile)
var jsonRows []ginkgoNode
err = jsonDecoder.Decode(&jsonRows)
Expect(err).To(BeNil(), "error reading JSON outline fixture: %s", err)
// marshal jsonRows into some comparable shape - the hierarchical structure needs to be flattened
var jsonRecords []ginkgoMetadata
flattenNodes(jsonRows, &jsonRecords)
Expect(csvRecords).To(Equal(jsonRecords))
}
// flattenNodes converts the hierarchical json output into a list of records
func flattenNodes(nodes []ginkgoNode, flatNodes *[]ginkgoMetadata) {
for _, node := range nodes {
record := ginkgoMetadata{
Name: node.Name,
Text: node.Text,
Start: node.Start,
End: node.End,
Spec: node.Spec,
Focused: node.Focused,
Pending: node.Pending,
Labels: node.Labels,
}
*flatNodes = append(*flatNodes, record)
// handle nested nodes
if len(node.Nodes) > 0 {
var nestedNodes []ginkgoNode
for _, nestedNode := range node.Nodes {
nestedNodes = append(nestedNodes, *nestedNode)
}
flattenNodes(nestedNodes, flatNodes)
}
}
}
|