{
    This file is part of the Free Component Library (FCL)
    Copyright (c) 2004 by Dean Zobec, Michael Van Canneyt

    Port to Free Pascal of the JUnit framework.
    Port to Pas2JS by Mattias Gaertner in 2017.

    See the file COPYING.FPC, included in this distribution,
    for details about the copyright.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

 **********************************************************************}
unit FPCUnit;

{$mode objfpc}

interface

uses
  Classes, SysUtils, JS, TypInfo;

type
  EAssertionFailedError = class(Exception);
  EIgnoredTest = class(EAssertionFailedError);

  TTestStep = (stSetUp, stRunTest, stTearDown, stNothing);

  TRunMethod = procedure of object;

  TTestResult = class;
  TTestSuite = class;

  { TTest }

  TTest = class(TObject)
  protected
    FLastStep: TTestStep;
    function GetTestName: string; virtual;
    function GetTestSuiteName: string; virtual;
    function GetEnableIgnores: boolean; virtual;
    procedure SetTestSuiteName(const aName: string); virtual; abstract;
    procedure SetEnableIgnores(Value: boolean); virtual; abstract;
  public
    function CountTestCases: integer; virtual;
    Function GetChildTestCount : Integer; virtual;
    Function GetChildTest(AIndex : Integer) : TTest; virtual;
    function FindChildTest(const AName: String): TTest;
    Function FindTest(Const AName : String) : TTest;
    procedure Run(AResult: TTestResult); virtual;
    procedure Ignore(const AMessage: string);
  published
    property TestName: string read GetTestName;
    property TestSuiteName: string read GetTestSuiteName write SetTestSuiteName;
    property LastStep: TTestStep read FLastStep;
    property EnableIgnores: boolean read GetEnableIgnores write SetEnableIgnores;
  end;

  { TAssert }

  TAssert = class(TTest)
  protected
    Class var AssertCount : Integer;
  public
    class procedure Fail(const AMessage: string);
    class procedure Fail(const AFmt: string; Args : Array of const);
    class procedure FailEquals(const expected, actual: string; const ErrorMsg: string = '');
    class procedure FailNotEquals(const expected, actual: string; const ErrorMsg: string = '');

    class procedure AssertTrue(const AMessage: string; ACondition: boolean); overload;
    class procedure AssertTrue(ACondition: boolean); overload;
    class procedure AssertFalse(const AMessage: string; ACondition: boolean); overload;
    class procedure AssertFalse(ACondition: boolean); overload;
    class procedure AssertEquals(const AMessage: string; Expected, Actual: string); overload;
    class procedure AssertEquals(Expected, Actual: string); overload;
    class procedure AssertEquals(const AMessage: string; Expected, Actual: NativeInt); overload;
    class procedure AssertEquals(Expected, Actual: NativeInt); overload;
    class procedure AssertEquals(const AMessage: string; Expected, Actual, Delta: double); overload;
    class procedure AssertEquals(Expected, Actual, Delta: double); overload;
    class procedure AssertEquals(const AMessage: string; Expected, Actual: boolean); overload;
    class procedure AssertEquals(Expected, Actual: boolean); overload;
    class procedure AssertEquals(const AMessage: string; Expected, Actual: char); overload;
    class procedure AssertEquals(Expected, Actual: char); overload;
    class procedure AssertEquals(const AMessage: string; Expected, Actual: TClass); overload;
    class procedure AssertEquals(Expected, Actual: TClass); overload;
    class procedure AssertSame(const AMessage: string; Expected, Actual: TObject); overload;
    class procedure AssertSame(Expected, Actual: TObject); overload;
    class procedure AssertSame(const AMessage: string; Expected, Actual: Pointer); overload;
    class procedure AssertSame(Expected, Actual: Pointer); overload;
    class procedure AssertNotSame(const AMessage: string; Expected, Actual: TObject); overload;
    class procedure AssertNotSame(Expected, Actual: TObject); overload;
    class procedure AssertNotSame(const AMessage: string; Expected, Actual: Pointer); overload;
    class procedure AssertNotSame(Expected, Actual: Pointer); overload;
    class procedure AssertNotNull(const AMessage: string; AObject: TObject); overload;
    class procedure AssertNotNull(AObject: TObject); overload;
    //class procedure AssertNotNullIntf(const AMessage: string; AInterface: IInterface); overload;
    //class procedure AssertNotNullIntf(AInterface: IInterface); overload;
    class procedure AssertNotNull(const AMessage: string; APointer: Pointer); overload;
    class procedure AssertNotNull(APointer: Pointer); overload;
    class procedure AssertNull(const AMessage: string; AObject: TObject); overload;
    class procedure AssertNull(AObject: TObject); overload;
    //class procedure AssertNullIntf(const AMessage: string; AInterface: IInterface); overload;
    //class procedure AssertNullIntf(AInterface: IInterface); overload;
    class procedure AssertNull(const AMessage: string; APointer: Pointer); overload;
    class procedure AssertNull(APointer: Pointer); overload;
    class procedure AssertNotNull(const AMessage, AString: string); overload;
    class procedure AssertNotNull(const AString: string); overload;
    class procedure AssertException(const AMessage: string;
      AExceptionClass: ExceptClass; const AMethod: TRunMethod;
      const AExceptionMessage : String = ''; AExceptionContext : Integer = 0); overload;
    class procedure AssertException(AExceptionClass: ExceptClass;
      const AMethod: TRunMethod; const AExceptionMessage : String = '';
      AExceptionContext : Integer = 0); overload;

    // DUnit compatible methods
    class procedure Check(pValue: boolean; pMessage: string = '');
    class procedure CheckEquals(expected, actual: double; msg: string = ''); overload;
    class procedure CheckEquals(expected, actual: double; delta: double; msg: string = ''); overload;
    class procedure CheckEquals(expected, actual: string; msg: string = ''); overload;
    class procedure CheckEquals(expected, actual: integer; msg: string = ''); overload;
    class procedure CheckEquals(expected, actual: boolean; msg: string = ''); overload;
    class procedure CheckEquals(expected, actual: TClass; msg: string = ''); overload;
    class procedure CheckNotEquals(expected, actual: string; msg: string = ''); overload;
    class procedure CheckNotEquals(expected, actual: integer; msg: string = ''); overload; virtual;
    class procedure CheckNotEquals(expected, actual: boolean; msg: string = ''); overload; virtual;
    class procedure CheckNotEquals(expected, actual: double; delta: double = 0; msg: string = ''); overload; virtual;
    //class procedure CheckNull(obj: IUnknown; msg: string = ''); overload;
    class procedure CheckNull(obj: TObject; msg: string = ''); overload;
    class procedure CheckNotNull(obj: TObject; msg: string = ''); overload;
    //class procedure CheckNotNull(obj: IUnknown; msg: string = ''); overload; virtual;
    class procedure CheckIs(obj :TObject; pClass: TClass; msg: string = ''); overload;
    class procedure CheckSame(expected, actual: TObject; msg: string = ''); overload;
    class procedure CheckTrue(condition: Boolean; msg: string = '');
    class procedure CheckFalse(condition: Boolean; msg: string = '');
    class procedure CheckException(const AMethod: TRunMethod; AExceptionClass: ExceptClass; msg: string = '');
    class function  EqualsErrorMessage(const expected, actual: string; const ErrorMsg: string): string;
    class function  NotEqualsErrorMessage(const expected, actual: string; const ErrorMsg: string): string;

    class function Suite: TTest;
  end;

  { TTestFailure }

  TTestFailure = class(TObject)
  private
    FTestName: string;
    FTestSuiteName: string;
    FLineNumber: longint;
    FFailedMethodName: string;
    FRaisedExceptionClass: TClass;
    FRaisedExceptionMessage: string;
    FSourceUnitName: string;
    //FThrownExceptionAddress: Pointer;
    FTestLastStep: TTestStep;
    function GetAsString: string;
    function GetExceptionMessage: string;
    function GetIsFailure: boolean;
    function GetIsIgnoredTest: boolean;
    function GetExceptionClassName: string;
    //function GetLocationInfo: string;
    procedure SetTestLastStep(const Value: TTestStep);
  public
    constructor CreateFailure(ATest: TTest; E: Exception; LastStep: TTestStep);
    property ExceptionClass: TClass read FRaisedExceptionClass;
  published
    property AsString: string read GetAsString;
    property IsFailure: boolean read GetIsFailure;
    property IsIgnoredTest: boolean read GetIsIgnoredTest;
    property ExceptionMessage: string read GetExceptionMessage;
    property ExceptionClassName: string read GetExceptionClassName;
    property SourceUnitName: string read FSourceUnitName write FSourceUnitName;
    property LineNumber: longint read FLineNumber write FLineNumber;
    //property LocationInfo: string read GetLocationInfo;
    property FailedMethodName: string read FFailedMethodName write FFailedMethodName;
    property TestLastStep: TTestStep read FTestLastStep write SetTestLastStep;
  end;

  // ToDo convert to ITestListener = interface

  ITestListener = class
  public
  //['{0CE9D3AE-882A-D811-9401-ADEB5E4C7FC1}']
    procedure AddFailure(ATest: TTest; AFailure: TTestFailure); virtual; abstract;
    procedure AddError(ATest: TTest; AError: TTestFailure); virtual; abstract;
    procedure StartTest(ATest: TTest); virtual; abstract;
    procedure EndTest(ATest: TTest); virtual; abstract;
    procedure StartTestSuite(ATestSuite: TTestSuite); virtual; abstract;
    procedure EndTestSuite(ATestSuite: TTestSuite); virtual; abstract;
  end;

  { TTestCase }

  TTestCase = class(TAssert)
  private
    FName: string;
    FTestSuiteName: string;
    FEnableIgnores: boolean;
    FExpectedExceptionFailMessage : String;
    FExpectedException : TClass;
    FExpectedExceptionMessage: String;
    FExpectedExceptionContext: Integer;
    //FExpectedExceptionCaller : Pointer;
  protected
    function CreateResult: TTestResult; virtual;
    procedure SetUp; virtual;
    procedure TearDown; virtual;
    procedure RunTest; virtual;
    function GetTestName: string; override;
    function GetTestSuiteName: string; override;
    function GetEnableIgnores: boolean; override;
    procedure SetTestSuiteName(const aName: string); override;
    procedure SetTestName(const Value: string); virtual;
    procedure SetEnableIgnores(Value: boolean); override;
    procedure RunBare; virtual;
    Class function SingleInstanceForSuite : Boolean; virtual;
  Public
    Class Var CheckAssertCalled : Boolean;
  public
    constructor Create; virtual; reintroduce;
    constructor CreateWith(const ATestName: string; const ATestSuiteName: string); virtual;
    constructor CreateWithName(const AName: string); virtual;
    procedure ExpectException(AExceptionClass: TClass; AExceptionMessage: string=''; AExceptionHelpContext: Integer=0);
    procedure ExpectException(const Msg: String; AExceptionClass: TClass; AExceptionMessage: string=''; AExceptionHelpContext: Integer=0);
    function CountTestCases: integer; override;
    function CreateResultAndRun: TTestResult; virtual;
    procedure Run(AResult: TTestResult); override;
    function AsString: string;
    property TestSuiteName: string read GetTestSuiteName write SetTestSuiteName;
    Property ExpectedExceptionFailMessage  : String Read FExpectedExceptionFailMessage;
    Property ExpectedException : TClass Read FExpectedException;
    Property ExpectedExceptionMessage : String Read FExpectedExceptionMessage;
    Property ExpectedExceptionContext: Integer Read FExpectedExceptionContext;
  published
    property TestName: string read GetTestName write SetTestName;
  end;

  TTestCaseClass = class of TTestCase;

  { TTestSuite }

  TTestSuite = class(TTest)
  private
    FTests: TFPList;
    FName: string;
    FTestSuiteName: string;
    FEnableIgnores: boolean;
  protected
    procedure ClearTests;
    function DoAddTest(ATest: TTest): Integer;
    function GetTestName: string; override;
    function GetTestSuiteName: string; override;
    function GetEnableIgnores: boolean; override;
    procedure SetTestSuiteName(const aName: string); override;
    procedure SetTestName(const Value: string); virtual;
    procedure SetEnableIgnores(Value: boolean); override;
  public
    constructor Create(AClass: TClass; AName: string); reintroduce; overload; virtual;
    constructor Create(AClass: TClass); reintroduce; overload; virtual;
    constructor Create(AClassArray: Array of TClass); reintroduce; overload; virtual;
    constructor Create(AName: string); reintroduce; overload; virtual;
    constructor Create; reintroduce; overload; virtual;
    destructor Destroy; override;
    function CountTestCases: integer; override;
    Function GetChildTestCount : Integer; override;
    Function GetChildTest(AIndex : Integer) : TTest; override;
    procedure Run(AResult: TTestResult); override;
    procedure RunTest(ATest: TTest; AResult: TTestResult); virtual;
    procedure AddTest(ATest: TTest); overload; virtual;
    procedure AddTestSuiteFromClass(ATestClass: TClass); virtual;
    class function Warning(const aMessage: string): TTestCase;
    property Test[Index: integer]: TTest read GetChildTest; default;
    Property ChildTestCount : Integer Read GetChildTestCount;
    property TestSuiteName: string read GetTestSuiteName write SetTestSuiteName;
    property TestName: string read GetTestName write SetTestName;
  end;

  TProtect = procedure(aTest: TTest; aResult: TTestResult);

  { TTestResult }

  TTestResult = class(TObject)
  protected
    FRunTests: integer;
    FFailures: TFPList;
    FIgnoredTests: TFPList;
    FErrors: TFPList;
    FListeners: TFPList; // list of ITestListener
    FSkippedTests: TFPList;
    FStartingTime: TDateTime;
    function GetNumErrors: integer;
    function GetNumFailures: integer;
    function GetNumIgnoredTests: integer;
    function GetNumSkipped: integer;
  public
    constructor Create; virtual; reintroduce;
    destructor Destroy; override;
    procedure ClearErrorLists;
    procedure StartTest(ATest: TTest);
    procedure AddFailure(ATest: TTest; E: EAssertionFailedError; aFailureList: TFPList{; AThrownExceptionAdrs: Pointer});
    procedure AddError(ATest: TTest; E: Exception{; AThrownExceptionAdrs: Pointer});
    procedure EndTest(ATest: TTest);
    procedure AddListener(AListener: ITestListener);
    procedure RemoveListener(AListener: ITestListener);
    procedure Run(ATestCase: TTestCase);
    procedure RunProtected(ATestCase: TTest; protect: TProtect);
    function WasSuccessful: boolean;
    function SkipTest(ATestCase: TTestCase): boolean;
    procedure AddToSkipList(ATestCase: TTestCase);
    procedure RemoveFromSkipList(ATestCase: TTestCase);
    procedure StartTestSuite(ATestSuite: TTestSuite);
    procedure EndTestSuite(ATestSuite: TTestSuite);
  published
    property Listeners: TFPList read FListeners;
    property Failures: TFPList read FFailures;
    property IgnoredTests: TFPList read FIgnoredTests;
    property Errors: TFPList read FErrors;
    property RunTests: integer read FRunTests;
    property NumberOfErrors: integer read GetNumErrors;
    property NumberOfFailures: integer read GetNumFailures;
    property NumberOfIgnoredTests: integer read GetNumIgnoredTests;
    property NumberOfSkippedTests: integer read GetNumSkipped;
    property StartingTime: TDateTime read FStartingTime;
  end;

function ComparisonMsg(const aExpected, aActual: string; const aCheckEqual: boolean=true): string; overload;
function ComparisonMsg(const aMsg, aExpected, aActual: string; const aCheckEqual: boolean=true): string; overload;

const
  SCompare: String = ' expected: <%s> but was: <%s>';
  SCompareNotEqual: String = ' expected: not equal to <%s> but was: <%s>';
  SExpectedNotSame: String = 'expected not same';
  SExceptionCompare: String = 'Exception %s expected but %s was raised';
  SExceptionMessageCompare: String = 'Exception raised but exception property Message differs: ';
  SExceptionHelpContextCompare: String = 'Exception raised but exception property HelpContext differs: ';
  SMethodNotFound: String = 'Method <%s> not found';
  SNoValidInheritance: String = ' does not inherit from TTestCase';
  SNoValidTests: String = 'No valid tests found in ';
  SNoException: String = 'no exception';
  SAssertNotCalled: String = 'Assert not called during test.';

procedure FreeObjects(List: TFPList);
procedure GetMethodList(AObject: TObject; AList: TStrings); overload;

implementation

Const
  sExpectedButWasFmt = 'Expected:' + LineEnding + '"%s"' + LineEnding + 'But was:' + LineEnding + '"%s"';
  sExpectedButWasAndMessageFmt = '%s' + LineEnding + sExpectedButWasFmt;

// Get the ClassName of C
function GetN(C : TClass) : string;
begin
  if C=Nil then
    Result:='<NIL>'
  else
    Result:=C.ClassName;
end;

// Get the name of o
function GetN(o : TObject) : string;
begin
  if o=Nil then
    Result:='<NIL>'
  else begin
    Result:=o.ClassName;
    if o is TComponent then
      Result:=TComponent(o).Name+':'+Result;
  end;
end;

// Get the name of p
function GetPtrN(p : Pointer) : string;
begin
  Result:=jsTypeOf(p);
  if isObject(p) then
  begin
    if isClassInstance(p) then
      exit(GetN(TObject(p)));
    if hasString(TJSObject(p)['name']) then
      Result:=String(TJSObject(p)['name'])+':'+Result
    else if hasString(TJSObject(p)['Name']) then
      Result:=String(TJSObject(p)['Name'])+':'+Result
    else if hasString(TJSObject(p)['$name']) then
      Result:=String(TJSObject(p)['$name'])+':'+Result;
  end;
end;

function ComparisonMsg(const aExpected, aActual: string;
  const aCheckEqual: boolean): string;
// aCheckEqual=false gives the error message if the test does *not* expect
// the results to be the same.
begin
  if aCheckEqual then
    Result := format(SCompare, [aExpected, aActual])
  else {check unequal requires opposite error message}
    Result := format(SCompareNotEqual, [aExpected, aActual]);
end;

function ComparisonMsg(const aMsg, aExpected, aActual: string;
  const aCheckEqual: boolean): string;
begin
  Result := '"' + aMsg + '"' + ComparisonMsg(aExpected, aActual, aCheckEqual);
end;

procedure FreeObjects(List: TFPList);
var
  i: integer;
begin
  for i:=0 to List.Count - 1 do
    TObject(List.Items[i]).Destroy;
  List.Clear;
end;

procedure GetMethodList(AObject: TObject; AList: TStrings);
var
  Methods: TTypeMemberMethodDynArray;
  i: Integer;
  m: TTypeMemberMethod;
begin
  Methods:=GetClassMethods(TypeInfo(AObject.ClassType));
  for i:=0 to length(Methods)-1 do
  begin
    m:=Methods[i];
    if AList.IndexOf(m.Name)>=0 then continue;
    AList.AddObject(m.Name,TObject(GetInstanceMethod(AObject,m.Name)));
  end;
end;

{ TTestResult }

function TTestResult.GetNumErrors: integer;
begin
  Result := FErrors.Count;
end;

function TTestResult.GetNumFailures: integer;
begin
  Result := FFailures.Count;
end;

function TTestResult.GetNumIgnoredTests: integer;
begin
  Result := FIgnoredTests.Count;
end;

function TTestResult.GetNumSkipped: integer;
begin
  Result := FSkippedTests.Count;
end;

constructor TTestResult.Create;
begin
  inherited Create;
  FFailures       := TFPList.Create;
  FIgnoredTests   := TFPList.Create;
  FErrors         := TFPList.Create;
  FListeners      := TFPList.Create;
  FSkippedTests   := TFPList.Create;
  FStartingTime   := Now;
end;

destructor TTestResult.Destroy;
begin
  FreeObjects(FFailures);
  FreeAndNil(FFailures);
  FreeObjects(FIgnoredTests);
  FreeAndNil(FIgnoredTests);
  FreeObjects(FErrors);
  FreeAndNil(FErrors);
  FreeAndNil(FListeners);
  FreeAndNil(FSkippedTests);
end;

procedure TTestResult.ClearErrorLists;
begin
  FreeObjects(FFailures);
  FFailures.Clear;
  FreeObjects(FIgnoredTests);
  FIgnoredTests.Clear;
  FreeObjects(FErrors);
  FreeAndNil(FErrors);
end;

procedure TTestResult.StartTest(ATest: TTest);
var
  count, i: integer;
begin
  count := ATest.CountTestCases;
  //lock mutex
  FRunTests := FRunTests + count;
  for i := 0 to FListeners.Count - 1 do
    ITestListener(FListeners[i]).StartTest(ATest);
  //unlock mutex
end;

procedure TTestResult.AddFailure(ATest: TTest; E: EAssertionFailedError;
  aFailureList: TFPList);
var
  f: TTestFailure;
  i: Integer;
begin
  //lock mutex
  f := TTestFailure.CreateFailure(ATest, E, ATest.LastStep{, AThrownExceptionAdrs});
  aFailureList.Add(f);
  for i := 0 to FListeners.Count - 1 do
    ITestListener(FListeners[i]).AddFailure(ATest, f);
  //unlock mutex
end;

procedure TTestResult.AddError(ATest: TTest; E: Exception);
var
  f: TTestFailure;
  i: Integer;
begin
  //lock mutex
  f := TTestFailure.CreateFailure(ATest, E, ATest.LastStep{, AThrownExceptionAdrs});
  FErrors.Add(f);
  for i := 0 to FListeners.Count - 1 do
    ITestListener(FListeners[i]).AddError(ATest, f);
  //unlock mutex
end;

procedure TTestResult.EndTest(ATest: TTest);
var
  i: integer;
begin
  for i := 0 to FListeners.Count - 1 do
    ITestListener(FListeners[i]).EndTest(ATest);
end;

procedure TTestResult.AddListener(AListener: ITestListener);
begin
  FListeners.Add(AListener);
end;

procedure TTestResult.RemoveListener(AListener: ITestListener);
begin
  FListeners.Remove(AListener);
end;

procedure ProtectTest(aTest: TTest; aResult: TTestResult);
begin
  if aResult=nil then ;
  TTestCase(aTest).RunBare;
end;

procedure TTestResult.Run(ATestCase: TTestCase);
begin
  if not SkipTest(ATestCase) then
  begin
    StartTest(ATestCase);
    RunProtected(ATestCase, @ProtectTest);
    EndTest(ATestCase);
  end;
end;

procedure TTestResult.RunProtected(ATestCase: TTest; protect: TProtect);
begin
  try
    protect(ATestCase, Self);
  except
    on E: EIgnoredTest do
      AddFailure(ATestCase, E, FIgnoredTests{, ExceptAddr});
    on E: EAssertionFailedError do
      AddFailure(ATestCase, E, FFailures{, ExceptAddr});
    on E: Exception do
      begin
        AddError(ATestCase, E{, ExceptAddr});
      end;
  end;
end;

function TTestResult.WasSuccessful: boolean;
begin
  //lock mutex
    Result := (FErrors.Count = 0) and (FFailures.Count = 0);
  //unlock mutex
end;

function TTestResult.SkipTest(ATestCase: TTestCase): boolean;
var
  i: integer;
begin
  Result := false;
  if FSkippedTests.Count = 0 then
  begin
    result := false;
    Exit;
  end
  else
    for i := 0 to FSkippedTests.Count - 1 do
    begin
      if FSkippedTests[i] = ATestCase then
      begin
        Result := true;
        Exit;
      end;
    end;
end;

procedure TTestResult.AddToSkipList(ATestCase: TTestCase);
begin
  FSkippedTests.Add(ATestCase);
end;

procedure TTestResult.RemoveFromSkipList(ATestCase: TTestCase);
begin
  FSkippedTests.Remove(ATestCase);
end;

procedure TTestResult.StartTestSuite(ATestSuite: TTestSuite);
var
  i: integer;
begin
  for i := 0 to FListeners.Count - 1 do
    ITestListener(FListeners[i]).StartTestSuite(ATestSuite);
end;

procedure TTestResult.EndTestSuite(ATestSuite: TTestSuite);
var
  i: integer;
begin
  for i := 0 to FListeners.Count - 1 do
    ITestListener(FListeners[i]).EndTestSuite(ATestSuite);
end;

type
  { TTestItem }

  TTestItem = Class(TObject)
  private
    FName: String;
    FOwnsTest: Boolean;
    FTest: TTest;
  public
    Constructor Create(T : TTest); reintroduce;
    Destructor Destroy; override;
    Property Test : TTest Read FTest;
    Property TestName : String Read FName;
    Property OwnsTest : Boolean Read FOwnsTest Write FOwnstest;
  end;

constructor TTestItem.Create(T: TTest);
begin
  FTest:=T;
  FName:=T.TestName;
  FOwnsTest:=True;
end;

destructor TTestItem.Destroy;
begin
  if OwnsTest then
    FreeAndNil(FTest);
  inherited Destroy;
end;

{ TTestSuite }

procedure TTestSuite.ClearTests;
begin
  FTests.Clear;
end;

function TTestSuite.DoAddTest(ATest: TTest): Integer;
begin
  Result:=FTests.Add(TTestItem.Create(ATest));
  if ATest.TestSuiteName = '' then
    ATest.TestSuiteName := Self.TestName;
  ATest.EnableIgnores := Self.EnableIgnores;
end;

function TTestSuite.GetTestName: string;
begin
  Result := FName;
end;

function TTestSuite.GetTestSuiteName: string;
begin
  Result := FTestSuiteName;
end;

function TTestSuite.GetEnableIgnores: boolean;
begin
  Result := FEnableIgnores;
end;

procedure TTestSuite.SetTestSuiteName(const aName: string);
begin
  if FTestSuiteName <> aName then
    FTestSuiteName := aName;
end;

procedure TTestSuite.SetTestName(const Value: string);
begin
  FName := Value;
end;

procedure TTestSuite.SetEnableIgnores(Value: boolean);
var
  i: integer;
begin
  if FEnableIgnores <> Value then
  begin
    FEnableIgnores := Value;
    for i := 0 to FTests.Count - 1 do
      TTestItem(FTests[i]).Test.EnableIgnores := Value;
  end
end;

constructor TTestSuite.Create(AClass: TClass; AName: string);
begin
  Create(AClass);
  FName := AName;
end;

constructor TTestSuite.Create(AClass: TClass);
var
  i,j: integer;
  tc: TTestCaseClass;
  C : TTestCase;
  SN : String;
  ml: TTypeMemberMethodDynArray;

begin
  TAssert.AssertNotNull(AClass);
  Create(AClass.ClassName);
  if AClass.InheritsFrom(TTestCase) then
  begin
    tc := TTestCaseClass(AClass);
    ml:=GetClassMethods(TypeInfo(AClass));
    SN:=tc.ClassName;
    if tc.SingleInstanceForSuite then
      begin
      c:=tc.CreateWith('',SN);
      for i := 0 to length(ml) -1 do
        begin
        C.TestName:=ml[i].Name;
        J:=DoAddTest(C);
        TTestItem(FTests[J]).OwnsTest:=(I=0);
        end;
      end
    else
      for i := 0 to length(ml) -1 do
        AddTest(tc.CreateWith(ml[i].Name, SN));
  end
  else
    AddTest(Warning(AClass.ClassName + SNoValidInheritance));
  if FTests.Count = 0 then
    AddTest(Warning(SNoValidTests + AClass.ClassName));
end;

constructor TTestSuite.Create(AClassArray: array of TClass);
var
  i: integer;
begin
  Create;
  for i := Low(AClassArray) to High(AClassArray) do
    if Assigned(AClassArray[i]) then
      AddTest(TTestSuite.Create(AClassArray[i]));
end;

constructor TTestSuite.Create(AName: string);
begin
  Create();
  FName := AName;
end;

constructor TTestSuite.Create;
begin
  inherited Create;
  FTests := TFPList.Create;
  FEnableIgnores := True;
end;

destructor TTestSuite.Destroy;
begin
  FreeObjects(FTests);
  FreeAndNil(FTests);
  inherited Destroy;
end;

function TTestSuite.CountTestCases: integer;
var
  i: integer;
begin
  Result := 0;
  for i := 0 to FTests.Count - 1 do
  begin
    Result := Result + TTestItem(FTests[i]).Test.CountTestCases;
  end;
end;

function TTestSuite.GetChildTestCount: Integer;
begin
  Result:=FTests.Count;
end;

function TTestSuite.GetChildTest(AIndex: Integer): TTest;
begin
  Result := TTestItem(FTests[AIndex]).Test;
end;

procedure TTestSuite.Run(AResult: TTestResult);
var
  i: integer;
  ti : TTestItem;

begin
  if FTests.Count > 0 then
    AResult.StartTestSuite(self);

  for i := 0 to FTests.Count - 1 do
    begin
    ti:=TTestItem(FTests[i]);
    if Ti.Test.InheritsFrom(TTestCase) and TTestCase(Ti.Test).SingleInstanceForSuite then
      TTestCase(Ti.Test).SetTestName(Ti.TestName);
    RunTest(TI.Test, AResult);
    end;

  if FTests.Count > 0 then
    AResult.EndTestSuite(self);
end;

procedure TTestSuite.RunTest(ATest: TTest; AResult: TTestResult);
begin
  ATest.Run(AResult);
end;

procedure TTestSuite.AddTest(ATest: TTest);
begin
  DoAddTest(ATest);
end;

procedure TTestSuite.AddTestSuiteFromClass(ATestClass: TClass);
begin
  AddTest(TTestSuite.Create(ATestClass));
end;

type
  TTestWarning = class(TTestCase)
  private
    FMessage: String;
  protected
    procedure RunTest; override;
  end;

procedure TTestWarning.RunTest;
begin
  Fail(FMessage);
end;

class function TTestSuite.Warning(const aMessage: string): TTestCase;
var
  w: TTestWarning;
begin
  w := TTestWarning.Create;
  w.FMessage := aMessage;
  Result := w;
end;

{ TTestCase }

function TTestCase.CreateResult: TTestResult;
begin
  Result := TTestResult.Create;
end;

procedure TTestCase.SetUp;
begin
  { do nothing }
end;

procedure TTestCase.TearDown;
begin
  { do nothing }
end;

procedure TTestCase.RunTest;
var
  RunMethod: TRunMethod;
  FailMessage : String;
begin
  AssertNotNull('name of the test not assigned', FName);
  RunMethod:=TRunMethod(GetInstanceMethod(Self,FName));
  if Assigned(RunMethod) then
  begin
    ExpectException('',Nil,'',0);
    try
      AssertCount:=0;
      FailMessage:='';
      RunMethod;
      if (FExpectedException<>Nil) then
        FailMessage:=Format(SExceptionCompare, [FExpectedException.ClassName, SNoException]);
      if CheckAssertCalled and (AssertCount=0) then
        FailMessage:=SAssertNotCalled;
    except
      On E : Exception do
        begin
        if FExpectedException=Nil then
          Raise;
        If not (E is FExpectedException) then
          FailMessage:=Format(SExceptionCompare, [FExpectedException.ClassName, E.ClassName]);
        if (FExpectedExceptionMessage<>'') then
          if (FExpectedExceptionMessage<>E.Message) then
            FailMessage:=Format(SExceptionmessageCompare+SCompare, [FExpectedExceptionMessage,E.Message]);
        if (FExpectedExceptionContext<>0) then
          if (FExpectedExceptionContext<>E.HelpContext) then
            FailMessage:=Format(SExceptionHelpContextCompare+SCompare, [IntToStr(FExpectedExceptionContext),IntToStr(E.HelpContext)])
        end;
    end;
    if (FailMessage<>'') then
      begin
      if (FExpectedExceptionFailMessage<>'') then
        FailMessage:=' : '+FailMessage;
      Fail(FExpectedExceptionFailMessage+FailMessage{,FExpectedExceptionCaller});
      end;
  end
  else
    begin
      Fail(format(SMethodNotFound, [FName]));
    end;
end;

function TTestCase.GetTestName: string;
begin
  Result := FName;
end;

function TTestCase.GetTestSuiteName: string;
begin
  Result := FTestSuiteName;
end;

function TTestCase.GetEnableIgnores: boolean;
begin
  Result := FEnableIgnores;
end;

procedure TTestCase.SetTestSuiteName(const aName: string);
begin
  if FTestSuiteName <> aName then
    FTestSuiteName := aName;
end;

procedure TTestCase.SetTestName(const Value: string);
begin
  FName := Value;
end;

procedure TTestCase.SetEnableIgnores(Value: boolean);
begin
  FEnableIgnores := Value;
end;

procedure TTestCase.RunBare;
begin
  FLastStep := stSetUp;
  SetUp;
  try
    FLastStep := stRunTest;
    RunTest;
    FLastStep := stTearDown;
  finally
    TearDown;
  end;
  FLastStep := stNothing;
end;

class function TTestCase.SingleInstanceForSuite: Boolean;
begin
  Result:=False;
end;

constructor TTestCase.Create;
begin
  inherited Create;
  FEnableIgnores := True;
end;

constructor TTestCase.CreateWith(const ATestName: string;
  const ATestSuiteName: string);
begin
  Create;
  FName := ATestName;
  FTestSuiteName := ATestSuiteName;
end;

constructor TTestCase.CreateWithName(const AName: string);
begin
  Create;
  FName := AName;
end;

procedure TTestCase.ExpectException(AExceptionClass: TClass;
  AExceptionMessage: string; AExceptionHelpContext: Integer);
begin
  FExpectedExceptionFailMessage:='';
  FExpectedException:=AExceptionClass;
  FExpectedExceptionMessage:=AExceptionMessage;
  FExpectedExceptionContext:=AExceptionHelpContext;
  //FExpectedExceptionCaller:=CallerAddr;
end;

procedure TTestCase.ExpectException(const Msg: String; AExceptionClass: TClass;
  AExceptionMessage: string; AExceptionHelpContext: Integer);
begin
  FExpectedExceptionFailMessage:=Msg;
  FExpectedException:=AExceptionClass;
  FExpectedExceptionMessage:=AExceptionMessage;
  FExpectedExceptionContext:=AExceptionHelpContext;
//  FExpectedExceptionCaller:=CallerAddr;
end;

function TTestCase.CountTestCases: integer;
begin
  Result := 1;
end;

function TTestCase.CreateResultAndRun: TTestResult;
begin
  Result := CreateResult;
  Run(Result);
end;

procedure TTestCase.Run(AResult: TTestResult);
begin
  AResult.Run(Self);
end;

function TTestCase.AsString: string;
begin
  Result := TestName + '(' + ClassName + ')';
end;

{ TTestFailure }

function TTestFailure.GetAsString: string;
var
  s: string;
begin
  if FTestSuiteName <> '' then
    s := FTestSuiteName + '.'
  else
    s := '';
  Result := s + FTestName + ': ' + FRaisedExceptionMessage;
end;

function TTestFailure.GetExceptionMessage: string;
begin
  Result := FRaisedExceptionMessage;
  if TestLastStep = stSetUp then
    Result := '[SETUP] ' + Result
  else if TestLastStep = stTearDown then
    Result := '[TEARDOWN] ' + Result;
end;

function TTestFailure.GetIsFailure: boolean;
begin
  Result := FRaisedExceptionClass.InheritsFrom(EAssertionFailedError);
end;

function TTestFailure.GetIsIgnoredTest: boolean;
begin
  Result := FRaisedExceptionClass.InheritsFrom(EIgnoredTest);
end;

function TTestFailure.GetExceptionClassName: string;
begin
  if Assigned(FRaisedExceptionClass) then
    Result := FRaisedExceptionClass.ClassName
  else
    Result := '<NIL>'
end;

procedure TTestFailure.SetTestLastStep(const Value: TTestStep);
begin
  FTestLastStep := Value;
end;

constructor TTestFailure.CreateFailure(ATest: TTest; E: Exception;
  LastStep: TTestStep);
begin
  inherited Create;
  FTestName := ATest.GetTestName;
  FTestSuiteName := ATest.GetTestSuiteName;
  FRaisedExceptionClass := E.ClassType;
  FRaisedExceptionMessage := E.Message;
  //FThrownExceptionAddress := ThrownExceptionAddrs;
  FTestLastStep := LastStep;
end;

{ TAssert }

class procedure TAssert.Fail(const AMessage: string);
begin
  Inc(AssertCount);
  raise EAssertionFailedError.Create(AMessage);
end;

class procedure TAssert.Fail(const AFmt: string; Args: array of Const);
begin
  Inc(AssertCount);
  raise EAssertionFailedError.CreateFmt(AFmt,Args);
end;

class procedure TAssert.FailEquals(const expected, actual: string;
  const ErrorMsg: string);
begin
  Fail(EqualsErrorMessage(expected, actual, ErrorMsg));
end;

class procedure TAssert.FailNotEquals(const expected, actual: string;
  const ErrorMsg: string);
begin
  Fail(NotEqualsErrorMessage(expected, actual, ErrorMsg));
end;

class procedure TAssert.AssertTrue(const AMessage: string; ACondition: boolean);
begin
  if (not ACondition) then
    Fail(AMessage)
  else
    Inc(AssertCount); // Fail will increae AssertCount
end;

class procedure TAssert.AssertTrue(ACondition: boolean);
begin
  AssertTrue('', ACondition);
end;

class procedure TAssert.AssertFalse(const AMessage: string; ACondition: boolean
  );
begin
  AssertTrue(AMessage, not ACondition);
end;

class procedure TAssert.AssertFalse(ACondition: boolean);
begin
  AssertFalse('', ACondition);
end;

class procedure TAssert.AssertEquals(const AMessage: string; Expected,
  Actual: string);
begin
  AssertTrue(ComparisonMsg(AMessage, Expected, Actual), Expected=Actual);
end;

class procedure TAssert.AssertEquals(Expected, Actual: string);
begin
  AssertTrue(ComparisonMsg(Expected, Actual), Expected=Actual);
end;

class procedure TAssert.AssertEquals(const AMessage: string; Expected,
  Actual: NativeInt);
begin
  AssertTrue(ComparisonMsg(AMessage,IntToStr(Expected), IntToStr(Actual)), Expected = Actual);
end;

class procedure TAssert.AssertEquals(Expected, Actual: NativeInt);
begin
  AssertTrue(ComparisonMsg(IntToStr(Expected), IntToStr(Actual)), Expected = Actual);
end;

class procedure TAssert.AssertEquals(const AMessage: string; Expected, Actual,
  Delta: double);
begin
  AssertTrue(ComparisonMsg(AMessage,FloatToStr(Expected),FloatToStr(Actual)),
    (Abs(Expected - Actual) <= Delta));
end;

class procedure TAssert.AssertEquals(Expected, Actual, Delta: double);
begin
  AssertTrue(ComparisonMsg(FloatToStr(Expected),FloatToStr(Actual)),
    (Abs(Expected - Actual) <= Delta));
end;

class procedure TAssert.AssertEquals(const AMessage: string; Expected,
  Actual: boolean);
begin
  AssertTrue(ComparisonMsg(AMessage,BoolToStr(Expected, true), BoolToStr(Actual, true)),
    Expected = Actual);
end;

class procedure TAssert.AssertEquals(Expected, Actual: boolean);
begin
  AssertTrue(ComparisonMsg(BoolToStr(Expected, true), BoolToStr(Actual, true)),
    Expected = Actual);
end;

class procedure TAssert.AssertEquals(const AMessage: string; Expected,
  Actual: char);
begin
  AssertTrue(ComparisonMsg(AMessage,Expected, Actual), Expected = Actual);
end;

class procedure TAssert.AssertEquals(Expected, Actual: char);
begin
  AssertTrue(ComparisonMsg(Expected, Actual), Expected = Actual);
end;

class procedure TAssert.AssertEquals(const AMessage: string; Expected,
  Actual: TClass);
begin
  AssertTrue(ComparisonMsg(AMessage,GetN(Expected), GetN(Actual)), Expected = Actual);
end;

class procedure TAssert.AssertEquals(Expected, Actual: TClass);
begin
  AssertTrue(ComparisonMsg(GetN(Expected), GetN(Actual)), Expected = Actual);
end;

class procedure TAssert.AssertSame(const AMessage: string; Expected,
  Actual: TObject);
begin
  AssertTrue(ComparisonMsg(AMessage,GetN(Expected), GetN(Actual)), Expected = Actual);
end;

class procedure TAssert.AssertSame(Expected, Actual: TObject);
begin
  AssertTrue(ComparisonMsg(GetN(Expected), GetN(Actual)), Expected = Actual);
end;

class procedure TAssert.AssertSame(const AMessage: string; Expected,
  Actual: Pointer);
begin
  AssertTrue(ComparisonMsg(AMessage,GetPtrN(Expected), GetPtrN(Actual)), Expected = Actual);
end;

class procedure TAssert.AssertSame(Expected, Actual: Pointer);
begin
  AssertTrue(ComparisonMsg(GetPtrN(Expected), GetPtrN(Actual)), Expected = Actual);
end;

class procedure TAssert.AssertNotSame(const AMessage: string; Expected,
  Actual: TObject);
begin
  AssertFalse('"' + aMessage + '"' + SExpectedNotSame, Expected = Actual);
end;

class procedure TAssert.AssertNotSame(Expected, Actual: TObject);
begin
  AssertFalse(SExpectedNotSame, Expected = Actual);
end;

class procedure TAssert.AssertNotSame(const AMessage: string; Expected,
  Actual: Pointer);
begin
  AssertFalse('"' + aMessage + '"' + SExpectedNotSame, Expected = Actual);
end;

class procedure TAssert.AssertNotSame(Expected, Actual: Pointer);
begin
  AssertFalse(SExpectedNotSame, Expected = Actual);
end;

class procedure TAssert.AssertNotNull(const AMessage: string; AObject: TObject);
begin
  AssertTrue(AMessage, (AObject <> nil));
end;

class procedure TAssert.AssertNotNull(AObject: TObject);
begin
  AssertTrue('',(AObject <> nil));
end;

class procedure TAssert.AssertNotNull(const AMessage: string; APointer: Pointer
  );
begin
  AssertTrue(AMessage, (APointer <> nil));
end;

class procedure TAssert.AssertNotNull(APointer: Pointer);
begin
  AssertTrue('', (APointer <> nil));
end;

class procedure TAssert.AssertNull(const AMessage: string; AObject: TObject);
begin
  AssertTrue(AMessage, (AObject = nil));
end;

class procedure TAssert.AssertNull(AObject: TObject);
begin
  AssertTrue('',(AObject = nil));
end;

class procedure TAssert.AssertNull(const AMessage: string; APointer: Pointer);
begin
  AssertTrue(AMessage, (APointer = nil));
end;

class procedure TAssert.AssertNull(APointer: Pointer);
begin
  AssertTrue('', (APointer = nil));
end;

class procedure TAssert.AssertNotNull(const AMessage, AString: string);
begin
  AssertTrue(AMessage, AString <> '');
end;

class procedure TAssert.AssertNotNull(const AString: string);
begin
  AssertNotNull('', AString);
end;

class procedure TAssert.AssertException(const AMessage: string;
  AExceptionClass: ExceptClass; const AMethod: TRunMethod;
  const AExceptionMessage: String; AExceptionContext: Integer);

  Function MisMatch (AClassName : String) : String;

  begin
    Result:=Format(SExceptionCompare,[AExceptionClass.ClassName, AClassName])
  end;

var
  FailMsg : string;
begin
  FailMsg:='';
  try
    AMethod;
    FailMsg:=MisMatch(SNoException);
  except
    on E: Exception do
      begin
      if Not E.ClassType.InheritsFrom(AExceptionClass) then
        FailMsg:=MisMatch(E.ClassName)
      else if not (AExceptionClass.ClassName = E.ClassName) then
        FailMsg:=MisMatch(E.ClassName)
      else if (AExceptionMessage<>'') and (AExceptionMessage<>E.Message) then
        FailMsg:=ComparisonMsg(SExceptionMessageCompare,AExceptionMessage,E.Message)
      else if (AExceptionContext<>0) and (AExceptionContext<>E.HelpContext) then
        FailMsg:=ComparisonMsg(SExceptionHelpContextCompare,IntToStr(AExceptionContext),IntToStr(E.HelpContext))
      end;
  end;
  AssertTrue(AMessage + FailMsg, FailMsg='');
end;

class procedure TAssert.AssertException(AExceptionClass: ExceptClass;
  const AMethod: TRunMethod; const AExceptionMessage: String;
  AExceptionContext: Integer);
begin
  AssertException('', AExceptionClass, AMethod, AExceptionMessage, AExceptionContext);
end;

class procedure TAssert.Check(pValue: boolean; pMessage: string);
begin
  AssertTrue(pMessage, pValue);
end;

class procedure TAssert.CheckEquals(expected, actual: double; msg: string);
begin
  CheckEquals(expected, actual, 0, msg);
end;

class procedure TAssert.CheckEquals(expected, actual: double; delta: double;
  msg: string);
begin
  AssertEquals(msg, expected, actual, delta);
end;

class procedure TAssert.CheckEquals(expected, actual: string; msg: string);
begin
  AssertEquals(msg, expected, actual);
end;

class procedure TAssert.CheckEquals(expected, actual: integer; msg: string);
begin
  AssertEquals(msg, expected, actual);
end;

class procedure TAssert.CheckEquals(expected, actual: boolean; msg: string);
begin
  AssertEquals(msg, expected, actual);
end;

class procedure TAssert.CheckEquals(expected, actual: TClass; msg: string);
begin
  AssertEquals(msg, expected, actual);
end;

class procedure TAssert.CheckNotEquals(expected, actual: string; msg: string);
begin
  if Expected=Actual then
    Fail(msg + ComparisonMsg(Expected, Actual, false));
end;

class procedure TAssert.CheckNotEquals(expected, actual: integer; msg: string);
begin
  if (expected = actual) then
    Fail(msg + ComparisonMsg(IntToStr(expected), IntToStr(actual), false));
end;

class procedure TAssert.CheckNotEquals(expected, actual: boolean; msg: string);
begin
  if (expected = actual) then
    Fail(msg + ComparisonMsg(BoolToStr(expected), BoolToStr(actual), false));
end;

class procedure TAssert.CheckNotEquals(expected, actual: double; delta: double;
  msg: string);
begin
  if (abs(expected-actual) <= delta) then
    FailNotEquals(FloatToStr(expected), FloatToStr(actual), msg);
end;

class procedure TAssert.CheckNull(obj: TObject; msg: string);
begin
  AssertNull(msg, obj);
end;

class procedure TAssert.CheckNotNull(obj: TObject; msg: string);
begin
  AssertNotNull(msg, obj);
end;

class procedure TAssert.CheckIs(obj: TObject; pClass: TClass; msg: string);
begin
  if pClass=nil then
    Fail('TAssert.CheckIs pClass=nil');
  if obj = nil then
    Fail(ComparisonMsg(msg,pClass.ClassName, 'nil'))
  else if not obj.ClassType.InheritsFrom(pClass) then
    Fail(ComparisonMsg(msg,pClass.ClassName, obj.ClassName));
end;

class procedure TAssert.CheckSame(expected, actual: TObject; msg: string);
begin
  AssertSame(msg, expected, actual);
end;

class procedure TAssert.CheckTrue(condition: Boolean; msg: string);
begin
  if (not condition) then
    FailNotEquals(BoolToStr(true, true), BoolToStr(false, true), msg);
end;

class procedure TAssert.CheckFalse(condition: Boolean; msg: string);
begin
  if (condition) then
    FailNotEquals(BoolToStr(false, true), BoolToStr(true, true), msg);
end;

class procedure TAssert.CheckException(const AMethod: TRunMethod;
  AExceptionClass: ExceptClass; msg: string);
begin
  AssertException(msg, AExceptionClass, AMethod);
end;

class function TAssert.EqualsErrorMessage(const expected, actual: string;
  const ErrorMsg: string): string;
begin
  if (ErrorMsg <> '') then
    Result := Format(sExpectedButWasAndMessageFmt, [ErrorMsg + ', ', expected, actual])
  else
    Result := Format(sExpectedButWasFmt, [expected, actual])
end;

class function TAssert.NotEqualsErrorMessage(const expected, actual: string;
  const ErrorMsg: string): string;
begin
  if (ErrorMsg <> '') then
    Result := Format(sExpectedButWasAndMessageFmt, [ErrorMsg, expected, actual])
  else
    Result := Format(sExpectedButWasFmt, [expected, actual]);
end;

class function TAssert.Suite: TTest;
begin
  Result := TTestSuite.Create(Self);
end;

{ TTest }

function TTest.GetTestName: string;
begin
  Result := 'TTest';
end;

function TTest.GetTestSuiteName: string;
begin
  Result := 'TTest';
end;

function TTest.GetEnableIgnores: boolean;
begin
  Result := True;
end;

function TTest.CountTestCases: integer;
begin
  Result := 0;
end;

function TTest.GetChildTestCount: Integer;
begin
  Result:=0;
end;

function TTest.GetChildTest(AIndex: Integer): TTest;
begin
  Result:=Nil;
  if AIndex=0 then ;
end;

function TTest.FindChildTest(const AName: String): TTest;
Var
  I : Integer;

begin
  Result:=Nil;
  I:=GetChildTestCount-1;
  While (Result=Nil) and (I>=0) do
    begin
    Result:=GetChildTest(I);
    if CompareText(Result.TestName,AName)<>0 then
      Result:=Nil;
    Dec(I);
    end;
end;

function TTest.FindTest(const AName: String): TTest;
Var
  S : String;
  I,P : Integer;

begin
  Result:=Nil;
  S:=AName;
  if S='' then exit;
  P:=Pos('.',S);
  If (P=0) then
    P:=Length(S)+1;
  Result:=FindChildTest(Copy(S,1,P-1));
  if (Result<>Nil) then
    begin
    Delete(S,1,P);
    If (S<>'') then
      Result:=Result.FindTest(S);
    end
  else
    begin
    P:=GetChildTestCount;
    I:=0;
    While (Result=Nil) and (I<P) do
      begin
      Result:=GetChildTest(I).FindTest(Aname);
      Inc(I);
      end;
    end;
end;

procedure TTest.Run(AResult: TTestResult);
begin
  { do nothing }
  if AResult=nil then ;
end;

procedure TTest.Ignore(const AMessage: string);
begin
  if EnableIgnores then raise EIgnoredTest.Create(AMessage);
end;

end.

