cros-test-finder: identify all tests matched suite definition
Identify all tests matched suite definition
$ cros-test-finder --input \
./src/chromiumos/test/test_finder/data/request.json \
-metadatadir ./src/chromiumos/test/execution/data/metadata \
-output finder.json
2021/09/30 23:53:10 cros-test-finder version cros-test-finder-9999
2021/09/30 23:53:10 Reading metadata from directory: ./src/chromiumos/test/execution/data/metadata
2021/09/30 23:53:10 Reading input file: ./src/chromiumos/test/test_finder/data/request.json
2021/09/30 23:53:10 Writing output file: finder.json
BUG=b:200620228
TEST=./fast_build.sh -T; sudo emerge cros-test-finder; cros-test-finder
Change-Id: I55de6078683edc354c67ffe4457a343032e7a7f2
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/dev-util/+/3198804
Reviewed-by: Sean McAllister <smcallis@google.com>
Reviewed-by: Jesse McGuire <jessemcguire@google.com>
Reviewed-by: Tim Bain <tbain@google.com>
Tested-by: Seewai Fu <seewaifu@google.com>
Commit-Queue: Seewai Fu <seewaifu@google.com>
diff --git a/src/chromiumos/test/test_finder/cmd/cros-test-finder/main.go b/src/chromiumos/test/test_finder/cmd/cros-test-finder/main.go
index 5809866..093ca8e 100644
--- a/src/chromiumos/test/test_finder/cmd/cros-test-finder/main.go
+++ b/src/chromiumos/test/test_finder/cmd/cros-test-finder/main.go
@@ -12,9 +12,15 @@
"log"
"os"
"path/filepath"
+ "strings"
"time"
+ "github.com/golang/protobuf/jsonpb"
+ "go.chromium.org/chromiumos/config/go/test/api"
+
"chromiumos/test/execution/errors"
+ "chromiumos/test/util/finder"
+ "chromiumos/test/util/metadata"
)
const (
@@ -51,6 +57,62 @@
return log.New(mw, "", log.LstdFlags|log.LUTC)
}
+// readInput reads a CrosTestFinderRequest jsonproto file and returns a pointer to RunTestsRequest.
+func readInput(fileName string) (*api.CrosTestFinderRequest, error) {
+ f, err := os.Open(fileName)
+ if err != nil {
+ return nil, errors.NewStatusError(errors.IOAccessError,
+ fmt.Errorf("fail to read file %v: %v", fileName, err))
+ }
+ req := api.CrosTestFinderRequest{}
+ if err := jsonpb.Unmarshal(f, &req); err != nil {
+ return nil, errors.NewStatusError(errors.UnmarshalError,
+ fmt.Errorf("fail to unmarshal file %v: %v", fileName, err))
+ }
+ return &req, nil
+}
+
+// writeOutput writes a CrosTestFinderResponse json.
+func writeOutput(output string, resp *api.CrosTestFinderResponse) error {
+ f, err := os.Create(output)
+ if err != nil {
+ return errors.NewStatusError(errors.IOCreateError,
+ fmt.Errorf("fail to create file %v: %v", output, err))
+ }
+ m := jsonpb.Marshaler{}
+ if err := m.Marshal(f, resp); err != nil {
+ return errors.NewStatusError(errors.MarshalError,
+ fmt.Errorf("failed to marshall response to file %v: %v", output, err))
+ }
+ return nil
+}
+
+// combineTestSuiteNames combines a list of test suite names to one single name.
+func combineTestSuiteNames(suites []*api.TestSuite) string {
+ if len(suites) == 0 {
+ return "CombinedSuite"
+ }
+ var names []string
+ for _, s := range suites {
+ names = append(names, s.Name)
+ }
+ return strings.Join(names, ",")
+}
+
+// metadataToTestSuite convert a list of test metadata to a test suite.
+func metadataToTestSuite(name string, mdList []*api.TestCaseMetadata) *api.TestSuite {
+ testIds := []*api.TestCase_Id{}
+ for _, md := range mdList {
+ testIds = append(testIds, md.TestCase.Id)
+ }
+ return &api.TestSuite{
+ Name: name,
+ Spec: &api.TestSuite_TestCaseIds{
+ TestCaseIds: &api.TestCaseIdList{TestCaseIds: testIds},
+ },
+ }
+}
+
func main() {
os.Exit(func() int {
t := time.Now()
@@ -79,9 +141,37 @@
logger := newLogger(logFile)
logger.Println("cros-test-finder version ", Version)
- logger.Println("cros-test-finder input file: ", *input)
- logger.Println("cros-test-finder output file ", *output)
- logger.Println("cros-test-finder metadata directory: ", *metadataDir)
+
+ logger.Println("Reading metadata from directory: ", *metadataDir)
+ allTestMetadata, err := metadata.ReadDir(*metadataDir)
+ if err != nil {
+ logger.Println("Error: ", err)
+ return errors.WriteError(os.Stderr, err)
+ }
+
+ logger.Println("Reading input file: ", *input)
+ req, err := readInput(*input)
+ if err != nil {
+ logger.Println("Error: ", err)
+ return errors.WriteError(os.Stderr, err)
+ }
+
+ suiteName := combineTestSuiteNames(req.TestSuites)
+
+ selectedTestMetadata, err := finder.MatchedTestsForSuites(allTestMetadata.Values, req.TestSuites)
+ if err != nil {
+ logger.Println("Error: ", err)
+ return errors.WriteError(os.Stderr, err)
+ }
+
+ resultTestSuite := metadataToTestSuite(suiteName, selectedTestMetadata)
+
+ logger.Println("Writing output file: ", *output)
+ rspn := &api.CrosTestFinderResponse{TestSuites: []*api.TestSuite{resultTestSuite}}
+ if err := writeOutput(*output, rspn); err != nil {
+ logger.Println("Error: ", err)
+ return errors.WriteError(os.Stderr, err)
+ }
return 0
}())
diff --git a/src/chromiumos/test/test_finder/cmd/cros-test-finder/main_test.go b/src/chromiumos/test/test_finder/cmd/cros-test-finder/main_test.go
new file mode 100644
index 0000000..5637b0e
--- /dev/null
+++ b/src/chromiumos/test/test_finder/cmd/cros-test-finder/main_test.go
@@ -0,0 +1,242 @@
+// Copyright 2021 The Chromium OS Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package main
+
+import (
+ "io/ioutil"
+ "os"
+ "path/filepath"
+ "testing"
+
+ "github.com/golang/protobuf/jsonpb"
+ "github.com/google/go-cmp/cmp"
+ "go.chromium.org/chromiumos/config/go/test/api"
+)
+
+func TestReadInput(t *testing.T) {
+ expReq := &api.CrosTestFinderRequest{
+ TestSuites: []*api.TestSuite{
+ {
+ Name: "suite1",
+ Spec: &api.TestSuite_TestCaseIds{
+ TestCaseIds: &api.TestCaseIdList{
+ TestCaseIds: []*api.TestCase_Id{
+ {
+ Value: "example.Pass",
+ },
+ {
+ Value: "example.Fail",
+ },
+ },
+ },
+ },
+ },
+ {
+ Name: "suite2",
+ Spec: &api.TestSuite_TestCaseTagCriteria_{
+ TestCaseTagCriteria: &api.TestSuite_TestCaseTagCriteria{
+ Tags: []string{"group:meta"},
+ },
+ },
+ },
+ },
+ }
+
+ m := jsonpb.Marshaler{}
+ encodedData, err := m.MarshalToString(expReq)
+ if err != nil {
+ t.Fatal("Failed to marshall request")
+ }
+ td, err := ioutil.TempDir("", "testexecserver_TestReadInput_*")
+ if err != nil {
+ t.Fatal("Failed to create temporary dictectory: ", err)
+ }
+
+ defer os.RemoveAll(td)
+ fn := filepath.Join(td, "t.json")
+ if err := ioutil.WriteFile(fn, []byte(encodedData), 0644); err != nil {
+ t.Fatalf("Failed to write file %v: %v", fn, err)
+ }
+ req, err := readInput(fn)
+ if err != nil {
+ t.Fatalf("Failed to read input file %v: %v", fn, err)
+ }
+ if diff := cmp.Diff(req, expReq, cmp.AllowUnexported(api.RunTestsRequest{})); diff != "" {
+ t.Errorf("Got unexpected request from readInput (-got +want):\n%s", diff)
+ }
+}
+
+func TestWriteOutput(t *testing.T) {
+ expectedRspn := api.CrosTestFinderResponse{
+ TestSuites: []*api.TestSuite{
+ {
+ Name: "suite1",
+ Spec: &api.TestSuite_TestCaseIds{
+ TestCaseIds: &api.TestCaseIdList{
+ TestCaseIds: []*api.TestCase_Id{
+ {
+ Value: "example.Pass",
+ },
+ {
+ Value: "example.Fail",
+ },
+ },
+ },
+ },
+ },
+ },
+ }
+ td, err := ioutil.TempDir("", "faketestrunner_TestWriteOutput_*")
+ if err != nil {
+ t.Fatal("Failed to create temporary dictectory: ", err)
+ }
+ defer os.RemoveAll(td)
+ fn := filepath.Join(td, "t.json")
+ if err := writeOutput(fn, &expectedRspn); err != nil {
+ t.Fatalf("Failed to write file %v: %v", fn, err)
+ }
+ f, err := os.Open(fn)
+ if err != nil {
+ t.Fatalf("Failed to read response from file %v: %v", fn, err)
+ }
+ rspn := api.CrosTestFinderResponse{}
+ if err := jsonpb.Unmarshal(f, &rspn); err != nil {
+ t.Fatalf("Failed to unmarshall data from file %v: %v", fn, err)
+ }
+ if diff := cmp.Diff(rspn, expectedRspn, cmp.AllowUnexported(api.CrosTestFinderResponse{})); diff != "" {
+ t.Errorf("Got unexpected data from writeOutput (-got +want):\n%s", diff)
+ }
+}
+
+func TestCombineSuiteNames(t *testing.T) {
+ suites := []*api.TestSuite{
+ {
+ Name: "suite1",
+ Spec: &api.TestSuite_TestCaseIds{
+ TestCaseIds: &api.TestCaseIdList{
+ TestCaseIds: []*api.TestCase_Id{
+ {
+ Value: "example.Pass",
+ },
+ {
+ Value: "example.Fail",
+ },
+ },
+ },
+ },
+ },
+ {
+ Name: "suite2",
+ Spec: &api.TestSuite_TestCaseTagCriteria_{
+ TestCaseTagCriteria: &api.TestSuite_TestCaseTagCriteria{
+ Tags: []string{"group:meta"},
+ },
+ },
+ },
+ }
+ name := combineTestSuiteNames(suites)
+ if name != "suite1,suite2" {
+ t.Errorf(`Got %s from combineTestSuiteNames; wanted "suite1,suite2"`, name)
+ }
+}
+
+func TestMetadataToTestSuite(t *testing.T) {
+ mdList := []*api.TestCaseMetadata{
+ {
+ TestCase: &api.TestCase{
+ Id: &api.TestCase_Id{
+ Value: "tast/test001",
+ },
+ Name: "tastTest",
+ Tags: []*api.TestCase_Tag{
+ {Value: "attr1"},
+ {Value: "attr2"},
+ },
+ },
+ TestCaseExec: &api.TestCaseExec{
+ TestHarness: &api.TestHarness{
+ TestHarnessType: &api.TestHarness_Tast_{
+ Tast: &api.TestHarness_Tast{},
+ },
+ },
+ },
+ TestCaseInfo: &api.TestCaseInfo{
+ Owners: []*api.Contact{
+ {Email: "someone1@chromium.org"},
+ },
+ },
+ },
+ {
+ TestCase: &api.TestCase{
+ Id: &api.TestCase_Id{
+ Value: "tauto/test002",
+ },
+ Name: "tautoTest",
+ Tags: []*api.TestCase_Tag{
+ {Value: "attr1"},
+ {Value: "attr2"},
+ },
+ },
+ TestCaseExec: &api.TestCaseExec{
+ TestHarness: &api.TestHarness{
+ TestHarnessType: &api.TestHarness_Tauto_{
+ Tauto: &api.TestHarness_Tauto{},
+ },
+ },
+ },
+ TestCaseInfo: &api.TestCaseInfo{
+ Owners: []*api.Contact{
+ {Email: "someone2@chromium.org"},
+ },
+ },
+ },
+ {
+ TestCase: &api.TestCase{
+ Id: &api.TestCase_Id{
+ Value: "tauto/test003",
+ },
+ Name: "tautoTest",
+ Tags: []*api.TestCase_Tag{
+ {Value: "attr3"},
+ },
+ },
+ TestCaseExec: &api.TestCaseExec{
+ TestHarness: &api.TestHarness{
+ TestHarnessType: &api.TestHarness_Tauto_{
+ Tauto: &api.TestHarness_Tauto{},
+ },
+ },
+ },
+ TestCaseInfo: &api.TestCaseInfo{
+ Owners: []*api.Contact{
+ {Email: "someone3@chromium.org"},
+ },
+ },
+ },
+ }
+ expected := api.TestSuite{
+ Name: "test_suite",
+ Spec: &api.TestSuite_TestCaseIds{
+ TestCaseIds: &api.TestCaseIdList{
+ TestCaseIds: []*api.TestCase_Id{
+ {
+ Value: "tast/test001",
+ },
+ {
+ Value: "tauto/test002",
+ },
+ {
+ Value: "tauto/test003",
+ },
+ },
+ },
+ },
+ }
+ suites := metadataToTestSuite("test_suite", mdList)
+ if diff := cmp.Diff(suites, &expected, cmp.AllowUnexported(api.TestSuite{})); diff != "" {
+ t.Errorf("Got unexpected test suite from metadataToTestSuite (-got +want):\n%s", diff)
+ }
+
+}
diff --git a/src/chromiumos/test/test_finder/data/request.json b/src/chromiumos/test/test_finder/data/request.json
new file mode 100644
index 0000000..95d99dd
--- /dev/null
+++ b/src/chromiumos/test/test_finder/data/request.json
@@ -0,0 +1,17 @@
+{
+ "testSuites":[{
+ "name":"suite1",
+ "testCaseIds":{
+ "testCaseIds":[
+ {"value":"tast.example.Pass"},
+ {"value":"tast.example.Fail"}
+ ]
+ }
+ },
+ {
+ "name":"suite2",
+ "testCaseTagCriteria":{
+ "tags":["group:meta"]
+ }
+ }]
+}