github.com/moby/docker@v26.1.3+incompatible/pkg/archive/changes_posix_test.go (about)

     1  package archive // import "github.com/docker/docker/pkg/archive"
     2  
     3  import (
     4  	"archive/tar"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path"
     9  	"sort"
    10  	"testing"
    11  
    12  	"github.com/docker/docker/pkg/idtools"
    13  )
    14  
    15  func TestHardLinkOrder(t *testing.T) {
    16  	names := []string{"file1.txt", "file2.txt", "file3.txt"}
    17  	msg := []byte("Hey y'all")
    18  
    19  	// Create dir
    20  	src, err := os.MkdirTemp("", "docker-hardlink-test-src-")
    21  	if err != nil {
    22  		t.Fatal(err)
    23  	}
    24  	defer os.RemoveAll(src)
    25  	for _, name := range names {
    26  		func() {
    27  			err := os.WriteFile(path.Join(src, name), msg, 0666)
    28  			if err != nil {
    29  				t.Fatal(err)
    30  			}
    31  		}()
    32  	}
    33  	// Create dest, with changes that includes hardlinks
    34  	dest, err := os.MkdirTemp("", "docker-hardlink-test-dest-")
    35  	if err != nil {
    36  		t.Fatal(err)
    37  	}
    38  	os.RemoveAll(dest) // we just want the name, at first
    39  	if err := copyDir(src, dest); err != nil {
    40  		t.Fatal(err)
    41  	}
    42  	defer os.RemoveAll(dest)
    43  	for _, name := range names {
    44  		for i := 0; i < 5; i++ {
    45  			if err := os.Link(path.Join(dest, name), path.Join(dest, fmt.Sprintf("%s.link%d", name, i))); err != nil {
    46  				t.Fatal(err)
    47  			}
    48  		}
    49  	}
    50  
    51  	// get changes
    52  	changes, err := ChangesDirs(dest, src)
    53  	if err != nil {
    54  		t.Fatal(err)
    55  	}
    56  
    57  	// sort
    58  	sort.Sort(changesByPath(changes))
    59  
    60  	// ExportChanges
    61  	ar, err := ExportChanges(dest, changes, idtools.IdentityMapping{})
    62  	if err != nil {
    63  		t.Fatal(err)
    64  	}
    65  	hdrs, err := walkHeaders(ar)
    66  	if err != nil {
    67  		t.Fatal(err)
    68  	}
    69  
    70  	// reverse sort
    71  	sort.Sort(sort.Reverse(changesByPath(changes)))
    72  	// ExportChanges
    73  	arRev, err := ExportChanges(dest, changes, idtools.IdentityMapping{})
    74  	if err != nil {
    75  		t.Fatal(err)
    76  	}
    77  	hdrsRev, err := walkHeaders(arRev)
    78  	if err != nil {
    79  		t.Fatal(err)
    80  	}
    81  
    82  	// line up the two sets
    83  	sort.Sort(tarHeaders(hdrs))
    84  	sort.Sort(tarHeaders(hdrsRev))
    85  
    86  	// compare Size and LinkName
    87  	for i := range hdrs {
    88  		if hdrs[i].Name != hdrsRev[i].Name {
    89  			t.Errorf("headers - expected name %q; but got %q", hdrs[i].Name, hdrsRev[i].Name)
    90  		}
    91  		if hdrs[i].Size != hdrsRev[i].Size {
    92  			t.Errorf("headers - %q expected size %d; but got %d", hdrs[i].Name, hdrs[i].Size, hdrsRev[i].Size)
    93  		}
    94  		if hdrs[i].Typeflag != hdrsRev[i].Typeflag {
    95  			t.Errorf("headers - %q expected type %d; but got %d", hdrs[i].Name, hdrs[i].Typeflag, hdrsRev[i].Typeflag)
    96  		}
    97  		if hdrs[i].Linkname != hdrsRev[i].Linkname {
    98  			t.Errorf("headers - %q expected linkname %q; but got %q", hdrs[i].Name, hdrs[i].Linkname, hdrsRev[i].Linkname)
    99  		}
   100  	}
   101  }
   102  
   103  type tarHeaders []tar.Header
   104  
   105  func (th tarHeaders) Len() int           { return len(th) }
   106  func (th tarHeaders) Swap(i, j int)      { th[j], th[i] = th[i], th[j] }
   107  func (th tarHeaders) Less(i, j int) bool { return th[i].Name < th[j].Name }
   108  
   109  func walkHeaders(r io.Reader) ([]tar.Header, error) {
   110  	t := tar.NewReader(r)
   111  	var headers []tar.Header
   112  	for {
   113  		hdr, err := t.Next()
   114  		if err != nil {
   115  			if err == io.EOF {
   116  				break
   117  			}
   118  			return headers, err
   119  		}
   120  		headers = append(headers, *hdr)
   121  	}
   122  	return headers, nil
   123  }