1// Copyright 2019 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	"io"
20	"reflect"
21	"testing"
22)
23
24type customModule struct {
25	ModuleBase
26	data      AndroidMkData
27	distFiles TaggedDistFiles
28}
29
30func (m *customModule) GenerateAndroidBuildActions(ctx ModuleContext) {
31	m.distFiles = m.GenerateTaggedDistFiles(ctx)
32}
33
34func (m *customModule) AndroidMk() AndroidMkData {
35	return AndroidMkData{
36		Custom: func(w io.Writer, name, prefix, moduleDir string, data AndroidMkData) {
37			m.data = data
38		},
39	}
40}
41
42func (m *customModule) OutputFiles(tag string) (Paths, error) {
43	switch tag {
44	case "":
45		return PathsForTesting("one.out"), nil
46	case ".multiple":
47		return PathsForTesting("two.out", "three/four.out"), nil
48	case ".another-tag":
49		return PathsForTesting("another.out"), nil
50	default:
51		return nil, fmt.Errorf("unsupported module reference tag %q", tag)
52	}
53}
54
55func (m *customModule) AndroidMkEntries() []AndroidMkEntries {
56	return []AndroidMkEntries{
57		{
58			Class:     "CUSTOM_MODULE",
59			DistFiles: m.distFiles,
60		},
61	}
62}
63
64func customModuleFactory() Module {
65	module := &customModule{}
66	InitAndroidModule(module)
67	return module
68}
69
70func TestAndroidMkSingleton_PassesUpdatedAndroidMkDataToCustomCallback(t *testing.T) {
71	bp := `
72	custom {
73		name: "foo",
74		required: ["bar"],
75		host_required: ["baz"],
76		target_required: ["qux"],
77	}
78	`
79
80	config := TestConfig(buildDir, nil, bp, nil)
81	config.inMake = true // Enable androidmk Singleton
82
83	ctx := NewTestContext()
84	ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
85	ctx.RegisterModuleType("custom", customModuleFactory)
86	ctx.Register(config)
87
88	_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
89	FailIfErrored(t, errs)
90	_, errs = ctx.PrepareBuildActions(config)
91	FailIfErrored(t, errs)
92
93	m := ctx.ModuleForTests("foo", "").Module().(*customModule)
94
95	assertEqual := func(expected interface{}, actual interface{}) {
96		if !reflect.DeepEqual(expected, actual) {
97			t.Errorf("%q expected, but got %q", expected, actual)
98		}
99	}
100	assertEqual([]string{"bar"}, m.data.Required)
101	assertEqual([]string{"baz"}, m.data.Host_required)
102	assertEqual([]string{"qux"}, m.data.Target_required)
103}
104
105func TestGetDistForGoals(t *testing.T) {
106	testCases := []struct {
107		bp                     string
108		expectedAndroidMkLines []string
109	}{
110		{
111			bp: `
112			custom {
113				name: "foo",
114				dist: {
115					targets: ["my_goal"]
116				}
117			}
118			`,
119			expectedAndroidMkLines: []string{
120				".PHONY: my_goal\n",
121				"$(call dist-for-goals,my_goal,one.out:one.out)\n",
122			},
123		},
124		{
125			bp: `
126			custom {
127				name: "foo",
128				dist: {
129					targets: ["my_goal"],
130					tag: ".another-tag",
131				}
132			}
133			`,
134			expectedAndroidMkLines: []string{
135				".PHONY: my_goal\n",
136				"$(call dist-for-goals,my_goal,another.out:another.out)\n",
137			},
138		},
139		{
140			bp: `
141			custom {
142				name: "foo",
143				dists: [
144					{
145						targets: ["my_goal"],
146						tag: ".another-tag",
147					},
148				],
149			}
150			`,
151			expectedAndroidMkLines: []string{
152				".PHONY: my_goal\n",
153				"$(call dist-for-goals,my_goal,another.out:another.out)\n",
154			},
155		},
156		{
157			bp: `
158			custom {
159				name: "foo",
160				dists: [
161					{
162						targets: ["my_goal"],
163					},
164					{
165						targets: ["my_second_goal", "my_third_goal"],
166					},
167				],
168			}
169			`,
170			expectedAndroidMkLines: []string{
171				".PHONY: my_goal\n",
172				"$(call dist-for-goals,my_goal,one.out:one.out)\n",
173				".PHONY: my_second_goal my_third_goal\n",
174				"$(call dist-for-goals,my_second_goal my_third_goal,one.out:one.out)\n",
175			},
176		},
177		{
178			bp: `
179			custom {
180				name: "foo",
181				dist: {
182					targets: ["my_goal"],
183				},
184				dists: [
185					{
186						targets: ["my_second_goal", "my_third_goal"],
187					},
188				],
189			}
190			`,
191			expectedAndroidMkLines: []string{
192				".PHONY: my_second_goal my_third_goal\n",
193				"$(call dist-for-goals,my_second_goal my_third_goal,one.out:one.out)\n",
194				".PHONY: my_goal\n",
195				"$(call dist-for-goals,my_goal,one.out:one.out)\n",
196			},
197		},
198		{
199			bp: `
200			custom {
201				name: "foo",
202				dist: {
203					targets: ["my_goal", "my_other_goal"],
204					tag: ".multiple",
205				},
206				dists: [
207					{
208						targets: ["my_second_goal"],
209						tag: ".multiple",
210					},
211					{
212						targets: ["my_third_goal"],
213						dir: "test/dir",
214					},
215					{
216						targets: ["my_fourth_goal"],
217						suffix: ".suffix",
218					},
219					{
220						targets: ["my_fifth_goal"],
221						dest: "new-name",
222					},
223					{
224						targets: ["my_sixth_goal"],
225						dest: "new-name",
226						dir: "some/dir",
227						suffix: ".suffix",
228					},
229				],
230			}
231			`,
232			expectedAndroidMkLines: []string{
233				".PHONY: my_second_goal\n",
234				"$(call dist-for-goals,my_second_goal,two.out:two.out)\n",
235				"$(call dist-for-goals,my_second_goal,three/four.out:four.out)\n",
236				".PHONY: my_third_goal\n",
237				"$(call dist-for-goals,my_third_goal,one.out:test/dir/one.out)\n",
238				".PHONY: my_fourth_goal\n",
239				"$(call dist-for-goals,my_fourth_goal,one.out:one.suffix.out)\n",
240				".PHONY: my_fifth_goal\n",
241				"$(call dist-for-goals,my_fifth_goal,one.out:new-name)\n",
242				".PHONY: my_sixth_goal\n",
243				"$(call dist-for-goals,my_sixth_goal,one.out:some/dir/new-name.suffix)\n",
244				".PHONY: my_goal my_other_goal\n",
245				"$(call dist-for-goals,my_goal my_other_goal,two.out:two.out)\n",
246				"$(call dist-for-goals,my_goal my_other_goal,three/four.out:four.out)\n",
247			},
248		},
249	}
250
251	for _, testCase := range testCases {
252		config := TestConfig(buildDir, nil, testCase.bp, nil)
253		config.inMake = true // Enable androidmk Singleton
254
255		ctx := NewTestContext()
256		ctx.RegisterSingletonType("androidmk", AndroidMkSingleton)
257		ctx.RegisterModuleType("custom", customModuleFactory)
258		ctx.Register(config)
259
260		_, errs := ctx.ParseFileList(".", []string{"Android.bp"})
261		FailIfErrored(t, errs)
262		_, errs = ctx.PrepareBuildActions(config)
263		FailIfErrored(t, errs)
264
265		module := ctx.ModuleForTests("foo", "").Module().(*customModule)
266		entries := AndroidMkEntriesForTest(t, config, "", module)
267		if len(entries) != 1 {
268			t.Errorf("Expected a single AndroidMk entry, got %d", len(entries))
269		}
270		androidMkLines := entries[0].GetDistForGoals(module)
271
272		if len(androidMkLines) != len(testCase.expectedAndroidMkLines) {
273			t.Errorf(
274				"Expected %d AndroidMk lines, got %d:\n%v",
275				len(testCase.expectedAndroidMkLines),
276				len(androidMkLines),
277				androidMkLines,
278			)
279		}
280		for idx, line := range androidMkLines {
281			expectedLine := testCase.expectedAndroidMkLines[idx]
282			if line != expectedLine {
283				t.Errorf(
284					"Expected AndroidMk line to be '%s', got '%s'",
285					line,
286					expectedLine,
287				)
288			}
289		}
290	}
291}
292