github.com/jpreese/tflint@v0.19.2-0.20200908152133-b01686250fb6/tflint/testing.go (about) 1 package tflint 2 3 import ( 4 "os" 5 "path/filepath" 6 "reflect" 7 "testing" 8 9 "github.com/google/go-cmp/cmp" 10 "github.com/google/go-cmp/cmp/cmpopts" 11 hcl "github.com/hashicorp/hcl/v2" 12 "github.com/hashicorp/terraform/terraform" 13 "github.com/spf13/afero" 14 ) 15 16 // TestRunner returns a runner for testing. 17 // Note that this runner ignores a config, annotations, and input variables. 18 func TestRunner(t *testing.T, files map[string]string) *Runner { 19 return TestRunnerWithConfig(t, files, EmptyConfig()) 20 } 21 22 // TestRunnerWithConfig returns a runner with passed config for testing. 23 func TestRunnerWithConfig(t *testing.T, files map[string]string, config *Config) *Runner { 24 fs := afero.Afero{Fs: afero.NewMemMapFs()} 25 for name, src := range files { 26 err := fs.WriteFile(name, []byte(src), os.ModePerm) 27 if err != nil { 28 t.Fatal(err) 29 } 30 } 31 32 loader, err := NewLoader(fs, config) 33 if err != nil { 34 t.Fatal(err) 35 } 36 37 dirMap := map[string]*struct{}{} 38 for file := range files { 39 dirMap[filepath.Dir(file)] = nil 40 } 41 dirs := make([]string, 0) 42 for dir := range dirMap { 43 dirs = append(dirs, dir) 44 } 45 46 if len(dirs) > 1 { 47 t.Fatalf("All test files must be in the same directory, got %d directories: %v", len(dirs), dirs) 48 return nil 49 } 50 51 var dir string 52 if len(dirs) == 0 { 53 dir = "." 54 } else { 55 dir = dirs[0] 56 } 57 58 cfg, err := loader.LoadConfig(dir) 59 if err != nil { 60 t.Fatal(err) 61 } 62 f, err := loader.Files() 63 if err != nil { 64 t.Fatal(err) 65 } 66 67 runner, err := NewRunner(config, f, map[string]Annotations{}, cfg, map[string]*terraform.InputValue{}) 68 if err != nil { 69 t.Fatal(err) 70 } 71 72 return runner 73 } 74 75 // AssertIssues is an assertion helper for comparing issues 76 func AssertIssues(t *testing.T, expected Issues, actual Issues) { 77 opts := []cmp.Option{ 78 // Byte field will be ignored because it's not important in tests such as positions 79 cmpopts.IgnoreFields(hcl.Pos{}, "Byte"), 80 ruleComparer(), 81 } 82 if !cmp.Equal(expected, actual, opts...) { 83 t.Fatalf("Expected issues are not matched:\n %s\n", cmp.Diff(expected, actual, opts...)) 84 } 85 } 86 87 // AssertIssuesWithoutRange is an assertion helper for comparing issues 88 func AssertIssuesWithoutRange(t *testing.T, expected Issues, actual Issues) { 89 opts := []cmp.Option{ 90 cmpopts.IgnoreFields(Issue{}, "Range"), 91 ruleComparer(), 92 } 93 if !cmp.Equal(expected, actual, opts...) { 94 t.Fatalf("Expected issues are not matched:\n %s\n", cmp.Diff(expected, actual, opts...)) 95 } 96 } 97 98 // AssertAppError is an assertion helper for comparing tflint.Error 99 func AssertAppError(t *testing.T, expected Error, got error) { 100 if appErr, ok := got.(*Error); ok { 101 if appErr == nil { 102 t.Fatalf("expected err is `%s`, but nothing occurred", expected.Error()) 103 } 104 if appErr.Code != expected.Code { 105 t.Fatalf("expected error code is `%s`, but get `%s`", expected.Code, appErr.Code) 106 } 107 if appErr.Level != expected.Level { 108 t.Fatalf("expected error level is `%s`, but get `%s`", expected.Level, appErr.Level) 109 } 110 if appErr.Error() != expected.Error() { 111 t.Fatalf("expected error is `%s`, but get `%s`", expected.Error(), appErr.Error()) 112 } 113 } else { 114 t.Fatalf("unexpected error occurred: %s", got) 115 } 116 } 117 118 // ruleComparer returns a Comparer func that checks that two rule interfaces 119 // have the same underlying type. It does not compare struct fields. 120 func ruleComparer() cmp.Option { 121 return cmp.Comparer(func(x, y Rule) bool { 122 return reflect.TypeOf(x) == reflect.TypeOf(y) 123 }) 124 }