blob: d7aa7503a29162fcb9f6fd67b482a0243581c8dc [file] [log] [blame]
// 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 implements the executionservice server
package main
import (
"context"
"errors"
"fmt"
"log"
"os"
"path/filepath"
"github.com/golang/protobuf/jsonpb"
"go.chromium.org/chromiumos/config/go/test/api"
"chromiumos/test/execution/cmd/cros-test/internal/driver"
statuserrors "chromiumos/test/execution/errors"
"chromiumos/test/util/finder"
)
// driverToTestsMapping builds a map between test and its driver.
func driverToTestsMapping(logger *log.Logger, mdList []*api.TestCaseMetadata) (map[driver.Driver][]*api.TestCaseMetadata, error) {
tastDriver := driver.NewTastDriver(logger)
tautoDriver := driver.NewTautoDriver(logger)
driverToTests := make(map[driver.Driver][]*api.TestCaseMetadata)
for _, md := range mdList {
if md.TestCase == nil {
return nil, statuserrors.NewStatusError(statuserrors.InvalidArgument,
fmt.Errorf("missing test case information %v", md))
}
if md.TestCaseExec == nil || md.TestCaseExec.TestHarness == nil {
return nil, statuserrors.NewStatusError(statuserrors.InvalidArgument,
fmt.Errorf("test case %v does not have test harness information", md.TestCase.Name))
}
if md.TestCaseExec.TestHarness.GetTast() != nil {
driverToTests[tastDriver] = append(driverToTests[tastDriver], md)
} else if md.TestCaseExec.TestHarness.GetTauto() != nil {
driverToTests[tautoDriver] = append(driverToTests[tautoDriver], md)
} else {
return nil, statuserrors.NewStatusError(statuserrors.InvalidArgument,
errors.New("manual harness has not been supported"))
}
}
return driverToTests, nil
}
// runTests runs the requested tests.
func runTests(ctx context.Context, logger *log.Logger, resultRootDir, tlwAddr string, metadataList *api.TestCaseMetadataList, req *api.CrosTestRequest) (*api.CrosTestResponse, error) {
matchedMdList, err := finder.MatchedTestsForSuites(metadataList.Values, req.TestSuites)
if err != nil {
return nil, statuserrors.NewStatusError(statuserrors.InvalidArgument,
fmt.Errorf("failed to match test metadata: %v", err))
}
driversToTests, err := driverToTestsMapping(logger, matchedMdList)
if err != nil {
return nil, err
}
allRspn := api.CrosTestResponse{}
for driver, tests := range driversToTests {
resultsDir := filepath.Join(resultRootDir, driver.Name())
// Make sure the result directory exists.
if err := os.MkdirAll(resultsDir, 0755); err != nil {
return nil, statuserrors.NewStatusError(statuserrors.IOCreateError,
fmt.Errorf("failed to create result directory %v", resultsDir))
}
rspn, err := driver.RunTests(ctx, resultsDir, req, tlwAddr, tests)
if err != nil {
return nil, err
}
allRspn.TestCaseResults = append(allRspn.TestCaseResults, rspn.TestCaseResults...)
}
return &allRspn, nil
}
// readInput reads an execution_service json file and returns a pointer to RunTestsRequest.
func readInput(fileName string) (*api.CrosTestRequest, error) {
f, err := os.Open(fileName)
if err != nil {
return nil, statuserrors.NewStatusError(statuserrors.IOAccessError,
fmt.Errorf("fail to read file %v: %v", fileName, err))
}
req := api.CrosTestRequest{}
if err := jsonpb.Unmarshal(f, &req); err != nil {
return nil, statuserrors.NewStatusError(statuserrors.UnmarshalError,
fmt.Errorf("fail to unmarshal file %v: %v", fileName, err))
}
return &req, nil
}
// writeOutput writes a RunTestsResponse json.
func writeOutput(output string, resp *api.CrosTestResponse) error {
f, err := os.Create(output)
if err != nil {
return statuserrors.NewStatusError(statuserrors.IOCreateError,
fmt.Errorf("fail to create file %v: %v", output, err))
}
m := jsonpb.Marshaler{}
if err := m.Marshal(f, resp); err != nil {
return statuserrors.NewStatusError(statuserrors.MarshalError,
fmt.Errorf("failed to marshall response to file %v: %v", output, err))
}
return nil
}