...

Source file src/oras.land/oras-go/pkg/content/passthrough_test.go

Documentation: oras.land/oras-go/pkg/content

     1  /*
     2  Copyright The ORAS Authors.
     3  Licensed under the Apache License, Version 2.0 (the "License");
     4  you may not use this file except in compliance with the License.
     5  You may obtain a copy of the License at
     6  
     7  http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package content_test
    17  
    18  import (
    19  	"bytes"
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"math/rand"
    24  	"testing"
    25  
    26  	ctrcontent "github.com/containerd/containerd/content"
    27  	digest "github.com/opencontainers/go-digest"
    28  	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
    29  
    30  	"oras.land/oras-go/pkg/content"
    31  )
    32  
    33  var (
    34  	testRef             = "abc123"
    35  	testContent         = []byte("Hello World!")
    36  	testContentHash     = digest.FromBytes(testContent)
    37  	appendText          = "1"
    38  	modifiedContent     = fmt.Sprintf("%s%s", testContent, appendText)
    39  	modifiedContentHash = digest.FromBytes([]byte(modifiedContent))
    40  	testDescriptor      = ocispec.Descriptor{
    41  		MediaType: ocispec.MediaTypeImageConfig,
    42  		Digest:    testContentHash,
    43  		Size:      int64(len(testContent)),
    44  		Annotations: map[string]string{
    45  			ocispec.AnnotationTitle: testRef,
    46  		},
    47  	}
    48  	modifiedDescriptor = ocispec.Descriptor{
    49  		MediaType: ocispec.MediaTypeImageConfig,
    50  		Digest:    modifiedContentHash,
    51  		Size:      int64(len(modifiedContent)),
    52  		Annotations: map[string]string{
    53  			ocispec.AnnotationTitle: testRef,
    54  		},
    55  	}
    56  )
    57  
    58  func TestPassthroughWriter(t *testing.T) {
    59  	// simple pass through function that modifies the data just slightly
    60  	f := func(r io.Reader, w io.Writer, done chan<- error) {
    61  		var (
    62  			err error
    63  			n   int
    64  		)
    65  		for {
    66  			b := make([]byte, 1024)
    67  			n, err = r.Read(b)
    68  			if err != nil && err != io.EOF {
    69  				t.Fatalf("data read error: %v", err)
    70  				break
    71  			}
    72  			l := n
    73  			if n > len(b) {
    74  				l = len(b)
    75  			}
    76  
    77  			// we change it just slightly
    78  			b = b[:l]
    79  			if l > 0 {
    80  				b = append(b, []byte(appendText)...)
    81  			}
    82  			if _, err := w.Write(b); err != nil {
    83  				t.Fatalf("error writing to underlying writer: %v", err)
    84  				break
    85  			}
    86  			if err == io.EOF {
    87  				break
    88  			}
    89  		}
    90  		done <- err
    91  	}
    92  
    93  	tests := []struct {
    94  		opts []content.WriterOpt
    95  		hash digest.Digest
    96  	}{
    97  		{nil, testContentHash},
    98  		{[]content.WriterOpt{content.WithInputHash(testContentHash), content.WithOutputHash(modifiedContentHash)}, testContentHash},
    99  	}
   100  
   101  	for _, tt := range tests {
   102  		ctx := context.Background()
   103  		mem := content.NewMemory()
   104  		pusher, _ := mem.Pusher(ctx, "")
   105  		memw, err := pusher.Push(ctx, modifiedDescriptor)
   106  		if err != nil {
   107  			t.Fatalf("unexpected error getting the memory store writer: %v", err)
   108  		}
   109  		writer := content.NewPassthroughWriter(memw, f, tt.opts...)
   110  		n, err := writer.Write(testContent)
   111  		if err != nil {
   112  			t.Fatalf("unexpected error on Write: %v", err)
   113  		}
   114  		if n != len(testContent) {
   115  			t.Fatalf("wrote %d bytes instead of %d", n, len(testContent))
   116  		}
   117  		if err := writer.Commit(ctx, testDescriptor.Size, tt.hash); err != nil {
   118  			t.Errorf("unexpected error on Commit: %v", err)
   119  		}
   120  		if digest := writer.Digest(); digest != tt.hash {
   121  			t.Errorf("mismatched digest: actual %v, expected %v", digest, tt.hash)
   122  		}
   123  
   124  		// make sure the data is what we expected
   125  		_, b, found := mem.Get(modifiedDescriptor)
   126  		if !found {
   127  			t.Fatalf("target descriptor not found in underlying memory store")
   128  		}
   129  		if len(b) != len(modifiedContent) {
   130  			t.Errorf("unexpectedly got %d bytes instead of expected %d", len(b), len(modifiedContent))
   131  		}
   132  		if string(b) != modifiedContent {
   133  			t.Errorf("mismatched content, expected '%s', got '%s'", modifiedContent, string(b))
   134  		}
   135  	}
   136  }
   137  
   138  func TestPassthroughMultiWriter(t *testing.T) {
   139  	// pass through function that selects one of two outputs
   140  	var (
   141  		b1, b2       bytes.Buffer
   142  		name1, name2 = "I am name 01", "I am name 02" // each of these is 12 bytes
   143  		data1, data2 = make([]byte, 500), make([]byte, 500)
   144  	)
   145  	rand.Read(data1)
   146  	rand.Read(data2)
   147  	combined := append([]byte(name1), data1...)
   148  	combined = append(combined, []byte(name2)...)
   149  	combined = append(combined, data2...)
   150  	f := func(r io.Reader, getwriter func(name string) io.Writer, done chan<- error) {
   151  		var (
   152  			err error
   153  		)
   154  		// test is done rather simply, with a single 1024 byte chunk, split into 2x512 data streams, each of which is
   155  		// 12 bytes of name and 500 bytes of data
   156  		b := make([]byte, 1024)
   157  		_, err = r.Read(b)
   158  		if err != nil && err != io.EOF {
   159  			t.Fatalf("data read error: %v", err)
   160  		}
   161  
   162  		// get the names and data for each
   163  		n1, n2 := string(b[0:12]), string(b[512+0:512+12])
   164  		w1, w2 := getwriter(n1), getwriter(n2)
   165  		if _, err := w1.Write(b[12:512]); err != nil {
   166  			t.Fatalf("w1 write error: %v", err)
   167  		}
   168  		if _, err := w2.Write(b[512+12 : 1024]); err != nil {
   169  			t.Fatalf("w2 write error: %v", err)
   170  		}
   171  		done <- err
   172  	}
   173  
   174  	var (
   175  		opts = []content.WriterOpt{content.WithInputHash(testContentHash), content.WithOutputHash(modifiedContentHash)}
   176  		hash = testContentHash
   177  	)
   178  	ctx := context.Background()
   179  	writers := func(name string) (ctrcontent.Writer, error) {
   180  		switch name {
   181  		case name1:
   182  			return content.NewIoContentWriter(&b1), nil
   183  		case name2:
   184  			return content.NewIoContentWriter(&b2), nil
   185  		}
   186  		return nil, fmt.Errorf("unknown name %s", name)
   187  	}
   188  	writer := content.NewPassthroughMultiWriter(writers, f, opts...)
   189  	n, err := writer.Write(combined)
   190  	if err != nil {
   191  		t.Fatalf("unexpected error on Write: %v", err)
   192  	}
   193  	if n != len(combined) {
   194  		t.Fatalf("wrote %d bytes instead of %d", n, len(combined))
   195  	}
   196  	if err := writer.Commit(ctx, testDescriptor.Size, hash); err != nil {
   197  		t.Errorf("unexpected error on Commit: %v", err)
   198  	}
   199  	if digest := writer.Digest(); digest != hash {
   200  		t.Errorf("mismatched digest: actual %v, expected %v", digest, hash)
   201  	}
   202  
   203  	// make sure the data is what we expected
   204  	if !bytes.Equal(data1, b1.Bytes()) {
   205  		t.Errorf("b1 data1 did not match")
   206  	}
   207  	if !bytes.Equal(data2, b2.Bytes()) {
   208  		t.Errorf("b2 data2 did not match")
   209  	}
   210  }
   211  

View as plain text