unit uXSLTTest;

{$I XpDefine.inc}

interface
uses
  TestFramework,
  SysUtils,
  Classes,
{$IFDEF MSWINDOWS}
  Forms,
  Windows,
{$ENDIF}
{$IFDEF LINUX}
  QForms,
{$ENDIF}
  XpParser,
  XpDOM,
  XpBase,
{$IFDEF UsingCLX}
  XpQFlBas,
  XpQXSLPr,
  XpQFlHTM,
  XpQFlXML,
{$ELSE}
  XpvFlBas,
  XpvXSLPr,
  XpvFlHTM,
  XpvFlXML,
{$ENDIF}
{$IFDEF LINUX}
  uProgress;
{$ENDIF}
{$IFDEF MSWINDOWS}
  uProgressW;
{$ENDIF}


type
  TXpXSLTTests = class(TTestCase)
    protected
{$IFDEF UsingCLX}
      FProc : TXpQXSLProcessor;
{$ELSE}
      FProc : TXpXSLProcessor;
{$ENDIF}

      function CompareStreams(aOldSt, aNewSt : TMemoryStream;
                              aCount : integer) : boolean;
        { Compares the specified number of bytes in both streams, starting at
          position zero in both streams. Returns True if the streams are
          equivalent. }

      procedure OnFunctionHandler(oOwner        : TObject;
                                  sFunctionName : DOMString;
                                  oArguments    : TStringList;
                              var sResult       : DOMString);
        { Associated with TestOnFunction. }

      procedure OnTextNodeTGP1(oOwner : TObject; oNode: TXpText);
      procedure OnTextNodeTGP2(oOwner : TObject; oNode: TXpText);
      procedure OnTextNodeTGP3(oOwner : TObject; oNode: TXpText);

      procedure OnTextNodeTGV1(oOwner : TObject; oNode: TXpText);
      procedure OnTextNodeTGV2(oOwner : TObject; oNode: TXpText);
      procedure OnTextNodeTGV3(oOwner : TObject; oNode: TXpText);

      procedure OnXSLMessage(Sender : TObject;
                       const Msg : DOMString;
                         var Terminate : Boolean);
                         
      procedure ProcessDocs(oFilter : TXpFilterBase;
                      const sDocPath : string;
                      const sMasterPath : string;
                      const sLogName : string;
                      const bNormalizeSrc : Boolean;
                      const bOverrideFilter : Boolean);
                   
    public
      procedure DoAttributes(oOwner     : TObject;
                       sName,
                       sValue     : DOMString;
                       bSpecified : Boolean);
    published
      procedure TestAttrSetCircularRef;
      procedure TestElementAvailable;
      procedure TestGlobalParams;
      procedure TestGlobalVars;
      procedure TestHTML;
        { Test situations involving HTML output. }
      procedure TestOnFunction;
        { Verify the OnFunction event is called. }
      procedure TestMessage;
        { Test the xsl:message element. }
      procedure TestOutputNoFilter;
        { Test xsl:output element where no static filter specified. }
      procedure TestOutputOverrideFilter;
        { Test xsl:output element where a static filter specified &
          the xsl:output element overrides the static filter. }
      procedure TestXML;
      procedure TestXMLStrip;
        { Test the xsl:strip-space and xsl:preserve-space elements. }

      procedure TestDecimalSeparator;
        { Ensure we convert numbers to strings and string to numbers
          correctly. }
  end;

implementation
uses
  XpChrFlt,
  XpExcept,
{$IFDEF UsingCLX}
  XpQXSLT;
{$ELSE}
  XpvXSLT;
{$ENDIF}

{=====================================================================}
procedure TXpXSLTTests.DoAttributes(oOwner     : TObject;
                       sName,
                       sValue     : DOMString;
                       bSpecified : Boolean);
begin

end;
{--------}
function TXpXSLTTests.CompareStreams(aOldSt, aNewSt : TMemoryStream;
                                     aCount : integer) : boolean;
var
  OldChar, NewChar : Char;
  i : integer;
begin
  aOldSt.Position := 0;
  aNewSt.Position := 0;
  Result := True;
  for i := 0 to pred(aCount) do begin
    aOldSt.Read(OldChar, 1);
    { Skip over #13. }
    if OldChar = #13 then
      aOldSt.Read(OldChar, 1);
    aNewSt.Read(NewChar, 1);
    { Skip over #13. }
    if NewChar = #13 then
      aNewSt.Read(NewChar, 1);
    Result := (OldChar = NewChar);
    if not Result then
      break;
  end;
end;
{--------}
procedure TXpXSLTTests.OnFunctionHandler(oOwner        : TObject;
                                         sFunctionName : DOMString;
                                         oArguments    : TStringList;
                                     var sResult       : DOMString);
var
  oNodeList : TXpNodeList;
  sXPathExpr : DOMString;
  sAttrib : DOMString;
  wInx,
  wSum : Integer;
begin
  sResult := '';

  if UpperCase(sFunctionName) = 'AVG' then begin
    { Calculate the average of numbers stored in a specific attribute
      of the elements within a nodeset.

      Two arguments required:
      1. XPath expression identifying the elements containing the numbers
         to be averaged (e.g., 'student')
      2. Name of the attribute containing the number to be averaged
         (e.g., 'score'). }
    sXPathExpr := oArguments.Strings[0];
    sAttrib := oArguments.Strings[1];
    if (sXPathExpr = '') or (sAttrib = '') then
      Exit;

    { Query the nodeset. }
    oNodeList := TXpElement(oOwner).SelectNodes(sXPathExpr);
    try
      wSum := 0;
      for wInx := 0 to Pred(oNodeList.Length) do
        inc(wSum, StrToInt(TXpElement(oNodeList.Item(wInx)).GetAttribute(sAttrib)));
      if oNodeList.Length > 0 then
        sResult := IntToStr(wSum div oNodeList.Length);
    finally
      oNodeList.Free;
    end;
  end
end;
{--------}
procedure TXpXSLTTests.OnTextNodeTGP1(oOwner : TObject; oNode: TXpText);
var
  oValue : TXpValue;
begin
  { Verify global parameter can be retrieved during execution. }
  oValue := FProc.GetParameter('testparam');
  Assert(oValue <> nil, 'Test 1c failed');

  CheckEquals('MyParamValue', oValue.AsString, 'Test 1d failed');
end;
{--------}
procedure TXpXSLTTests.OnTextNodeTGP2(oOwner : TObject; oNode: TXpText);
var
  oValue : TXpValue;
begin
  { Verify global parameter can be changed during execution. }
  oValue := FProc.GetParameter('testparam');
  Assert(oValue <> nil, 'Test 1e failed');

  oValue.AsString := 'newValue';
  oValue := FProc.GetParameter('testparam');
  Assert(oValue <> nil, 'Test 1f failed');
  CheckEquals('newValue', oValue.AsString, 'Test 1g failed');
end;
{--------}
procedure TXpXSLTTests.OnTextNodeTGP3(oOwner : TObject; oNode: TXpText);
var
  oValue : TXpValue;
begin
  { Verify default value used if no parameter value specified by app. }
  oValue := FProc.GetParameter('testparam');
  Assert(oValue <> nil, 'Test 1h failed');

  CheckEquals('aDefaultValue', oValue.AsString, 'Test 1i failed');
end;
{--------}
procedure TXpXSLTTests.ProcessDocs(oFilter : TXpFilterBase;
                             const sDocPath : string;
                             const sMasterPath : string;
                             const sLogName : string;
                             const bNormalizeSrc : Boolean;
                             const bOverrideFilter : Boolean);
var
  SR         : TSearchRec;
  FindResult : Boolean;
  DOM        : TXpObjModel;
  ParsedOK   : Boolean;
  FailedTest : Boolean;
  Log        : TStringList;
  Idx        : Integer;
  FileName, MasterFileName   : string;
  FileCount  : Integer;
  Dlg        : TDlgProgress;
{$IFDEF UsingCLX}
  XSLProcessor : TXpQXSLProcessor;
{$ELSE}
  XSLProcessor : TXpXSLProcessor;
{$ENDIF}
  GeneratedStream, MasterStream : TMemoryStream;
begin
  Log := nil;
  FailedTest := False;
  ParsedOK := True;

  FindResult := FindFirst(sDocPath + '*.xml', faAnyFile, SR) = 0;
{$IFDEF UsingCLX}
  XSLProcessor := TXpQXSLProcessor.Create(nil);
{$ELSE}
  XSLProcessor := TXpXSLProcessor.Create(nil);
{$ENDIF}
  try
    { Set up the XSL processor and filter. }
    XSLProcessor.RaiseErrors := False;
    XSLProcessor.Filter := oFilter;
    Log := TStringList.Create;
    FileCount := 0;
    if FindResult then
      repeat
        inc(FileCount);
      until FindNext(SR) <> 0;
    SysUtils.FindClose(SR);

    Dlg := TDlgProgress.Create(nil);
    try
      Dlg.Show;
      Application.ProcessMessages;
      FindResult := FindFirst(sDocPath + '*.xml', faAnyFile, SR) = 0;
      if FindResult then begin
        repeat
          if AnsiCompareText(ExtractFileExt(Sr.Name), '.xml') = 0 then begin
            {Update progressbar}
            Dlg.FileName := FileName;
            Dlg.Position := Dlg.Position + 1;
            Dlg.Max := FileCount;

            {Verify that XML document is OK}
            DOM := TXpObjModel.Create(nil);
            DOM.NormalizeData := bNormalizeSrc;
            try
              FileName :=   sDocPath + Sr.Name;
              try
                ParsedOK := DOM.LoadDataSource(FileName);
              except
                on E:Exception do
                  ParsedOK := False;
              end;
              { Did the document load successfully?}
              if ParsedOK then begin
                { Yes. Look for the corresponding stylesheet. }
                FileName := ChangeFileExt(FileName, '.xsl');
                { If a stylesheet exists then use it otherwise we assume the
                  document is using an embedded stylesheet or has a reference
                  to an external stylesheet. }
                if FileExists(FileName) then
                  XSLProcessor.StyleURL := FileName
                else
                  XSLProcessor.StyleURL := '';
                XSLProcessor.XmlObjModel := DOM;
                if bOverrideFilter then
                  XSLProcessor.OverrideFilter := True;
                if XSLProcessor.ApplyStyle then begin
                  { Find the master output file & compare to generated file. }
                  MasterFileName := sMasterPath +
                                    ChangeFileExt
                                      (ExtractFileName(FileName), '.out');
                  if FileExists(MasterFileName) then begin
                    FileName := ChangeFileExt(Filename, '.tst');
                    { Using static filter? }
                    if oFilter = nil then
                      { No. }
                      XSLProcessor.Filter.SaveToFile(FileName)
                    else begin
                      { Yes. }
                      if (oFilter is TXpFilterXML) then
                        TXpFilterXML(oFilter).OutputEncoding := ceISO88591;
                      if (oFilter is TXpFilterHTML) then
                        TXpFilterHTML(oFilter).OutputEncoding := ceISO88591;
                      oFilter.SaveToFile(FileName);
                    end;
                    GeneratedStream := TMemoryStream.Create;
                    GeneratedStream.LoadFromFile(FileName);
                    MasterStream := TMemoryStream.Create;
                    MasterStream.LoadFromFile(MasterFileName);
                    try
                      if not CompareStreams(MasterStream, GeneratedStream,
                                            GeneratedStream.Size) then begin
                        FailedTest := True;
                        Log.Add('-----------------------------------------------');
                        Log.Add('  Test failed : ' + FileName);
                        Log.Add('-----------------------------------------------');
                        Log.Add('');
                        Log.SaveToFile(sLogName);

                        Dlg.AddError('-----------------------------------------------');
                        Dlg.AddError('  Test failed : ' + FileName);
                        Dlg.AddError('-----------------------------------------------');
                        Dlg.AddError('');
                      end;
                    finally
                      GeneratedStream.Free;
                      MasterStream.Free;
                    end;
                  end
                  else begin
                    FailedTest := True;
                    Log.Add('-----------------------------------------------');
                    Log.Add('  Output master not found : ' + MasterFileName);
                    Log.Add('-----------------------------------------------');
                    Log.Add('');
                    Log.SaveToFile(sLogName);

                    Dlg.AddError('-----------------------------------------------');
                    Dlg.AddError('  Output master not found : ' + MasterFileName);
                    Dlg.AddError('-----------------------------------------------');
                    Dlg.AddError('');
                  end;
                end
                else begin
                  FailedTest := True;
                  Log.Add('-----------------------------------------------');
                  Log.Add('  XSL processing failure : ' + FileName);
                  Log.Add('-----------------------------------------------');
                  Log.Add('');
                  Log.SaveToFile(sLogName);

                  Dlg.AddError('-----------------------------------------------');
                  Dlg.AddError('  XSL processing failure : ' + FileName);
                  Dlg.AddError('-----------------------------------------------');
                  Dlg.AddError('');
                end;
              end else begin
                { No. Document failed to load. Log error information. }
                FailedTest := True;
                {Store error messages}
                Log.Add('-----------------------------------------------');
                Log.Add('  Error(s) parsing : ' + FileName);
                Log.Add('');
                for Idx := 0 to Pred(DOM.Errors.Count) do
                  Log.Add(DOM.Errors.Strings[Idx]);
                Log.Add('');
                Log.Add('-----------------------------------------------');
                Log.Add('');
                Log.Add('');
                Log.SaveToFile(sLogName);

                Dlg.AddError('-----------------------------------------------');
                Dlg.AddError('  Error(s) parsing : ' + FileName);
                Dlg.AddError('');
                for Idx := 0 to Pred(DOM.Errors.Count) do
                  Dlg.AddError(DOM.Errors.Strings[Idx]);
                Dlg.AddError('');
                Dlg.AddError('-----------------------------------------------');
                Dlg.AddError('');
                Dlg.AddError('');
              end
            finally
              DOM.Free;
            end;
          end;
        until FindNext(SR) <> 0;
      end else
        Fail('Documents not found');
    finally
      Dlg.Hide;
      Dlg.Free;
    end;
  finally
    Log.Free;
    SysUtils.FindClose(SR);
    XSLProcessor.Free;
  end;
  Assert(not FailedTest, 'At least one document failed. See ' +
                         sLogName + ' for info');
end;
{--------}
procedure TXpXSLTTests.TestAttrSetCircularRef;
var
  DOM : TXpObjModel;
begin
  DOM := TXpObjModel.Create(nil);
{$IFDEF UsingCLX}
  FProc := TXpQXSLProcessor.Create(nil);
{$ELSE}
  FProc := TXpXSLProcessor.Create(nil);
{$ENDIF}
  try
{$IFDEF MSWINDOWS}
    Assert(DOM.LoadDataSource('..\Data\XSLTbad\attrsetbad1.xml'));
{$ENDIF}
{$IFDEF LINUX}
    Assert(DOM.LoadDataSource('../data/xsltbad/attrsetbad1.xml'));
{$ENDIF}
    FProc.XmlObjModel := DOM;
    FProc.OnXSLMessage := OnXSLMessage;
    FProc.RaiseErrors := False;
{$IFDEF MSWINDOWS}
    FProc.StyleURL := '..\Data\XSLTbad\attrsetbad1.xsl';
{$ENDIF}
{$IFDEF LINUX}
    FProc.StyleURL := '../data/xsltbad/attrsetbad1.xsl';
{$ENDIF}
    Assert(not FProc.ApplyStyle, 'Stylesheet applied correctly');
    Assert(FProc.Errors.Count = 1, 'Invalid error count');
  finally
    FProc.Free;
    DOM.Free;
  end;
end;
{--------}
procedure TXpXSLTTests.TestElementAvailable;
var
  DOM : TXpObjModel;
  oFilter : TXpFilterText;
  XMLStr : string;
  StyleStr : string;
begin
  XMLStr :=
    '<root>' +
    '  <sub1/>' +
    '</root>';

  DOM := TXpObjModel.Create(nil);
  oFilter := TXpFilterText.Create(nil);
{$IFDEF UsingCLX}
  FProc := TXpQXSLProcessor.Create(nil);
{$ELSE}
  FProc := TXpXSLProcessor.Create(nil);
{$ENDIF}
  FProc.Filter := oFilter;
  try
    Assert(DOM.LoadMemory(XMLStr[1], Length(XMLStr)), 'test 1');
    FProc.XmlObjModel := DOM;

    StyleStr :=
      '<xsl:stylesheet version="1.0"' +
      '                      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">' +
      '<xsl:template match="/">' +
      '  <xsl:choose>' +
      '    <xsl:when test="element-available(''xsl:preserve-space'')">Yes</xsl:when>' +
      '    <xsl:otherwise>No</xsl:otherwise>' +
      '  </xsl:choose>' +
      '</xsl:template>' +
      '</xsl:stylesheet>';
      
    FProc.StyleData := StyleStr;
    Assert(FProc.ApplyStyle, 'test 2');
    CheckEquals('Yes', oFilter.Text, 'test 3');

    StyleStr :=
      '<xsl:stylesheet version="1.0"' +
      '                      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">' +
      '<xsl:template match="/">' +
      '  <xsl:choose>' +
      '    <xsl:when test="element-available(''xsl:doodah'')">Yes</xsl:when>' +
      '    <xsl:otherwise>No</xsl:otherwise>' +
      '  </xsl:choose>' +
      '</xsl:template>' +
      '</xsl:stylesheet>';

    FProc.StyleData := StyleStr;
    Assert(FProc.ApplyStyle, 'test 4');
    CheckEquals('No', oFilter.Text, 'test 5');

    StyleStr :=
      '<xsl:stylesheet version="1.0"' +
      '                      xmlns:xsl="http://www.w3.org/1999/XSL/Transform">' +
      '<xsl:template match="/">' +
      '  <xsl:choose>' +
      '    <xsl:when test="element-available(''fo:block'')">Yes</xsl:when>' +
      '    <xsl:otherwise>No</xsl:otherwise>' +
      '  </xsl:choose>' +
      '</xsl:template>' +
      '</xsl:stylesheet>';

    FProc.StyleData := StyleStr;
    Assert(FProc.ApplyStyle, 'test 6');
    CheckEquals('Yes', oFilter.Text, 'test 7');

  finally
    FProc.Free;
    oFilter.Free;
    DOM.Free;
  end;
end;
{--------}
procedure TXpXSLTTests.TestGlobalParams;
const csStylesheet =
  '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"' +
  ' version="1.0">' +
  '<xsl:param name="testparam" select="''aDefaultValue''"/>' +
  '<xsl:template match="*">' +
  '<xsl:value-of select="."/>' +
  '</xsl:template>' +
  '</xsl:stylesheet>';

var
  DOM : TXpObjModel;
  oValue, oValue2 : TXpValue;
  sDoc : string;
begin

  sDoc := '<root>Some text</root>';
  DOM := TXpObjModel.Create(nil);
{$IFDEF UsingCLX}
  FProc := TXpQXSLProcessor.Create(nil);
{$ELSE}
  FProc := TXpXSLProcessor.Create(nil);
{$ENDIF}
  try
    Assert(DOM.LoadMemory(sDoc[1], Length(sDoc)));
    FProc.XmlObjModel := DOM;
    FProc.RaiseErrors := True;
    FProc.StyleData := csStylesheet;

    { Verify they can be set & queried before execution. }
    oValue := TXpValue.Create;
    oValue.AsString := 'MyParamValue';
    FProc.SetParameter('testparam', oValue);
    CheckEquals('MyParamValue', FProc.GetParameter('testparam').AsString,
                'Test 1a failed');
    Assert(FProc.GetParameter('DoesNotExist') = nil, 'Test 1b failed');

    oValue2 := TXpValue.Create;
    oValue2.AsString := 'ValueToBeReplaced';
    FProc.SetParameter('testparam', oValue2);
    oValue := TXpValue.Create;
    oValue.AsString := 'MyParamValue';
    FProc.SetParameter('testparam', oValue);
    CheckEquals('MyParamValue', FProc.GetParameter('testparam').AsString,
                'Test 2a failed');
    Assert(FProc.GetParameter('DoesNotExist') = nil, 'Test 2b failed');

    { Verify they can be queried during execution. }
    FProc.OnTextNode := OnTextNodeTGP1;
    FProc.ApplyStyle;

    { Verify they can be set during execution. }
    FProc.OnTextNode := OnTextNodeTGP2;
    FProc.ApplyStyle;

    { Verify that original value restored after execution. }
    CheckEquals('MyParamValue', FProc.GetParameter('testparam').AsString,
                'Test 1j failed');

    { Verify they have a default value if not set. }
    FProc.ClearParameters;
    FProc.OnTextNode := OnTextNodeTGP3;
    FProc.ApplyStyle;
  finally
    FProc.Free;
    DOM.Free;
  end;
end;
{--------}
procedure TXpXSLTTests.OnTextNodeTGV1(oOwner : TObject; oNode: TXpText);
var
  oValue : TXpValue;
begin
  { Verify global variable can be retrieved during execution. }
  oValue := FProc.GetVariable('testvar');
  Assert(oValue <> nil, 'Test 1c failed');

  CheckEquals('MyVarValue', oValue.AsString, 'Test 1d failed');
end;
{--------}
procedure TXpXSLTTests.OnTextNodeTGV2(oOwner : TObject; oNode: TXpText);
var
  oValue : TXpValue;
begin
  { Verify global parameter can be changed during execution. }
  oValue := FProc.GetVariable('testvar');
  Assert(oValue <> nil, 'Test 1e failed');

  oValue.AsString := 'newValue';
  oValue := FProc.GetVariable('testvar');
  Assert(oValue <> nil, 'Test 1f failed');
  CheckEquals('newValue', oValue.AsString, 'Test 1g failed');
end;
{--------}
procedure TXpXSLTTests.OnTextNodeTGV3(oOwner : TObject; oNode: TXpText);
var
  oValue : TXpValue;
begin
  { Verify default value used if no parameter value specified by app. }
  oValue := FProc.GetVariable('testvar');
  Assert(oValue <> nil, 'Test 1h failed');

  CheckEquals('aDefaultValue', oValue.AsString, 'Test 1i failed');
end;
{--------}
procedure TXpXSLTTests.OnXSLMessage(Sender : TObject;
                              const Msg : DOMString;
                                var Terminate : Boolean);
begin
  Assert(Sender is TXpXSLMessage, 'Invalid sender');
  Assert(Msg = 'xsl:document not supported by this XSL processor.',
         'Incorrect message: ' + Msg);
  Assert(Terminate, 'Invalid value for Terminate');
end;
{--------}
procedure TXpXSLTTests.TestGlobalVars;
const csStylesheet =
  '<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"' +
  ' version="1.0">' +
  '<xsl:variable name="sumdumvar" select="''sumDumValue''"/>' +
  '<xsl:variable name="testvar" select="''aDefaultValue''"/>' +
  '<xsl:template match="*">' +
  '<xsl:value-of select="."/>' +
  '</xsl:template>' +
  '</xsl:stylesheet>';

var
  DOM : TXpObjModel;
  oValue : TXpValue;
  sDoc : string;
begin

  sDoc := '<root>Some text</root>';
  DOM := TXpObjModel.Create(nil);
{$IFDEF UsingCLX}
  FProc := TXpQXSLProcessor.Create(nil);
{$ELSE}
  FProc := TXpXSLProcessor.Create(nil);
{$ENDIF}
  try
    Assert(DOM.LoadMemory(sDoc[1], Length(sDoc)));
    FProc.XmlObjModel := DOM;
    FProc.RaiseErrors := True;
    FProc.StyleData := csStylesheet;

    { Verify they can be set & queried before execution. }
    oValue := TXpValue.Create;
    oValue.AsString := 'MyVarValue';
    FProc.SetVariable('testvar', oValue);
    CheckEquals('MyVarValue', FProc.GetVariable('testvar').AsString,
                'Test 1a failed');
    Assert(FProc.GetVariable('DoesNotExist') = nil, 'Test 1b failed');

    { Verify they can be queried during execution. }
    FProc.OnTextNode := OnTextNodeTGV1;
    FProc.ApplyStyle;

    { Verify they can be set during execution. }
    FProc.OnTextNode := OnTextNodeTGV2;
    FProc.ApplyStyle;

    { Verify that original value restored after execution. }
    CheckEquals('MyVarValue', FProc.GetVariable('testvar').AsString,
                'Test 1j failed');

    { Verify they have original value if not set. }
    FProc.ClearVariables;
    FProc.OnTextNode := OnTextNodeTGV3;
    FProc.ApplyStyle;

  finally
    FProc.Free;
    DOM.Free;
  end;
end;
{--------}
procedure TXpXSLTTests.TestHTML;
var
  FilePath   : string;
  LogName    : string;
  oFilter : TXpFilterHTML;
begin
{$IFDEF MSWINDOWS}
  FilePath := '..\Data\XSLTHTML\';
  LogName := 'c:\XSLTHTMLGoodGoneBad.txt';
{$ENDIF}
{$IFDEF LINUX}
  FilePath := '../data/xslthtml/';
  LogName := '/tmp/XSLTHTMLGoodGoneBad.txt';
{$ENDIF}
  oFilter := TXpFilterHTML.Create(nil);
  oFilter.OutputMode := xphomFragment;
  try
    ProcessDocs(oFilter, FilePath, FilePath, LogName, True, False);
  finally
    oFilter.Free;
  end;
end;
{--------}
procedure TXpXSLTTests.TestOnFunction;
var
  DOM : TXpObjModel;
  Filter : TXpFilterText;
  XMLSrc : string;
begin
  XMLSrc :=
    '<test>' +
    ' <student name="B. Bigger" score="38"/>' +
    ' <student name="M. Walkowski" score="89"/>' +
    ' <student name="C. Weatherspoon" score="75"/>' +
    ' <student name="Q. Kard" score="91"/>' +
    ' <student name="L. Bean" score="84"/>' +
    ' <student name="R. Too" score="45"/>' +
    ' <student name="A. Knot" score="67"/>' +
    ' <student name="C. Nough" score="100"/>' +
    ' <student name="V. Tory" score="84"/>' +
    ' <student name="H. Main" score="78"/>' +
    ' <student name="B. Brown" score="81"/>' +
    '</test>';

  { Load the XML document. }
  DOM := TXpObjModel.Create(nil);
  Assert(DOM.LoadMemory(XMLSrc[1], Length(XMLSrc)), 'Failed to load source XML');

  { Set up a filter. }
  Filter := TXpFilterText.Create(nil);

  { Set up the XSL processor. }
{$IFDEF UsingCLX}
  FProc := TXpQXSLProcessor.Create(nil);
{$ELSE}
  FProc := TXpXSLProcessor.Create(nil);
{$ENDIF}
  try
    FProc.XmlObjModel := DOM;
    FProc.RaiseErrors := False;
    FProc.OnFunction := OnFunctionHandler;
    FProc.Filter := Filter;
    { Load the stylesheet. }
    FProc.StyleData :=
      '<xsl:stylesheet version="1.0"' +
      '  xmlns:xsl="http://www.w3.org/1999/XSL/Transform">' +
      '<xsl:template match="test">' +
      '  <xsl:value-of select="avg(''student'', ''score'')"/>' +
      '</xsl:template>' +
      '</xsl:stylesheet>';
    { Apply the stylesheet. }
    Assert(FProc.ApplyStyle, 'Stylesheet failed to apply');
    { Verify output. }
    Assert(FProc.Errors.Count = 0, 'Apply error: ' + FProc.Errors[1]);
    CheckEquals('75', Filter.Text);
    { TODO }
  finally
    FProc.Free;
    Filter.Free;
    DOM.Free;
  end;
end;
{--------}
procedure TXpXSLTTests.TestMessage;
var
  DOM : TXpObjModel;
begin
  DOM := TXpObjModel.Create(nil);
{$IFDEF UsingCLX}
  FProc := TXpQXSLProcessor.Create(nil);
{$ELSE}
  FProc := TXpXSLProcessor.Create(nil);
{$ENDIF}
  try
{$IFDEF MSWINDOWS}
    Assert(DOM.LoadDataSource('..\Data\XSLT\message.xml'));
{$ENDIF}
{$IFDEF LINUX}
    Assert(DOM.LoadDataSource('../data/xslt/message.xml'));
{$ENDIF}
    FProc.XmlObjModel := DOM;
    FProc.OnXSLMessage := OnXSLMessage;
    FProc.RaiseErrors := False;
{$IFDEF MSWINDOWS}
    FProc.StyleURL := '..\Data\XSLT\message.xsl';
{$ENDIF}
{$IFDEF LINUX}
    FProc.StyleURL := '../data/xslt/message.xsl';
{$ENDIF}

    Assert(not FProc.ApplyStyle, 'Stylesheet applied correctly');
    Assert(FProc.Errors.Count = 1, 'Invalid error count');
  finally
    FProc.Free;
    DOM.Free;
  end;
end;
{--------}
procedure TXpXSLTTests.TestOutputNoFilter;
var
  FilePath   : string;
  LogName    : string;
begin
{$IFDEF MSWINDOWS}
  FilePath := '..\Data\xsltout\';
  LogName := 'c:\XSLTOutGoodGoneBad.txt';
{$ENDIF}
{$IFDEF LINUX}
  FilePath := '../data/xsltout/';
  LogName := '/tmp/XSLTOutGoodGoneBad.txt';
{$ENDIF}
  ProcessDocs(nil, FilePath, FilePath, LogName, True, False);
end;
{--------}
procedure TXpXSLTTests.TestOutputOverrideFilter;
var
  FilePath   : string;
  LogName    : string;
  oFilter : TXpFilterXML;
begin
{$IFDEF MSWINDOWS}
  FilePath := '..\Data\xsltout\';
  LogName := 'c:\XSLTOutGoodGoneBad.txt';
{$ENDIF}
{$IFDEF LINUX}
  FilePath := '../data/xsltout/';
  LogName := '/tmp/XSLTOutGoodGoneBad.txt';
{$ENDIF}
  oFilter := TXpFilterXML.Create(nil);
  oFilter.FormattedOutput := False;
  try
    ProcessDocs(nil, FilePath, FilePath, LogName, True, False);
  finally
    oFilter.Free;
  end;
end;
{--------}
procedure TXpXSLTTests.TestXML;
var
  FilePath   : string;
  LogName    : string;
  oFilter : TXpFilterXML;
begin
{$IFDEF MSWINDOWS}
  FilePath := '..\Data\XSLTGood\';
  LogName := 'c:\XSLTGoodGoneBad.txt';
{$ENDIF}
{$IFDEF LINUX}
  FilePath := '../data/xsltgood/';
  LogName := '/tmp/XSLTGoodGoneBad.txt';
{$ENDIF}
  oFilter := TXpFilterXML.Create(nil);
  try
    ProcessDocs(oFilter, FilePath, FilePath, LogName, True, False);
  finally
    oFilter.Free;
  end;
end;
{--------}
procedure TXpXSLTTests.TestXMLStrip;
var
  FilePath   : string;
  LogName    : string;
  oFilter : TXpFilterXML;
begin
{$IFDEF MSWINDOWS}
  FilePath := '..\Data\XSLTStrip\';
  LogName := 'c:\XSLTStripGoodGoneBad.txt';
{$ENDIF}
{$IFDEF LINUX}
  FilePath := '../data/xsltstrip/';
  LogName := '/tmp/XSLTStripGoodGoneBad.txt';
{$ENDIF}
  oFilter := TXpFilterXML.Create(nil);
  oFilter.FormattedOutput := False;
  try
    ProcessDocs(oFilter, FilePath, FilePath, LogName, False, False);
  finally
    oFilter.Free;
  end;
end;
{--------}
procedure TXpXSLTTests.TestDecimalSeparator;
const
  cXMLBad = '<?xml version="1.0" encoding="UTF-8"?>' +
            '<one>' +
            '  <value>5,00</value>' +
            '  <value>15,00</value>' +
            '</one>';
  cXMLGood = '<?xml version="1.0" encoding="UTF-8"?>' +
             '<one>' +
             '  <value>5.00</value>' +
             '  <value>15.00</value>' +
             '</one>';
  cXSL = '<?xml version="1.0" encoding="UTF-8"?>' +
         '<xsl:stylesheet xmlns:xsl="http://www.w3.org/TR/WD-xsl" version="1.0">' +
         '  <xsl:template match="/">' +
         '    <xsl:value-of select="sum(/one/value)"/>' +
         '  </xsl:template>' +
         '</xsl:stylesheet>';
var
  XSLPrc : TXpXSLProcessor;
  Filter : TXpFilterText;
  DOM    : TXpObjModel;
  XMLDoc : string;
  OldSep : Char;
begin
  OldSep := DecimalSeparator;
  DecimalSeparator := ',';
  try
    DOM := TXpObjModel.Create(nil);
    try
      XMLDoc := cXMLGood;
      Assert(DOM.LoadMemory(XMLDoc[1], Length(XMLDoc)),
             'Unable to load good XML string');
      Filter := TXpFilterText.Create(nil);
      try
        XSLPrc := TXpXSLProcessor.Create(nil);
        try
          XSLPrc.XmlObjModel := DOM;
          XSLPrc.Filter := Filter;
          XSLPrc.StyleData := cXSL;
          Assert(XSLPrc.ApplyStyle,
                 'Unable to apply stylesheet');

          Assert(Filter.Text = '20',
                 'Sum is incorrect');

          //!!! The following test will fail until issue 2432 is resolved
          DOM.ClearDocument;
          XMLDoc := cXMLBad;
          Assert(DOM.LoadMemory(XMLDoc[1], Length(XMLDoc)),
                 'Could''t load bad XML into DOM');

          { This should fail because the commas in the bad doc are not
            allowed as decimal separators or grouping symbols. }
          Assert(not XSLPrc.ApplyStyle,
                 '!!!EXPECTED (bug 2432): Didn''t raise error for bad decimal separator.');
        finally
          XSLPrc.Free;
        end;
      finally
        Filter.Free;
      end;
    finally
      DOM.Free;
    end;
  finally
    DecimalSeparator := OldSep;
  end;
end;
{=====================================================================}
initialization
  RegisterTest('XSLT Tests', TXpXSLTTests);
end.
