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