1
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
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
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
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
140 var (
141 b1, b2 bytes.Buffer
142 name1, name2 = "I am name 01", "I am name 02"
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
155
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
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
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