mkmerge_test.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. // Copyright 2020 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. // +build ignore
  5. // Test cases for mkmerge.go.
  6. // Usage:
  7. // $ go test mkmerge.go mkmerge_test.go
  8. package main
  9. import (
  10. "bytes"
  11. "fmt"
  12. "go/parser"
  13. "go/token"
  14. "html/template"
  15. "strings"
  16. "testing"
  17. )
  18. func TestImports(t *testing.T) {
  19. t.Run("importName", func(t *testing.T) {
  20. cases := []struct {
  21. src string
  22. ident string
  23. }{
  24. {`"syscall"`, "syscall"},
  25. {`. "foobar"`, "."},
  26. {`"go/ast"`, "ast"},
  27. {`moo "go/format"`, "moo"},
  28. {`. "go/token"`, "."},
  29. {`"golang.org/x/sys/unix"`, "unix"},
  30. {`nix "golang.org/x/sys/unix"`, "nix"},
  31. {`_ "golang.org/x/sys/unix"`, "_"},
  32. }
  33. for _, c := range cases {
  34. pkgSrc := fmt.Sprintf("package main\nimport %s", c.src)
  35. f, err := parser.ParseFile(token.NewFileSet(), "", pkgSrc, parser.ImportsOnly)
  36. if err != nil {
  37. t.Error(err)
  38. continue
  39. }
  40. if len(f.Imports) != 1 {
  41. t.Errorf("Got %d imports, expected 1", len(f.Imports))
  42. continue
  43. }
  44. got, err := importName(f.Imports[0])
  45. if err != nil {
  46. t.Fatal(err)
  47. }
  48. if got != c.ident {
  49. t.Errorf("Got %q, expected %q", got, c.ident)
  50. }
  51. }
  52. })
  53. t.Run("filterImports", func(t *testing.T) {
  54. cases := []struct{ before, after string }{
  55. {`package test
  56. import (
  57. "foo"
  58. "bar"
  59. )`,
  60. "package test\n"},
  61. {`package test
  62. import (
  63. "foo"
  64. "bar"
  65. )
  66. func useFoo() { foo.Usage() }`,
  67. `package test
  68. import (
  69. "foo"
  70. )
  71. func useFoo() { foo.Usage() }
  72. `},
  73. }
  74. for _, c := range cases {
  75. got, err := filterImports([]byte(c.before))
  76. if err != nil {
  77. t.Error(err)
  78. }
  79. if string(got) != c.after {
  80. t.Errorf("Got:\n%s\nExpected:\n%s\n", got, c.after)
  81. }
  82. }
  83. })
  84. }
  85. func TestMerge(t *testing.T) {
  86. // Input architecture files
  87. inTmpl := template.Must(template.New("input").Parse(`
  88. // Package comments
  89. // build directives for arch{{.}}
  90. // +build goos,arch{{.}}
  91. package main
  92. /*
  93. #include <stdint.h>
  94. #include <stddef.h>
  95. int utimes(uintptr_t, uintptr_t);
  96. int utimensat(int, uintptr_t, uintptr_t, int);
  97. */
  98. import "C"
  99. // The imports
  100. import (
  101. "commonDep"
  102. "uniqueDep{{.}}"
  103. )
  104. // Vars
  105. var (
  106. commonVar = commonDep.Use("common")
  107. uniqueVar{{.}} = "unique{{.}}"
  108. )
  109. // Common free standing comment
  110. // Common comment
  111. const COMMON_INDEPENDENT = 1234
  112. const UNIQUE_INDEPENDENT_{{.}} = "UNIQUE_INDEPENDENT_{{.}}"
  113. // Group comment
  114. const (
  115. COMMON_GROUP = "COMMON_GROUP"
  116. UNIQUE_GROUP_{{.}} = "UNIQUE_GROUP_{{.}}"
  117. )
  118. // Group2 comment
  119. const (
  120. UNIQUE_GROUP21_{{.}} = "UNIQUE_GROUP21_{{.}}"
  121. UNIQUE_GROUP22_{{.}} = "UNIQUE_GROUP22_{{.}}"
  122. )
  123. // Group3 comment
  124. const (
  125. sub1Common1 = 11
  126. sub1Unique2{{.}} = 12
  127. sub1Common3_LONG = 13
  128. sub2Unique1{{.}} = 21
  129. sub2Common2 = 22
  130. sub2Common3 = 23
  131. sub2Unique4{{.}} = 24
  132. )
  133. type commonInt int
  134. type uniqueInt{{.}} int
  135. func commonF() string {
  136. return commonDep.Use("common")
  137. }
  138. func uniqueF() string {
  139. C.utimes(0, 0)
  140. return uniqueDep{{.}}.Use("{{.}}")
  141. }
  142. // Group4 comment
  143. const (
  144. sub3Common1 = 31
  145. sub3Unique2{{.}} = 32
  146. sub3Unique3{{.}} = 33
  147. sub3Common4 = 34
  148. sub4Common1, sub4Unique2{{.}} = 41, 42
  149. sub4Unique3{{.}}, sub4Common4 = 43, 44
  150. )
  151. `))
  152. // Filtered architecture files
  153. outTmpl := template.Must(template.New("output").Parse(`// Package comments
  154. // build directives for arch{{.}}
  155. // +build goos,arch{{.}}
  156. package main
  157. /*
  158. #include <stdint.h>
  159. #include <stddef.h>
  160. int utimes(uintptr_t, uintptr_t);
  161. int utimensat(int, uintptr_t, uintptr_t, int);
  162. */
  163. import "C"
  164. // The imports
  165. import (
  166. "commonDep"
  167. "uniqueDep{{.}}"
  168. )
  169. // Vars
  170. var (
  171. commonVar = commonDep.Use("common")
  172. uniqueVar{{.}} = "unique{{.}}"
  173. )
  174. const UNIQUE_INDEPENDENT_{{.}} = "UNIQUE_INDEPENDENT_{{.}}"
  175. // Group comment
  176. const (
  177. UNIQUE_GROUP_{{.}} = "UNIQUE_GROUP_{{.}}"
  178. )
  179. // Group2 comment
  180. const (
  181. UNIQUE_GROUP21_{{.}} = "UNIQUE_GROUP21_{{.}}"
  182. UNIQUE_GROUP22_{{.}} = "UNIQUE_GROUP22_{{.}}"
  183. )
  184. // Group3 comment
  185. const (
  186. sub1Unique2{{.}} = 12
  187. sub2Unique1{{.}} = 21
  188. sub2Unique4{{.}} = 24
  189. )
  190. type uniqueInt{{.}} int
  191. func uniqueF() string {
  192. C.utimes(0, 0)
  193. return uniqueDep{{.}}.Use("{{.}}")
  194. }
  195. // Group4 comment
  196. const (
  197. sub3Unique2{{.}} = 32
  198. sub3Unique3{{.}} = 33
  199. sub4Common1, sub4Unique2{{.}} = 41, 42
  200. sub4Unique3{{.}}, sub4Common4 = 43, 44
  201. )
  202. `))
  203. const mergedFile = `// Package comments
  204. package main
  205. // The imports
  206. import (
  207. "commonDep"
  208. )
  209. // Common free standing comment
  210. // Common comment
  211. const COMMON_INDEPENDENT = 1234
  212. // Group comment
  213. const (
  214. COMMON_GROUP = "COMMON_GROUP"
  215. )
  216. // Group3 comment
  217. const (
  218. sub1Common1 = 11
  219. sub1Common3_LONG = 13
  220. sub2Common2 = 22
  221. sub2Common3 = 23
  222. )
  223. type commonInt int
  224. func commonF() string {
  225. return commonDep.Use("common")
  226. }
  227. // Group4 comment
  228. const (
  229. sub3Common1 = 31
  230. sub3Common4 = 34
  231. )
  232. `
  233. // Generate source code for different "architectures"
  234. var inFiles, outFiles []srcFile
  235. for _, arch := range strings.Fields("A B C D") {
  236. buf := new(bytes.Buffer)
  237. err := inTmpl.Execute(buf, arch)
  238. if err != nil {
  239. t.Fatal(err)
  240. }
  241. inFiles = append(inFiles, srcFile{"file" + arch, buf.Bytes()})
  242. buf = new(bytes.Buffer)
  243. err = outTmpl.Execute(buf, arch)
  244. if err != nil {
  245. t.Fatal(err)
  246. }
  247. outFiles = append(outFiles, srcFile{"file" + arch, buf.Bytes()})
  248. }
  249. t.Run("getCodeSet", func(t *testing.T) {
  250. got, err := getCodeSet(inFiles[0].src)
  251. if err != nil {
  252. t.Fatal(err)
  253. }
  254. expectedElems := []codeElem{
  255. {token.COMMENT, "Package comments\n"},
  256. {token.COMMENT, "build directives for archA\n"},
  257. {token.COMMENT, "+build goos,archA\n"},
  258. {token.CONST, `COMMON_INDEPENDENT = 1234`},
  259. {token.CONST, `UNIQUE_INDEPENDENT_A = "UNIQUE_INDEPENDENT_A"`},
  260. {token.CONST, `COMMON_GROUP = "COMMON_GROUP"`},
  261. {token.CONST, `UNIQUE_GROUP_A = "UNIQUE_GROUP_A"`},
  262. {token.CONST, `UNIQUE_GROUP21_A = "UNIQUE_GROUP21_A"`},
  263. {token.CONST, `UNIQUE_GROUP22_A = "UNIQUE_GROUP22_A"`},
  264. {token.CONST, `sub1Common1 = 11`},
  265. {token.CONST, `sub1Unique2A = 12`},
  266. {token.CONST, `sub1Common3_LONG = 13`},
  267. {token.CONST, `sub2Unique1A = 21`},
  268. {token.CONST, `sub2Common2 = 22`},
  269. {token.CONST, `sub2Common3 = 23`},
  270. {token.CONST, `sub2Unique4A = 24`},
  271. {token.CONST, `sub3Common1 = 31`},
  272. {token.CONST, `sub3Unique2A = 32`},
  273. {token.CONST, `sub3Unique3A = 33`},
  274. {token.CONST, `sub3Common4 = 34`},
  275. {token.CONST, `sub4Common1, sub4Unique2A = 41, 42`},
  276. {token.CONST, `sub4Unique3A, sub4Common4 = 43, 44`},
  277. {token.TYPE, `commonInt int`},
  278. {token.TYPE, `uniqueIntA int`},
  279. {token.FUNC, `func commonF() string {
  280. return commonDep.Use("common")
  281. }`},
  282. {token.FUNC, `func uniqueF() string {
  283. C.utimes(0, 0)
  284. return uniqueDepA.Use("A")
  285. }`},
  286. }
  287. expected := newCodeSet()
  288. for _, d := range expectedElems {
  289. expected.add(d)
  290. }
  291. if len(got.set) != len(expected.set) {
  292. t.Errorf("Got %d codeElems, expected %d", len(got.set), len(expected.set))
  293. }
  294. for expElem := range expected.set {
  295. if !got.has(expElem) {
  296. t.Errorf("Didn't get expected codeElem %#v", expElem)
  297. }
  298. }
  299. for gotElem := range got.set {
  300. if !expected.has(gotElem) {
  301. t.Errorf("Got unexpected codeElem %#v", gotElem)
  302. }
  303. }
  304. })
  305. t.Run("getCommonSet", func(t *testing.T) {
  306. got, err := getCommonSet(inFiles)
  307. if err != nil {
  308. t.Fatal(err)
  309. }
  310. expected := newCodeSet()
  311. expected.add(codeElem{token.COMMENT, "Package comments\n"})
  312. expected.add(codeElem{token.CONST, `COMMON_INDEPENDENT = 1234`})
  313. expected.add(codeElem{token.CONST, `COMMON_GROUP = "COMMON_GROUP"`})
  314. expected.add(codeElem{token.CONST, `sub1Common1 = 11`})
  315. expected.add(codeElem{token.CONST, `sub1Common3_LONG = 13`})
  316. expected.add(codeElem{token.CONST, `sub2Common2 = 22`})
  317. expected.add(codeElem{token.CONST, `sub2Common3 = 23`})
  318. expected.add(codeElem{token.CONST, `sub3Common1 = 31`})
  319. expected.add(codeElem{token.CONST, `sub3Common4 = 34`})
  320. expected.add(codeElem{token.TYPE, `commonInt int`})
  321. expected.add(codeElem{token.FUNC, `func commonF() string {
  322. return commonDep.Use("common")
  323. }`})
  324. if len(got.set) != len(expected.set) {
  325. t.Errorf("Got %d codeElems, expected %d", len(got.set), len(expected.set))
  326. }
  327. for expElem := range expected.set {
  328. if !got.has(expElem) {
  329. t.Errorf("Didn't get expected codeElem %#v", expElem)
  330. }
  331. }
  332. for gotElem := range got.set {
  333. if !expected.has(gotElem) {
  334. t.Errorf("Got unexpected codeElem %#v", gotElem)
  335. }
  336. }
  337. })
  338. t.Run("filter(keepCommon)", func(t *testing.T) {
  339. commonSet, err := getCommonSet(inFiles)
  340. if err != nil {
  341. t.Fatal(err)
  342. }
  343. got, err := filter(inFiles[0].src, commonSet.keepCommon)
  344. expected := []byte(mergedFile)
  345. if !bytes.Equal(got, expected) {
  346. t.Errorf("Got:\n%s\nExpected:\n%s", addLineNr(got), addLineNr(expected))
  347. diffLines(t, got, expected)
  348. }
  349. })
  350. t.Run("filter(keepArchSpecific)", func(t *testing.T) {
  351. commonSet, err := getCommonSet(inFiles)
  352. if err != nil {
  353. t.Fatal(err)
  354. }
  355. for i := range inFiles {
  356. got, err := filter(inFiles[i].src, commonSet.keepArchSpecific)
  357. if err != nil {
  358. t.Fatal(err)
  359. }
  360. expected := outFiles[i].src
  361. if !bytes.Equal(got, expected) {
  362. t.Errorf("Got:\n%s\nExpected:\n%s", addLineNr(got), addLineNr(expected))
  363. diffLines(t, got, expected)
  364. }
  365. }
  366. })
  367. }
  368. func TestMergedName(t *testing.T) {
  369. t.Run("getValidGOOS", func(t *testing.T) {
  370. testcases := []struct {
  371. filename, goos string
  372. ok bool
  373. }{
  374. {"zerrors_aix.go", "aix", true},
  375. {"zerrors_darwin.go", "darwin", true},
  376. {"zerrors_dragonfly.go", "dragonfly", true},
  377. {"zerrors_freebsd.go", "freebsd", true},
  378. {"zerrors_linux.go", "linux", true},
  379. {"zerrors_netbsd.go", "netbsd", true},
  380. {"zerrors_openbsd.go", "openbsd", true},
  381. {"zerrors_solaris.go", "solaris", true},
  382. {"zerrors_multics.go", "", false},
  383. }
  384. for _, tc := range testcases {
  385. goos, ok := getValidGOOS(tc.filename)
  386. if goos != tc.goos {
  387. t.Errorf("got GOOS %q, expected %q", goos, tc.goos)
  388. }
  389. if ok != tc.ok {
  390. t.Errorf("got ok %v, expected %v", ok, tc.ok)
  391. }
  392. }
  393. })
  394. }
  395. // Helper functions to diff test sources
  396. func diffLines(t *testing.T, got, expected []byte) {
  397. t.Helper()
  398. gotLines := bytes.Split(got, []byte{'\n'})
  399. expLines := bytes.Split(expected, []byte{'\n'})
  400. i := 0
  401. for i < len(gotLines) && i < len(expLines) {
  402. if !bytes.Equal(gotLines[i], expLines[i]) {
  403. t.Errorf("Line %d: Got:\n%q\nExpected:\n%q", i+1, gotLines[i], expLines[i])
  404. return
  405. }
  406. i++
  407. }
  408. if i < len(gotLines) && i >= len(expLines) {
  409. t.Errorf("Line %d: got %q, expected EOF", i+1, gotLines[i])
  410. }
  411. if i >= len(gotLines) && i < len(expLines) {
  412. t.Errorf("Line %d: got EOF, expected %q", i+1, gotLines[i])
  413. }
  414. }
  415. func addLineNr(src []byte) []byte {
  416. lines := bytes.Split(src, []byte("\n"))
  417. for i, line := range lines {
  418. lines[i] = []byte(fmt.Sprintf("%d: %s", i+1, line))
  419. }
  420. return bytes.Join(lines, []byte("\n"))
  421. }