source: box/trunk/lib/common/Test.cpp @ 2845

Revision 2845, 8.6 KB checked in by chris, 17 months ago (diff)

Move accurate sleep code from Test.cpp to BoxTime?, allow requesting times
in microseconds with ShortSleep?(), make safe_sleep() use it.

Rename MILLI_SEC_IN_NANO_SEC to MILLI_SEC_IN_SEC which is what it actually is.

Line 
1// --------------------------------------------------------------------------
2//
3// File
4//              Name:    Test.cpp
5//              Purpose: Useful stuff for tests
6//              Created: 2008/04/05
7//
8// --------------------------------------------------------------------------
9
10#include "Box.h"
11
12#include <errno.h>
13#include <signal.h>
14#include <stdio.h>
15#include <stdlib.h>
16
17#include <sys/stat.h>
18#include <sys/types.h>
19
20#ifdef HAVE_UNISTD_H
21        #include <unistd.h>
22#endif
23
24#include "BoxTime.h"
25#include "Test.h"
26
27bool TestFileExists(const char *Filename)
28{
29        EMU_STRUCT_STAT st;
30        return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0;
31}
32
33bool TestFileNotEmpty(const char *Filename)
34{
35        EMU_STRUCT_STAT st;
36        return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == 0 &&
37                st.st_size > 0;
38}
39
40bool TestDirExists(const char *Filename)
41{
42        EMU_STRUCT_STAT st;
43        return EMU_STAT(Filename, &st) == 0 && (st.st_mode & S_IFDIR) == S_IFDIR;
44}
45
46// -1 if doesn't exist
47int TestGetFileSize(const std::string& Filename)
48{
49        EMU_STRUCT_STAT st;
50        if(EMU_STAT(Filename.c_str(), &st) == 0)
51        {
52                return st.st_size;
53        }
54        return -1;
55}
56
57std::string ConvertPaths(const std::string& rOriginal)
58{
59#ifdef WIN32
60        // convert UNIX paths to native
61
62        std::string converted;
63        for (size_t i = 0; i < rOriginal.size(); i++)
64        {
65                if (rOriginal[i] == '/')
66                {
67                        converted += '\\';
68                }
69                else
70                {
71                        converted += rOriginal[i];
72                }
73        }
74        return converted;
75
76#else // !WIN32
77        return rOriginal;
78#endif
79}
80
81int RunCommand(const std::string& rCommandLine)
82{
83        return ::system(ConvertPaths(rCommandLine).c_str());
84}
85
86#ifdef WIN32
87#include <windows.h>
88#endif
89
90bool ServerIsAlive(int pid)
91{
92        #ifdef WIN32
93
94                HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION,
95                        false, pid);
96                if (hProcess == NULL)
97                {
98                        if (GetLastError() != ERROR_INVALID_PARAMETER)
99                        {
100                                BOX_ERROR("Failed to open process " << pid <<
101                                        ": " <<
102                                        GetErrorMessage(GetLastError()));
103                        }
104                        return false;
105                }
106
107                DWORD exitCode;
108                BOOL result = GetExitCodeProcess(hProcess, &exitCode);
109                CloseHandle(hProcess);
110
111                if (result == 0)
112                {
113                        BOX_ERROR("Failed to get exit code for process " <<
114                                pid << ": " <<
115                                GetErrorMessage(GetLastError()))
116                        return false;
117                }
118
119                if (exitCode == STILL_ACTIVE)
120                {
121                        return true;
122                }
123               
124                return false;
125
126        #else // !WIN32
127
128                if(pid == 0) return false;
129                return ::kill(pid, 0) != -1;
130
131        #endif // WIN32
132}
133
134int ReadPidFile(const char *pidFile)
135{
136        if(!TestFileNotEmpty(pidFile))
137        {
138                TEST_FAIL_WITH_MESSAGE("Server didn't save PID file "
139                        "(perhaps one was already running?)"); 
140                return -1;
141        }
142       
143        int pid = -1;
144
145        FILE *f = fopen(pidFile, "r");
146        if(f == NULL || fscanf(f, "%d", &pid) != 1)
147        {
148                TEST_FAIL_WITH_MESSAGE("Couldn't read PID file");       
149                return -1;
150        }
151        fclose(f);
152       
153        return pid;
154}
155
156int LaunchServer(const std::string& rCommandLine, const char *pidFile)
157{
158        ::fprintf(stdout, "Starting server: %s\n", rCommandLine.c_str());
159
160#ifdef WIN32
161
162        PROCESS_INFORMATION procInfo;
163
164        STARTUPINFO startInfo;
165        startInfo.cb = sizeof(startInfo);
166        startInfo.lpReserved = NULL;
167        startInfo.lpDesktop  = NULL;
168        startInfo.lpTitle    = NULL;
169        startInfo.dwFlags = 0;
170        startInfo.cbReserved2 = 0;
171        startInfo.lpReserved2 = NULL;
172
173        std::string cmd = ConvertPaths(rCommandLine);
174        CHAR* tempCmd = strdup(cmd.c_str());
175
176        DWORD result = CreateProcess
177        (
178                NULL,        // lpApplicationName, naughty!
179                tempCmd,     // lpCommandLine
180                NULL,        // lpProcessAttributes
181                NULL,        // lpThreadAttributes
182                false,       // bInheritHandles
183                0,           // dwCreationFlags
184                NULL,        // lpEnvironment
185                NULL,        // lpCurrentDirectory
186                &startInfo,  // lpStartupInfo
187                &procInfo    // lpProcessInformation
188        );
189
190        free(tempCmd);
191
192        if (result == 0)
193        {
194                DWORD err = GetLastError();
195                printf("Launch failed: %s: error %d\n", rCommandLine.c_str(),
196                        (int)err);
197                TEST_FAIL_WITH_MESSAGE("Couldn't start server");
198                return -1;
199        }
200
201        CloseHandle(procInfo.hProcess);
202        CloseHandle(procInfo.hThread);
203
204        return WaitForServerStartup(pidFile, (int)procInfo.dwProcessId);
205
206#else // !WIN32
207
208        if(RunCommand(rCommandLine) != 0)
209        {
210                TEST_FAIL_WITH_MESSAGE("Couldn't start server");
211                return -1;
212        }
213
214        return WaitForServerStartup(pidFile, 0);
215
216#endif // WIN32
217}
218
219int WaitForServerStartup(const char *pidFile, int pidIfKnown)
220{
221        #ifdef WIN32
222        if (pidFile == NULL)
223        {
224                return pidIfKnown;
225        }
226        #else
227        // on other platforms there is no other way to get
228        // the PID, so a NULL pidFile doesn't make sense.
229        ASSERT(pidFile != NULL);
230        #endif
231
232        // time for it to start up
233        if (Logging::GetGlobalLevel() >= Log::TRACE)
234        {
235                BOX_TRACE("Waiting for server to start");
236        }
237        else
238        {
239                ::fprintf(stdout, "Waiting for server to start: ");
240        }
241
242        for (int i = 0; i < 15; i++)
243        {
244                if (TestFileNotEmpty(pidFile)) 
245                {
246                        break;
247                }
248
249                if (pidIfKnown && !ServerIsAlive(pidIfKnown))
250                {
251                        break;
252                }
253
254                if (Logging::GetGlobalLevel() < Log::TRACE)
255                {
256                        ::fprintf(stdout, ".");
257                        ::fflush(stdout);
258                }
259
260                ::sleep(1);
261        }
262
263        // on Win32 we can check whether the process is alive
264        // without even checking the PID file
265
266        if (pidIfKnown && !ServerIsAlive(pidIfKnown))
267        {
268                if (Logging::GetGlobalLevel() >= Log::TRACE)
269                {
270                        BOX_ERROR("server died!");
271                }
272                else
273                {
274                        ::fprintf(stdout, " server died!\n");
275                }
276
277                TEST_FAIL_WITH_MESSAGE("Server died!"); 
278                return -1;
279        }
280
281        if (!TestFileNotEmpty(pidFile))
282        {
283                if (Logging::GetGlobalLevel() >= Log::TRACE)
284                {
285                        BOX_ERROR("timed out!");
286                }
287                else
288                {
289                        ::fprintf(stdout, " timed out!\n");
290                }
291
292                TEST_FAIL_WITH_MESSAGE("Server didn't save PID file"); 
293                return -1;
294        }
295
296        if (Logging::GetGlobalLevel() >= Log::TRACE)
297        {
298                BOX_TRACE("Server started");
299        }
300        else
301        {
302                ::fprintf(stdout, " done.\n");
303        }
304
305        // wait a second for the pid to be written to the file
306        ::sleep(1);
307
308        // read pid file
309        int pid = ReadPidFile(pidFile);
310
311        // On Win32 we can check whether the PID in the pidFile matches
312        // the one returned by the system, which it always should.
313
314        if (pidIfKnown && pid != pidIfKnown)
315        {
316                BOX_ERROR("Server wrote wrong pid to file (" << pidFile <<
317                        "): expected " << pidIfKnown << " but found " <<
318                        pid);
319                TEST_FAIL_WITH_MESSAGE("Server wrote wrong pid to file");       
320                return -1;
321        }
322
323        return pid;
324}
325
326void TestRemoteProcessMemLeaksFunc(const char *filename,
327        const char* file, int line)
328{
329#ifdef BOX_MEMORY_LEAK_TESTING
330        // Does the file exist?
331        if(!TestFileExists(filename))
332        {
333                if (failures == 0)
334                {
335                        first_fail_file = file;
336                        first_fail_line = line;
337                }
338                ++failures;
339                printf("FAILURE: MemLeak report not available (file %s) "
340                        "at %s:%d\n", filename, file, line);
341        }
342        else
343        {
344                // Is it empty?
345                if(TestGetFileSize(filename) > 0)
346                {
347                        if (failures == 0)
348                        {
349                                first_fail_file = file;
350                                first_fail_line = line;
351                        }
352                        ++failures;
353                        printf("FAILURE: Memory leaks found in other process "
354                                "(file %s) at %s:%d\n==========\n", 
355                                filename, file, line);
356                        FILE *f = fopen(filename, "r");
357                        char linebuf[512];
358                        while(::fgets(linebuf, sizeof(linebuf), f) != 0)
359                        {
360                                printf("%s", linebuf);
361                        }
362                        fclose(f);
363                        printf("==========\n");
364                }
365               
366                // Delete it
367                ::unlink(filename);
368        }
369#endif
370}
371
372void force_sync()
373{
374        TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
375                "force-sync") == 0);
376        TestRemoteProcessMemLeaks("bbackupctl.memleaks");
377}
378
379void wait_for_sync_start()
380{
381        TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
382                "wait-for-sync") == 0);
383        TestRemoteProcessMemLeaks("bbackupctl.memleaks");
384}
385
386void wait_for_sync_end()
387{
388        TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
389                "wait-for-end") == 0);
390        TestRemoteProcessMemLeaks("bbackupctl.memleaks");
391}
392
393void sync_and_wait()
394{
395        TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
396                "sync-and-wait") == 0);
397        TestRemoteProcessMemLeaks("bbackupctl.memleaks");
398}
399
400void terminate_bbackupd(int pid)
401{
402        TEST_THAT(::system(BBACKUPCTL " -q -c testfiles/bbackupd.conf "
403                "terminate") == 0);
404        TestRemoteProcessMemLeaks("bbackupctl.memleaks");
405
406        for (int i = 0; i < 20; i++)
407        {
408                if (!ServerIsAlive(pid)) break;
409                fprintf(stdout, ".");
410                fflush(stdout);
411                sleep(1);
412        }
413
414        TEST_THAT(!ServerIsAlive(pid));
415        TestRemoteProcessMemLeaks("bbackupd.memleaks");
416}
417
418
419// Wait a given number of seconds for something to complete
420void wait_for_operation(int seconds, const char* message)
421{
422        if (Logging::GetGlobalLevel() >= Log::TRACE)
423        {
424                BOX_TRACE("Waiting " << seconds << " seconds for " << message);
425        }
426        else
427        {
428                printf("Waiting for %s: ", message);
429                fflush(stdout);
430        }
431
432        for(int l = 0; l < seconds; ++l)
433        {
434                sleep(1);
435                if (Logging::GetGlobalLevel() < Log::TRACE)
436                {
437                        printf(".");
438                        fflush(stdout);
439                }
440        }
441
442        if (Logging::GetGlobalLevel() >= Log::TRACE)
443        {
444                BOX_TRACE("Finished waiting for " << message);
445        }
446        else
447        {
448                printf(" done.\n");
449                fflush(stdout);
450        }
451}
452
453void safe_sleep(int seconds)
454{
455        ShortSleep(SecondsToBoxTime(seconds), true);
456}
457
Note: See TracBrowser for help on using the repository browser.