2024-06-23 13:43:33 +00:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-06-24 17:40:04 +00:00
|
|
|
"bytes"
|
2024-06-23 16:33:34 +00:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2024-06-23 13:43:33 +00:00
|
|
|
"io"
|
2024-06-23 16:33:34 +00:00
|
|
|
"io/fs"
|
2024-06-23 13:43:33 +00:00
|
|
|
"os"
|
2024-06-24 17:40:04 +00:00
|
|
|
"path/filepath"
|
2024-06-23 13:43:33 +00:00
|
|
|
"testing"
|
|
|
|
)
|
|
|
|
|
|
|
|
func mkDirForTest(t *testing.T, fp string) {
|
|
|
|
err := os.MkdirAll(fp, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func cpFileForTest(t *testing.T, src string, dst string) {
|
|
|
|
var srcfd *os.File
|
|
|
|
var dstfd *os.File
|
|
|
|
var err error
|
|
|
|
var srcinfo os.FileInfo
|
|
|
|
srcfd, err = os.Open(src)
|
|
|
|
if err != nil {
|
2024-06-23 16:33:34 +00:00
|
|
|
if errors.Is(err, fs.ErrNotExist) {
|
|
|
|
return
|
|
|
|
} else {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
2024-06-23 13:43:33 +00:00
|
|
|
}
|
2024-06-23 16:33:34 +00:00
|
|
|
// if err != nil {
|
|
|
|
// t.Fatal(err)
|
|
|
|
// }
|
2024-06-23 13:43:33 +00:00
|
|
|
defer srcfd.Close()
|
|
|
|
dstfd, err = os.Create(dst)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer dstfd.Close()
|
|
|
|
_, err = io.Copy(dstfd, srcfd)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
srcinfo, err = os.Stat(src)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
err = os.Chmod(dst, srcinfo.Mode())
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func writeForTest(t *testing.T, fp string, data []byte) {
|
|
|
|
err := os.WriteFile(fp, data, 0666)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func IsDirEmpty(t *testing.T, name string) bool {
|
|
|
|
f, err := os.Open(name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer f.Close()
|
|
|
|
|
|
|
|
// read in ONLY one file
|
|
|
|
_, err = f.Readdir(1)
|
|
|
|
|
|
|
|
// and if the file is EOF... well, the dir is empty.
|
|
|
|
return err == io.EOF
|
|
|
|
}
|
2024-06-23 16:33:34 +00:00
|
|
|
|
|
|
|
func doesFileExist(name string) bool {
|
2024-06-24 05:55:08 +00:00
|
|
|
_, err := os.ReadFile(name)
|
|
|
|
// defer fp.Close()
|
|
|
|
return err == nil
|
2024-06-23 16:33:34 +00:00
|
|
|
}
|
|
|
|
|
2024-06-24 17:40:04 +00:00
|
|
|
// Derived from here (https://stackoverflow.com/a/55300382)
|
|
|
|
func walkMatch(t *testing.T, root, pattern string) []string {
|
|
|
|
var matches []string
|
|
|
|
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if info.IsDir() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
if matched, err := filepath.Match(pattern, filepath.Base(path)); err != nil {
|
|
|
|
return err
|
|
|
|
} else if matched {
|
|
|
|
matches = append(matches, path)
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
return matches
|
|
|
|
}
|
|
|
|
|
2024-06-24 05:55:08 +00:00
|
|
|
// func doesFileExist(name string) bool {
|
|
|
|
// _, err := os.Stat(name)
|
|
|
|
// return !errors.Is(err, fs.ErrNotExist)
|
|
|
|
// }
|
|
|
|
|
2024-06-23 16:33:34 +00:00
|
|
|
func removeFileForTest(t *testing.T, name string) {
|
|
|
|
err := os.Remove(name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-24 05:55:08 +00:00
|
|
|
func writeJsonForTest(t *testing.T, data map[string]any, fp string) {
|
2024-06-23 16:33:34 +00:00
|
|
|
jsonString, _ := json.Marshal(data)
|
|
|
|
err := os.WriteFile(fp, jsonString, os.ModePerm)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
}
|
2024-06-24 05:55:08 +00:00
|
|
|
|
|
|
|
func isFileEmpty(t *testing.T, name string) bool {
|
|
|
|
fd, err := os.Open(name)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer fd.Close()
|
|
|
|
finfo, err := fd.Stat()
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
return finfo.Size() < 1
|
|
|
|
}
|
2024-06-24 17:40:04 +00:00
|
|
|
|
|
|
|
// Derived from here (https://stackoverflow.com/a/73411967)
|
|
|
|
func areFilesTheSame(t *testing.T, fp_1 string, fp_2 string) bool {
|
|
|
|
chunkSize := 4 * 1024
|
|
|
|
|
|
|
|
// shortcuts: check file metadata
|
|
|
|
finfo_1, err := os.Stat(fp_1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
finfo_2, err := os.Stat(fp_2)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// are inputs are literally the same file?
|
|
|
|
if os.SameFile(finfo_1, finfo_2) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// do inputs at least have the same size?
|
|
|
|
if finfo_1.Size() != finfo_2.Size() {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
// long way: compare contents
|
|
|
|
fd_1, err := os.Open(fp_1)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
defer fd_1.Close()
|
|
|
|
|
|
|
|
fd_2, err := os.Open(fp_2)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatal(err)
|
|
|
|
}
|
|
|
|
defer fd_2.Close()
|
|
|
|
|
|
|
|
bfd_1 := make([]byte, chunkSize)
|
|
|
|
bfd_2 := make([]byte, chunkSize)
|
|
|
|
for {
|
|
|
|
n1, err1 := io.ReadFull(fd_1, bfd_1)
|
|
|
|
n2, err2 := io.ReadFull(fd_2, bfd_2)
|
|
|
|
|
|
|
|
// https://pkg.go.dev/io#Reader
|
|
|
|
// > Callers should always process the n > 0 bytes returned
|
|
|
|
// > before considering the error err. Doing so correctly
|
|
|
|
// > handles I/O errors that happen after reading some bytes
|
|
|
|
// > and also both of the allowed EOF behaviors.
|
|
|
|
|
|
|
|
if !bytes.Equal(bfd_1[:n1], bfd_2[:n2]) {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err1 == io.EOF && err2 == io.EOF) || (err1 == io.ErrUnexpectedEOF && err2 == io.ErrUnexpectedEOF) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
|
|
|
|
// some other error, like a dropped network connection or a bad transfer
|
|
|
|
if err1 != nil {
|
|
|
|
t.Fatal(err1)
|
|
|
|
}
|
|
|
|
if err2 != nil {
|
|
|
|
t.Fatal(err2)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|