blob: 2cc25dd7accb86a8ec0aad46bac7f01cd44f6fb4 [file] [log] [blame]
// Copyright 2020 The LUCI Authors. All rights reserved.
// Use of this source code is governed under the Apache License, Version 2.0
// that can be found in the LICENSE file.
package main
import (
sinkpb ""
func cmdJSON() *subcommands.Command {
return &subcommands.Command{
UsageLine: `json [flags] TEST_CMD [TEST_ARG]...`,
ShortDesc: "Batch upload results of json test result format to ResultSink",
LongDesc: text.Doc(`
Runs the test command and waits for it to finish, then converts the json
test results to ResultSink native format and uploads them to ResultDB via ResultSink.
CommandRun: func() subcommands.CommandRun {
r := &jsonRun{}
r.Flags.BoolVar(&r.testLocations, "test-location", false, text.Doc(`
Flag for set testLocation in each TestResult.
If true, testIds will be used as testLocations.
It only makes sense to set the flag for blink_web_tests and webgl_conformance_tests.
return r
type jsonRun struct {
testLocations bool
func (r *jsonRun) Run(a subcommands.Application, args []string, env subcommands.Env) (ret int) {
if err := r.validate(); err != nil {
return r.done(err)
ctx := cli.GetContext(a, r, env)
return, args, r.generateTestResults)
func (r *jsonRun) validate() (err error) {
if r.artifactDir == "" {
return errors.Reason("-artifact-directory is required").Err()
return r.baseRun.validate()
// generateTestResults converts test results from results file to sinkpb.TestResult.
func (r *jsonRun) generateTestResults(ctx context.Context, _ []byte) ([]*sinkpb.TestResult, error) {
// Get artifacts.
normPathToFullPath, err := r.processArtifacts()
if err != nil {
return nil, errors.Annotate(err, "open artifact directory").Err()
// Get results.
f, err := os.Open(r.resultFile)
if err != nil {
return nil, errors.Annotate(err, "open result file").Err()
defer f.Close()
jsonFormat := &JSONTestResults{}
if err = jsonFormat.ConvertFromJSON(f); err != nil {
return nil, errors.Annotate(err, "did not recognize as json test result format").Err()
// Convert the results to ResultSink native format.
trs, err := jsonFormat.ToProtos(ctx, normPathToFullPath, r.testLocations)
if err != nil {
return nil, errors.Annotate(err, "converting as json results format").Err()
return trs, nil
// processArtifacts walks the files in r.artifactDir then returns a map from normalized relative path to full path.
func (r *jsonRun) processArtifacts() (normPathToFullPath map[string]string, err error) {
normPathToFullPath = map[string]string{}
err = filepath.Walk(r.artifactDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
if info.Mode().IsRegular() {
// normPath is the normalized relative path to r.artifactDir.
relPath, err := filepath.Rel(r.artifactDir, path)
if err != nil {
return err
normPath := normalizePath(relPath)
normPathToFullPath[normPath] = path
return nil
if err != nil {
return nil, err
return normPathToFullPath, err