1 #ifndef __cxxtest__Win32Gui_h__ 2 #define __cxxtest__Win32Gui_h__ 3 4 // 5 // The Win32Gui displays a simple progress bar using the Win32 API. 6 // 7 // It accepts the following command line options: 8 // -minimized Start minimized, pop up on error 9 // -keep Don't close the window at the end 10 // -title TITLE Set the window caption 11 // 12 // If both -minimized and -keep are specified, GUI will only keep the 13 // window if it's in focus. 14 // 15 // N.B. If you're wondering why this class doesn't use any standard 16 // library or STL (<string> would have been nice) it's because it only 17 // uses "straight" Win32 API. 18 // 19 20 #include <cxxtest/Gui.h> 21 22 #include <windows.h> 23 #include <commctrl.h> 24 25 namespace CxxTest 26 { 27 class Win32Gui : public GuiListener 28 { 29 public: enterGui(int & argc,char ** argv)30 void enterGui( int &argc, char **argv ) 31 { 32 parseCommandLine( argc, argv ); 33 } 34 enterWorld(const WorldDescription & wd)35 void enterWorld( const WorldDescription &wd ) 36 { 37 getTotalTests( wd ); 38 _testsDone = 0; 39 startGuiThread(); 40 } 41 guiEnterSuite(const char * suiteName)42 void guiEnterSuite( const char *suiteName ) 43 { 44 showSuiteName( suiteName ); 45 reset( _suiteStart ); 46 } 47 guiEnterTest(const char * suiteName,const char * testName)48 void guiEnterTest( const char *suiteName, const char *testName ) 49 { 50 ++ _testsDone; 51 setTestCaption( suiteName, testName ); 52 showTestName( testName ); 53 showTestsDone(); 54 progressBarMessage( PBM_STEPIT ); 55 reset( _testStart ); 56 } 57 yellowBar()58 void yellowBar() 59 { 60 setColor( 255, 255, 0 ); 61 setIcon( IDI_WARNING ); 62 getTotalTests(); 63 } 64 redBar()65 void redBar() 66 { 67 if ( _startMinimized ) 68 showMainWindow( SW_SHOWNORMAL ); 69 setColor( 255, 0, 0 ); 70 setIcon( IDI_ERROR ); 71 getTotalTests(); 72 } 73 leaveGui()74 void leaveGui() 75 { 76 if ( keep() ) 77 { 78 showSummary(); 79 WaitForSingleObject( _gui, INFINITE ); 80 } 81 DestroyWindow( _mainWindow ); 82 } 83 84 private: 85 const char *_title; 86 bool _startMinimized, _keep; 87 HANDLE _gui; 88 WNDCLASSEX _windowClass; 89 HWND _mainWindow, _progressBar, _statusBar; 90 HANDLE _canStartTests; 91 unsigned _numTotalTests, _testsDone; 92 char _strTotalTests[WorldDescription::MAX_STRLEN_TOTAL_TESTS]; 93 enum { 94 STATUS_SUITE_NAME, STATUS_SUITE_TIME, 95 STATUS_TEST_NAME, STATUS_TEST_TIME, 96 STATUS_TESTS_DONE, STATUS_WORLD_TIME, 97 STATUS_TOTAL_PARTS 98 }; 99 int _statusWidths[STATUS_TOTAL_PARTS]; 100 unsigned _statusOffsets[STATUS_TOTAL_PARTS]; 101 unsigned _statusTotal; 102 char _statusTestsDone[sizeof("1000000000 of (100%)") + WorldDescription::MAX_STRLEN_TOTAL_TESTS]; 103 DWORD _worldStart, _suiteStart, _testStart; 104 char _timeString[sizeof("00:00:00")]; 105 parseCommandLine(int argc,char ** argv)106 void parseCommandLine( int argc, char **argv ) 107 { 108 _startMinimized = _keep = false; 109 _title = argv[0]; 110 111 for ( int i = 1; i < argc; ++ i ) 112 { 113 if ( !lstrcmpA( argv[i], "-minimized" ) ) 114 _startMinimized = true; 115 else if ( !lstrcmpA( argv[i], "-keep" ) ) 116 _keep = true; 117 else if ( !lstrcmpA( argv[i], "-title" ) && (i + 1 < argc) ) 118 _title = argv[++i]; 119 } 120 } 121 getTotalTests()122 void getTotalTests() 123 { 124 getTotalTests( tracker().world() ); 125 } 126 getTotalTests(const WorldDescription & wd)127 void getTotalTests( const WorldDescription &wd ) 128 { 129 _numTotalTests = wd.numTotalTests(); 130 wd.strTotalTests( _strTotalTests ); 131 } 132 startGuiThread()133 void startGuiThread() 134 { 135 _canStartTests = CreateEvent( NULL, TRUE, FALSE, NULL ); 136 DWORD threadId; 137 _gui = CreateThread( NULL, 0, &(Win32Gui::guiThread), (LPVOID)this, 0, &threadId ); 138 WaitForSingleObject( _canStartTests, INFINITE ); 139 } 140 guiThread(LPVOID parameter)141 static DWORD WINAPI guiThread( LPVOID parameter ) 142 { 143 ((Win32Gui *)parameter)->gui(); 144 return 0; 145 } 146 gui()147 void gui() 148 { 149 registerWindowClass(); 150 createMainWindow(); 151 initCommonControls(); 152 createProgressBar(); 153 createStatusBar(); 154 centerMainWindow(); 155 showMainWindow(); 156 startTimer(); 157 startTests(); 158 159 messageLoop(); 160 } 161 registerWindowClass()162 void registerWindowClass() 163 { 164 _windowClass.cbSize = sizeof(_windowClass); 165 _windowClass.style = CS_HREDRAW | CS_VREDRAW; 166 _windowClass.lpfnWndProc = &(Win32Gui::windowProcedure); 167 _windowClass.cbClsExtra = 0; 168 _windowClass.cbWndExtra = sizeof(LONG); 169 _windowClass.hInstance = (HINSTANCE)NULL; 170 _windowClass.hIcon = (HICON)NULL; 171 _windowClass.hCursor = (HCURSOR)NULL; 172 _windowClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); 173 _windowClass.lpszMenuName = NULL; 174 _windowClass.lpszClassName = TEXT("CxxTest Window Class"); 175 _windowClass.hIconSm = (HICON)NULL; 176 177 RegisterClassEx( &_windowClass ); 178 } 179 createMainWindow()180 void createMainWindow() 181 { 182 _mainWindow = createWindow( _windowClass.lpszClassName, WS_OVERLAPPEDWINDOW ); 183 } 184 initCommonControls()185 void initCommonControls() 186 { 187 HMODULE dll = LoadLibraryA( "comctl32.dll" ); 188 if ( !dll ) 189 return; 190 191 typedef void (WINAPI *FUNC)( void ); 192 FUNC func = (FUNC)GetProcAddress( dll, "InitCommonControls" ); 193 if ( !func ) 194 return; 195 196 func(); 197 } 198 createProgressBar()199 void createProgressBar() 200 { 201 _progressBar = createWindow( PROGRESS_CLASS, WS_CHILD | WS_VISIBLE | PBS_SMOOTH, _mainWindow ); 202 203 #ifdef PBM_SETRANGE32 204 progressBarMessage( PBM_SETRANGE32, 0, _numTotalTests ); 205 #else // No PBM_SETRANGE32, use PBM_SETRANGE 206 progressBarMessage( PBM_SETRANGE, 0, MAKELPARAM( 0, (WORD)_numTotalTests ) ); 207 #endif // PBM_SETRANGE32 208 progressBarMessage( PBM_SETPOS, 0 ); 209 progressBarMessage( PBM_SETSTEP, 1 ); 210 greenBar(); 211 UpdateWindow( _progressBar ); 212 } 213 createStatusBar()214 void createStatusBar() 215 { 216 _statusBar = createWindow( STATUSCLASSNAME, WS_CHILD | WS_VISIBLE, _mainWindow ); 217 setRatios( 4, 1, 3, 1, 3, 1 ); 218 } 219 setRatios(unsigned suiteNameRatio,unsigned suiteTimeRatio,unsigned testNameRatio,unsigned testTimeRatio,unsigned testsDoneRatio,unsigned worldTimeRatio)220 void setRatios( unsigned suiteNameRatio, unsigned suiteTimeRatio, 221 unsigned testNameRatio, unsigned testTimeRatio, 222 unsigned testsDoneRatio, unsigned worldTimeRatio ) 223 { 224 _statusTotal = 0; 225 _statusOffsets[STATUS_SUITE_NAME] = (_statusTotal += suiteNameRatio); 226 _statusOffsets[STATUS_SUITE_TIME] = (_statusTotal += suiteTimeRatio); 227 _statusOffsets[STATUS_TEST_NAME] = (_statusTotal += testNameRatio); 228 _statusOffsets[STATUS_TEST_TIME] = (_statusTotal += testTimeRatio); 229 _statusOffsets[STATUS_TESTS_DONE] = (_statusTotal += testsDoneRatio); 230 _statusOffsets[STATUS_WORLD_TIME] = (_statusTotal += worldTimeRatio); 231 } 232 233 HWND createWindow( LPCTSTR className, DWORD style, HWND parent = (HWND)NULL ) 234 { 235 return CreateWindow( className, NULL, style, 0, 0, 0, 0, parent, 236 (HMENU)NULL, (HINSTANCE)NULL, (LPVOID)this ); 237 } 238 239 void progressBarMessage( UINT message, WPARAM wParam = 0, LPARAM lParam = 0 ) 240 { 241 SendMessage( _progressBar, message, wParam, lParam ); 242 } 243 centerMainWindow()244 void centerMainWindow() 245 { 246 RECT screen; 247 getScreenArea( screen ); 248 249 LONG screenWidth = screen.right - screen.left; 250 LONG screenHeight = screen.bottom - screen.top; 251 252 LONG xCenter = (screen.right + screen.left) / 2; 253 LONG yCenter = (screen.bottom + screen.top) / 2; 254 255 LONG windowWidth = (screenWidth * 4) / 5; 256 LONG windowHeight = screenHeight / 10; 257 LONG minimumHeight = 2 * (GetSystemMetrics( SM_CYCAPTION ) + GetSystemMetrics( SM_CYFRAME )); 258 if ( windowHeight < minimumHeight ) 259 windowHeight = minimumHeight; 260 261 SetWindowPos( _mainWindow, HWND_TOP, 262 xCenter - (windowWidth / 2), yCenter - (windowHeight / 2), 263 windowWidth, windowHeight, 0 ); 264 } 265 getScreenArea(RECT & area)266 void getScreenArea( RECT &area ) 267 { 268 if ( !getScreenAreaWithoutTaskbar( area ) ) 269 getWholeScreenArea( area ); 270 } 271 getScreenAreaWithoutTaskbar(RECT & area)272 bool getScreenAreaWithoutTaskbar( RECT &area ) 273 { 274 return (SystemParametersInfo( SPI_GETWORKAREA, sizeof(RECT), &area, 0 ) != 0); 275 } 276 getWholeScreenArea(RECT & area)277 void getWholeScreenArea( RECT &area ) 278 { 279 area.left = area.top = 0; 280 area.right = GetSystemMetrics( SM_CXSCREEN ); 281 area.bottom = GetSystemMetrics( SM_CYSCREEN ); 282 } 283 showMainWindow()284 void showMainWindow() 285 { 286 showMainWindow( _startMinimized ? SW_MINIMIZE : SW_SHOWNORMAL ); 287 UpdateWindow( _mainWindow ); 288 } 289 showMainWindow(int mode)290 void showMainWindow( int mode ) 291 { 292 ShowWindow( _mainWindow, mode ); 293 } 294 295 enum { TIMER_ID = 1, TIMER_DELAY = 1000 }; 296 startTimer()297 void startTimer() 298 { 299 reset( _worldStart ); 300 reset( _suiteStart ); 301 reset( _testStart ); 302 SetTimer( _mainWindow, TIMER_ID, TIMER_DELAY, 0 ); 303 } 304 reset(DWORD & tick)305 void reset( DWORD &tick ) 306 { 307 tick = GetTickCount(); 308 } 309 startTests()310 void startTests() 311 { 312 SetEvent( _canStartTests ); 313 } 314 messageLoop()315 void messageLoop() 316 { 317 MSG message; 318 while ( BOOL haveMessage = GetMessage( &message, NULL, 0, 0 ) ) 319 if ( haveMessage != -1 ) 320 DispatchMessage( &message ); 321 } 322 windowProcedure(HWND window,UINT message,WPARAM wParam,LPARAM lParam)323 static LRESULT CALLBACK windowProcedure( HWND window, UINT message, WPARAM wParam, LPARAM lParam ) 324 { 325 if ( message == WM_CREATE ) 326 setUp( window, (LPCREATESTRUCT)lParam ); 327 328 Win32Gui *that = (Win32Gui *)GetWindowLong( window, GWL_USERDATA ); 329 return that->handle( window, message, wParam, lParam ); 330 } 331 setUp(HWND window,LPCREATESTRUCT create)332 static void setUp( HWND window, LPCREATESTRUCT create ) 333 { 334 SetWindowLong( window, GWL_USERDATA, (LONG)create->lpCreateParams ); 335 } 336 handle(HWND window,UINT message,WPARAM wParam,LPARAM lParam)337 LRESULT handle( HWND window, UINT message, WPARAM wParam, LPARAM lParam ) 338 { 339 switch ( message ) 340 { 341 case WM_SIZE: resizeControls(); break; 342 343 case WM_TIMER: updateTime(); break; 344 345 case WM_CLOSE: 346 case WM_DESTROY: 347 case WM_QUIT: 348 ExitProcess( 0 ); 349 350 default: return DefWindowProc( window, message, wParam, lParam ); 351 } 352 return 0; 353 } 354 resizeControls()355 void resizeControls() 356 { 357 RECT r; 358 GetClientRect( _mainWindow, &r ); 359 LONG width = r.right - r.left; 360 LONG height = r.bottom - r.top; 361 362 GetClientRect( _statusBar, &r ); 363 LONG statusHeight = r.bottom - r.top; 364 LONG resizeGripWidth = statusHeight; 365 LONG progressHeight = height - statusHeight; 366 367 SetWindowPos( _progressBar, HWND_TOP, 0, 0, width, progressHeight, 0 ); 368 SetWindowPos( _statusBar, HWND_TOP, 0, progressHeight, width, statusHeight, 0 ); 369 setStatusParts( width - resizeGripWidth ); 370 } 371 setStatusParts(LONG width)372 void setStatusParts( LONG width ) 373 { 374 for ( unsigned i = 0; i < STATUS_TOTAL_PARTS; ++ i ) 375 _statusWidths[i] = (width * _statusOffsets[i]) / _statusTotal; 376 377 statusBarMessage( SB_SETPARTS, STATUS_TOTAL_PARTS, _statusWidths ); 378 } 379 380 void statusBarMessage( UINT message, WPARAM wParam = 0, const void *lParam = 0 ) 381 { 382 SendMessage( _statusBar, message, wParam, (LPARAM)lParam ); 383 } 384 greenBar()385 void greenBar() 386 { 387 setColor( 0, 255, 0 ); 388 setIcon( IDI_INFORMATION ); 389 } 390 391 #ifdef PBM_SETBARCOLOR setColor(BYTE red,BYTE green,BYTE blue)392 void setColor( BYTE red, BYTE green, BYTE blue ) 393 { 394 progressBarMessage( PBM_SETBARCOLOR, 0, RGB( red, green, blue ) ); 395 } 396 #else // !PBM_SETBARCOLOR setColor(BYTE,BYTE,BYTE)397 void setColor( BYTE, BYTE, BYTE ) 398 { 399 } 400 #endif // PBM_SETBARCOLOR 401 setIcon(LPCTSTR icon)402 void setIcon( LPCTSTR icon ) 403 { 404 SendMessage( _mainWindow, WM_SETICON, ICON_BIG, (LPARAM)loadStandardIcon( icon ) ); 405 } 406 loadStandardIcon(LPCTSTR icon)407 HICON loadStandardIcon( LPCTSTR icon ) 408 { 409 return LoadIcon( (HINSTANCE)NULL, icon ); 410 } 411 setTestCaption(const char * suiteName,const char * testName)412 void setTestCaption( const char *suiteName, const char *testName ) 413 { 414 setCaption( suiteName, "::", testName, "()" ); 415 } 416 417 void setCaption( const char *a = "", const char *b = "", const char *c = "", const char *d = "" ) 418 { 419 unsigned length = lstrlenA( _title ) + sizeof( " - " ) + 420 lstrlenA( a ) + lstrlenA( b ) + lstrlenA( c ) + lstrlenA( d ); 421 char *name = allocate( length ); 422 lstrcpyA( name, _title ); 423 lstrcatA( name, " - " ); 424 lstrcatA( name, a ); 425 lstrcatA( name, b ); 426 lstrcatA( name, c ); 427 lstrcatA( name, d ); 428 SetWindowTextA( _mainWindow, name ); 429 deallocate( name ); 430 } 431 showSuiteName(const char * suiteName)432 void showSuiteName( const char *suiteName ) 433 { 434 setStatusPart( STATUS_SUITE_NAME, suiteName ); 435 } 436 showTestName(const char * testName)437 void showTestName( const char *testName ) 438 { 439 setStatusPart( STATUS_TEST_NAME, testName ); 440 } 441 showTestsDone()442 void showTestsDone() 443 { 444 wsprintfA( _statusTestsDone, "%u of %s (%u%%)", 445 _testsDone, _strTotalTests, 446 (_testsDone * 100) / _numTotalTests ); 447 setStatusPart( STATUS_TESTS_DONE, _statusTestsDone ); 448 } 449 updateTime()450 void updateTime() 451 { 452 setStatusTime( STATUS_WORLD_TIME, _worldStart ); 453 setStatusTime( STATUS_SUITE_TIME, _suiteStart ); 454 setStatusTime( STATUS_TEST_TIME, _testStart ); 455 } 456 setStatusTime(unsigned part,DWORD start)457 void setStatusTime( unsigned part, DWORD start ) 458 { 459 unsigned total = (GetTickCount() - start) / 1000; 460 unsigned hours = total / 3600; 461 unsigned minutes = (total / 60) % 60; 462 unsigned seconds = total % 60; 463 464 if ( hours ) 465 wsprintfA( _timeString, "%u:%02u:%02u", hours, minutes, seconds ); 466 else 467 wsprintfA( _timeString, "%02u:%02u", minutes, seconds ); 468 469 setStatusPart( part, _timeString ); 470 } 471 keep()472 bool keep() 473 { 474 if ( !_keep ) 475 return false; 476 if ( !_startMinimized ) 477 return true; 478 return (_mainWindow == GetForegroundWindow()); 479 } 480 showSummary()481 void showSummary() 482 { 483 stopTimer(); 484 setSummaryStatusBar(); 485 setSummaryCaption(); 486 } 487 setStatusPart(unsigned part,const char * text)488 void setStatusPart( unsigned part, const char *text ) 489 { 490 statusBarMessage( SB_SETTEXTA, part, text ); 491 } 492 stopTimer()493 void stopTimer() 494 { 495 KillTimer( _mainWindow, TIMER_ID ); 496 setStatusTime( STATUS_WORLD_TIME, _worldStart ); 497 } 498 setSummaryStatusBar()499 void setSummaryStatusBar() 500 { 501 setRatios( 0, 0, 0, 0, 1, 1 ); 502 resizeControls(); 503 504 const char *tests = (_numTotalTests == 1) ? "test" : "tests"; 505 if ( tracker().failedTests() ) 506 wsprintfA( _statusTestsDone, "Failed %u of %s %s", 507 tracker().failedTests(), _strTotalTests, tests ); 508 else 509 wsprintfA( _statusTestsDone, "%s %s passed", _strTotalTests, tests ); 510 511 setStatusPart( STATUS_TESTS_DONE, _statusTestsDone ); 512 } 513 setSummaryCaption()514 void setSummaryCaption() 515 { 516 setCaption( _statusTestsDone ); 517 } 518 allocate(unsigned length)519 char *allocate( unsigned length ) 520 { 521 return (char *)HeapAlloc( GetProcessHeap(), 0, length ); 522 } 523 deallocate(char * data)524 void deallocate( char *data ) 525 { 526 HeapFree( GetProcessHeap(), 0, data ); 527 } 528 }; 529 } 530 531 #endif // __cxxtest__Win32Gui_h__ 532