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

Commit 4277d617 authored by Aditya Choudhary's avatar Aditya Choudhary Committed by Gerrit Code Review
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
parents 3b12b769 51f97c19
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