1#ifdef __STRICT_ANSI__
2#define _BSD_SOURCE
3#endif
4#include <time.h>
5#include <stdarg.h>
6#include <rfb/rfb.h>
7#include <rfb/rfbclient.h>
8
9#ifndef LIBVNCSERVER_HAVE_LIBPTHREAD
10#error This test need pthread support (otherwise the client blocks the client)
11#endif
12
13#define ALL_AT_ONCE
14/*#define VERY_VERBOSE*/
15
16static MUTEX(frameBufferMutex);
17
18typedef struct { int id; char* str; } encoding_t;
19static encoding_t testEncodings[]={
20        { rfbEncodingRaw, "raw" },
21	{ rfbEncodingRRE, "rre" },
22	{ rfbEncodingCoRRE, "corre" },
23	{ rfbEncodingHextile, "hextile" },
24	{ rfbEncodingUltra, "ultra" },
25#ifdef LIBVNCSERVER_HAVE_LIBZ
26	{ rfbEncodingZlib, "zlib" },
27	{ rfbEncodingZlibHex, "zlibhex" },
28	{ rfbEncodingZRLE, "zrle" },
29	{ rfbEncodingZYWRLE, "zywrle" },
30#ifdef LIBVNCSERVER_HAVE_LIBJPEG
31	{ rfbEncodingTight, "tight" },
32#endif
33#endif
34	{ 0, NULL }
35};
36
37#define NUMBER_OF_ENCODINGS_TO_TEST (sizeof(testEncodings)/sizeof(encoding_t)-1)
38/*#define NUMBER_OF_ENCODINGS_TO_TEST 1*/
39
40/* Here come the variables/functions to handle the test output */
41
42static const int width=400,height=300;
43static unsigned int statistics[2][NUMBER_OF_ENCODINGS_TO_TEST];
44static unsigned int totalFailed,totalCount;
45static unsigned int countGotUpdate;
46static MUTEX(statisticsMutex);
47
48static void initStatistics(void) {
49	memset(statistics[0],0,sizeof(int)*NUMBER_OF_ENCODINGS_TO_TEST);
50	memset(statistics[1],0,sizeof(int)*NUMBER_OF_ENCODINGS_TO_TEST);
51	totalFailed=totalCount=0;
52	INIT_MUTEX(statisticsMutex);
53}
54
55
56static void updateStatistics(int encodingIndex,rfbBool failed) {
57	LOCK(statisticsMutex);
58	if(failed) {
59		statistics[1][encodingIndex]++;
60		totalFailed++;
61	}
62	statistics[0][encodingIndex]++;
63	totalCount++;
64	countGotUpdate++;
65	UNLOCK(statisticsMutex);
66}
67
68/* Here begin the functions for the client. They will be called in a
69 * pthread. */
70
71/* maxDelta=0 means they are expected to match exactly;
72 * maxDelta>0 means that the average difference must be lower than maxDelta */
73static rfbBool doFramebuffersMatch(rfbScreenInfo* server,rfbClient* client,
74		int maxDelta)
75{
76	int i,j,k;
77	unsigned int total=0,diff=0;
78	if(server->width!=client->width || server->height!=client->height)
79		return FALSE;
80	LOCK(frameBufferMutex);
81	/* TODO: write unit test for colour transformation, use here, too */
82	for(i=0;i<server->width;i++)
83		for(j=0;j<server->height;j++)
84			for(k=0;k<3/*server->serverFormat.bitsPerPixel/8*/;k++) {
85				unsigned char s=server->frameBuffer[k+i*4+j*server->paddedWidthInBytes];
86				unsigned char cl=client->frameBuffer[k+i*4+j*client->width*4];
87
88				if(maxDelta==0 && s!=cl) {
89					UNLOCK(frameBufferMutex);
90					return FALSE;
91				} else {
92					total++;
93					diff+=(s>cl?s-cl:cl-s);
94				}
95			}
96	UNLOCK(frameBufferMutex);
97	if(maxDelta>0 && diff/total>=maxDelta)
98		return FALSE;
99	return TRUE;
100}
101
102static rfbBool resize(rfbClient* cl) {
103	if(cl->frameBuffer)
104		free(cl->frameBuffer);
105	cl->frameBuffer=malloc(cl->width*cl->height*cl->format.bitsPerPixel/8);
106	if(!cl->frameBuffer)
107		return FALSE;
108	SendFramebufferUpdateRequest(cl,0,0,cl->width,cl->height,FALSE);
109	return TRUE;
110}
111
112typedef struct clientData {
113	int encodingIndex;
114	rfbScreenInfo* server;
115	char* display;
116} clientData;
117
118static void update(rfbClient* client,int x,int y,int w,int h) {
119#ifndef VERY_VERBOSE
120
121	static const char* progress="|/-\\";
122	static int counter=0;
123
124	if(++counter>sizeof(progress)) counter=0;
125	fprintf(stderr,"%c\r",progress[counter]);
126#else
127	clientData* cd=(clientData*)client->clientData;
128	rfbClientLog("Got update (encoding=%s): (%d,%d)-(%d,%d)\n",
129			testEncodings[cd->encodingIndex].str,
130			x,y,x+w,y+h);
131#endif
132}
133
134static void update_finished(rfbClient* client) {
135	clientData* cd=(clientData*)client->clientData;
136        int maxDelta=0;
137
138#ifdef LIBVNCSERVER_HAVE_LIBZ
139	if(testEncodings[cd->encodingIndex].id==rfbEncodingZYWRLE)
140		maxDelta=5;
141#ifdef LIBVNCSERVER_HAVE_LIBJPEG
142	if(testEncodings[cd->encodingIndex].id==rfbEncodingTight)
143		maxDelta=5;
144#endif
145#endif
146	updateStatistics(cd->encodingIndex,
147			!doFramebuffersMatch(cd->server,client,maxDelta));
148}
149
150
151static void* clientLoop(void* data) {
152	rfbClient* client=(rfbClient*)data;
153	clientData* cd=(clientData*)client->clientData;
154
155	client->appData.encodingsString=strdup(testEncodings[cd->encodingIndex].str);
156	client->appData.qualityLevel = 7; /* ZYWRLE fails the test with standard settings */
157
158	sleep(1);
159	rfbClientLog("Starting client (encoding %s, display %s)\n",
160			testEncodings[cd->encodingIndex].str,
161			cd->display);
162	if(!rfbInitClient(client,NULL,NULL)) {
163		rfbClientErr("Had problems starting client (encoding %s)\n",
164				testEncodings[cd->encodingIndex].str);
165		updateStatistics(cd->encodingIndex,TRUE);
166		return NULL;
167	}
168	while(1) {
169		if(WaitForMessage(client,50)>=0)
170			if(!HandleRFBServerMessage(client))
171				break;
172	}
173	free(((clientData*)client->clientData)->display);
174	free(client->clientData);
175	client->clientData = NULL;
176	if(client->frameBuffer)
177		free(client->frameBuffer);
178	rfbClientCleanup(client);
179	return NULL;
180}
181
182static pthread_t all_threads[NUMBER_OF_ENCODINGS_TO_TEST];
183static int thread_counter;
184
185static void startClient(int encodingIndex,rfbScreenInfo* server) {
186	rfbClient* client=rfbGetClient(8,3,4);
187	clientData* cd;
188
189	client->clientData=malloc(sizeof(clientData));
190	client->MallocFrameBuffer=resize;
191	client->GotFrameBufferUpdate=update;
192	client->FinishedFrameBufferUpdate=update_finished;
193
194	cd=(clientData*)client->clientData;
195	cd->encodingIndex=encodingIndex;
196	cd->server=server;
197	cd->display=(char*)malloc(6);
198	sprintf(cd->display,":%d",server->port-5900);
199
200	pthread_create(&all_threads[thread_counter++],NULL,clientLoop,(void*)client);
201}
202
203/* Here begin the server functions */
204
205static void idle(rfbScreenInfo* server)
206{
207	int c;
208	rfbBool goForward;
209
210	LOCK(statisticsMutex);
211#ifdef ALL_AT_ONCE
212	goForward=(countGotUpdate==NUMBER_OF_ENCODINGS_TO_TEST);
213#else
214	goForward=(countGotUpdate==1);
215#endif
216
217	UNLOCK(statisticsMutex);
218	if(!goForward)
219	  return;
220	countGotUpdate=0;
221
222	LOCK(frameBufferMutex);
223	{
224		int i,j;
225		int x1=(rand()%(server->width-1)),x2=(rand()%(server->width-1)),
226		y1=(rand()%(server->height-1)),y2=(rand()%(server->height-1));
227		if(x1>x2) { i=x1; x1=x2; x2=i; }
228		if(y1>y2) { i=y1; y1=y2; y2=i; }
229		x2++; y2++;
230		for(c=0;c<3;c++) {
231			for(i=x1;i<x2;i++)
232				for(j=y1;j<y2;j++)
233					server->frameBuffer[i*4+c+j*server->paddedWidthInBytes]=255*(i-x1+j-y1)/(x2-x1+y2-y1);
234		}
235		rfbMarkRectAsModified(server,x1,y1,x2,y2);
236
237#ifdef VERY_VERBOSE
238		rfbLog("Sent update (%d,%d)-(%d,%d)\n",x1,y1,x2,y2);
239#endif
240	}
241	UNLOCK(frameBufferMutex);
242}
243
244/* log function (to show what messages are from the client) */
245
246static void
247rfbTestLog(const char *format, ...)
248{
249	va_list args;
250	char buf[256];
251	time_t log_clock;
252
253	if(!rfbEnableClientLogging)
254		return;
255
256	va_start(args, format);
257
258	time(&log_clock);
259	strftime(buf, 255, "%d/%m/%Y %X (client) ", localtime(&log_clock));
260	fprintf(stderr,"%s",buf);
261
262	vfprintf(stderr, format, args);
263	fflush(stderr);
264
265	va_end(args);
266}
267
268/* the main function */
269
270int main(int argc,char** argv)
271{
272	int i,j;
273	time_t t;
274	rfbScreenInfoPtr server;
275
276	rfbClientLog=rfbTestLog;
277	rfbClientErr=rfbTestLog;
278
279	/* Initialize server */
280	server=rfbGetScreen(&argc,argv,width,height,8,3,4);
281        if(!server)
282          return 0;
283
284	server->frameBuffer=malloc(400*300*4);
285	server->cursor=NULL;
286	for(j=0;j<400*300*4;j++)
287		server->frameBuffer[j]=j;
288	rfbInitServer(server);
289	rfbProcessEvents(server,0);
290
291	initStatistics();
292
293#ifndef ALL_AT_ONCE
294	for(i=0;i<NUMBER_OF_ENCODINGS_TO_TEST;i++) {
295#else
296	/* Initialize clients */
297	for(i=0;i<NUMBER_OF_ENCODINGS_TO_TEST;i++)
298#endif
299		startClient(i,server);
300
301	t=time(NULL);
302	/* test 20 seconds */
303	while(time(NULL)-t<20) {
304
305		idle(server);
306
307		rfbProcessEvents(server,1);
308	}
309	rfbLog("%d failed, %d received\n",totalFailed,totalCount);
310#ifndef ALL_AT_ONCE
311	{
312		rfbClientPtr cl;
313		rfbClientIteratorPtr iter=rfbGetClientIterator(server);
314		while((cl=rfbClientIteratorNext(iter)))
315			rfbCloseClient(cl);
316		rfbReleaseClientIterator(iter);
317	}
318	}
319#endif
320
321	/* shut down server, disconnecting all clients */
322	rfbShutdownServer(server, TRUE);
323
324	for(i=0;i<thread_counter;i++)
325		pthread_join(all_threads[i], NULL);
326
327	free(server->frameBuffer);
328	rfbScreenCleanup(server);
329
330	rfbLog("Statistics:\n");
331	for(i=0;i<NUMBER_OF_ENCODINGS_TO_TEST;i++)
332		rfbLog("%s encoding: %d failed, %d received\n",
333				testEncodings[i].str,statistics[1][i],statistics[0][i]);
334	if(totalFailed)
335		return 1;
336	return(0);
337}
338
339
340