@@ -1509,5 +1509,201 @@ func TestCopyGraph_WithConcurrencyLimit(t *testing.T) {
1509
1509
}
1510
1510
}
1511
1511
}
1512
+ }
1513
+
1514
+ func TestCopyGraph_ForeignLayers (t * testing.T ) {
1515
+ src := cas .NewMemory ()
1516
+ dst := cas .NewMemory ()
1517
+
1518
+ // generate test content
1519
+ var blobs [][]byte
1520
+ var descs []ocispec.Descriptor
1521
+ appendBlob := func (mediaType string , blob []byte ) {
1522
+ desc := ocispec.Descriptor {
1523
+ MediaType : mediaType ,
1524
+ Digest : digest .FromBytes (blob ),
1525
+ Size : int64 (len (blob )),
1526
+ }
1527
+ if mediaType == ocispec .MediaTypeImageLayerNonDistributable {
1528
+ desc .URLs = append (desc .URLs , "http://127.0.0.1/dummy" )
1529
+ blob = nil
1530
+ }
1531
+ descs = append (descs , desc )
1532
+ blobs = append (blobs , blob )
1533
+ }
1534
+ generateManifest := func (config ocispec.Descriptor , layers ... ocispec.Descriptor ) {
1535
+ manifest := ocispec.Manifest {
1536
+ Config : config ,
1537
+ Layers : layers ,
1538
+ }
1539
+ manifestJSON , err := json .Marshal (manifest )
1540
+ if err != nil {
1541
+ t .Fatal (err )
1542
+ }
1543
+ appendBlob (ocispec .MediaTypeImageManifest , manifestJSON )
1544
+ }
1545
+
1546
+ appendBlob (ocispec .MediaTypeImageConfig , []byte ("config" )) // Blob 0
1547
+ appendBlob (ocispec .MediaTypeImageLayerNonDistributable , []byte ("hello" )) // Blob 1
1548
+ appendBlob (ocispec .MediaTypeImageLayer , []byte ("foo" )) // Blob 2
1549
+ appendBlob (ocispec .MediaTypeImageLayer , []byte ("bar" )) // Blob 3
1550
+ generateManifest (descs [0 ], descs [1 :4 ]... ) // Blob 4
1551
+
1552
+ ctx := context .Background ()
1553
+ for i := range blobs {
1554
+ if blobs [i ] == nil {
1555
+ continue
1556
+ }
1557
+ err := src .Push (ctx , descs [i ], bytes .NewReader (blobs [i ]))
1558
+ if err != nil {
1559
+ t .Fatalf ("failed to push test content to src: %d: %v" , i , err )
1560
+ }
1561
+ }
1562
+
1563
+ // test copy
1564
+ srcTracker := & storageTracker {Storage : src }
1565
+ dstTracker := & storageTracker {Storage : dst }
1566
+ root := descs [len (descs )- 1 ]
1567
+ if err := oras .CopyGraph (ctx , srcTracker , dstTracker , root , oras.CopyGraphOptions {}); err != nil {
1568
+ t .Fatalf ("CopyGraph() error = %v, wantErr %v" , err , false )
1569
+ }
1570
+
1571
+ // verify contents
1572
+ contents := dst .Map ()
1573
+ if got , want := len (contents ), len (blobs )- 1 ; got != want {
1574
+ t .Errorf ("len(dst) = %v, wantErr %v" , got , want )
1575
+ }
1576
+ for i := range blobs {
1577
+ if blobs [i ] == nil {
1578
+ continue
1579
+ }
1580
+ got , err := content .FetchAll (ctx , dst , descs [i ])
1581
+ if err != nil {
1582
+ t .Errorf ("content[%d] error = %v, wantErr %v" , i , err , false )
1583
+ continue
1584
+ }
1585
+ if want := blobs [i ]; ! bytes .Equal (got , want ) {
1586
+ t .Errorf ("content[%d] = %v, want %v" , i , got , want )
1587
+ }
1588
+ }
1589
+
1590
+ // verify API counts
1591
+ if got , want := srcTracker .fetch , int64 (len (blobs )- 1 ); got != want {
1592
+ t .Errorf ("count(src.Fetch()) = %v, want %v" , got , want )
1593
+ }
1594
+ if got , want := srcTracker .push , int64 (0 ); got != want {
1595
+ t .Errorf ("count(src.Push()) = %v, want %v" , got , want )
1596
+ }
1597
+ if got , want := srcTracker .exists , int64 (0 ); got != want {
1598
+ t .Errorf ("count(src.Exists()) = %v, want %v" , got , want )
1599
+ }
1600
+ if got , want := dstTracker .fetch , int64 (0 ); got != want {
1601
+ t .Errorf ("count(dst.Fetch()) = %v, want %v" , got , want )
1602
+ }
1603
+ if got , want := dstTracker .push , int64 (len (blobs )- 1 ); got != want {
1604
+ t .Errorf ("count(dst.Push()) = %v, want %v" , got , want )
1605
+ }
1606
+ if got , want := dstTracker .exists , int64 (len (blobs )- 1 ); got != want {
1607
+ t .Errorf ("count(dst.Exists()) = %v, want %v" , got , want )
1608
+ }
1609
+ }
1610
+
1611
+ func TestCopyGraph_ForeignLayers_Mixed (t * testing.T ) {
1612
+ src := cas .NewMemory ()
1613
+ dst := cas .NewMemory ()
1614
+
1615
+ // generate test content
1616
+ var blobs [][]byte
1617
+ var descs []ocispec.Descriptor
1618
+ appendBlob := func (mediaType string , blob []byte ) {
1619
+ desc := ocispec.Descriptor {
1620
+ MediaType : mediaType ,
1621
+ Digest : digest .FromBytes (blob ),
1622
+ Size : int64 (len (blob )),
1623
+ }
1624
+ if mediaType == ocispec .MediaTypeImageLayerNonDistributable {
1625
+ desc .URLs = append (desc .URLs , "http://127.0.0.1/dummy" )
1626
+ blob = nil
1627
+ }
1628
+ descs = append (descs , desc )
1629
+ blobs = append (blobs , blob )
1630
+ }
1631
+ generateManifest := func (config ocispec.Descriptor , layers ... ocispec.Descriptor ) {
1632
+ manifest := ocispec.Manifest {
1633
+ Config : config ,
1634
+ Layers : layers ,
1635
+ }
1636
+ manifestJSON , err := json .Marshal (manifest )
1637
+ if err != nil {
1638
+ t .Fatal (err )
1639
+ }
1640
+ appendBlob (ocispec .MediaTypeImageManifest , manifestJSON )
1641
+ }
1642
+
1643
+ appendBlob (ocispec .MediaTypeImageConfig , []byte ("config" )) // Blob 0
1644
+ appendBlob (ocispec .MediaTypeImageLayerNonDistributable , []byte ("hello" )) // Blob 1
1645
+ appendBlob (ocispec .MediaTypeImageLayer , []byte ("hello" )) // Blob 2
1646
+ appendBlob (ocispec .MediaTypeImageLayer , []byte ("foo" )) // Blob 3
1647
+ appendBlob (ocispec .MediaTypeImageLayer , []byte ("bar" )) // Blob 4
1648
+ generateManifest (descs [0 ], descs [1 :5 ]... ) // Blob 5
1649
+
1650
+ ctx := context .Background ()
1651
+ for i := range blobs {
1652
+ if blobs [i ] == nil {
1653
+ continue
1654
+ }
1655
+ err := src .Push (ctx , descs [i ], bytes .NewReader (blobs [i ]))
1656
+ if err != nil {
1657
+ t .Fatalf ("failed to push test content to src: %d: %v" , i , err )
1658
+ }
1659
+ }
1512
1660
1661
+ // test copy
1662
+ srcTracker := & storageTracker {Storage : src }
1663
+ dstTracker := & storageTracker {Storage : dst }
1664
+ root := descs [len (descs )- 1 ]
1665
+ if err := oras .CopyGraph (ctx , srcTracker , dstTracker , root , oras.CopyGraphOptions {
1666
+ Concurrency : 1 ,
1667
+ }); err != nil {
1668
+ t .Fatalf ("CopyGraph() error = %v, wantErr %v" , err , false )
1669
+ }
1670
+
1671
+ // verify contents
1672
+ contents := dst .Map ()
1673
+ if got , want := len (contents ), len (blobs )- 1 ; got != want {
1674
+ t .Errorf ("len(dst) = %v, wantErr %v" , got , want )
1675
+ }
1676
+ for i := range blobs {
1677
+ if blobs [i ] == nil {
1678
+ continue
1679
+ }
1680
+ got , err := content .FetchAll (ctx , dst , descs [i ])
1681
+ if err != nil {
1682
+ t .Errorf ("content[%d] error = %v, wantErr %v" , i , err , false )
1683
+ continue
1684
+ }
1685
+ if want := blobs [i ]; ! bytes .Equal (got , want ) {
1686
+ t .Errorf ("content[%d] = %v, want %v" , i , got , want )
1687
+ }
1688
+ }
1689
+
1690
+ // verify API counts
1691
+ if got , want := srcTracker .fetch , int64 (len (blobs )- 1 ); got != want {
1692
+ t .Errorf ("count(src.Fetch()) = %v, want %v" , got , want )
1693
+ }
1694
+ if got , want := srcTracker .push , int64 (0 ); got != want {
1695
+ t .Errorf ("count(src.Push()) = %v, want %v" , got , want )
1696
+ }
1697
+ if got , want := srcTracker .exists , int64 (0 ); got != want {
1698
+ t .Errorf ("count(src.Exists()) = %v, want %v" , got , want )
1699
+ }
1700
+ if got , want := dstTracker .fetch , int64 (0 ); got != want {
1701
+ t .Errorf ("count(dst.Fetch()) = %v, want %v" , got , want )
1702
+ }
1703
+ if got , want := dstTracker .push , int64 (len (blobs )- 1 ); got != want {
1704
+ t .Errorf ("count(dst.Push()) = %v, want %v" , got , want )
1705
+ }
1706
+ if got , want := dstTracker .exists , int64 (len (blobs )- 1 ); got != want {
1707
+ t .Errorf ("count(dst.Exists()) = %v, want %v" , got , want )
1708
+ }
1513
1709
}
0 commit comments