| // +build !windows |
| // TODO(jen20): These need fixing on Windows but fmt is not used right now |
| // and red CI is making it harder to process other bugs, so ignore until |
| // we get around to fixing them. |
| |
| package fmtcmd |
| |
| import ( |
| "bytes" |
| "fmt" |
| "io/ioutil" |
| "os" |
| "path/filepath" |
| "reflect" |
| "regexp" |
| "sort" |
| "syscall" |
| "testing" |
| |
| "google3/third_party/golang/hashicorp/hcl/testhelper/testhelper" |
| ) |
| |
| var fixtureExtensions = []string{"hcl"} |
| |
| func init() { |
| sort.Sort(ByFilename(fixtures)) |
| } |
| |
| func TestIsValidFile(t *testing.T) { |
| const fixtureDir = "./test-fixtures" |
| |
| cases := []struct { |
| Path string |
| Expected bool |
| }{ |
| {"good.hcl", true}, |
| {".hidden.ignore", false}, |
| {"file.ignore", false}, |
| {"dir.ignore", false}, |
| } |
| |
| for _, tc := range cases { |
| file, err := os.Stat(filepath.Join(fixtureDir, tc.Path)) |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| |
| if res := isValidFile(file, fixtureExtensions); res != tc.Expected { |
| t.Errorf("want: %t, got: %t", tc.Expected, res) |
| } |
| } |
| } |
| |
| func TestRunMultiplePaths(t *testing.T) { |
| path1, err := renderFixtures("") |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| defer os.RemoveAll(path1) |
| path2, err := renderFixtures("") |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| defer os.RemoveAll(path2) |
| |
| var expectedOut bytes.Buffer |
| for _, path := range []string{path1, path2} { |
| for _, fixture := range fixtures { |
| if !bytes.Equal(fixture.golden, fixture.input) { |
| expectedOut.WriteString(filepath.Join(path, fixture.filename) + "\n") |
| } |
| } |
| } |
| |
| _, stdout := mockIO() |
| err = Run( |
| []string{path1, path2}, |
| fixtureExtensions, |
| nil, stdout, |
| Options{ |
| List: true, |
| }, |
| ) |
| |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| if stdout.String() != expectedOut.String() { |
| t.Errorf("stdout want:\n%s\ngot:\n%s", expectedOut.String(), stdout.String()) |
| } |
| } |
| |
| func TestRunSubDirectories(t *testing.T) { |
| pathParent, err := ioutil.TempDir("", "") |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| defer os.RemoveAll(pathParent) |
| |
| path1, err := renderFixtures(pathParent) |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| path2, err := renderFixtures(pathParent) |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| |
| paths := []string{path1, path2} |
| sort.Strings(paths) |
| |
| var expectedOut bytes.Buffer |
| for _, path := range paths { |
| for _, fixture := range fixtures { |
| if !bytes.Equal(fixture.golden, fixture.input) { |
| expectedOut.WriteString(filepath.Join(path, fixture.filename) + "\n") |
| } |
| } |
| } |
| |
| _, stdout := mockIO() |
| err = Run( |
| []string{pathParent}, |
| fixtureExtensions, |
| nil, stdout, |
| Options{ |
| List: true, |
| }, |
| ) |
| |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| if stdout.String() != expectedOut.String() { |
| t.Errorf("stdout want:\n%s\ngot:\n%s", expectedOut.String(), stdout.String()) |
| } |
| } |
| |
| func TestRunStdin(t *testing.T) { |
| var expectedOut bytes.Buffer |
| for i, fixture := range fixtures { |
| if i != 0 { |
| expectedOut.WriteString("\n") |
| } |
| expectedOut.Write(fixture.golden) |
| } |
| |
| stdin, stdout := mockIO() |
| for _, fixture := range fixtures { |
| stdin.Write(fixture.input) |
| } |
| |
| err := Run( |
| []string{}, |
| fixtureExtensions, |
| stdin, stdout, |
| Options{}, |
| ) |
| |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| if !bytes.Equal(stdout.Bytes(), expectedOut.Bytes()) { |
| t.Errorf("stdout want:\n%s\ngot:\n%s", expectedOut.String(), stdout.String()) |
| } |
| } |
| |
| func TestRunStdinAndWrite(t *testing.T) { |
| var expectedOut = []byte{} |
| |
| stdin, stdout := mockIO() |
| stdin.WriteString("") |
| err := Run( |
| []string{}, []string{}, |
| stdin, stdout, |
| Options{ |
| Write: true, |
| }, |
| ) |
| |
| if err != ErrWriteStdin { |
| t.Errorf("error want:\n%s\ngot:\n%s", ErrWriteStdin, err) |
| } |
| if !bytes.Equal(stdout.Bytes(), expectedOut) { |
| t.Errorf("stdout want:\n%s\ngot:\n%s", expectedOut, stdout) |
| } |
| } |
| |
| func TestRunFileError(t *testing.T) { |
| path, err := ioutil.TempDir("", "") |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| defer os.RemoveAll(path) |
| filename := filepath.Join(path, "unreadable.hcl") |
| |
| var expectedError = &os.PathError{ |
| Op: "open", |
| Path: filename, |
| Err: syscall.EACCES, |
| } |
| |
| err = ioutil.WriteFile(filename, []byte{}, 0000) |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| |
| _, stdout := mockIO() |
| err = Run( |
| []string{path}, |
| fixtureExtensions, |
| nil, stdout, |
| Options{}, |
| ) |
| |
| if !reflect.DeepEqual(err, expectedError) { |
| t.Errorf("error want: %#v, got: %#v", expectedError, err) |
| } |
| } |
| |
| func TestRunNoOptions(t *testing.T) { |
| path, err := renderFixtures("") |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| defer os.RemoveAll(path) |
| |
| var expectedOut bytes.Buffer |
| for _, fixture := range fixtures { |
| expectedOut.Write(fixture.golden) |
| } |
| |
| _, stdout := mockIO() |
| err = Run( |
| []string{path}, |
| fixtureExtensions, |
| nil, stdout, |
| Options{}, |
| ) |
| |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| if stdout.String() != expectedOut.String() { |
| t.Errorf("stdout want:\n%s\ngot:\n%s", expectedOut.String(), stdout.String()) |
| } |
| } |
| |
| func TestRunList(t *testing.T) { |
| path, err := renderFixtures("") |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| defer os.RemoveAll(path) |
| |
| var expectedOut bytes.Buffer |
| for _, fixture := range fixtures { |
| if !bytes.Equal(fixture.golden, fixture.input) { |
| expectedOut.WriteString(fmt.Sprintln(filepath.Join(path, fixture.filename))) |
| } |
| } |
| |
| _, stdout := mockIO() |
| err = Run( |
| []string{path}, |
| fixtureExtensions, |
| nil, stdout, |
| Options{ |
| List: true, |
| }, |
| ) |
| |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| if stdout.String() != expectedOut.String() { |
| t.Errorf("stdout want:\n%s\ngot:\n%s", expectedOut.String(), stdout.String()) |
| } |
| } |
| |
| func TestRunWrite(t *testing.T) { |
| path, err := renderFixtures("") |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| defer os.RemoveAll(path) |
| |
| _, stdout := mockIO() |
| err = Run( |
| []string{path}, |
| fixtureExtensions, |
| nil, stdout, |
| Options{ |
| Write: true, |
| }, |
| ) |
| |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| for _, fixture := range fixtures { |
| res, err := ioutil.ReadFile(filepath.Join(path, fixture.filename)) |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| if !bytes.Equal(res, fixture.golden) { |
| t.Errorf("file %q contents want:\n%s\ngot:\n%s", fixture.filename, fixture.golden, res) |
| } |
| } |
| } |
| |
| func TestRunDiff(t *testing.T) { |
| path, err := renderFixtures("") |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| defer os.RemoveAll(path) |
| |
| var expectedOut bytes.Buffer |
| for _, fixture := range fixtures { |
| if len(fixture.diff) > 0 { |
| expectedOut.WriteString( |
| regexp.QuoteMeta( |
| fmt.Sprintf("diff a/%s/%s b/%s/%s\n", path, fixture.filename, path, fixture.filename), |
| ), |
| ) |
| // Need to use regex to ignore datetimes in diff. |
| expectedOut.WriteString(`--- .+?\n`) |
| expectedOut.WriteString(`\+\+\+ .+?\n`) |
| expectedOut.WriteString(regexp.QuoteMeta(string(fixture.diff))) |
| } |
| } |
| |
| expectedOutString := testhelper.Unix2dos(expectedOut.String()) |
| |
| _, stdout := mockIO() |
| err = Run( |
| []string{path}, |
| fixtureExtensions, |
| nil, stdout, |
| Options{ |
| Diff: true, |
| }, |
| ) |
| |
| if err != nil { |
| t.Errorf("unexpected error: %s", err) |
| } |
| if !regexp.MustCompile(expectedOutString).Match(stdout.Bytes()) { |
| t.Errorf("stdout want match:\n%s\ngot:\n%q", expectedOutString, stdout) |
| } |
| } |
| |
| func mockIO() (stdin, stdout *bytes.Buffer) { |
| return new(bytes.Buffer), new(bytes.Buffer) |
| } |
| |
| type fixture struct { |
| filename string |
| input, golden, diff []byte |
| } |
| |
| type ByFilename []fixture |
| |
| func (s ByFilename) Len() int { return len(s) } |
| func (s ByFilename) Swap(i, j int) { s[i], s[j] = s[j], s[i] } |
| func (s ByFilename) Less(i, j int) bool { return len(s[i].filename) > len(s[j].filename) } |
| |
| var fixtures = []fixture{ |
| { |
| "noop.hcl", |
| []byte(`resource "aws_security_group" "firewall" { |
| count = 5 |
| } |
| `), |
| []byte(`resource "aws_security_group" "firewall" { |
| count = 5 |
| } |
| `), |
| []byte(``), |
| }, { |
| "align_equals.hcl", |
| []byte(`variable "foo" { |
| default = "bar" |
| description = "bar" |
| } |
| `), |
| []byte(`variable "foo" { |
| default = "bar" |
| description = "bar" |
| } |
| `), |
| []byte(`@@ -1,4 +1,4 @@ |
| variable "foo" { |
| - default = "bar" |
| + default = "bar" |
| description = "bar" |
| } |
| `), |
| }, { |
| "indentation.hcl", |
| []byte(`provider "aws" { |
| access_key = "foo" |
| secret_key = "bar" |
| } |
| `), |
| []byte(`provider "aws" { |
| access_key = "foo" |
| secret_key = "bar" |
| } |
| `), |
| []byte(`@@ -1,4 +1,4 @@ |
| provider "aws" { |
| - access_key = "foo" |
| - secret_key = "bar" |
| + access_key = "foo" |
| + secret_key = "bar" |
| } |
| `), |
| }, |
| } |
| |
| // parent can be an empty string, in which case the system's default |
| // temporary directory will be used. |
| func renderFixtures(parent string) (path string, err error) { |
| path, err = ioutil.TempDir(parent, "") |
| if err != nil { |
| return "", err |
| } |
| |
| for _, fixture := range fixtures { |
| err = ioutil.WriteFile(filepath.Join(path, fixture.filename), []byte(fixture.input), 0644) |
| if err != nil { |
| os.RemoveAll(path) |
| return "", err |
| } |
| } |
| |
| return path, nil |
| } |