Merge "Add metadata generator tool for test spec metadata generation. Bug: 296873595 Test: Manual test (use go test inside tools/metadata/testdata)" into main
This commit is contained in:
commit
4277d617f2
14 changed files with 411 additions and 0 deletions
14
tools/metadata/Android.bp
Normal file
14
tools/metadata/Android.bp
Normal file
|
@ -0,0 +1,14 @@
|
|||
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",
|
||||
]
|
||||
}
|
169
tools/metadata/generator.go
Normal file
169
tools/metadata/generator.go
Normal file
|
@ -0,0 +1,169 @@
|
|||
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)
|
||||
}
|
7
tools/metadata/go.mod
Normal file
7
tools/metadata/go.mod
Normal file
|
@ -0,0 +1,7 @@
|
|||
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
|
10
tools/metadata/go.work
Normal file
10
tools/metadata/go.work
Normal file
|
@ -0,0 +1,10 @@
|
|||
go 1.18
|
||||
|
||||
use (
|
||||
.
|
||||
../../../../external/golang-protobuf
|
||||
../../../soong/testing/test_spec_proto
|
||||
|
||||
)
|
||||
|
||||
replace google.golang.org/protobuf v0.0.0 => ../../../../external/golang-protobuf
|
22
tools/metadata/testdata/expectedOutputFile.txt
vendored
Normal file
22
tools/metadata/testdata/expectedOutputFile.txt
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
.
|
||||
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
|
13
tools/metadata/testdata/file1.txt
vendored
Normal file
13
tools/metadata/testdata/file1.txt
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
.
|
||||
java-test-module-name-one
|
||||
Android.bp12345
|
||||
.
|
||||
java-test-module-name-two
|
||||
Android.bp12345
|
||||
.
|
||||
java-test-module-name-two
|
||||
Asdfghj.bp12345
|
||||
.
|
||||
java-test-module-name-two
|
||||
Azxcvbn.bp12345
|
25
tools/metadata/testdata/file2.txt
vendored
Normal file
25
tools/metadata/testdata/file2.txt
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
.
|
||||
java-test-module-name-one
|
||||
Android.bp12345
|
||||
.
|
||||
java-test-module-name-six
|
||||
Android.bp12346
|
||||
.
|
||||
java-test-module-name-one
|
||||
Android.bp12345
|
||||
.
|
||||
java-test-module-name-six
|
||||
Aqwerty.bp12346
|
||||
.
|
||||
java-test-module-name-six
|
||||
Apoiuyt.bp12346
|
||||
.
|
||||
java-test-module-name-six
|
||||
Apoiuyt.bp12346
|
||||
.
|
||||
java-test-module-name-six
|
||||
Apoiuyt.bp12346
|
||||
.
|
||||
java-test-module-name-six
|
||||
Apoiuyt.bp12346
|
13
tools/metadata/testdata/file3.txt
vendored
Normal file
13
tools/metadata/testdata/file3.txt
vendored
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
.
|
||||
java-test-module-name-one
|
||||
Android.bp12345
|
||||
.
|
||||
java-test-module-name-two
|
||||
Android.bp12345
|
||||
.
|
||||
java-test-module-name-two
|
||||
Asdfghj.bp12345
|
||||
.
|
||||
java-test-module-name-two
|
||||
Azxcvbn.bp12345
|
25
tools/metadata/testdata/file4.txt
vendored
Normal file
25
tools/metadata/testdata/file4.txt
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
|
||||
.
|
||||
java-test-module-name-one
|
||||
Android.bp12345
|
||||
.
|
||||
java-test-module-name-six
|
||||
Android.bp12346
|
||||
.
|
||||
java-test-module-name-one
|
||||
Android.bp12346
|
||||
.
|
||||
java-test-module-name-six
|
||||
Aqwerty.bp12346
|
||||
.
|
||||
java-test-module-name-six
|
||||
Apoiuyt.bp12346
|
||||
.
|
||||
java-test-module-name-six
|
||||
Apoiuyt.bp12346
|
||||
.
|
||||
java-test-module-name-six
|
||||
Apoiuyt.bp12346
|
||||
.
|
||||
java-test-module-name-six
|
||||
Apoiuyt.bp12346
|
22
tools/metadata/testdata/generatedOutputFile.txt
vendored
Normal file
22
tools/metadata/testdata/generatedOutputFile.txt
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
.
|
||||
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
|
2
tools/metadata/testdata/inputFiles.txt
vendored
Normal file
2
tools/metadata/testdata/inputFiles.txt
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
file1.txt
|
||||
file2.txt
|
2
tools/metadata/testdata/inputFilesNegativeCase.txt
vendored
Normal file
2
tools/metadata/testdata/inputFilesNegativeCase.txt
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
file3.txt
|
||||
file4.txt
|
65
tools/metadata/testdata/metadata_test.go
vendored
Normal file
65
tools/metadata/testdata/metadata_test.go
vendored
Normal file
|
@ -0,0 +1,65 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMetadata(t *testing.T) {
|
||||
cmd := exec.Command(
|
||||
"metadata", "-inputFile", "./inputFiles.txt", "-outputFile",
|
||||
"./generatedOutputFile.txt",
|
||||
)
|
||||
stderr, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("Error running metadata command: %s. Error: %v", stderr, err)
|
||||
}
|
||||
|
||||
// Read the contents of the expected output file
|
||||
expectedOutput, err := ioutil.ReadFile("./expectedOutputFile.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading expected output file: %s", err)
|
||||
}
|
||||
|
||||
// Read the contents of the generated output file
|
||||
generatedOutput, err := ioutil.ReadFile("./generatedOutputFile.txt")
|
||||
if err != nil {
|
||||
t.Fatalf("Error reading generated output file: %s", err)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
// Compare the contents
|
||||
if string(expectedOutput) != string(generatedOutput) {
|
||||
t.Errorf("Generated file contents do not match the expected output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMetadataNegativeCase(t *testing.T) {
|
||||
cmd := exec.Command(
|
||||
"metadata", "-inputFile", "./inputFilesNegativeCase.txt", "-outputFile",
|
||||
"./generatedOutputFileNegativeCase.txt",
|
||||
)
|
||||
stderr, err := cmd.CombinedOutput()
|
||||
if err == nil {
|
||||
t.Fatalf(
|
||||
"Expected an error, but the metadata command executed successfully. Output: %s",
|
||||
stderr,
|
||||
)
|
||||
}
|
||||
|
||||
expectedError := "Conflicting trendy team IDs found for java-test-module" +
|
||||
"-name-one at:\nAndroid.bp with teamId: 12346," +
|
||||
"\nAndroid.bp with teamId: 12345"
|
||||
if !strings.Contains(
|
||||
strings.TrimSpace(string(stderr)), strings.TrimSpace(expectedError),
|
||||
) {
|
||||
t.Errorf(
|
||||
"Unexpected error message. Expected to contain: %s, Got: %s",
|
||||
expectedError, stderr,
|
||||
)
|
||||
}
|
||||
}
|
22
tools/metadata/testdata/outputFile.txt
vendored
Normal file
22
tools/metadata/testdata/outputFile.txt
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
|
||||
.
|
||||
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
|
Loading…
Reference in a new issue