blob: 466fb269efcbfeb6175f961f33324cd4d4847e75 [file] [log] [blame]
Jens Geyered994552019-11-09 23:24:52 +01001unit TestSerializer.Tests;
2(*
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *)
20
21interface
22
23uses
24 Classes,
25 Windows,
26 SysUtils,
27 Generics.Collections,
28 Thrift,
29 Thrift.Exception,
30 Thrift.Socket,
31 Thrift.Transport,
32 Thrift.Protocol,
33 Thrift.Protocol.JSON,
34 Thrift.Protocol.Compact,
35 Thrift.Collections,
Jens Geyera019cda2019-11-09 23:24:52 +010036 Thrift.Configuration,
Jens Geyered994552019-11-09 23:24:52 +010037 Thrift.Server,
38 Thrift.Utils,
39 Thrift.Serializer,
40 Thrift.Stream,
41 Thrift.WinHTTP,
42 Thrift.TypeRegistry,
43 System_,
Jens Geyere9f63e02024-11-23 01:01:13 +010044 test.ExceptionStruct,
45 test.SimpleException,
Jens Geyer07f4bb52022-09-03 14:50:06 +020046 DebugProtoTest;
Jens Geyered994552019-11-09 23:24:52 +010047
Jens Geyer62445c12022-06-29 00:00:00 +020048{$TYPEINFO ON}
Jens Geyered994552019-11-09 23:24:52 +010049
50type
51 TFactoryPair = record
52 prot : IProtocolFactory;
53 trans : ITransportFactory;
54 end;
55
56 TTestSerializer = class //extends TestCase {
57 private type
58 TMethod = (
59 mt_Bytes,
60 mt_Stream
61 );
62
Jens Geyer07f4bb52022-09-03 14:50:06 +020063 strict private
Jens Geyered994552019-11-09 23:24:52 +010064 FProtocols : TList< TFactoryPair>;
65 procedure AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory);
66 class function UserFriendlyName( const factory : TFactoryPair) : string; overload;
67 class function UserFriendlyName( const method : TMethod) : string; overload;
68
69 class function Serialize(const input : IBase; const factory : TFactoryPair) : TBytes; overload;
70 class procedure Serialize(const input : IBase; const factory : TFactoryPair; const aStream : TStream); overload;
71
72 class procedure Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair); overload;
73 class procedure Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair); overload;
74
Jens Geyer41f47af2019-11-09 23:24:52 +010075 class procedure ValidateReadToEnd( const input : TBytes; const serial : TDeserializer); overload;
76 class procedure ValidateReadToEnd( const input : TStream; const serial : TDeserializer); overload;
77
Jens Geyer07f4bb52022-09-03 14:50:06 +020078 class function LengthOf( const bytes : TBytes) : Integer; overload; inline;
79 class function LengthOf( const bytes : IThriftBytes) : Integer; overload; inline;
80
81 class function DataPtrOf( const bytes : TBytes) : Pointer; overload; inline;
82 class function DataPtrOf( const bytes : IThriftBytes) : Pointer; overload; inline;
83
Jens Geyered994552019-11-09 23:24:52 +010084 procedure Test_Serializer_Deserializer;
Jens Geyer07f4bb52022-09-03 14:50:06 +020085 procedure Test_COM_Types;
Jens Geyer16819262024-03-07 23:01:20 +010086 procedure Test_ThriftBytesCTORs;
Jens Geyere9f63e02024-11-23 01:01:13 +010087
88 procedure Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
89 procedure Test_CompactStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
90 procedure Test_ExceptionStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
91 procedure Test_SimpleException( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
Jens Geyered994552019-11-09 23:24:52 +010092
93 public
94 constructor Create;
95 destructor Destroy; override;
96
97 procedure RunTests;
98 end;
99
100
101implementation
102
Jens Geyer07f4bb52022-09-03 14:50:06 +0200103const SERIALIZERDATA_DLL = 'SerializerData.dll';
104function CreateOneOfEach : IOneOfEach; stdcall; external SERIALIZERDATA_DLL;
105function CreateNesting : INesting; stdcall; external SERIALIZERDATA_DLL;
106function CreateHolyMoley : IHolyMoley; stdcall; external SERIALIZERDATA_DLL;
107function CreateCompactProtoTestStruct : ICompactProtoTestStruct; stdcall; external SERIALIZERDATA_DLL;
Jens Geyere9f63e02024-11-23 01:01:13 +0100108function CreateBatchGetResponse : IBatchGetResponse; stdcall; external SERIALIZERDATA_DLL;
109function CreateSimpleException : IError; stdcall; external SERIALIZERDATA_DLL;
Jens Geyer07f4bb52022-09-03 14:50:06 +0200110
Jens Geyered994552019-11-09 23:24:52 +0100111
112{ TTestSerializer }
113
114constructor TTestSerializer.Create;
115begin
116 inherited Create;
117 FProtocols := TList< TFactoryPair>.Create;
118
119 AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, nil);
120 AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, nil);
121 AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, nil);
122
123 AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create);
124 AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create);
125 AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create);
126
127 AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create);
128 AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create);
129 AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create);
130end;
131
132
133destructor TTestSerializer.Destroy;
134begin
135 try
136 FreeAndNil( FProtocols);
137 finally
138 inherited Destroy;
139 end;
140end;
141
142
143procedure TTestSerializer.AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory);
144var rec : TFactoryPair;
145begin
146 rec.prot := aProto;
147 rec.trans := aTrans;
148 FProtocols.Add( rec);
149end;
150
151
Jens Geyer07f4bb52022-09-03 14:50:06 +0200152class function TTestSerializer.LengthOf( const bytes : TBytes) : Integer;
153begin
154 result := Length(bytes);
155end;
156
157
158class function TTestSerializer.LengthOf( const bytes : IThriftBytes) : Integer;
159begin
160 if bytes <> nil
161 then result := bytes.Count
162 else result := 0;
163end;
164
165
166class function TTestSerializer.DataPtrOf( const bytes : TBytes) : Pointer;
167begin
168 result := bytes;
169end;
170
171
172class function TTestSerializer.DataPtrOf( const bytes : IThriftBytes) : Pointer;
173begin
174 if bytes <> nil
175 then result := bytes.QueryRawDataPtr
176 else result := nil;
177end;
178
179
Jens Geyered994552019-11-09 23:24:52 +0100180procedure TTestSerializer.Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
181var tested, correct : IOneOfEach;
182 bytes : TBytes;
183 i : Integer;
184begin
185 // write
Jens Geyer07f4bb52022-09-03 14:50:06 +0200186 tested := CreateOneOfEach;
Jens Geyered994552019-11-09 23:24:52 +0100187 case method of
188 mt_Bytes: bytes := Serialize( tested, factory);
189 mt_Stream: begin
190 stream.Size := 0;
191 Serialize( tested, factory, stream);
192 end
193 else
194 ASSERT( FALSE);
195 end;
196
197 // init + read
198 tested := TOneOfEachImpl.Create;
199 case method of
200 mt_Bytes: Deserialize( bytes, tested, factory);
201 mt_Stream: begin
202 stream.Position := 0;
203 Deserialize( stream, tested, factory);
204 end
205 else
206 ASSERT( FALSE);
207 end;
208
209 // check
Jens Geyer07f4bb52022-09-03 14:50:06 +0200210 correct := CreateOneOfEach;
Jens Geyered994552019-11-09 23:24:52 +0100211 ASSERT( tested.Im_true = correct.Im_true);
212 ASSERT( tested.Im_false = correct.Im_false);
213 ASSERT( tested.A_bite = correct.A_bite);
214 ASSERT( tested.Integer16 = correct.Integer16);
215 ASSERT( tested.Integer32 = correct.Integer32);
216 ASSERT( tested.Integer64 = correct.Integer64);
217 ASSERT( Abs( tested.Double_precision - correct.Double_precision) < 1E-12);
218 ASSERT( tested.Some_characters = correct.Some_characters);
219 ASSERT( tested.Zomg_unicode = correct.Zomg_unicode);
Jens Geyer62445c12022-06-29 00:00:00 +0200220 ASSERT( tested.Rfc4122_uuid = correct.Rfc4122_uuid);
Jens Geyered994552019-11-09 23:24:52 +0100221 ASSERT( tested.What_who = correct.What_who);
222
Jens Geyer07f4bb52022-09-03 14:50:06 +0200223 ASSERT( LengthOf(tested.Base64) = LengthOf(correct.Base64));
224 ASSERT( CompareMem( DataPtrOf(tested.Base64), DataPtrOf(correct.Base64), LengthOf(correct.Base64)));
Jens Geyered994552019-11-09 23:24:52 +0100225
226 ASSERT( tested.Byte_list.Count = correct.Byte_list.Count);
227 for i := 0 to tested.Byte_list.Count-1
228 do ASSERT( tested.Byte_list[i] = correct.Byte_list[i]);
229
230 ASSERT( tested.I16_list.Count = correct.I16_list.Count);
231 for i := 0 to tested.I16_list.Count-1
232 do ASSERT( tested.I16_list[i] = correct.I16_list[i]);
233
234 ASSERT( tested.I64_list.Count = correct.I64_list.Count);
235 for i := 0 to tested.I64_list.Count-1
236 do ASSERT( tested.I64_list[i] = correct.I64_list[i]);
237end;
238
239
240procedure TTestSerializer.Test_CompactStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
241var tested, correct : ICompactProtoTestStruct;
242 bytes : TBytes;
243begin
244 // write
Jens Geyer07f4bb52022-09-03 14:50:06 +0200245 tested := CreateCompactProtoTestStruct;
Jens Geyered994552019-11-09 23:24:52 +0100246 case method of
247 mt_Bytes: bytes := Serialize( tested, factory);
248 mt_Stream: begin
249 stream.Size := 0;
250 Serialize( tested, factory, stream);
251 end
252 else
253 ASSERT( FALSE);
254 end;
255
256 // init + read
257 correct := TCompactProtoTestStructImpl.Create;
258 case method of
259 mt_Bytes: Deserialize( bytes, tested, factory);
260 mt_Stream: begin
261 stream.Position := 0;
262 Deserialize( stream, tested, factory);
263 end
264 else
265 ASSERT( FALSE);
266 end;
267
268 // check
Jens Geyer07f4bb52022-09-03 14:50:06 +0200269 correct := CreateCompactProtoTestStruct;
Jens Geyered994552019-11-09 23:24:52 +0100270 ASSERT( correct.Field500 = tested.Field500);
271 ASSERT( correct.Field5000 = tested.Field5000);
272 ASSERT( correct.Field20000 = tested.Field20000);
273end;
274
275
Jens Geyere9f63e02024-11-23 01:01:13 +0100276procedure TTestSerializer.Test_ExceptionStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
277var tested, correct : IBatchGetResponse;
278 bytes : TBytes;
279 corrDP, testDP : TPair<WideString, IGetRequest>;
280 corrEP, testEP : TPair<WideString, ISomeException>;
281begin
282 // write
283 tested := CreateBatchGetResponse;
284 case method of
285 mt_Bytes: bytes := Serialize( tested, factory);
286 mt_Stream: begin
287 stream.Size := 0;
288 Serialize( tested, factory, stream);
289 end
290 else
291 ASSERT( FALSE);
292 end;
293
294 // init + read
295 correct := TBatchGetResponseImpl.Create;
296 case method of
297 mt_Bytes: Deserialize( bytes, tested, factory);
298 mt_Stream: begin
299 stream.Position := 0;
300 Deserialize( stream, tested, factory);
301 end
302 else
303 ASSERT( FALSE);
304 end;
305
306 // check
307 correct := CreateBatchGetResponse;
308
309 // rewrite the following test if not
310 ASSERT( tested.Responses.Count = 1);
311 ASSERT( correct.Responses.Count = tested.Responses.Count);
312 for corrDP in correct.Responses do begin
313 for testDP in tested.Responses do begin
314 ASSERT( corrDP.Key = testDP.Key);
315 ASSERT( corrDP.Value.Id = testDP.Value.Id);
316 ASSERT( corrDP.Value.Data.Count = testDP.Value.Data.Count);
317 end;
318 end;
319
320 // rewrite the following test if not
321 ASSERT( tested.Errors.Count = 1);
322 ASSERT( correct.Errors.Count = tested.Errors.Count);
323 for corrEP in correct.Errors do begin
324 for testEP in tested.Errors do begin
325 ASSERT( corrEP.Key = testEP.Key);
326 ASSERT( corrEP.Value.Error = testEP.Value.Error);
327 end;
328 end;
329end;
330
331
332procedure TTestSerializer.Test_SimpleException( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
333var tested, correct : IError;
334 bytes : TBytes;
335begin
336 // write
337 tested := CreateSimpleException;
338 case method of
339 mt_Bytes: bytes := Serialize( tested, factory);
340 mt_Stream: begin
341 stream.Size := 0;
342 Serialize( tested, factory, stream);
343 end
344 else
345 ASSERT( FALSE);
346 end;
347
348 // init + read
349 correct := TErrorImpl.Create;
350 case method of
351 mt_Bytes: Deserialize( bytes, tested, factory);
352 mt_Stream: begin
353 stream.Position := 0;
354 Deserialize( stream, tested, factory);
355 end
356 else
357 ASSERT( FALSE);
358 end;
359
360 // check
361 correct := CreateSimpleException;
362 while correct <> nil do begin
363 // validate
364 ASSERT( correct.ErrorCode = tested.ErrorCode);
365 ASSERT( IsEqualGUID( correct.ExceptionData, tested.ExceptionData));
366
367 // iterate
368 correct := correct.InnerException;
369 tested := tested.InnerException;
370 ASSERT( (tested <> nil) xor (correct = nil)); // both or none
371 end;
372end;
373
374
Jens Geyered994552019-11-09 23:24:52 +0100375procedure TTestSerializer.Test_Serializer_Deserializer;
376var factory : TFactoryPair;
377 stream : TFileStream;
378 method : TMethod;
379begin
380 stream := TFileStream.Create( 'TestSerializer.dat', fmCreate);
381 try
382 for method in [Low(TMethod)..High(TMethod)] do begin
383 Writeln( UserFriendlyName(method));
384
385 for factory in FProtocols do begin
386 Writeln('- '+UserFriendlyName(factory));
387
Jens Geyere9f63e02024-11-23 01:01:13 +0100388 Test_OneOfEach( method, factory, stream);
389 Test_CompactStruct( method, factory, stream);
390 Test_ExceptionStruct( method, factory, stream);
391 Test_SimpleException( method, factory, stream);
Jens Geyered994552019-11-09 23:24:52 +0100392 end;
393
394 Writeln;
395 end;
396
397 finally
398 stream.Free;
399 end;
400end;
401
402
403class function TTestSerializer.UserFriendlyName( const factory : TFactoryPair) : string;
404begin
405 result := Copy( (factory.prot as TObject).ClassName, 2, MAXINT);
406
407 if factory.trans <> nil
408 then result := Copy( (factory.trans as TObject).ClassName, 2, MAXINT) +' '+ result;
409
410 result := StringReplace( result, 'Impl', '', [rfReplaceAll]);
411 result := StringReplace( result, 'Transport.TFactory', '', [rfReplaceAll]);
412 result := StringReplace( result, 'Protocol.TFactory', '', [rfReplaceAll]);
413end;
414
415
416class function TTestSerializer.UserFriendlyName( const method : TMethod) : string;
Jens Geyer62445c12022-06-29 00:00:00 +0200417const NAMES : array[TMethod] of string = ('TBytes','Stream');
Jens Geyered994552019-11-09 23:24:52 +0100418begin
Jens Geyer62445c12022-06-29 00:00:00 +0200419 result := NAMES[method];
Jens Geyered994552019-11-09 23:24:52 +0100420end;
421
422
Jens Geyer07f4bb52022-09-03 14:50:06 +0200423procedure TTestSerializer.Test_COM_Types;
424var tested : IOneOfEach;
425begin
426 {$IF cDebugProtoTest_Option_COM_types}
427 ASSERT( SizeOf(TSomeEnum) = SizeOf(Int32)); // -> MINENUMSIZE 4
428
429 // try to set values that allocate memory
430 tested := CreateOneOfEach;
431 tested.Zomg_unicode := 'This is a test';
432 tested.Base64 := TThriftBytesImpl.Create( TEncoding.UTF8.GetBytes('abc'));
433 {$IFEND}
434end;
435
436
Jens Geyer16819262024-03-07 23:01:20 +0100437procedure TTestSerializer.Test_ThriftBytesCTORs;
438var one, two : IThriftBytes;
439 bytes : TBytes;
440 sAscii : AnsiString;
441begin
442 sAscii := 'ABC/xzy';
Jens Geyerb53fa8e2024-03-08 00:33:22 +0100443 bytes := TEncoding.ASCII.GetBytes(string(sAscii));
Jens Geyer16819262024-03-07 23:01:20 +0100444
445 one := TThriftBytesImpl.Create( PAnsiChar(sAscii), Length(sAscii));
446 two := TThriftBytesImpl.Create( bytes, TRUE);
447
448 ASSERT( one.Count = two.Count);
449 ASSERT( CompareMem( one.QueryRawDataPtr, two.QueryRawDataPtr, one.Count));
450end;
451
452
Jens Geyered994552019-11-09 23:24:52 +0100453procedure TTestSerializer.RunTests;
454begin
455 try
456 Test_Serializer_Deserializer;
Jens Geyer07f4bb52022-09-03 14:50:06 +0200457 Test_COM_Types;
Jens Geyer16819262024-03-07 23:01:20 +0100458 Test_ThriftBytesCTORs;
Jens Geyered994552019-11-09 23:24:52 +0100459 except
460 on e:Exception do begin
461 Writeln( e.ClassName+': '+ e.Message);
462 Write('Hit ENTER to close ... '); Readln;
463 end;
464 end;
465end;
466
467
468class function TTestSerializer.Serialize(const input : IBase; const factory : TFactoryPair) : TBytes;
469var serial : TSerializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100470 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100471begin
Jens Geyera019cda2019-11-09 23:24:52 +0100472 config := TThriftConfigurationImpl.Create;
Jens Geyer62445c12022-06-29 00:00:00 +0200473 //config.MaxMessageSize := 0; // we don't read anything here
Jens Geyera019cda2019-11-09 23:24:52 +0100474
475 serial := TSerializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100476 try
477 result := serial.Serialize( input);
478 finally
479 serial.Free;
480 end;
481end;
482
483
484class procedure TTestSerializer.Serialize(const input : IBase; const factory : TFactoryPair; const aStream : TStream);
485var serial : TSerializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100486 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100487begin
Jens Geyera019cda2019-11-09 23:24:52 +0100488 config := TThriftConfigurationImpl.Create;
Jens Geyer62445c12022-06-29 00:00:00 +0200489 //config.MaxMessageSize := 0; // we don't read anything here
Jens Geyera019cda2019-11-09 23:24:52 +0100490
491 serial := TSerializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100492 try
493 serial.Serialize( input, aStream);
494 finally
495 serial.Free;
496 end;
497end;
498
499
500class procedure TTestSerializer.Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair);
501var serial : TDeserializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100502 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100503begin
Jens Geyera019cda2019-11-09 23:24:52 +0100504 config := TThriftConfigurationImpl.Create;
505 config.MaxMessageSize := Length(input);
506
507 serial := TDeserializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100508 try
509 serial.Deserialize( input, target);
Jens Geyer41f47af2019-11-09 23:24:52 +0100510 ValidateReadToEnd( input, serial);
Jens Geyered994552019-11-09 23:24:52 +0100511 finally
512 serial.Free;
513 end;
514end;
515
516
517class procedure TTestSerializer.Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair);
518var serial : TDeserializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100519 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100520begin
Jens Geyera019cda2019-11-09 23:24:52 +0100521 config := TThriftConfigurationImpl.Create;
522 config.MaxMessageSize := input.Size;
523
524 serial := TDeserializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100525 try
526 serial.Deserialize( input, target);
Jens Geyer41f47af2019-11-09 23:24:52 +0100527 ValidateReadToEnd( input, serial);
Jens Geyered994552019-11-09 23:24:52 +0100528 finally
529 serial.Free;
530 end;
531end;
532
533
Jens Geyer41f47af2019-11-09 23:24:52 +0100534class procedure TTestSerializer.ValidateReadToEnd( const input : TBytes; const serial : TDeserializer);
535// we should not have any more byte to read
536var dummy : IBase;
537begin
538 try
539 dummy := TOneOfEachImpl.Create;
540 serial.Deserialize( input, dummy);
541 raise EInOutError.Create('Expected exception not thrown?');
542 except
543 on e:TTransportExceptionEndOfFile do {expected};
544 on e:Exception do raise; // unexpected
545 end;
546end;
547
548
549class procedure TTestSerializer.ValidateReadToEnd( const input : TStream; const serial : TDeserializer);
550// we should not have any more byte to read
551var dummy : IBase;
552begin
553 try
554 input.Position := 0;
555 dummy := TOneOfEachImpl.Create;
556 serial.Deserialize( input, dummy);
557 raise EInOutError.Create('Expected exception not thrown?');
558 except
559 on e:TTransportExceptionEndOfFile do {expected};
560 on e:Exception do raise; // unexpected
561 end;
562end;
563
Jens Geyered994552019-11-09 23:24:52 +0100564end.