github.com/tilt-dev/tilt@v0.33.15-0.20240515162809-0a22ed45d8a0/internal/testutils/tar.go (about) 1 package testutils 2 3 import ( 4 "archive/tar" 5 "bytes" 6 "fmt" 7 "io" 8 "os" 9 "runtime" 10 "testing" 11 12 "github.com/stretchr/testify/assert" 13 ) 14 15 const expectedUidAndGid = 0 16 17 type ExpectedFile struct { 18 Path string 19 Contents string 20 21 // If true, we will assert that the file is not in the tarball. 22 Missing bool 23 24 // If true, we will assert the file is a dir. 25 IsDir bool 26 27 // If true, we will assert that UID and GID are 0 28 AssertUidAndGidAreZero bool 29 30 // If true, we will assert that this is a symlink with a linkname. 31 Linkname string 32 33 // If non-zero, assert that file permission and mode bits match this value 34 Mode int64 35 36 // Windows filesystem APIs don't expose an executable bit. 37 // So when we create docker images on windows, we set the executable 38 // bit on all files, for consistency with Docker. 39 HasExecBitWindows bool 40 } 41 42 // Asserts whether or not this file is in the tar. 43 func AssertFileInTar(t testing.TB, tr *tar.Reader, expected ExpectedFile) { 44 AssertFilesInTar(t, tr, []ExpectedFile{expected}) 45 } 46 47 // Asserts whether or not these files are in the tar, but not that they are the only 48 // files in the tarball. 49 func AssertFilesInTar(t testing.TB, tr *tar.Reader, expectedFiles []ExpectedFile, 50 msgAndArgs ...interface{}) { 51 msg := "AssertFilesInTar" 52 if len(msgAndArgs) > 0 { 53 m := msgAndArgs[0] 54 s, ok := m.(string) 55 if !ok { 56 t.Fatalf("first arg to msgAndArgs (%v) not string", m) 57 } 58 msg = fmt.Sprintf(s, msgAndArgs[1:]...) 59 } 60 dupes := make(map[string]bool) 61 62 burndownMap := make(map[string]ExpectedFile, len(expectedFiles)) 63 for _, f := range expectedFiles { 64 burndownMap[f.Path] = f 65 } 66 67 for { 68 header, err := tr.Next() 69 if err == io.EOF { 70 break 71 } else if err != nil { 72 t.Fatalf("Error reading tar file: %v (%s)", err, msg) 73 } 74 75 if dupes[header.Name] { 76 t.Fatalf("File in tarball twice. This is invalid and will break when extracted: %v (%s)", header.Name, msg) 77 } 78 79 dupes[header.Name] = true 80 81 expected, ok := burndownMap[header.Name] 82 if !ok { 83 continue 84 } 85 86 // we found it! 87 delete(burndownMap, expected.Path) 88 89 if expected.Missing { 90 t.Errorf("Path %q was not expected in the tarball (%s)", expected.Path, msg) 91 continue 92 } 93 94 expectedReg := !expected.IsDir && expected.Linkname == "" 95 expectedDir := expected.IsDir 96 expectedSymlink := expected.Linkname != "" 97 if expectedReg && header.Typeflag != tar.TypeReg { 98 t.Errorf("Path %q exists but is not a regular file (%s)", expected.Path, msg) 99 continue 100 } 101 102 if expectedDir && header.Typeflag != tar.TypeDir { 103 t.Errorf("Path %q exists but is not a directory (%s)", expected.Path, msg) 104 continue 105 } 106 107 if expectedSymlink && header.Typeflag != tar.TypeSymlink { 108 t.Errorf("Path %q exists but is not a directory (%s)", expected.Path, msg) 109 continue 110 } 111 112 if expected.AssertUidAndGidAreZero && header.Uid != expectedUidAndGid { 113 t.Errorf("Expected %s to have UID 0, got %d (%s)", header.Name, header.Uid, msg) 114 } 115 116 if expected.AssertUidAndGidAreZero && header.Gid != expectedUidAndGid { 117 t.Errorf("Expected %s to have GID 0, got %d (%s)", header.Name, header.Gid, msg) 118 } 119 120 if expected.Mode != 0 && header.Mode != expected.Mode { 121 t.Errorf("Expected %s to have mode %d, got %d", header.Name, expected.Mode, header.Mode) 122 } 123 124 if expected.HasExecBitWindows && runtime.GOOS == "windows" && (os.FileMode(header.Mode)&0111 == 0) { 125 t.Errorf("Expected %s to have Executable bit, got %s", header.Name, os.FileMode(header.Mode)) 126 } 127 128 if header.Linkname != expected.Linkname { 129 t.Errorf("Expected linkname %q, actual %q (%s)", expected.Linkname, header.Linkname, msg) 130 } 131 132 if expectedReg { 133 contents := bytes.NewBuffer(nil) 134 _, err = io.Copy(contents, tr) 135 if err != nil { 136 t.Fatalf("Error reading tar file: %v (%s)", err, msg) 137 } 138 139 if !assert.Equal(t, expected.Contents, contents.String()) { 140 fmt.Printf("wrong contents in %q\n (%s)", expected.Path, msg) 141 continue 142 } 143 } 144 145 continue 146 } 147 148 for _, f := range burndownMap { 149 if !f.Missing { 150 t.Errorf("File not found in container: %s (%s)", f.Path, msg) 151 } 152 } 153 }