1/* Test/example program for the base library
2
3   Copyright (C) 2005 Free Software Foundation, Inc.
4
5  Copying and distribution of this file, with or without modification,
6  are permitted in any medium without royalty provided the copyright
7  notice and this notice are preserved.
8
9   This file is part of the GNUstep Base Library.
10*/
11#include <stdio.h>
12#include <Foundation/NSObject.h>
13#include <Foundation/NSConnection.h>
14#include <Foundation/NSPort.h>
15#include <Foundation/NSPortNameServer.h>
16#include <Foundation/NSDistantObject.h>
17#include <Foundation/NSDictionary.h>
18#include <Foundation/NSString.h>
19#include <Foundation/NSRunLoop.h>
20#include <Foundation/NSData.h>
21#include <Foundation/NSDate.h>
22#include <Foundation/NSAutoreleasePool.h>
23#include <Foundation/NSDebug.h>
24#include <Foundation/NSProcessInfo.h>
25#include <Foundation/NSException.h>
26#include <Foundation/NSUserDefaults.h>
27#include <assert.h>
28#include "server.h"
29
30#include "wgetopt.h"
31
32/*
33 * Dummy declaration with different bycopy/byref info from the one
34 * in the server ... we expect the info from the server to be used.
35 */
36@interface	Dummy : NSObject
37- (id) quietBycopy: (byref id)a;
38@end
39
40@interface	CallbackClient : NSObject <ClientProtocol>
41- (BOOL) callback;
42@end
43
44@implementation	CallbackClient
45- (BOOL) callback
46{
47  return YES;
48}
49@end
50
51
52@interface	Auth : NSObject
53@end
54
55@implementation	Auth
56- (BOOL) authenticateComponents: (NSMutableArray*)components
57		       withData: (NSData*)authData
58{
59  unsigned	count = [components count];
60
61  while (count-- > 0)
62    {
63      id	obj = [components objectAtIndex: count];
64
65      if ([obj isKindOfClass: [NSData class]] == YES)
66	{
67	  NSMutableData	*d = [obj mutableCopy];
68	  unsigned	l = [d length];
69	  char		*p = (char*)[d mutableBytes];
70
71	  while (l-- > 0)
72	    p[l] ^= 42;
73	  [components replaceObjectAtIndex: count withObject: d];
74	  RELEASE(d);
75	}
76    }
77  return YES;
78}
79@end
80
81int con_data (id prx)
82{
83  id	pool;
84  BOOL b, br;
85  unsigned char uc, ucr;
86  char c, cr;
87  short s, sr;
88  int i, ir;
89  long l, lr;
90  float flt, fltr;
91  double dbl, dblr;
92  char *str;
93  id obj;
94  small_struct ss = {12};
95  foo ffoo = {'Z', 1234.5678, 99, "cow", 9876543};
96  foo bck;
97
98  printf("Testing data sending\n");
99
100  printf("Boolean:\n");
101  b = YES;
102  printf("  sending %d", b);
103  br = [prx sendBoolean: b];
104  printf(" got %d", br);
105  if (b == !br)
106    printf(" ...ok\n");
107  else
108    printf(" *** ERROR ***\n");
109  br = b = YES;
110  printf("  sending ptr to %d", br);
111  [prx getBoolean: &br];
112  printf(" got %d", br);
113  if (b == !br)
114    printf(" ...ok\n");
115  else
116    printf(" *** ERROR ***\n");
117  printf("  error is ok (due to incorrect encoding by gcc)\n");
118
119#define TEST_CALL(test, send, got, sendp, var, varr, val, msg1, msg2)	\
120  pool = [NSAutoreleasePool new];					\
121  printf(test);								\
122  var = val;								\
123  printf(send, var);							\
124  varr = [prx msg1 var];						\
125  printf(got, varr);							\
126  if (varr != (var+ADD_CONST))					\
127    printf(" *** ERROR ***\n");						\
128  else									\
129    printf(" ...ok\n");							\
130  varr = var = val+1;							\
131  printf(sendp, varr);							\
132  [prx msg2 &varr];							\
133  printf(got, varr);							\
134  if (varr != (var+ADD_CONST))					\
135    printf(" *** ERROR ***\n");						\
136  else									\
137    printf(" ...ok\n");							\
138  [pool release];
139
140#define TEST_FCALL(test, send, got, sendp, var, varr, val, msg1, msg2)	\
141  pool = [NSAutoreleasePool new];					\
142  printf(test);								\
143  var = val;								\
144  printf(send, var);							\
145  varr = [prx msg1 var];						\
146  printf(got, varr);							\
147  if (varr - (var+ADD_CONST) > 1e-3)					\
148    printf(" *** ERROR ***\n");						\
149  else									\
150    printf(" ...ok\n");							\
151  varr = var = val+1;							\
152  printf(sendp, varr);							\
153  [prx msg2 &varr];							\
154  printf(got, varr);							\
155  if (varr - (var+ADD_CONST) > 1e-3)					\
156    printf(" *** ERROR ***\n");						\
157  else									\
158    printf(" ...ok\n");							\
159  [pool release];
160
161  TEST_CALL("UChar:\n", "  sending %d", " got %d", "  sending ptr to %d",
162	    uc, ucr, 23, sendUChar:, getUChar:)
163  printf("  error is ok (due to incorrect encoding by gcc)\n");
164
165  TEST_CALL("Char:\n", "  sending %d", " got %d", "  sending ptr to %d",
166	    c, cr, 23, sendChar:, getChar:)
167  printf("  error is ok (due to incorrect encoding by gcc)\n");
168
169  TEST_CALL("Short:\n", "  sending %hd", " got %hd", "  sending ptr to %hd",
170	    s, sr, 23, sendShort:, getShort:)
171
172  TEST_CALL("Int:\n", "  sending %d", " got %d", "  sending ptr to %d",
173	    i, ir, 23, sendInt:, getInt:)
174
175  TEST_CALL("Long:\n", "  sending %ld", " got %ld", "  sending ptr to %ld",
176	    l, lr, 23, sendLong:, getLong:)
177
178  TEST_FCALL("Float:\n", "  sending %f", " got %f", "  sending ptr to %f",
179	    flt, fltr, 23.2, sendFloat:, getFloat:)
180
181  TEST_FCALL("Double:\n", "  sending %g", " got %g", "  sending ptr to %g",
182	    dbl, dblr, 23.2, sendDouble:, getDouble:)
183
184  flt = 2.718;
185  dbl = 3.14159265358979323846264338327;
186  printf("  sending double %f, float %f\n", dbl, flt);
187  [prx sendDouble:dbl andFloat:flt];
188
189
190  pool = [NSAutoreleasePool new];
191  printf("String:\n");
192  str = "My String 1";
193  printf("  sending (%s)", str);
194  str = [prx sendString: str];
195  printf(" got (%s)\n", str);
196  [pool release];
197
198  pool = [NSAutoreleasePool new];
199  str = "My String 3";
200  printf("  sending ptr to (%s)", str);
201  [prx getString: &str];
202  printf(" got (%s)\n", str);
203  [pool release];
204
205  pool = [NSAutoreleasePool new];
206  printf("Small Struct:\n");
207  //printf("  sending %x", ss.z);
208  //ss = [prx sendSmallStruct: ss];
209  //printf(" got %x\n", ss.z);
210  printf("  sending ptr to %x", ss.z);
211  [prx getSmallStruct: &ss];
212  printf(" got %x\n", ss.z);
213  [pool release];
214
215#if 1 || !defined(__MINGW__)
216  pool = [NSAutoreleasePool new];
217  printf("Struct:\n");
218  memcpy(&bck, &ffoo, sizeof(bck));
219  printf("  sending c='%c',d=%g,i=%d,s=%s,l=%ld",
220    ffoo.c, ffoo.d, ffoo.i, ffoo.s, ffoo.l);
221  ffoo = [prx sendStruct: ffoo];
222  printf(" got c='%c',d=%g,i=%d,s=%s,l=%ld\n",
223    ffoo.c, ffoo.d, ffoo.i, ffoo.s, ffoo.l);
224  memcpy(&ffoo, &bck, sizeof(bck));
225  printf("  sending ptr to  c='%c',d=%g,i=%d,s=%s,l=%ld",
226    ffoo.c, ffoo.d, ffoo.i, ffoo.s, ffoo.l);
227  [prx getStruct: &ffoo];
228  printf(" got c='%c',d=%g,i=%d,s=%s,l=%ld\n",
229    ffoo.c, ffoo.d, ffoo.i, ffoo.s, ffoo.l);
230  [pool release];
231#endif
232
233  pool = [NSAutoreleasePool new];
234  printf("Object:\n");
235  obj = [NSObject new];
236  [prx addObject: obj];  // FIXME: Why is this needed?
237  printf("  sending %s", [[obj description] cString]);
238  obj = [prx sendObject: obj];
239  printf(" got %s\n", [[obj description] cString]);
240  printf("  sending ptr to %s", [[obj description] cString]);
241  [prx getObject: &obj];
242  printf(" got %s\n",  [[obj description] cString]);
243  [pool release];
244
245  printf("Many Arguments:\n");
246  [prx manyArgs:1 :2 :3 :4 :5 :6 :7 :8 :9 :10 :11 :12];
247
248  printf("Done\n");
249  return 0;
250}
251
252int
253con_messages (id prx)
254{
255  id obj;
256  Protocol	*pc = @protocol(ClientProtocol);
257  Protocol	*ps = @protocol(ServerProtocol);
258
259  obj = [NSObject new];
260
261  printf("Conforms to protocol (remote) should be 1: %d\n",
262    [prx conformsToProtocol: ps]);
263  printf("Conforms to protocol (remote) should be 0: %d\n",
264    [prx conformsToProtocol: pc]);
265
266  [prx setProtocolForProxy: ps];
267
268  printf("Conforms to protocol (local) should be 1: %d\n",
269    [prx conformsToProtocol: ps]);
270  printf("Conforms to protocol (local) should be 0: %d\n",
271    [prx conformsToProtocol: pc]);
272
273  printf("Oneway Void message:\n");
274  [prx shout];
275  printf("  ok\n");
276
277  printf("Testing exception in method with return value:\n");
278  NS_DURING
279    {
280      [prx exceptionTest1];
281      printf("  ERROR\n");
282    }
283  NS_HANDLER
284    {
285      printf("  ok ... %s\n", [[localException description] cString]);
286    }
287  NS_ENDHANDLER
288
289  printf("Testing exception in method with void return:\n");
290  NS_DURING
291    {
292      [prx exceptionTest2];
293      printf("  ERROR\n");
294    }
295  NS_HANDLER
296    {
297      printf("  ok ... %s\n", [[localException description] cString]);
298    }
299  NS_ENDHANDLER
300
301  printf("Testing exception in oneway void method:\n");
302  NS_DURING
303    {
304      [prx exceptionTest3];
305      printf("  ok\n");
306    }
307  NS_HANDLER
308    {
309      printf("  ERROR ... %s\n", [[localException description] cString]);
310    }
311  NS_ENDHANDLER
312
313  /* this next line doesn't actually test callbacks, it tests
314     sending the same object twice in the same message. */
315  printf("Send same object twice in message\n");
316  [prx sendObject: prx];
317  printf("  ok\n");
318
319  printf("performSelector:\n");
320  if (prx != [prx performSelector: GSSelectorFromName("self")])
321    printf("  ERROR\n");
322  else
323    printf("  ok\n");
324
325  printf("Testing bycopy/byref:\n");
326  [prx sendBycopy: obj];
327  [prx quietBycopy: obj];
328
329#ifdef	_F_BYREF
330  [prx sendByref: obj];
331  [prx sendByref: @"hello"];
332  [prx sendByref: [NSDate date]];
333  {
334    NSMutableString	*str = [NSMutableString string];
335
336    [prx modifyByref: str];
337    printf("  Modified '%s'\n", [str lossyCString]);
338  }
339#endif
340  printf("  ok\n");
341
342  printf("Done\n");
343  return 0;
344}
345
346int
347con_benchmark (id prx)
348{
349  int i;
350  NSDate	  *d = [NSDate date];
351  NSMutableData *sen = [NSMutableData data];
352  id localObj;
353  id rep;
354
355  printf("Benchmarking\n");
356  [sen setLength: 100000];
357  rep = [prx sendObject: sen];
358  printf("  Sent: 0x%p, Reply: 0x%p, Length: %d\n", sen, rep, [rep length]);
359
360  localObj = [[NSObject alloc] init];
361  [prx addObject: localObj];  // FIXME: Why is this needed?
362  for (i = 0; i < 10000; i++)
363    {
364#if 0
365      k = [prx count];
366      for (j = 0; j < k; j++)
367	{
368	  id remote_peer_obj = [prx objectAt: j];
369	}
370#endif
371      [prx echoObject: localObj];
372    }
373
374  printf("  Delay is %f\n", [d timeIntervalSinceNow]);
375  printf("Done\n");
376  return 0;
377}
378
379int
380con_statistics (id prx)
381{
382  int j;
383  id localObj, cobj, a, o;
384
385  printf("------------------------------------------------------------\n");
386  printf("Printing Statistics\n");
387  localObj = [[NSObject alloc] init];
388  [prx outputStats: localObj];
389  printf("  >>list proxy's hash is 0x%d\n", [prx hash]);
390  printf("  >>list proxy's self is 0x%p = 0x%p\n", [prx self], prx);
391  printf("  >>proxy's description is (%s)\n", [[prx description] lossyCString]);
392
393  cobj = [prx connectionForProxy];
394  o = [cobj statistics];
395  a = [o allKeys];
396
397  for (j = 0; j < [a count]; j++)
398    {
399      id k = [a objectAtIndex:j];
400      id v = [o objectForKey:k];
401
402      printf("  %s - %s\n", [k cString], [[v description] cString]);
403    }
404  printf("------------------------------------------------------------\n");
405
406  return 0;
407}
408
409int
410con_loop (id prx)
411{
412  NSAutoreleasePool *arp;
413  id cobj;
414
415  arp = [NSAutoreleasePool new];
416  [prx addObject: [NSObject new]];  // So loss of this connection is logged.
417  cobj = [prx connectionForProxy];
418  printf("%d\n", [cobj retainCount]);
419  printf("%s\n", [[[cobj statistics] description] cString]);
420  //printf("%s\n", GSDebugAllocationList(YES));
421
422  printf("loop left running idle for 30 seconds\n");
423  [[NSRunLoop currentRunLoop] runUntilDate:
424    [NSDate dateWithTimeIntervalSinceNow: 30]];
425  [cobj invalidate];
426  [arp release];
427  return 0;
428}
429
430int
431con_objects (id prx)
432{
433  int j, k;
434  id localObj;
435
436  localObj = [NSObject new];
437  [prx addObject:localObj];
438  k = [prx count];
439  for (j = 0; j < k; j++)
440    {
441      id remote_peer_obj = [prx objectAt:j];
442      printf("triangle %d object proxy's hash is 0x%x\n",
443	     j, (unsigned)[remote_peer_obj hash]);
444
445#if 0
446      /* xxx look at this again after we use release/retain everywhere */
447      if ([remote_peer_obj isProxy])
448	[remote_peer_obj release];
449#endif
450      remote_peer_obj = [prx objectAt:j];
451      printf("repeated triangle %d object proxy's hash is 0x%x\n",
452	     j, (unsigned)[remote_peer_obj hash]);
453    }
454  return 0;
455}
456
457int
458con_callback (id prx)
459{
460  int j, k;
461  id localObj;
462
463  localObj = [CallbackClient new];
464  [prx registerClient: localObj];
465  k = 1000;
466  for (j = 0; j < k; j++)
467    {
468      ENTER_POOL
469      [prx unregisterClient: localObj];
470      [prx registerClient: localObj];
471      [prx tryClientCallback];
472      if (j < 10 || j %10 == 0)
473	printf("repeated client registration and callback %d\n", j);
474      LEAVE_POOL
475    }
476  printf("repeated client registration and callback %d\n", j);
477  RELEASE(localObj);
478  return 0;
479}
480
481void
482usage(const char *program)
483{
484  printf("Usage: %s [-ds] [t|b|m|l|o] [host] [server]\n", program);
485  printf("  -d     - Debug connection\n");
486  printf("  -s     - Print Statistics\n");
487  printf("  -t     - Data type test [default]\n");
488  printf("  -b     - Benchmark test\n");
489  printf("  -m     - Messaging test\n");
490  printf("  -l     - Loop test\n");
491  printf("  -o     - Objects test\n");
492  printf("  -c     - Connect test\n");
493  printf("  -r     - Registration and callback test\n");
494}
495
496typedef enum {
497  NO_TEST, TYPE_TEST, BENCHMARK_TEST, MESSAGE_TEST,
498  LOOP_TEST, OBJECT_TEST, CONNECT_TEST, CALLBACK_TEST
499} test_t;
500
501int main (int argc, char *argv[], char **env)
502{
503  int c, debug, stats;
504  test_t type_test;
505  id cobj, prx;
506  unsigned	connect_attempts;
507  NSAutoreleasePool	*arp;
508  NSPortNameServer	*ns;
509  Auth *auth;
510#if	!defined(__MINGW__)
511  extern int optind;
512  extern char *optarg;
513#endif
514
515  [NSProcessInfo initializeWithArguments: argv count: argc environment: env];
516  arp = [NSAutoreleasePool new];
517  auth = [Auth new];
518  GSDebugAllocationActive(YES);
519
520  setvbuf(stdout, 0, _IONBF, 0);
521  debug = 0;
522  type_test = 0;
523  stats = 0;
524  while ((c = wgetopt(argc, argv, "hdtbmslocr")) != EOF)
525    switch (c)
526      {
527      case 'd':
528	debug++;
529	break;
530      case 't':
531	type_test = TYPE_TEST;
532	break;
533      case 'b':
534	type_test = BENCHMARK_TEST;
535	break;
536      case 'm':
537	type_test = MESSAGE_TEST;
538	break;
539      case 's':
540	stats = 1;
541	break;
542      case 'l':
543	type_test = LOOP_TEST;
544	break;
545      case 'o':
546	type_test = OBJECT_TEST;
547	break;
548      case 'c':
549	type_test = CONNECT_TEST;
550	break;
551      case 'r':
552	type_test = CALLBACK_TEST;
553	break;
554      case 'h':
555	usage(argv[0]);
556	exit(0);
557	break;
558      default:
559#if 0
560	usage(argv[0]);
561	exit(1);
562#endif
563	break;
564      }
565  if (type_test == NO_TEST)
566    type_test = TYPE_TEST;
567
568  if (type_test == CONNECT_TEST)
569    connect_attempts = 100000;
570  else
571    connect_attempts = 1;
572
573  if (debug > 0)
574    {
575      [NSConnection setDebug: debug];
576      [NSDistantObject setDebug: debug];
577      [NSObject enableDoubleReleaseCheck: YES];
578    }
579
580  ns = [NSSocketPortNameServer sharedInstance];
581  while (connect_attempts-- > 0)
582    {
583      if (optind < argc)
584	{
585	  if (optind+1 < argc)
586	    prx = [NSConnection rootProxyForConnectionWithRegisteredName:
587				  [NSString stringWithCString: argv[optind+1]]
588			    host: [NSString stringWithCString:argv[optind]]
589		 usingNameServer: ns];
590	  else
591	    prx = [NSConnection rootProxyForConnectionWithRegisteredName:
592				 @"test2server"
593			    host:[NSString stringWithCString:argv[optind]]
594		 usingNameServer: ns];
595	}
596      else
597	prx = [NSConnection rootProxyForConnectionWithRegisteredName:
598		@"test2server" host: @""
599		    usingNameServer: ns];
600      if (prx == nil)
601	{
602	  printf("ERROR: Failed to connect to server\n");
603	  return -1;
604	}
605      if (type_test == CONNECT_TEST)
606	{
607	  NSLog(@"Made connection\n");
608	  if (connect_attempts > 0)
609	    {
610	      RELEASE(arp);
611	      arp = [NSAutoreleasePool new];
612	    }
613	}
614    }
615
616#if 1
617  /* Check that we can retain the connection, release the proxy,
618   * and then regain the proxy from the connection.
619   */
620  cobj = RETAIN([prx connectionForProxy]);
621  RELEASE(arp);
622  arp = [NSAutoreleasePool new];
623  prx = [cobj rootProxy];
624  AUTORELEASE(cobj);
625#else
626  cobj = [prx connectionForProxy];
627#endif
628
629  [cobj setDelegate:auth];
630  [cobj setRequestTimeout:180.0];
631  [cobj setReplyTimeout:180.0];
632
633  [prx print: "This is a message from the client. Starting Tests!"];
634
635  switch (type_test)
636    {
637    case TYPE_TEST:
638      con_data (prx);
639      break;
640    case BENCHMARK_TEST:
641      con_benchmark (prx);
642      break;
643    case MESSAGE_TEST:
644      con_messages (prx);
645      break;
646    case LOOP_TEST:
647      con_loop (prx);
648      break;
649    case OBJECT_TEST:
650      con_objects (prx);
651      break;
652    case CALLBACK_TEST:
653      con_callback (prx);
654      break;
655    default:
656      break;
657    }
658
659  if (stats)
660    con_statistics (prx);
661
662  [cobj invalidate];
663
664  [arp release];
665  return 0;
666}
667