diff --git a/pkg/v1/match/match_test.go b/pkg/v1/match/match_test.go new file mode 100644 index 000000000..cbdee93d5 --- /dev/null +++ b/pkg/v1/match/match_test.go @@ -0,0 +1,106 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package match_test + +import ( + "testing" + + v1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/match" + "github.com/google/go-containerregistry/pkg/v1/types" + imagespec "github.com/opencontainers/image-spec/specs-go/v1" +) + +func TestName(t *testing.T) { + tests := []struct { + desc v1.Descriptor + name string + match bool + }{ + {v1.Descriptor{Annotations: map[string]string{imagespec.AnnotationRefName: "foo"}}, "foo", true}, + {v1.Descriptor{Annotations: map[string]string{imagespec.AnnotationRefName: "foo"}}, "bar", false}, + {v1.Descriptor{Annotations: map[string]string{}}, "bar", false}, + {v1.Descriptor{Annotations: nil}, "bar", false}, + {v1.Descriptor{}, "bar", false}, + } + for i, tt := range tests { + f := match.Name(tt.name) + if match := f(tt.desc); match != tt.match { + t.Errorf("%d: mismatched, got %v expected %v for desc %#v name %s", i, match, tt.match, tt.desc, tt.name) + } + } +} + +func TestAnnotation(t *testing.T) { + tests := []struct { + desc v1.Descriptor + key string + value string + match bool + }{ + {v1.Descriptor{Annotations: map[string]string{"foo": "bar"}}, "foo", "bar", true}, + {v1.Descriptor{Annotations: map[string]string{"foo": "bar"}}, "bar", "foo", false}, + {v1.Descriptor{Annotations: map[string]string{}}, "foo", "bar", false}, + {v1.Descriptor{Annotations: nil}, "foo", "bar", false}, + {v1.Descriptor{}, "foo", "bar", false}, + } + for i, tt := range tests { + f := match.Annotation(tt.key, tt.value) + if match := f(tt.desc); match != tt.match { + t.Errorf("%d: mismatched, got %v expected %v for desc %#v annotation %s:%s", i, match, tt.match, tt.desc, tt.key, tt.value) + } + } +} + +func TestPlatform(t *testing.T) { + tests := []struct { + desc v1.Descriptor + platform v1.Platform + match bool + }{ + {v1.Descriptor{Platform: &v1.Platform{Architecture: "amd64", OS: "linux"}}, v1.Platform{Architecture: "amd64", OS: "linux"}, true}, + {v1.Descriptor{Platform: &v1.Platform{Architecture: "amd64", OS: "linux"}}, v1.Platform{Architecture: "arm64", OS: "linux"}, false}, + {v1.Descriptor{Platform: &v1.Platform{OS: "linux"}}, v1.Platform{Architecture: "arm64", OS: "linux"}, false}, + {v1.Descriptor{Platform: &v1.Platform{}}, v1.Platform{Architecture: "arm64", OS: "linux"}, false}, + {v1.Descriptor{Platform: nil}, v1.Platform{Architecture: "arm64", OS: "linux"}, false}, + {v1.Descriptor{}, v1.Platform{Architecture: "arm64", OS: "linux"}, false}, + } + for i, tt := range tests { + f := match.Platform(tt.platform) + if match := f(tt.desc); match != tt.match { + t.Errorf("%d: mismatched, got %v expected %v for desc %#v platform %#v", i, match, tt.match, tt.desc, tt.platform) + } + } +} + +func TestMediaTypes(t *testing.T) { + tests := []struct { + desc v1.Descriptor + mediaTypes []string + match bool + }{ + {v1.Descriptor{MediaType: types.OCIImageIndex}, []string{string(types.OCIImageIndex)}, true}, + {v1.Descriptor{MediaType: types.OCIImageIndex}, []string{string(types.OCIManifestSchema1)}, false}, + {v1.Descriptor{MediaType: types.OCIImageIndex}, []string{string(types.OCIManifestSchema1), string(types.OCIImageIndex)}, true}, + {v1.Descriptor{MediaType: types.OCIImageIndex}, []string{"a", "b"}, false}, + {v1.Descriptor{}, []string{string(types.OCIManifestSchema1), string(types.OCIImageIndex)}, false}, + } + for i, tt := range tests { + f := match.MediaTypes(tt.mediaTypes) + if match := f(tt.desc); match != tt.match { + t.Errorf("%d: mismatched, got %v expected %v for desc %#v mediaTypes %#v", i, match, tt.match, tt.desc, tt.mediaTypes) + } + } +} diff --git a/pkg/v1/platform.go b/pkg/v1/platform.go index 4fe21a85d..a022ab0c9 100644 --- a/pkg/v1/platform.go +++ b/pkg/v1/platform.go @@ -14,6 +14,10 @@ package v1 +import ( + "sort" +) + // Platform represents the target os/arch for an image. type Platform struct { Architecture string `json:"architecture"` @@ -26,9 +30,8 @@ type Platform struct { // Equals returns true if another platform is equivalent to this one. It must have all of the fields be the same func (p Platform) Equals(o Platform) bool { - // ignoring the Platform.Features and Platform.OSFeatures for now return p.OS == o.OS && p.Architecture == o.Architecture && p.Variant == o.Variant && p.OSVersion == o.OSVersion && - stringSliceEqual(p.OSFeatures, o.OSFeatures) && stringSliceEqual(p.Features, o.Features) + stringSliceEqualIgnoreOrder(p.OSFeatures, o.OSFeatures) && stringSliceEqualIgnoreOrder(p.Features, o.Features) } // stringSliceEqual compares 2 string slices and returns if their contents are identical. @@ -43,3 +46,13 @@ func stringSliceEqual(a, b []string) bool { } return true } + +// stringSliceEqualIgnoreOrder compares 2 string slices and returns if their contents are identical, ignoring order +func stringSliceEqualIgnoreOrder(a, b []string) bool { + a1, b1 := a[:], b[:] + if a1 != nil && b1 != nil { + sort.Strings(a1) + sort.Strings(b1) + } + return stringSliceEqual(a1, b1) +} diff --git a/pkg/v1/platform_test.go b/pkg/v1/platform_test.go new file mode 100644 index 000000000..4f60c36f3 --- /dev/null +++ b/pkg/v1/platform_test.go @@ -0,0 +1,62 @@ +// Copyright 2020 Google LLC All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package v1_test + +import ( + "testing" + + v1 "github.com/google/go-containerregistry/pkg/v1" +) + +/* +type Platform struct { + Architecture string `json:"architecture"` + OS string `json:"os"` + OSVersion string `json:"os.version,omitempty"` + OSFeatures []string `json:"os.features,omitempty"` + Variant string `json:"variant,omitempty"` + Features []string `json:"features,omitempty"` +} +*/ +func TestPlatformEquals(t *testing.T) { + tests := []struct { + a v1.Platform + b v1.Platform + equal bool + }{ + {v1.Platform{Architecture: "amd64", OS: "linux"}, v1.Platform{Architecture: "amd64", OS: "linux"}, true}, + {v1.Platform{Architecture: "amd64", OS: "linux"}, v1.Platform{Architecture: "arm64", OS: "linux"}, false}, + {v1.Platform{Architecture: "amd64", OS: "linux"}, v1.Platform{Architecture: "amd64", OS: "darwin"}, false}, + {v1.Platform{Architecture: "amd64", OS: "linux", OSVersion: "5.0"}, v1.Platform{Architecture: "amd64", OS: "linux"}, false}, + {v1.Platform{Architecture: "amd64", OS: "linux", OSVersion: "5.0"}, v1.Platform{Architecture: "amd64", OS: "linux", OSVersion: "3.6"}, false}, + {v1.Platform{Architecture: "amd64", OS: "linux", Variant: "pios"}, v1.Platform{Architecture: "amd64", OS: "linux"}, false}, + {v1.Platform{Architecture: "amd64", OS: "linux", Variant: "pios"}, v1.Platform{Architecture: "amd64", OS: "linux", Variant: "ubuntu"}, false}, + {v1.Platform{Architecture: "amd64", OS: "linux", Variant: "pios"}, v1.Platform{Architecture: "amd64", OS: "linux", Variant: "pios"}, true}, + {v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}}, v1.Platform{Architecture: "amd64", OS: "linux"}, false}, + {v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}}, v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}}, true}, + {v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}}, v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"ac", "bd"}}, false}, + {v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"a", "b"}}, v1.Platform{Architecture: "amd64", OS: "linux", OSFeatures: []string{"b", "a"}}, true}, + + {v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}}, v1.Platform{Architecture: "amd64", OS: "linux"}, false}, + {v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}}, v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}}, true}, + {v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}}, v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"ac", "bd"}}, false}, + {v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"a", "b"}}, v1.Platform{Architecture: "amd64", OS: "linux", Features: []string{"b", "a"}}, true}, + } + for i, tt := range tests { + if equal := tt.a.Equals(tt.b); equal != tt.equal { + t.Errorf("%d: mismatched was %v expected %v; original %#v compared %#v", i, equal, tt.equal, tt.a, tt.b) + } + } +}