Jens Geyer | 82fc93e | 2024-05-24 23:36:07 +0200 | [diff] [blame] | 1 | (* |
| 2 | * Licensed to the Apache Software Foundation (ASF) under one |
| 3 | * or more contributor license agreements. See the NOTICE file |
| 4 | * distributed with this work for additional information |
| 5 | * regarding copyright ownership. The ASF licenses this file |
| 6 | * to you under the Apache License, Version 2.0 (the |
| 7 | * "License"); you may not use this file except in compliance |
| 8 | * with the License. You may obtain a copy of the License at |
| 9 | * |
| 10 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | * |
| 12 | * Unless required by applicable law or agreed to in writing, |
| 13 | * software distributed under the License is distributed on an |
| 14 | * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | * KIND, either express or implied. See the License for the |
| 16 | * specific language governing permissions and limitations |
| 17 | * under the License. |
| 18 | *) |
| 19 | |
| 20 | unit UnitTests; |
| 21 | |
| 22 | {$I ../src/Thrift.Defines.inc} |
| 23 | |
| 24 | interface |
| 25 | |
| 26 | uses |
| 27 | Classes, Windows, SysUtils, Math, ActiveX, ComObj, |
| 28 | {$IFDEF SupportsAsync} System.Threading, {$ENDIF} |
| 29 | DateUtils, |
| 30 | Generics.Collections, |
| 31 | TestConstants, |
| 32 | TestLogger, |
| 33 | ConsoleHelper, |
| 34 | Thrift, |
| 35 | Thrift.Protocol.Compact, |
| 36 | Thrift.Protocol.JSON, |
| 37 | Thrift.Protocol, |
| 38 | Thrift.Transport.Pipes, |
| 39 | Thrift.Transport.WinHTTP, |
| 40 | Thrift.Transport.MsxmlHTTP, |
| 41 | Thrift.Transport, |
| 42 | Thrift.Stream, |
| 43 | Thrift.Test, |
| 44 | Thrift.WinHTTP, |
| 45 | Thrift.Utils, |
| 46 | Thrift.Configuration, |
| 47 | Thrift.Collections; |
| 48 | |
| 49 | type |
| 50 | TQuickUnitTests = class sealed |
| 51 | strict private |
| 52 | FLogger : ITestLogger; |
| 53 | |
| 54 | strict protected |
| 55 | // Helper |
| 56 | procedure StartTestGroup( const aGroup : string; const aTest : TClientTestGroup); inline; |
| 57 | procedure Expect( aTestResult : Boolean; const aTestInfo : string); inline; |
| 58 | |
| 59 | // Test impl |
| 60 | procedure JSONProtocolReadWriteTest; |
| 61 | procedure HashSetTest; |
| 62 | {$IFDEF Win64} |
| 63 | procedure UseInterlockedExchangeAdd64; |
| 64 | {$ENDIF} |
| 65 | |
| 66 | // main execution part |
| 67 | constructor Create( const logger : ITestLogger); reintroduce; |
| 68 | procedure Execute; overload; |
| 69 | public |
| 70 | destructor Destroy; override; |
| 71 | |
| 72 | class procedure Execute( const logger : ITestLogger); overload; static; |
| 73 | end; |
| 74 | |
| 75 | |
| 76 | implementation |
| 77 | |
| 78 | |
| 79 | constructor TQuickUnitTests.Create( const logger : ITestLogger); |
| 80 | begin |
| 81 | inherited Create; |
| 82 | FLogger := logger; |
| 83 | end; |
| 84 | |
| 85 | |
| 86 | destructor TQuickUnitTests.Destroy; |
| 87 | begin |
| 88 | try |
| 89 | FLogger := nil; //-> Release |
| 90 | finally |
| 91 | inherited Destroy; |
| 92 | end; |
| 93 | end; |
| 94 | |
| 95 | |
| 96 | class procedure TQuickUnitTests.Execute( const logger : ITestLogger); |
| 97 | var instance : TQuickUnitTests; |
| 98 | begin |
| 99 | instance := TQuickUnitTests.Create(logger); |
| 100 | try |
| 101 | instance.Execute; |
| 102 | finally |
| 103 | instance.Free; |
| 104 | end; |
| 105 | end; |
| 106 | |
| 107 | |
| 108 | procedure TQuickUnitTests.Execute; |
| 109 | begin |
| 110 | {$IFDEF Win64} |
| 111 | UseInterlockedExchangeAdd64; |
| 112 | {$ENDIF} |
| 113 | |
| 114 | JSONProtocolReadWriteTest; |
| 115 | HashSetTest; |
| 116 | end; |
| 117 | |
| 118 | |
| 119 | procedure TQuickUnitTests.StartTestGroup( const aGroup : string; const aTest : TClientTestGroup); |
| 120 | begin |
| 121 | FLogger.StartTestGroup( aGroup, aTest); |
| 122 | end; |
| 123 | |
| 124 | |
| 125 | procedure TQuickUnitTests.Expect( aTestResult : Boolean; const aTestInfo : string); |
| 126 | begin |
| 127 | FLogger.Expect( aTestResult, aTestInfo); |
| 128 | end; |
| 129 | |
| 130 | |
| 131 | {$IFDEF Win64} |
| 132 | procedure TQuickUnitTests.UseInterlockedExchangeAdd64; |
| 133 | var a,b : Int64; |
| 134 | begin |
| 135 | a := 1; |
| 136 | b := 2; |
| 137 | Thrift.Utils.InterlockedExchangeAdd64( a,b); |
| 138 | Expect( a = 3, 'InterlockedExchangeAdd64'); |
| 139 | end; |
| 140 | {$ENDIF} |
| 141 | |
| 142 | |
| 143 | procedure TQuickUnitTests.JSONProtocolReadWriteTest; |
| 144 | // Tests only then read/write procedures of the JSON protocol |
| 145 | // All tests succeed, if we can read what we wrote before |
| 146 | // Note that passing this test does not imply, that our JSON is really compatible to what |
| 147 | // other clients or servers expect as the real JSON. This is beyond the scope of this test. |
| 148 | var prot : IProtocol; |
| 149 | stm : TStringStream; |
| 150 | list : TThriftList; |
| 151 | config : IThriftConfiguration; |
| 152 | binary, binRead, emptyBinary : TBytes; |
| 153 | i,iErr : Integer; |
| 154 | const |
| 155 | TEST_SHORT = ShortInt( $FE); |
| 156 | TEST_SMALL = SmallInt( $FEDC); |
| 157 | TEST_LONG = LongInt( $FEDCBA98); |
| 158 | TEST_I64 = Int64( $FEDCBA9876543210); |
| 159 | TEST_DOUBLE = -1.234e-56; |
| 160 | DELTA_DOUBLE = TEST_DOUBLE * 1e-14; |
| 161 | TEST_STRING = 'abc-'#$00E4#$00f6#$00fc; // german umlauts (en-us: "funny chars") |
| 162 | // Test THRIFT-2336 and THRIFT-3404 with U+1D11E (G Clef symbol) and 'Русское Название'; |
| 163 | G_CLEF_AND_CYRILLIC_TEXT = #$1d11e' '#$0420#$0443#$0441#$0441#$043a#$043e#$0435' '#$041d#$0430#$0437#$0432#$0430#$043d#$0438#$0435; |
| 164 | G_CLEF_AND_CYRILLIC_JSON = '"\ud834\udd1e \u0420\u0443\u0441\u0441\u043a\u043e\u0435 \u041d\u0430\u0437\u0432\u0430\u043d\u0438\u0435"'; |
| 165 | // test both possible solidus encodings |
| 166 | SOLIDUS_JSON_DATA = '"one/two\/three"'; |
| 167 | SOLIDUS_EXCPECTED = 'one/two/three'; |
| 168 | begin |
| 169 | stm := TStringStream.Create; |
| 170 | try |
| 171 | FLogger.StartTestGroup( 'JsonProtocolTest', test_Unknown); |
| 172 | |
| 173 | config := TThriftConfigurationImpl.Create; |
| 174 | |
| 175 | // prepare binary data |
| 176 | binary := PrepareBinaryData( FALSE, TTestSize.Normal); |
| 177 | SetLength( emptyBinary, 0); // empty binary data block |
| 178 | |
| 179 | // output setup |
| 180 | prot := TJSONProtocolImpl.Create( |
| 181 | TStreamTransportImpl.Create( |
| 182 | nil, TThriftStreamAdapterDelphi.Create( stm, FALSE), config)); |
| 183 | |
| 184 | // write |
| 185 | Init( list, TType.String_, 9); |
| 186 | prot.WriteListBegin( list); |
| 187 | prot.WriteBool( TRUE); |
| 188 | prot.WriteBool( FALSE); |
| 189 | prot.WriteByte( TEST_SHORT); |
| 190 | prot.WriteI16( TEST_SMALL); |
| 191 | prot.WriteI32( TEST_LONG); |
| 192 | prot.WriteI64( TEST_I64); |
| 193 | prot.WriteDouble( TEST_DOUBLE); |
| 194 | prot.WriteString( TEST_STRING); |
| 195 | prot.WriteBinary( binary); |
| 196 | prot.WriteString( ''); // empty string |
| 197 | prot.WriteBinary( emptyBinary); // empty binary data block |
| 198 | prot.WriteListEnd; |
| 199 | |
| 200 | // input setup |
| 201 | Expect( stm.Position = stm.Size, 'Stream position/length after write'); |
| 202 | stm.Position := 0; |
| 203 | prot := TJSONProtocolImpl.Create( |
| 204 | TStreamTransportImpl.Create( |
| 205 | TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config)); |
| 206 | |
| 207 | // read and compare |
| 208 | list := prot.ReadListBegin; |
| 209 | Expect( list.ElementType = TType.String_, 'list element type'); |
| 210 | Expect( list.Count = 9, 'list element count'); |
| 211 | Expect( prot.ReadBool, 'WriteBool/ReadBool: TRUE'); |
| 212 | Expect( not prot.ReadBool, 'WriteBool/ReadBool: FALSE'); |
| 213 | Expect( prot.ReadByte = TEST_SHORT, 'WriteByte/ReadByte'); |
| 214 | Expect( prot.ReadI16 = TEST_SMALL, 'WriteI16/ReadI16'); |
| 215 | Expect( prot.ReadI32 = TEST_LONG, 'WriteI32/ReadI32'); |
| 216 | Expect( prot.ReadI64 = TEST_I64, 'WriteI64/ReadI64'); |
| 217 | Expect( abs(prot.ReadDouble-TEST_DOUBLE) < abs(DELTA_DOUBLE), 'WriteDouble/ReadDouble'); |
| 218 | Expect( prot.ReadString = TEST_STRING, 'WriteString/ReadString'); |
| 219 | binRead := prot.ReadBinary; |
| 220 | Expect( Length(prot.ReadString) = 0, 'WriteString/ReadString (empty string)'); |
| 221 | Expect( Length(prot.ReadBinary) = 0, 'empty WriteBinary/ReadBinary (empty data block)'); |
| 222 | prot.ReadListEnd; |
| 223 | |
| 224 | // test binary data |
| 225 | Expect( Length(binary) = Length(binRead), 'Binary data length check'); |
| 226 | iErr := -1; |
| 227 | for i := Low(binary) to High(binary) do begin |
| 228 | if binary[i] <> binRead[i] then begin |
| 229 | iErr := i; |
| 230 | Break; |
| 231 | end; |
| 232 | end; |
| 233 | if iErr < 0 |
| 234 | then Expect( TRUE, 'Binary data check ('+IntToStr(Length(binary))+' Bytes)') |
| 235 | else Expect( FALSE, 'Binary data check at offset '+IntToStr(iErr)); |
| 236 | |
| 237 | Expect( stm.Position = stm.Size, 'Stream position after read'); |
| 238 | |
| 239 | |
| 240 | // Solidus can be encoded in two ways. Make sure we can read both |
| 241 | stm.Position := 0; |
| 242 | stm.Size := 0; |
| 243 | stm.WriteString(SOLIDUS_JSON_DATA); |
| 244 | stm.Position := 0; |
| 245 | prot := TJSONProtocolImpl.Create( |
| 246 | TStreamTransportImpl.Create( |
| 247 | TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config)); |
| 248 | Expect( prot.ReadString = SOLIDUS_EXCPECTED, 'Solidus encoding'); |
| 249 | |
| 250 | |
| 251 | // Widechars should work too. Do they? |
| 252 | // After writing, we ensure that we are able to read it back |
| 253 | // We can't assume hex-encoding, since (nearly) any Unicode char is valid JSON |
| 254 | stm.Position := 0; |
| 255 | stm.Size := 0; |
| 256 | prot := TJSONProtocolImpl.Create( |
| 257 | TStreamTransportImpl.Create( |
| 258 | nil, TThriftStreamAdapterDelphi.Create( stm, FALSE), config)); |
| 259 | prot.WriteString( G_CLEF_AND_CYRILLIC_TEXT); |
| 260 | stm.Position := 0; |
| 261 | prot := TJSONProtocolImpl.Create( |
| 262 | TStreamTransportImpl.Create( |
| 263 | TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config)); |
| 264 | FLogger.Expect( prot.ReadString = G_CLEF_AND_CYRILLIC_TEXT, 'Writing JSON with chars > 8 bit'); |
| 265 | |
| 266 | // Widechars should work with hex-encoding too. Do they? |
| 267 | stm.Position := 0; |
| 268 | stm.Size := 0; |
| 269 | stm.WriteString( G_CLEF_AND_CYRILLIC_JSON); |
| 270 | stm.Position := 0; |
| 271 | prot := TJSONProtocolImpl.Create( |
| 272 | TStreamTransportImpl.Create( |
| 273 | TThriftStreamAdapterDelphi.Create( stm, FALSE), nil, config)); |
| 274 | FLogger.Expect( prot.ReadString = G_CLEF_AND_CYRILLIC_TEXT, 'Reading JSON with chars > 8 bit'); |
| 275 | |
| 276 | |
| 277 | finally |
| 278 | stm.Free; |
| 279 | prot := nil; //-> Release |
| 280 | FLogger.StartTestGroup( '', test_Unknown); // no more tests here |
| 281 | end; |
| 282 | end; |
| 283 | |
| 284 | |
| 285 | procedure TQuickUnitTests.HashSetTest; |
| 286 | var container : IThriftHashSet<Integer>; |
| 287 | testdata : array of Integer; |
| 288 | i : Integer; |
| 289 | const |
| 290 | TEST_COUNT = 4096; |
| 291 | begin |
| 292 | StartTestGroup( 'IThriftHashSet<T> implementation', test_Containers); |
| 293 | |
| 294 | // prepare test data |
| 295 | SetLength( testdata, 5); |
| 296 | testdata[0] := -2; |
| 297 | testdata[1] := 0; |
| 298 | testdata[2] := 42; |
| 299 | testdata[3] := MaxInt; |
| 300 | testdata[4] := Low(Integer); |
| 301 | |
| 302 | // first insert |
| 303 | container := TThriftHashSetImpl<Integer>.Create; |
| 304 | for i in testdata do begin |
| 305 | Expect( container.Add( i), 'add first '+IntToStr(i)); |
| 306 | Expect( container.Contains( i), 'contains '+IntToStr(i)); |
| 307 | end; |
| 308 | Expect( container.Count = Length(testdata), 'container size'); |
| 309 | |
| 310 | // insert again |
| 311 | for i in testdata do begin |
| 312 | Expect( not container.Add( i), 'add second '+IntToStr(i)); |
| 313 | Expect( container.Contains( i), 'contains '+IntToStr(i)); |
| 314 | end; |
| 315 | Expect( container.Count = Length(testdata), 'container size'); |
| 316 | |
| 317 | // remove |
| 318 | for i in testdata do begin |
| 319 | Expect( container.Remove( i), 'first remove '+IntToStr(i)); |
| 320 | Expect( not container.Contains( i), 'not contains '+IntToStr(i)); |
| 321 | end; |
| 322 | Expect( container.Count = 0, 'container size'); |
| 323 | |
| 324 | // remove again |
| 325 | for i in testdata do begin |
| 326 | Expect( not container.Remove( i), 'second remove '+IntToStr(i)); |
| 327 | Expect( not container.Contains( i), 'not contains '+IntToStr(i)); |
| 328 | end; |
| 329 | Expect( container.Count = 0, 'container size'); |
| 330 | |
| 331 | // append and clear |
| 332 | for i := 0 to TEST_COUNT-1 do begin |
| 333 | container.Add(-i); |
| 334 | container.Add(+i); |
| 335 | end; |
| 336 | Expect( container.Count = 2*TEST_COUNT-1, 'container size check'); |
| 337 | Expect( not container.Contains( -TEST_COUNT), 'element not contained'); |
| 338 | Expect( not container.Contains( TEST_COUNT), 'element not contained'); |
| 339 | container.Clear; |
| 340 | Expect( container.Count = 0, 'count=0 after clear'); |
| 341 | end; |
| 342 | |
| 343 | |
| 344 | |
| 345 | |
| 346 | |
| 347 | |
| 348 | |
| 349 | |
| 350 | end. |