blob: ec9186ad3a7d1cebf018376514dfa7bac9f516ea [file] [log] [blame]
// Copyright 2020 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 tast_rtd executable, used to invoke tast in RTD.
package main
import (
rtd ""
// Version is the version info of this command. It is filled in during emerge.
var Version = "<unknown>"
// createLogFile creates a file and its parent directory for logging purpose.
func createLogFile() (*os.File, error) {
t := time.Now()
fullPath := filepath.Join("/tmp/tast_rtd/results", t.Format("20060102-150405"))
if err := os.MkdirAll(fullPath, 0755); err != nil {
return nil, fmt.Errorf("failed to create log directory %v: %v", fullPath, err)
logFullPathName := filepath.Join(fullPath, "log.txt")
// Log the full output of the command to disk.
logFile, err := os.Create(logFullPathName)
if err != nil {
return nil, fmt.Errorf("failed to create log file %v: %v", fullPath, err)
return logFile, nil
// newLogger creates a logger.
func newLogger(logFile *os.File) *log.Logger {
mw := io.MultiWriter(logFile, os.Stderr)
return log.New(mw, "", log.LstdFlags)
// readInput reads an invocation protobuf file and returns a pointer to rtd.Invocation.
// TODO: Populate rtd.Invocation.
func readInput(fileName string) (*rtd.Invocation, error) {
data, err := ioutil.ReadFile(fileName)
if err != nil {
return nil, fmt.Errorf("fail to read file %v: %v", fileName, err)
inv, err := unmarshalInvocation(data)
if err != nil {
return nil, fmt.Errorf("fail to unmarshal file %v: %v", fileName, err)
return inv, nil
func main() {
os.Exit(func() int {
ctx := context.Background()
version := flag.Bool("version", false, "print version and exit")
input := flag.String("input", "", "specify the test invocation request protobuf input file")
rtdPath := flag.String("rtd", "/usr/src/rtd", "specify the root directory of rtd files and executables.")
serverPort := flag.Int("reports_port", 0, "specify the port number to start Reports server.")
if *version {
fmt.Printf("tast_rtd version %s\n", Version)
return 0
srv, err := rpc.NewReportsServer(*serverPort)
if err != nil {
log.Fatalf("Failed to start Reports server: %v", err)
defer srv.Stop()
logFile, err := createLogFile()
if err != nil {
log.Fatalf("Failed to create log file: %v", err)
defer logFile.Close()
logger := newLogger(logFile)
logger.Println("Input File:", *input)
inv, err := readInput(*input)
if err != nil {
logger.Printf("Failed to read invocation protobuf file %v: %v", *input, err)
return 1
resultDir, err := invokeTast(logger, inv, *rtdPath)
if err != nil {
logger.Printf("Failed to invoke tast: %v", err)
return 1
// Set up connection with ProgressSink
psAddr := fmt.Sprintf(":%d", inv.GetProgressSinkClientConfig().GetPort())
conn, err := grpc.DialContext(ctx, psAddr, grpc.WithBlock(), grpc.WithInsecure())
if err != nil {
logger.Printf("Failed to connect to progress sink %v: %v", psAddr, err)
return 1
defer conn.Close()
psClient := rtd.NewProgressSinkClient(conn)
if err := sendTestResults(ctx, logger, psClient, inv, resultDir); err != nil {
logger.Printf("Failed in reading tast test results: %v", err)
return 1
return 0