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