1// Copyright 2018 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5package expect_test
6
7import (
8	"bytes"
9	"go/token"
10	"io/ioutil"
11	"testing"
12
13	"golang.org/x/tools/go/expect"
14)
15
16func TestMarker(t *testing.T) {
17	for _, tt := range []struct {
18		filename      string
19		expectNotes   int
20		expectMarkers map[string]string
21		expectChecks  map[string][]interface{}
22	}{
23		{
24			filename:    "testdata/test.go",
25			expectNotes: 13,
26			expectMarkers: map[string]string{
27				"αSimpleMarker": "α",
28				"OffsetMarker":  "β",
29				"RegexMarker":   "γ",
30				"εMultiple":     "ε",
31				"ζMarkers":      "ζ",
32				"ηBlockMarker":  "η",
33				"Declared":      "η",
34				"Comment":       "ι",
35				"LineComment":   "someFunc",
36				"NonIdentifier": "+",
37				"StringMarker":  "\"hello\"",
38			},
39			expectChecks: map[string][]interface{}{
40				"αSimpleMarker": nil,
41				"StringAndInt":  {"Number %d", int64(12)},
42				"Bool":          {true},
43			},
44		},
45		{
46			filename:    "testdata/go.mod",
47			expectNotes: 2,
48			expectMarkers: map[string]string{
49				"αMarker": "αfake1α",
50				"βMarker": "require golang.org/modfile v0.0.0",
51			},
52		},
53	} {
54		t.Run(tt.filename, func(t *testing.T) {
55			content, err := ioutil.ReadFile(tt.filename)
56			if err != nil {
57				t.Fatal(err)
58			}
59			readFile := func(string) ([]byte, error) { return content, nil }
60
61			markers := make(map[string]token.Pos)
62			for name, tok := range tt.expectMarkers {
63				offset := bytes.Index(content, []byte(tok))
64				markers[name] = token.Pos(offset + 1)
65				end := bytes.Index(content[offset:], []byte(tok))
66				if end > 0 {
67					markers[name+"@"] = token.Pos(offset + end + 2)
68				}
69			}
70
71			fset := token.NewFileSet()
72			notes, err := expect.Parse(fset, tt.filename, content)
73			if err != nil {
74				t.Fatalf("Failed to extract notes: %v", err)
75			}
76			if len(notes) != tt.expectNotes {
77				t.Errorf("Expected %v notes, got %v", tt.expectNotes, len(notes))
78			}
79			for _, n := range notes {
80				switch {
81				case n.Args == nil:
82					// A //@foo note associates the name foo with the position of the
83					// first match of "foo" on the current line.
84					checkMarker(t, fset, readFile, markers, n.Pos, n.Name, n.Name)
85				case n.Name == "mark":
86					// A //@mark(name, "pattern") note associates the specified name
87					// with the position on the first match of pattern on the current line.
88					if len(n.Args) != 2 {
89						t.Errorf("%v: expected 2 args to mark, got %v", fset.Position(n.Pos), len(n.Args))
90						continue
91					}
92					ident, ok := n.Args[0].(expect.Identifier)
93					if !ok {
94						t.Errorf("%v: identifier, got %T", fset.Position(n.Pos), n.Args[0])
95						continue
96					}
97					checkMarker(t, fset, readFile, markers, n.Pos, string(ident), n.Args[1])
98
99				case n.Name == "check":
100					// A //@check(args, ...) note specifies some hypothetical action to
101					// be taken by the test driver and its expected outcome.
102					// In this test, the action is to compare the arguments
103					// against expectChecks.
104					if len(n.Args) < 1 {
105						t.Errorf("%v: expected 1 args to check, got %v", fset.Position(n.Pos), len(n.Args))
106						continue
107					}
108					ident, ok := n.Args[0].(expect.Identifier)
109					if !ok {
110						t.Errorf("%v: identifier, got %T", fset.Position(n.Pos), n.Args[0])
111						continue
112					}
113					args, ok := tt.expectChecks[string(ident)]
114					if !ok {
115						t.Errorf("%v: unexpected check %v", fset.Position(n.Pos), ident)
116						continue
117					}
118					if len(n.Args) != len(args)+1 {
119						t.Errorf("%v: expected %v args to check, got %v", fset.Position(n.Pos), len(args)+1, len(n.Args))
120						continue
121					}
122					for i, got := range n.Args[1:] {
123						if args[i] != got {
124							t.Errorf("%v: arg %d expected %v, got %v", fset.Position(n.Pos), i, args[i], got)
125						}
126					}
127				default:
128					t.Errorf("Unexpected note %v at %v", n.Name, fset.Position(n.Pos))
129				}
130			}
131		})
132	}
133}
134
135func checkMarker(t *testing.T, fset *token.FileSet, readFile expect.ReadFile, markers map[string]token.Pos, pos token.Pos, name string, pattern interface{}) {
136	start, end, err := expect.MatchBefore(fset, readFile, pos, pattern)
137	if err != nil {
138		t.Errorf("%v: MatchBefore failed: %v", fset.Position(pos), err)
139		return
140	}
141	if start == token.NoPos {
142		t.Errorf("%v: Pattern %v did not match", fset.Position(pos), pattern)
143		return
144	}
145	expectStart, ok := markers[name]
146	if !ok {
147		t.Errorf("%v: unexpected marker %v", fset.Position(pos), name)
148		return
149	}
150	if start != expectStart {
151		t.Errorf("%v: Expected %v got %v", fset.Position(pos), fset.Position(expectStart), fset.Position(start))
152	}
153	if expectEnd, ok := markers[name+"@"]; ok && end != expectEnd {
154		t.Errorf("%v: Expected end %v got %v", fset.Position(pos), fset.Position(expectEnd), fset.Position(end))
155	}
156}
157