github.com/SagerNet/gvisor@v0.0.0-20210707092255-7731c139d75c/test/runner/gtest/gtest.go (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  // Package gtest contains helpers for running google-test tests from Go.
    16  package gtest
    17  
    18  import (
    19  	"fmt"
    20  	"os/exec"
    21  	"strings"
    22  )
    23  
    24  var (
    25  	// listTestFlag is the flag that will list tests in gtest binaries.
    26  	listTestFlag = "--gtest_list_tests"
    27  
    28  	// filterTestFlag is the flag that will filter tests in gtest binaries.
    29  	filterTestFlag = "--gtest_filter"
    30  
    31  	// listBechmarkFlag is the flag that will list benchmarks in gtest binaries.
    32  	listBenchmarkFlag = "--benchmark_list_tests"
    33  
    34  	// filterBenchmarkFlag is the flag that will run specified benchmarks.
    35  	filterBenchmarkFlag = "--benchmark_filter"
    36  )
    37  
    38  // TestCase is a single gtest test case.
    39  type TestCase struct {
    40  	// Suite is the suite for this test.
    41  	Suite string
    42  
    43  	// Name is the name of this individual test.
    44  	Name string
    45  
    46  	// all indicates that this will run without flags. This takes
    47  	// precendence over benchmark below.
    48  	all bool
    49  
    50  	// benchmark indicates that this is a benchmark. In this case, the
    51  	// suite will be empty, and we will use the appropriate test and
    52  	// benchmark flags.
    53  	benchmark bool
    54  }
    55  
    56  // FullName returns the name of the test including the suite. It is suitable to
    57  // pass to "-gtest_filter".
    58  func (tc TestCase) FullName() string {
    59  	return fmt.Sprintf("%s.%s", tc.Suite, tc.Name)
    60  }
    61  
    62  // Args returns arguments to be passed when invoking the test.
    63  func (tc TestCase) Args() []string {
    64  	if tc.all {
    65  		return []string{} // No arguments.
    66  	}
    67  	if tc.benchmark {
    68  		return []string{
    69  			fmt.Sprintf("%s=^%s$", filterBenchmarkFlag, tc.Name),
    70  			fmt.Sprintf("%s=", filterTestFlag),
    71  		}
    72  	}
    73  	return []string{
    74  		fmt.Sprintf("%s=%s", filterTestFlag, tc.FullName()),
    75  	}
    76  }
    77  
    78  // ParseTestCases calls a gtest test binary to list its test and returns a
    79  // slice with the name and suite of each test.
    80  //
    81  // If benchmarks is true, then benchmarks will be included in the list of test
    82  // cases provided. Note that this requires the binary to support the
    83  // benchmarks_list_tests flag.
    84  func ParseTestCases(testBin string, benchmarks bool, extraArgs ...string) ([]TestCase, error) {
    85  	// Run to extract test cases.
    86  	args := append([]string{listTestFlag}, extraArgs...)
    87  	cmd := exec.Command(testBin, args...)
    88  	out, err := cmd.Output()
    89  	if err != nil {
    90  		// We failed to list tests with the given flags. Just
    91  		// return something that will run the binary with no
    92  		// flags, which should execute all tests.
    93  		return []TestCase{
    94  			{
    95  				Suite: "Default",
    96  				Name:  "All",
    97  				all:   true,
    98  			},
    99  		}, nil
   100  	}
   101  
   102  	// Parse test output.
   103  	var t []TestCase
   104  	var suite string
   105  	for _, line := range strings.Split(string(out), "\n") {
   106  		// Strip comments.
   107  		line = strings.Split(line, "#")[0]
   108  
   109  		// New suite?
   110  		if !strings.HasPrefix(line, " ") {
   111  			suite = strings.TrimSuffix(strings.TrimSpace(line), ".")
   112  			continue
   113  		}
   114  
   115  		// Individual test.
   116  		name := strings.TrimSpace(line)
   117  
   118  		// Do we have a suite yet?
   119  		if suite == "" {
   120  			return nil, fmt.Errorf("test without a suite: %v", name)
   121  		}
   122  
   123  		// Add this individual test.
   124  		t = append(t, TestCase{
   125  			Suite: suite,
   126  			Name:  name,
   127  		})
   128  	}
   129  
   130  	// Finished?
   131  	if !benchmarks {
   132  		return t, nil
   133  	}
   134  
   135  	// Run again to extract benchmarks.
   136  	args = append([]string{listBenchmarkFlag}, extraArgs...)
   137  	cmd = exec.Command(testBin, args...)
   138  	out, err = cmd.Output()
   139  	if err != nil {
   140  		// We were able to enumerate tests above, but not benchmarks?
   141  		// We requested them, so we return an error in this case.
   142  		exitErr, ok := err.(*exec.ExitError)
   143  		if !ok {
   144  			return nil, fmt.Errorf("could not enumerate gtest benchmarks: %v", err)
   145  		}
   146  		return nil, fmt.Errorf("could not enumerate gtest benchmarks: %v\nstderr\n%s", err, exitErr.Stderr)
   147  	}
   148  
   149  	benches := strings.Trim(string(out), "\n")
   150  	if len(benches) == 0 {
   151  		return t, nil
   152  	}
   153  
   154  	// Parse benchmark output.
   155  	for _, line := range strings.Split(benches, "\n") {
   156  		// Strip comments.
   157  		line = strings.Split(line, "#")[0]
   158  
   159  		// Single benchmark.
   160  		name := strings.TrimSpace(line)
   161  
   162  		// Add the single benchmark.
   163  		t = append(t, TestCase{
   164  			Suite:     "Benchmarks",
   165  			Name:      name,
   166  			benchmark: true,
   167  		})
   168  	}
   169  	return t, nil
   170  }