1package main
2
3import (
4	"bufio"
5	"bytes"
6	"fmt"
7	"io"
8	"os"
9	"os/exec"
10	"runtime"
11	"sync"
12
13	"strings"
14
15	"github.com/golang/protobuf/proto"
16	harness "github.com/envoyproxy/protoc-gen-validate/tests/harness/go"
17	"golang.org/x/net/context"
18)
19
20func Harnesses(goFlag bool, gogoFlag bool, ccFlag bool, javaFlag bool) []Harness {
21	harnesses := make([]Harness, 0)
22	if goFlag {
23		harnesses = append(harnesses, InitHarness("tests/harness/go/main/go-harness"))
24	}
25	if gogoFlag {
26		harnesses = append(harnesses, InitHarness("tests/harness/gogo/main/go-harness"))
27	}
28	if ccFlag {
29		harnesses = append(harnesses, InitHarness("tests/harness/cc/cc-harness"))
30	}
31	if javaFlag {
32		harnesses = append(harnesses, InitHarness("tests/harness/java/java-harness"))
33	}
34	return harnesses
35}
36
37type Harness struct {
38	Name string
39	Exec func(context.Context, io.Reader) (*harness.TestResult, error)
40}
41
42func InitHarness(cmd string, args ...string) Harness {
43	if runtime.GOOS == "windows" {
44		// Bazel runfiles are not symlinked in on windows,
45		// so we have to use the manifest instead. If the manifest
46		// doesn't exist, assume we're running in a non-Bazel context
47		f, err := os.Open("MANIFEST")
48		if err == nil {
49			defer f.Close()
50
51			s := bufio.NewScanner(f)
52			manifest := map[string]string{}
53			for s.Scan() {
54				values := strings.Split(s.Text(), " ")
55				manifest[values[0]] = values[1]
56			}
57			for k, v := range manifest {
58				if strings.Contains(k, cmd) {
59					cmd = v
60				}
61			}
62		}
63	}
64
65	return Harness{
66		Name: cmd,
67		Exec: initHarness(cmd, args...),
68	}
69}
70
71func initHarness(cmd string, args ...string) func(context.Context, io.Reader) (*harness.TestResult, error) {
72	return func(ctx context.Context, r io.Reader) (*harness.TestResult, error) {
73		out, errs := getBuf(), getBuf()
74		defer relBuf(out)
75		defer relBuf(errs)
76
77		cmd := exec.CommandContext(ctx, cmd, args...)
78		cmd.Stdin = r
79		cmd.Stdout = out
80		cmd.Stderr = errs
81
82		if err := cmd.Run(); err != nil {
83			return nil, fmt.Errorf("[%s] failed execution (%v) - captured stderr:\n%s", cmdStr(cmd), err, errs.String())
84		}
85
86		res := new(harness.TestResult)
87		if err := proto.Unmarshal(out.Bytes(), res); err != nil {
88			return nil, fmt.Errorf("[%s] failed to unmarshal result: %v", cmdStr(cmd), err)
89		}
90
91		return res, nil
92	}
93}
94
95var bufPool = &sync.Pool{New: func() interface{} { return new(bytes.Buffer) }}
96
97func getBuf() *bytes.Buffer {
98	return bufPool.Get().(*bytes.Buffer)
99}
100
101func relBuf(b *bytes.Buffer) {
102	b.Reset()
103	bufPool.Put(b)
104}
105
106func cmdStr(cmd *exec.Cmd) string {
107	return fmt.Sprintf("%s %s", cmd.Path, strings.Join(cmd.Args, " "))
108}
109