github.com/dctrud/umoci@v0.4.3-0.20191016193643-05a1d37de015/oci/casext/map_test.go (about) 1 /* 2 * umoci: Umoci Modifies Open Containers' Images 3 * Copyright (C) 2017, 2018 SUSE LLC. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 */ 17 18 package casext 19 20 import ( 21 crand "crypto/rand" 22 "io" 23 "math/rand" 24 "reflect" 25 "testing" 26 27 "github.com/mohae/deepcopy" 28 "github.com/opencontainers/go-digest" 29 ispec "github.com/opencontainers/image-spec/specs-go/v1" 30 ) 31 32 func descriptorPtr(d ispec.Descriptor) *ispec.Descriptor { return &d } 33 34 func randomDescriptor(t *testing.T) ispec.Descriptor { 35 var descriptor ispec.Descriptor 36 37 // Generate a random digest and length. 38 descriptor.Size = int64(rand.Intn(512 * 1024)) 39 digester := digest.SHA256.Digester() 40 io.CopyN(digester.Hash(), crand.Reader, descriptor.Size) 41 descriptor.Digest = digester.Digest() 42 43 // Generate a random number of annotations, with random key/values. 44 descriptor.Annotations = map[string]string{} 45 n := rand.Intn(32) 46 for i := 0; i < n; i++ { 47 descriptor.Annotations[randomString(32)] = randomString(32) 48 } 49 50 return descriptor 51 } 52 53 // Make sure that an identity mapping doesn't change the struct, and that it 54 // actually does visit all of the descriptors once. 55 func TestMapDescriptors_Identity(t *testing.T) { 56 // List of interfaces to use MapDescriptors on, as well as how many 57 // *unique* descriptors they contain. 58 ociList := []struct { 59 num int 60 obj interface{} 61 }{ 62 // Make sure that "base" types work. 63 { 64 num: 0, 65 obj: nil, 66 }, 67 { 68 num: 1, 69 obj: randomDescriptor(t), 70 }, 71 { 72 num: 1, 73 obj: descriptorPtr(randomDescriptor(t)), 74 }, 75 { 76 num: 3, 77 obj: []ispec.Descriptor{ 78 randomDescriptor(t), 79 randomDescriptor(t), 80 randomDescriptor(t), 81 }, 82 }, 83 { 84 num: 7, 85 obj: []*ispec.Descriptor{ 86 descriptorPtr(randomDescriptor(t)), 87 descriptorPtr(randomDescriptor(t)), 88 descriptorPtr(randomDescriptor(t)), 89 descriptorPtr(randomDescriptor(t)), 90 descriptorPtr(randomDescriptor(t)), 91 descriptorPtr(randomDescriptor(t)), 92 descriptorPtr(randomDescriptor(t)), 93 }, 94 }, 95 // Make sure official OCI structs work. 96 { 97 num: 7, 98 obj: ispec.Manifest{ 99 Config: randomDescriptor(t), 100 Layers: []ispec.Descriptor{ 101 randomDescriptor(t), 102 randomDescriptor(t), 103 randomDescriptor(t), 104 randomDescriptor(t), 105 randomDescriptor(t), 106 randomDescriptor(t), 107 }, 108 }, 109 }, 110 { 111 num: 2, 112 obj: ispec.Index{ 113 Manifests: []ispec.Descriptor{ 114 randomDescriptor(t), 115 randomDescriptor(t), 116 }, 117 }, 118 }, 119 // Check that pointers also work. 120 { 121 num: 5, 122 obj: &ispec.Manifest{ 123 Config: randomDescriptor(t), 124 Layers: []ispec.Descriptor{ 125 randomDescriptor(t), 126 randomDescriptor(t), 127 randomDescriptor(t), 128 randomDescriptor(t), 129 }, 130 }, 131 }, 132 { 133 num: 9, 134 obj: &ispec.Index{ 135 Manifests: []ispec.Descriptor{ 136 randomDescriptor(t), 137 randomDescriptor(t), 138 randomDescriptor(t), 139 randomDescriptor(t), 140 randomDescriptor(t), 141 randomDescriptor(t), 142 randomDescriptor(t), 143 randomDescriptor(t), 144 randomDescriptor(t), 145 }, 146 }, 147 }, 148 // Make sure that an empty []ispec.Descriptor works properly. 149 { 150 num: 0, 151 obj: []ispec.Descriptor{}, 152 }, 153 { 154 num: 1, 155 obj: ispec.Manifest{ 156 Config: randomDescriptor(t), 157 Layers: nil, 158 }, 159 }, 160 { 161 num: 0, 162 obj: ispec.Index{ 163 Manifests: []ispec.Descriptor{}, 164 }, 165 }, 166 // TODO: Add support for descending into maps. 167 } 168 169 for idx, test := range ociList { 170 // Make a copy for later comparison. 171 original := deepcopy.Copy(test.obj) 172 173 foundSet := map[digest.Digest]int{} 174 175 if err := MapDescriptors(test.obj, func(descriptor ispec.Descriptor) ispec.Descriptor { 176 foundSet[descriptor.Digest]++ 177 return descriptor 178 }); err != nil { 179 t.Errorf("MapDescriptors(%d) unexpected error: %v", idx, err) 180 continue 181 } 182 183 // Make sure that we hit everything uniquely. 184 found := 0 185 for d, n := range foundSet { 186 found++ 187 if n != 1 { 188 t.Errorf("MapDescriptors(%d) hit a descriptor more than once: %#v hit %d times", idx, d, n) 189 } 190 } 191 if found != test.num { 192 t.Errorf("MapDescriptors(%d) didn't hit the right number, expected %d got %d", idx, test.num, found) 193 } 194 195 if !reflect.DeepEqual(original, test.obj) { 196 t.Errorf("MapDescriptors(%d) descriptors were modified with identity mapping, expected %#v got %#v", idx, original, test.obj) 197 } 198 } 199 } 200 201 // Make sure that it is possible to modify a variety of different interfaces. 202 func TestMapDescriptors_ModifyOCI(t *testing.T) { 203 // List of interfaces to use MapDescriptors on. 204 ociList := []struct { 205 obj interface{} 206 }{ 207 // Make sure that "base" types work. 208 { 209 obj: descriptorPtr(randomDescriptor(t)), 210 }, 211 { 212 obj: []ispec.Descriptor{ 213 randomDescriptor(t), 214 randomDescriptor(t), 215 randomDescriptor(t), 216 }, 217 }, 218 { 219 obj: []*ispec.Descriptor{ 220 descriptorPtr(randomDescriptor(t)), 221 descriptorPtr(randomDescriptor(t)), 222 }, 223 }, 224 // TODO: Add the ability to mutate map keys and values. 225 // Make sure official OCI structs work. 226 { 227 obj: &ispec.Manifest{ 228 Config: randomDescriptor(t), 229 Layers: []ispec.Descriptor{ 230 randomDescriptor(t), 231 randomDescriptor(t), 232 randomDescriptor(t), 233 randomDescriptor(t), 234 randomDescriptor(t), 235 randomDescriptor(t), 236 }, 237 }, 238 }, 239 { 240 obj: ispec.Index{ 241 Manifests: []ispec.Descriptor{ 242 randomDescriptor(t), 243 randomDescriptor(t), 244 }, 245 }, 246 }, 247 { 248 obj: &ispec.Index{ 249 Manifests: []ispec.Descriptor{ 250 randomDescriptor(t), 251 randomDescriptor(t), 252 }, 253 }, 254 }, 255 } 256 257 for idx, test := range ociList { 258 // Make a copy for later comparison. 259 original := deepcopy.Copy(test.obj) 260 261 if err := MapDescriptors(&test.obj, func(descriptor ispec.Descriptor) ispec.Descriptor { 262 // Create an entirely new descriptor. 263 return randomDescriptor(t) 264 }); err != nil { 265 t.Errorf("MapDescriptors(%d) unexpected error: %v", idx, err) 266 continue 267 } 268 269 if reflect.DeepEqual(original, test.obj) { 270 t.Errorf("MapDescriptors(%d) descriptor was unmodified when replacing with a random descriptor!", idx) 271 } 272 } 273 } 274 275 // TODO: We should be able to rewrite non-OCI structs in the future.