// Copyright 2020 Google Inc. All rights reserved. // // 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 testing import ( "path/filepath" "strconv" "android/soong/android" "android/soong/testing/test_spec_proto" "google.golang.org/protobuf/proto" "github.com/google/blueprint" ) // ErrTestModuleDataNotFound is the error message for missing test module provider data. const 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" func TestSpecFactory() android.Module { module := &TestSpecModule{} android.InitAndroidModule(module) android.InitDefaultableModule(module) module.AddProperties(&module.properties) return module } type TestSpecModule struct { android.ModuleBase android.DefaultableModuleBase // Properties for "test_spec" properties struct { // Specifies the name of the test config. Name string // Specifies the team ID. TeamId string // Specifies the list of tests covered under this module. Tests []string } } type testsDepTagType struct { blueprint.BaseDependencyTag } var testsDepTag = testsDepTagType{} func (module *TestSpecModule) DepsMutator(ctx android.BottomUpMutatorContext) { // Validate Properties if len(module.properties.TeamId) == 0 { ctx.PropertyErrorf("TeamId", "Team Id not found in the test_spec module. Hint: Maybe the TeamId property hasn't been properly specified.") } if !isInt(module.properties.TeamId) { ctx.PropertyErrorf("TeamId", "Invalid value for Team ID. The Team ID must be an integer.") } if len(module.properties.Tests) == 0 { ctx.PropertyErrorf("Tests", "Expected to attribute some test but none found. Hint: Maybe the test property hasn't been properly specified.") } ctx.AddDependency(ctx.Module(), testsDepTag, module.properties.Tests...) } func isInt(s string) bool { _, err := strconv.Atoi(s) return err == nil } // Provider published by TestSpec type TestSpecProviderData struct { IntermediatePath android.WritablePath } var TestSpecProviderKey = blueprint.NewProvider[TestSpecProviderData]() type TestModuleProviderData struct { } var TestModuleProviderKey = blueprint.NewProvider[TestModuleProviderData]() func (module *TestSpecModule) GenerateAndroidBuildActions(ctx android.ModuleContext) { for _, m := range ctx.GetDirectDepsWithTag(testsDepTag) { if _, ok := android.OtherModuleProvider(ctx, m, TestModuleProviderKey); !ok { ctx.ModuleErrorf(ErrTestModuleDataNotFound, m.Name()) } } bpFilePath := filepath.Join(ctx.ModuleDir(), ctx.BlueprintsFile()) metadataList := make( []*test_spec_proto.TestSpec_OwnershipMetadata, 0, len(module.properties.Tests), ) for _, test := range module.properties.Tests { targetName := test metadata := test_spec_proto.TestSpec_OwnershipMetadata{ TrendyTeamId: &module.properties.TeamId, TargetName: &targetName, Path: &bpFilePath, } metadataList = append(metadataList, &metadata) } intermediatePath := android.PathForModuleOut( ctx, "intermediateTestSpecMetadata.pb", ) testSpecMetadata := test_spec_proto.TestSpec{OwnershipMetadataList: metadataList} protoData, err := proto.Marshal(&testSpecMetadata) if err != nil { ctx.ModuleErrorf("Error: %s", err.Error()) } android.WriteFileRuleVerbatim(ctx, intermediatePath, string(protoData)) android.SetProvider(ctx, TestSpecProviderKey, TestSpecProviderData{ IntermediatePath: intermediatePath, }, ) }