blob: e8edd82192a54ae0f3ef0062045b4e59e8c71826 [file] [log] [blame]
Jake Farrell7ae13e12011-10-18 14:35:26 +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
Jake Farrell27274222011-11-10 20:32:44 +000025 Windows, SysUtils, Classes,
26 DateUtils,
27 Generics.Collections,
28 TestConstants,
29 Thrift.Protocol.JSON,
30 Thrift.Protocol,
31 Thrift.Transport,
32 Thrift.Stream,
33 Thrift.Test,
34 Thrift.Collections,
35 Thrift.Console;
Jake Farrell7ae13e12011-10-18 14:35:26 +000036
37type
Jake Farrell7ae13e12011-10-18 14:35:26 +000038 TThreadConsole = class
39 private
40 FThread : TThread;
41 public
42 procedure Write( const S : string);
43 procedure WriteLine( const S : string);
44 constructor Create( AThread: TThread);
45 end;
46
47 TClientThread = class( TThread )
48 private
49 FTransport : ITransport;
Jake Farrell27274222011-11-10 20:32:44 +000050 FProtocol : IProtocol;
Jake Farrell7ae13e12011-10-18 14:35:26 +000051 FNumIteration : Integer;
52 FConsole : TThreadConsole;
53
Jake Farrell27274222011-11-10 20:32:44 +000054 FErrors, FSuccesses : Integer;
55 procedure Expect( aTestResult : Boolean; const aTestInfo : string);
56
Jake Farrell7ae13e12011-10-18 14:35:26 +000057 procedure ClientTest;
Jake Farrell27274222011-11-10 20:32:44 +000058 procedure JSONProtocolReadWriteTest;
Jake Farrell7ae13e12011-10-18 14:35:26 +000059 protected
60 procedure Execute; override;
61 public
Jake Farrell27274222011-11-10 20:32:44 +000062 constructor Create(ATransport: ITransport; AProtocol : IProtocol; ANumIteration: Integer);
Jake Farrell7ae13e12011-10-18 14:35:26 +000063 destructor Destroy; override;
64 end;
65
66 TTestClient = class
67 private
68 class var
69 FNumIteration : Integer;
70 FNumThread : Integer;
71 public
72 class procedure Execute( const args: array of string);
73 end;
74
75implementation
76
Jake Farrell27274222011-11-10 20:32:44 +000077
Jake Farrell7ae13e12011-10-18 14:35:26 +000078{ TTestClient }
79
80class procedure TTestClient.Execute(const args: array of string);
81var
82 i : Integer;
83 host : string;
84 port : Integer;
85 url : string;
86 bBuffered : Boolean;
87 bFramed : Boolean;
88 s : string;
89 n : Integer;
90 threads : array of TThread;
91 dtStart : TDateTime;
92 test : Integer;
93 thread : TThread;
94 trans : ITransport;
Jake Farrell27274222011-11-10 20:32:44 +000095 prot : IProtocol;
Jake Farrell7ae13e12011-10-18 14:35:26 +000096 streamtrans : IStreamTransport;
97 http : IHTTPClient;
Jake Farrell27274222011-11-10 20:32:44 +000098 protType, p : TKnownProtocol;
Jake Farrell7ae13e12011-10-18 14:35:26 +000099begin
100 bBuffered := False;;
101 bFramed := False;
Jake Farrell27274222011-11-10 20:32:44 +0000102 protType := prot_Binary;
Jake Farrell7ae13e12011-10-18 14:35:26 +0000103 try
104 host := 'localhost';
105 port := 9090;
106 url := '';
107 i := 0;
108 try
109 while ( i < Length(args) ) do
110 begin
111 try
112 if ( args[i] = '-h') then
113 begin
114 Inc( i );
115 s := args[i];
116 n := Pos( ':', s);
117 if ( n > 0 ) then
118 begin
119 host := Copy( s, 1, n - 1);
120 port := StrToInt( Copy( s, n + 1, MaxInt));
121 end else
122 begin
123 host := s;
124 end;
125 end else
126 if (args[i] = '-u') then
127 begin
128 Inc( i );
129 url := args[i];
130 end else
131 if (args[i] = '-n') then
132 begin
133 Inc( i );
134 FNumIteration := StrToInt( args[i] );
135 end else
136 if (args[i] = '-b') then
137 begin
138 bBuffered := True;
139 Console.WriteLine('Using buffered transport');
140 end else
141 if (args[i] = '-f' ) or ( args[i] = '-framed') then
142 begin
143 bFramed := True;
144 Console.WriteLine('Using framed transport');
145 end else
146 if (args[i] = '-t') then
147 begin
148 Inc( i );
149 FNumThread := StrToInt( args[i] );
Jake Farrell27274222011-11-10 20:32:44 +0000150 end else
151 if (args[i] = '-prot') then // -prot JSON|binary
152 begin
153 Inc( i );
154 s := args[i];
155 for p:= Low(TKnownProtocol) to High(TKnownProtocol) do begin
156 if SameText( s, KNOWN_PROTOCOLS[p]) then begin
157 protType := p;
158 Console.WriteLine('Using '+KNOWN_PROTOCOLS[protType]+' protocol');
159 Break;
160 end;
161 end;
Jake Farrell7ae13e12011-10-18 14:35:26 +0000162 end;
163 finally
164 Inc( i );
165 end;
166 end;
167 except
168 on E: Exception do
169 begin
170 Console.WriteLine( E.Message );
171 end;
172 end;
173
174 SetLength( threads, FNumThread);
175 dtStart := Now;
176
177 for test := 0 to FNumThread - 1 do
178 begin
179 if url = '' then
180 begin
181 streamtrans := TSocketImpl.Create( host, port );
182 trans := streamtrans;
183 if bBuffered then
184 begin
185 trans := TBufferedTransportImpl.Create( streamtrans );
186 end;
187
188 if bFramed then
189 begin
190 trans := TFramedTransportImpl.Create( trans );
191 end;
192 end else
193 begin
194 http := THTTPClientImpl.Create( url );
195 trans := http;
196 end;
Jake Farrell27274222011-11-10 20:32:44 +0000197
198 // create protocol instance, default to BinaryProtocol
199 case protType of
200 prot_Binary: prot := TBinaryProtocolImpl.Create( trans);
201 prot_JSON : prot := TJSONProtocolImpl.Create( trans);
202 else
203 ASSERT( FALSE); // unhandled case!
204 prot := TBinaryProtocolImpl.Create( trans); // use default
205 end;
206
207 thread := TClientThread.Create( trans, prot, FNumIteration);
Jake Farrell7ae13e12011-10-18 14:35:26 +0000208 threads[test] := thread;
209{$WARN SYMBOL_DEPRECATED OFF}
210 thread.Resume;
211{$WARN SYMBOL_DEPRECATED ON}
212 end;
213
214 for test := 0 to FNumThread - 1 do
215 begin
216 threads[test].WaitFor;
217 end;
218
219 for test := 0 to FNumThread - 1 do
220 begin
221 threads[test].Free;
222 end;
223
224 Console.Write('Total time: ' + IntToStr( MilliSecondsBetween(Now, dtStart)));
225
226 except
227 on E: Exception do
228 begin
229 Console.WriteLine( E.Message + ' ST: ' + E.StackTrace );
230 end;
231 end;
232
233 Console.WriteLine('');
234 Console.WriteLine('done!');
235end;
236
237{ TClientThread }
238
239procedure TClientThread.ClientTest;
240var
Jake Farrell7ae13e12011-10-18 14:35:26 +0000241 client : TThriftTest.Iface;
242 s : string;
243 i8 : ShortInt;
244 i32 : Integer;
245 i64 : Int64;
246 dub : Double;
247 o : IXtruct;
248 o2 : IXtruct2;
249 i : IXtruct;
250 i2 : IXtruct2;
251 mapout : IThriftDictionary<Integer,Integer>;
252 mapin : IThriftDictionary<Integer,Integer>;
253 j : Integer;
254 first : Boolean;
255 key : Integer;
256 listout : IThriftList<Integer>;
257 listin : IThriftList<Integer>;
258 setout : IHashSet<Integer>;
259 setin : IHashSet<Integer>;
260 ret : TNumberz;
261 uid : Int64;
262 mm : IThriftDictionary<Integer, IThriftDictionary<Integer, Integer>>;
263 m2 : IThriftDictionary<Integer, Integer>;
264 k2 : Integer;
265 insane : IInsanity;
266 truck : IXtruct;
267 whoa : IThriftDictionary<Int64, IThriftDictionary<TNumberz, IInsanity>>;
268 key64 : Int64;
269 val : IThriftDictionary<TNumberz, IInsanity>;
270 k2_2 : TNumberz;
271 k3 : TNumberz;
272 v2 : IInsanity;
Jake Farrell27274222011-11-10 20:32:44 +0000273 userMap : IThriftDictionary<TNumberz, Int64>;
Jake Farrell7ae13e12011-10-18 14:35:26 +0000274 xtructs : IThriftList<IXtruct>;
275 x : IXtruct;
276 arg0 : ShortInt;
277 arg1 : Integer;
278 arg2 : Int64;
279 multiDict : IThriftDictionary<SmallInt, string>;
280 arg4 : TNumberz;
281 arg5 : Int64;
282 StartTick : Cardinal;
283 k : Integer;
284 proc : TThreadProcedure;
285
286begin
Jake Farrell27274222011-11-10 20:32:44 +0000287 client := TThriftTest.TClient.Create( FProtocol);
Jake Farrell7ae13e12011-10-18 14:35:26 +0000288 try
289 if not FTransport.IsOpen then
290 begin
291 FTransport.Open;
292 end;
293 except
294 on E: Exception do
295 begin
296 Console.WriteLine( E.Message );
297 Exit;
298 end;
299 end;
300
301 Console.Write('testException()');
302 try
303 client.testException('Xception');
304 except
305 on E: TXception do
306 begin
307 Console.WriteLine( ' = ' + IntToStr(E.ErrorCode) + ', ' + E.Message_ );
308 end;
309 end;
310
311 Console.Write('testVoid()');
312 client.testVoid();
313 Console.WriteLine(' = void');
314
315 Console.Write('testString(''Test'')');
316 s := client.testString('Test');
317 Console.WriteLine(' := ''' + s + '''');
318
319 Console.Write('testByte(1)');
320 i8 := client.testByte(1);
321 Console.WriteLine(' := ' + IntToStr( i8 ));
322
323 Console.Write('testI32(-1)');
324 i32 := client.testI32(-1);
325 Console.WriteLine(' := ' + IntToStr(i32));
326
327 Console.Write('testI64(-34359738368)');
328 i64 := client.testI64(-34359738368);
329 Console.WriteLine(' := ' + IntToStr( i64));
330
331 Console.Write('testDouble(5.325098235)');
332 dub := client.testDouble(5.325098235);
333 Console.WriteLine(' := ' + FloatToStr( dub));
334
335 Console.Write('testStruct({''Zero'', 1, -3, -5})');
336 o := TXtructImpl.Create;
337 o.String_thing := 'Zero';
338 o.Byte_thing := 1;
339 o.I32_thing := -3;
340 o.I64_thing := -5;
341 i := client.testStruct(o);
342 Console.WriteLine(' := {''' +
343 i.String_thing + ''', ' +
344 IntToStr( i.Byte_thing) + ', ' +
345 IntToStr( i.I32_thing) + ', ' +
346 IntToStr( i.I64_thing) + '}');
347
348 Console.Write('testNest({1, {''Zero'', 1, -3, -5}, 5})');
349 o2 := TXtruct2Impl.Create;
350 o2.Byte_thing := 1;
351 o2.Struct_thing := o;
352 o2.I32_thing := 5;
353 i2 := client.testNest(o2);
354 i := i2.Struct_thing;
355 Console.WriteLine(' := {' + IntToStr( i2.Byte_thing) + ', {''' +
356 i.String_thing + ''', ' +
357 IntToStr( i.Byte_thing) + ', ' +
358 IntToStr( i.I32_thing) + ', ' +
359 IntToStr( i.I64_thing) + '}, ' +
360 IntToStr( i2.I32_thing) + '}');
361
362
363 mapout := TThriftDictionaryImpl<Integer,Integer>.Create;
364
365 for j := 0 to 4 do
366 begin
367 mapout.AddOrSetValue( j, j - 10);
368 end;
369 Console.Write('testMap({');
370 first := True;
371 for key in mapout.Keys do
372 begin
373 if first then
374 begin
375 first := False;
376 end else
377 begin
378 Console.Write( ', ' );
379 end;
380 Console.Write( IntToStr( key) + ' => ' + IntToStr( mapout[key]));
381 end;
382 Console.Write('})');
383
384 mapin := client.testMap( mapout );
385 Console.Write(' = {');
386 first := True;
387 for key in mapin.Keys do
388 begin
389 if first then
390 begin
391 first := False;
392 end else
393 begin
394 Console.Write( ', ' );
395 end;
396 Console.Write( IntToStr( key) + ' => ' + IntToStr( mapin[key]));
397 end;
398 Console.WriteLine('}');
399
400 setout := THashSetImpl<Integer>.Create;
401 for j := -2 to 2 do
402 begin
403 setout.Add( j );
404 end;
405 Console.Write('testSet({');
406 first := True;
407 for j in setout do
408 begin
409 if first then
410 begin
411 first := False;
412 end else
413 begin
414 Console.Write(', ');
415 end;
416 Console.Write(IntToStr( j));
417 end;
418 Console.Write('})');
419
420 Console.Write(' = {');
421
422 first := True;
423 setin := client.testSet(setout);
424 for j in setin do
425 begin
426 if first then
427 begin
428 first := False;
429 end else
430 begin
431 Console.Write(', ');
432 end;
433 Console.Write(IntToStr( j));
434 end;
435 Console.WriteLine('}');
436
437 Console.Write('testEnum(ONE)');
438 ret := client.testEnum(TNumberz.ONE);
439 Console.WriteLine(' = ' + IntToStr( Integer( ret)));
440
441 Console.Write('testEnum(TWO)');
442 ret := client.testEnum(TNumberz.TWO);
443 Console.WriteLine(' = ' + IntToStr( Integer( ret)));
444
445 Console.Write('testEnum(THREE)');
446 ret := client.testEnum(TNumberz.THREE);
447 Console.WriteLine(' = ' + IntToStr( Integer( ret)));
448
449 Console.Write('testEnum(FIVE)');
450 ret := client.testEnum(TNumberz.FIVE);
451 Console.WriteLine(' = ' + IntToStr( Integer( ret)));
452
453 Console.Write('testEnum(EIGHT)');
454 ret := client.testEnum(TNumberz.EIGHT);
455 Console.WriteLine(' = ' + IntToStr( Integer( ret)));
456
457 Console.Write('testTypedef(309858235082523)');
458 uid := client.testTypedef(309858235082523);
459 Console.WriteLine(' = ' + IntToStr( uid));
460
461 Console.Write('testMapMap(1)');
462 mm := client.testMapMap(1);
463 Console.Write(' = {');
464 for key in mm.Keys do
465 begin
466 Console.Write( IntToStr( key) + ' => {');
467 m2 := mm[key];
468 for k2 in m2.Keys do
469 begin
470 Console.Write( IntToStr( k2) + ' => ' + IntToStr( m2[k2]) + ', ');
471 end;
472 Console.Write('}, ');
473 end;
474 Console.WriteLine('}');
475
476 insane := TInsanityImpl.Create;
477 insane.UserMap := TThriftDictionaryImpl<TNumberz, Int64>.Create;
478 insane.UserMap.AddOrSetValue( TNumberz.FIVE, 5000);
479 truck := TXtructImpl.Create;
480 truck.String_thing := 'Truck';
481 truck.Byte_thing := 8;
482 truck.I32_thing := 8;
483 truck.I64_thing := 8;
484 insane.Xtructs := TThriftListImpl<IXtruct>.Create;
485 insane.Xtructs.Add( truck );
486 Console.Write('testInsanity()');
487 whoa := client.testInsanity( insane );
488 Console.Write(' = {');
489 for key64 in whoa.Keys do
490 begin
491 val := whoa[key64];
492 Console.Write( IntToStr( key64) + ' => {');
493 for k2_2 in val.Keys do
494 begin
495 v2 := val[k2_2];
496 Console.Write( IntToStr( Integer( k2_2)) + ' => {');
497 userMap := v2.UserMap;
498 Console.Write('{');
499 if userMap <> nil then
500 begin
501 for k3 in userMap.Keys do
502 begin
503 Console.Write( IntToStr( Integer( k3)) + ' => ' + IntToStr( userMap[k3]) + ', ');
504 end;
505 end else
506 begin
507 Console.Write('null');
508 end;
509 Console.Write('}, ');
510 xtructs := v2.Xtructs;
511 Console.Write('{');
512
513 if xtructs <> nil then
514 begin
515 for x in xtructs do
516 begin
517 Console.Write('{"' + x.String_thing + '", ' +
518 IntToStr( x.Byte_thing) + ', ' +
519 IntToStr( x.I32_thing) + ', ' +
520 IntToStr( x.I32_thing) + '}, ');
521 end;
522 end else
523 begin
524 Console.Write('null');
525 end;
526 Console.Write('}');
527 Console.Write('}, ');
528 end;
529 Console.Write('}, ');
530 end;
531 Console.WriteLine('}');
532
533 arg0 := 1;
534 arg1 := 2;
535 arg2 := High(Int64);
536
537 multiDict := TThriftDictionaryImpl<SmallInt, string>.Create;
538 multiDict.AddOrSetValue( 1, 'one');
539
540 arg4 := TNumberz.FIVE;
541 arg5 := 5000000;
542 Console.WriteLine('Test Multi(' + IntToStr( arg0) + ',' +
543 IntToStr( arg1) + ',' + IntToStr( arg2) + ',' +
544 multiDict.ToString + ',' + IntToStr( Integer( arg4)) + ',' +
545 IntToStr( arg5) + ')');
546
547 Console.WriteLine('Test Oneway(1)');
548 client.testOneway(1);
549
550 Console.Write('Test Calltime()');
551 StartTick := GetTIckCount;
552
553 for k := 0 to 1000 - 1 do
554 begin
555 client.testVoid();
556 end;
557 Console.WriteLine(' = ' + FloatToStr( (GetTickCount - StartTick) / 1000 ) + ' ms a testVoid() call' );
558
559end;
560
Jake Farrell27274222011-11-10 20:32:44 +0000561
562procedure TClientThread.JSONProtocolReadWriteTest;
563// Tests only then read/write procedures of the JSON protocol
564// All tests succeed, if we can read what we wrote before
565// Note that passing this test does not imply, that our JSON is really compatible to what
566// other clients or servers expect as the real JSON. This is beyond the scope of this test.
567var prot : IProtocol;
568 stm : TStringStream;
569 list : IList;
570 binary, binRead : TBytes;
571 i,iErr : Integer;
572const
573 TEST_SHORT = ShortInt( $FE);
574 TEST_SMALL = SmallInt( $FEDC);
575 TEST_LONG = LongInt( $FEDCBA98);
576 TEST_I64 = Int64( $FEDCBA9876543210);
577 TEST_DOUBLE = -1.234e-56;
578 DELTA_DOUBLE = TEST_DOUBLE * 1e-14;
579 TEST_STRING = 'abc-'#$00E4#$00f6#$00fc; // german umlauts (en-us: "funny chars")
580begin
581 stm := TStringStream.Create;
582 try
583 // prepare binary data
584 SetLength( binary, $100);
585 for i := Low(binary) to High(binary) do binary[i] := i;
586
587 // output setup
588 prot := TJSONProtocolImpl.Create(
589 TStreamTransportImpl.Create(
590 nil, TThriftStreamAdapterDelphi.Create( stm, FALSE)));
591
592 // write
593 prot.WriteListBegin( TListImpl.Create( TType.String_, 9));
594 prot.WriteBool( TRUE);
595 prot.WriteBool( FALSE);
596 prot.WriteByte( TEST_SHORT);
597 prot.WriteI16( TEST_SMALL);
598 prot.WriteI32( TEST_LONG);
599 prot.WriteI64( TEST_I64);
600 prot.WriteDouble( TEST_DOUBLE);
601 prot.WriteString( TEST_STRING);
602 prot.WriteBinary( binary);
603 prot.WriteListEnd;
604
605 // input setup
606 Expect( stm.Position = stm.Size, 'Stream position/length after write');
607 stm.Position := 0;
608 prot := TJSONProtocolImpl.Create(
609 TStreamTransportImpl.Create(
610 TThriftStreamAdapterDelphi.Create( stm, FALSE), nil));
611
612 // read and compare
613 list := prot.ReadListBegin;
614 Expect( list.ElementType = TType.String_, 'list element type');
615 Expect( list.Count = 9, 'list element count');
616 Expect( prot.ReadBool, 'WriteBool/ReadBool: TRUE');
617 Expect( not prot.ReadBool, 'WriteBool/ReadBool: FALSE');
618 Expect( prot.ReadByte = TEST_SHORT, 'WriteByte/ReadByte');
619 Expect( prot.ReadI16 = TEST_SMALL, 'WriteI16/ReadI16');
620 Expect( prot.ReadI32 = TEST_LONG, 'WriteI32/ReadI32');
621 Expect( prot.ReadI64 = TEST_I64, 'WriteI64/ReadI64');
622 Expect( abs(prot.ReadDouble-TEST_DOUBLE) < abs(DELTA_DOUBLE), 'WriteDouble/ReadDouble');
623 Expect( prot.ReadString = TEST_STRING, 'WriteString/ReadString');
624 binRead := prot.ReadBinary;
625 prot.ReadListEnd;
626
627 // test binary data
628 Expect( Length(binary) = Length(binRead), 'Binary data length check');
629 iErr := -1;
630 for i := Low(binary) to High(binary) do begin
631 if binary[i] <> binRead[i] then begin
632 iErr := i;
633 Break;
634 end;
635 end;
636 if iErr < 0
637 then Expect( TRUE, 'Binary data check ('+IntToStr(Length(binary))+' Bytes)')
638 else Expect( FALSE, 'Binary data check at offset '+IntToStr(iErr));
639
640 Expect( stm.Position = stm.Size, 'Stream position after read');
641
642 finally
643 stm.Free;
644 prot := nil; //-> Release
645 end;
646end;
647
648
649procedure TClientThread.Expect( aTestResult : Boolean; const aTestInfo : string);
650begin
651 if aTestResult then begin
652 Inc(FSuccesses);
653 Console.WriteLine( aTestInfo+' = OK');
654 end
655 else begin
656 Inc(FErrors);
657 Console.WriteLine( aTestInfo+' = FAILED');
658 ASSERT( FALSE); // we have a failed test!
659 end;
660end;
661
662
663constructor TClientThread.Create(ATransport: ITransport; AProtocol : IProtocol; ANumIteration: Integer);
Jake Farrell7ae13e12011-10-18 14:35:26 +0000664begin
665 inherited Create( True );
666 FNumIteration := ANumIteration;
667 FTransport := ATransport;
Jake Farrell27274222011-11-10 20:32:44 +0000668 FProtocol := AProtocol;
Jake Farrell7ae13e12011-10-18 14:35:26 +0000669 FConsole := TThreadConsole.Create( Self );
670end;
671
672destructor TClientThread.Destroy;
673begin
674 FConsole.Free;
675 inherited;
676end;
677
678procedure TClientThread.Execute;
679var
680 i : Integer;
681 proc : TThreadProcedure;
682begin
683 for i := 0 to FNumIteration - 1 do
684 begin
685 ClientTest;
Jake Farrell27274222011-11-10 20:32:44 +0000686 JSONProtocolReadWriteTest;
Jake Farrell7ae13e12011-10-18 14:35:26 +0000687 end;
688
689 proc := procedure
690 begin
691 if FTransport <> nil then
692 begin
693 FTransport.Close;
694 FTransport := nil;
695 end;
696 end;
697
698 Synchronize( proc );
699end;
700
701{ TThreadConsole }
702
703constructor TThreadConsole.Create(AThread: TThread);
704begin
705 FThread := AThread;
706end;
707
708procedure TThreadConsole.Write(const S: string);
709var
710 proc : TThreadProcedure;
711begin
712 proc := procedure
713 begin
714 Console.Write( S );
715 end;
716 TThread.Synchronize( FThread, proc);
717end;
718
719procedure TThreadConsole.WriteLine(const S: string);
720var
721 proc : TThreadProcedure;
722begin
723 proc := procedure
724 begin
725 Console.WriteLine( S );
726 end;
727 TThread.Synchronize( FThread, proc);
728end;
729
730initialization
731begin
732 TTestClient.FNumIteration := 1;
733 TTestClient.FNumThread := 1;
734end;
735
736end.