GoPLS Viewer

Home|gopls/refactor/rename/mvpkg_test.go
1// Copyright 2015 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 rename
6
7import (
8    "fmt"
9    "go/build"
10    "go/token"
11    "io/ioutil"
12    "path/filepath"
13    "reflect"
14    "regexp"
15    "strings"
16    "testing"
17
18    "golang.org/x/tools/go/buildutil"
19)
20
21func TestErrors(t *testing.T) {
22    tests := []struct {
23        ctxt     *build.Context
24        fromto string
25        want     string // regexp to match error, or "OK"
26    }{
27        // Simple example.
28        {
29            ctxtfakeContext(map[string][]string{
30                "foo": {`package foo; type T int`},
31                "bar": {`package bar`},
32                "main": {`package main
33
34import "foo"
35
36var _ foo.T
37`},
38            }),
39            from"foo"to"bar",
40            want`invalid move destination: bar conflicts with directory .go.src.bar`,
41        },
42        // Subpackage already exists.
43        {
44            ctxtfakeContext(map[string][]string{
45                "foo":     {`package foo; type T int`},
46                "foo/sub": {`package sub`},
47                "bar/sub": {`package sub`},
48                "main": {`package main
49
50import "foo"
51
52var _ foo.T
53`},
54            }),
55            from"foo"to"bar",
56            want"invalid move destination: bar; package or subpackage bar/sub already exists",
57        },
58        // Invalid base name.
59        {
60            ctxtfakeContext(map[string][]string{
61                "foo": {`package foo; type T int`},
62                "main": {`package main
63
64import "foo"
65
66var _ foo.T
67`},
68            }),
69            from"foo"to"bar-v2.0",
70            want"invalid move destination: bar-v2.0; gomvpkg does not " +
71                "support move destinations whose base names are not valid " +
72                "go identifiers",
73        },
74        {
75            ctxtfakeContext(map[string][]string{
76                "foo": {``},
77                "bar": {`package bar`},
78            }),
79            from"foo"to"bar",
80            want`no initial packages were loaded`,
81        },
82    }
83
84    for _test := range tests {
85        ctxt := test.ctxt
86
87        got := make(map[string]string)
88        writeFile = func(filename stringcontent []byteerror {
89            got[filename] = string(content)
90            return nil
91        }
92        moveDirectory = func(fromto stringerror {
93            for pathcontents := range got {
94                if strings.HasPrefix(pathfrom) {
95                    newPath := strings.Replace(pathfromto1)
96                    delete(gotpath)
97                    got[newPath] = contents
98                }
99            }
100            return nil
101        }
102
103        err := Move(ctxttest.fromtest.to"")
104        prefix := fmt.Sprintf("-from %q -to %q"test.fromtest.to)
105        if err == nil {
106            t.Errorf("%s: nil error. Expected error: %s"prefixtest.want)
107            continue
108        }
109        matchederr2 := regexp.MatchString(test.wanterr.Error())
110        if err2 != nil {
111            t.Errorf("regexp.MatchString failed %s"err2)
112            continue
113        }
114        if !matched {
115            t.Errorf("%s: conflict does not match expectation:\n"+
116                "Error: %q\n"+
117                "Pattern: %q",
118                prefixerr.Error(), test.want)
119        }
120    }
121}
122
123func TestMoves(t *testing.T) {
124    tests := []struct {
125        ctxt         *build.Context
126        fromto     string
127        want         map[string]string
128        wantWarnings []string
129    }{
130        // Simple example.
131        {
132            ctxtfakeContext(map[string][]string{
133                "foo": {`package foo; type T int`},
134                "main": {`package main
135
136import "foo"
137
138var _ foo.T
139`},
140            }),
141            from"foo"to"bar",
142            want: map[string]string{
143                "/go/src/main/0.go"`package main
144
145import "bar"
146
147var _ bar.T
148`,
149                "/go/src/bar/0.go"`package bar
150
151type T int
152`,
153            },
154        },
155
156        // Example with subpackage.
157        {
158            ctxtfakeContext(map[string][]string{
159                "foo":     {`package foo; type T int`},
160                "foo/sub": {`package sub; type T int`},
161                "main": {`package main
162
163import "foo"
164import "foo/sub"
165
166var _ foo.T
167var _ sub.T
168`},
169            }),
170            from"foo"to"bar",
171            want: map[string]string{
172                "/go/src/main/0.go"`package main
173
174import "bar"
175import "bar/sub"
176
177var _ bar.T
178var _ sub.T
179`,
180                "/go/src/bar/0.go"`package bar
181
182type T int
183`,
184                "/go/src/bar/sub/0.go"`package sub; type T int`,
185            },
186        },
187
188        // References into subpackages
189        {
190            ctxtfakeContext(map[string][]string{
191                "foo":   {`package foo; import "foo/a"; var _ a.T`},
192                "foo/a": {`package a; type T int`},
193                "foo/b": {`package b; import "foo/a"; var _ a.T`},
194            }),
195            from"foo"to"bar",
196            want: map[string]string{
197                "/go/src/bar/0.go"`package bar
198
199import "bar/a"
200
201var _ a.T
202`,
203                "/go/src/bar/a/0.go"`package a; type T int`,
204                "/go/src/bar/b/0.go"`package b
205
206import "bar/a"
207
208var _ a.T
209`,
210            },
211        },
212
213        // References into subpackages where directories have overlapped names
214        {
215            ctxtfakeContext(map[string][]string{
216                "foo":    {},
217                "foo/a":  {`package a`},
218                "foo/aa": {`package bar`},
219                "foo/c":  {`package c; import _ "foo/bar";`},
220            }),
221            from"foo/a"to"foo/spam",
222            want: map[string]string{
223                "/go/src/foo/spam/0.go"`package spam
224`,
225                "/go/src/foo/aa/0.go"`package bar`,
226                "/go/src/foo/c/0.go":  `package c; import _ "foo/bar";`,
227            },
228        },
229
230        // External test packages
231        {
232            ctxtbuildutil.FakeContext(map[string]map[string]string{
233                "foo": {
234                    "0.go":      `package foo; type T int`,
235                    "0_test.go"`package foo_test; import "foo"; var _ foo.T`,
236                },
237                "baz": {
238                    "0_test.go"`package baz_test; import "foo"; var _ foo.T`,
239                },
240            }),
241            from"foo"to"bar",
242            want: map[string]string{
243                "/go/src/bar/0.go"`package bar
244
245type T int
246`,
247                "/go/src/bar/0_test.go"`package bar_test
248
249import "bar"
250
251var _ bar.T
252`,
253                "/go/src/baz/0_test.go"`package baz_test
254
255import "bar"
256
257var _ bar.T
258`,
259            },
260        },
261        // package import comments
262        {
263            ctxtfakeContext(map[string][]string{"foo": {`package foo // import "baz"`}}),
264            from"foo"to"bar",
265            want: map[string]string{"/go/src/bar/0.go"`package bar // import "bar"
266`},
267        },
268        {
269            ctxtfakeContext(map[string][]string{"foo": {`package foo /* import "baz" */`}}),
270            from"foo"to"bar",
271            want: map[string]string{"/go/src/bar/0.go"`package bar /* import "bar" */
272`},
273        },
274        {
275            ctxtfakeContext(map[string][]string{"foo": {`package foo       // import "baz"`}}),
276            from"foo"to"bar",
277            want: map[string]string{"/go/src/bar/0.go"`package bar // import "bar"
278`},
279        },
280        {
281            ctxtfakeContext(map[string][]string{"foo": {`package foo
282// import " this is not an import comment`}}),
283            from"foo"to"bar",
284            want: map[string]string{"/go/src/bar/0.go"`package bar
285
286// import " this is not an import comment
287`},
288        },
289        {
290            ctxtfakeContext(map[string][]string{"foo": {`package foo
291/* import " this is not an import comment */`}}),
292            from"foo"to"bar",
293            want: map[string]string{"/go/src/bar/0.go"`package bar
294
295/* import " this is not an import comment */
296`},
297        },
298        // Import name conflict generates a warning, not an error.
299        {
300            ctxtfakeContext(map[string][]string{
301                "x": {},
302                "a": {`package a; type A int`},
303                "b": {`package b; type B int`},
304                "conflict": {`package conflict
305
306import "a"
307import "b"
308var _ a.A
309var _ b.B
310`},
311                "ok": {`package ok
312import "b"
313var _ b.B
314`},
315            }),
316            from"b"to"x/a",
317            want: map[string]string{
318                "/go/src/a/0.go"`package a; type A int`,
319                "/go/src/ok/0.go"`package ok
320
321import "x/a"
322
323var _ a.B
324`,
325                "/go/src/conflict/0.go"`package conflict
326
327import "a"
328import "x/a"
329
330var _ a.A
331var _ b.B
332`,
333                "/go/src/x/a/0.go"`package a
334
335type B int
336`,
337            },
338            wantWarnings: []string{
339                `/go/src/conflict/0.go:4:8: renaming this imported package name "b" to "a"`,
340                `/go/src/conflict/0.go:3:8:     conflicts with imported package name in same block`,
341                `/go/src/conflict/0.go:3:8: skipping update of this file`,
342            },
343        },
344        // Rename with same base name.
345        {
346            ctxtfakeContext(map[string][]string{
347                "x": {},
348                "y": {},
349                "x/foo": {`package foo
350
351type T int
352`},
353                "main": {`package main; import "x/foo"; var _ foo.T`},
354            }),
355            from"x/foo"to"y/foo",
356            want: map[string]string{
357                "/go/src/y/foo/0.go"`package foo
358
359type T int
360`,
361                "/go/src/main/0.go"`package main
362
363import "y/foo"
364
365var _ foo.T
366`,
367            },
368        },
369    }
370
371    for _test := range tests {
372        ctxt := test.ctxt
373
374        got := make(map[string]string)
375        // Populate got with starting file set. rewriteFile and moveDirectory
376        // will mutate got to produce resulting file set.
377        buildutil.ForEachPackage(ctxt, func(importPath stringerr error) {
378            if err != nil {
379                return
380            }
381            path := filepath.Join("/go/src"importPath"0.go")
382            if !buildutil.FileExists(ctxtpath) {
383                return
384            }
385            ferr := ctxt.OpenFile(path)
386            if err != nil {
387                t.Errorf("unexpected error opening file: %s"err)
388                return
389            }
390            byteserr := ioutil.ReadAll(f)
391            f.Close()
392            if err != nil {
393                t.Errorf("unexpected error reading file: %s"err)
394                return
395            }
396            got[path] = string(bytes)
397        })
398        var warnings []string
399        reportError = func(posn token.Positionmessage string) {
400            warning := fmt.Sprintf("%s:%d:%d: %s",
401                filepath.ToSlash(posn.Filename), // for MS Windows
402                posn.Line,
403                posn.Column,
404                message)
405            warnings = append(warningswarning)
406
407        }
408        writeFile = func(filename stringcontent []byteerror {
409            got[filename] = string(content)
410            return nil
411        }
412        moveDirectory = func(fromto stringerror {
413            for pathcontents := range got {
414                if !(strings.HasPrefix(pathfrom) &&
415                    (len(path) == len(from) || path[len(from)] == filepath.Separator)) {
416                    continue
417                }
418                newPath := strings.Replace(pathfromto1)
419                delete(gotpath)
420                got[newPath] = contents
421            }
422            return nil
423        }
424
425        err := Move(ctxttest.fromtest.to"")
426        prefix := fmt.Sprintf("-from %q -to %q"test.fromtest.to)
427        if err != nil {
428            t.Errorf("%s: unexpected error: %s"prefixerr)
429            continue
430        }
431
432        if !reflect.DeepEqual(warningstest.wantWarnings) {
433            t.Errorf("%s: unexpected warnings:\n%s\nwant:\n%s",
434                prefix,
435                strings.Join(warnings"\n"),
436                strings.Join(test.wantWarnings"\n"))
437        }
438
439        for filewantContent := range test.want {
440            k := filepath.FromSlash(file)
441            gotContentok := got[k]
442            delete(gotk)
443            if !ok {
444                // TODO(matloob): some testcases might have files that won't be
445                // rewritten
446                t.Errorf("%s: file %s not rewritten"prefixfile)
447                continue
448            }
449            if gotContent != wantContent {
450                t.Errorf("%s: rewritten file %s does not match expectation; got <<<%s>>>\n"+
451                    "want <<<%s>>>"prefixfilegotContentwantContent)
452            }
453        }
454        // got should now be empty
455        for file := range got {
456            t.Errorf("%s: unexpected rewrite of file %s"prefixfile)
457        }
458    }
459}
460
MembersX
TestMoves.RangeStmt_7760.BlockStmt.ctxt
TestMoves.RangeStmt_7760.BlockStmt.BlockStmt.path
TestMoves.RangeStmt_7760.BlockStmt.RangeStmt_10120.file
TestErrors.tests
TestErrors.RangeStmt_1710.BlockStmt.err2
TestMoves.RangeStmt_7760.BlockStmt.BlockStmt.bytes
TestMoves.RangeStmt_7760.BlockStmt.BlockStmt.warning
TestMoves.RangeStmt_7760.BlockStmt.RangeStmt_9611.BlockStmt.k
TestMoves.RangeStmt_7760.BlockStmt.BlockStmt.RangeStmt_8916.path
TestErrors
TestErrors.t
TestErrors.RangeStmt_1710.BlockStmt.BlockStmt.RangeStmt_1957.contents
TestErrors.RangeStmt_1710.BlockStmt.err
TestMoves.t
TestMoves.RangeStmt_7760.test
TestMoves.RangeStmt_7760.BlockStmt.got
TestMoves.RangeStmt_7760.BlockStmt.RangeStmt_9611.file
TestMoves.RangeStmt_7760.BlockStmt.RangeStmt_9611.wantContent
TestErrors.RangeStmt_1710.test
TestErrors.RangeStmt_1710.BlockStmt.BlockStmt.RangeStmt_1957.path
TestMoves.RangeStmt_7760.BlockStmt.BlockStmt.RangeStmt_8916.contents
testing
TestErrors.RangeStmt_1710.BlockStmt.ctxt
TestMoves.RangeStmt_7760.BlockStmt.BlockStmt.f
TestErrors.RangeStmt_1710.BlockStmt.prefix
TestErrors.RangeStmt_1710.BlockStmt.matched
reflect
TestErrors.RangeStmt_1710.BlockStmt.BlockStmt.RangeStmt_1957.BlockStmt.BlockStmt.newPath
TestMoves.RangeStmt_7760.BlockStmt.prefix
TestMoves.RangeStmt_7760.BlockStmt.err
ioutil
TestErrors.RangeStmt_1710.BlockStmt.got
TestMoves
TestMoves.tests
TestMoves.RangeStmt_7760.BlockStmt.BlockStmt.err
TestMoves.RangeStmt_7760.BlockStmt.warnings
TestMoves.RangeStmt_7760.BlockStmt.BlockStmt.RangeStmt_8916.BlockStmt.newPath
Members
X