1// Copyright 2017 Google Inc. All rights reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package android
16
17import (
18	"fmt"
19	"path/filepath"
20	"reflect"
21	"strings"
22	"testing"
23)
24
25func validateConfigAnnotations(configurable jsonConfigurable) (err error) {
26	reflectType := reflect.TypeOf(configurable)
27	reflectType = reflectType.Elem()
28	for i := 0; i < reflectType.NumField(); i++ {
29		field := reflectType.Field(i)
30		jsonTag := field.Tag.Get("json")
31		// Check for mistakes in the json tag
32		if jsonTag != "" && !strings.HasPrefix(jsonTag, ",") {
33			if !strings.Contains(jsonTag, ",") {
34				// Probably an accidental rename, most likely "omitempty" instead of ",omitempty"
35				return fmt.Errorf("Field %s.%s has tag %s which specifies to change its json field name to %q.\n"+
36					"Did you mean to use an annotation of %q?\n"+
37					"(Alternatively, to change the json name of the field, rename the field in source instead.)",
38					reflectType.Name(), field.Name, field.Tag, jsonTag, ","+jsonTag)
39			} else {
40				// Although this rename was probably intentional,
41				// a json annotation is still more confusing than renaming the source variable
42				requestedName := strings.Split(jsonTag, ",")[0]
43				return fmt.Errorf("Field %s.%s has tag %s which specifies to change its json field name to %q.\n"+
44					"To change the json name of the field, rename the field in source instead.",
45					reflectType.Name(), field.Name, field.Tag, requestedName)
46
47			}
48		}
49	}
50	return nil
51}
52
53type configType struct {
54	PopulateMe *bool `json:"omitempty"`
55}
56
57func (c *configType) SetDefaultConfig() {
58}
59
60// tests that ValidateConfigAnnotation works
61func TestValidateConfigAnnotations(t *testing.T) {
62	config := configType{}
63	err := validateConfigAnnotations(&config)
64	expectedError := `Field configType.PopulateMe has tag json:"omitempty" which specifies to change its json field name to "omitempty".
65Did you mean to use an annotation of ",omitempty"?
66(Alternatively, to change the json name of the field, rename the field in source instead.)`
67	if err.Error() != expectedError {
68		t.Errorf("Incorrect error; expected:\n"+
69			"%s\n"+
70			"got:\n"+
71			"%s",
72			expectedError, err.Error())
73	}
74}
75
76// run validateConfigAnnotations against each type that might have json annotations
77func TestProductConfigAnnotations(t *testing.T) {
78	err := validateConfigAnnotations(&productVariables{})
79	if err != nil {
80		t.Errorf(err.Error())
81	}
82}
83
84func TestMissingVendorConfig(t *testing.T) {
85	c := &config{}
86	if c.VendorConfig("test").Bool("not_set") {
87		t.Errorf("Expected false")
88	}
89}
90
91func verifyProductVariableMarshaling(t *testing.T, v productVariables) {
92	dir := t.TempDir()
93	path := filepath.Join(dir, "test.variables")
94	err := saveToConfigFile(&v, path)
95	if err != nil {
96		t.Errorf("Couldn't save default product config: %q", err)
97	}
98
99	var v2 productVariables
100	err = loadFromConfigFile(&v2, path)
101	if err != nil {
102		t.Errorf("Couldn't load default product config: %q", err)
103	}
104}
105func TestDefaultProductVariableMarshaling(t *testing.T) {
106	v := productVariables{}
107	v.SetDefaultConfig()
108	verifyProductVariableMarshaling(t, v)
109}
110
111func TestBootJarsMarshaling(t *testing.T) {
112	v := productVariables{}
113	v.SetDefaultConfig()
114	v.BootJars = ConfiguredJarList{
115		apexes: []string{"apex"},
116		jars:   []string{"jar"},
117	}
118
119	verifyProductVariableMarshaling(t, v)
120}
121
122func assertStringEquals(t *testing.T, expected, actual string) {
123	if actual != expected {
124		t.Errorf("expected %q found %q", expected, actual)
125	}
126}
127
128func TestConfiguredJarList(t *testing.T) {
129	list1 := CreateTestConfiguredJarList([]string{"apex1:jarA"})
130
131	t.Run("create", func(t *testing.T) {
132		assertStringEquals(t, "apex1:jarA", list1.String())
133	})
134
135	t.Run("create invalid - missing apex", func(t *testing.T) {
136		defer func() {
137			err := recover().(error)
138			assertStringEquals(t, "malformed (apex, jar) pair: 'jarA', expected format: <apex>:<jar>", err.Error())
139		}()
140		CreateTestConfiguredJarList([]string{"jarA"})
141	})
142
143	t.Run("create invalid - empty apex", func(t *testing.T) {
144		defer func() {
145			err := recover().(error)
146			assertStringEquals(t, "invalid apex '' in <apex>:<jar> pair ':jarA', expected format: <apex>:<jar>", err.Error())
147		}()
148		CreateTestConfiguredJarList([]string{":jarA"})
149	})
150
151	list2 := list1.Append("apex2", "jarB")
152	t.Run("append", func(t *testing.T) {
153		assertStringEquals(t, "apex1:jarA,apex2:jarB", list2.String())
154	})
155
156	t.Run("append does not modify", func(t *testing.T) {
157		assertStringEquals(t, "apex1:jarA", list1.String())
158	})
159
160	// Make sure that two lists created by appending to the same list do not share storage.
161	list3 := list1.Append("apex3", "jarC")
162	t.Run("append does not share", func(t *testing.T) {
163		assertStringEquals(t, "apex1:jarA,apex2:jarB", list2.String())
164		assertStringEquals(t, "apex1:jarA,apex3:jarC", list3.String())
165	})
166
167	list4 := list3.RemoveList(list1)
168	t.Run("remove", func(t *testing.T) {
169		assertStringEquals(t, "apex3:jarC", list4.String())
170	})
171
172	t.Run("remove does not modify", func(t *testing.T) {
173		assertStringEquals(t, "apex1:jarA,apex3:jarC", list3.String())
174	})
175
176	// Make sure that two lists created by removing from the same list do not share storage.
177	list5 := list3.RemoveList(CreateTestConfiguredJarList([]string{"apex3:jarC"}))
178	t.Run("remove", func(t *testing.T) {
179		assertStringEquals(t, "apex3:jarC", list4.String())
180		assertStringEquals(t, "apex1:jarA", list5.String())
181	})
182}
183