blob: 7829d97d149299dd953af33a479066a8db756aec [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 Geyer3a172092024-11-19 01:14:32 +010044 test.ExceptionStruct,
Jens Geyer07f4bb52022-09-03 14:50:06 +020045 DebugProtoTest;
Jens Geyered994552019-11-09 23:24:52 +010046
Jens Geyer62445c12022-06-29 00:00:00 +020047{$TYPEINFO ON}
Jens Geyered994552019-11-09 23:24:52 +010048
49type
50 TFactoryPair = record
51 prot : IProtocolFactory;
52 trans : ITransportFactory;
53 end;
54
55 TTestSerializer = class //extends TestCase {
56 private type
57 TMethod = (
58 mt_Bytes,
59 mt_Stream
60 );
61
Jens Geyer07f4bb52022-09-03 14:50:06 +020062 strict private
Jens Geyered994552019-11-09 23:24:52 +010063 FProtocols : TList< TFactoryPair>;
64 procedure AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory);
65 class function UserFriendlyName( const factory : TFactoryPair) : string; overload;
66 class function UserFriendlyName( const method : TMethod) : string; overload;
67
68 class function Serialize(const input : IBase; const factory : TFactoryPair) : TBytes; overload;
69 class procedure Serialize(const input : IBase; const factory : TFactoryPair; const aStream : TStream); overload;
70
71 class procedure Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair); overload;
72 class procedure Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair); overload;
73
Jens Geyer41f47af2019-11-09 23:24:52 +010074 class procedure ValidateReadToEnd( const input : TBytes; const serial : TDeserializer); overload;
75 class procedure ValidateReadToEnd( const input : TStream; const serial : TDeserializer); overload;
76
Jens Geyer07f4bb52022-09-03 14:50:06 +020077 class function LengthOf( const bytes : TBytes) : Integer; overload; inline;
78 class function LengthOf( const bytes : IThriftBytes) : Integer; overload; inline;
79
80 class function DataPtrOf( const bytes : TBytes) : Pointer; overload; inline;
81 class function DataPtrOf( const bytes : IThriftBytes) : Pointer; overload; inline;
82
Jens Geyered994552019-11-09 23:24:52 +010083 procedure Test_Serializer_Deserializer;
Jens Geyer07f4bb52022-09-03 14:50:06 +020084 procedure Test_COM_Types;
Jens Geyer16819262024-03-07 23:01:20 +010085 procedure Test_ThriftBytesCTORs;
Jens Geyer3a172092024-11-19 01:14:32 +010086
87 procedure Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
88 procedure Test_CompactStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
89 procedure Test_ExceptionStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
Jens Geyered994552019-11-09 23:24:52 +010090
91 public
92 constructor Create;
93 destructor Destroy; override;
94
95 procedure RunTests;
96 end;
97
98
99implementation
100
Jens Geyer07f4bb52022-09-03 14:50:06 +0200101const SERIALIZERDATA_DLL = 'SerializerData.dll';
102function CreateOneOfEach : IOneOfEach; stdcall; external SERIALIZERDATA_DLL;
103function CreateNesting : INesting; stdcall; external SERIALIZERDATA_DLL;
104function CreateHolyMoley : IHolyMoley; stdcall; external SERIALIZERDATA_DLL;
105function CreateCompactProtoTestStruct : ICompactProtoTestStruct; stdcall; external SERIALIZERDATA_DLL;
Jens Geyer3a172092024-11-19 01:14:32 +0100106function CreateBatchGetResponse : IBatchGetResponse; stdcall; external SERIALIZERDATA_DLL;
Jens Geyer07f4bb52022-09-03 14:50:06 +0200107
Jens Geyered994552019-11-09 23:24:52 +0100108
109{ TTestSerializer }
110
111constructor TTestSerializer.Create;
112begin
113 inherited Create;
114 FProtocols := TList< TFactoryPair>.Create;
115
116 AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, nil);
117 AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, nil);
118 AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, nil);
119
120 AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create);
121 AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create);
122 AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create);
123
124 AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create);
125 AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create);
126 AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create);
127end;
128
129
130destructor TTestSerializer.Destroy;
131begin
132 try
133 FreeAndNil( FProtocols);
134 finally
135 inherited Destroy;
136 end;
137end;
138
139
140procedure TTestSerializer.AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory);
141var rec : TFactoryPair;
142begin
143 rec.prot := aProto;
144 rec.trans := aTrans;
145 FProtocols.Add( rec);
146end;
147
148
Jens Geyer07f4bb52022-09-03 14:50:06 +0200149class function TTestSerializer.LengthOf( const bytes : TBytes) : Integer;
150begin
151 result := Length(bytes);
152end;
153
154
155class function TTestSerializer.LengthOf( const bytes : IThriftBytes) : Integer;
156begin
157 if bytes <> nil
158 then result := bytes.Count
159 else result := 0;
160end;
161
162
163class function TTestSerializer.DataPtrOf( const bytes : TBytes) : Pointer;
164begin
165 result := bytes;
166end;
167
168
169class function TTestSerializer.DataPtrOf( const bytes : IThriftBytes) : Pointer;
170begin
171 if bytes <> nil
172 then result := bytes.QueryRawDataPtr
173 else result := nil;
174end;
175
176
Jens Geyered994552019-11-09 23:24:52 +0100177procedure TTestSerializer.Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
178var tested, correct : IOneOfEach;
179 bytes : TBytes;
180 i : Integer;
181begin
182 // write
Jens Geyer07f4bb52022-09-03 14:50:06 +0200183 tested := CreateOneOfEach;
Jens Geyered994552019-11-09 23:24:52 +0100184 case method of
185 mt_Bytes: bytes := Serialize( tested, factory);
186 mt_Stream: begin
187 stream.Size := 0;
188 Serialize( tested, factory, stream);
189 end
190 else
191 ASSERT( FALSE);
192 end;
193
194 // init + read
195 tested := TOneOfEachImpl.Create;
196 case method of
197 mt_Bytes: Deserialize( bytes, tested, factory);
198 mt_Stream: begin
199 stream.Position := 0;
200 Deserialize( stream, tested, factory);
201 end
202 else
203 ASSERT( FALSE);
204 end;
205
206 // check
Jens Geyer07f4bb52022-09-03 14:50:06 +0200207 correct := CreateOneOfEach;
Jens Geyered994552019-11-09 23:24:52 +0100208 ASSERT( tested.Im_true = correct.Im_true);
209 ASSERT( tested.Im_false = correct.Im_false);
210 ASSERT( tested.A_bite = correct.A_bite);
211 ASSERT( tested.Integer16 = correct.Integer16);
212 ASSERT( tested.Integer32 = correct.Integer32);
213 ASSERT( tested.Integer64 = correct.Integer64);
214 ASSERT( Abs( tested.Double_precision - correct.Double_precision) < 1E-12);
215 ASSERT( tested.Some_characters = correct.Some_characters);
216 ASSERT( tested.Zomg_unicode = correct.Zomg_unicode);
Jens Geyer62445c12022-06-29 00:00:00 +0200217 ASSERT( tested.Rfc4122_uuid = correct.Rfc4122_uuid);
Jens Geyered994552019-11-09 23:24:52 +0100218 ASSERT( tested.What_who = correct.What_who);
219
Jens Geyer07f4bb52022-09-03 14:50:06 +0200220 ASSERT( LengthOf(tested.Base64) = LengthOf(correct.Base64));
221 ASSERT( CompareMem( DataPtrOf(tested.Base64), DataPtrOf(correct.Base64), LengthOf(correct.Base64)));
Jens Geyered994552019-11-09 23:24:52 +0100222
223 ASSERT( tested.Byte_list.Count = correct.Byte_list.Count);
224 for i := 0 to tested.Byte_list.Count-1
225 do ASSERT( tested.Byte_list[i] = correct.Byte_list[i]);
226
227 ASSERT( tested.I16_list.Count = correct.I16_list.Count);
228 for i := 0 to tested.I16_list.Count-1
229 do ASSERT( tested.I16_list[i] = correct.I16_list[i]);
230
231 ASSERT( tested.I64_list.Count = correct.I64_list.Count);
232 for i := 0 to tested.I64_list.Count-1
233 do ASSERT( tested.I64_list[i] = correct.I64_list[i]);
234end;
235
236
237procedure TTestSerializer.Test_CompactStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
238var tested, correct : ICompactProtoTestStruct;
239 bytes : TBytes;
240begin
241 // write
Jens Geyer07f4bb52022-09-03 14:50:06 +0200242 tested := CreateCompactProtoTestStruct;
Jens Geyered994552019-11-09 23:24:52 +0100243 case method of
244 mt_Bytes: bytes := Serialize( tested, factory);
245 mt_Stream: begin
246 stream.Size := 0;
247 Serialize( tested, factory, stream);
248 end
249 else
250 ASSERT( FALSE);
251 end;
252
253 // init + read
254 correct := TCompactProtoTestStructImpl.Create;
255 case method of
256 mt_Bytes: Deserialize( bytes, tested, factory);
257 mt_Stream: begin
258 stream.Position := 0;
259 Deserialize( stream, tested, factory);
260 end
261 else
262 ASSERT( FALSE);
263 end;
264
265 // check
Jens Geyer07f4bb52022-09-03 14:50:06 +0200266 correct := CreateCompactProtoTestStruct;
Jens Geyered994552019-11-09 23:24:52 +0100267 ASSERT( correct.Field500 = tested.Field500);
268 ASSERT( correct.Field5000 = tested.Field5000);
269 ASSERT( correct.Field20000 = tested.Field20000);
270end;
271
272
Jens Geyer3a172092024-11-19 01:14:32 +0100273procedure TTestSerializer.Test_ExceptionStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
274var tested, correct : IBatchGetResponse;
275 bytes : TBytes;
276begin
277 // write
278 tested := CreateBatchGetResponse;
279 case method of
280 mt_Bytes: bytes := Serialize( tested, factory);
281 mt_Stream: begin
282 stream.Size := 0;
283 Serialize( tested, factory, stream);
284 end
285 else
286 ASSERT( FALSE);
287 end;
288
289 // init + read
290 correct := TBatchGetResponseImpl.Create;
291 case method of
292 mt_Bytes: Deserialize( bytes, tested, factory);
293 mt_Stream: begin
294 stream.Position := 0;
295 Deserialize( stream, tested, factory);
296 end
297 else
298 ASSERT( FALSE);
299 end;
300
301 // check
302 correct := CreateCompactProtoTestStruct;
303 ASSERT( correct.Field500 = tested.Field500);
304 ASSERT( correct.Field5000 = tested.Field5000);
305 ASSERT( correct.Field20000 = tested.Field20000);
306end;
307
308
Jens Geyered994552019-11-09 23:24:52 +0100309procedure TTestSerializer.Test_Serializer_Deserializer;
310var factory : TFactoryPair;
311 stream : TFileStream;
312 method : TMethod;
313begin
314 stream := TFileStream.Create( 'TestSerializer.dat', fmCreate);
315 try
316 for method in [Low(TMethod)..High(TMethod)] do begin
317 Writeln( UserFriendlyName(method));
318
319 for factory in FProtocols do begin
320 Writeln('- '+UserFriendlyName(factory));
321
Jens Geyer3a172092024-11-19 01:14:32 +0100322 Test_OneOfEach( method, factory, stream);
323 Test_CompactStruct( method, factory, stream);
324 Test_ExceptionStruct( method, factory, stream);
Jens Geyered994552019-11-09 23:24:52 +0100325 end;
326
327 Writeln;
328 end;
329
330 finally
331 stream.Free;
332 end;
333end;
334
335
336class function TTestSerializer.UserFriendlyName( const factory : TFactoryPair) : string;
337begin
338 result := Copy( (factory.prot as TObject).ClassName, 2, MAXINT);
339
340 if factory.trans <> nil
341 then result := Copy( (factory.trans as TObject).ClassName, 2, MAXINT) +' '+ result;
342
343 result := StringReplace( result, 'Impl', '', [rfReplaceAll]);
344 result := StringReplace( result, 'Transport.TFactory', '', [rfReplaceAll]);
345 result := StringReplace( result, 'Protocol.TFactory', '', [rfReplaceAll]);
346end;
347
348
349class function TTestSerializer.UserFriendlyName( const method : TMethod) : string;
Jens Geyer62445c12022-06-29 00:00:00 +0200350const NAMES : array[TMethod] of string = ('TBytes','Stream');
Jens Geyered994552019-11-09 23:24:52 +0100351begin
Jens Geyer62445c12022-06-29 00:00:00 +0200352 result := NAMES[method];
Jens Geyered994552019-11-09 23:24:52 +0100353end;
354
355
Jens Geyer07f4bb52022-09-03 14:50:06 +0200356procedure TTestSerializer.Test_COM_Types;
357var tested : IOneOfEach;
358begin
359 {$IF cDebugProtoTest_Option_COM_types}
360 ASSERT( SizeOf(TSomeEnum) = SizeOf(Int32)); // -> MINENUMSIZE 4
361
362 // try to set values that allocate memory
363 tested := CreateOneOfEach;
364 tested.Zomg_unicode := 'This is a test';
365 tested.Base64 := TThriftBytesImpl.Create( TEncoding.UTF8.GetBytes('abc'));
366 {$IFEND}
367end;
368
369
Jens Geyer16819262024-03-07 23:01:20 +0100370procedure TTestSerializer.Test_ThriftBytesCTORs;
371var one, two : IThriftBytes;
372 bytes : TBytes;
373 sAscii : AnsiString;
374begin
375 sAscii := 'ABC/xzy';
Jens Geyerb53fa8e2024-03-08 00:33:22 +0100376 bytes := TEncoding.ASCII.GetBytes(string(sAscii));
Jens Geyer16819262024-03-07 23:01:20 +0100377
378 one := TThriftBytesImpl.Create( PAnsiChar(sAscii), Length(sAscii));
379 two := TThriftBytesImpl.Create( bytes, TRUE);
380
381 ASSERT( one.Count = two.Count);
382 ASSERT( CompareMem( one.QueryRawDataPtr, two.QueryRawDataPtr, one.Count));
383end;
384
385
Jens Geyered994552019-11-09 23:24:52 +0100386procedure TTestSerializer.RunTests;
387begin
388 try
389 Test_Serializer_Deserializer;
Jens Geyer07f4bb52022-09-03 14:50:06 +0200390 Test_COM_Types;
Jens Geyer16819262024-03-07 23:01:20 +0100391 Test_ThriftBytesCTORs;
Jens Geyer3a172092024-11-19 01:14:32 +0100392 Test_ExceptionStructs;
Jens Geyered994552019-11-09 23:24:52 +0100393 except
394 on e:Exception do begin
395 Writeln( e.ClassName+': '+ e.Message);
396 Write('Hit ENTER to close ... '); Readln;
397 end;
398 end;
399end;
400
401
402class function TTestSerializer.Serialize(const input : IBase; const factory : TFactoryPair) : TBytes;
403var serial : TSerializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100404 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100405begin
Jens Geyera019cda2019-11-09 23:24:52 +0100406 config := TThriftConfigurationImpl.Create;
Jens Geyer62445c12022-06-29 00:00:00 +0200407 //config.MaxMessageSize := 0; // we don't read anything here
Jens Geyera019cda2019-11-09 23:24:52 +0100408
409 serial := TSerializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100410 try
411 result := serial.Serialize( input);
412 finally
413 serial.Free;
414 end;
415end;
416
417
418class procedure TTestSerializer.Serialize(const input : IBase; const factory : TFactoryPair; const aStream : TStream);
419var serial : TSerializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100420 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100421begin
Jens Geyera019cda2019-11-09 23:24:52 +0100422 config := TThriftConfigurationImpl.Create;
Jens Geyer62445c12022-06-29 00:00:00 +0200423 //config.MaxMessageSize := 0; // we don't read anything here
Jens Geyera019cda2019-11-09 23:24:52 +0100424
425 serial := TSerializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100426 try
427 serial.Serialize( input, aStream);
428 finally
429 serial.Free;
430 end;
431end;
432
433
434class procedure TTestSerializer.Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair);
435var serial : TDeserializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100436 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100437begin
Jens Geyera019cda2019-11-09 23:24:52 +0100438 config := TThriftConfigurationImpl.Create;
439 config.MaxMessageSize := Length(input);
440
441 serial := TDeserializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100442 try
443 serial.Deserialize( input, target);
Jens Geyer41f47af2019-11-09 23:24:52 +0100444 ValidateReadToEnd( input, serial);
Jens Geyered994552019-11-09 23:24:52 +0100445 finally
446 serial.Free;
447 end;
448end;
449
450
451class procedure TTestSerializer.Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair);
452var serial : TDeserializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100453 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100454begin
Jens Geyera019cda2019-11-09 23:24:52 +0100455 config := TThriftConfigurationImpl.Create;
456 config.MaxMessageSize := input.Size;
457
458 serial := TDeserializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100459 try
460 serial.Deserialize( input, target);
Jens Geyer41f47af2019-11-09 23:24:52 +0100461 ValidateReadToEnd( input, serial);
Jens Geyered994552019-11-09 23:24:52 +0100462 finally
463 serial.Free;
464 end;
465end;
466
467
Jens Geyer41f47af2019-11-09 23:24:52 +0100468class procedure TTestSerializer.ValidateReadToEnd( const input : TBytes; const serial : TDeserializer);
469// we should not have any more byte to read
470var dummy : IBase;
471begin
472 try
473 dummy := TOneOfEachImpl.Create;
474 serial.Deserialize( input, dummy);
475 raise EInOutError.Create('Expected exception not thrown?');
476 except
477 on e:TTransportExceptionEndOfFile do {expected};
478 on e:Exception do raise; // unexpected
479 end;
480end;
481
482
483class procedure TTestSerializer.ValidateReadToEnd( const input : TStream; const serial : TDeserializer);
484// we should not have any more byte to read
485var dummy : IBase;
486begin
487 try
488 input.Position := 0;
489 dummy := TOneOfEachImpl.Create;
490 serial.Deserialize( input, dummy);
491 raise EInOutError.Create('Expected exception not thrown?');
492 except
493 on e:TTransportExceptionEndOfFile do {expected};
494 on e:Exception do raise; // unexpected
495 end;
496end;
497
Jens Geyered994552019-11-09 23:24:52 +0100498end.