...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package bzltestutil
16
17 import (
18 "bufio"
19 "bytes"
20 "fmt"
21 "io"
22 "io/ioutil"
23 "log"
24 "os"
25 "os/exec"
26 "os/signal"
27 "path/filepath"
28 "strconv"
29 "strings"
30 "sync"
31 "syscall"
32
33 "github.com/bazelbuild/rules_go/go/tools/bzltestutil/chdir"
34 )
35
36
37
38
39 const TestWrapperAbnormalExit = 6
40
41 func ShouldWrap() bool {
42 if wrapEnv, ok := os.LookupEnv("GO_TEST_WRAP"); ok {
43 wrap, err := strconv.ParseBool(wrapEnv)
44 if err != nil {
45 log.Fatalf("invalid value for GO_TEST_WRAP: %q", wrapEnv)
46 }
47 return wrap
48 }
49 _, ok := os.LookupEnv("XML_OUTPUT_FILE")
50 return ok
51 }
52
53
54
55
56 func shouldAddTestV() bool {
57 if wrapEnv, ok := os.LookupEnv("GO_TEST_WRAP_TESTV"); ok {
58 wrap, err := strconv.ParseBool(wrapEnv)
59 if err != nil {
60 log.Fatalf("invalid value for GO_TEST_WRAP_TESTV: %q", wrapEnv)
61 }
62 return wrap
63 }
64 return false
65 }
66
67
68
69
70 type streamMerger struct {
71 OutW, ErrW *io.PipeWriter
72 mutex sync.Mutex
73 inner io.Writer
74 wg sync.WaitGroup
75 outR, errR *bufio.Reader
76 }
77
78 func NewStreamMerger(w io.Writer) *streamMerger {
79 outR, outW := io.Pipe()
80 errR, errW := io.Pipe()
81 return &streamMerger{
82 inner: w,
83 OutW: outW,
84 ErrW: errW,
85 outR: bufio.NewReader(outR),
86 errR: bufio.NewReader(errR),
87 }
88 }
89
90 func (m *streamMerger) Start() {
91 m.wg.Add(2)
92 process := func(r *bufio.Reader) {
93 for {
94 s, err := r.ReadString('\n')
95 if len(s) > 0 {
96 m.mutex.Lock()
97 io.WriteString(m.inner, s)
98 m.mutex.Unlock()
99 }
100 if err == io.EOF {
101 break
102 }
103 }
104 m.wg.Done()
105 }
106 go process(m.outR)
107 go process(m.errR)
108 }
109
110 func (m *streamMerger) Wait() {
111 m.wg.Wait()
112 }
113
114 func Wrap(pkg string) error {
115 var jsonBuffer bytes.Buffer
116 jsonConverter := NewConverter(&jsonBuffer, pkg, Timestamp)
117 streamMerger := NewStreamMerger(jsonConverter)
118
119 args := os.Args[1:]
120 if shouldAddTestV() {
121 args = append([]string{"-test.v"}, args...)
122 }
123 exePath := os.Args[0]
124 if !filepath.IsAbs(exePath) && strings.ContainsRune(exePath, filepath.Separator) && chdir.TestExecDir != "" {
125 exePath = filepath.Join(chdir.TestExecDir, exePath)
126 }
127
128
129
130
131
132 signal.Ignore(syscall.SIGTERM)
133
134 cmd := exec.Command(exePath, args...)
135 cmd.Env = append(os.Environ(), "GO_TEST_WRAP=0")
136 cmd.Stderr = io.MultiWriter(os.Stderr, streamMerger.ErrW)
137 cmd.Stdout = io.MultiWriter(os.Stdout, streamMerger.OutW)
138 streamMerger.Start()
139 err := cmd.Run()
140 streamMerger.ErrW.Close()
141 streamMerger.OutW.Close()
142 streamMerger.Wait()
143 jsonConverter.Close()
144 if out, ok := os.LookupEnv("XML_OUTPUT_FILE"); ok {
145 werr := writeReport(jsonBuffer, pkg, out)
146 if werr != nil {
147 if err != nil {
148 return fmt.Errorf("error while generating testreport: %s, (error wrapping test execution: %s)", werr, err)
149 }
150 return fmt.Errorf("error while generating testreport: %s", werr)
151 }
152 }
153 return err
154 }
155
156 func writeReport(jsonBuffer bytes.Buffer, pkg string, path string) error {
157 xml, cerr := json2xml(&jsonBuffer, pkg)
158 if cerr != nil {
159 return fmt.Errorf("error converting test output to xml: %s", cerr)
160 }
161 if err := ioutil.WriteFile(path, xml, 0664); err != nil {
162 return fmt.Errorf("error writing test xml: %s", err)
163 }
164 return nil
165 }
166
View as plain text