1unit tcbufferedfilestream; 2 3{$mode objfpc}{$H+} 4 5interface 6 7uses 8 Classes, SysUtils, fpcunit, testregistry, bufstream; 9 10type 11 12 { TTestBufferedFileStream } 13 14 TTestBufferedFileStream= class(TTestCase) 15 private 16 const 17 TEST_RANDOM_READS=10000; 18 TEST_SEQUENTIAL_READS=1000000; 19 TEST_FILENAME='testfile.bin'; 20 TEST_WRITEC_FILE='testwritecache.bin'; 21 TEST_WRITEF_FILE='testwritedirec.bin'; 22 private 23 function CompareStreams(const aStream1: TStream; const aStream2: TStream): Boolean; 24 protected 25 procedure SetUp; override; 26 procedure TearDown; override; 27 published 28 procedure TestCacheRead; 29 procedure TestCacheWrite; 30 procedure TestCacheSeek; 31 end; 32 33implementation 34 35procedure TTestBufferedFileStream.TestCacheRead; 36var 37 lBufferedStream: TBufferedFileStream; 38 lStream: TFileStream; 39 b: array [0..10000-1] of char; 40 j,k: integer; 41 lBytesToRead: integer; 42 lEffectiveRead: integer; 43 {$IFDEF CHECK_AGAINST_FILE} 44 lEffectiveRead2: integer; 45 {$ENDIF} 46 lReadPosition: int64; 47 lCheckInitV: integer; 48 lTick: QWord; 49begin 50 b[0]:=#0; // Avoid initalization hint 51 lBufferedStream:=TBufferedFileStream.Create(TEST_FILENAME,fmOpenRead or fmShareDenyWrite); 52 lStream:=TFileStream.Create(TEST_FILENAME,fmOpenRead or fmShareDenyWrite); 53 try 54 RandSeed:=1; 55 Randomize; 56 lTick:=GetTickCount64; 57 for j := 0 to Pred(TEST_RANDOM_READS) do begin 58 lBytesToRead:=Random(10000); 59 lReadPosition:=Random(lBufferedStream.Size); 60 lBufferedStream.Position:=lReadPosition; 61 62 lEffectiveRead:=lBufferedStream.Read(b,lBytesToRead); 63 64 {$IFDEF CHECK_AGAINST_FILE} 65 // Now read without cache 66 lStream.Position:=lReadPosition; 67 lEffectiveRead2:=lStream.Read(b2,lBytesToRead); 68 if lEffectiveRead<>lEffectiveRead2 then begin 69 FAIL('Read length mismatch'); 70 end; 71 if not CompareMem(@b[0],@b2[0],lEffectiveRead) then begin 72 FAIL('Compare buffer data error'); 73 end; 74 F.Position:=0; 75 {$ELSE} 76 lCheckInitV:=lReadPosition mod 10; 77 for k := 0 to Pred(lEffectiveRead) do begin 78 if b[k]<>char(ord('0')+lCheckInitV mod 10) then begin 79 FAIL('Expected data error'); 80 end; 81 inc(lCheckInitV); 82 end; 83 {$ENDIF} 84 end; 85 // Writeln('CACHE ',TEST_RANDOM_READS,' random reads in ',GetTickCount64-lTick,' ms.'); 86 87 RandSeed:=1; 88 Randomize; 89 90 // Writeln('Same operation without cache'); 91 lTick:=GetTickCount64; 92 for j := 0 to Pred(TEST_RANDOM_READS) do begin 93 lBytesToRead:=Random(10000); 94 lReadPosition:=Random(lBufferedStream.Size); 95 96 lStream.Position:=lReadPosition; 97 lEffectiveRead:=lStream.Read(b,lBytesToRead); 98 99 lCheckInitV:=lReadPosition mod 10; 100 for k := 0 to Pred(lEffectiveRead) do begin 101 if b[k]<>char(ord('0')+lCheckInitV mod 10) then begin 102 FAIL('Expected data error'); 103 end; 104 inc(lCheckInitV); 105 end; 106 end; 107 // Writeln('FILE ',TEST_RANDOM_READS,' random reads in ',GetTickCount64-lTick,' ms.'); 108 109 // Writeln('Check sequential read'); 110 111 RandSeed:=1; 112 Randomize; 113 lTick:=GetTickCount64; 114 lBytesToRead:=1; 115 lReadPosition:=0; 116 lBufferedStream.Position:=lReadPosition; 117 lStream.Position:=lReadPosition; 118 for j := 0 to Pred(TEST_SEQUENTIAL_READS) do begin 119 120 lEffectiveRead:=lBufferedStream.Read(b,lBytesToRead); 121 122 {$IFDEF CHECK_AGAINST_FILE} 123 // Now read without cache 124 lEffectiveRead2:=lStream.Read(b2,lBytesToRead); 125 if lEffectiveRead<>lEffectiveRead2 then begin 126 FAIL('Read length mismatch'); 127 end; 128 if not CompareMem(@b[0],@b2[0],lEffectiveRead) then begin 129 FAIL('Compare buffer data error'); 130 end; 131 F.Position:=0; 132 {$ELSE} 133 lCheckInitV:=lReadPosition mod 10; 134 for k := 0 to Pred(lEffectiveRead) do begin 135 if b[k]<>char(ord('0')+lCheckInitV mod 10) then begin 136 FAIL('Expected data error'); 137 end; 138 inc(lCheckInitV); 139 end; 140 {$ENDIF} 141 inc(lReadPosition,lBytesToRead); 142 end; 143 // Writeln('CACHE ',TEST_SEQUENTIAL_READS,' byte sequential reads in ',GetTickCount64-lTick,' ms.'); 144 145 RandSeed:=1; 146 Randomize; 147 lTick:=GetTickCount64; 148 lBytesToRead:=1; 149 lReadPosition:=0; 150 lStream.Position:=lReadPosition; 151 for j := 0 to Pred(TEST_SEQUENTIAL_READS) do begin 152 153 lEffectiveRead:=lStream.Read(b,lBytesToRead); 154 155 lCheckInitV:=lReadPosition mod 10; 156 for k := 0 to Pred(lEffectiveRead) do begin 157 if b[k]<>char(ord('0')+lCheckInitV mod 10) then begin 158 FAIL('Expected data error'); 159 end; 160 inc(lCheckInitV); 161 end; 162 inc(lReadPosition,lBytesToRead); 163 end; 164 // Writeln('FILE ',TEST_SEQUENTIAL_READS,' byte sequential reads in ',GetTickCount64-lTick,' ms.'); 165 166 // Writeln('CACHE Trying read beyond limits'); 167 lBufferedStream.Position:=lBufferedStream.Size-1; 168 lEffectiveRead:=lBufferedStream.Read(b,2); 169 if lEffectiveRead<>1 then begin 170 FAIL('Read beyond limits, returned bytes: '+inttostr(lEffectiveRead)); 171 end else begin 172 // Writeln('CACHE OK, read beyond limits returns 0 bytes.'); 173 end; 174 finally 175 lBufferedStream.Free; 176 lStream.Free; 177 end; 178end; 179 180procedure TTestBufferedFileStream.TestCacheWrite; 181const 182 EXPECTED_SIZE=10000000; 183 TEST_ROUNDS=100000; 184var 185 lBufferedStream: TBufferedFileStream; 186 lStream: TFileStream; 187 lVerifyStream1,lVerifyStream2: TFileStream; 188 b: array [0..10000-1] of char; 189 j: integer; 190 lBytesToWrite: integer; 191 lWritePosition: int64; 192begin 193 // Writeln('Testing write cache'); 194 // All test should return the same random sequence 195 RandSeed:=1; 196 Randomize; 197 for j := 0 to Pred(10000) do begin 198 b[j]:='0'; 199 end; 200 lBufferedStream:=TBufferedFileStream.Create(TEST_WRITEC_FILE,fmCreate); 201 lStream:=TFileStream.Create(TEST_WRITEF_FILE,fmCreate); 202 try 203 for j := 0 to Pred(EXPECTED_SIZE div Sizeof(b)) do begin 204 lBufferedStream.Write(b,sizeof(b)); 205 lStream.Write(b,sizeof(b)); 206 end; 207 for j := 0 to Pred(Sizeof(b)) do begin 208 b[j]:=char(ord('0')+j mod 10); 209 end; 210 finally 211 lBufferedStream.Free; 212 lStream.Free; 213 end; 214 lBufferedStream:=TBufferedFileStream.Create(TEST_WRITEC_FILE,fmOpenReadWrite); 215 lStream:=TFileStream.Create(TEST_WRITEF_FILE,fmOpenWrite); 216 try 217 for j := 0 to Pred(TEST_ROUNDS) do begin 218 if lStream.Size<>lBufferedStream.Size then begin 219 FAIL('Mismatched lengths'); 220 end; 221 lWritePosition:=Random(EXPECTED_SIZE); 222 lBytesToWrite:=Random(sizeof(b)); 223 lBufferedStream.Position:=lWritePosition; 224 lStream.Position:=lWritePosition; 225 lBufferedStream.Write(b,lBytesToWrite); 226 lStream.Write(b,lBytesToWrite); 227 // if j mod 1273 = 0 then write(j,' / ',TEST_ROUNDS,#13); 228 end; 229 // Writeln(TEST_ROUNDS,' / ',TEST_ROUNDS); 230 if lStream.Size<>lBufferedStream.Size then begin 231 FAIL('Mismatched lengths'); 232 end; 233 finally 234 lBufferedStream.Free; 235 lStream.Free; 236 end; 237 238 // Verify both generated files are identical. 239 lVerifyStream1:=TFileStream.Create(TEST_WRITEC_FILE,fmOpenRead or fmShareDenyWrite); 240 lVerifyStream2:=TFileStream.Create(TEST_WRITEF_FILE,fmOpenRead or fmShareDenyWrite); 241 try 242 if not CompareStreams(lVerifyStream1,lVerifyStream2) then begin 243 FAIL('Streams are different!!'); 244 end else begin 245 // Writeln('Streams are identical. OK.'); 246 end; 247 finally 248 lVerifyStream1.Free; 249 lVerifyStream2.Free; 250 end; 251end; 252 253procedure TTestBufferedFileStream.TestCacheSeek; 254var 255 lBufferedStream: TBufferedFileStream; 256 lStream: TFileStream; 257 bBuffered: array [0..10000] of BYTE; 258 bStream: array [0..10000] of BYTE; 259 bread : Integer; 260 261begin 262 bBuffered[0]:=0; // Avoid initalization hint 263 bStream[0]:=0; // Avoid initalization hint 264 lBufferedStream:=TBufferedFileStream.Create(TEST_FILENAME,fmOpenRead or fmShareDenyWrite); 265 lStream:=TFileStream.Create(TEST_FILENAME,fmOpenRead or fmShareDenyWrite); 266 try 267 // Writeln('Set position=-1'); 268 lStream.Position:=-1; 269 // Writeln('TFileStream position=',lStream.Position); 270 lBufferedStream.Position:=-1; 271 // Writeln('Buffered position=',lBufferedStream.Position); 272 if lStream.Position<>lBufferedStream.Position then begin 273 FAIL('Positions are not the same.'); 274 end else begin 275 // Writeln('Positions are the same.'); 276 end; 277 278 // Writeln('Read data when position=-1'); 279 bread:=lStream.Read(bBuffered[0],10); 280 // Writeln('TFileStream read bytes : ',bread); 281 // Writeln('TFileStream end position: ',lStream.Position); 282 bread:=lBufferedStream.Read(bStream[0],10); 283 // Writeln('Buffered read bytes: ',bread); 284 // Writeln('Buffered end position: ',lBufferedStream.Position); 285 if (not CompareMem(@bBuffered[0],@bStream[0],10)) or (lStream.Position<>lBufferedStream.Position) then begin 286 FAIL('Read data or positions are not the same.'); 287 end else begin 288 // Writeln('Read data at -1 is the same.'); 289 end; 290 291 // Writeln('Testing Seek operations'); 292 // Writeln('Seek -1 from beginning'); 293 bread:=lStream.Seek(-1,soBeginning); 294 // Writeln('Stream seek result : ',bread); 295 bread:=lBufferedStream.Seek(-1,soBeginning); 296 // Writeln('Buffered seek result: ',); 297 298 // Writeln('Read data when Seek -1'); 299 bread:=lStream.Read(bBuffered[0],10); 300 // Writeln('TFileStream read bytes : ',bread); 301 // Writeln('TFileStream end position: ',lStream.Position); 302 bread:=lBufferedStream.Read(bStream[0],10); 303 // Writeln('Buffered read bytes: ',bread); 304 // Writeln('Buffered end position: ',lBufferedStream.Position); 305 if (not CompareMem(@bBuffered[0],@bStream[0],10)) or (lStream.Position<>lBufferedStream.Position) then begin 306 FAIL('Read data or positions are not the same.'); 307 end else begin 308 // Writeln('Read data at -1 is the same.'); 309 end; 310 311 // Writeln('Seek -current*2 from current'); 312 bread:=lStream.Seek(lStream.Position*-2,soCurrent); 313 // Writeln('Stream seek result : ',bread); 314 bread:=lBufferedStream.Seek(lBufferedStream.Position*-2,soCurrent); 315 // Writeln('Buffered seek result: ',bread); 316 // Writeln('Read data when Seek from current -current*2'); 317 bread:=lStream.Read(bBuffered[0],10); 318 // Writeln('TFileStream read bytes : ',bread); 319 // Writeln('TFileStream end position: ',lStream.Position); 320 bread:=lBufferedStream.Read(bStream[0],10); 321 // Writeln('Buffered read bytes: ',); 322 // Writeln('Buffered end position: ',lBufferedStream.Position); 323 if (not CompareMem(@bBuffered[0],@bStream[0],10)) or (lStream.Position<>lBufferedStream.Position) then begin 324 FAIL('Read data or positions are not the same.'); 325 end else begin 326 // Writeln('Read data at -current*2 is the same.'); 327 end; 328 finally 329 lBufferedStream.Free; 330 lStream.Free; 331 end; 332end; 333 334procedure TTestBufferedFileStream.SetUp; 335var 336 F: TFileStream; 337 b: array [0..10000-1] of char; 338 j: integer; 339begin 340 for j := 0 to Pred(10000) do begin 341 b[j]:=char(ord('0')+j mod 10); 342 end; 343 F:=TFileStream.Create(TEST_FILENAME,fmCreate); 344 for j := 0 to Pred(1000) do begin 345 F.Write(b,sizeof(b)); 346 end; 347 F.Free; 348end; 349 350procedure TTestBufferedFileStream.TearDown; 351begin 352 DeleteFile(TEST_FILENAME); 353 DeleteFile(TEST_WRITEC_FILE); 354 DeleteFile(TEST_WRITEF_FILE); 355end; 356 357function TTestBufferedFileStream.CompareStreams(const aStream1: TStream; 358 const aStream2: TStream): Boolean; 359const 360 BUFFER_SIZE=5213; // Odd number 361var 362 b1: array [0..BUFFER_SIZE-1] of BYTE; 363 b2: array [0..BUFFER_SIZE-1] of BYTE; 364 lReadBytes: integer; 365 lAvailable: integer; 366 lEffectiveRead1: integer; 367 lEffectiveRead2: integer; 368begin 369 b1[0]:=0; // Avoid initalization hint 370 b2[0]:=0; // Avoid initalization hint 371 Result:=false; 372 if aStream1.Size<>aStream2.Size then exit; 373 aStream1.Position:=0; 374 aStream2.Position:=0; 375 while aStream1.Position<aStream1.Size do begin 376 lAvailable:=aStream1.Size-aStream1.Position; 377 if lAvailable>=BUFFER_SIZE then begin 378 lReadBytes:=BUFFER_SIZE; 379 end else begin 380 lReadBytes:=aStream1.Size-aStream1.Position; 381 end; 382 lEffectiveRead1:=aStream1.Read(b1[0],lReadBytes); 383 lEffectiveRead2:=aStream2.Read(b2[0],lReadBytes); 384 if lEffectiveRead1<>lEffectiveRead2 then exit; 385 if not CompareMem(@b1[0],@b2[0],lEffectiveRead1) then exit; 386 end; 387 Result:=true; 388end; 389 390initialization 391 RegisterTest(TTestBufferedFileStream); 392end. 393 394