package getter import ( "crypto/md5" "encoding/hex" "io" "io/ioutil" "os" "path/filepath" "reflect" "runtime" "sort" "strings" "time" "github.com/mitchellh/go-testing-interface" ) // TestDecompressCase is a single test case for testing decompressors type TestDecompressCase struct { Input string // Input is the complete path to the input file Dir bool // Dir is whether or not we're testing directory mode Err bool // Err is whether we expect an error or not DirList []string // DirList is the list of files for Dir mode FileMD5 string // FileMD5 is the expected MD5 for a single file Mtime *time.Time // Mtime is the optionally expected mtime for a single file (or all files if in Dir mode) } // TestDecompressor is a helper function for testing generic decompressors. func TestDecompressor(t testing.T, d Decompressor, cases []TestDecompressCase) { t.Helper() for _, tc := range cases { t.Logf("Testing: %s", tc.Input) // Temporary dir to store stuff td, err := ioutil.TempDir("", "getter") if err != nil { t.Fatalf("err: %s", err) } // Destination is always joining result so that we have a new path dst := filepath.Join(td, "subdir", "result") // We use a function so defers work func() { defer os.RemoveAll(td) // Decompress err := d.Decompress(dst, tc.Input, tc.Dir) if (err != nil) != tc.Err { t.Fatalf("err %s: %s", tc.Input, err) } if tc.Err { return } // If it isn't a directory, then check for a single file if !tc.Dir { fi, err := os.Stat(dst) if err != nil { t.Fatalf("err %s: %s", tc.Input, err) } if fi.IsDir() { t.Fatalf("err %s: expected file, got directory", tc.Input) } if tc.FileMD5 != "" { actual := testMD5(t, dst) expected := tc.FileMD5 if actual != expected { t.Fatalf("err %s: expected MD5 %s, got %s", tc.Input, expected, actual) } } if tc.Mtime != nil { actual := fi.ModTime() if tc.Mtime.Unix() > 0 { expected := *tc.Mtime if actual != expected { t.Fatalf("err %s: expected mtime '%s' for %s, got '%s'", tc.Input, expected.String(), dst, actual.String()) } } else if actual.Unix() <= 0 { t.Fatalf("err %s: expected mtime to be > 0, got '%s'", actual.String()) } } return } // Convert expected for windows expected := tc.DirList if runtime.GOOS == "windows" { for i, v := range expected { expected[i] = strings.Replace(v, "/", "\\", -1) } } // Directory, check for the correct contents actual := testListDir(t, dst) if !reflect.DeepEqual(actual, expected) { t.Fatalf("bad %s\n\n%#v\n\n%#v", tc.Input, actual, expected) } // Check for correct atime/mtime for _, dir := range actual { path := filepath.Join(dst, dir) if tc.Mtime != nil { fi, err := os.Stat(path) if err != nil { t.Fatalf("err: %s", err) } actual := fi.ModTime() if tc.Mtime.Unix() > 0 { expected := *tc.Mtime if actual != expected { t.Fatalf("err %s: expected mtime '%s' for %s, got '%s'", tc.Input, expected.String(), path, actual.String()) } } else if actual.Unix() < 0 { t.Fatalf("err %s: expected mtime to be > 0, got '%s'", actual.String()) } } } }() } } func testListDir(t testing.T, path string) []string { var result []string err := filepath.Walk(path, func(sub string, info os.FileInfo, err error) error { if err != nil { return err } sub = strings.TrimPrefix(sub, path) if sub == "" { return nil } sub = sub[1:] // Trim the leading path sep. // If it is a dir, add trailing sep if info.IsDir() { sub += string(os.PathSeparator) } result = append(result, sub) return nil }) if err != nil { t.Fatalf("err: %s", err) } sort.Strings(result) return result } func testMD5(t testing.T, path string) string { f, err := os.Open(path) if err != nil { t.Fatalf("err: %s", err) } defer f.Close() h := md5.New() _, err = io.Copy(h, f) if err != nil { t.Fatalf("err: %s", err) } result := h.Sum(nil) return hex.EncodeToString(result) }