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

Commit 51f97c19 authored by Aditya Choudhary's avatar Aditya Choudhary
Browse files

Add metadata generator tool for test spec metadata generation.

Bug: 296873595
Test: Manual test (use go test inside tools/metadata/testdata)

Change-Id: I404b57224828149f26bcf4deadb662f513886231
parent 2c8ece0b
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