k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/pkg/benchmarkjunit/integration_test.go (about)

     1  /*
     2  Copyright 2019 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 main
    18  
    19  import (
    20  	"flag"
    21  	"fmt"
    22  	"os"
    23  	"path"
    24  	"path/filepath"
    25  	"reflect"
    26  	"sort"
    27  	"strconv"
    28  	"strings"
    29  	"testing"
    30  
    31  	"github.com/GoogleCloudPlatform/testgrid/metadata/junit"
    32  )
    33  
    34  func createTempFile() (string, error) {
    35  	outFile, err := os.CreateTemp("", "dummybenchmarks")
    36  	if err != nil {
    37  		return "", fmt.Errorf("create error: %w", err)
    38  	}
    39  	if err := outFile.Close(); err != nil {
    40  		return "", fmt.Errorf("close error: %w", err)
    41  	}
    42  	return outFile.Name(), nil
    43  }
    44  
    45  var goBinaryPath = flag.String("go", "go", "The location of the go binary. This flag is primarily intended for use with bazel.")
    46  
    47  func TestDummybenchmarksIntegration(t *testing.T) {
    48  	// Set HOME and GOROOT envvars for bazel if needed.
    49  	if os.Getenv("TEST_WORKSPACE") != "" { // Running in bazel
    50  		if os.Getenv("HOME") == "" {
    51  			wd, _ := os.Getwd() // Just use `/home` if we can't determine working dir.
    52  			os.Setenv("HOME", wd+"/home")
    53  		}
    54  		if os.Getenv("GOROOT") == "" {
    55  			goRoot, _ := filepath.Abs("../../external/go_sdk")
    56  			os.Setenv("GOROOT", goRoot)
    57  			t.Logf("Setting GOROOT to %q.\n", goRoot)
    58  		}
    59  	}
    60  
    61  	outFile, err := createTempFile()
    62  	if err != nil {
    63  		t.Fatalf("Error creating output file: %v.", err)
    64  	}
    65  	defer os.Remove(outFile)
    66  	logFile, err := createTempFile()
    67  	if err != nil {
    68  		t.Fatalf("Error creating log file: %v.", err)
    69  	}
    70  	defer os.Remove(logFile)
    71  	t.Logf("Logging benchmark output to %q.", logFile)
    72  	opts := &options{
    73  		outputFile:   outFile,
    74  		logFile:      logFile,
    75  		goBinaryPath: *goBinaryPath,
    76  		passOnError:  true,
    77  		// Omit benchmarks that aren't in the core group to keep test time reasonable.
    78  		benchRegexp: "Core",
    79  	}
    80  
    81  	t.Logf("Starting benchmarkjunit outputting to %q...", opts.outputFile)
    82  	run(opts, []string{"../../experiment/dummybenchmarks/..."})
    83  	t.Log("Finished running benchmarkjunit. Validating JUnit XML...")
    84  
    85  	// Print log file contents.
    86  	rawLog, err := os.ReadFile(opts.logFile)
    87  	if err != nil {
    88  		t.Fatalf("Error reading log file: %v.", err)
    89  	}
    90  	t.Logf("Log file output:\n%s\n\n", string(rawLog))
    91  
    92  	// Read and parse JUnit output.
    93  	raw, err := os.ReadFile(opts.outputFile)
    94  	if err != nil {
    95  		t.Fatalf("Error reading output file: %v.", err)
    96  	}
    97  	suites, err := junit.Parse(raw)
    98  	if err != nil {
    99  		t.Fatalf("Error parsing JUnit XML testsuites: %v.", err)
   100  	}
   101  
   102  	if len(suites.Suites) != 2 {
   103  		t.Fatalf("Expected 2 testsuites, but found %d.", len(suites.Suites))
   104  	}
   105  	// Validate the main 'dummybenchmarks' suite
   106  	s := suites.Suites[0]
   107  	if base := path.Base(s.Name); base != "dummybenchmarks" {
   108  		t.Errorf("Expected testsuites[0] to have basename \"dummybenchmarks\", but got %q.", base)
   109  	}
   110  	expectedBenchmarks := []string{
   111  		"BenchmarkCoreSimple",
   112  		"BenchmarkCoreAllocsAndBytes",
   113  		"BenchmarkCoreParallel",
   114  		"BenchmarkCoreLog",
   115  		"BenchmarkCoreSkip",
   116  		"BenchmarkCoreSkipNow",
   117  		"BenchmarkCoreError",
   118  		"BenchmarkCoreFatal",
   119  		"BenchmarkCoreFailNow",
   120  		"BenchmarkCoreNestedShallow/simple",
   121  		"BenchmarkCoreNestedShallow/parallel",
   122  	}
   123  	var allocsAndBytes junit.Result
   124  	var foundBenchmarks []string
   125  	for _, result := range s.Results {
   126  		// Remove the trailing "-\d+"
   127  		name := strings.Split(result.Name, "-")[0]
   128  		foundBenchmarks = append(foundBenchmarks, name)
   129  
   130  		if name == "BenchmarkCoreAllocsAndBytes" {
   131  			allocsAndBytes = result
   132  		}
   133  	}
   134  	sort.Strings(expectedBenchmarks)
   135  	sort.Strings(foundBenchmarks)
   136  	if !reflect.DeepEqual(expectedBenchmarks, foundBenchmarks) {
   137  		t.Errorf("Expected benchmarks %q, but got %q.", expectedBenchmarks, foundBenchmarks)
   138  	}
   139  	// Check that all properties exist on the AllocsAndBytes benchmark and that
   140  	// all parse to float64. (This is the only benchmark that has all properties.)
   141  	foundProps := make(map[string]string)
   142  	for _, prop := range allocsAndBytes.Properties.PropertyList {
   143  		if val, ok := foundProps[prop.Name]; ok {
   144  			t.Errorf("BenchmarkCoreAllocsAndBytes has duplicated property %q. Values: %q, %q.", prop.Name, val, prop.Value)
   145  		}
   146  		foundProps[prop.Name] = prop.Value
   147  	}
   148  	expectedProps := []string{
   149  		"op count",
   150  		"avg op duration (ns/op)",
   151  		"MB/s",
   152  		"alloced B/op",
   153  		"allocs/op",
   154  	}
   155  	for _, expectedProp := range expectedProps {
   156  		value, ok := foundProps[expectedProp]
   157  		if !ok {
   158  			t.Errorf("BenchmarkCoreAllocsAndBytes is missing property %q.", expectedProp)
   159  			continue
   160  		}
   161  		if _, err := strconv.ParseFloat(value, 64); err != nil {
   162  			t.Errorf("Failed to parse the %q=%q property of BenchmarkCoreAllocsAndBytes: %v.", expectedProp, value, err)
   163  		}
   164  	}
   165  
   166  	// Validate the 'subpkg' suite.
   167  	s = suites.Suites[1]
   168  	if base := path.Base(s.Name); base != "subpkg" {
   169  		t.Errorf("Expected testsuites[1] to have basename \"subpkg\", but got %q.", base)
   170  	}
   171  	if len(s.Results) != 1 || !strings.HasPrefix(s.Results[0].Name, "BenchmarkCoreSubPkg") {
   172  		t.Errorf("Expected one \"BenchmarkCoreSubPkg\" result, but found %+v.", s.Results)
   173  	}
   174  }