blob: 3726c8735945d3c8ae6a4d3209104601f11c6c59 [file] [log] [blame]
(*
* 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.
*)
unit UnitTests;
{$I ../src/Thrift.Defines.inc}
interface
uses
Classes, Windows, SysUtils, Math, ActiveX, ComObj,
{$IFDEF SupportsAsync} System.Threading, {$ENDIF}
DateUtils,
Generics.Collections,
TestConstants,
TestLogger,
ConsoleHelper,
Thrift,
Thrift.Protocol.Compact,
Thrift.Protocol.JSON,
Thrift.Protocol,
Thrift.Transport.Pipes,
Thrift.Transport.WinHTTP,
Thrift.Transport.MsxmlHTTP,
Thrift.Transport,
Thrift.Stream,
Thrift.Test,
Thrift.WinHTTP,
Thrift.Utils,
Thrift.Configuration,
Thrift.Collections;
type
TQuickUnitTests = class sealed
strict private
FLogger : ITestLogger;
strict protected
// Helper
procedure StartTestGroup( const aGroup : string; const aTest : TClientTestGroup); inline;
procedure Expect( aTestResult : Boolean; const aTestInfo : string); inline;
// Test impl
procedure JSONProtocolReadWriteTest;
procedure HashSetTest;
{$IFDEF Win64}
procedure UseInterlockedExchangeAdd64;
{$ENDIF}
// main execution part
constructor Create( const logger : ITestLogger); reintroduce;
procedure Execute; overload;
public
destructor Destroy; override;
class procedure Execute( const logger : ITestLogger); overload; static;
end;
implementation
constructor TQuickUnitTests.Create( const logger : ITestLogger);
begin
inherited Create;
FLogger := logger;
end;
destructor TQuickUnitTests.Destroy;
begin
try
FLogger := nil; //-> Release
finally
inherited Destroy;
end;
end;
class procedure TQuickUnitTests.Execute( const logger : ITestLogger);
var instance : TQuickUnitTests;
begin
instance := TQuickUnitTests.Create(logger);
try
instance.Execute;
finally
instance.Free;
end;
end;
procedure TQuickUnitTests.Execute;
begin
{$IFDEF Win64}
UseInterlockedExchangeAdd64;
{$ENDIF}
JSONProtocolReadWriteTest;
HashSetTest;
end;
procedure TQuickUnitTests.StartTestGroup( const aGroup : string; const aTest : TClientTestGroup);
begin
FLogger.StartTestGroup( aGroup, aTest);
end;
procedure TQuickUnitTests.Expect( aTestResult : Boolean; const aTestInfo : string);
begin
FLogger.Expect( aTestResult, aTestInfo);
end;
{$IFDEF Win64}
procedure TQuickUnitTests.UseInterlockedExchangeAdd64;
var a,b : Int64;
begin
a := 1;
b := 2;
Thrift.Utils.InterlockedExchangeAdd64( a,b);
Expect( a = 3, 'InterlockedExchangeAdd64');
end;
{$ENDIF}
procedure TQuickUnitTests.JSONProtocolReadWriteTest;
// Tests only then read/write procedures of the JSON protocol
// All tests succeed, if we can read what we wrote before
// Note that passing this test does not imply, that our JSON is really compatible to what
// other clients or servers expect as the real JSON. This is beyond the scope of this test.
var prot : IProtocol;
stm : TStringStream;
list : TThriftList;
config : IThriftConfiguration;
binary, binRead, emptyBinary : TBytes;
i,iErr : Integer;
const
TEST_SHORT = ShortInt( $FE);
TEST_SMALL = SmallInt( $FEDC);
TEST_LONG = LongInt( $FEDCBA98);
TEST_I64 = Int64( $FEDCBA9876543210);
TEST_DOUBLE = -1.234e-56;
DELTA_DOUBLE = TEST_DOUBLE * 1e-14;
TEST_STRING = 'abc-'#$00E4#$00f6#$00fc; // german umlauts (en-us: "funny chars")
// Test THRIFT-2336 and THRIFT-3404 with U+1D11E (G Clef symbol) and 'Русское Название';
G_CLEF_AND_CYRILLIC_TEXT = #$1d11e' '#$0420#$0443#$0441#$0441#$043a#$043e#$0435' '#$041d#$0430#$0437#$0432#$0430#$043d#$0438#$0435;
G_CLEF_AND_CYRILLIC_JSON = '"\ud834\udd1e \u0420\u0443\u0441\u0441\u043a\u043e\u0435 \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435"';
// test both possible solidus encodings
SOLIDUS_JSON_DATA = '"one/two\/three"';
SOLIDUS_EXCPECTED = 'one/two/three';
begin
stm := TStringStream.Create;
try
FLogger.StartTestGroup( 'JsonProtocolTest', test_Unknown);
config := TThriftConfigurationImpl.Create;
// prepare binary data
binary := PrepareBinaryData( FALSE, TTestSize.Normal);
SetLength( emptyBinary, 0); // empty binary data block
// output setup
prot := TJSONProtocolImpl.Create(
TStreamTransportImpl.Create(
nil, TThriftStreamAdapterDelphi.Create( stm, FALSE), config));
// write
Init( list, TType.String_, 9);
prot.WriteListBegin( list);
prot.WriteBool( TRUE);
prot.WriteBool( FALSE);
prot.WriteByte( TEST_SHORT);
prot.WriteI16( TEST_SMALL);
prot.WriteI32( TEST_LONG);
prot.WriteI64( TEST_I64);
prot.WriteDouble( TEST_DOUBLE);
prot.WriteString( TEST_STRING);
prot.WriteBinary( binary);
prot.WriteString( ''); // empty string
prot.WriteBinary( emptyBinary); // empty binary data block
prot.WriteListEnd;
// input setup
Expect( stm.Position = stm.Size, 'Stream position/length after write');
stm.Position := 0;
prot := TJSONProtocolImpl.Create(
TStreamTransportImpl.Create(
TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config));
// read and compare
list := prot.ReadListBegin;
Expect( list.ElementType = TType.String_, 'list element type');
Expect( list.Count = 9, 'list element count');
Expect( prot.ReadBool, 'WriteBool/ReadBool: TRUE');
Expect( not prot.ReadBool, 'WriteBool/ReadBool: FALSE');
Expect( prot.ReadByte = TEST_SHORT, 'WriteByte/ReadByte');
Expect( prot.ReadI16 = TEST_SMALL, 'WriteI16/ReadI16');
Expect( prot.ReadI32 = TEST_LONG, 'WriteI32/ReadI32');
Expect( prot.ReadI64 = TEST_I64, 'WriteI64/ReadI64');
Expect( abs(prot.ReadDouble-TEST_DOUBLE) < abs(DELTA_DOUBLE), 'WriteDouble/ReadDouble');
Expect( prot.ReadString = TEST_STRING, 'WriteString/ReadString');
binRead := prot.ReadBinary;
Expect( Length(prot.ReadString) = 0, 'WriteString/ReadString (empty string)');
Expect( Length(prot.ReadBinary) = 0, 'empty WriteBinary/ReadBinary (empty data block)');
prot.ReadListEnd;
// test binary data
Expect( Length(binary) = Length(binRead), 'Binary data length check');
iErr := -1;
for i := Low(binary) to High(binary) do begin
if binary[i] <> binRead[i] then begin
iErr := i;
Break;
end;
end;
if iErr < 0
then Expect( TRUE, 'Binary data check ('+IntToStr(Length(binary))+' Bytes)')
else Expect( FALSE, 'Binary data check at offset '+IntToStr(iErr));
Expect( stm.Position = stm.Size, 'Stream position after read');
// Solidus can be encoded in two ways. Make sure we can read both
stm.Position := 0;
stm.Size := 0;
stm.WriteString(SOLIDUS_JSON_DATA);
stm.Position := 0;
prot := TJSONProtocolImpl.Create(
TStreamTransportImpl.Create(
TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config));
Expect( prot.ReadString = SOLIDUS_EXCPECTED, 'Solidus encoding');
// Widechars should work too. Do they?
// After writing, we ensure that we are able to read it back
// We can't assume hex-encoding, since (nearly) any Unicode char is valid JSON
stm.Position := 0;
stm.Size := 0;
prot := TJSONProtocolImpl.Create(
TStreamTransportImpl.Create(
nil, TThriftStreamAdapterDelphi.Create( stm, FALSE), config));
prot.WriteString( G_CLEF_AND_CYRILLIC_TEXT);
stm.Position := 0;
prot := TJSONProtocolImpl.Create(
TStreamTransportImpl.Create(
TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config));
FLogger.Expect( prot.ReadString = G_CLEF_AND_CYRILLIC_TEXT, 'Writing JSON with chars > 8 bit');
// Widechars should work with hex-encoding too. Do they?
stm.Position := 0;
stm.Size := 0;
stm.WriteString( G_CLEF_AND_CYRILLIC_JSON);
stm.Position := 0;
prot := TJSONProtocolImpl.Create(
TStreamTransportImpl.Create(
TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config));
FLogger.Expect( prot.ReadString = G_CLEF_AND_CYRILLIC_TEXT, 'Reading JSON with chars > 8 bit');
finally
stm.Free;
prot := nil; //-> Release
FLogger.StartTestGroup( '', test_Unknown); // no more tests here
end;
end;
procedure TQuickUnitTests.HashSetTest;
var container : IThriftHashSet<Integer>;
testdata : array of Integer;
i : Integer;
const
TEST_COUNT = 4096;
begin
StartTestGroup( 'IThriftHashSet<T> implementation', test_Containers);
// prepare test data
SetLength( testdata, 5);
testdata[0] := -2;
testdata[1] := 0;
testdata[2] := 42;
testdata[3] := MaxInt;
testdata[4] := Low(Integer);
// first insert
container := TThriftHashSetImpl<Integer>.Create;
for i in testdata do begin
Expect( container.Add( i), 'add first '+IntToStr(i));
Expect( container.Contains( i), 'contains '+IntToStr(i));
end;
Expect( container.Count = Length(testdata), 'container size');
// insert again
for i in testdata do begin
Expect( not container.Add( i), 'add second '+IntToStr(i));
Expect( container.Contains( i), 'contains '+IntToStr(i));
end;
Expect( container.Count = Length(testdata), 'container size');
// remove
for i in testdata do begin
Expect( container.Remove( i), 'first remove '+IntToStr(i));
Expect( not container.Contains( i), 'not contains '+IntToStr(i));
end;
Expect( container.Count = 0, 'container size');
// remove again
for i in testdata do begin
Expect( not container.Remove( i), 'second remove '+IntToStr(i));
Expect( not container.Contains( i), 'not contains '+IntToStr(i));
end;
Expect( container.Count = 0, 'container size');
// append and clear
for i := 0 to TEST_COUNT-1 do begin
container.Add(-i);
container.Add(+i);
end;
Expect( container.Count = 2*TEST_COUNT-1, 'container size check');
Expect( not container.Contains( -TEST_COUNT), 'element not contained');
Expect( not container.Contains( TEST_COUNT), 'element not contained');
container.Clear;
Expect( container.Count = 0, 'count=0 after clear');
end;
end.