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