// Copyright 2011 The Go Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. package generate import ( "internal/testenv" "os" "path/filepath" "reflect" "runtime" "testing" ) type splitTest struct { in string out []string } // Same as above, except including source line number to set type splitTestWithLine struct { in string out []string lineNumber int } const anyLineNo = 0 var splitTests = []splitTest{ {"", nil}, {"x", []string{"x"}}, {" a b\tc ", []string{"a", "b", "c"}}, {` " a " `, []string{" a "}}, {"$GOARCH", []string{runtime.GOARCH}}, {"$GOOS", []string{runtime.GOOS}}, {"$GOFILE", []string{"proc.go"}}, {"$GOPACKAGE", []string{"sys"}}, {"a $XXNOTDEFINEDXX b", []string{"a", "", "b"}}, {"/$XXNOTDEFINED/", []string{"//"}}, {"/$DOLLAR/", []string{"/$/"}}, {"yacc -o $GOARCH/yacc_$GOFILE", []string{"go", "tool", "yacc", "-o", runtime.GOARCH + "/yacc_proc.go"}}, } func TestGenerateCommandParse(t *testing.T) { dir := filepath.Join(testenv.GOROOT(t), "src", "sys") g := &Generator{ r: nil, // Unused here. path: filepath.Join(dir, "proc.go"), dir: dir, file: "proc.go", pkg: "sys", commands: make(map[string][]string), } g.setEnv() g.setShorthand([]string{"-command", "yacc", "go", "tool", "yacc"}) for _, test := range splitTests { // First with newlines. got := g.split("//go:generate " + test.in + "\n") if !reflect.DeepEqual(got, test.out) { t.Errorf("split(%q): got %q expected %q", test.in, got, test.out) } // Then with CRLFs, thank you Windows. got = g.split("//go:generate " + test.in + "\r\n") if !reflect.DeepEqual(got, test.out) { t.Errorf("split(%q): got %q expected %q", test.in, got, test.out) } } } // These environment variables will be undefined before the splitTestWithLine tests var undefEnvList = []string{ "_XYZZY_", } // These environment variables will be defined before the splitTestWithLine tests var defEnvMap = map[string]string{ "_PLUGH_": "SomeVal", "_X": "Y", } // TestGenerateCommandShortHand - similar to TestGenerateCommandParse, // except: // 1. if the result starts with -command, record that shorthand // before moving on to the next test. // 2. If a source line number is specified, set that in the parser // before executing the test. i.e., execute the split as if it // processing that source line. func TestGenerateCommandShorthand(t *testing.T) { dir := filepath.Join(testenv.GOROOT(t), "src", "sys") g := &Generator{ r: nil, // Unused here. path: filepath.Join(dir, "proc.go"), dir: dir, file: "proc.go", pkg: "sys", commands: make(map[string][]string), } var inLine string var expected, got []string g.setEnv() // Set up the system environment variables for i := range undefEnvList { os.Unsetenv(undefEnvList[i]) } for k := range defEnvMap { os.Setenv(k, defEnvMap[k]) } // simple command from environment variable inLine = "//go:generate -command CMD0 \"ab${_X}cd\"" expected = []string{"-command", "CMD0", "abYcd"} got = g.split(inLine + "\n") if !reflect.DeepEqual(got, expected) { t.Errorf("split(%q): got %q expected %q", inLine, got, expected) } // try again, with an extra level of indirection (should leave variable in command) inLine = "//go:generate -command CMD0 \"ab${DOLLAR}{_X}cd\"" expected = []string{"-command", "CMD0", "ab${_X}cd"} got = g.split(inLine + "\n") if !reflect.DeepEqual(got, expected) { t.Errorf("split(%q): got %q expected %q", inLine, got, expected) } // Now the interesting part, record that output as a command g.setShorthand(got) // see that the command still substitutes correctly from env. variable inLine = "//go:generate CMD0" expected = []string{"abYcd"} got = g.split(inLine + "\n") if !reflect.DeepEqual(got, expected) { t.Errorf("split(%q): got %q expected %q", inLine, got, expected) } // Now change the value of $X and see if the recorded definition is // still intact (vs. having the $_X already substituted out) os.Setenv("_X", "Z") inLine = "//go:generate CMD0" expected = []string{"abZcd"} got = g.split(inLine + "\n") if !reflect.DeepEqual(got, expected) { t.Errorf("split(%q): got %q expected %q", inLine, got, expected) } // What if the variable is now undefined? Should be empty substitution. os.Unsetenv("_X") inLine = "//go:generate CMD0" expected = []string{"abcd"} got = g.split(inLine + "\n") if !reflect.DeepEqual(got, expected) { t.Errorf("split(%q): got %q expected %q", inLine, got, expected) } // Try another undefined variable as an extra check os.Unsetenv("_Z") inLine = "//go:generate -command CMD1 \"ab${_Z}cd\"" expected = []string{"-command", "CMD1", "abcd"} got = g.split(inLine + "\n") if !reflect.DeepEqual(got, expected) { t.Errorf("split(%q): got %q expected %q", inLine, got, expected) } g.setShorthand(got) inLine = "//go:generate CMD1" expected = []string{"abcd"} got = g.split(inLine + "\n") if !reflect.DeepEqual(got, expected) { t.Errorf("split(%q): got %q expected %q", inLine, got, expected) } const val = "someNewValue" os.Setenv("_Z", val) // try again with the properly-escaped variable. inLine = "//go:generate -command CMD2 \"ab${DOLLAR}{_Z}cd\"" expected = []string{"-command", "CMD2", "ab${_Z}cd"} got = g.split(inLine + "\n") if !reflect.DeepEqual(got, expected) { t.Errorf("split(%q): got %q expected %q", inLine, got, expected) } g.setShorthand(got) inLine = "//go:generate CMD2" expected = []string{"ab" + val + "cd"} got = g.split(inLine + "\n") if !reflect.DeepEqual(got, expected) { t.Errorf("split(%q): got %q expected %q", inLine, got, expected) } } // Command-related tests for TestGenerateCommandShortHand2 // -- Note line numbers included to check substitutions from "build-in" variable - $GOLINE var splitTestsLines = []splitTestWithLine{ {"-command TEST1 $GOLINE", []string{"-command", "TEST1", "22"}, 22}, {"-command TEST2 ${DOLLAR}GOLINE", []string{"-command", "TEST2", "$GOLINE"}, 26}, {"TEST1", []string{"22"}, 33}, {"TEST2", []string{"66"}, 66}, {"TEST1 ''", []string{"22", "''"}, 99}, {"TEST2 ''", []string{"44", "''"}, 44}, } // TestGenerateCommandShortHand - similar to TestGenerateCommandParse, // except: // 1. if the result starts with -command, record that shorthand // before moving on to the next test. // 2. If a source line number is specified, set that in the parser // before executing the test. i.e., execute the split as if it // processing that source line. func TestGenerateCommandShortHand2(t *testing.T) { dir := filepath.Join(testenv.GOROOT(t), "src", "sys") g := &Generator{ r: nil, // Unused here. path: filepath.Join(dir, "proc.go"), dir: dir, file: "proc.go", pkg: "sys", commands: make(map[string][]string), } g.setEnv() for _, test := range splitTestsLines { // if the test specified a line number, reflect that if test.lineNumber != anyLineNo { g.lineNum = test.lineNumber g.setEnv() } // First with newlines. got := g.split("//go:generate " + test.in + "\n") if !reflect.DeepEqual(got, test.out) { t.Errorf("split(%q): got %q expected %q", test.in, got, test.out) } // Then with CRLFs, thank you Windows. got = g.split("//go:generate " + test.in + "\r\n") if !reflect.DeepEqual(got, test.out) { t.Errorf("split(%q): got %q expected %q", test.in, got, test.out) } if got[0] == "-command" { // record commands g.setShorthand(got) } } }