| // Copyright 2020 The Fuchsia 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 ( |
| "encoding/json" |
| "flag" |
| "fmt" |
| "io/ioutil" |
| "log" |
| "os" |
| |
| "go.fuchsia.dev/fuchsia/tools/testing/runtests" |
| "go.fuchsia.dev/fuchsia/tools/testing/tefmocheck" |
| ) |
| |
| func usage() { |
| fmt.Printf(`tefmocheck [flags] |
| |
| Reads inputs from [flags] and writes a JSON formatted summary to stdout. |
| The summary contains a synthetic test for each supported failure mode. |
| `) |
| } |
| |
| func loadSwarmingTaskSummary(path string) (*tefmocheck.SwarmingTaskSummary, error) { |
| data, err := ioutil.ReadFile(path) |
| if err != nil { |
| return nil, fmt.Errorf("failed to read swarming task summary file %q", path) |
| } |
| |
| var ret tefmocheck.SwarmingTaskSummary |
| if err := json.Unmarshal(data, &ret); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal swarming task summary: %w", err) |
| } |
| return &ret, nil |
| } |
| |
| func loadTestSummary(path string) (*runtests.TestSummary, error) { |
| if path == "" { |
| return &runtests.TestSummary{}, nil |
| } |
| data, err := ioutil.ReadFile(path) |
| if err != nil { |
| return nil, fmt.Errorf("failed to read test summary file %q", path) |
| } |
| |
| var ret runtests.TestSummary |
| if err := json.Unmarshal(data, &ret); err != nil { |
| return nil, fmt.Errorf("failed to unmarshal test summary: %w", err) |
| } |
| return &ret, nil |
| } |
| |
| func main() { |
| var help = flag.Bool("help", false, "Whether to show Usage and exit.") |
| flag.Usage = usage |
| var swarmingSummaryPath = flag.String("swarming-summary-json", "", "Path to the Swarming task summary file. Required.") |
| var swarmingHost = flag.String("swarming-host", "", "Swarming server host. Optional.") |
| var inputSummaryPath = flag.String("test-summary-json", "", "Path to test summary file. Optional.") |
| var swarmingOutputPath = flag.String("swarming-output", "", "Path to a file containing the stdout and stderr of the Swarming task. Optional.") |
| var syslogPath = flag.String("syslog", "", "Path to a file containing the syslog. Optional.") |
| var serialLogPath = flag.String("serial-log", "", "Path to a file containing the serial log. Optional.") |
| var outputsDir = flag.String("outputs-dir", "", "If set, will produce text output files for the produced tests in this dir. Optional.") |
| flag.Parse() |
| |
| if *help || flag.NArg() > 0 || *swarmingSummaryPath == "" { |
| flag.Usage() |
| flag.PrintDefaults() |
| if *help { |
| return |
| } |
| os.Exit(64) |
| } |
| |
| swarmingSummary, err := loadSwarmingTaskSummary(*swarmingSummaryPath) |
| if err != nil { |
| log.Fatal(err) |
| } |
| swarmingSummary.Host = *swarmingHost |
| |
| inputSummary, err := loadTestSummary(*inputSummaryPath) |
| if err != nil { |
| log.Fatal(err) |
| } |
| |
| var serialLog []byte |
| if *serialLogPath != "" { |
| serialLog, err = ioutil.ReadFile(*serialLogPath) |
| if err != nil { |
| log.Fatalf("failed to read serial log from %s: %e", *serialLogPath, err) |
| } |
| } |
| |
| var swarmingOutput []byte |
| if *swarmingOutputPath != "" { |
| swarmingOutput, err = ioutil.ReadFile(*swarmingOutputPath) |
| if err != nil { |
| log.Fatalf("failed to read swarming output from %s: %e", *swarmingOutputPath, err) |
| } |
| } |
| |
| var syslog []byte |
| if *syslogPath != "" { |
| syslog, err = ioutil.ReadFile(*syslogPath) |
| if err != nil { |
| log.Fatalf("failed to read syslog from %s: %e", *syslogPath, err) |
| } |
| } |
| |
| testingOutputs := tefmocheck.TestingOutputs{ |
| TestSummary: inputSummary, |
| SwarmingSummary: swarmingSummary, |
| SerialLog: serialLog, |
| SwarmingOutput: swarmingOutput, |
| Syslog: syslog, |
| } |
| |
| // These should be ordered from most specific to least specific. If an earlier |
| // check finds a failure mode, then we skip running later checks because we assume |
| // they'll add no useful information. |
| checks := []tefmocheck.FailureModeCheck{} |
| checks = append(checks, tefmocheck.StringInLogsChecks()...) |
| checks = append(checks, tefmocheck.MassTestFailureCheck{MaxFailed: 5}) |
| // TaskStateChecks should go toward the end, since they're not very specific. |
| checks = append(checks, tefmocheck.TaskStateChecks...) |
| // No tests being run is only an issue if the task didn't fail for another |
| // reason, since conditions handled by many other checks can result in a |
| // missing summary.json. So run this check last. |
| checks = append(checks, tefmocheck.NoTestsRanCheck{}) |
| |
| checkTests, err := tefmocheck.RunChecks(checks, &testingOutputs, *outputsDir) |
| if err != nil { |
| log.Fatalf("failed to run checks: %v", err) |
| } |
| |
| inputSummary.Tests = append(inputSummary.Tests, checkTests...) |
| jsonOutput, err := json.MarshalIndent(inputSummary, "", " ") |
| if err != nil { |
| log.Fatalf("failed to marshal output test summary: %v", err) |
| } |
| os.Stdout.Write(jsonOutput) |
| fmt.Println("") // Terminate output with new line. |
| } |