summaryrefslogtreecommitdiffhomepage
path: root/test/runner/gtest/gtest.go
blob: 23bf7b5f657fb88990ac5b1a9cb55ae4eb3a3c40 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package gtest contains helpers for running google-test tests from Go.
package gtest

import (
	"fmt"
	"os/exec"
	"strings"
)

var (
	// listTestFlag is the flag that will list tests in gtest binaries.
	listTestFlag = "--gtest_list_tests"

	// filterTestFlag is the flag that will filter tests in gtest binaries.
	filterTestFlag = "--gtest_filter"

	// listBechmarkFlag is the flag that will list benchmarks in gtest binaries.
	listBenchmarkFlag = "--benchmark_list_tests"

	// filterBenchmarkFlag is the flag that will run specified benchmarks.
	filterBenchmarkFlag = "--benchmark_filter"
)

// TestCase is a single gtest test case.
type TestCase struct {
	// Suite is the suite for this test.
	Suite string

	// Name is the name of this individual test.
	Name string

	// benchmark indicates that this is a benchmark. In this case, the
	// suite will be empty, and we will use the appropriate test and
	// benchmark flags.
	benchmark bool
}

// FullName returns the name of the test including the suite. It is suitable to
// pass to "-gtest_filter".
func (tc TestCase) FullName() string {
	return fmt.Sprintf("%s.%s", tc.Suite, tc.Name)
}

// Args returns arguments to be passed when invoking the test.
func (tc TestCase) Args() []string {
	if tc.benchmark {
		return []string{
			fmt.Sprintf("%s=^$", filterTestFlag),
			fmt.Sprintf("%s=^%s$", filterBenchmarkFlag, tc.Name),
		}
	}
	return []string{
		fmt.Sprintf("%s=^%s$", filterTestFlag, tc.FullName()),
		fmt.Sprintf("%s=^$", filterBenchmarkFlag),
	}
}

// ParseTestCases calls a gtest test binary to list its test and returns a
// slice with the name and suite of each test.
//
// If benchmarks is true, then benchmarks will be included in the list of test
// cases provided. Note that this requires the binary to support the
// benchmarks_list_tests flag.
func ParseTestCases(testBin string, benchmarks bool, extraArgs ...string) ([]TestCase, error) {
	// Run to extract test cases.
	args := append([]string{listTestFlag}, extraArgs...)
	cmd := exec.Command(testBin, args...)
	out, err := cmd.Output()
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if !ok {
			return nil, fmt.Errorf("could not enumerate gtest tests: %v", err)
		}
		return nil, fmt.Errorf("could not enumerate gtest tests: %v\nstderr:\n%s", err, exitErr.Stderr)
	}

	// Parse test output.
	var t []TestCase
	var suite string
	for _, line := range strings.Split(string(out), "\n") {
		// Strip comments.
		line = strings.Split(line, "#")[0]

		// New suite?
		if !strings.HasPrefix(line, " ") {
			suite = strings.TrimSuffix(strings.TrimSpace(line), ".")
			continue
		}

		// Individual test.
		name := strings.TrimSpace(line)

		// Do we have a suite yet?
		if suite == "" {
			return nil, fmt.Errorf("test without a suite: %v", name)
		}

		// Add this individual test.
		t = append(t, TestCase{
			Suite: suite,
			Name:  name,
		})

	}

	// Finished?
	if !benchmarks {
		return t, nil
	}

	// Run again to extract benchmarks.
	args = append([]string{listBenchmarkFlag}, extraArgs...)
	cmd = exec.Command(testBin, args...)
	out, err = cmd.Output()
	if err != nil {
		exitErr, ok := err.(*exec.ExitError)
		if !ok {
			return nil, fmt.Errorf("could not enumerate gtest benchmarks: %v", err)
		}
		return nil, fmt.Errorf("could not enumerate gtest benchmarks: %v\nstderr\n%s", err, exitErr.Stderr)
	}

	// Parse benchmark output.
	for _, line := range strings.Split(string(out), "\n") {
		// Strip comments.
		line = strings.Split(line, "#")[0]

		// Single benchmark.
		name := strings.TrimSpace(line)

		// Add the single benchmark.
		t = append(t, TestCase{
			Suite:     "Benchmarks",
			Name:      name,
			benchmark: true,
		})
	}

	return t, nil
}