unit TestSerializer.Tests; (* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. *) interface uses Classes, Windows, SysUtils, Generics.Collections, Thrift, Thrift.Exception, Thrift.Socket, Thrift.Transport, Thrift.Protocol, Thrift.Protocol.JSON, Thrift.Protocol.Compact, Thrift.Collections, Thrift.Configuration, Thrift.Server, Thrift.Utils, Thrift.Serializer, Thrift.Stream, Thrift.WinHTTP, Thrift.TypeRegistry, System_, DebugProtoTest; {$TYPEINFO ON} type TFactoryPair = record prot : IProtocolFactory; trans : ITransportFactory; end; TTestSerializer = class //extends TestCase { private type TMethod = ( mt_Bytes, mt_Stream ); strict private FProtocols : TList< TFactoryPair>; procedure AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory); class function UserFriendlyName( const factory : TFactoryPair) : string; overload; class function UserFriendlyName( const method : TMethod) : string; overload; class function Serialize(const input : IBase; const factory : TFactoryPair) : TBytes; overload; class procedure Serialize(const input : IBase; const factory : TFactoryPair; const aStream : TStream); overload; class procedure Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair); overload; class procedure Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair); overload; class procedure ValidateReadToEnd( const input : TBytes; const serial : TDeserializer); overload; class procedure ValidateReadToEnd( const input : TStream; const serial : TDeserializer); overload; class function LengthOf( const bytes : TBytes) : Integer; overload; inline; class function LengthOf( const bytes : IThriftBytes) : Integer; overload; inline; class function DataPtrOf( const bytes : TBytes) : Pointer; overload; inline; class function DataPtrOf( const bytes : IThriftBytes) : Pointer; overload; inline; procedure Test_Serializer_Deserializer; procedure Test_COM_Types; procedure Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); procedure Test_CompactStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); public constructor Create; destructor Destroy; override; procedure RunTests; end; implementation const SERIALIZERDATA_DLL = 'SerializerData.dll'; function CreateOneOfEach : IOneOfEach; stdcall; external SERIALIZERDATA_DLL; function CreateNesting : INesting; stdcall; external SERIALIZERDATA_DLL; function CreateHolyMoley : IHolyMoley; stdcall; external SERIALIZERDATA_DLL; function CreateCompactProtoTestStruct : ICompactProtoTestStruct; stdcall; external SERIALIZERDATA_DLL; { TTestSerializer } constructor TTestSerializer.Create; begin inherited Create; FProtocols := TList< TFactoryPair>.Create; AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, nil); AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, nil); AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, nil); AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create); AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create); AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, TFramedTransportImpl.TFactory.Create); AddFactoryCombination( TBinaryProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create); AddFactoryCombination( TCompactProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create); AddFactoryCombination( TJSONProtocolImpl.TFactory.Create, TBufferedTransportImpl.TFactory.Create); end; destructor TTestSerializer.Destroy; begin try FreeAndNil( FProtocols); finally inherited Destroy; end; end; procedure TTestSerializer.AddFactoryCombination( const aProto : IProtocolFactory; const aTrans : ITransportFactory); var rec : TFactoryPair; begin rec.prot := aProto; rec.trans := aTrans; FProtocols.Add( rec); end; class function TTestSerializer.LengthOf( const bytes : TBytes) : Integer; begin result := Length(bytes); end; class function TTestSerializer.LengthOf( const bytes : IThriftBytes) : Integer; begin if bytes <> nil then result := bytes.Count else result := 0; end; class function TTestSerializer.DataPtrOf( const bytes : TBytes) : Pointer; begin result := bytes; end; class function TTestSerializer.DataPtrOf( const bytes : IThriftBytes) : Pointer; begin if bytes <> nil then result := bytes.QueryRawDataPtr else result := nil; end; procedure TTestSerializer.Test_OneOfEach( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); var tested, correct : IOneOfEach; bytes : TBytes; i : Integer; begin // write tested := CreateOneOfEach; case method of mt_Bytes: bytes := Serialize( tested, factory); mt_Stream: begin stream.Size := 0; Serialize( tested, factory, stream); end else ASSERT( FALSE); end; // init + read tested := TOneOfEachImpl.Create; case method of mt_Bytes: Deserialize( bytes, tested, factory); mt_Stream: begin stream.Position := 0; Deserialize( stream, tested, factory); end else ASSERT( FALSE); end; // check correct := CreateOneOfEach; ASSERT( tested.Im_true = correct.Im_true); ASSERT( tested.Im_false = correct.Im_false); ASSERT( tested.A_bite = correct.A_bite); ASSERT( tested.Integer16 = correct.Integer16); ASSERT( tested.Integer32 = correct.Integer32); ASSERT( tested.Integer64 = correct.Integer64); ASSERT( Abs( tested.Double_precision - correct.Double_precision) < 1E-12); ASSERT( tested.Some_characters = correct.Some_characters); ASSERT( tested.Zomg_unicode = correct.Zomg_unicode); ASSERT( tested.Rfc4122_uuid = correct.Rfc4122_uuid); ASSERT( tested.What_who = correct.What_who); ASSERT( LengthOf(tested.Base64) = LengthOf(correct.Base64)); ASSERT( CompareMem( DataPtrOf(tested.Base64), DataPtrOf(correct.Base64), LengthOf(correct.Base64))); ASSERT( tested.Byte_list.Count = correct.Byte_list.Count); for i := 0 to tested.Byte_list.Count-1 do ASSERT( tested.Byte_list[i] = correct.Byte_list[i]); ASSERT( tested.I16_list.Count = correct.I16_list.Count); for i := 0 to tested.I16_list.Count-1 do ASSERT( tested.I16_list[i] = correct.I16_list[i]); ASSERT( tested.I64_list.Count = correct.I64_list.Count); for i := 0 to tested.I64_list.Count-1 do ASSERT( tested.I64_list[i] = correct.I64_list[i]); end; procedure TTestSerializer.Test_CompactStruct( const method : TMethod; const factory : TFactoryPair; const stream : TFileStream); var tested, correct : ICompactProtoTestStruct; bytes : TBytes; begin // write tested := CreateCompactProtoTestStruct; case method of mt_Bytes: bytes := Serialize( tested, factory); mt_Stream: begin stream.Size := 0; Serialize( tested, factory, stream); end else ASSERT( FALSE); end; // init + read correct := TCompactProtoTestStructImpl.Create; case method of mt_Bytes: Deserialize( bytes, tested, factory); mt_Stream: begin stream.Position := 0; Deserialize( stream, tested, factory); end else ASSERT( FALSE); end; // check correct := CreateCompactProtoTestStruct; ASSERT( correct.Field500 = tested.Field500); ASSERT( correct.Field5000 = tested.Field5000); ASSERT( correct.Field20000 = tested.Field20000); end; procedure TTestSerializer.Test_Serializer_Deserializer; var factory : TFactoryPair; stream : TFileStream; method : TMethod; begin stream := TFileStream.Create( 'TestSerializer.dat', fmCreate); try for method in [Low(TMethod)..High(TMethod)] do begin Writeln( UserFriendlyName(method)); for factory in FProtocols do begin Writeln('- '+UserFriendlyName(factory)); Test_OneOfEach( method, factory, stream); Test_CompactStruct( method, factory, stream); end; Writeln; end; finally stream.Free; end; end; class function TTestSerializer.UserFriendlyName( const factory : TFactoryPair) : string; begin result := Copy( (factory.prot as TObject).ClassName, 2, MAXINT); if factory.trans <> nil then result := Copy( (factory.trans as TObject).ClassName, 2, MAXINT) +' '+ result; result := StringReplace( result, 'Impl', '', [rfReplaceAll]); result := StringReplace( result, 'Transport.TFactory', '', [rfReplaceAll]); result := StringReplace( result, 'Protocol.TFactory', '', [rfReplaceAll]); end; class function TTestSerializer.UserFriendlyName( const method : TMethod) : string; const NAMES : array[TMethod] of string = ('TBytes','Stream'); begin result := NAMES[method]; end; procedure TTestSerializer.Test_COM_Types; var tested : IOneOfEach; begin {$IF cDebugProtoTest_Option_COM_types} ASSERT( SizeOf(TSomeEnum) = SizeOf(Int32)); // -> MINENUMSIZE 4 // try to set values that allocate memory tested := CreateOneOfEach; tested.Zomg_unicode := 'This is a test'; tested.Base64 := TThriftBytesImpl.Create( TEncoding.UTF8.GetBytes('abc')); {$IFEND} end; procedure TTestSerializer.RunTests; begin try Test_Serializer_Deserializer; Test_COM_Types; except on e:Exception do begin Writeln( e.ClassName+': '+ e.Message); Write('Hit ENTER to close ... '); Readln; end; end; end; class function TTestSerializer.Serialize(const input : IBase; const factory : TFactoryPair) : TBytes; var serial : TSerializer; config : IThriftConfiguration; begin config := TThriftConfigurationImpl.Create; //config.MaxMessageSize := 0; // we don't read anything here serial := TSerializer.Create( factory.prot, factory.trans, config); try result := serial.Serialize( input); finally serial.Free; end; end; class procedure TTestSerializer.Serialize(const input : IBase; const factory : TFactoryPair; const aStream : TStream); var serial : TSerializer; config : IThriftConfiguration; begin config := TThriftConfigurationImpl.Create; //config.MaxMessageSize := 0; // we don't read anything here serial := TSerializer.Create( factory.prot, factory.trans, config); try serial.Serialize( input, aStream); finally serial.Free; end; end; class procedure TTestSerializer.Deserialize( const input : TBytes; const target : IBase; const factory : TFactoryPair); var serial : TDeserializer; config : IThriftConfiguration; begin config := TThriftConfigurationImpl.Create; config.MaxMessageSize := Length(input); serial := TDeserializer.Create( factory.prot, factory.trans, config); try serial.Deserialize( input, target); ValidateReadToEnd( input, serial); finally serial.Free; end; end; class procedure TTestSerializer.Deserialize( const input : TStream; const target : IBase; const factory : TFactoryPair); var serial : TDeserializer; config : IThriftConfiguration; begin config := TThriftConfigurationImpl.Create; config.MaxMessageSize := input.Size; serial := TDeserializer.Create( factory.prot, factory.trans, config); try serial.Deserialize( input, target); ValidateReadToEnd( input, serial); finally serial.Free; end; end; class procedure TTestSerializer.ValidateReadToEnd( const input : TBytes; const serial : TDeserializer); // we should not have any more byte to read var dummy : IBase; begin try dummy := TOneOfEachImpl.Create; serial.Deserialize( input, dummy); raise EInOutError.Create('Expected exception not thrown?'); except on e:TTransportExceptionEndOfFile do {expected}; on e:Exception do raise; // unexpected end; end; class procedure TTestSerializer.ValidateReadToEnd( const input : TStream; const serial : TDeserializer); // we should not have any more byte to read var dummy : IBase; begin try input.Position := 0; dummy := TOneOfEachImpl.Create; serial.Deserialize( input, dummy); raise EInOutError.Create('Expected exception not thrown?'); except on e:TTransportExceptionEndOfFile do {expected}; on e:Exception do raise; // unexpected end; end; end.