Show execution time of tests
[reactos.git] / reactos / tools / rbuild / testsupportcode.cpp
1 #include "pch.h"
2 #include <assert.h>
3
4 #include "rbuild.h"
5
6 using std::string;
7 using std::vector;
8
9 TestSupportCode::TestSupportCode ( const Project& project )
10 : project ( project )
11 {
12 }
13
14 TestSupportCode::~TestSupportCode ()
15 {
16 }
17
18 bool
19 TestSupportCode::IsTestModule ( const Module& module )
20 {
21 return module.type == Test;
22 }
23
24 void
25 TestSupportCode::GenerateTestSupportCode ( bool verbose )
26 {
27 for ( size_t i = 0; i < project.modules.size (); i++ )
28 {
29 if ( IsTestModule ( *project.modules[i] ) )
30 {
31 GenerateTestSupportCodeForModule ( *project.modules[i],
32 verbose );
33 }
34 }
35 }
36
37 void
38 TestSupportCode::GenerateTestSupportCodeForModule ( Module& module,
39 bool verbose )
40 {
41 if ( verbose )
42 {
43 printf ( "\nGenerating test support code for %s",
44 module.name.c_str () );
45 }
46
47 WriteHooksFile ( module );
48 WriteStubsFile ( module );
49 WriteStartupFile ( module );
50 }
51
52 string
53 TestSupportCode::GetHooksFilename ( Module& module )
54 {
55 return NormalizeFilename ( Environment::GetIntermediatePath () + SSEP + module.GetBasePath () + SSEP + "_hooks.c" );
56 }
57
58 char*
59 TestSupportCode::WriteStubbedSymbolToHooksFile ( char* buffer,
60 const StubbedComponent& component,
61 const StubbedSymbol& symbol )
62 {
63 buffer = buffer + sprintf ( buffer,
64 " {\"%s\", \"%s\", NULL, NULL, NULL},\n",
65 component.name.c_str (),
66 symbol.newname.c_str () );
67 return buffer;
68 }
69
70 char*
71 TestSupportCode::WriteStubbedComponentToHooksFile ( char* buffer,
72 const StubbedComponent& component )
73 {
74 for ( size_t i = 0; i < component.symbols.size () ; i++ )
75 buffer = WriteStubbedSymbolToHooksFile ( buffer,
76 component,
77 *component.symbols[i] );
78 return buffer;
79 }
80
81 void
82 TestSupportCode::WriteHooksFile ( Module& module )
83 {
84 char* buf;
85 char* s;
86
87 buf = (char*) malloc ( 50*1024 );
88 if ( buf == NULL )
89 throw OutOfMemoryException ();
90
91 s = buf;
92 s = s + sprintf ( s, "/* This file is automatically generated. */\n" );
93 s = s + sprintf ( s, "#include <windows.h>\n" );
94 s = s + sprintf ( s, "#include \"regtests.h\"\n" );
95 s = s + sprintf ( s, "\n" );
96 s = s + sprintf ( s, "API_DESCRIPTION ExternalDependencies[] =\n" );
97 s = s + sprintf ( s, "{\n" );
98
99 int symbolCount = 0;
100 for ( size_t i = 0; i < module.stubbedComponents.size () ; i++ )
101 {
102 s = WriteStubbedComponentToHooksFile ( s,
103 *module.stubbedComponents[i] );
104 symbolCount += module.stubbedComponents[i]->symbols.size ();
105 }
106
107 s = s + sprintf ( s, "};\n" );
108 s = s + sprintf ( s, "\n" );
109 s = s + sprintf ( s, "#define ExternalDependencyCount %d\n", symbolCount );
110 s = s + sprintf ( s, "ULONG MaxExternalDependency = ExternalDependencyCount - 1;\n" );
111 s = s + sprintf ( s, "\n" );
112
113 FileSupportCode::WriteIfChanged ( buf, GetHooksFilename ( module ) );
114
115 free ( buf );
116 }
117
118 string
119 TestSupportCode::GetStubsFilename ( Module& module )
120 {
121 return NormalizeFilename ( Environment::GetIntermediatePath () + SSEP + module.GetBasePath () + SSEP + "_stubs.S" );
122 }
123
124 char*
125 TestSupportCode::WriteStubbedSymbolToStubsFile ( char* buffer,
126 const StubbedComponent& component,
127 const StubbedSymbol& symbol,
128 int stubIndex )
129 {
130 buffer = buffer + sprintf ( buffer,
131 ".globl _%s\n",
132 symbol.symbol.c_str () );
133 buffer = buffer + sprintf ( buffer,
134 "_%s:\n",
135 symbol.symbol.c_str () );
136 buffer = buffer + sprintf ( buffer,
137 " pushl $%d\n",
138 stubIndex );
139 buffer = buffer + sprintf ( buffer,
140 " jmp passthrough\n" );
141 buffer = buffer + sprintf ( buffer, "\n" );
142 return buffer;
143 }
144
145 char*
146 TestSupportCode::WriteStubbedComponentToStubsFile ( char* buffer,
147 const StubbedComponent& component,
148 int* stubIndex )
149 {
150 for ( size_t i = 0; i < component.symbols.size () ; i++ )
151 buffer = WriteStubbedSymbolToStubsFile ( buffer,
152 component,
153 *component.symbols[i],
154 (*stubIndex)++ );
155 return buffer;
156 }
157
158 void
159 TestSupportCode::WriteStubsFile ( Module& module )
160 {
161 char* buf;
162 char* s;
163
164 buf = (char*) malloc ( 50*1024 );
165 if ( buf == NULL )
166 throw OutOfMemoryException ();
167
168 s = buf;
169 s = s + sprintf ( s, "/* This file is automatically generated. */\n" );
170 s = s + sprintf ( s, "passthrough:\n" );
171 s = s + sprintf ( s, " call _FrameworkGetHook@4\n" );
172 s = s + sprintf ( s, " test %%eax, %%eax\n" );
173 s = s + sprintf ( s, " je .return\n" );
174 s = s + sprintf ( s, " jmp *%%eax\n" );
175 s = s + sprintf ( s, ".return:\n" );
176 s = s + sprintf ( s, " /* This will most likely corrupt the stack */\n" );
177 s = s + sprintf ( s, " ret\n" );
178 s = s + sprintf ( s, "\n" );
179
180 int stubIndex = 0;
181 for ( size_t i = 0; i < module.stubbedComponents.size () ; i++ )
182 {
183 s = WriteStubbedComponentToStubsFile ( s,
184 *module.stubbedComponents[i],
185 &stubIndex );
186 }
187
188 FileSupportCode::WriteIfChanged ( buf, GetStubsFilename ( module ) );
189
190 free ( buf );
191 }
192
193 string
194 TestSupportCode::GetStartupFilename ( Module& module )
195 {
196 return NormalizeFilename ( Environment::GetIntermediatePath () + SSEP + module.GetBasePath () + SSEP + "_startup.c" );
197 }
198
199 string
200 TestSupportCode::GetTestDispatcherName ( string filename )
201 {
202 string filenamePart = ReplaceExtension ( GetFilename ( filename ), "" );
203 if ( filenamePart.length () > 0 )
204 filenamePart[0] = toupper ( filenamePart[0] );
205 for ( size_t i = 1; i < filenamePart.length (); i++ )
206 {
207 filenamePart[i] = tolower ( filenamePart[i] );
208 }
209 return filenamePart + "Test";
210 }
211
212 bool
213 TestSupportCode::IsTestFile ( string& filename ) const
214 {
215 if ( stricmp ( GetFilename ( filename ).c_str (), "setup.c" ) == 0 )
216 return false;
217 return true;
218 }
219
220 void
221 TestSupportCode::GetSourceFilenames ( string_list& list,
222 Module& module ) const
223 {
224 size_t i;
225
226 const vector<File*>& files = module.non_if_data.files;
227 for ( i = 0; i < files.size (); i++ )
228 {
229 if ( !files[i]->IsGeneratedFile () && IsTestFile ( files[i]->name ) )
230 list.push_back ( files[i]->name );
231 }
232 // intentionally make a copy so that we can append more work in
233 // the middle of processing without having to go recursive
234 vector<If*> v = module.non_if_data.ifs;
235 for ( i = 0; i < v.size (); i++ )
236 {
237 size_t j;
238 If& rIf = *v[i];
239 // check for sub-ifs to add to list
240 const vector<If*>& ifs = rIf.data.ifs;
241 for ( j = 0; j < ifs.size (); j++ )
242 v.push_back ( ifs[j] );
243 const vector<File*>& files = rIf.data.files;
244 for ( j = 0; j < files.size (); j++ )
245 {
246 File& file = *files[j];
247 if ( !file.IsGeneratedFile () && IsTestFile ( file.name ) )
248 {
249 list.push_back ( file.name );
250 }
251 }
252 }
253 }
254
255 char*
256 TestSupportCode::WriteTestDispatcherPrototypesToStartupFile ( char* buffer,
257 Module& module )
258 {
259 string_list files;
260 GetSourceFilenames ( files,
261 module );
262 for ( size_t i = 0; i < files.size (); i++ )
263 {
264 buffer = buffer + sprintf ( buffer,
265 "extern void %s(int Command, char *Buffer);\n",
266 GetTestDispatcherName ( files[i] ).c_str () );
267 }
268 buffer = buffer + sprintf ( buffer, "\n" );
269 return buffer;
270 }
271
272 char*
273 TestSupportCode::WriteRegisterTestsFunctionToStartupFile ( char* buffer,
274 Module& module )
275 {
276 buffer = buffer + sprintf ( buffer,
277 "extern void AddTest(TestRoutine Routine);\n" );
278 buffer = buffer + sprintf ( buffer,
279 "\n" );
280
281 buffer = buffer + sprintf ( buffer,
282 "void\n" );
283 buffer = buffer + sprintf ( buffer,
284 "RegisterTests()\n" );
285 buffer = buffer + sprintf ( buffer,
286 "{\n" );
287
288 string_list files;
289 GetSourceFilenames ( files,
290 module );
291 for ( size_t i = 0; i < files.size (); i++ )
292 {
293 buffer = buffer + sprintf ( buffer,
294 "AddTest((TestRoutine)%s);\n",
295 GetTestDispatcherName ( files[i]).c_str () );
296 }
297 buffer = buffer + sprintf ( buffer,
298 "}\n" );
299 buffer = buffer + sprintf ( buffer, "\n" );
300 return buffer;
301 }
302
303 void
304 TestSupportCode::WriteStartupFile ( Module& module )
305 {
306 char* buf;
307 char* s;
308
309 buf = (char*) malloc ( 50*1024 );
310 if ( buf == NULL )
311 throw OutOfMemoryException ();
312
313 s = buf;
314 s = s + sprintf ( s, "/* This file is automatically generated. */\n" );
315 s = s + sprintf ( s, "\n" );
316 s = s + sprintf ( s, "#include <windows.h>\n" );
317 s = s + sprintf ( s, "#include \"regtests.h\"\n" );
318 s = s + sprintf ( s, "\n" );
319 s = WriteTestDispatcherPrototypesToStartupFile ( s,
320 module );
321 s = WriteRegisterTestsFunctionToStartupFile ( s,
322 module );
323 s = s + sprintf ( s, "\n" );
324 s = s + sprintf ( s, "void\n" );
325 s = s + sprintf ( s, "ConsoleWrite(char *Buffer)\n" );
326 s = s + sprintf ( s, "{\n" );
327 s = s + sprintf ( s, " printf(Buffer);\n" );
328 s = s + sprintf ( s, "}\n" );
329 s = s + sprintf ( s, "\n" );
330 s = s + sprintf ( s, "int\n" );
331 s = s + sprintf ( s, "mainCRTStartup(HANDLE hInstance,\n" );
332 s = s + sprintf ( s, " HANDLE hPrevInstance,\n" );
333 s = s + sprintf ( s, " LPSTR lpszCmdParam,\n" );
334 s = s + sprintf ( s, " int nCmdShow)\n" );
335 s = s + sprintf ( s, "{\n" );
336 s = s + sprintf ( s, " _SetPriorityClass(GetCurrentProcess(), IDLE_PRIORITY_CLASS);\n" );
337 s = s + sprintf ( s, " InitializeTests();\n" );
338 s = s + sprintf ( s, " RegisterTests();\n" );
339 s = s + sprintf ( s, " SetupOnce();\n" );
340 s = s + sprintf ( s, " PerformTests(ConsoleWrite, NULL);\n" );
341 s = s + sprintf ( s, " _ExitProcess(0);\n" );
342 s = s + sprintf ( s, " return 0;\n" );
343 s = s + sprintf ( s, "}\n" );
344 s = s + sprintf ( s, "\n" );
345
346 FileSupportCode::WriteIfChanged ( buf, GetStartupFilename ( module ) );
347
348 free ( buf );
349 }