| #ifdef __STRICT_ANSI__ |
| #define _BSD_SOURCE |
| #endif |
| #include <time.h> |
| #include <stdarg.h> |
| #include <rfb/rfb.h> |
| #include <rfb/rfbclient.h> |
| |
| #ifndef LIBVNCSERVER_HAVE_LIBPTHREAD |
| #error This test need pthread support (otherwise the client blocks the client) |
| #endif |
| |
| #define ALL_AT_ONCE |
| /*#define VERY_VERBOSE*/ |
| |
| static MUTEX(frameBufferMutex); |
| |
| typedef struct { int id; char* str; } encoding_t; |
| static encoding_t testEncodings[]={ |
| { rfbEncodingRaw, "raw" }, |
| { rfbEncodingRRE, "rre" }, |
| { rfbEncodingCoRRE, "corre" }, |
| { rfbEncodingHextile, "hextile" }, |
| { rfbEncodingUltra, "ultra" }, |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| { rfbEncodingZlib, "zlib" }, |
| { rfbEncodingZlibHex, "zlibhex" }, |
| { rfbEncodingZRLE, "zrle" }, |
| { rfbEncodingZYWRLE, "zywrle" }, |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| { rfbEncodingTight, "tight" }, |
| #endif |
| #endif |
| { 0, NULL } |
| }; |
| |
| #define NUMBER_OF_ENCODINGS_TO_TEST (sizeof(testEncodings)/sizeof(encoding_t)-1) |
| /*#define NUMBER_OF_ENCODINGS_TO_TEST 1*/ |
| |
| /* Here come the variables/functions to handle the test output */ |
| |
| static const int width=400,height=300; |
| static unsigned int statistics[2][NUMBER_OF_ENCODINGS_TO_TEST]; |
| static unsigned int totalFailed,totalCount; |
| static unsigned int countGotUpdate; |
| static MUTEX(statisticsMutex); |
| |
| static void initStatistics(void) { |
| memset(statistics[0],0,sizeof(int)*NUMBER_OF_ENCODINGS_TO_TEST); |
| memset(statistics[1],0,sizeof(int)*NUMBER_OF_ENCODINGS_TO_TEST); |
| totalFailed=totalCount=0; |
| INIT_MUTEX(statisticsMutex); |
| } |
| |
| |
| static void updateStatistics(int encodingIndex,rfbBool failed) { |
| LOCK(statisticsMutex); |
| if(failed) { |
| statistics[1][encodingIndex]++; |
| totalFailed++; |
| } |
| statistics[0][encodingIndex]++; |
| totalCount++; |
| countGotUpdate++; |
| UNLOCK(statisticsMutex); |
| } |
| |
| /* Here begin the functions for the client. They will be called in a |
| * pthread. */ |
| |
| /* maxDelta=0 means they are expected to match exactly; |
| * maxDelta>0 means that the average difference must be lower than maxDelta */ |
| static rfbBool doFramebuffersMatch(rfbScreenInfo* server,rfbClient* client, |
| int maxDelta) |
| { |
| int i,j,k; |
| unsigned int total=0,diff=0; |
| if(server->width!=client->width || server->height!=client->height) |
| return FALSE; |
| LOCK(frameBufferMutex); |
| /* TODO: write unit test for colour transformation, use here, too */ |
| for(i=0;i<server->width;i++) |
| for(j=0;j<server->height;j++) |
| for(k=0;k<3/*server->serverFormat.bitsPerPixel/8*/;k++) { |
| unsigned char s=server->frameBuffer[k+i*4+j*server->paddedWidthInBytes]; |
| unsigned char cl=client->frameBuffer[k+i*4+j*client->width*4]; |
| |
| if(maxDelta==0 && s!=cl) { |
| UNLOCK(frameBufferMutex); |
| return FALSE; |
| } else { |
| total++; |
| diff+=(s>cl?s-cl:cl-s); |
| } |
| } |
| UNLOCK(frameBufferMutex); |
| if(maxDelta>0 && diff/total>=maxDelta) |
| return FALSE; |
| return TRUE; |
| } |
| |
| static rfbBool resize(rfbClient* cl) { |
| if(cl->frameBuffer) |
| free(cl->frameBuffer); |
| cl->frameBuffer=malloc(cl->width*cl->height*cl->format.bitsPerPixel/8); |
| if(!cl->frameBuffer) |
| return FALSE; |
| SendFramebufferUpdateRequest(cl,0,0,cl->width,cl->height,FALSE); |
| return TRUE; |
| } |
| |
| typedef struct clientData { |
| int encodingIndex; |
| rfbScreenInfo* server; |
| char* display; |
| } clientData; |
| |
| static void update(rfbClient* client,int x,int y,int w,int h) { |
| #ifndef VERY_VERBOSE |
| |
| static const char* progress="|/-\\"; |
| static int counter=0; |
| |
| if(++counter>sizeof(progress)) counter=0; |
| fprintf(stderr,"%c\r",progress[counter]); |
| #else |
| clientData* cd=(clientData*)client->clientData; |
| rfbClientLog("Got update (encoding=%s): (%d,%d)-(%d,%d)\n", |
| testEncodings[cd->encodingIndex].str, |
| x,y,x+w,y+h); |
| #endif |
| } |
| |
| static void update_finished(rfbClient* client) { |
| clientData* cd=(clientData*)client->clientData; |
| int maxDelta=0; |
| |
| #ifdef LIBVNCSERVER_HAVE_LIBZ |
| if(testEncodings[cd->encodingIndex].id==rfbEncodingZYWRLE) |
| maxDelta=5; |
| #ifdef LIBVNCSERVER_HAVE_LIBJPEG |
| if(testEncodings[cd->encodingIndex].id==rfbEncodingTight) |
| maxDelta=5; |
| #endif |
| #endif |
| updateStatistics(cd->encodingIndex, |
| !doFramebuffersMatch(cd->server,client,maxDelta)); |
| } |
| |
| |
| static void* clientLoop(void* data) { |
| rfbClient* client=(rfbClient*)data; |
| clientData* cd=(clientData*)client->clientData; |
| |
| client->appData.encodingsString=strdup(testEncodings[cd->encodingIndex].str); |
| client->appData.qualityLevel = 7; /* ZYWRLE fails the test with standard settings */ |
| |
| sleep(1); |
| rfbClientLog("Starting client (encoding %s, display %s)\n", |
| testEncodings[cd->encodingIndex].str, |
| cd->display); |
| if(!rfbInitClient(client,NULL,NULL)) { |
| rfbClientErr("Had problems starting client (encoding %s)\n", |
| testEncodings[cd->encodingIndex].str); |
| updateStatistics(cd->encodingIndex,TRUE); |
| return NULL; |
| } |
| while(1) { |
| if(WaitForMessage(client,50)>=0) |
| if(!HandleRFBServerMessage(client)) |
| break; |
| } |
| free(((clientData*)client->clientData)->display); |
| free(client->clientData); |
| client->clientData = NULL; |
| if(client->frameBuffer) |
| free(client->frameBuffer); |
| rfbClientCleanup(client); |
| return NULL; |
| } |
| |
| static pthread_t all_threads[NUMBER_OF_ENCODINGS_TO_TEST]; |
| static int thread_counter; |
| |
| static void startClient(int encodingIndex,rfbScreenInfo* server) { |
| rfbClient* client=rfbGetClient(8,3,4); |
| clientData* cd; |
| |
| client->clientData=malloc(sizeof(clientData)); |
| client->MallocFrameBuffer=resize; |
| client->GotFrameBufferUpdate=update; |
| client->FinishedFrameBufferUpdate=update_finished; |
| |
| cd=(clientData*)client->clientData; |
| cd->encodingIndex=encodingIndex; |
| cd->server=server; |
| cd->display=(char*)malloc(6); |
| sprintf(cd->display,":%d",server->port-5900); |
| |
| pthread_create(&all_threads[thread_counter++],NULL,clientLoop,(void*)client); |
| } |
| |
| /* Here begin the server functions */ |
| |
| static void idle(rfbScreenInfo* server) |
| { |
| int c; |
| rfbBool goForward; |
| |
| LOCK(statisticsMutex); |
| #ifdef ALL_AT_ONCE |
| goForward=(countGotUpdate==NUMBER_OF_ENCODINGS_TO_TEST); |
| #else |
| goForward=(countGotUpdate==1); |
| #endif |
| |
| UNLOCK(statisticsMutex); |
| if(!goForward) |
| return; |
| countGotUpdate=0; |
| |
| LOCK(frameBufferMutex); |
| { |
| int i,j; |
| int x1=(rand()%(server->width-1)),x2=(rand()%(server->width-1)), |
| y1=(rand()%(server->height-1)),y2=(rand()%(server->height-1)); |
| if(x1>x2) { i=x1; x1=x2; x2=i; } |
| if(y1>y2) { i=y1; y1=y2; y2=i; } |
| x2++; y2++; |
| for(c=0;c<3;c++) { |
| for(i=x1;i<x2;i++) |
| for(j=y1;j<y2;j++) |
| server->frameBuffer[i*4+c+j*server->paddedWidthInBytes]=255*(i-x1+j-y1)/(x2-x1+y2-y1); |
| } |
| rfbMarkRectAsModified(server,x1,y1,x2,y2); |
| |
| #ifdef VERY_VERBOSE |
| rfbLog("Sent update (%d,%d)-(%d,%d)\n",x1,y1,x2,y2); |
| #endif |
| } |
| UNLOCK(frameBufferMutex); |
| } |
| |
| /* log function (to show what messages are from the client) */ |
| |
| static void |
| rfbTestLog(const char *format, ...) |
| { |
| va_list args; |
| char buf[256]; |
| time_t log_clock; |
| |
| if(!rfbEnableClientLogging) |
| return; |
| |
| va_start(args, format); |
| |
| time(&log_clock); |
| strftime(buf, 255, "%d/%m/%Y %X (client) ", localtime(&log_clock)); |
| fprintf(stderr,"%s",buf); |
| |
| vfprintf(stderr, format, args); |
| fflush(stderr); |
| |
| va_end(args); |
| } |
| |
| /* the main function */ |
| |
| int main(int argc,char** argv) |
| { |
| int i,j; |
| time_t t; |
| rfbScreenInfoPtr server; |
| |
| rfbClientLog=rfbTestLog; |
| rfbClientErr=rfbTestLog; |
| |
| /* Initialize server */ |
| server=rfbGetScreen(&argc,argv,width,height,8,3,4); |
| if(!server) |
| return 0; |
| |
| server->frameBuffer=malloc(400*300*4); |
| server->cursor=NULL; |
| for(j=0;j<400*300*4;j++) |
| server->frameBuffer[j]=j; |
| rfbInitServer(server); |
| rfbProcessEvents(server,0); |
| |
| initStatistics(); |
| |
| #ifndef ALL_AT_ONCE |
| for(i=0;i<NUMBER_OF_ENCODINGS_TO_TEST;i++) { |
| #else |
| /* Initialize clients */ |
| for(i=0;i<NUMBER_OF_ENCODINGS_TO_TEST;i++) |
| #endif |
| startClient(i,server); |
| |
| t=time(NULL); |
| /* test 20 seconds */ |
| while(time(NULL)-t<20) { |
| |
| idle(server); |
| |
| rfbProcessEvents(server,1); |
| } |
| rfbLog("%d failed, %d received\n",totalFailed,totalCount); |
| #ifndef ALL_AT_ONCE |
| { |
| rfbClientPtr cl; |
| rfbClientIteratorPtr iter=rfbGetClientIterator(server); |
| while((cl=rfbClientIteratorNext(iter))) |
| rfbCloseClient(cl); |
| rfbReleaseClientIterator(iter); |
| } |
| } |
| #endif |
| |
| /* shut down server, disconnecting all clients */ |
| rfbShutdownServer(server, TRUE); |
| |
| for(i=0;i<thread_counter;i++) |
| pthread_join(all_threads[i], NULL); |
| |
| free(server->frameBuffer); |
| rfbScreenCleanup(server); |
| |
| rfbLog("Statistics:\n"); |
| for(i=0;i<NUMBER_OF_ENCODINGS_TO_TEST;i++) |
| rfbLog("%s encoding: %d failed, %d received\n", |
| testEncodings[i].str,statistics[1][i],statistics[0][i]); |
| if(totalFailed) |
| return 1; |
| return(0); |
| } |
| |
| |