parse_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. /*
  2. Copyright 2015 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package parser_test
  14. import (
  15. "bytes"
  16. "path/filepath"
  17. "reflect"
  18. "testing"
  19. "text/template"
  20. "k8s.io/kubernetes/cmd/libs/go2idl/namer"
  21. "k8s.io/kubernetes/cmd/libs/go2idl/parser"
  22. "k8s.io/kubernetes/cmd/libs/go2idl/types"
  23. )
  24. func construct(t *testing.T, files map[string]string, testNamer namer.Namer) (*parser.Builder, types.Universe, []*types.Type) {
  25. b := parser.New()
  26. for name, src := range files {
  27. if err := b.AddFile(filepath.Dir(name), name, []byte(src)); err != nil {
  28. t.Fatal(err)
  29. }
  30. }
  31. u, err := b.FindTypes()
  32. if err != nil {
  33. t.Fatal(err)
  34. }
  35. orderer := namer.Orderer{Namer: testNamer}
  36. o := orderer.OrderUniverse(u)
  37. return b, u, o
  38. }
  39. func TestBuilder(t *testing.T) {
  40. var testFiles = map[string]string{
  41. "base/foo/proto/foo.go": `
  42. package foo
  43. import (
  44. "base/common/proto"
  45. )
  46. type Blah struct {
  47. common.Object
  48. Count int64
  49. Frobbers map[string]*Frobber
  50. Baz []Object
  51. Nickname *string
  52. NumberIsAFavorite map[int]bool
  53. }
  54. type Frobber struct {
  55. Name string
  56. Amount int64
  57. }
  58. type Object struct {
  59. common.Object
  60. }
  61. func AFunc(obj1 common.Object, obj2 Object) Frobber {
  62. }
  63. var AVar Frobber
  64. var (
  65. AnotherVar = Frobber{}
  66. )
  67. `,
  68. "base/common/proto/common.go": `
  69. package common
  70. type Object struct {
  71. ID int64
  72. }
  73. `,
  74. }
  75. var tmplText = `
  76. package o
  77. {{define "Struct"}}type {{Name .}} interface { {{range $m := .Members}}{{$n := Name $m.Type}}
  78. {{if $m.Embedded}}{{$n}}{{else}}{{$m.Name}}() {{$n}}{{if $m.Type.Elem}}{{else}}
  79. Set{{$m.Name}}({{$n}}){{end}}{{end}}{{end}}
  80. }
  81. {{end}}
  82. {{define "Func"}}{{$s := .Underlying.Signature}}var {{Name .}} func({{range $index,$elem := $s.Parameters}}{{if $index}}, {{end}}{{Raw $elem}}{{end}}) {{if $s.Results|len |gt 1}}({{end}}{{range $index,$elem := $s.Results}}{{if $index}}, {{end}}{{Raw .}}{{end}}{{if $s.Results|len |gt 1}}){{end}} = {{Raw .}}
  83. {{end}}
  84. {{define "Var"}}{{$t := .Underlying}}var {{Name .}} {{Raw $t}} = {{Raw .}}
  85. {{end}}
  86. {{range $t := .}}{{if eq $t.Kind "Struct"}}{{template "Struct" $t}}{{end}}{{end}}
  87. {{range $t := .}}{{if eq $t.Kind "DeclarationOf"}}{{if eq $t.Underlying.Kind "Func"}}{{template "Func" $t}}{{end}}{{end}}{{end}}
  88. {{range $t := .}}{{if eq $t.Kind "DeclarationOf"}}{{if ne $t.Underlying.Kind "Func"}}{{template "Var" $t}}{{end}}{{end}}{{end}}`
  89. var expect = `
  90. package o
  91. type CommonObject interface {
  92. ID() Int64
  93. SetID(Int64)
  94. }
  95. type FooBlah interface {
  96. CommonObject
  97. Count() Int64
  98. SetCount(Int64)
  99. Frobbers() MapStringToPointerFooFrobber
  100. Baz() SliceFooObject
  101. Nickname() PointerString
  102. NumberIsAFavorite() MapIntToBool
  103. }
  104. type FooFrobber interface {
  105. Name() String
  106. SetName(String)
  107. Amount() Int64
  108. SetAmount(Int64)
  109. }
  110. type FooObject interface {
  111. CommonObject
  112. }
  113. var FooAFunc func(proto.Object, proto.Object) proto.Frobber = proto.AFunc
  114. var FooAVar proto.Frobber = proto.AVar
  115. var FooAnotherVar proto.Frobber = proto.AnotherVar
  116. `
  117. testNamer := namer.NewPublicNamer(1, "proto")
  118. rawNamer := namer.NewRawNamer("o", nil)
  119. _, u, o := construct(t, testFiles, testNamer)
  120. t.Logf("\n%v\n\n", o)
  121. args := map[string]interface{}{
  122. "Name": testNamer.Name,
  123. "Raw": rawNamer.Name,
  124. }
  125. tmpl := template.Must(
  126. template.New("").
  127. Funcs(args).
  128. Parse(tmplText),
  129. )
  130. buf := &bytes.Buffer{}
  131. tmpl.Execute(buf, o)
  132. if e, a := expect, buf.String(); e != a {
  133. t.Errorf("Wanted, got:\n%v\n-----\n%v\n", e, a)
  134. }
  135. if p := u.Package("base/foo/proto"); !p.HasImport("base/common/proto") {
  136. t.Errorf("Unexpected lack of import line: %s", p.Imports)
  137. }
  138. }
  139. func TestStructParse(t *testing.T) {
  140. var structTest = map[string]string{
  141. "base/foo/proto/foo.go": `
  142. package foo
  143. // Blah is a test.
  144. // A test, I tell you.
  145. type Blah struct {
  146. // A is the first field.
  147. A int64 ` + "`" + `json:"a"` + "`" + `
  148. // B is the second field.
  149. // Multiline comments work.
  150. B string ` + "`" + `json:"b"` + "`" + `
  151. }
  152. `,
  153. }
  154. _, u, o := construct(t, structTest, namer.NewPublicNamer(0))
  155. t.Logf("%#v", o)
  156. blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "Blah"})
  157. if blahT == nil {
  158. t.Fatal("type not found")
  159. }
  160. if e, a := types.Struct, blahT.Kind; e != a {
  161. t.Errorf("struct kind wrong, wanted %v, got %v", e, a)
  162. }
  163. if e, a := []string{"Blah is a test.", "A test, I tell you."}, blahT.CommentLines; !reflect.DeepEqual(e, a) {
  164. t.Errorf("struct comment wrong, wanted %q, got %q", e, a)
  165. }
  166. m := types.Member{
  167. Name: "B",
  168. Embedded: false,
  169. CommentLines: []string{"B is the second field.", "Multiline comments work."},
  170. Tags: `json:"b"`,
  171. Type: types.String,
  172. }
  173. if e, a := m, blahT.Members[1]; !reflect.DeepEqual(e, a) {
  174. t.Errorf("wanted, got:\n%#v\n%#v", e, a)
  175. }
  176. }
  177. func TestParseSecondClosestCommentLines(t *testing.T) {
  178. const fileName = "base/foo/proto/foo.go"
  179. testCases := []struct {
  180. testFile map[string]string
  181. expected []string
  182. }{
  183. {
  184. map[string]string{fileName: `package foo
  185. // Blah's SecondClosestCommentLines.
  186. // Another line.
  187. // Blah is a test.
  188. // A test, I tell you.
  189. type Blah struct {
  190. a int
  191. }
  192. `},
  193. []string{"Blah's SecondClosestCommentLines.", "Another line."},
  194. },
  195. {
  196. map[string]string{fileName: `package foo
  197. // Blah's SecondClosestCommentLines.
  198. // Another line.
  199. type Blah struct {
  200. a int
  201. }
  202. `},
  203. []string{"Blah's SecondClosestCommentLines.", "Another line."},
  204. },
  205. }
  206. for _, test := range testCases {
  207. _, u, o := construct(t, test.testFile, namer.NewPublicNamer(0))
  208. t.Logf("%#v", o)
  209. blahT := u.Type(types.Name{Package: "base/foo/proto", Name: "Blah"})
  210. if e, a := test.expected, blahT.SecondClosestCommentLines; !reflect.DeepEqual(e, a) {
  211. t.Errorf("struct second closest comment wrong, wanted %q, got %q", e, a)
  212. }
  213. }
  214. }
  215. func TestTypeKindParse(t *testing.T) {
  216. var testFiles = map[string]string{
  217. "a/foo.go": "package a\ntype Test string\n",
  218. "b/foo.go": "package b\ntype Test map[int]string\n",
  219. "c/foo.go": "package c\ntype Test []string\n",
  220. "d/foo.go": "package d\ntype Test struct{a int; b struct{a int}; c map[int]string; d *string}\n",
  221. "e/foo.go": "package e\ntype Test *string\n",
  222. "f/foo.go": `
  223. package f
  224. import (
  225. "a"
  226. "b"
  227. )
  228. type Test []a.Test
  229. type Test2 *a.Test
  230. type Test3 map[a.Test]b.Test
  231. type Test4 struct {
  232. a struct {a a.Test; b b.Test}
  233. b map[a.Test]b.Test
  234. c *a.Test
  235. d []a.Test
  236. e []string
  237. }
  238. `,
  239. "g/foo.go": `
  240. package g
  241. type Test func(a, b string) (c, d string)
  242. func (t Test) Method(a, b string) (c, d string) { return t(a, b) }
  243. type Interface interface{Method(a, b string) (c, d string)}
  244. `,
  245. }
  246. // Check that the right types are found, and the namers give the expected names.
  247. assertions := []struct {
  248. Package, Name string
  249. k types.Kind
  250. names []string
  251. }{
  252. {
  253. Package: "a", Name: "Test", k: types.Alias,
  254. names: []string{"Test", "ATest", "test", "aTest", "a.Test"},
  255. },
  256. {
  257. Package: "b", Name: "Test", k: types.Map,
  258. names: []string{"Test", "BTest", "test", "bTest", "b.Test"},
  259. },
  260. {
  261. Package: "c", Name: "Test", k: types.Slice,
  262. names: []string{"Test", "CTest", "test", "cTest", "c.Test"},
  263. },
  264. {
  265. Package: "d", Name: "Test", k: types.Struct,
  266. names: []string{"Test", "DTest", "test", "dTest", "d.Test"},
  267. },
  268. {
  269. Package: "e", Name: "Test", k: types.Pointer,
  270. names: []string{"Test", "ETest", "test", "eTest", "e.Test"},
  271. },
  272. {
  273. Package: "f", Name: "Test", k: types.Slice,
  274. names: []string{"Test", "FTest", "test", "fTest", "f.Test"},
  275. },
  276. {
  277. Package: "g", Name: "Test", k: types.Func,
  278. names: []string{"Test", "GTest", "test", "gTest", "g.Test"},
  279. },
  280. {
  281. Package: "g", Name: "Interface", k: types.Interface,
  282. names: []string{"Interface", "GInterface", "interface", "gInterface", "g.Interface"},
  283. },
  284. {
  285. Package: "", Name: "string", k: types.Builtin,
  286. names: []string{"String", "String", "string", "string", "string"},
  287. },
  288. {
  289. Package: "", Name: "int", k: types.Builtin,
  290. names: []string{"Int", "Int", "int", "int", "int"},
  291. },
  292. {
  293. Package: "", Name: "struct{a int}", k: types.Struct,
  294. names: []string{"StructInt", "StructInt", "structInt", "structInt", "struct{a int}"},
  295. },
  296. {
  297. Package: "", Name: "struct{a a.Test; b b.Test}", k: types.Struct,
  298. names: []string{"StructTestTest", "StructATestBTest", "structTestTest", "structATestBTest", "struct{a a.Test; b b.Test}"},
  299. },
  300. {
  301. Package: "", Name: "map[int]string", k: types.Map,
  302. names: []string{"MapIntToString", "MapIntToString", "mapIntToString", "mapIntToString", "map[int]string"},
  303. },
  304. {
  305. Package: "", Name: "map[a.Test]b.Test", k: types.Map,
  306. names: []string{"MapTestToTest", "MapATestToBTest", "mapTestToTest", "mapATestToBTest", "map[a.Test]b.Test"},
  307. },
  308. {
  309. Package: "", Name: "[]string", k: types.Slice,
  310. names: []string{"SliceString", "SliceString", "sliceString", "sliceString", "[]string"},
  311. },
  312. {
  313. Package: "", Name: "[]a.Test", k: types.Slice,
  314. names: []string{"SliceTest", "SliceATest", "sliceTest", "sliceATest", "[]a.Test"},
  315. },
  316. {
  317. Package: "", Name: "*string", k: types.Pointer,
  318. names: []string{"PointerString", "PointerString", "pointerString", "pointerString", "*string"},
  319. },
  320. {
  321. Package: "", Name: "*a.Test", k: types.Pointer,
  322. names: []string{"PointerTest", "PointerATest", "pointerTest", "pointerATest", "*a.Test"},
  323. },
  324. }
  325. namers := []namer.Namer{
  326. namer.NewPublicNamer(0),
  327. namer.NewPublicNamer(1),
  328. namer.NewPrivateNamer(0),
  329. namer.NewPrivateNamer(1),
  330. namer.NewRawNamer("", nil),
  331. }
  332. for nameIndex, namer := range namers {
  333. _, u, _ := construct(t, testFiles, namer)
  334. t.Logf("Found types:\n")
  335. for pkgName, pkg := range u {
  336. for typeName, cur := range pkg.Types {
  337. t.Logf("%q-%q: %s %s", pkgName, typeName, cur.Name, cur.Kind)
  338. }
  339. }
  340. t.Logf("\n\n")
  341. for _, item := range assertions {
  342. n := types.Name{Package: item.Package, Name: item.Name}
  343. thisType := u.Type(n)
  344. if thisType == nil {
  345. t.Errorf("type %s not found", n)
  346. continue
  347. }
  348. underlyingType := thisType
  349. if item.k != types.Alias && thisType.Kind == types.Alias {
  350. underlyingType = thisType.Underlying
  351. if underlyingType == nil {
  352. t.Errorf("underlying type %s not found", n)
  353. continue
  354. }
  355. }
  356. if e, a := item.k, underlyingType.Kind; e != a {
  357. t.Errorf("%v-%s: type kind wrong, wanted %v, got %v (%#v)", nameIndex, n, e, a, underlyingType)
  358. }
  359. if e, a := item.names[nameIndex], namer.Name(thisType); e != a {
  360. t.Errorf("%v-%s: Expected %q, got %q", nameIndex, n, e, a)
  361. }
  362. }
  363. // Also do some one-off checks
  364. gtest := u.Type(types.Name{Package: "g", Name: "Test"})
  365. if e, a := 1, len(gtest.Methods); e != a {
  366. t.Errorf("expected %v but found %v methods: %#v", e, a, gtest)
  367. }
  368. iface := u.Type(types.Name{Package: "g", Name: "Interface"})
  369. if e, a := 1, len(iface.Methods); e != a {
  370. t.Errorf("expected %v but found %v methods: %#v", e, a, iface)
  371. }
  372. }
  373. }