package platforms import ( "fmt" "reflect" "runtime" "testing" specs "github.com/opencontainers/image-spec/specs-go/v1" ) func TestParseSelector(t *testing.T) { var ( defaultOS = runtime.GOOS defaultArch = runtime.GOARCH ) for _, testcase := range []struct { skip bool input string expected specs.Platform formatted string }{ // While wildcards are a valid use case for platform selection, // addressing these cases is outside the initial scope for this // package. When we do add platform wildcards, we should add in these // testcases to ensure that they are correctly represented. { skip: true, input: "*", expected: specs.Platform{ OS: "*", Architecture: "*", }, formatted: "*/*", }, { skip: true, input: "linux/*", expected: specs.Platform{ OS: "linux", Architecture: "*", }, formatted: "linux/*", }, { skip: true, input: "*/arm64", expected: specs.Platform{ OS: "*", Architecture: "arm64", }, formatted: "*/arm64", }, { // NOTE(stevvooe): In this case, the consumer can assume this is v7 // but we leave the variant blank. This will represent the vast // majority of arm images. input: "linux/arm", expected: specs.Platform{ OS: "linux", Architecture: "arm", }, formatted: "linux/arm", }, { input: "linux/arm/v6", expected: specs.Platform{ OS: "linux", Architecture: "arm", Variant: "v6", }, formatted: "linux/arm/v6", }, { input: "linux/arm/v7", expected: specs.Platform{ OS: "linux", Architecture: "arm", Variant: "v7", }, formatted: "linux/arm/v7", }, { input: "arm", expected: specs.Platform{ OS: defaultOS, Architecture: "arm", }, formatted: joinNotEmpty(defaultOS, "arm"), }, { input: "armel", expected: specs.Platform{ OS: defaultOS, Architecture: "arm", Variant: "v6", }, formatted: joinNotEmpty(defaultOS, "arm/v6"), }, { input: "armhf", expected: specs.Platform{ OS: defaultOS, Architecture: "arm", }, formatted: joinNotEmpty(defaultOS, "arm"), }, { input: "Aarch64", expected: specs.Platform{ OS: defaultOS, Architecture: "arm64", }, formatted: joinNotEmpty(defaultOS, "arm64"), }, { input: "x86_64", expected: specs.Platform{ OS: defaultOS, Architecture: "amd64", }, formatted: joinNotEmpty(defaultOS, "amd64"), }, { input: "Linux/x86_64", expected: specs.Platform{ OS: "linux", Architecture: "amd64", }, formatted: "linux/amd64", }, { input: "i386", expected: specs.Platform{ OS: defaultOS, Architecture: "386", }, formatted: joinNotEmpty(defaultOS, "386"), }, { input: "linux", expected: specs.Platform{ OS: "linux", Architecture: defaultArch, }, formatted: joinNotEmpty("linux", defaultArch), }, { input: "s390x", expected: specs.Platform{ OS: defaultOS, Architecture: "s390x", }, formatted: joinNotEmpty(defaultOS, "s390x"), }, { input: "linux/s390x", expected: specs.Platform{ OS: "linux", Architecture: "s390x", }, formatted: "linux/s390x", }, { input: "macOS", expected: specs.Platform{ OS: "darwin", Architecture: defaultArch, }, formatted: joinNotEmpty("darwin", defaultArch), }, } { t.Run(testcase.input, func(t *testing.T) { if testcase.skip { t.Skip("this case is not yet supported") } m, err := Parse(testcase.input) if err != nil { t.Fatal(err) } if !reflect.DeepEqual(m.Spec(), testcase.expected) { t.Fatalf("platform did not match expected: %#v != %#v", m.Spec(), testcase.expected) } // ensure that match works on the input to the output. if ok := m.Match(testcase.expected); !ok { t.Fatalf("expected specifier %q matches %v", testcase.input, testcase.expected) } if fmt.Sprint(m) != testcase.formatted { t.Fatalf("unexpected matcher string: %q != %q", fmt.Sprint(m), testcase.formatted) } formatted := Format(m.Spec()) if formatted != testcase.formatted { t.Fatalf("unexpected format: %q != %q", formatted, testcase.formatted) } // re-parse the formatted output and ensure we are stable reparsed, err := Parse(formatted) if err != nil { t.Fatalf("error parsing formatted output: %v", err) } if Format(reparsed.Spec()) != formatted { t.Fatalf("normalized output did not survive the round trip: %v != %v", Format(reparsed.Spec()), formatted) } }) } } func TestParseSelectorInvalid(t *testing.T) { for _, testcase := range []struct { input string }{ { input: "", // empty }, { input: "/linux/arm", // leading slash }, { input: "linux/arm/", // trailing slash }, { input: "linux /arm", // spaces }, { input: "linux/&arm", // invalid character }, { input: "linux/arm/foo/bar", // too many components }, } { t.Run(testcase.input, func(t *testing.T) { if _, err := Parse(testcase.input); err == nil { t.Fatalf("should have received an error") } }) } }