blob: e72775eda80d806e01893219527101aeb76134de [file] [log] [blame]
Roger Meier3bef8c22012-10-06 06:58:00 +00001(*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 *)
19
20unit TestClient;
21
22interface
23
24uses
25 Windows, SysUtils, Classes,
26 DateUtils,
27 Generics.Collections,
28 TestConstants,
29 Thrift,
30 Thrift.Protocol.JSON,
31 Thrift.Protocol,
32 Thrift.Transport.Pipes,
33 Thrift.Transport,
34 Thrift.Stream,
35 Thrift.Test,
36 Thrift.Collections,
37 Thrift.Console;
38
39type
40 TThreadConsole = class
41 private
42 FThread : TThread;
43 public
44 procedure Write( const S : string);
45 procedure WriteLine( const S : string);
46 constructor Create( AThread: TThread);
47 end;
48
49 TClientThread = class( TThread )
50 private
51 FTransport : ITransport;
52 FProtocol : IProtocol;
53 FNumIteration : Integer;
54 FConsole : TThreadConsole;
55
56 // test reporting, will be refactored out into separate class later
57 FTestGroup : string;
58 FSuccesses : Integer;
59 FErrors : TStringList;
60 procedure StartTestGroup( const aGroup : string);
61 procedure Expect( aTestResult : Boolean; const aTestInfo : string);
62 procedure ReportResults;
63
64 procedure ClientTest;
65 procedure JSONProtocolReadWriteTest;
66 protected
67 procedure Execute; override;
68 public
69 constructor Create( const ATransport: ITransport; const AProtocol : IProtocol; ANumIteration: Integer);
70 destructor Destroy; override;
71 end;
72
73 TTestClient = class
74 private
75 class var
76 FNumIteration : Integer;
77 FNumThread : Integer;
78 public
79 class procedure Execute( const args: array of string);
80 end;
81
82implementation
83
84function BoolToString( b : Boolean) : string;
85// overrides global BoolToString()
86begin
87 if b
88 then result := 'true'
89 else result := 'false';
90end;
91
92// not available in all versions, so make sure we have this one imported
93function IsDebuggerPresent: BOOL; stdcall; external KERNEL32 name 'IsDebuggerPresent';
94
95{ TTestClient }
96
97class procedure TTestClient.Execute(const args: array of string);
98var
99 i : Integer;
100 host : string;
101 port : Integer;
102 url : string;
103 bBuffered : Boolean;
104 bAnonPipe : Boolean;
105 bFramed : Boolean;
106 sPipeName : string;
107 hAnonRead, hAnonWrite : THandle;
108 s : string;
109 n : Integer;
110 threads : array of TThread;
111 dtStart : TDateTime;
112 test : Integer;
113 thread : TThread;
114 trans : ITransport;
115 prot : IProtocol;
116 streamtrans : IStreamTransport;
117 http : IHTTPClient;
118 protType, p : TKnownProtocol;
119begin
120 bBuffered := False;;
121 bFramed := False;
122 protType := prot_Binary;
123 try
124 host := 'localhost';
125 port := 9090;
126 url := '';
127 sPipeName := '';
128 bAnonPipe := FALSE;
129 hAnonRead := INVALID_HANDLE_VALUE;
130 hAnonWrite := INVALID_HANDLE_VALUE;
131 i := 0;
132 try
133 while ( i < Length(args) ) do
134 begin
135
136 try
137 if ( args[i] = '-h') then
138 begin
139 Inc( i );
140 s := args[i];
141 n := Pos( ':', s);
142 if ( n > 0 ) then
143 begin
144 host := Copy( s, 1, n - 1);
145 port := StrToInt( Copy( s, n + 1, MaxInt));
146 end else
147 begin
148 host := s;
149 end;
150 end
151 else if (args[i] = '-u') then
152 begin
153 Inc( i );
154 url := args[i];
155 end
156 else if (args[i] = '-n') then
157 begin
158 Inc( i );
159 FNumIteration := StrToInt( args[i] );
160 end
161 else if (args[i] = '-b') then
162 begin
163 bBuffered := True;
164 Console.WriteLine('Buffered transport');
165 end
166 else if (args[i] = '-f' ) or ( args[i] = '-framed') then
167 begin
168 bFramed := True;
169 Console.WriteLine('Framed transport');
170 end
171 else if (args[i] = '-pipe') then // -pipe <name>
172 begin
173 Console.WriteLine('Named pipes transport');
174 Inc( i );
175 sPipeName := args[i];
176 end
177 else if (args[i] = '-anon') then // -anon <hReadPipe> <hWritePipe>
178 begin
Jens Geyerb64a7742013-01-23 20:58:47 +0100179 if Length(args) <= (i+2) then begin
180 Console.WriteLine('Invalid args: -anon <hRead> <hWrite> or use "server.exe -anon"');
181 Halt(1);
182 end;
Roger Meier3bef8c22012-10-06 06:58:00 +0000183 Console.WriteLine('Anonymous pipes transport');
184 Inc( i);
185 hAnonRead := THandle( StrToIntDef( args[i], Integer(INVALID_HANDLE_VALUE)));
186 Inc( i);
187 hAnonWrite := THandle( StrToIntDef( args[i], Integer(INVALID_HANDLE_VALUE)));
188 bAnonPipe := TRUE;
189 end
190 else if (args[i] = '-t') then
191 begin
192 Inc( i );
193 FNumThread := StrToInt( args[i] );
194 end
195 else if (args[i] = '-prot') then // -prot JSON|binary
196 begin
197 Inc( i );
198 s := args[i];
199 for p:= Low(TKnownProtocol) to High(TKnownProtocol) do begin
200 if SameText( s, KNOWN_PROTOCOLS[p]) then begin
201 protType := p;
202 Console.WriteLine('Using '+KNOWN_PROTOCOLS[protType]+' protocol');
203 Break;
204 end;
205 end;
206 end;
207 finally
208 Inc( i );
209 end;
210
211 end;
212
213 except
214 on E: Exception do
215 begin
216 Console.WriteLine( E.Message );
217 end;
218 end;
219
Roger Meier79655fb2012-10-20 20:59:41 +0000220 // In the anonymous pipes mode the client is launched by the test server
221 // -> behave nicely and allow for attaching a debugger to this process
222 if bAnonPipe and not IsDebuggerPresent
223 then MessageBox( 0, 'Attach Debugger and/or click OK to continue.',
224 'Thrift TestClient (Delphi)',
225 MB_OK or MB_ICONEXCLAMATION);
226
Roger Meier3bef8c22012-10-06 06:58:00 +0000227 SetLength( threads, FNumThread);
228 dtStart := Now;
229
230 for test := 0 to FNumThread - 1 do
231 begin
232 if url = '' then
233 begin
234 if sPipeName <> '' then begin
235 Console.WriteLine('Using named pipe ('+sPipeName+')');
236 streamtrans := TNamedPipeImpl.Create( sPipeName);
237 end
238 else if bAnonPipe then begin
239 Console.WriteLine('Using anonymous pipes ('+IntToStr(Integer(hAnonRead))+' and '+IntToStr(Integer(hAnonWrite))+')');
240 streamtrans := TAnonymousPipeImpl.Create( hAnonRead, hAnonWrite, FALSE);
241 end
242 else begin
243 Console.WriteLine('Using sockets ('+host+' port '+IntToStr(port)+')');
244 streamtrans := TSocketImpl.Create( host, port );
245 end;
246
247 trans := streamtrans;
248
249 if bBuffered then begin
250 trans := TBufferedTransportImpl.Create( streamtrans);
251 Console.WriteLine('Using buffered transport');
252 end;
253
254 if bFramed then begin
255 trans := TFramedTransportImpl.Create( trans );
256 Console.WriteLine('Using framed transport');
257 end;
258
259 end
260 else begin
261 Console.WriteLine('Using HTTPClient');
262 http := THTTPClientImpl.Create( url );
263 trans := http;
264 end;
265
266 // create protocol instance, default to BinaryProtocol
267 case protType of
268 prot_Binary: prot := TBinaryProtocolImpl.Create( trans);
269 prot_JSON : prot := TJSONProtocolImpl.Create( trans);
270 else
271 ASSERT( FALSE); // unhandled case!
272 prot := TBinaryProtocolImpl.Create( trans); // use default
273 end;
274
275 thread := TClientThread.Create( trans, prot, FNumIteration);
276 threads[test] := thread;
277{$WARN SYMBOL_DEPRECATED OFF}
278 thread.Resume;
279{$WARN SYMBOL_DEPRECATED ON}
280 end;
281
282 for test := 0 to FNumThread - 1 do
283 begin
284 threads[test].WaitFor;
285 end;
286
287 for test := 0 to FNumThread - 1 do
288 begin
289 threads[test].Free;
290 end;
291
292 Console.Write('Total time: ' + IntToStr( MilliSecondsBetween(Now, dtStart)));
293
294 except
295 on E: Exception do
296 begin
297 Console.WriteLine( E.Message + ' ST: ' + E.StackTrace );
298 end;
299 end;
300
301 Console.WriteLine('');
302 Console.WriteLine('done!');
303end;
304
305{ TClientThread }
306
307procedure TClientThread.ClientTest;
308var
309 client : TThriftTest.Iface;
310 s : string;
311 i8 : ShortInt;
312 i32 : Integer;
313 i64 : Int64;
314 dub : Double;
315 o : IXtruct;
316 o2 : IXtruct2;
317 i : IXtruct;
318 i2 : IXtruct2;
319 mapout : IThriftDictionary<Integer,Integer>;
320 mapin : IThriftDictionary<Integer,Integer>;
321 strmapout : IThriftDictionary<string,string>;
322 strmapin : IThriftDictionary<string,string>;
323 j : Integer;
324 first : Boolean;
325 key : Integer;
326 strkey : string;
327 listout : IThriftList<Integer>;
328 listin : IThriftList<Integer>;
329 setout : IHashSet<Integer>;
330 setin : IHashSet<Integer>;
331 ret : TNumberz;
332 uid : Int64;
333 mm : IThriftDictionary<Integer, IThriftDictionary<Integer, Integer>>;
334 pos : IThriftDictionary<Integer, Integer>;
335 neg : IThriftDictionary<Integer, Integer>;
336 m2 : IThriftDictionary<Integer, Integer>;
337 k2 : Integer;
338 insane : IInsanity;
339 truck : IXtruct;
340 whoa : IThriftDictionary<Int64, IThriftDictionary<TNumberz, IInsanity>>;
341 key64 : Int64;
342 val : IThriftDictionary<TNumberz, IInsanity>;
343 k2_2 : TNumberz;
344 k3 : TNumberz;
345 v2 : IInsanity;
346 userMap : IThriftDictionary<TNumberz, Int64>;
347 xtructs : IThriftList<IXtruct>;
348 x : IXtruct;
349 arg0 : ShortInt;
350 arg1 : Integer;
351 arg2 : Int64;
352 arg3 : IThriftDictionary<SmallInt, string>;
353 arg4 : TNumberz;
354 arg5 : Int64;
355 StartTick : Cardinal;
356 k : Integer;
357 proc : TThreadProcedure;
358 hello, goodbye : IXtruct;
359 crazy : IInsanity;
360 looney : IInsanity;
361 first_map : IThriftDictionary<TNumberz, IInsanity>;
362 second_map : IThriftDictionary<TNumberz, IInsanity>;
363
364begin
365 client := TThriftTest.TClient.Create( FProtocol);
366 FTransport.Open;
367
368 // in-depth exception test
369 // (1) do we get an exception at all?
370 // (2) do we get the right exception?
371 // (3) does the exception contain the expected data?
372 StartTestGroup( 'testException');
373 // case 1: exception type declared in IDL at the function call
374 try
375 client.testException('Xception');
376 Expect( FALSE, 'testException(''Xception''): must trow an exception');
377 except
378 on e:TXception do begin
379 Expect( e.ErrorCode = 1001, 'error code');
380 Expect( e.Message_ = 'Xception', 'error message');
381 Console.WriteLine( ' = ' + IntToStr(e.ErrorCode) + ', ' + e.Message_ );
382 end;
383 on e:TTransportException do Expect( FALSE, 'Unexpected : "'+e.ToString+'"');
384 on e:Exception do Expect( FALSE, 'Unexpected exception type "'+e.ClassName+'"');
385 end;
386
387 // case 2: exception type NOT declared in IDL at the function call
388 // this will close the connection
389 try
390 client.testException('TException');
391 Expect( FALSE, 'testException(''TException''): must trow an exception');
392 except
393 on e:TTransportException do begin
394 Console.WriteLine( e.ClassName+' = '+e.Message); // this is what we get
395 if FTransport.IsOpen then FTransport.Close;
396 FTransport.Open; // re-open connection, server has already closed
397 end;
398 on e:TException do Expect( FALSE, 'Unexpected exception type "'+e.ClassName+'"');
399 on e:Exception do Expect( FALSE, 'Unexpected exception type "'+e.ClassName+'"');
400 end;
401
402 // case 3: no exception
403 try
404 client.testException('something');
405 Expect( TRUE, 'testException(''something''): must not trow an exception');
406 except
407 on e:TTransportException do Expect( FALSE, 'Unexpected : "'+e.ToString+'"');
408 on e:Exception do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'"');
409 end;
410
411
412 // simple things
413 StartTestGroup( 'simple Thrift calls');
414 client.testVoid();
415 Expect( TRUE, 'testVoid()'); // success := no exception
416
417 s := client.testString('Test');
418 Expect( s = 'Test', 'testString(''Test'') = "'+s+'"');
419
420 i8 := client.testByte(1);
421 Expect( i8 = 1, 'testByte(1) = ' + IntToStr( i8 ));
422
423 i32 := client.testI32(-1);
424 Expect( i32 = -1, 'testI32(-1) = ' + IntToStr(i32));
425
426 Console.WriteLine('testI64(-34359738368)');
427 i64 := client.testI64(-34359738368);
428 Expect( i64 = -34359738368, 'testI64(-34359738368) = ' + IntToStr( i64));
429
430 Console.WriteLine('testDouble(5.325098235)');
431 dub := client.testDouble(5.325098235);
432 Expect( abs(dub-5.325098235) < 1e-14, 'testDouble(5.325098235) = ' + FloatToStr( dub));
433
434 // structs
435 StartTestGroup( 'testStruct');
436 Console.WriteLine('testStruct({''Zero'', 1, -3, -5})');
437 o := TXtructImpl.Create;
438 o.String_thing := 'Zero';
439 o.Byte_thing := 1;
440 o.I32_thing := -3;
441 o.I64_thing := -5;
442 i := client.testStruct(o);
443 Expect( i.String_thing = 'Zero', 'i.String_thing = "'+i.String_thing+'"');
444 Expect( i.Byte_thing = 1, 'i.Byte_thing = '+IntToStr(i.Byte_thing));
445 Expect( i.I32_thing = -3, 'i.I32_thing = '+IntToStr(i.I32_thing));
446 Expect( i.I64_thing = -5, 'i.I64_thing = '+IntToStr(i.I64_thing));
447 Expect( i.__isset_String_thing, 'i.__isset_String_thing = '+BoolToString(i.__isset_String_thing));
448 Expect( i.__isset_Byte_thing, 'i.__isset_Byte_thing = '+BoolToString(i.__isset_Byte_thing));
449 Expect( i.__isset_I32_thing, 'i.__isset_I32_thing = '+BoolToString(i.__isset_I32_thing));
450 Expect( i.__isset_I64_thing, 'i.__isset_I64_thing = '+BoolToString(i.__isset_I64_thing));
451
452 // nested structs
453 StartTestGroup( 'testNest');
454 Console.WriteLine('testNest({1, {''Zero'', 1, -3, -5}, 5})');
455 o2 := TXtruct2Impl.Create;
456 o2.Byte_thing := 1;
457 o2.Struct_thing := o;
458 o2.I32_thing := 5;
459 i2 := client.testNest(o2);
460 i := i2.Struct_thing;
461 Expect( i.String_thing = 'Zero', 'i.String_thing = "'+i.String_thing+'"');
462 Expect( i.Byte_thing = 1, 'i.Byte_thing = '+IntToStr(i.Byte_thing));
463 Expect( i.I32_thing = -3, 'i.I32_thing = '+IntToStr(i.I32_thing));
464 Expect( i.I64_thing = -5, 'i.I64_thing = '+IntToStr(i.I64_thing));
465 Expect( i2.Byte_thing = 1, 'i2.Byte_thing = '+IntToStr(i2.Byte_thing));
466 Expect( i2.I32_thing = 5, 'i2.I32_thing = '+IntToStr(i2.I32_thing));
467 Expect( i.__isset_String_thing, 'i.__isset_String_thing = '+BoolToString(i.__isset_String_thing));
468 Expect( i.__isset_Byte_thing, 'i.__isset_Byte_thing = '+BoolToString(i.__isset_Byte_thing));
469 Expect( i.__isset_I32_thing, 'i.__isset_I32_thing = '+BoolToString(i.__isset_I32_thing));
470 Expect( i.__isset_I64_thing, 'i.__isset_I64_thing = '+BoolToString(i.__isset_I64_thing));
471 Expect( i2.__isset_Byte_thing, 'i2.__isset_Byte_thing');
472 Expect( i2.__isset_I32_thing, 'i2.__isset_I32_thing');
473
474 // map<type1,type2>: A map of strictly unique keys to values.
475 // Translates to an STL map, Java HashMap, PHP associative array, Python/Ruby dictionary, etc.
476 StartTestGroup( 'testMap');
477 mapout := TThriftDictionaryImpl<Integer,Integer>.Create;
478 for j := 0 to 4 do
479 begin
480 mapout.AddOrSetValue( j, j - 10);
481 end;
482 Console.Write('testMap({');
483 first := True;
484 for key in mapout.Keys do
485 begin
486 if first
487 then first := False
488 else Console.Write( ', ' );
489 Console.Write( IntToStr( key) + ' => ' + IntToStr( mapout[key]));
490 end;
491 Console.WriteLine('})');
492
493 mapin := client.testMap( mapout );
494 Expect( mapin.Count = mapout.Count, 'testMap: mapin.Count = mapout.Count');
495 for j := 0 to 4 do
496 begin
497 Expect( mapout.ContainsKey(j), 'testMap: mapout.ContainsKey('+IntToStr(j)+') = '+BoolToString(mapout.ContainsKey(j)));
498 end;
499 for key in mapin.Keys do
500 begin
501 Expect( mapin[key] = mapout[key], 'testMap: '+IntToStr(key) + ' => ' + IntToStr( mapin[key]));
502 Expect( mapin[key] = key - 10, 'testMap: mapin['+IntToStr(key)+'] = '+IntToStr( mapin[key]));
503 end;
504
505
506 // map<type1,type2>: A map of strictly unique keys to values.
507 // Translates to an STL map, Java HashMap, PHP associative array, Python/Ruby dictionary, etc.
508 StartTestGroup( 'testStringMap');
509 strmapout := TThriftDictionaryImpl<string,string>.Create;
510 for j := 0 to 4 do
511 begin
512 strmapout.AddOrSetValue( IntToStr(j), IntToStr(j - 10));
513 end;
514 Console.Write('testStringMap({');
515 first := True;
516 for strkey in strmapout.Keys do
517 begin
518 if first
519 then first := False
520 else Console.Write( ', ' );
521 Console.Write( strkey + ' => ' + strmapout[strkey]);
522 end;
523 Console.WriteLine('})');
524
525 strmapin := client.testStringMap( strmapout );
526 Expect( strmapin.Count = strmapout.Count, 'testStringMap: strmapin.Count = strmapout.Count');
527 for j := 0 to 4 do
528 begin
529 Expect( strmapout.ContainsKey(IntToStr(j)),
530 'testStringMap: strmapout.ContainsKey('+IntToStr(j)+') = '
531 + BoolToString(strmapout.ContainsKey(IntToStr(j))));
532 end;
533 for strkey in strmapin.Keys do
534 begin
535 Expect( strmapin[strkey] = strmapout[strkey], 'testStringMap: '+strkey + ' => ' + strmapin[strkey]);
536 Expect( strmapin[strkey] = IntToStr( StrToInt(strkey) - 10), 'testStringMap: strmapin['+strkey+'] = '+strmapin[strkey]);
537 end;
538
539
540 // set<type>: An unordered set of unique elements.
541 // Translates to an STL set, Java HashSet, set in Python, etc.
542 // Note: PHP does not support sets, so it is treated similar to a List
543 StartTestGroup( 'testSet');
544 setout := THashSetImpl<Integer>.Create;
545 for j := -2 to 2 do
546 begin
547 setout.Add( j );
548 end;
549 Console.Write('testSet({');
550 first := True;
551 for j in setout do
552 begin
553 if first
554 then first := False
555 else Console.Write(', ');
556 Console.Write(IntToStr( j));
557 end;
558 Console.WriteLine('})');
559
560 setin := client.testSet(setout);
561 Expect( setin.Count = setout.Count, 'testSet: setin.Count = setout.Count');
562 Expect( setin.Count = 5, 'testSet: setin.Count = '+IntToStr(setin.Count));
563 for j := -2 to 2 do // unordered, we can't rely on the order => test for known elements only
564 begin
565 Expect( setin.Contains(j), 'testSet: setin.Contains('+IntToStr(j)+') => '+BoolToString(setin.Contains(j)));
566 end;
567
568 // list<type>: An ordered list of elements.
569 // Translates to an STL vector, Java ArrayList, native arrays in scripting languages, etc.
570 StartTestGroup( 'testList');
571 listout := TThriftListImpl<Integer>.Create;
572 listout.Add( +1);
573 listout.Add( -2);
574 listout.Add( +3);
575 listout.Add( -4);
576 listout.Add( 0);
577 Console.Write('testList({');
578 first := True;
579 for j in listout do
580 begin
581 if first
582 then first := False
583 else Console.Write(', ');
584 Console.Write(IntToStr( j));
585 end;
586 Console.WriteLine('})');
587
588 listin := client.testList(listout);
589 Expect( listin.Count = listout.Count, 'testList: listin.Count = listout.Count');
590 Expect( listin.Count = 5, 'testList: listin.Count = '+IntToStr(listin.Count));
591 Expect( listin[0] = +1, 'listin[0] = '+IntToStr( listin[0]));
592 Expect( listin[1] = -2, 'listin[1] = '+IntToStr( listin[1]));
593 Expect( listin[2] = +3, 'listin[2] = '+IntToStr( listin[2]));
594 Expect( listin[3] = -4, 'listin[3] = '+IntToStr( listin[3]));
595 Expect( listin[4] = 0, 'listin[4] = '+IntToStr( listin[4]));
596
597 // enums
598 ret := client.testEnum(TNumberz.ONE);
599 Expect( ret = TNumberz.ONE, 'testEnum(ONE) = '+IntToStr(Ord(ret)));
600
601 ret := client.testEnum(TNumberz.TWO);
602 Expect( ret = TNumberz.TWO, 'testEnum(TWO) = '+IntToStr(Ord(ret)));
603
604 ret := client.testEnum(TNumberz.THREE);
605 Expect( ret = TNumberz.THREE, 'testEnum(THREE) = '+IntToStr(Ord(ret)));
606
607 ret := client.testEnum(TNumberz.FIVE);
608 Expect( ret = TNumberz.FIVE, 'testEnum(FIVE) = '+IntToStr(Ord(ret)));
609
610 ret := client.testEnum(TNumberz.EIGHT);
611 Expect( ret = TNumberz.EIGHT, 'testEnum(EIGHT) = '+IntToStr(Ord(ret)));
612
613
614 // typedef
615 uid := client.testTypedef(309858235082523);
616 Expect( uid = 309858235082523, 'testTypedef(309858235082523) = '+IntToStr(uid));
617
618
619 // maps of maps
620 StartTestGroup( 'testMapMap(1)');
621 mm := client.testMapMap(1);
622 Console.Write(' = {');
623 for key in mm.Keys do
624 begin
625 Console.Write( IntToStr( key) + ' => {');
626 m2 := mm[key];
627 for k2 in m2.Keys do
628 begin
629 Console.Write( IntToStr( k2) + ' => ' + IntToStr( m2[k2]) + ', ');
630 end;
631 Console.Write('}, ');
632 end;
633 Console.WriteLine('}');
634
635 // verify result data
636 Expect( mm.Count = 2, 'mm.Count = '+IntToStr(mm.Count));
637 pos := mm[4];
638 neg := mm[-4];
639 for j := 1 to 4 do
640 begin
641 Expect( pos[j] = j, 'pos[j] = '+IntToStr(pos[j]));
642 Expect( neg[-j] = -j, 'neg[-j] = '+IntToStr(neg[-j]));
643 end;
644
645
646
647 // insanity
648 StartTestGroup( 'testInsanity');
649 insane := TInsanityImpl.Create;
650 insane.UserMap := TThriftDictionaryImpl<TNumberz, Int64>.Create;
651 insane.UserMap.AddOrSetValue( TNumberz.FIVE, 5000);
652 truck := TXtructImpl.Create;
653 truck.String_thing := 'Truck';
654 truck.Byte_thing := 8;
655 truck.I32_thing := 8;
656 truck.I64_thing := 8;
657 insane.Xtructs := TThriftListImpl<IXtruct>.Create;
658 insane.Xtructs.Add( truck );
659 whoa := client.testInsanity( insane );
660 Console.Write(' = {');
661 for key64 in whoa.Keys do
662 begin
663 val := whoa[key64];
664 Console.Write( IntToStr( key64) + ' => {');
665 for k2_2 in val.Keys do
666 begin
667 v2 := val[k2_2];
668 Console.Write( IntToStr( Integer( k2_2)) + ' => {');
669 userMap := v2.UserMap;
670 Console.Write('{');
671 if userMap <> nil then
672 begin
673 for k3 in userMap.Keys do
674 begin
675 Console.Write( IntToStr( Integer( k3)) + ' => ' + IntToStr( userMap[k3]) + ', ');
676 end;
677 end else
678 begin
679 Console.Write('null');
680 end;
681 Console.Write('}, ');
682 xtructs := v2.Xtructs;
683 Console.Write('{');
684
685 if xtructs <> nil then
686 begin
687 for x in xtructs do
688 begin
689 Console.Write('{"' + x.String_thing + '", ' +
690 IntToStr( x.Byte_thing) + ', ' +
691 IntToStr( x.I32_thing) + ', ' +
692 IntToStr( x.I32_thing) + '}, ');
693 end;
694 end else
695 begin
696 Console.Write('null');
697 end;
698 Console.Write('}');
699 Console.Write('}, ');
700 end;
701 Console.Write('}, ');
702 end;
703 Console.WriteLine('}');
704
705 // verify result data
706 Expect( whoa.Count = 2, 'whoa.Count = '+IntToStr(whoa.Count));
707 //
708 first_map := whoa[1];
709 second_map := whoa[2];
710 Expect( first_map.Count = 2, 'first_map.Count = '+IntToStr(first_map.Count));
711 Expect( second_map.Count = 1, 'second_map.Count = '+IntToStr(second_map.Count));
712 //
713 looney := second_map[TNumberz.SIX];
714 Expect( Assigned(looney), 'Assigned(looney) = '+BoolToString(Assigned(looney)));
715 Expect( not looney.__isset_UserMap, 'looney.__isset_UserMap = '+BoolToString(looney.__isset_UserMap));
716 Expect( not looney.__isset_Xtructs, 'looney.__isset_Xtructs = '+BoolToString(looney.__isset_Xtructs));
717 //
718 for ret in [TNumberz.TWO, TNumberz.THREE] do begin
719 crazy := first_map[ret];
720 Console.WriteLine('first_map['+intToStr(Ord(ret))+']');
721
722 Expect( crazy.__isset_UserMap, 'crazy.__isset_UserMap = '+BoolToString(crazy.__isset_UserMap));
723 Expect( crazy.__isset_Xtructs, 'crazy.__isset_Xtructs = '+BoolToString(crazy.__isset_Xtructs));
724
725 Expect( crazy.UserMap.Count = 2, 'crazy.UserMap.Count = '+IntToStr(crazy.UserMap.Count));
726 Expect( crazy.UserMap[TNumberz.FIVE] = 5, 'crazy.UserMap[TNumberz.FIVE] = '+IntToStr(crazy.UserMap[TNumberz.FIVE]));
727 Expect( crazy.UserMap[TNumberz.EIGHT] = 8, 'crazy.UserMap[TNumberz.EIGHT] = '+IntToStr(crazy.UserMap[TNumberz.EIGHT]));
728
729 Expect( crazy.Xtructs.Count = 2, 'crazy.Xtructs.Count = '+IntToStr(crazy.Xtructs.Count));
730 goodbye := crazy.Xtructs[0]; // lists are ordered, so we are allowed to assume this order
731 hello := crazy.Xtructs[1];
732
733 Expect( goodbye.String_thing = 'Goodbye4', 'goodbye.String_thing = "'+goodbye.String_thing+'"');
734 Expect( goodbye.Byte_thing = 4, 'goodbye.Byte_thing = '+IntToStr(goodbye.Byte_thing));
735 Expect( goodbye.I32_thing = 4, 'goodbye.I32_thing = '+IntToStr(goodbye.I32_thing));
736 Expect( goodbye.I64_thing = 4, 'goodbye.I64_thing = '+IntToStr(goodbye.I64_thing));
737 Expect( goodbye.__isset_String_thing, 'goodbye.__isset_String_thing = '+BoolToString(goodbye.__isset_String_thing));
738 Expect( goodbye.__isset_Byte_thing, 'goodbye.__isset_Byte_thing = '+BoolToString(goodbye.__isset_Byte_thing));
739 Expect( goodbye.__isset_I32_thing, 'goodbye.__isset_I32_thing = '+BoolToString(goodbye.__isset_I32_thing));
740 Expect( goodbye.__isset_I64_thing, 'goodbye.__isset_I64_thing = '+BoolToString(goodbye.__isset_I64_thing));
741
742 Expect( hello.String_thing = 'Hello2', 'hello.String_thing = "'+hello.String_thing+'"');
743 Expect( hello.Byte_thing = 2, 'hello.Byte_thing = '+IntToStr(hello.Byte_thing));
744 Expect( hello.I32_thing = 2, 'hello.I32_thing = '+IntToStr(hello.I32_thing));
745 Expect( hello.I64_thing = 2, 'hello.I64_thing = '+IntToStr(hello.I64_thing));
746 Expect( hello.__isset_String_thing, 'hello.__isset_String_thing = '+BoolToString(hello.__isset_String_thing));
747 Expect( hello.__isset_Byte_thing, 'hello.__isset_Byte_thing = '+BoolToString(hello.__isset_Byte_thing));
748 Expect( hello.__isset_I32_thing, 'hello.__isset_I32_thing = '+BoolToString(hello.__isset_I32_thing));
749 Expect( hello.__isset_I64_thing, 'hello.__isset_I64_thing = '+BoolToString(hello.__isset_I64_thing));
750 end;
751
752
753 // multi args
754 StartTestGroup( 'testMulti');
755 arg0 := 1;
756 arg1 := 2;
757 arg2 := High(Int64);
758 arg3 := TThriftDictionaryImpl<SmallInt, string>.Create;
759 arg3.AddOrSetValue( 1, 'one');
760 arg4 := TNumberz.FIVE;
761 arg5 := 5000000;
762 Console.WriteLine('Test Multi(' + IntToStr( arg0) + ',' +
763 IntToStr( arg1) + ',' + IntToStr( arg2) + ',' +
764 arg3.ToString + ',' + IntToStr( Integer( arg4)) + ',' +
765 IntToStr( arg5) + ')');
766
767 i := client.testMulti( arg0, arg1, arg2, arg3, arg4, arg5);
768 Expect( i.String_thing = 'Hello2', 'testMulti: i.String_thing = "'+i.String_thing+'"');
769 Expect( i.Byte_thing = arg0, 'testMulti: i.Byte_thing = '+IntToStr(i.Byte_thing));
770 Expect( i.I32_thing = arg1, 'testMulti: i.I32_thing = '+IntToStr(i.I32_thing));
771 Expect( i.I64_thing = arg2, 'testMulti: i.I64_thing = '+IntToStr(i.I64_thing));
772 Expect( i.__isset_String_thing, 'testMulti: i.__isset_String_thing = '+BoolToString(i.__isset_String_thing));
773 Expect( i.__isset_Byte_thing, 'testMulti: i.__isset_Byte_thing = '+BoolToString(i.__isset_Byte_thing));
774 Expect( i.__isset_I32_thing, 'testMulti: i.__isset_I32_thing = '+BoolToString(i.__isset_I32_thing));
775 Expect( i.__isset_I64_thing, 'testMulti: i.__isset_I64_thing = '+BoolToString(i.__isset_I64_thing));
776
777 // multi exception
778 StartTestGroup( 'testMultiException(1)');
779 try
780 i := client.testMultiException( 'need more pizza', 'run out of beer');
781 Expect( i.String_thing = 'run out of beer', 'i.String_thing = "' +i.String_thing+ '"');
782 Expect( i.__isset_String_thing, 'i.__isset_String_thing = '+BoolToString(i.__isset_String_thing));
783 Expect( not i.__isset_Byte_thing, 'i.__isset_Byte_thing = '+BoolToString(i.__isset_Byte_thing));
784 Expect( not i.__isset_I32_thing, 'i.__isset_I32_thing = '+BoolToString(i.__isset_I32_thing));
785 Expect( not i.__isset_I64_thing, 'i.__isset_I64_thing = '+BoolToString(i.__isset_I64_thing));
786 except
787 on e:Exception do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'"');
788 end;
789
790 StartTestGroup( 'testMultiException(Xception)');
791 try
792 i := client.testMultiException( 'Xception', 'second test');
793 Expect( FALSE, 'testMultiException(''Xception''): must trow an exception');
794 except
795 on x:TXception do begin
796 Expect( x.__isset_ErrorCode, 'x.__isset_ErrorCode = '+BoolToString(x.__isset_ErrorCode));
797 Expect( x.__isset_Message_, 'x.__isset_Message_ = '+BoolToString(x.__isset_Message_));
798 Expect( x.ErrorCode = 1001, 'x.ErrorCode = '+IntToStr(x.ErrorCode));
799 Expect( x.Message_ = 'This is an Xception', 'x.Message = "'+x.Message_+'"');
800 end;
801 on e:Exception do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'"');
802 end;
803
804 StartTestGroup( 'testMultiException(Xception2)');
805 try
806 i := client.testMultiException( 'Xception2', 'third test');
807 Expect( FALSE, 'testMultiException(''Xception2''): must trow an exception');
808 except
809 on x:TXception2 do begin
810 Expect( x.__isset_ErrorCode, 'x.__isset_ErrorCode = '+BoolToString(x.__isset_ErrorCode));
811 Expect( x.__isset_Struct_thing, 'x.__isset_Struct_thing = '+BoolToString(x.__isset_Struct_thing));
812 Expect( x.ErrorCode = 2002, 'x.ErrorCode = '+IntToStr(x.ErrorCode));
813 Expect( x.Struct_thing.String_thing = 'This is an Xception2', 'x.Struct_thing.String_thing = "'+x.Struct_thing.String_thing+'"');
814 Expect( x.Struct_thing.__isset_String_thing, 'x.Struct_thing.__isset_String_thing = '+BoolToString(x.Struct_thing.__isset_String_thing));
815 Expect( not x.Struct_thing.__isset_Byte_thing, 'x.Struct_thing.__isset_Byte_thing = '+BoolToString(x.Struct_thing.__isset_Byte_thing));
816 Expect( not x.Struct_thing.__isset_I32_thing, 'x.Struct_thing.__isset_I32_thing = '+BoolToString(x.Struct_thing.__isset_I32_thing));
817 Expect( not x.Struct_thing.__isset_I64_thing, 'x.Struct_thing.__isset_I64_thing = '+BoolToString(x.Struct_thing.__isset_I64_thing));
818 end;
819 on e:Exception do Expect( FALSE, 'Unexpected exception "'+e.ClassName+'"');
820 end;
821
822
823 // oneway functions
824 StartTestGroup( 'Test Oneway(1)');
825 client.testOneway(1);
826 Expect( TRUE, 'Test Oneway(1)'); // success := no exception
827
828 // call time
829 StartTestGroup( 'Test Calltime()');
830 StartTick := GetTIckCount;
831 for k := 0 to 1000 - 1 do
832 begin
833 client.testVoid();
834 end;
835 Console.WriteLine(' = ' + FloatToStr( (GetTickCount - StartTick) / 1000 ) + ' ms a testVoid() call' );
836
837 // no more tests here
838 StartTestGroup( '');
839end;
840
841
842procedure TClientThread.JSONProtocolReadWriteTest;
843// Tests only then read/write procedures of the JSON protocol
844// All tests succeed, if we can read what we wrote before
845// Note that passing this test does not imply, that our JSON is really compatible to what
846// other clients or servers expect as the real JSON. This is beyond the scope of this test.
847var prot : IProtocol;
848 stm : TStringStream;
849 list : IList;
850 binary, binRead : TBytes;
851 i,iErr : Integer;
852const
853 TEST_SHORT = ShortInt( $FE);
854 TEST_SMALL = SmallInt( $FEDC);
855 TEST_LONG = LongInt( $FEDCBA98);
856 TEST_I64 = Int64( $FEDCBA9876543210);
857 TEST_DOUBLE = -1.234e-56;
858 DELTA_DOUBLE = TEST_DOUBLE * 1e-14;
859 TEST_STRING = 'abc-'#$00E4#$00f6#$00fc; // german umlauts (en-us: "funny chars")
860begin
861 stm := TStringStream.Create;
862 try
863 StartTestGroup( 'JsonProtocolTest'); // no more tests here
864
865 // prepare binary data
866 SetLength( binary, $100);
867 for i := Low(binary) to High(binary) do binary[i] := i;
868
869 // output setup
870 prot := TJSONProtocolImpl.Create(
871 TStreamTransportImpl.Create(
872 nil, TThriftStreamAdapterDelphi.Create( stm, FALSE)));
873
874 // write
875 prot.WriteListBegin( TListImpl.Create( TType.String_, 9));
876 prot.WriteBool( TRUE);
877 prot.WriteBool( FALSE);
878 prot.WriteByte( TEST_SHORT);
879 prot.WriteI16( TEST_SMALL);
880 prot.WriteI32( TEST_LONG);
881 prot.WriteI64( TEST_I64);
882 prot.WriteDouble( TEST_DOUBLE);
883 prot.WriteString( TEST_STRING);
884 prot.WriteBinary( binary);
885 prot.WriteListEnd;
886
887 // input setup
888 Expect( stm.Position = stm.Size, 'Stream position/length after write');
889 stm.Position := 0;
890 prot := TJSONProtocolImpl.Create(
891 TStreamTransportImpl.Create(
892 TThriftStreamAdapterDelphi.Create( stm, FALSE), nil));
893
894 // read and compare
895 list := prot.ReadListBegin;
896 Expect( list.ElementType = TType.String_, 'list element type');
897 Expect( list.Count = 9, 'list element count');
898 Expect( prot.ReadBool, 'WriteBool/ReadBool: TRUE');
899 Expect( not prot.ReadBool, 'WriteBool/ReadBool: FALSE');
900 Expect( prot.ReadByte = TEST_SHORT, 'WriteByte/ReadByte');
901 Expect( prot.ReadI16 = TEST_SMALL, 'WriteI16/ReadI16');
902 Expect( prot.ReadI32 = TEST_LONG, 'WriteI32/ReadI32');
903 Expect( prot.ReadI64 = TEST_I64, 'WriteI64/ReadI64');
904 Expect( abs(prot.ReadDouble-TEST_DOUBLE) < abs(DELTA_DOUBLE), 'WriteDouble/ReadDouble');
905 Expect( prot.ReadString = TEST_STRING, 'WriteString/ReadString');
906 binRead := prot.ReadBinary;
907 prot.ReadListEnd;
908
909 // test binary data
910 Expect( Length(binary) = Length(binRead), 'Binary data length check');
911 iErr := -1;
912 for i := Low(binary) to High(binary) do begin
913 if binary[i] <> binRead[i] then begin
914 iErr := i;
915 Break;
916 end;
917 end;
918 if iErr < 0
919 then Expect( TRUE, 'Binary data check ('+IntToStr(Length(binary))+' Bytes)')
920 else Expect( FALSE, 'Binary data check at offset '+IntToStr(iErr));
921
922 Expect( stm.Position = stm.Size, 'Stream position after read');
923
924 finally
925 stm.Free;
926 prot := nil; //-> Release
927 StartTestGroup( ''); // no more tests here
928 end;
929end;
930
931
932procedure TClientThread.StartTestGroup( const aGroup : string);
933begin
934 FTestGroup := aGroup;
935 if FTestGroup <> '' then begin
936 Console.WriteLine('');
937 Console.WriteLine( aGroup+' tests');
938 Console.WriteLine( StringOfChar('-',60));
939 end;
940end;
941
942
943procedure TClientThread.Expect( aTestResult : Boolean; const aTestInfo : string);
944begin
945 if aTestResult then begin
946 Inc(FSuccesses);
947 Console.WriteLine( aTestInfo+': passed');
948 end
949 else begin
950 FErrors.Add( FTestGroup+': '+aTestInfo);
951 Console.WriteLine( aTestInfo+': *** FAILED ***');
952
953 // We have a failed test!
954 // -> issue DebugBreak ONLY if a debugger is attached,
955 // -> unhandled DebugBreaks would cause Windows to terminate the app otherwise
956 if IsDebuggerPresent then asm int 3 end;
957 end;
958end;
959
960
961procedure TClientThread.ReportResults;
962var nTotal : Integer;
963 sLine : string;
964begin
965 // prevent us from stupid DIV/0 errors
966 nTotal := FSuccesses + FErrors.Count;
967 if nTotal = 0 then begin
968 Console.WriteLine('No results logged');
969 Exit;
970 end;
971
972 Console.WriteLine('');
973 Console.WriteLine( StringOfChar('=',60));
974 Console.WriteLine( IntToStr(nTotal)+' tests performed');
975 Console.WriteLine( IntToStr(FSuccesses)+' tests succeeded ('+IntToStr(round(100*FSuccesses/nTotal))+'%)');
976 Console.WriteLine( IntToStr(FErrors.Count)+' tests failed ('+IntToStr(round(100*FErrors.Count/nTotal))+'%)');
977 Console.WriteLine( StringOfChar('=',60));
978 if FErrors.Count > 0 then begin
979 Console.WriteLine('FAILED TESTS:');
980 for sLine in FErrors do Console.WriteLine('- '+sLine);
981 Console.WriteLine( StringOfChar('=',60));
982 InterlockedIncrement( ExitCode); // return <> 0 on errors
983 end;
984 Console.WriteLine('');
985end;
986
987
988constructor TClientThread.Create( const ATransport: ITransport; const AProtocol : IProtocol; ANumIteration: Integer);
989begin
990 inherited Create( True );
991 FNumIteration := ANumIteration;
992 FTransport := ATransport;
993 FProtocol := AProtocol;
994 FConsole := TThreadConsole.Create( Self );
995
996 // error list: keep correct order, allow for duplicates
997 FErrors := TStringList.Create;
998 FErrors.Sorted := FALSE;
999 FErrors.Duplicates := dupAccept;
1000end;
1001
1002destructor TClientThread.Destroy;
1003begin
1004 FreeAndNil( FConsole);
1005 FreeAndNil( FErrors);
1006 inherited;
1007end;
1008
1009procedure TClientThread.Execute;
1010var
1011 i : Integer;
1012 proc : TThreadProcedure;
1013begin
1014 // perform all tests
1015 try
1016 for i := 0 to FNumIteration - 1 do
1017 begin
1018 ClientTest;
1019 JSONProtocolReadWriteTest;
1020 end;
1021 except
1022 on e:Exception do Expect( FALSE, 'unexpected exception: "'+e.message+'"');
1023 end;
1024
1025 // report the outcome
1026 ReportResults;
1027
1028 // shutdown
1029 proc := procedure
1030 begin
1031 if FTransport <> nil then
1032 begin
1033 FTransport.Close;
1034 FTransport := nil;
1035 end;
1036 end;
1037
1038 Synchronize( proc );
1039end;
1040
1041{ TThreadConsole }
1042
1043constructor TThreadConsole.Create(AThread: TThread);
1044begin
1045 FThread := AThread;
1046end;
1047
1048procedure TThreadConsole.Write(const S: string);
1049var
1050 proc : TThreadProcedure;
1051begin
1052 proc := procedure
1053 begin
1054 Console.Write( S );
1055 end;
1056 TThread.Synchronize( FThread, proc);
1057end;
1058
1059procedure TThreadConsole.WriteLine(const S: string);
1060var
1061 proc : TThreadProcedure;
1062begin
1063 proc := procedure
1064 begin
1065 Console.WriteLine( S );
1066 end;
1067 TThread.Synchronize( FThread, proc);
1068end;
1069
1070initialization
1071begin
1072 TTestClient.FNumIteration := 1;
1073 TTestClient.FNumThread := 1;
1074end;
1075
1076end.