package tartest import ( "archive/tar" "bytes" "compress/gzip" "fmt" "io" "testing" ) type ( // Tarrer is an object that can tar itself to an archive Tarrer interface { Tar(*tar.Writer) error } // Tarball is a list of tarrables Tarball []Tarrer // Extractable is an empty interface for comparing extracted outputs in tests. // Using that just to avoid the ugly `interface{}`. Extractable interface{} // Dir is a tarrable directory representation Dir struct { Name string UID int } // File is a tarrable file representation File struct { Name string UID int Contents *bytes.Buffer } // Hardlink is a representation of a hardlink Hardlink struct { Name string UID int } ) // Buffer returns a byte buffer func (tb Tarball) Buffer() *bytes.Buffer { var buf bytes.Buffer tw := tar.NewWriter(&buf) for _, member := range tb { member.Tar(tw) } tw.Close() return &buf } // Gzip returns a gzipped buffer func (tb Tarball) Gzip() *bytes.Buffer { var buf bytes.Buffer w := gzip.NewWriter(&buf) _, err := io.Copy(w, tb.Buffer()) if err != nil { panic(fmt.Errorf("Gzip(): %w", err)) } if err := w.Close(); err != nil { panic(fmt.Errorf("gzip.Close(): %w", err)) } return &buf } // Tar tars the Dir func (d Dir) Tar(tw *tar.Writer) error { hdr := &tar.Header{ Typeflag: tar.TypeDir, Name: d.Name, Mode: 0644, Uid: d.UID, } return tw.WriteHeader(hdr) } // Tar tars the File func (f File) Tar(tw *tar.Writer) error { var contents []byte if f.Contents != nil { contents = f.Contents.Bytes() } hdr := &tar.Header{ Typeflag: tar.TypeReg, Name: f.Name, Mode: 0644, Uid: f.UID, Size: int64(len(contents)), } if err := tw.WriteHeader(hdr); err != nil { return err } if _, err := tw.Write(contents); err != nil { return err } return nil } // Tar tars the Hardlink func (h Hardlink) Tar(tw *tar.Writer) error { return tw.WriteHeader(&tar.Header{ Typeflag: tar.TypeLink, Name: h.Name, Mode: 0644, Uid: h.UID, }) } // Extract extracts a tarball to a slice of extractables func Extract(t *testing.T, r io.Reader) []Extractable { t.Helper() ret := []Extractable{} tr := tar.NewReader(r) for { hdr, err := tr.Next() if err == io.EOF { break } if err != nil { t.Errorf("expected nil error: %v", err) } var elem Extractable switch hdr.Typeflag { case tar.TypeDir: elem = Dir{Name: hdr.Name, UID: hdr.Uid} case tar.TypeLink: elem = Hardlink{Name: hdr.Name} case tar.TypeReg: f := File{Name: hdr.Name, UID: hdr.Uid} if hdr.Size > 0 { var buf bytes.Buffer io.Copy(&buf, tr) f.Contents = &buf } elem = f } ret = append(ret, elem) } return ret }