blob: e6a309e68617e959eca0e8249bdb2ab14c618c2e [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 Geyer07f4bb52022-09-03 14:50:06 +020044 DebugProtoTest;
Jens Geyered994552019-11-09 23:24:52 +010045
Jens Geyer62445c12022-06-29 00:00:00 +020046{$TYPEINFO ON}
Jens Geyered994552019-11-09 23:24:52 +010047
48type
49 TFactoryPair = record
50 prot : IProtocolFactory;
51 trans : ITransportFactory;
52 end;
53
54 TTestSerializer = class //extends TestCase {
55 private type
56 TMethod = (
57 mt_Bytes,
58 mt_Stream
59 );
60
Jens Geyer07f4bb52022-09-03 14:50:06 +020061 strict private
Jens Geyered994552019-11-09 23:24:52 +010062 FProtocols : TList< TFactoryPair>;
63 procedure AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory);
64 class function UserFriendlyName( const factory : TFactoryPair) : string; overload;
65 class function UserFriendlyName( const method : TMethod) : string; overload;
66
67 class function Serialize(const input : IBase; const factory : TFactoryPair) : TBytes; overload;
68 class procedure Serialize(const input : IBase; const factory : TFactoryPair; const aStream : TStream); overload;
69
70 class procedure Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair); overload;
71 class procedure Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair); overload;
72
Jens Geyer41f47af2019-11-09 23:24:52 +010073 class procedure ValidateReadToEnd( const input : TBytes; const serial : TDeserializer); overload;
74 class procedure ValidateReadToEnd( const input : TStream; const serial : TDeserializer); overload;
75
Jens Geyer07f4bb52022-09-03 14:50:06 +020076 class function LengthOf( const bytes : TBytes) : Integer; overload; inline;
77 class function LengthOf( const bytes : IThriftBytes) : Integer; overload; inline;
78
79 class function DataPtrOf( const bytes : TBytes) : Pointer; overload; inline;
80 class function DataPtrOf( const bytes : IThriftBytes) : Pointer; overload; inline;
81
Jens Geyered994552019-11-09 23:24:52 +010082 procedure Test_Serializer_Deserializer;
Jens Geyer07f4bb52022-09-03 14:50:06 +020083 procedure Test_COM_Types;
Jens Geyer16819262024-03-07 23:01:20 +010084 procedure Test_ThriftBytesCTORs;
Jens Geyered994552019-11-09 23:24:52 +010085 procedure Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
86 procedure Test_CompactStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
87
88 public
89 constructor Create;
90 destructor Destroy; override;
91
92 procedure RunTests;
93 end;
94
95
96implementation
97
Jens Geyer07f4bb52022-09-03 14:50:06 +020098const SERIALIZERDATA_DLL = 'SerializerData.dll';
99function CreateOneOfEach : IOneOfEach; stdcall; external SERIALIZERDATA_DLL;
100function CreateNesting : INesting; stdcall; external SERIALIZERDATA_DLL;
101function CreateHolyMoley : IHolyMoley; stdcall; external SERIALIZERDATA_DLL;
102function CreateCompactProtoTestStruct : ICompactProtoTestStruct; stdcall; external SERIALIZERDATA_DLL;
103
Jens Geyered994552019-11-09 23:24:52 +0100104
105{ TTestSerializer }
106
107constructor TTestSerializer.Create;
108begin
109 inherited Create;
110 FProtocols := TList< TFactoryPair>.Create;
111
112 AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, nil);
113 AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, nil);
114 AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, nil);
115
116 AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create);
117 AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create);
118 AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create);
119
120 AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create);
121 AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create);
122 AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create);
123end;
124
125
126destructor TTestSerializer.Destroy;
127begin
128 try
129 FreeAndNil( FProtocols);
130 finally
131 inherited Destroy;
132 end;
133end;
134
135
136procedure TTestSerializer.AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory);
137var rec : TFactoryPair;
138begin
139 rec.prot := aProto;
140 rec.trans := aTrans;
141 FProtocols.Add( rec);
142end;
143
144
Jens Geyer07f4bb52022-09-03 14:50:06 +0200145class function TTestSerializer.LengthOf( const bytes : TBytes) : Integer;
146begin
147 result := Length(bytes);
148end;
149
150
151class function TTestSerializer.LengthOf( const bytes : IThriftBytes) : Integer;
152begin
153 if bytes <> nil
154 then result := bytes.Count
155 else result := 0;
156end;
157
158
159class function TTestSerializer.DataPtrOf( const bytes : TBytes) : Pointer;
160begin
161 result := bytes;
162end;
163
164
165class function TTestSerializer.DataPtrOf( const bytes : IThriftBytes) : Pointer;
166begin
167 if bytes <> nil
168 then result := bytes.QueryRawDataPtr
169 else result := nil;
170end;
171
172
Jens Geyered994552019-11-09 23:24:52 +0100173procedure TTestSerializer.Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
174var tested, correct : IOneOfEach;
175 bytes : TBytes;
176 i : Integer;
177begin
178 // write
Jens Geyer07f4bb52022-09-03 14:50:06 +0200179 tested := CreateOneOfEach;
Jens Geyered994552019-11-09 23:24:52 +0100180 case method of
181 mt_Bytes: bytes := Serialize( tested, factory);
182 mt_Stream: begin
183 stream.Size := 0;
184 Serialize( tested, factory, stream);
185 end
186 else
187 ASSERT( FALSE);
188 end;
189
190 // init + read
191 tested := TOneOfEachImpl.Create;
192 case method of
193 mt_Bytes: Deserialize( bytes, tested, factory);
194 mt_Stream: begin
195 stream.Position := 0;
196 Deserialize( stream, tested, factory);
197 end
198 else
199 ASSERT( FALSE);
200 end;
201
202 // check
Jens Geyer07f4bb52022-09-03 14:50:06 +0200203 correct := CreateOneOfEach;
Jens Geyered994552019-11-09 23:24:52 +0100204 ASSERT( tested.Im_true = correct.Im_true);
205 ASSERT( tested.Im_false = correct.Im_false);
206 ASSERT( tested.A_bite = correct.A_bite);
207 ASSERT( tested.Integer16 = correct.Integer16);
208 ASSERT( tested.Integer32 = correct.Integer32);
209 ASSERT( tested.Integer64 = correct.Integer64);
210 ASSERT( Abs( tested.Double_precision - correct.Double_precision) < 1E-12);
211 ASSERT( tested.Some_characters = correct.Some_characters);
212 ASSERT( tested.Zomg_unicode = correct.Zomg_unicode);
Jens Geyer62445c12022-06-29 00:00:00 +0200213 ASSERT( tested.Rfc4122_uuid = correct.Rfc4122_uuid);
Jens Geyered994552019-11-09 23:24:52 +0100214 ASSERT( tested.What_who = correct.What_who);
215
Jens Geyer07f4bb52022-09-03 14:50:06 +0200216 ASSERT( LengthOf(tested.Base64) = LengthOf(correct.Base64));
217 ASSERT( CompareMem( DataPtrOf(tested.Base64), DataPtrOf(correct.Base64), LengthOf(correct.Base64)));
Jens Geyered994552019-11-09 23:24:52 +0100218
219 ASSERT( tested.Byte_list.Count = correct.Byte_list.Count);
220 for i := 0 to tested.Byte_list.Count-1
221 do ASSERT( tested.Byte_list[i] = correct.Byte_list[i]);
222
223 ASSERT( tested.I16_list.Count = correct.I16_list.Count);
224 for i := 0 to tested.I16_list.Count-1
225 do ASSERT( tested.I16_list[i] = correct.I16_list[i]);
226
227 ASSERT( tested.I64_list.Count = correct.I64_list.Count);
228 for i := 0 to tested.I64_list.Count-1
229 do ASSERT( tested.I64_list[i] = correct.I64_list[i]);
230end;
231
232
233procedure TTestSerializer.Test_CompactStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream);
234var tested, correct : ICompactProtoTestStruct;
235 bytes : TBytes;
236begin
237 // write
Jens Geyer07f4bb52022-09-03 14:50:06 +0200238 tested := CreateCompactProtoTestStruct;
Jens Geyered994552019-11-09 23:24:52 +0100239 case method of
240 mt_Bytes: bytes := Serialize( tested, factory);
241 mt_Stream: begin
242 stream.Size := 0;
243 Serialize( tested, factory, stream);
244 end
245 else
246 ASSERT( FALSE);
247 end;
248
249 // init + read
250 correct := TCompactProtoTestStructImpl.Create;
251 case method of
252 mt_Bytes: Deserialize( bytes, tested, factory);
253 mt_Stream: begin
254 stream.Position := 0;
255 Deserialize( stream, tested, factory);
256 end
257 else
258 ASSERT( FALSE);
259 end;
260
261 // check
Jens Geyer07f4bb52022-09-03 14:50:06 +0200262 correct := CreateCompactProtoTestStruct;
Jens Geyered994552019-11-09 23:24:52 +0100263 ASSERT( correct.Field500 = tested.Field500);
264 ASSERT( correct.Field5000 = tested.Field5000);
265 ASSERT( correct.Field20000 = tested.Field20000);
266end;
267
268
269procedure TTestSerializer.Test_Serializer_Deserializer;
270var factory : TFactoryPair;
271 stream : TFileStream;
272 method : TMethod;
273begin
274 stream := TFileStream.Create( 'TestSerializer.dat', fmCreate);
275 try
276 for method in [Low(TMethod)..High(TMethod)] do begin
277 Writeln( UserFriendlyName(method));
278
279 for factory in FProtocols do begin
280 Writeln('- '+UserFriendlyName(factory));
281
282 Test_OneOfEach( method, factory, stream);
283 Test_CompactStruct( method, factory, stream);
284 end;
285
286 Writeln;
287 end;
288
289 finally
290 stream.Free;
291 end;
292end;
293
294
295class function TTestSerializer.UserFriendlyName( const factory : TFactoryPair) : string;
296begin
297 result := Copy( (factory.prot as TObject).ClassName, 2, MAXINT);
298
299 if factory.trans <> nil
300 then result := Copy( (factory.trans as TObject).ClassName, 2, MAXINT) +' '+ result;
301
302 result := StringReplace( result, 'Impl', '', [rfReplaceAll]);
303 result := StringReplace( result, 'Transport.TFactory', '', [rfReplaceAll]);
304 result := StringReplace( result, 'Protocol.TFactory', '', [rfReplaceAll]);
305end;
306
307
308class function TTestSerializer.UserFriendlyName( const method : TMethod) : string;
Jens Geyer62445c12022-06-29 00:00:00 +0200309const NAMES : array[TMethod] of string = ('TBytes','Stream');
Jens Geyered994552019-11-09 23:24:52 +0100310begin
Jens Geyer62445c12022-06-29 00:00:00 +0200311 result := NAMES[method];
Jens Geyered994552019-11-09 23:24:52 +0100312end;
313
314
Jens Geyer07f4bb52022-09-03 14:50:06 +0200315procedure TTestSerializer.Test_COM_Types;
316var tested : IOneOfEach;
317begin
318 {$IF cDebugProtoTest_Option_COM_types}
319 ASSERT( SizeOf(TSomeEnum) = SizeOf(Int32)); // -> MINENUMSIZE 4
320
321 // try to set values that allocate memory
322 tested := CreateOneOfEach;
323 tested.Zomg_unicode := 'This is a test';
324 tested.Base64 := TThriftBytesImpl.Create( TEncoding.UTF8.GetBytes('abc'));
325 {$IFEND}
326end;
327
328
Jens Geyer16819262024-03-07 23:01:20 +0100329procedure TTestSerializer.Test_ThriftBytesCTORs;
330var one, two : IThriftBytes;
331 bytes : TBytes;
332 sAscii : AnsiString;
333begin
334 sAscii := 'ABC/xzy';
Jens Geyerb53fa8e2024-03-08 00:33:22 +0100335 bytes := TEncoding.ASCII.GetBytes(string(sAscii));
Jens Geyer16819262024-03-07 23:01:20 +0100336
337 one := TThriftBytesImpl.Create( PAnsiChar(sAscii), Length(sAscii));
338 two := TThriftBytesImpl.Create( bytes, TRUE);
339
340 ASSERT( one.Count = two.Count);
341 ASSERT( CompareMem( one.QueryRawDataPtr, two.QueryRawDataPtr, one.Count));
342end;
343
344
Jens Geyered994552019-11-09 23:24:52 +0100345procedure TTestSerializer.RunTests;
346begin
347 try
348 Test_Serializer_Deserializer;
Jens Geyer07f4bb52022-09-03 14:50:06 +0200349 Test_COM_Types;
Jens Geyer16819262024-03-07 23:01:20 +0100350 Test_ThriftBytesCTORs;
Jens Geyered994552019-11-09 23:24:52 +0100351 except
352 on e:Exception do begin
353 Writeln( e.ClassName+': '+ e.Message);
354 Write('Hit ENTER to close ... '); Readln;
355 end;
356 end;
357end;
358
359
360class function TTestSerializer.Serialize(const input : IBase; const factory : TFactoryPair) : TBytes;
361var serial : TSerializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100362 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100363begin
Jens Geyera019cda2019-11-09 23:24:52 +0100364 config := TThriftConfigurationImpl.Create;
Jens Geyer62445c12022-06-29 00:00:00 +0200365 //config.MaxMessageSize := 0; // we don't read anything here
Jens Geyera019cda2019-11-09 23:24:52 +0100366
367 serial := TSerializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100368 try
369 result := serial.Serialize( input);
370 finally
371 serial.Free;
372 end;
373end;
374
375
376class procedure TTestSerializer.Serialize(const input : IBase; const factory : TFactoryPair; const aStream : TStream);
377var serial : TSerializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100378 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100379begin
Jens Geyera019cda2019-11-09 23:24:52 +0100380 config := TThriftConfigurationImpl.Create;
Jens Geyer62445c12022-06-29 00:00:00 +0200381 //config.MaxMessageSize := 0; // we don't read anything here
Jens Geyera019cda2019-11-09 23:24:52 +0100382
383 serial := TSerializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100384 try
385 serial.Serialize( input, aStream);
386 finally
387 serial.Free;
388 end;
389end;
390
391
392class procedure TTestSerializer.Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair);
393var serial : TDeserializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100394 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100395begin
Jens Geyera019cda2019-11-09 23:24:52 +0100396 config := TThriftConfigurationImpl.Create;
397 config.MaxMessageSize := Length(input);
398
399 serial := TDeserializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100400 try
401 serial.Deserialize( input, target);
Jens Geyer41f47af2019-11-09 23:24:52 +0100402 ValidateReadToEnd( input, serial);
Jens Geyered994552019-11-09 23:24:52 +0100403 finally
404 serial.Free;
405 end;
406end;
407
408
409class procedure TTestSerializer.Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair);
410var serial : TDeserializer;
Jens Geyera019cda2019-11-09 23:24:52 +0100411 config : IThriftConfiguration;
Jens Geyered994552019-11-09 23:24:52 +0100412begin
Jens Geyera019cda2019-11-09 23:24:52 +0100413 config := TThriftConfigurationImpl.Create;
414 config.MaxMessageSize := input.Size;
415
416 serial := TDeserializer.Create( factory.prot, factory.trans, config);
Jens Geyered994552019-11-09 23:24:52 +0100417 try
418 serial.Deserialize( input, target);
Jens Geyer41f47af2019-11-09 23:24:52 +0100419 ValidateReadToEnd( input, serial);
Jens Geyered994552019-11-09 23:24:52 +0100420 finally
421 serial.Free;
422 end;
423end;
424
425
Jens Geyer41f47af2019-11-09 23:24:52 +0100426class procedure TTestSerializer.ValidateReadToEnd( const input : TBytes; const serial : TDeserializer);
427// we should not have any more byte to read
428var dummy : IBase;
429begin
430 try
431 dummy := TOneOfEachImpl.Create;
432 serial.Deserialize( input, dummy);
433 raise EInOutError.Create('Expected exception not thrown?');
434 except
435 on e:TTransportExceptionEndOfFile do {expected};
436 on e:Exception do raise; // unexpected
437 end;
438end;
439
440
441class procedure TTestSerializer.ValidateReadToEnd( const input : TStream; const serial : TDeserializer);
442// we should not have any more byte to read
443var dummy : IBase;
444begin
445 try
446 input.Position := 0;
447 dummy := TOneOfEachImpl.Create;
448 serial.Deserialize( input, dummy);
449 raise EInOutError.Create('Expected exception not thrown?');
450 except
451 on e:TTransportExceptionEndOfFile do {expected};
452 on e:Exception do raise; // unexpected
453 end;
454end;
455
Jens Geyered994552019-11-09 23:24:52 +0100456end.