Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit bb858bb6 authored by Aditya Choudhary's avatar Aditya Choudhary Committed by Automerger Merge Worker
Browse files

Merge "Add metadata generator tool for test spec metadata generation. Bug:...

Merge "Add metadata generator tool for test spec metadata generation. Bug: 296873595 Test: Manual test (use go test inside tools/metadata/testdata)" into main am: 4277d617

Original change: https://android-review.googlesource.com/c/platform/build/+/2815214



Change-Id: I23419ea04ee69275aad70e6e485d33dd7ff6c9b5
Signed-off-by: default avatarAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
parents 07d41e27 4277d617
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
package {
    default_applicable_licenses: ["Android-Apache-2.0"],
}

blueprint_go_binary {
    name: "metadata",
    deps: [
            "soong-testing-test_spec_proto",
            "golang-protobuf-proto",
        ],
    srcs: [
        "generator.go",
    ]
}
 No newline at end of file
+169 −0
Original line number Diff line number Diff line
package main

import (
	"flag"
	"fmt"
	"io"
	"log"
	"os"
	"sort"
	"strings"
	"sync"

	"android/soong/testing/test_spec_proto"
	"google.golang.org/protobuf/proto"
)

type keyToLocksMap struct {
	locks sync.Map
}

func (kl *keyToLocksMap) GetLockForKey(key string) *sync.Mutex {
	mutex, _ := kl.locks.LoadOrStore(key, &sync.Mutex{})
	return mutex.(*sync.Mutex)
}

func getSortedKeys(syncMap *sync.Map) []string {
	var allKeys []string
	syncMap.Range(
		func(key, _ interface{}) bool {
			allKeys = append(allKeys, key.(string))
			return true
		},
	)

	sort.Strings(allKeys)
	return allKeys
}

func writeOutput(
	outputFile string,
	allMetadata []*test_spec_proto.TestSpec_OwnershipMetadata,
) {
	testSpec := &test_spec_proto.TestSpec{
		OwnershipMetadataList: allMetadata,
	}
	data, err := proto.Marshal(testSpec)
	if err != nil {
		log.Fatal(err)
	}
	file, err := os.Create(outputFile)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	_, err = file.Write(data)
	if err != nil {
		log.Fatal(err)
	}
}

func readFileToString(filePath string) string {
	file, err := os.Open(filePath)
	if err != nil {
		log.Fatal(err)
	}
	defer file.Close()

	data, err := io.ReadAll(file)
	if err != nil {
		log.Fatal(err)
	}
	return string(data)
}

func processProtobuf(
	filePath string, ownershipMetadataMap *sync.Map, keyLocks *keyToLocksMap,
	errCh chan error, wg *sync.WaitGroup,
) {
	defer wg.Done()

	fileContent := strings.TrimRight(readFileToString(filePath), "\n")
	testData := test_spec_proto.TestSpec{}
	err := proto.Unmarshal([]byte(fileContent), &testData)
	if err != nil {
		errCh <- err
		return
	}

	ownershipMetadata := testData.GetOwnershipMetadataList()
	for _, metadata := range ownershipMetadata {
		key := metadata.GetTargetName()
		lock := keyLocks.GetLockForKey(key)
		lock.Lock()

		value, loaded := ownershipMetadataMap.LoadOrStore(
			key, []*test_spec_proto.TestSpec_OwnershipMetadata{metadata},
		)
		if loaded {
			existingMetadata := value.([]*test_spec_proto.TestSpec_OwnershipMetadata)
			isDuplicate := false
			for _, existing := range existingMetadata {
				if metadata.GetTrendyTeamId() != existing.GetTrendyTeamId() {
					errCh <- fmt.Errorf(
						"Conflicting trendy team IDs found for %s at:\n%s with teamId"+
							": %s,\n%s with teamId: %s",
						key,
						metadata.GetPath(), metadata.GetTrendyTeamId(), existing.GetPath(),
						existing.GetTrendyTeamId(),
					)

					lock.Unlock()
					return
				}
				if metadata.GetTrendyTeamId() == existing.GetTrendyTeamId() && metadata.GetPath() == existing.GetPath() {
					isDuplicate = true
					break
				}
			}
			if !isDuplicate {
				existingMetadata = append(existingMetadata, metadata)
				ownershipMetadataMap.Store(key, existingMetadata)
			}
		}

		lock.Unlock()
	}
}

func main() {
	inputFile := flag.String("inputFile", "", "Input file path")
	outputFile := flag.String("outputFile", "", "Output file path")
	flag.Parse()

	if *inputFile == "" || *outputFile == "" {
		fmt.Println("Usage: metadata -inputFile <input file path> -outputFile <output file path>")
		os.Exit(1)
	}

	inputFileData := strings.TrimRight(readFileToString(*inputFile), "\n")
	filePaths := strings.Split(inputFileData, "\n")
	ownershipMetadataMap := &sync.Map{}
	keyLocks := &keyToLocksMap{}
	errCh := make(chan error, len(filePaths))
	var wg sync.WaitGroup

	for _, filePath := range filePaths {
		wg.Add(1)
		go processProtobuf(filePath, ownershipMetadataMap, keyLocks, errCh, &wg)
	}

	wg.Wait()
	close(errCh)

	for err := range errCh {
		log.Fatal(err)
	}

	allKeys := getSortedKeys(ownershipMetadataMap)
	var allMetadata []*test_spec_proto.TestSpec_OwnershipMetadata

	for _, key := range allKeys {
		value, _ := ownershipMetadataMap.Load(key)
		metadataList := value.([]*test_spec_proto.TestSpec_OwnershipMetadata)
		allMetadata = append(allMetadata, metadataList...)
	}

	writeOutput(*outputFile, allMetadata)
}

tools/metadata/go.mod

0 → 100644
+7 −0
Original line number Diff line number Diff line
module android/soong/tools/metadata

require google.golang.org/protobuf v0.0.0

replace google.golang.org/protobuf v0.0.0 => ../../../external/golang-protobuf

go 1.18
 No newline at end of file

tools/metadata/go.work

0 → 100644
+10 −0
Original line number Diff line number Diff line
go 1.18

use (
	.
	../../../../external/golang-protobuf
	../../../soong/testing/test_spec_proto

)

replace google.golang.org/protobuf v0.0.0 => ../../../../external/golang-protobuf
+22 −0
Original line number Diff line number Diff line

.
java-test-module-name-one
Android.bp12345
.
java-test-module-name-six
Android.bp12346
.
java-test-module-name-six
Aqwerty.bp12346
.
java-test-module-name-six
Apoiuyt.bp12346
.
java-test-module-name-two
Android.bp12345
.
java-test-module-name-two
Asdfghj.bp12345
.
java-test-module-name-two
Azxcvbn.bp12345
 No newline at end of file
Loading