@@ -10,6 +10,7 @@ import (
10
10
"fmt"
11
11
"io/ioutil"
12
12
"net/http"
13
+ "net/http/httptest"
13
14
"sync"
14
15
"time"
15
16
@@ -24,6 +25,12 @@ type ValgrindCLI struct {
24
25
Timeout time.Duration
25
26
}
26
27
28
+ type ValgrindCGI struct {
29
+ CGI
30
+ Valgrind string
31
+ Timeout time.Duration
32
+ }
33
+
27
34
func (tx * ValgrindCLI ) Execute () (http.Header , []byte , error ) {
28
35
if len (tx .Path ) == 0 {
29
36
return nil , []byte ("skip: executable not specified" ), nil
@@ -34,8 +41,8 @@ func (tx *ValgrindCLI) Execute() (http.Header, []byte, error) {
34
41
// valgrind reports to their tests in the same way we use the appname
35
42
// to link the transaction data. Failing that, perhaps we could use
36
43
// the valgrind process's pid instead.
37
- valgrindMu .Lock ()
38
- defer valgrindMu .Unlock ()
44
+ // valgrindMu.Lock()
45
+ // defer valgrindMu.Unlock()
39
46
40
47
cmd := valgrind .Memcheck (tx .Valgrind , "--quiet" )
41
48
cmd .Args = append (cmd .Args , "--xml=yes" )
@@ -105,6 +112,82 @@ func (tx *ValgrindCLI) Execute() (http.Header, []byte, error) {
105
112
return nil , output , err
106
113
}
107
114
115
+ func (tx * ValgrindCGI ) Execute () (http.Header , []byte , error ) {
116
+ if len (tx .handler .Path ) == 0 {
117
+ return nil , []byte ("skip: executable not specified" ), nil
118
+ }
119
+
120
+ // For now, we don't have a mechanism to handle concurrent invocations
121
+ // of Valgrind. In the future, we could use the appname to connect
122
+ // valgrind reports to their tests in the same way we use the appname
123
+ // to link the transaction data. Failing that, perhaps we could use
124
+ // the valgrind process's pid instead.
125
+ //valgrindMu.Lock()
126
+ //defer valgrindMu.Unlock()
127
+
128
+ cmd := valgrind .Memcheck (tx .Valgrind , "--quiet" )
129
+ cmd .Args = append (cmd .Args , "--xml=yes" )
130
+ //cmd.Args = append(cmd.Args, "--child-silent-after-fork=no")
131
+ cmd .Args = append (cmd .Args , "--xml-socket=" + valgrindLn .Addr ().String ())
132
+ cmd .Args = append (cmd .Args , "--" )
133
+ cmd .Args = append (cmd .Args , tx .handler .Path )
134
+ if len (tx .handler .Args ) > 0 {
135
+ cmd .Args = append (cmd .Args , tx .handler .Args ... )
136
+ }
137
+
138
+ log .Debugf ("command: %v" , cmd )
139
+
140
+ // Replace the handler with valgrind
141
+ tx .handler .Path = cmd .Path
142
+ tx .handler .Args = cmd .Args [1 :] // The first Arg is the Path.
143
+ // ServeHTTP will re-add Path
144
+ // to the front of Args
145
+
146
+ ch := make (chan resultOrError , 1 )
147
+ go func () {
148
+ var result resultOrError
149
+ result .R , result .E = acceptOneReport (tx .Timeout )
150
+ ch <- result
151
+ }()
152
+
153
+ resp := httptest .NewRecorder ()
154
+ tx .handler .ServeHTTP (resp , tx .request )
155
+
156
+ vgOutput := <- ch
157
+
158
+ // Append the output from Valgrind to the test output.
159
+ //
160
+ // TODO: Eventually we want to report valgrind output separately, so we can
161
+ // treat valgrind errors similarly to failed test expectations. i.e. We'd
162
+ // like to add them to Test.Failures. After all, each test has an implicit
163
+ // expectation that it will not exhibit memory bugs!
164
+ //
165
+ // TODO: Once the former is in place, we should be able to parse the memory
166
+ // leak reports produced by the Zend Memory Manager from the test output and
167
+ // report them the same way as valgrind errors.
168
+ output := resp .Body .Bytes ()
169
+ if vgOutput .R != nil && len (vgOutput .R .Errors ) > 0 {
170
+ // Safe to ignore the error here, Report.MarshalText() never fails.
171
+ data , _ := vgOutput .R .MarshalText ()
172
+ output = append (output , '\n' )
173
+ output = append (output , data ... )
174
+ }
175
+
176
+
177
+ // Ensure a non-nil error is returned when valgrind detects errors.
178
+ // Otherwise, the test could be marked as passing if it does not have
179
+ // any expectations on the test output. This sucks.
180
+ //
181
+ // TODO: Remove this when valgrind errors can be treated as failed test
182
+ // expectations.
183
+ err := vgOutput .E
184
+ if err == nil && vgOutput .R != nil && len (vgOutput .R .Errors ) > 0 {
185
+ err = fmt .Errorf ("detected %d memory errors" , len (vgOutput .R .Errors ))
186
+ }
187
+
188
+ return resp .HeaderMap , output , err
189
+
190
+ }
108
191
// resultOrError is a poor man's sum type.
109
192
type resultOrError struct {
110
193
R * valgrind.Report
0 commit comments