blob: b2f662a89dfcb2fa7bb66b45b94e93ad2bc95498 [file] [log] [blame]
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)
}