github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/spyglass/lenses/junit/lens.go (about)

     1  /*
     2  Copyright 2018 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  // Package junit provides a junit viewer for Spyglass
    18  package junit
    19  
    20  import (
    21  	"bytes"
    22  
    23  	junit "github.com/joshdk/go-junit"
    24  	"github.com/sirupsen/logrus"
    25  
    26  	"fmt"
    27  	"html/template"
    28  	"path/filepath"
    29  
    30  	"k8s.io/test-infra/prow/spyglass/lenses"
    31  )
    32  
    33  const (
    34  	name     = "junit"
    35  	title    = "JUnit"
    36  	priority = 5
    37  )
    38  
    39  func init() {
    40  	lenses.RegisterLens(Lens{})
    41  }
    42  
    43  // Lens is the implementation of a JUnit-rendering Spyglass lens.
    44  type Lens struct{}
    45  
    46  // Name returns the name.
    47  func (lens Lens) Name() string {
    48  	return name
    49  }
    50  
    51  // Title returns the title.
    52  func (lens Lens) Title() string {
    53  	return title
    54  }
    55  
    56  // Priority returns the priority.
    57  func (lens Lens) Priority() int {
    58  	return priority
    59  }
    60  
    61  // Header renders the content of <head> from template.html.
    62  func (lens Lens) Header(artifacts []lenses.Artifact, resourceDir string) string {
    63  	t, err := template.ParseFiles(filepath.Join(resourceDir, "template.html"))
    64  	if err != nil {
    65  		return fmt.Sprintf("<!-- FAILED LOADING HEADER: %v -->", err)
    66  	}
    67  	var buf bytes.Buffer
    68  	if err := t.ExecuteTemplate(&buf, "header", nil); err != nil {
    69  		return fmt.Sprintf("<!-- FAILED EXECUTING HEADER TEMPLATE: %v -->", err)
    70  	}
    71  	return buf.String()
    72  }
    73  
    74  // Callback does nothing.
    75  func (lens Lens) Callback(artifacts []lenses.Artifact, resourceDir string, data string) string {
    76  	return ""
    77  }
    78  
    79  // TestResult holds data about a test extracted from junit output
    80  type TestResult struct {
    81  	Junit junit.Test
    82  	Link  string
    83  }
    84  
    85  // Body renders the <body> for JUnit tests
    86  func (lens Lens) Body(artifacts []lenses.Artifact, resourceDir string, data string) string {
    87  	type JunitViewData struct {
    88  		NumTests int
    89  		Passed   []TestResult
    90  		Failed   []TestResult
    91  		Skipped  []TestResult
    92  	}
    93  
    94  	jvd := JunitViewData{
    95  		Passed:   []TestResult{},
    96  		Failed:   []TestResult{},
    97  		Skipped:  []TestResult{},
    98  		NumTests: 0,
    99  	}
   100  
   101  	var err error
   102  	for _, a := range artifacts {
   103  		contents, err := a.ReadAll()
   104  		if err != nil {
   105  			logrus.WithError(err).Error("Error reading artifact")
   106  			continue
   107  		}
   108  		suites, err := junit.Ingest(contents)
   109  		if err != nil {
   110  			logrus.WithError(err).Error("Error parsing junit file.")
   111  			continue
   112  		}
   113  		for _, suite := range suites {
   114  			for _, test := range suite.Tests {
   115  				if test.Status == "failed" {
   116  					jvd.Failed = append(jvd.Failed, TestResult{
   117  						Junit: test,
   118  						Link:  a.CanonicalLink(),
   119  					})
   120  				} else if test.Status == "skipped" {
   121  					jvd.Skipped = append(jvd.Skipped, TestResult{
   122  						Junit: test,
   123  						Link:  a.CanonicalLink(),
   124  					})
   125  				} else if test.Status == "passed" {
   126  					jvd.Passed = append(jvd.Passed, TestResult{
   127  						Junit: test,
   128  						Link:  a.CanonicalLink(),
   129  					})
   130  				} else {
   131  					err = fmt.Errorf("Invalid test status string: %s", test.Status)
   132  					logrus.Error(err)
   133  				}
   134  			}
   135  		}
   136  		jvd.NumTests = len(jvd.Passed) + len(jvd.Failed) + len(jvd.Skipped)
   137  
   138  	}
   139  
   140  	if jvd.NumTests == 0 {
   141  		if err != nil {
   142  			return fmt.Sprintf("Failed to parse JUnit test results: %v", err)
   143  		}
   144  		return "Found no JUnit tests"
   145  	}
   146  
   147  	junitTemplate, err := template.ParseFiles(filepath.Join(resourceDir, "template.html"))
   148  	if err != nil {
   149  		logrus.WithError(err).Error("Error executing template.")
   150  		return fmt.Sprintf("Failed to load template file: %v", err)
   151  	}
   152  
   153  	var buf bytes.Buffer
   154  	if err := junitTemplate.ExecuteTemplate(&buf, "body", jvd); err != nil {
   155  		logrus.WithError(err).Error("Error executing template.")
   156  	}
   157  
   158  	return buf.String()
   159  }