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.