THRIFT-5789 Refactor test suite client implementation
Client: Delphi
Patch: Jens Geyer
diff --git a/lib/delphi/test/testsuite/client/UnitTests.pas b/lib/delphi/test/testsuite/client/UnitTests.pas
new file mode 100644
index 0000000..3726c87
--- /dev/null
+++ b/lib/delphi/test/testsuite/client/UnitTests.pas
@@ -0,0 +1,350 @@
+(*
+ * 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.