|  | // Copyright (C) 2021 The Android Open Source Project | 
|  | // | 
|  | // 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 sdk | 
|  |  | 
|  | import ( | 
|  | "encoding/json" | 
|  | "fmt" | 
|  | "testing" | 
|  |  | 
|  | "android/soong/android" | 
|  | ) | 
|  |  | 
|  | // Tests for build_release.go | 
|  |  | 
|  | var ( | 
|  | // Some additional test specific releases that are added after the currently supported ones and | 
|  | // so are treated as being for future releases. | 
|  | buildReleaseFuture1 = initBuildRelease("F1") | 
|  | buildReleaseFuture2 = initBuildRelease("F2") | 
|  | ) | 
|  |  | 
|  | func TestNameToRelease(t *testing.T) { | 
|  | t.Run("single release", func(t *testing.T) { | 
|  | release, err := nameToRelease("S") | 
|  | android.AssertDeepEquals(t, "errors", nil, err) | 
|  | android.AssertDeepEquals(t, "release", buildReleaseS, release) | 
|  | }) | 
|  | t.Run("invalid release", func(t *testing.T) { | 
|  | release, err := nameToRelease("A") | 
|  | android.AssertDeepEquals(t, "release", (*buildRelease)(nil), release) | 
|  | // Uses a wildcard in the error message to allow for additional build releases to be added to | 
|  | // the supported set without breaking this test. | 
|  | android.FailIfNoMatchingErrors(t, `unknown release "A", expected one of \[S,T.*,F1,F2\]`, []error{err}) | 
|  | }) | 
|  | } | 
|  |  | 
|  | func TestParseBuildReleaseSet(t *testing.T) { | 
|  | t.Run("single release", func(t *testing.T) { | 
|  | set, err := parseBuildReleaseSet("S") | 
|  | android.AssertDeepEquals(t, "errors", nil, err) | 
|  | android.AssertStringEquals(t, "set", "[S]", set.String()) | 
|  | }) | 
|  | t.Run("open range", func(t *testing.T) { | 
|  | set, err := parseBuildReleaseSet("F1+") | 
|  | android.AssertDeepEquals(t, "errors", nil, err) | 
|  | android.AssertStringEquals(t, "set", "[F1,F2]", set.String()) | 
|  | }) | 
|  | t.Run("closed range", func(t *testing.T) { | 
|  | set, err := parseBuildReleaseSet("S-F1") | 
|  | android.AssertDeepEquals(t, "errors", nil, err) | 
|  | android.AssertStringEquals(t, "set", "[S,T,F1]", set.String()) | 
|  | }) | 
|  | invalidAReleaseMessage := `unknown release "A", expected one of ` + allBuildReleaseSet.String() | 
|  | t.Run("invalid release", func(t *testing.T) { | 
|  | set, err := parseBuildReleaseSet("A") | 
|  | android.AssertDeepEquals(t, "set", (*buildReleaseSet)(nil), set) | 
|  | android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage) | 
|  | }) | 
|  | t.Run("invalid release in open range", func(t *testing.T) { | 
|  | set, err := parseBuildReleaseSet("A+") | 
|  | android.AssertDeepEquals(t, "set", (*buildReleaseSet)(nil), set) | 
|  | android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage) | 
|  | }) | 
|  | t.Run("invalid release in closed range start", func(t *testing.T) { | 
|  | set, err := parseBuildReleaseSet("A-S") | 
|  | android.AssertDeepEquals(t, "set", (*buildReleaseSet)(nil), set) | 
|  | android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage) | 
|  | }) | 
|  | t.Run("invalid release in closed range end", func(t *testing.T) { | 
|  | set, err := parseBuildReleaseSet("T-A") | 
|  | android.AssertDeepEquals(t, "set", (*buildReleaseSet)(nil), set) | 
|  | android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), invalidAReleaseMessage) | 
|  | }) | 
|  | t.Run("invalid closed range reversed", func(t *testing.T) { | 
|  | set, err := parseBuildReleaseSet("F1-S") | 
|  | android.AssertDeepEquals(t, "set", (*buildReleaseSet)(nil), set) | 
|  | android.AssertStringDoesContain(t, "errors", fmt.Sprint(err), `invalid closed range, start release "F1" is later than end release "S"`) | 
|  | }) | 
|  | } | 
|  |  | 
|  | func TestBuildReleaseSetContains(t *testing.T) { | 
|  | t.Run("contains", func(t *testing.T) { | 
|  | set, _ := parseBuildReleaseSet("F1-F2") | 
|  | android.AssertBoolEquals(t, "set contains F1", true, set.contains(buildReleaseFuture1)) | 
|  | android.AssertBoolEquals(t, "set does not contain S", false, set.contains(buildReleaseS)) | 
|  | android.AssertBoolEquals(t, "set contains F2", true, set.contains(buildReleaseFuture2)) | 
|  | android.AssertBoolEquals(t, "set does not contain T", false, set.contains(buildReleaseT)) | 
|  | }) | 
|  | } | 
|  |  | 
|  | func TestPropertyPrunerInvalidTag(t *testing.T) { | 
|  | type brokenStruct struct { | 
|  | Broken string `supported_build_releases:"A"` | 
|  | } | 
|  | type containingStruct struct { | 
|  | Nested brokenStruct | 
|  | } | 
|  |  | 
|  | t.Run("broken struct", func(t *testing.T) { | 
|  | android.AssertPanicMessageContains(t, "error", "invalid `supported_build_releases` tag on Broken of *sdk.brokenStruct: unknown release \"A\"", func() { | 
|  | newPropertyPrunerByBuildRelease(&brokenStruct{}, buildReleaseS) | 
|  | }) | 
|  | }) | 
|  |  | 
|  | t.Run("nested broken struct", func(t *testing.T) { | 
|  | android.AssertPanicMessageContains(t, "error", "invalid `supported_build_releases` tag on Nested.Broken of *sdk.containingStruct: unknown release \"A\"", func() { | 
|  | newPropertyPrunerByBuildRelease(&containingStruct{}, buildReleaseS) | 
|  | }) | 
|  | }) | 
|  | } | 
|  |  | 
|  | func TestPropertyPrunerByBuildRelease(t *testing.T) { | 
|  | type nested struct { | 
|  | F1_only string `supported_build_releases:"F1"` | 
|  | } | 
|  |  | 
|  | type mapped struct { | 
|  | Default string | 
|  | T_only  string `supported_build_releases:"T"` | 
|  | } | 
|  |  | 
|  | type testBuildReleasePruner struct { | 
|  | Default      string | 
|  | S_and_T_only string `supported_build_releases:"S-T"` | 
|  | T_later      string `supported_build_releases:"T+"` | 
|  | Nested       nested | 
|  | Mapped       map[string]*mapped | 
|  | } | 
|  |  | 
|  | inputFactory := func() testBuildReleasePruner { | 
|  | return testBuildReleasePruner{ | 
|  | Default:      "Default", | 
|  | S_and_T_only: "S_and_T_only", | 
|  | T_later:      "T_later", | 
|  | Nested: nested{ | 
|  | F1_only: "F1_only", | 
|  | }, | 
|  | Mapped: map[string]*mapped{ | 
|  | "one": { | 
|  | Default: "one-default", | 
|  | T_only:  "one-t-only", | 
|  | }, | 
|  | "two": { | 
|  | Default: "two-default", | 
|  | T_only:  "two-t-only", | 
|  | }, | 
|  | }, | 
|  | } | 
|  | } | 
|  |  | 
|  | marshal := func(t interface{}) string { | 
|  | bytes, err := json.MarshalIndent(t, "", "  ") | 
|  | if err != nil { | 
|  | panic(err) | 
|  | } | 
|  | return string(bytes) | 
|  | } | 
|  |  | 
|  | assertJsonEquals := func(t *testing.T, expected, actual interface{}) { | 
|  | t.Helper() | 
|  | expectedJson := marshal(expected) | 
|  | actualJson := marshal(actual) | 
|  | if actualJson != expectedJson { | 
|  | t.Errorf("test struct: expected:\n%s\n got:\n%s", expectedJson, actualJson) | 
|  | } | 
|  | } | 
|  |  | 
|  | t.Run("target S", func(t *testing.T) { | 
|  | testStruct := inputFactory() | 
|  | pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseS) | 
|  | pruner.pruneProperties(&testStruct) | 
|  |  | 
|  | expected := inputFactory() | 
|  | expected.T_later = "" | 
|  | expected.Nested.F1_only = "" | 
|  | expected.Mapped["one"].T_only = "" | 
|  | expected.Mapped["two"].T_only = "" | 
|  | assertJsonEquals(t, expected, testStruct) | 
|  | }) | 
|  |  | 
|  | t.Run("target T", func(t *testing.T) { | 
|  | testStruct := inputFactory() | 
|  | pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseT) | 
|  | pruner.pruneProperties(&testStruct) | 
|  |  | 
|  | expected := inputFactory() | 
|  | expected.Nested.F1_only = "" | 
|  | assertJsonEquals(t, expected, testStruct) | 
|  | }) | 
|  |  | 
|  | t.Run("target F1", func(t *testing.T) { | 
|  | testStruct := inputFactory() | 
|  | pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseFuture1) | 
|  | pruner.pruneProperties(&testStruct) | 
|  |  | 
|  | expected := inputFactory() | 
|  | expected.S_and_T_only = "" | 
|  | expected.Mapped["one"].T_only = "" | 
|  | expected.Mapped["two"].T_only = "" | 
|  | assertJsonEquals(t, expected, testStruct) | 
|  | }) | 
|  |  | 
|  | t.Run("target F2", func(t *testing.T) { | 
|  | testStruct := inputFactory() | 
|  | pruner := newPropertyPrunerByBuildRelease(&testStruct, buildReleaseFuture2) | 
|  | pruner.pruneProperties(&testStruct) | 
|  |  | 
|  | expected := inputFactory() | 
|  | expected.S_and_T_only = "" | 
|  | expected.Nested.F1_only = "" | 
|  | expected.Mapped["one"].T_only = "" | 
|  | expected.Mapped["two"].T_only = "" | 
|  | assertJsonEquals(t, expected, testStruct) | 
|  | }) | 
|  | } |