1// Copyright 2020 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 testing
16
17import (
18	"path/filepath"
19	"strconv"
20
21	"android/soong/android"
22	"android/soong/testing/test_spec_proto"
23	"google.golang.org/protobuf/proto"
24
25	"github.com/google/blueprint"
26)
27
28// ErrTestModuleDataNotFound is the error message for missing test module provider data.
29const ErrTestModuleDataNotFound = "The module '%s' does not provide test specification data. Hint: This issue could arise if either the module is not a valid testing module or if it lacks the required 'TestModuleProviderKey' provider.\n"
30
31func TestSpecFactory() android.Module {
32	module := &TestSpecModule{}
33
34	android.InitAndroidModule(module)
35	android.InitDefaultableModule(module)
36	module.AddProperties(&module.properties)
37
38	return module
39}
40
41type TestSpecModule struct {
42	android.ModuleBase
43	android.DefaultableModuleBase
44
45	// Properties for "test_spec"
46	properties struct {
47		// Specifies the name of the test config.
48		Name string
49		// Specifies the team ID.
50		TeamId string
51		// Specifies the list of tests covered under this module.
52		Tests []string
53	}
54}
55
56type testsDepTagType struct {
57	blueprint.BaseDependencyTag
58}
59
60var testsDepTag = testsDepTagType{}
61
62func (module *TestSpecModule) DepsMutator(ctx android.BottomUpMutatorContext) {
63	// Validate Properties
64	if len(module.properties.TeamId) == 0 {
65		ctx.PropertyErrorf("TeamId", "Team Id not found in the test_spec module. Hint: Maybe the TeamId property hasn't been properly specified.")
66	}
67	if !isInt(module.properties.TeamId) {
68		ctx.PropertyErrorf("TeamId", "Invalid value for Team ID. The Team ID must be an integer.")
69	}
70	if len(module.properties.Tests) == 0 {
71		ctx.PropertyErrorf("Tests", "Expected to attribute some test but none found. Hint: Maybe the test property hasn't been properly specified.")
72	}
73	ctx.AddDependency(ctx.Module(), testsDepTag, module.properties.Tests...)
74}
75func isInt(s string) bool {
76	_, err := strconv.Atoi(s)
77	return err == nil
78}
79
80// Provider published by TestSpec
81type TestSpecProviderData struct {
82	IntermediatePath android.WritablePath
83}
84
85var TestSpecProviderKey = blueprint.NewProvider[TestSpecProviderData]()
86
87type TestModuleProviderData struct {
88}
89
90var TestModuleProviderKey = blueprint.NewProvider[TestModuleProviderData]()
91
92func (module *TestSpecModule) GenerateAndroidBuildActions(ctx android.ModuleContext) {
93	for _, m := range ctx.GetDirectDepsWithTag(testsDepTag) {
94		if _, ok := android.OtherModuleProvider(ctx, m, TestModuleProviderKey); !ok {
95			ctx.ModuleErrorf(ErrTestModuleDataNotFound, m.Name())
96		}
97	}
98	bpFilePath := filepath.Join(ctx.ModuleDir(), ctx.BlueprintsFile())
99	metadataList := make(
100		[]*test_spec_proto.TestSpec_OwnershipMetadata, 0,
101		len(module.properties.Tests),
102	)
103	for _, test := range module.properties.Tests {
104		targetName := test
105		metadata := test_spec_proto.TestSpec_OwnershipMetadata{
106			TrendyTeamId: &module.properties.TeamId,
107			TargetName:   &targetName,
108			Path:         &bpFilePath,
109		}
110		metadataList = append(metadataList, &metadata)
111	}
112	intermediatePath := android.PathForModuleOut(
113		ctx, "intermediateTestSpecMetadata.pb",
114	)
115	testSpecMetadata := test_spec_proto.TestSpec{OwnershipMetadataList: metadataList}
116	protoData, err := proto.Marshal(&testSpecMetadata)
117	if err != nil {
118		ctx.ModuleErrorf("Error: %s", err.Error())
119	}
120	android.WriteFileRuleVerbatim(ctx, intermediatePath, string(protoData))
121
122	android.SetProvider(ctx,
123		TestSpecProviderKey, TestSpecProviderData{
124			IntermediatePath: intermediatePath,
125		},
126	)
127}
128