53db1c0ddd5865aca525b58bc73edd8aa1dfccb2
[reactos.git] / reactos / tools / rbuild / testsupportcode.cpp
1 /*
2 * Copyright (C) 2005 Casper S. Hornstrup
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17 */
18 #include "pch.h"
19 #include <assert.h>
20
21 #include "rbuild.h"
22
23 using std::string;
24 using std::vector;
25
26 TestSupportCode::TestSupportCode ( const Project& project )
27 : project ( project )
28 {
29 }
30
31 TestSupportCode::~TestSupportCode ()
32 {
33 }
34
35 bool
36 TestSupportCode::IsTestModule ( const Module& module )
37 {
38 return module.type == Test;
39 }
40
41 void
42 TestSupportCode::GenerateTestSupportCode ( bool verbose )
43 {
44 for ( size_t i = 0; i < project.modules.size (); i++ )
45 {
46 if ( IsTestModule ( *project.modules[i] ) )
47 {
48 GenerateTestSupportCodeForModule ( *project.modules[i],
49 verbose );
50 }
51 }
52 }
53
54 void
55 TestSupportCode::GenerateTestSupportCodeForModule ( Module& module,
56 bool verbose )
57 {
58 if ( verbose )
59 {
60 printf ( "\nGenerating test support code for %s",
61 module.name.c_str () );
62 }
63
64 WriteHooksFile ( module );
65 WriteStubsFile ( module );
66 WriteStartupFile ( module );
67 }
68
69 string
70 TestSupportCode::GetHooksFilename ( Module& module )
71 {
72 return NormalizeFilename ( Environment::GetIntermediatePath () + sSep + module.GetBasePath () + sSep + "_hooks.c" );
73 }
74
75 char*
76 TestSupportCode::WriteStubbedSymbolToHooksFile ( char* buffer,
77 const StubbedComponent& component,
78 const StubbedSymbol& symbol )
79 {
80 buffer = buffer + sprintf ( buffer,
81 " {\"%s\", \"%s\", NULL, NULL, NULL},\n",
82 component.name.c_str (),
83 symbol.newname.c_str () );
84 return buffer;
85 }
86
87 char*
88 TestSupportCode::WriteStubbedComponentToHooksFile ( char* buffer,
89 const StubbedComponent& component )
90 {
91 for ( size_t i = 0; i < component.symbols.size () ; i++ )
92 buffer = WriteStubbedSymbolToHooksFile ( buffer,
93 component,
94 *component.symbols[i] );
95 return buffer;
96 }
97
98 void
99 TestSupportCode::WriteHooksFile ( Module& module )
100 {
101 char* buf;
102 char* s;
103
104 buf = (char*) malloc ( 50*1024 );
105 if ( buf == NULL )
106 throw OutOfMemoryException ();
107
108 s = buf;
109 s = s + sprintf ( s, "/* This file is automatically generated. */\n" );
110 s = s + sprintf ( s, "#include <windows.h>\n" );
111 s = s + sprintf ( s, "#include \"regtests.h\"\n" );
112 s = s + sprintf ( s, "\n" );
113 s = s + sprintf ( s, "_API_DESCRIPTION ExternalDependencies[] =\n" );
114 s = s + sprintf ( s, "{\n" );
115
116 int symbolCount = 0;
117 for ( size_t i = 0; i < module.stubbedComponents.size () ; i++ )
118 {
119 s = WriteStubbedComponentToHooksFile ( s,
120 *module.stubbedComponents[i] );
121 symbolCount += module.stubbedComponents[i]->symbols.size ();
122 }
123
124 s = s + sprintf ( s, "};\n" );
125 s = s + sprintf ( s, "\n" );
126 s = s + sprintf ( s, "#define ExternalDependencyCount %d\n", symbolCount );
127 s = s + sprintf ( s, "ULONG MaxExternalDependency = ExternalDependencyCount - 1;\n" );
128 s = s + sprintf ( s, "\n" );
129
130 FileSupportCode::WriteIfChanged ( buf, GetHooksFilename ( module ) );
131
132 free ( buf );
133 }
134
135 string
136 TestSupportCode::GetStubsFilename ( Module& module )
137 {
138 return NormalizeFilename ( Environment::GetIntermediatePath () + sSep + module.GetBasePath () + sSep + "_stubs.S" );
139 }
140
141 string
142 GetLinkerSymbol ( const StubbedSymbol& symbol )
143 {
144 if (symbol.symbol[0] == '@')
145 return symbol.symbol;
146 else
147 return "_" + symbol.symbol;
148 }
149
150 string
151 GetLinkerImportSymbol ( const StubbedSymbol& symbol )
152 {
153 if (symbol.symbol[0] == '@')
154 return "__imp_" + symbol.symbol;
155 else
156 return "__imp__" + symbol.symbol;
157 }
158
159 string
160 GetIndirectCallTargetSymbol ( const StubbedSymbol& symbol )
161 {
162 return GetLinkerSymbol ( symbol ) + "_";
163 }
164
165 char*
166 TestSupportCode::WriteStubbedSymbolToStubsFile ( char* buffer,
167 const StubbedComponent& component,
168 const StubbedSymbol& symbol,
169 int stubIndex )
170 {
171 string linkerSymbol = GetLinkerSymbol ( symbol );
172 string linkerImportSymbol = GetLinkerImportSymbol ( symbol );
173 string indirectCallTargetSymbol = GetIndirectCallTargetSymbol ( symbol );
174 buffer = buffer + sprintf ( buffer,
175 ".globl %s\n",
176 linkerSymbol.c_str () );
177 buffer = buffer + sprintf ( buffer,
178 ".globl %s\n",
179 linkerImportSymbol.c_str () );
180 buffer = buffer + sprintf ( buffer,
181 "%s:\n",
182 linkerSymbol.c_str () );
183 buffer = buffer + sprintf ( buffer,
184 "%s:\n",
185 linkerImportSymbol.c_str () );
186 buffer = buffer + sprintf ( buffer,
187 " .long %s\n",
188 indirectCallTargetSymbol.c_str () );
189 buffer = buffer + sprintf ( buffer,
190 "%s:\n",
191 indirectCallTargetSymbol.c_str () );
192 buffer = buffer + sprintf ( buffer,
193 " pushl $%d\n",
194 stubIndex );
195 buffer = buffer + sprintf ( buffer,
196 " jmp passthrough\n" );
197 buffer = buffer + sprintf ( buffer, "\n" );
198 return buffer;
199 }
200
201 char*
202 TestSupportCode::WriteStubbedComponentToStubsFile ( char* buffer,
203 const StubbedComponent& component,
204 int* stubIndex )
205 {
206 for ( size_t i = 0; i < component.symbols.size () ; i++ )
207 buffer = WriteStubbedSymbolToStubsFile ( buffer,
208 component,
209 *component.symbols[i],
210 (*stubIndex)++ );
211 return buffer;
212 }
213
214 void
215 TestSupportCode::WriteStubsFile ( Module& module )
216 {
217 char* buf;
218 char* s;
219
220 buf = (char*) malloc ( 512*1024 );
221 if ( buf == NULL )
222 throw OutOfMemoryException ();
223
224 s = buf;
225 s = s + sprintf ( s, "/* This file is automatically generated. */\n" );
226 s = s + sprintf ( s, "passthrough:\n" );
227 s = s + sprintf ( s, " call _FrameworkGetHook@4\n" );
228 s = s + sprintf ( s, " test %%eax, %%eax\n" );
229 s = s + sprintf ( s, " je .return\n" );
230 s = s + sprintf ( s, " jmp *%%eax\n" );
231 s = s + sprintf ( s, ".return:\n" );
232 s = s + sprintf ( s, " /* This will most likely corrupt the stack */\n" );
233 s = s + sprintf ( s, " ret\n" );
234 s = s + sprintf ( s, "\n" );
235
236 int stubIndex = 0;
237 for ( size_t i = 0; i < module.stubbedComponents.size () ; i++ )
238 {
239 s = WriteStubbedComponentToStubsFile ( s,
240 *module.stubbedComponents[i],
241 &stubIndex );
242 }
243
244 FileSupportCode::WriteIfChanged ( buf, GetStubsFilename ( module ) );
245
246 free ( buf );
247 }
248
249 string
250 TestSupportCode::GetStartupFilename ( Module& module )
251 {
252 return NormalizeFilename ( Environment::GetIntermediatePath () + sSep + module.GetBasePath () + sSep + "_startup.c" );
253 }
254
255 bool
256 TestSupportCode::IsUnknownCharacter ( char ch )
257 {
258 if ( ch >= 'a' && ch <= 'z' )
259 return false;
260 if ( ch >= 'A' && ch <= 'Z' )
261 return false;
262 if ( ch >= '0' && ch <= '9' )
263 return false;
264 return true;
265 }
266
267 string
268 TestSupportCode::GetTestDispatcherName ( string filename )
269 {
270 string filenamePart = ReplaceExtension ( GetFilename ( filename ), "" );
271 if ( filenamePart.length () > 0 )
272 filenamePart[0] = toupper ( filenamePart[0] );
273 for ( size_t i = 1; i < filenamePart.length (); i++ )
274 {
275 if ( IsUnknownCharacter ( filenamePart[i] ) )
276 filenamePart[i] = '_';
277 else
278 filenamePart[i] = tolower ( filenamePart[i] );
279 }
280 return filenamePart + "Test";
281 }
282
283 bool
284 TestSupportCode::IsTestFile ( string& filename ) const
285 {
286 if ( stricmp ( GetFilename ( filename ).c_str (), "setup.c" ) == 0 )
287 return false;
288 return true;
289 }
290
291 void
292 TestSupportCode::GetSourceFilenames ( string_list& list,
293 Module& module ) const
294 {
295 size_t i;
296
297 const vector<CompilationUnit*>& compilationUnits = module.non_if_data.compilationUnits;
298 for ( i = 0; i < compilationUnits.size (); i++ )
299 {
300 string filename = compilationUnits[i]->GetFilename();
301 if ( !compilationUnits[i]->IsGeneratedFile () && IsTestFile ( filename ) )
302 list.push_back ( filename );
303 }
304 // intentionally make a copy so that we can append more work in
305 // the middle of processing without having to go recursive
306 vector<If*> v = module.non_if_data.ifs;
307 for ( i = 0; i < v.size (); i++ )
308 {
309 size_t j;
310 If& rIf = *v[i];
311 // check for sub-ifs to add to list
312 const vector<If*>& ifs = rIf.data.ifs;
313 for ( j = 0; j < ifs.size (); j++ )
314 v.push_back ( ifs[j] );
315 const vector<CompilationUnit*>& compilationUnits = rIf.data.compilationUnits;
316 for ( j = 0; j < compilationUnits.size (); j++ )
317 {
318 CompilationUnit& compilationUnit = *compilationUnits[j];
319 string filename = compilationUnits[j]->GetFilename();
320 if ( !compilationUnit.IsGeneratedFile () && IsTestFile ( filename ) )
321 list.push_back ( filename );
322 }
323 }
324 }
325
326 char*
327 TestSupportCode::WriteTestDispatcherPrototypesToStartupFile ( char* buffer,
328 Module& module )
329 {
330 string_list files;
331 GetSourceFilenames ( files,
332 module );
333 for ( size_t i = 0; i < files.size (); i++ )
334 {
335 buffer = buffer + sprintf ( buffer,
336 "extern void %s(int Command, char *Buffer);\n",
337 GetTestDispatcherName ( files[i] ).c_str () );
338 }
339 buffer = buffer + sprintf ( buffer, "\n" );
340 return buffer;
341 }
342
343 char*
344 TestSupportCode::WriteRegisterTestsFunctionToStartupFile ( char* buffer,
345 Module& module )
346 {
347 buffer = buffer + sprintf ( buffer,
348 "extern void AddTest(TestRoutine Routine);\n" );
349 buffer = buffer + sprintf ( buffer,
350 "\n" );
351
352 buffer = buffer + sprintf ( buffer,
353 "void\n" );
354 buffer = buffer + sprintf ( buffer,
355 "RegisterTests()\n" );
356 buffer = buffer + sprintf ( buffer,
357 "{\n" );
358
359 string_list files;
360 GetSourceFilenames ( files,
361 module );
362 for ( size_t i = 0; i < files.size (); i++ )
363 {
364 buffer = buffer + sprintf ( buffer,
365 "AddTest((TestRoutine)%s);\n",
366 GetTestDispatcherName ( files[i]).c_str () );
367 }
368 buffer = buffer + sprintf ( buffer,
369 "}\n" );
370 buffer = buffer + sprintf ( buffer, "\n" );
371 return buffer;
372 }
373
374 void
375 TestSupportCode::WriteStartupFile ( Module& module )
376 {
377 char* buf;
378 char* s;
379
380 buf = (char*) malloc ( 50*1024 );
381 if ( buf == NULL )
382 throw OutOfMemoryException ();
383
384 s = buf;
385 s = s + sprintf ( s, "/* This file is automatically generated. */\n" );
386 s = s + sprintf ( s, "\n" );
387 s = s + sprintf ( s, "#include <windows.h>\n" );
388 s = s + sprintf ( s, "#include \"regtests.h\"\n" );
389 s = s + sprintf ( s, "\n" );
390 s = WriteTestDispatcherPrototypesToStartupFile ( s,
391 module );
392 s = WriteRegisterTestsFunctionToStartupFile ( s,
393 module );
394 s = s + sprintf ( s, "\n" );
395 s = s + sprintf ( s, "void\n" );
396 s = s + sprintf ( s, "ConsoleWrite(char *Buffer)\n" );
397 s = s + sprintf ( s, "{\n" );
398 s = s + sprintf ( s, " printf(Buffer);\n" );
399 s = s + sprintf ( s, "}\n" );
400 s = s + sprintf ( s, "\n" );
401 s = s + sprintf ( s, "int\n" );
402 s = s + sprintf ( s, "STDCALL\n" );
403 s = s + sprintf ( s, "WinMain(HINSTANCE hInstance,\n" );
404 s = s + sprintf ( s, " HINSTANCE hPrevInstance,\n" );
405 s = s + sprintf ( s, " LPSTR lpszCmdParam,\n" );
406 s = s + sprintf ( s, " int nCmdShow)\n" );
407 s = s + sprintf ( s, "{\n" );
408 s = s + sprintf ( s, " _SetPriorityClass(_GetCurrentProcess(), HIGH_PRIORITY_CLASS);\n" );
409 s = s + sprintf ( s, " _SetThreadPriority(_GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL);\n" );
410 s = s + sprintf ( s, " InitializeTests();\n" );
411 s = s + sprintf ( s, " RegisterTests();\n" );
412 s = s + sprintf ( s, " SetupOnce();\n" );
413 s = s + sprintf ( s, " PerformTests(ConsoleWrite, NULL);\n" );
414 s = s + sprintf ( s, " _ExitProcess(0);\n" );
415 s = s + sprintf ( s, " return 0;\n" );
416 s = s + sprintf ( s, "}\n" );
417 s = s + sprintf ( s, "\n" );
418
419 FileSupportCode::WriteIfChanged ( buf, GetStartupFilename ( module ) );
420
421 free ( buf );
422 }