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