unit uDocCacheTest;

{$I XpDefine.inc}

interface

uses
  XpBase,
  XpDOM,
{$IFDEF UsingCLX}
  XpQXSLPr,
{$ELSE}
  XpvXSLPr,
{$ENDIF}
  TestFramework;

type
  TXpDocCacheTests = class(TTestCase)
  protected
    FCache : TXpDocumentCache;

    function GetURI(const sDoc : DOMString) : DOMString;
    procedure Load(const sDoc : DOMString);
    procedure Setup; override;
    procedure Teardown; override;
  published
    procedure TestGetSame;
      { Verify that a document loaded into the cache is retrieved
        from the cache when it is next requested. }
    procedure TestInvalidDocName;
      { Verify that an invalid document name raises an error. }
    procedure TestIsDOM;
      { Verify that we retrieve an instance of TXpObjModel from the
        cache. }
    procedure TestMaxDoc;
      { Verify that least frequently used document is purged from the
        cache. }
  end;

implementation

uses
  SysUtils;

const
{$IFDEF LINUX}
  csDataDir = '../data/xsltgood/';
{$ELSE}
  csDataDir = '..\data\xsltgood\';
{$ENDIF}
  csLongSpeech = 'longest-speech.xml';

{===TXpDocCacheTests==================================================}
procedure TXpDocCacheTests.Load(const sDoc : DOMString);
begin
  FCache.Get(GetURI(sDoc));
end;
{--------}
function TXpDocCacheTests.GetURI(const sDoc : DOMString) : DOMString;
begin
  Result := csDataDir + sDoc;
end;
{--------}
procedure TXpDocCacheTests.Setup;
begin
  inherited;
  FCache := TXpDocumentCache.Create(nil);
end;
{--------}
procedure TXpDocCacheTests.Teardown;
begin
  FCache.Free;
  inherited;
end;
{--------}
procedure TXpDocCacheTests.TestGetSame;
var
  wInx : Integer;
begin
  Randomize;
  for wInx := 1 to random(10) + 2 do
    Load(csLongSpeech);
  CheckEquals(1, FCache.Count);
end;
{--------}
procedure TXpDocCacheTests.TestInvalidDocName;
var
  bExcept : Boolean;
begin
  bExcept := False;
  try
    FCache.Get('glorp.xml');
  except
    bExcept := True;
  end;
  Assert(bExcept, 'Exception not raised');
end;
{--------}
procedure TXpDocCacheTests.TestIsDOM;
var
  oDOM : TXpObjModel;
begin
  Load(csLongSpeech);
  oDOM := FCache.Get(GetURI(csLongSpeech));
  Assert(oDOM is TXpObjModel, 'Incorrect object class returned');
end;
{--------}
procedure TXpDocCacheTests.TestMaxDoc;
const
  ciMaxToGen = 50;
  ciLoopCnt = 20;
  csFileName = 'dummy%d.xml';
var
  oDOM,
  oCacheDOM : TXpObjModel;
  sDir,
  sDummy,
  sFileName : string;
  wCheckRemoved,
  wCount,
  wLoaded,
  wLoop,
  wWritten,
  wInx,
  wInx2 : Integer;
begin
  { Init }
  sDummy :=
    '<?xml version="1.0"?>' +
    '<root>' +
    '  <sub1>sub1Text</sub1>' +
    '  <sub2>sub2Text</sub2>' +
    '</root>';

  Randomize;
  oDOM := TXpObjModel.Create(nil);
  sDir := ExpandFileName(csDataDir);
  try
    for wLoop := 1 to ciLoopCnt do begin
      FCache.Clear;
      wWritten := 0;
      wCount := random(ciMaxToGen) + 1;
      try
        oDOM.LoadMemory(sDummy[1], Length(sDummy));
        { Create a bunch of dummy XML documents. }
        for wInx := 1 to wCount do begin
          oDOM.SaveToFile(sDir + Format(csFileName, [wInx]));
          inc(wWritten);
        end;

        wLoaded := 0;
        wCheckRemoved := 0;
        for wInx := 1 to Pred(wCount) do begin
          oCacheDOM := FCache.Get(sDir + Format(csFileName, [wInx]));
          Assert(oCacheDOM <> nil,
                 Format('Document not %d retrieved during usage increment',
                        [sFileName]));
          inc(wLoaded);
          { Have we reached a boundary point? }
          if (wCheckRemoved > 0) or
             (wLoaded mod FCache.MaxDocs = 0) then begin
            { Yes. Increment usage of all docs but the last by one. On the next
              load, verify the count & verify the least used document is
              out of the cache. }
            for wInx2 := (wLoaded - XpcMaxDocs + 2) to wLoaded do begin
              sFileName := sDir + Format(csFileName, [wInx2]);
              oCacheDOM := FCache.Get(sFileName);
              Assert(oCacheDOM <> nil,
                     Format('Document not %d retrieved on usage inc',
                            [sFileName]));
              CheckEquals(sFileName, oCacheDOM.DocLocation + oCacheDOM.DocName,
                          'Document mismatch on usage inc retrieval');
            end;
            wCheckRemoved := wLoaded - XpcMaxDocs + 1;
          end;

          { Do we need to check that a document was removed? }
          if wCheckRemoved <> 0 then begin
            { Yes. }
            { Fetch the next document. This should cause doc # wCheckRemoved
              to be thrown out of the cache. }
            oCacheDOM := FCache.Get(sDir + Format(csFileName, [wLoaded + 1]));
            Assert(not FCache.InCache
                         (sDir + Format(csFileName, [wCheckRemoved])),
                   Format('Document %s still in cache',
                          [sDir + Format(csFileName, [wCheckRemoved])]));
            inc(wCheckRemoved);
            { Verify the other documents are still accessible. }
            for wInx2 := wLoaded downto (wLoaded - XpcMaxDocs + 1) do begin
              sFileName := sDir + Format(csFileName, [wInx2]);
              oCacheDOM := FCache.Get(sFileName);
              Assert(oCacheDOM <> nil,
                     Format('Document not %d retrieved on verification',
                            [sFileName]));
              CheckEquals(sFileName, oCacheDOM.DocLocation + oCacheDOM.DocName,
                          'Document mismatch on verification retrieval');
            end;
          end;
        end;

      finally
        for wInx := 1 to wWritten do
          DeleteFile(Format(sDir + csFileName, [wInx]));
      end;
    end;  { for wloop }
  finally
    oDOM.Free;
  end;
end;
{=====================================================================}

initialization
  RegisterTest('Document Cache Tests', TXpDocCacheTests);
end.

