1
+ /* *****************************************************************************
2
+ * The MIT License (MIT)
3
+ *
4
+ * Copyright (c) 2017 Baldur Karlsson
5
+ *
6
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ * of this software and associated documentation files (the "Software"), to deal
8
+ * in the Software without restriction, including without limitation the rights
9
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ * copies of the Software, and to permit persons to whom the Software is
11
+ * furnished to do so, subject to the following conditions:
12
+ *
13
+ * The above copyright notice and this permission notice shall be included in
14
+ * all copies or substantial portions of the Software.
15
+ *
16
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ * THE SOFTWARE.
23
+ ******************************************************************************/
24
+
25
+ #define CATCH_CONFIG_RUNNER
26
+ #define CATCH_CONFIG_NOSTDOUT
27
+ #include " catch.hpp"
28
+ #include " api/replay/renderdoc_replay.h"
29
+ #include " serialise/serialiser.h"
30
+ #include " serialise/string_utils.h"
31
+
32
+ struct AppVeyorListener : Catch::TestEventListenerBase
33
+ {
34
+ using TestEventListenerBase::TestEventListenerBase; // inherit constructor
35
+
36
+ bool enabled = false ;
37
+ std::string hostname;
38
+ uint16_t port = 0 ;
39
+
40
+ virtual void testRunStarting (Catch::TestRunInfo const &testRunInfo)
41
+ {
42
+ const char *url = Process::GetEnvVariable (" APPVEYOR_API_URL" );
43
+
44
+ if (url)
45
+ {
46
+ if (strncmp (url, " http://" , 7 ))
47
+ return ;
48
+
49
+ url += 7 ;
50
+
51
+ const char *sep = strchr (url, ' :' );
52
+
53
+ if (!sep)
54
+ return ;
55
+
56
+ hostname = std::string (url, sep);
57
+
58
+ url = sep + 1 ;
59
+
60
+ port = 0 ;
61
+ while (*url >= ' 0' && *url <= ' 9' )
62
+ {
63
+ port *= 10 ;
64
+ port += int ((*url) - ' 0' );
65
+ url++;
66
+ }
67
+
68
+ Network::Socket *sock = Network::CreateClientSocket (hostname.c_str (), port, 10 );
69
+
70
+ if (sock)
71
+ enabled = true ;
72
+
73
+ SAFE_DELETE (sock);
74
+ }
75
+ }
76
+
77
+ std::string curTest;
78
+ std::vector<std::string> sectionStack;
79
+
80
+ virtual void testCaseStarting (Catch::TestCaseInfo const &testInfo) { curTest = testInfo.name ; }
81
+ virtual void sectionStarting (Catch::SectionInfo const §ionInfo)
82
+ {
83
+ if (curTest == sectionInfo.name )
84
+ return ;
85
+
86
+ sectionStack.push_back (sectionInfo.name );
87
+
88
+ if (enabled)
89
+ {
90
+ Network::Socket *sock = Network::CreateClientSocket (hostname.c_str (), port, 10 );
91
+
92
+ if (sock)
93
+ {
94
+ std::string req = MakeHTTPRequest ();
95
+ sock->SendDataBlocking (req.c_str (), (uint32_t )req.size ());
96
+ }
97
+
98
+ SAFE_DELETE (sock);
99
+ }
100
+ }
101
+
102
+ std::string errorList;
103
+
104
+ virtual bool assertionEnded (Catch::AssertionStats const &assertionStats)
105
+ {
106
+ using namespace Catch ;
107
+
108
+ if (!assertionStats.assertionResult .isOk ())
109
+ {
110
+ std::ostringstream msg;
111
+ msg << assertionStats.assertionResult .getSourceInfo () << " : " ;
112
+
113
+ switch (assertionStats.assertionResult .getResultType ())
114
+ {
115
+ case ResultWas::ExpressionFailed: msg << " Failed" ; break ;
116
+ case ResultWas::ThrewException: msg << " Threw exception" ; break ;
117
+ case ResultWas::FatalErrorCondition: msg << " Fatal error'd" ; break ;
118
+ case ResultWas::DidntThrowException: msg << " Didn't throw expected exception" ; break ;
119
+ case ResultWas::ExplicitFailure: msg << " Explicitly failed" ; break ;
120
+
121
+ case ResultWas::Ok:
122
+ case ResultWas::Info:
123
+ case ResultWas::Warning:
124
+ case ResultWas::Unknown:
125
+ case ResultWas::FailureBit:
126
+ case ResultWas::Exception: break ;
127
+ }
128
+
129
+ if (assertionStats.infoMessages .size () >= 1 )
130
+ msg << " with message(s):" ;
131
+ for (auto it = assertionStats.infoMessages .begin (); it != assertionStats.infoMessages .end (); ++it)
132
+ msg << " \n " << it->message ;
133
+
134
+ if (assertionStats.assertionResult .hasExpression ())
135
+ {
136
+ msg << " \n " << assertionStats.assertionResult .getExpressionInMacro ()
137
+ << " \n with expansion:\n " << assertionStats.assertionResult .getExpandedExpression ()
138
+ << " \n " ;
139
+ }
140
+
141
+ errorList += msg.str ();
142
+ }
143
+
144
+ return true ;
145
+ }
146
+
147
+ virtual void sectionEnded (Catch::SectionStats const §ionStats)
148
+ {
149
+ if (curTest == sectionStats.sectionInfo .name )
150
+ return ;
151
+
152
+ if (enabled)
153
+ {
154
+ Network::Socket *sock = Network::CreateClientSocket (hostname.c_str (), port, 10 );
155
+
156
+ if (sock)
157
+ {
158
+ std::string req = MakeHTTPRequest (sectionStats.durationInSeconds * 1000.0 ,
159
+ sectionStats.assertions .allOk ());
160
+ sock->SendDataBlocking (req.c_str (), (uint32_t )req.size ());
161
+ }
162
+
163
+ errorList.clear ();
164
+
165
+ SAFE_DELETE (sock);
166
+ }
167
+
168
+ sectionStack.pop_back ();
169
+ }
170
+
171
+ private:
172
+ std::string MakeHTTPRequest (double msDuration = -1.0 , bool passed = false )
173
+ {
174
+ std::string json;
175
+
176
+ bool update = msDuration >= 0.0 ;
177
+
178
+ const char *outcome = " Running" ;
179
+
180
+ if (update)
181
+ outcome = passed ? " Passed" : " Failed" ;
182
+
183
+ std::string testName;
184
+ for (const std::string §ion : sectionStack)
185
+ {
186
+ if (!testName.empty ())
187
+ testName += " > " ;
188
+ testName += section;
189
+ }
190
+
191
+ json = StringFormat::Fmt (R"(
192
+ {
193
+ "testName": "%s",
194
+ "testFramework": "Catch.hpp",
195
+ "fileName": "%s",
196
+ "outcome": "%s",
197
+ "durationMilliseconds": "%.0f",
198
+ "ErrorMessage": "%s",
199
+ "ErrorStackTrace": "",
200
+ "StdOut": "",
201
+ "StdErr": ""
202
+ })" ,
203
+ testName.c_str (), curTest.c_str (), outcome,
204
+ RDCMAX (msDuration * 1000.0 , 0.0 ), escape (trim (errorList)).c_str ());
205
+
206
+ std::string http;
207
+ http += StringFormat::Fmt (" %s /api/tests HTTP/1.1\r\n " , update ? " PUT" : " POST" );
208
+ http += StringFormat::Fmt (" Host: %s\r\n " , hostname.c_str ());
209
+ http += " Connection: close\r\n " ;
210
+ http += " Content-Type: application/json\r\n " ;
211
+ http += StringFormat::Fmt (" Content-Length: %zu\r\n " , json.size ());
212
+ http += " User-Agent: Catch.hpp appveyor updater\r\n " ;
213
+ http += " \r\n " ;
214
+ return http + json;
215
+ }
216
+
217
+ std::string escape (const std::string &input)
218
+ {
219
+ std::string ret = input;
220
+ size_t i = ret.find_first_of (" \"\n\\ " , 0 );
221
+ while (i != std::string::npos)
222
+ {
223
+ if (ret[i] == ' "' )
224
+ ret.replace (i, 1 , " \\\" " );
225
+ else if (ret[i] == ' \\ ' )
226
+ ret.replace (i, 1 , " \\\\ " );
227
+ else if (ret[i] == ' \n ' )
228
+ ret.replace (i, 1 , " \\ n" );
229
+
230
+ i = ret.find_first_of (" \"\n\\ " , i + 2 );
231
+ }
232
+
233
+ return ret;
234
+ }
235
+ };
236
+ CATCH_REGISTER_LISTENER (AppVeyorListener)
237
+
238
+ class LogOutputter : public std::stringbuf
239
+ {
240
+ public:
241
+ LogOutputter () {}
242
+ virtual int sync () override
243
+ {
244
+ std::string msg = this ->str ();
245
+ OSUtility::WriteOutput (OSUtility::Output_DebugMon, msg.c_str ());
246
+ OSUtility::WriteOutput (OSUtility::Output_StdOut, msg.c_str ());
247
+ this ->str (" " );
248
+ return 0 ;
249
+ }
250
+
251
+ // force a sync on every output
252
+ virtual std::streamsize xsputn (const char *s, std::streamsize n) override
253
+ {
254
+ std::streamsize ret = std::stringbuf::xsputn (s, n);
255
+ sync ();
256
+ return ret;
257
+ }
258
+ };
259
+
260
+ std::ostream *stream = NULL ;
261
+
262
+ namespace Catch
263
+ {
264
+ std::ostream &cout ()
265
+ {
266
+ return *stream;
267
+ }
268
+ std::ostream &cerr ()
269
+ {
270
+ return *stream;
271
+ }
272
+ std::ostream &clog ()
273
+ {
274
+ return *stream;
275
+ }
276
+ }
277
+
278
+ extern " C" RENDERDOC_API int RENDERDOC_CC
279
+ RENDERDOC_RunUnitTests (const rdctype::str &command, const rdctype::array<rdctype::str> &args)
280
+ {
281
+ LogOutputter logbuf;
282
+ std::ostream logstream (&logbuf);
283
+ stream = &logstream;
284
+
285
+ Catch::Session session;
286
+
287
+ session.configData ().name = " RenderDoc" ;
288
+ session.configData ().shouldDebugBreak = OSUtility::DebuggerPresent ();
289
+
290
+ const char **argv = new const char *[args.count + 1 ];
291
+ argv[0 ] = command.c_str ();
292
+ for (int i = 0 ; i < args.count ; i++)
293
+ argv[i + 1 ] = args.elems [i].c_str ();
294
+
295
+ int ret = session.applyCommandLine (args.count + 1 , argv);
296
+
297
+ delete[] argv;
298
+
299
+ // command line error
300
+ if (ret != 0 )
301
+ return ret;
302
+
303
+ int numFailed = session.run ();
304
+
305
+ // Note that on unices only the lower 8 bits are usually used, clamping
306
+ // the return value to 255 prevents false negative when some multiple
307
+ // of 256 tests has failed
308
+ return (numFailed < 0xff ? numFailed : 0xff );
309
+ }
0 commit comments