{
 Test with:
     ./runtests --format=plain --suite=TTestCompReaderWriterPas
     ./runtests --format=plain --suite=TTestCompReaderWriterPas.TestBaseTypesMaxValues

ToDo:
- enum: add unit
- enum: avoid nameclash with-do
- custom integer TColor: add unit
- custom integer: avoid nameclash with-do
- method: avoid nameclash with-do
- insert/update code and helper class into unit/program
  - find call init proc
  - find old init code
  - error if init proc is behind call
  - add call in existing constructor
  - add constructor with call
  - add new init code
  - replace init code
  - add missing units
}
unit TestCompReaderWriterPas;

{$mode objfpc}{$H+}

{off $DEFINE VerboseCompWriterPas}

interface

uses
  Classes, SysUtils, typinfo, LazLoggerBase, LazUTF8, LazLogger, CompWriterPas,
  LazPasReadUtil, fpcunit, testregistry, CodeToolManager, LinkScanner,
  CodeToolsStructs, TestStdCodetools,
  variants;

// Tests =======================================================================
const
  MinSafeIntCurrency = Low(Int64) div 10000;
  MaxSafeIntCurrency = High(Int64) div 10000;
  MinSafeIntSingle = -16777216;
  MaxSafeIntSingle =  16777216;
  MaskUIntSingle = $3fffff;
  MinSafeIntDouble = -$10000000000000;
  MaxSafeIntDouble =   $fffffffffffff;
  MaskUIntDouble = $fffffffffffff;
type
  TEnum = (red, green, blue, white, black);
  TEnumRg = green..white;
  TSetOfEnum = set of TEnum;
  TSetOfEnumRg = set of TEnumRg;
  TSetOfBool = set of boolean;
  TMyInt = 1..7;
  TSetOfMyInt = set of TMyInt;
  TMyChar = #3..#10;
  TSetOfMyChar = set of TMyChar;

  { TCompBaseTypes }

  TCompBaseTypes = class(TComponent)
  private
    FABoolean: Boolean;
    FAByte: Byte;
    FAByteBool: ByteBool;
    FAChar: Char;
    FACurrency: Currency;
    FADouble: Double;
    FAExtended: Extended;
    FAInt64: Int64;
    FALongBool: LongBool;
    FALongInt: LongInt;
    FALongWord: LongWord;
    FAQWord: QWord;
    FAShortInt: ShortInt;
    FAShortString: ShortString;
    FASingle: Single;
    FASmallInt: SmallInt;
    FAString: String;
    FAUnicodeString: UnicodeString;
    FAWideChar: WideChar;
    FAWideString: WideString;
    FAWord: Word;
    FAWordBool: WordBool;
    FEnum: TEnum;
    FEnumRg: TEnumRg;
    FMyChar: TMyChar;
    FMyInt: TMyInt;
    FSetOfBool: TSetOfBool;
    FSetOfEnum: TSetOfEnum;
    FSetOfEnumRg: TSetOfEnumRg;
    FSetOfMyChar: TSetOfMyChar;
    FSetOfMyInt: TSetOfMyInt;
    function isACurrencyStored: Boolean;
    function isADoubleStored: Boolean;
    function isAExtendedStored: Boolean;
    function isAShortStringStored: Boolean;
    function isASingleStored: Boolean;
    function isAStringStored: Boolean;
    function isAUnicodeStringStored: Boolean;
    function isAWideStringStored: Boolean;
  published
    constructor Create(AOwner: TComponent); override;
    property ABoolean: Boolean read FABoolean write FABoolean default false;
    property AByteBool: ByteBool read FAByteBool write FAByteBool default false;
    property AWordBool: WordBool read FAWordBool write FAWordBool default false;
    property ALongBool: LongBool read FALongBool write FALongBool default false;
    property AByte: Byte read FAByte write FAByte default 0;
    property AShortInt: ShortInt read FAShortInt write FAShortInt default 0;
    property AWord: Word read FAWord write FAWord default 0;
    property ASmallInt: SmallInt read FASmallInt write FASmallInt default 0;
    property ALongWord: LongWord read FALongWord write FALongWord default 0;
    property ALongInt: LongInt read FALongInt write FALongInt default 0;
    property AQWord: QWord read FAQWord write FAQWord default 0;
    property AInt64: Int64 read FAInt64 write FAInt64 default 0;
    property ACurrency: Currency read FACurrency write FACurrency stored isACurrencyStored;
    property ASingle: Single read FASingle write FASingle stored isASingleStored;
    property ADouble: Double read FADouble write FADouble stored isADoubleStored;
    property AExtended: Extended read FAExtended write FAExtended stored isAExtendedStored;
    property AChar: Char read FAChar write FAChar default #0;
    property AWideChar: WideChar read FAWideChar write FAWideChar default #0;
    property AString: String read FAString write FAString stored isAStringStored;
    property AShortString: ShortString read FAShortString write FAShortString stored isAShortStringStored;
    property AWideString: WideString read FAWideString write FAWideString stored isAWideStringStored;
    property AUnicodeString: UnicodeString read FAUnicodeString write FAUnicodeString stored isAUnicodeStringStored;
    property Enum: TEnum read FEnum write FEnum default low(TEnum);
    property EnumRg: TEnumRg read FEnumRg write FEnumRg default low(TEnumRg);
    property SetOfEnum: TSetOfEnum read FSetOfEnum write FSetOfEnum default [];
    property SetOfEnumRg: TSetOfEnumRg read FSetOfEnumRg write FSetOfEnumRg default [];
    property SetOfBool: TSetOfBool read FSetOfBool write FSetOfBool default [];
    property MyInt: TMyInt read FMyInt write FMyInt default low(TMyInt);
    property SetOfMyInt: TSetOfMyInt read FSetOfMyInt write FSetOfMyInt default [];
    property MyChar: TMyChar read FMyChar write FMyChar default low(TMyChar);
    property SetOfMyChar: TSetOfMyChar read FSetOfMyChar write FSetOfMyChar default [];
  end;

  { TCompBaseTypesCustomStored }

  TCompBaseTypesCustomStored = class(TComponent)
    procedure OnClick(Sender: TObject);
  private
    FABoolean: Boolean;
    FAByte: Byte;
    FAByteBool: ByteBool;
    FAChar: Char;
    FACurrency: Currency;
    FADouble: Double;
    FAExtended: Extended;
    FAInt64: Int64;
    FALongBool: LongBool;
    FALongInt: LongInt;
    FALongWord: LongWord;
    FAQWord: QWord;
    FAShortInt: ShortInt;
    FAShortString: ShortString;
    FASingle: Single;
    FASmallInt: SmallInt;
    FAString: String;
    FAUnicodeString: UnicodeString;
    FAWideChar: WideChar;
    FAWideString: WideString;
    FAWord: Word;
    FAWordBool: WordBool;
    FEnum: TEnum;
    FEnumRg: TEnumRg;
    FEvent: TNotifyEvent;
    FMyChar: TMyChar;
    FMyInt: TMyInt;
    FSetOfBool: TSetOfBool;
    FSetOfEnum: TSetOfEnum;
    FSetOfEnumRg: TSetOfEnumRg;
    FSetOfMyChar: TSetOfMyChar;
    FSetOfMyInt: TSetOfMyInt;
    function ABooleanIsStored: Boolean;
    function AByteBoolIsStored: Boolean;
    function AByteIsStored: Boolean;
    function ACharIsStored: Boolean;
    function ACurrencyIsStored: Boolean;
    function ADoubleIsStored: Boolean;
    function AExtendedIsStored: Boolean;
    function AInt64IsStored: Boolean;
    function ALongBoolIsStored: Boolean;
    function ALongIntIsStored: Boolean;
    function ALongWordIsStored: Boolean;
    function AQWordIsStored: Boolean;
    function AShortIntIsStored: Boolean;
    function AShortStringIsStored: Boolean;
    function ASingleIsStored: Boolean;
    function ASmallIntIsStored: Boolean;
    function AStringIsStored: Boolean;
    function AUnicodeStringIsStored: Boolean;
    function AWideCharIsStored: Boolean;
    function AWideStringIsStored: Boolean;
    function AWordBoolIsStored: Boolean;
    function AWordIsStored: Boolean;
    function EnumIsStored: Boolean;
    function EnumRgIsStored: Boolean;
    function EventIsStored: Boolean;
    function MyCharIsStored: Boolean;
    function MyIntIsStored: Boolean;
    function SetOfBoolIsStored: Boolean;
    function SetOfEnumIsStored: Boolean;
    function SetOfEnumRgIsStored: Boolean;
    function SetOfMyCharIsStored: Boolean;
    function SetOfMyIntIsStored: Boolean;
  public
    DefABoolean: Boolean;
    DefAByteBool: ByteBool;
    DefAWordBool: WordBool;
    DefALongBool: LongBool;
    DefAByte: Byte;
    DefAShortInt: ShortInt;
    DefAWord: Word;
    DefASmallInt: SmallInt;
    DefALongWord: LongWord;
    DefALongInt: LongInt;
    DefAQWord: QWord;
    DefAInt64: Int64;
    DefACurrency: Currency;
    DefASingle: Single;
    DefADouble: Double;
    DefAExtended: Extended;
    DefAChar: Char;
    DefAWideChar: WideChar;
    DefAString: String;
    DefAShortString: ShortString;
    DefAWideString: WideString;
    DefAUnicodeString: UnicodeString;
    DefEnum: TEnum;
    DefEnumRg: TEnumRg;
    DefSetOfEnum: TSetOfEnum;
    DefSetOfEnumRg: TSetOfEnumRg;
    DefSetOfBool: TSetOfBool;
    DefMyInt: TMyInt;
    DefSetOfMyInt: TSetOfMyInt;
    DefMyChar: TMyChar;
    DefSetOfMyChar: TSetOfMyChar;
    DefEvent: TNotifyEvent;
    constructor Create(AOwner: TComponent); override;
  published
    property ABoolean: Boolean read FABoolean write FABoolean stored ABooleanIsStored;
    property AByteBool: ByteBool read FAByteBool write FAByteBool stored AByteBoolIsStored;
    property AWordBool: WordBool read FAWordBool write FAWordBool stored AWordBoolIsStored;
    property ALongBool: LongBool read FALongBool write FALongBool stored ALongBoolIsStored;
    property AByte: Byte read FAByte write FAByte stored AByteIsStored;
    property AShortInt: ShortInt read FAShortInt write FAShortInt stored AShortIntIsStored;
    property AWord: Word read FAWord write FAWord stored AWordIsStored;
    property ASmallInt: SmallInt read FASmallInt write FASmallInt stored ASmallIntIsStored;
    property ALongWord: LongWord read FALongWord write FALongWord stored ALongWordIsStored;
    property ALongInt: LongInt read FALongInt write FALongInt stored ALongIntIsStored;
    property AQWord: QWord read FAQWord write FAQWord stored AQWordIsStored;
    property AInt64: Int64 read FAInt64 write FAInt64 stored AInt64IsStored;
    property ACurrency: Currency read FACurrency write FACurrency stored ACurrencyIsStored;
    property ASingle: Single read FASingle write FASingle stored ASingleIsStored nodefault;
    property ADouble: Double read FADouble write FADouble stored ADoubleIsStored nodefault;
    property AExtended: Extended read FAExtended write FAExtended stored AExtendedIsStored;
    property AChar: Char read FAChar write FAChar stored ACharIsStored;
    property AWideChar: WideChar read FAWideChar write FAWideChar stored AWideCharIsStored;
    property AString: String read FAString write FAString stored AStringIsStored;
    property AShortString: ShortString read FAShortString write FAShortString stored AShortStringIsStored;
    property AWideString: WideString read FAWideString write FAWideString stored AWideStringIsStored;
    property AUnicodeString: UnicodeString read FAUnicodeString write FAUnicodeString stored AUnicodeStringIsStored;
    property Enum: TEnum read FEnum write FEnum stored EnumIsStored;
    property EnumRg: TEnumRg read FEnumRg write FEnumRg stored EnumRgIsStored;
    property SetOfEnum: TSetOfEnum read FSetOfEnum write FSetOfEnum stored SetOfEnumIsStored;
    property SetOfEnumRg: TSetOfEnumRg read FSetOfEnumRg write FSetOfEnumRg stored SetOfEnumRgIsStored;
    property SetOfBool: TSetOfBool read FSetOfBool write FSetOfBool stored SetOfBoolIsStored;
    property MyInt: TMyInt read FMyInt write FMyInt stored MyIntIsStored;
    property SetOfMyInt: TSetOfMyInt read FSetOfMyInt write FSetOfMyInt stored SetOfMyIntIsStored;
    property MyChar: TMyChar read FMyChar write FMyChar stored MyCharIsStored;
    property SetOfMyChar: TSetOfMyChar read FSetOfMyChar write FSetOfMyChar stored SetOfMyCharIsStored;
    property Event: TNotifyEvent read FEvent write FEvent stored EventIsStored;
  end;

  { TCompVariants }

  TCompVariants = class(TComponent)
  private
    FV1: variant;
    FV10: variant;
    FV11: variant;
    FV12: variant;
    FV13: variant;
    FV14: variant;
    FV15: variant;
    FV16: variant;
    FV17: variant;
    FV18: variant;
    FV19: variant;
    FV2: variant;
    FV20: variant;
    FV3: variant;
    FV4: variant;
    FV5: variant;
    FV6: variant;
    FV7: variant;
    FV8: variant;
    FV9: variant;
  published
    property V1: variant read FV1 write FV1;
    property V2: variant read FV2 write FV2;
    property V3: variant read FV3 write FV3;
    property V4: variant read FV4 write FV4;
    property V5: variant read FV5 write FV5;
    property V6: variant read FV6 write FV6;
    property V7: variant read FV7 write FV7;
    property V8: variant read FV8 write FV8;
    property V9: variant read FV9 write FV9;
    property V10: variant read FV10 write FV10;
    property V11: variant read FV11 write FV11;
    property V12: variant read FV12 write FV12;
    property V13: variant read FV13 write FV13;
    property V14: variant read FV14 write FV14;
    property V15: variant read FV15 write FV15;
    property V16: variant read FV16 write FV16;
    property V17: variant read FV17 write FV17;
    property V18: variant read FV18 write FV18;
    property V19: variant read FV19 write FV19;
    property V20: variant read FV20 write FV20;
  end;

  { TPersistentSimple }

  TPersistentSimple = class(TPersistent)
  private
    FSize: longint;
    FSub: TPersistentSimple;
  published
    property Size: longint read FSize write FSize default 0;
    property Sub: TPersistentSimple read FSub write FSub;
  end;

  { TCompPropPersistent }

  TCompPropPersistent = class(TComponent)
    procedure OnA(Sender: TObject);
    procedure OnB(Sender: TObject);
    procedure OnC(Sender: TObject);
  private
    FAfter: longint;
    FBefore: longint;
    FMiddle: longint;
    FOnClick: TNotifyEvent;
    FSub: TPersistentSimple;
    FSub2: TPersistentSimple;
  published
    property Before: longint read FBefore write FBefore default 0;
    property Sub: TPersistentSimple read FSub write FSub;
    property Middle: longint read FMiddle write FMiddle default 0;
    property Sub2: TPersistentSimple read FSub2 write FSub2;
    property After: longint read FAfter write FAfter default 0;
    property OnClick: TNotifyEvent read FOnClick write FOnClick;
  end;

  { TSimpleControl }

  TSimpleControl = class(TComponent)
    procedure OnA(Sender: TObject);
    procedure OnB(Sender: TObject);
    procedure OnC(Sender: TObject);
  private
    FChildren: TFPList;
    FNext: TSimpleControl;
    FOnClick: TNotifyEvent;
    FParent: TSimpleControl;
    FSub: TPersistentSimple;
    function GetControls(Index: integer): TSimpleControl;
    procedure SetParent(const AValue: TSimpleControl);
  protected
    procedure GetChildren(Proc: TGetChildProc; Root: TComponent); override;
    procedure SetParentComponent(Value: TComponent); override;
    procedure SetChildOrder(Child: TComponent; Order: Integer); override;
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
    function GetParentComponent: TComponent; override;
    property Parent: TSimpleControl read FParent write SetParent;
    function ControlCount: integer;
    property Controls[Index: integer]: TSimpleControl read GetControls;
  published
    property Next: TSimpleControl read FNext write FNext;
    property Sub: TPersistentSimple read FSub write FSub;
    property OnClick: TNotifyEvent read FOnClick write FOnClick;
  end;

  { TSimpleCollectionItem }

  TSimpleCollectionItem = class(TCollectionItem)
  private
    FBefore: longint;
    FOnClick: TNotifyEvent;
    FSub: TPersistentSimple;
  published
    property Before: longint read FBefore write FBefore default 0;
    property Sub: TPersistentSimple read FSub write FSub;
    property OnClick: TNotifyEvent read FOnClick write FOnClick;
  end;

  TSimpleCollection = class(TCollection)
  private
    function GetThings(Index: integer): TSimpleCollectionItem;
  public
    property Things[Index: integer]: TSimpleCollectionItem read GetThings; default;
  end;

  { TSimpleControlWithCollection }

  TSimpleControlWithCollection = class(TSimpleControl)
  private
    FItems: TSimpleCollection;
    procedure SetItems(const AValue: TSimpleCollection);
  public
    constructor Create(AOwner: TComponent); override;
    destructor Destroy; override;
  published
    property Items: TSimpleCollection read FItems write SetItems;
  end;

  { TSimpleControlWithInterface }

  TSimpleControlWithInterface = class(TSimpleControl, IInterfaceComponentReference)
  private
    FIntf: IInterfaceComponentReference;
  public
    function GetComponent: TComponent;
  published
    property Intf: IInterfaceComponentReference read FIntf write FIntf;
  end;

  { TSimpleControlWithStrings }

  TSimpleControlWithStrings = class(TSimpleControl)
  private
    FLines: TStrings;
  published
    property Lines: TStrings read FLines write FLines;
  end;

  { TTestCompReaderWriterPas }

  TTestCompReaderWriterPas = class(TCustomTestCTStdCodetools)
  private
    FStream: TMemoryStream;
    FWriter: TCompWriterPas;
    FAncestors: TPointerToPointerTree;
    procedure OnDefinePropertiesTStrings(Writer: TCompWriterPas;
      Instance: TPersistent; const Identifier: string; var Handled: boolean);
    procedure OnWriterFindAncestor(Sender: TCompWriterPas; Component: TComponent;
      const Name: string; var Ancestor, RootAncestor: TComponent);
    procedure OnWriterGetParentProperty(Sender: TCompWriterPas; Component: TComponent;
      var PropName: string);
  protected
    procedure SetUp; override;
    procedure TearDown; override;
    function WriteDescendant(Component: TComponent; Ancestor: TComponent = nil): string;
    procedure TestWriteDescendant(Msg: string; Component: TComponent;
      Ancestor: TComponent; const Expected: array of string; NeedAccessClass: boolean = false);
    property Writer: TCompWriterPas read FWriter write FWriter;
  public
    constructor Create; override;
    destructor Destroy; override;
    procedure AddAncestor(Component, Ancestor: TComponent);
  published
    procedure TestBaseTypesSkipDefaultValue;
    procedure TestBaseTypesZeroes;
    procedure TestBaseTypesMinValues;
    procedure TestBaseTypesMaxValues;
    procedure TestStringASCII;
    procedure TestStringUTF8;
    procedure TestWideString_SrcCodePageSystem;
    procedure TestWideString_SrcCodePageUTF8;
    procedure TestVariant;
    procedure TestPropPersistent;
    procedure TestInterface;
    procedure TestAncestor;
    procedure TestAncestorChildPos;
    procedure TestWithLookupRootName;
    procedure TestChildComponents;
    procedure TestChildComponentsNoWith;
    procedure TestForeignReference;
    procedure TestCollection;
    procedure TestInline; // e.g. a Frame on a Form
    procedure TestAncestorWithInline; // e.g. a Form inherited from a Form with a Frame
    procedure TestInlineDescendant; // e.g. a Form with a Frame, Frame is inherited from another Frame

    procedure TestDesignInfo;
    procedure TestDefineProperties_ListOfStrings;
    procedure Test_TStrings;
  end;

implementation

type
  TAccessComp = class(TComponent);

function CreateRootName(aComponent: TComponent): string;
begin
  Result:=aComponent.ClassName;
  Delete(Result,1,1);
  Result:=Result+'1';
end;

{ TSimpleControlWithInterface }

function TSimpleControlWithInterface.GetComponent: TComponent;
begin
  Result:=Self;
end;

{ TSimpleCollection }

function TSimpleCollection.GetThings(Index: integer): TSimpleCollectionItem;
begin
  Result:=TSimpleCollectionItem(Items[Index]);
end;

{ TSimpleControlWithCollection }

procedure TSimpleControlWithCollection.SetItems(const AValue: TSimpleCollection
  );
begin
  if FItems=AValue then Exit;
  FItems.Assign(AValue);
end;

constructor TSimpleControlWithCollection.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FItems:=TSimpleCollection.Create(TSimpleCollectionItem);
end;

destructor TSimpleControlWithCollection.Destroy;
begin
  FreeAndNil(FItems);
  inherited Destroy;
end;

{ TSimpleControl }

procedure TSimpleControl.OnA(Sender: TObject);
begin
  if Sender=nil then ;
end;

procedure TSimpleControl.OnB(Sender: TObject);
begin
  if Sender=nil then ;
end;

procedure TSimpleControl.OnC(Sender: TObject);
begin
  if Sender=nil then ;
end;

procedure TSimpleControl.SetParent(const AValue: TSimpleControl);
begin
  if FParent=AValue then Exit;
  if FParent<>nil then
    FParent.FChildren.Remove(Self);
  FParent:=AValue;
  if FParent<>nil then
    FParent.FChildren.Add(Self);
end;

function TSimpleControl.GetControls(Index: integer): TSimpleControl;
begin
  Result:=TSimpleControl(FChildren[INdex]);
end;

procedure TSimpleControl.GetChildren(Proc: TGetChildProc; Root: TComponent);
var
  i: Integer;
begin
  if Root=nil then ;
  for i:=0 to ControlCount-1 do
    Proc(Controls[i]);
end;

procedure TSimpleControl.SetParentComponent(Value: TComponent);
begin
  Parent:=Value as TSimpleControl;
end;

procedure TSimpleControl.SetChildOrder(Child: TComponent; Order: Integer);
begin
  FChildren.Move(FChildren.IndexOf(Child),Order);
end;

constructor TSimpleControl.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FChildren:=TFPList.Create;
end;

destructor TSimpleControl.Destroy;
var
  i: Integer;
begin
  for i:=FChildren.Count-1 downto 0 do
    TSimpleControl(FChildren[i]).Parent:=nil;
  FreeAndNil(FChildren);
  inherited Destroy;
end;

function TSimpleControl.GetParentComponent: TComponent;
begin
  Result:=FParent;
end;

function TSimpleControl.ControlCount: integer;
begin
  Result:=FChildren.Count;
end;

{ TCompPropPersistent }

procedure TCompPropPersistent.OnA(Sender: TObject);
begin
  if Sender=nil then ;
end;

procedure TCompPropPersistent.OnB(Sender: TObject);
begin
  if Sender=nil then ;
end;

procedure TCompPropPersistent.OnC(Sender: TObject);
begin
  if Sender=nil then ;
end;

{ TCompBaseTypesCustomStored }

procedure TCompBaseTypesCustomStored.OnClick(Sender: TObject);
begin
  if Sender=nil then ;
end;

function TCompBaseTypesCustomStored.ABooleanIsStored: Boolean;
begin
  Result:=FABoolean<>DefABoolean;
end;

function TCompBaseTypesCustomStored.AByteBoolIsStored: Boolean;
begin
  Result:=FAByteBool<>DefAByteBool;
end;

function TCompBaseTypesCustomStored.AByteIsStored: Boolean;
begin
  Result:=FAByte<>DefAByte;
end;

function TCompBaseTypesCustomStored.ACharIsStored: Boolean;
begin
  Result:=FAChar<>DefAChar;
end;

function TCompBaseTypesCustomStored.ACurrencyIsStored: Boolean;
begin
  Result:=FACurrency<>DefACurrency;
end;

function TCompBaseTypesCustomStored.ADoubleIsStored: Boolean;
begin
  Result:=FADouble<>DefADouble;
end;

function TCompBaseTypesCustomStored.AExtendedIsStored: Boolean;
begin
  Result:=FAExtended<>DefAExtended;
end;

function TCompBaseTypesCustomStored.AInt64IsStored: Boolean;
begin
  Result:=FAInt64<>DefAInt64;
end;

function TCompBaseTypesCustomStored.ALongBoolIsStored: Boolean;
begin
  Result:=FALongBool<>DefALongBool;
end;

function TCompBaseTypesCustomStored.ALongIntIsStored: Boolean;
begin
  Result:=FALongInt<>DefALongInt;
end;

function TCompBaseTypesCustomStored.ALongWordIsStored: Boolean;
begin
  Result:=FALongWord<>DefALongWord;
end;

function TCompBaseTypesCustomStored.AQWordIsStored: Boolean;
begin
  Result:=FAWord<>DefAWord;
end;

function TCompBaseTypesCustomStored.AShortIntIsStored: Boolean;
begin
  Result:=FAShortInt<>DefAShortInt;
end;

function TCompBaseTypesCustomStored.AShortStringIsStored: Boolean;
begin
  Result:=FAShortString<>DefAShortString;
end;

function TCompBaseTypesCustomStored.ASingleIsStored: Boolean;
begin
  Result:=FASingle<>DefASingle;
end;

function TCompBaseTypesCustomStored.ASmallIntIsStored: Boolean;
begin
  Result:=FASmallInt<>DefASmallInt;
end;

function TCompBaseTypesCustomStored.AStringIsStored: Boolean;
begin
  Result:=FAString<>DefAString;
end;

function TCompBaseTypesCustomStored.AUnicodeStringIsStored: Boolean;
begin
  Result:=FAUnicodeString<>DefAUnicodeString;
end;

function TCompBaseTypesCustomStored.AWideCharIsStored: Boolean;
begin
  Result:=FAWideChar<>DefAWideChar;
end;

function TCompBaseTypesCustomStored.AWideStringIsStored: Boolean;
begin
  Result:=FAWideString<>DefAWideString;
end;

function TCompBaseTypesCustomStored.AWordBoolIsStored: Boolean;
begin
  Result:=FAWordBool<>DefAWordBool;
end;

function TCompBaseTypesCustomStored.AWordIsStored: Boolean;
begin
  Result:=FAWord<>DefAWord;
end;

function TCompBaseTypesCustomStored.EnumIsStored: Boolean;
begin
  Result:=FEnum<>DefEnum;
end;

function TCompBaseTypesCustomStored.EnumRgIsStored: Boolean;
begin
  Result:=FEnumRg<>DefEnumRg;
end;

function TCompBaseTypesCustomStored.EventIsStored: Boolean;
begin
  Result:=TMethod(FEvent).Code<>TMethod(DefEvent).Code;
end;

function TCompBaseTypesCustomStored.MyCharIsStored: Boolean;
begin
  Result:=MyChar<>DefMyChar;
end;

function TCompBaseTypesCustomStored.MyIntIsStored: Boolean;
begin
  Result:=FMyInt<>DefMyInt;
end;

function TCompBaseTypesCustomStored.SetOfBoolIsStored: Boolean;
begin
  Result:=FSetOfBool<>DefSetOfBool;
end;

function TCompBaseTypesCustomStored.SetOfEnumIsStored: Boolean;
begin
  Result:=FSetOfEnum<>DefSetOfEnum;
end;

function TCompBaseTypesCustomStored.SetOfEnumRgIsStored: Boolean;
begin
  Result:=FSetOfEnumRg<>DefSetOfEnumRg;
end;

function TCompBaseTypesCustomStored.SetOfMyCharIsStored: Boolean;
begin
  Result:=SetOfMyChar<>DefSetOfMyChar;
end;

function TCompBaseTypesCustomStored.SetOfMyIntIsStored: Boolean;
begin
  Result:=FSetOfMyInt<>DefSetOfMyInt;
end;

constructor TCompBaseTypesCustomStored.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
end;

{ TCompBaseTypes }

function TCompBaseTypes.isACurrencyStored: Boolean;
begin
  Result:=ACurrency<>0;
end;

function TCompBaseTypes.isADoubleStored: Boolean;
begin
  Result:=ADouble<>0;
end;

function TCompBaseTypes.isAExtendedStored: Boolean;
begin
  Result:=AExtended<>0;
end;

function TCompBaseTypes.isAShortStringStored: Boolean;
begin
  Result:=AShortString<>'';
end;

function TCompBaseTypes.isASingleStored: Boolean;
begin
  Result:=ASingle<>0;
end;

function TCompBaseTypes.isAStringStored: Boolean;
begin
  Result:=AString<>'';
end;

function TCompBaseTypes.isAUnicodeStringStored: Boolean;
begin
  Result:=AUnicodeString<>'';
end;

function TCompBaseTypes.isAWideStringStored: Boolean;
begin
  Result:=AWideString<>'';
end;

constructor TCompBaseTypes.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  EnumRg:=low(TEnumRg);
  MyInt:=low(TMyInt);
  MyChar:=low(TMyChar);
end;

{ TTestCompReaderWriterPas }

procedure TTestCompReaderWriterPas.OnWriterFindAncestor(Sender: TCompWriterPas;
  Component: TComponent; const Name: string; var Ancestor,
  RootAncestor: TComponent);
var
  C: TComponent;
begin
  if Name='' then ;
  C:=TComponent(FAncestors[Component]);
  if C=nil then exit;
  Ancestor:=C;
  if C.Owner=nil then
    RootAncestor:=C;
end;

procedure TTestCompReaderWriterPas.OnDefinePropertiesTStrings(
  Writer: TCompWriterPas; Instance: TPersistent; const Identifier: string;
  var Handled: boolean);
var
  List: TStrings;
  HasData: Boolean;
  i: Integer;
begin
  if not (Instance is TStrings) then exit;
  List:=TStrings(Instance);
  if Assigned(Writer.Ancestor) then
    // Only serialize if string list is different from ancestor
    if Writer.Ancestor.InheritsFrom(TStrings) then
      HasData := not List.Equals(TStrings(Writer.Ancestor))
    else
      HasData := True
  else
    HasData := List.Count > 0;
  if not HasData then exit;
  Writer.WriteStatement('with '+Identifier+' do begin');
  Writer.Indent;
  Writer.WriteStatement('Clear;');
  for i:=0 to List.Count-1 do
    Writer.WriteStatement('Add('+Writer.GetStringLiteral(List[i])+');');
  Writer.Unindent;
  Writer.WriteStatement('end;');
  Handled:=true;
end;

procedure TTestCompReaderWriterPas.OnWriterGetParentProperty(
  Sender: TCompWriterPas; Component: TComponent; var PropName: string);
begin
  if Component is TSimpleControl then
    PropName:='Parent';
end;

procedure TTestCompReaderWriterPas.SetUp;
begin
  inherited SetUp;
  FStream:=TMemoryStream.Create;
  FWriter:=TCompWriterPas.Create(FStream);
  FWriter.OnFindAncestor:=@OnWriterFindAncestor;
  FWriter.OnGetParentProperty:=@OnWriterGetParentProperty;
end;

procedure TTestCompReaderWriterPas.TearDown;
begin
  FAncestors.Clear;
  FreeAndNil(FWriter);
  FreeAndNil(FStream);
  inherited TearDown;
end;

function TTestCompReaderWriterPas.WriteDescendant(Component: TComponent;
  Ancestor: TComponent): string;
begin
  Writer.WriteDescendant(Component,Ancestor);
  FStream.Position:=0;
  SetLength(Result{%H-},FStream.size);
  if Result<>'' then
    FStream.Read(Result[1],length(Result));
  {$IFDEF VerboseCompWriterPas}
  writeln('TTestCompReaderWriterPas.WriteDescendant "',Result,'"');
  {$ENDIF}
end;

procedure TTestCompReaderWriterPas.TestWriteDescendant(Msg: string;
  Component: TComponent; Ancestor: TComponent; const Expected: array of string;
  NeedAccessClass: boolean);
var
  Actual, ExpS, s: String;
begin
  Actual:=WriteDescendant(Component,Ancestor);
  ExpS:=CSPDefaultSignatureBegin+LineEnding;
  ExpS:=ExpS+Writer.GetVersionStatement+LineEnding;
  if cwpoNoSelf in Writer.Options then begin
    ExpS:=ExpS+'with '+Component.Name+' do begin'+LineEnding;
    ExpS:=ExpS+'  Name:='''+Component.Name+''';'+LineEnding;
  end else
    ExpS:=ExpS+'Name:='''+Component.Name+''';'+LineEnding;
  for s in Expected do
    ExpS:=ExpS+s+LineEnding;
  if cwpoNoSelf in Writer.Options then
    ExpS:=ExpS+'end;'+LineEnding;
  ExpS:=ExpS+CSPDefaultSignatureEnd+LineEnding;
  //writeln('TTestCompReaderWriterPas.TestWriteDescendant ACTUAL=',Actual);
  //writeln('TTestCompReaderWriterPas.TestWriteDescendant EXP=',ExpS);
  CheckDiff(Msg,ExpS,Actual);
  AssertEquals(Msg+' NeedAccessClass',NeedAccessClass,Writer.NeedAccessClass);
end;

constructor TTestCompReaderWriterPas.Create;
begin
  inherited Create;
  FAncestors:=TPointerToPointerTree.Create;
end;

destructor TTestCompReaderWriterPas.Destroy;
begin
  FreeAndNil(FAncestors);
  inherited Destroy;
end;

procedure TTestCompReaderWriterPas.AddAncestor(Component, Ancestor: TComponent);
begin
  FAncestors[Component]:=Ancestor;
end;

procedure TTestCompReaderWriterPas.TestBaseTypesSkipDefaultValue;
var
  AComponent: TCompBaseTypes;
begin
  AComponent:=TCompBaseTypes.Create(nil);
  try
    AComponent.Name:=CreateRootName(AComponent);
    TestWriteDescendant('TestBaseTypesSkipDefaultValue',AComponent,nil,[
    ]);
  finally
    AComponent.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestBaseTypesZeroes;
var
  AComponent: TCompBaseTypesCustomStored;
begin
  AComponent:=TCompBaseTypesCustomStored.Create(nil);
  try
    with AComponent do begin
      Name:=CreateRootName(AComponent);
      AByte:=0;
      DefAByte:=AByte+1;
      AShortInt:=0;
      DefAShortInt:=AShortInt+1;
      AWord:=0;
      DefAWord:=AWord+1;
      ASmallInt:=0;
      DefASmallInt:=ASmallInt+1;
      ALongWord:=0;
      DefALongWord:=ALongWord+1;
      ALongInt:=0;
      DefALongInt:=ALongInt+1;
      AQWord:=0;
      DefAQWord:=AQWord+1;
      AInt64:=0;
      DefAInt64:=AInt64+1;
      ACurrency:=0;
      DefACurrency:=ACurrency+1;
      ASingle:=0;
      DefASingle:=ASingle+1;
      ADouble:=0;
      DefADouble:=ADouble+1;
      // ToDo: extended
      AChar:=#0;
      DefAChar:=succ(AChar);
      AWideChar:=#0;
      DefAWideChar:=succ(AWideChar);
      Enum:=TEnum(0);
      DefEnum:=succ(Enum);
      EnumRg:={%H-}TEnumRg(0);
      DefEnumRg:=succ(EnumRg);
      SetOfEnum:=[];
      DefSetOfEnum:=[red];
      SetOfEnumRg:=[];
      DefSetOfEnumRg:=[red];
      SetOfBool:=[];
      DefSetOfBool:=[true];
      MyInt:={%H-}TMyInt(0);
      DefMyInt:=MyInt+1;
      SetOfMyInt:=[];
      DefSetOfMyInt:=[2];
      MyChar:={%H-}TMyChar(0);
      DefMyChar:=succ(MyChar);
      SetOfMyChar:=[];
      DefSetOfMyChar:=[#4];
      Event:=nil;
      DefEvent:=@OnClick;
    end;
    TestWriteDescendant('TestBaseTypesZeroes',AComponent,nil,[
    'AByte:=0;',
    'AShortInt:=0;',
    'AWord:=0;',
    'ASmallInt:=0;',
    'ALongWord:=0;',
    'ALongInt:=0;',
    'ACurrency:= 0.0;',
    'ASingle:= 0.0;',
    'ADouble:= 0.0;',
    'AChar:=#0;',
    'AWideChar:=#0;',
    'Enum:=red;',
    'EnumRg:=TEnumRg(0);',
    'SetOfEnum:=[];',
    'SetOfEnumRg:=[];',
    'SetOfBool:=[];',
    'MyInt:=0;',
    'SetOfMyInt:=[];',
    'MyChar:=#0;',
    'SetOfMyChar:=[];',
    //'Event:=nil;', must not be written
    '']);
  finally
    AComponent.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestBaseTypesMinValues;
var
  AComponent: TCompBaseTypesCustomStored;
begin
  AComponent:=TCompBaseTypesCustomStored.Create(nil);
  try
    with AComponent do begin
      Name:=CreateRootName(AComponent);
      ABoolean:=low(boolean);
      DefABoolean:=not ABoolean;
      AByteBool:=boolean(low(byte));
      DefAByteBool:=not AByteBool;
      AWordBool:=boolean(low(word));
      DefAWordBool:=not AWordBool;
      ALongBool:=boolean(low(longword));
      DefALongBool:=not ALongBool;
      AByte:=low(byte);
      DefAByte:=AByte+1;
      AShortInt:=low(ShortInt);
      DefAShortInt:=AShortInt+1;
      AWord:=low(word);
      DefAWord:=AWord+1;
      ASmallInt:=low(SmallInt);
      DefASmallInt:=ASmallInt+1;
      ALongWord:=low(LongWord);
      DefALongWord:=ALongWord+1;
      ALongInt:=low(LongInt);
      DefALongInt:=ALongInt+1;
      AQWord:=low(qword);
      DefAQWord:=AQWord+1;
      AInt64:=low(Int64);
      DefAInt64:=AInt64+1;
      ACurrency:=MinSafeIntCurrency;
      DefACurrency:=ACurrency+1;
      ASingle:=MinSafeIntSingle;
      DefASingle:=ASingle+1;
      ADouble:=MinSafeIntDouble;
      DefADouble:=ADouble+1;
      // ToDo: extended
      AChar:=low(char);
      DefAChar:=succ(AChar);
      AWideChar:=low(WideChar);
      DefAWideChar:=succ(AWideChar);
      Enum:=low(TEnum);
      DefEnum:=succ(Enum);
      EnumRg:=low(TEnumRg);
      DefEnumRg:=succ(EnumRg);
      SetOfEnum:=[];
      DefSetOfEnum:=[red];
      SetOfEnumRg:=[];
      DefSetOfEnumRg:=[red];
      SetOfBool:=[];
      DefSetOfBool:=[true];
      MyInt:=low(TMyInt);
      DefMyInt:=MyInt+1;
      SetOfMyInt:=[];
      DefSetOfMyInt:=[2];
      MyChar:=low(TMyChar);
      DefMyChar:=succ(MyChar);
      SetOfMyChar:=[];
      DefSetOfMyChar:=[#4];
      Event:=@OnClick;
      DefEvent:=nil;
    end;
    TestWriteDescendant('TestBaseTypesMinValues',AComponent,nil,[
    'ABoolean:=False;',
    'AByteBool:=False;',
    'AWordBool:=False;',
    'ALongBool:=False;',
    'AByte:=0;',
    'AShortInt:=-128;',
    'AWord:=0;',
    'ASmallInt:=-32768;',
    'ALongWord:=0;',
    'ALongInt:=-2147483648;',
    'AInt64:=-9223372036854775808;',
    'ACurrency:=-9.22337203685477E14;',
    'ASingle:=-1.6777216E7;',
    'ADouble:=-4.503599627370496E15;',
    'AChar:=#0;',
    'AWideChar:=#0;',
    'Enum:=red;',
    'EnumRg:=green;',
    'SetOfEnum:=[];',
    'SetOfEnumRg:=[];',
    'SetOfBool:=[];',
    'MyInt:=1;',
    'SetOfMyInt:=[];',
    'MyChar:=#3;',
    'SetOfMyChar:=[];',
    'Event:=@OnClick;',
    '']);
  finally
    AComponent.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestBaseTypesMaxValues;
var
  AComponent: TCompBaseTypesCustomStored;
begin
  AComponent:=TCompBaseTypesCustomStored.Create(nil);
  try
    with AComponent do begin
      Name:=CreateRootName(AComponent);
      ABoolean:=high(boolean);
      DefABoolean:=not ABoolean;
      AByteBool:=boolean(high(byte));
      DefAByteBool:=not AByteBool;
      AWordBool:=wordbool(high(word));
      DefAWordBool:=not AWordBool;
      ALongBool:=longbool(high(longword));
      DefALongBool:=not ALongBool;
      AByte:=high(byte);
      DefAByte:=AByte-1;
      AShortInt:=high(ShortInt);
      DefAShortInt:=AShortInt-1;
      AWord:=high(word);
      DefAWord:=AWord-1;
      ASmallInt:=high(SmallInt);
      DefASmallInt:=ASmallInt-1;
      ALongWord:=high(LongWord);
      DefALongWord:=ALongWord-1;
      ALongInt:=high(LongInt);
      DefALongInt:=ALongInt-1;
      AQWord:=high(qword);
      DefAQWord:=AQWord-1;
      AInt64:=high(Int64);
      DefAInt64:=AInt64-1;
      ACurrency:=MaxSafeIntCurrency;
      DefACurrency:=ACurrency-1;
      ASingle:=MaxSafeIntSingle;
      DefASingle:=ASingle-1;
      ADouble:=MaxSafeIntDouble;
      DefADouble:=ADouble-1;
      // ToDo: extended
      AChar:=high(char);
      DefAChar:=pred(AChar);
      AWideChar:=high(WideChar);
      DefAWideChar:=pred(AWideChar);
      Enum:=high(TEnum);
      DefEnum:=pred(Enum);
      EnumRg:=high(TEnumRg);
      DefEnumRg:=pred(EnumRg);
      SetOfEnum:=[low(SetOfEnum)..high(SetOfEnum)];
      DefSetOfEnum:=[red];
      SetOfEnumRg:=[low(SetOfEnumRg)..high(SetOfEnumRg)];
      DefSetOfEnumRg:=[red];
      SetOfBool:=[low(Boolean)..high(Boolean)];
      DefSetOfBool:=[true];
      MyInt:=high(TMyInt);
      DefMyInt:=pred(MyInt);
      SetOfMyInt:=[low(MyInt)..high(MyInt)];
      DefSetOfMyInt:=[3];
      MyChar:=high(TMyChar);
      DefMyChar:=pred(MyChar);
      SetOfMyChar:=[low(MyChar)..high(MyChar)];
      DefSetOfMyChar:=[#5];
    end;
    TestWriteDescendant('TestBaseTypesMaxValues',AComponent,nil,[
    'ABoolean:=True;',
    'AByteBool:=True;',
    'AWordBool:=True;',
    'ALongBool:=True;',
    'AByte:=255;',
    'AShortInt:=127;',
    'AWord:=65535;',
    'ASmallInt:=32767;',
    'ALongWord:=4294967295;',
    'ALongInt:=2147483647;',
    'AQWord:=18446744073709551615;',
    'AInt64:=9223372036854775807;',
    'ACurrency:=9.22337203685477E14;',
    'ASingle:=1.6777216E7;',
    'ADouble:=4.503599627370495E15;',
    'AChar:=#255;',
    'AWideChar:=#65535;',
    'Enum:=black;',
    'EnumRg:=white;',
    'SetOfEnum:=[red..black];',
    'SetOfEnumRg:=[green..white];',
    'SetOfBool:=[False..True];',
    'MyInt:=7;',
    'SetOfMyInt:=[1..7];',
    'MyChar:=#10;',
    'SetOfMyChar:=[#3..#10];',
    '']);
  finally
    AComponent.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestStringASCII;
var
  AComponent: TCompBaseTypes;
begin
  AComponent:=TCompBaseTypes.Create(nil);
  try
    with AComponent do begin
      Name:=CreateRootName(AComponent);
      AString:=#9'A'#13#10;
      AShortString:=#9'A'#13#10;
    end;
    TestWriteDescendant('TestStringASCII',AComponent,nil,[
    'AString:=#9''A''#13#10;',
    'AShortString:=#9''A''#13#10;']);
  finally
    AComponent.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestStringUTF8;
var
  AComponent: TCompBaseTypes;
begin
  AComponent:=TCompBaseTypes.Create(nil);
  try
    with AComponent do begin
      Name:=CreateRootName(AComponent);
      AString:='äöü';
      AShortString:='äöü';
    end;
    TestWriteDescendant('TestStringUTF8',AComponent,nil,[
    'AString:=''äöü'';',
    'AShortString:=''äöü'';',
    '']);
  finally
    AComponent.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestWideString_SrcCodePageSystem;
var
  AComponent: TCompBaseTypes;
begin
  AComponent:=TCompBaseTypes.Create(nil);
  try
    with AComponent do begin
      Name:=CreateRootName(AComponent);
      AWideString:=UTF8ToUTF16('äAöü');
      AUnicodeString:=UTF8ToUTF16('äöBCü');
    end;
    TestWriteDescendant('TestWideString_SrcCodePageSystem',AComponent,nil,[
    'AWideString:=#0228''A''#0246#0252;',
    'AUnicodeString:=#0228#0246''BC''#0252;',
    '']);
  finally
    AComponent.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestWideString_SrcCodePageUTF8;
var
  AComponent: TCompBaseTypes;
begin
  Writer.Options:=Writer.Options+[cwpoSrcCodepageUTF8];
  AComponent:=TCompBaseTypes.Create(nil);
  try
    with AComponent do begin
      Name:=CreateRootName(AComponent);
      AWideString:=UTF8ToUTF16('äöü');
      AUnicodeString:=UTF8ToUTF16('äöü');
    end;
    TestWriteDescendant('TestWideString_SrcCodePageUTF8',AComponent,nil,[
    'AWideString:=''äöü'';',
    'AUnicodeString:=''äöü'';',
    '']);
  finally
    AComponent.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestVariant;
var
  AComponent: TCompVariants;
begin
  Writer.Options:=Writer.Options+[cwpoSrcCodepageUTF8];
  AComponent:=TCompVariants.Create(nil);
  try
    with AComponent do begin
      Name:=CreateRootName(AComponent);
      V1:=high(byte);
      V2:=low(ShortInt);
      V3:=high(Word);
      V4:=low(SmallInt);
      V5:=high(LongWord){%H-};
      V6:=low(LongInt);
      V7:=high(QWord);
      V8:=low(int64);
      V9:=true;
      V10:='äöü';
      V11:=single(-1.25);
      V12:=double(1.5);
      V13:=currency(17.0001);
    end;
    TestWriteDescendant('TestVariant',AComponent,nil,[
    'V1:=Byte(255);',
    'V2:=ShortInt(-128);',
    'V3:=Word(65535);',
    'V4:=SmallInt(-32768);',
    'V5:=LongWord(4294967295);',
    'V6:=LongInt(-2147483648);',
    'V7:=QWord(18446744073709551615);',
    'V8:=Int64(-9223372036854775808);',
    'V9:=True;',
    'V10:=''äöü'';',
    'V11:=Double(-1.25);',
    'V12:=Double(1.5);',
    'V13:=Currency(17.0001);',
    '']);
  finally
    AComponent.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestPropPersistent;
var
  aRoot: TCompPropPersistent;
begin
  aRoot:=TCompPropPersistent.Create(nil);
  try
    with aRoot do begin
      Name:=CreateRootName(aRoot);
      Before:=1;
      Sub:=TPersistentSimple.Create;
      Sub.Size:=11;
      Middle:=2;
      Sub2:=TPersistentSimple.Create;
      Sub2.Size:=21;
      Sub2.Sub:=TPersistentSimple.Create;
      Sub2.Sub.Size:=211;
      After:=3;
    end;
    TestWriteDescendant('TestPropPersistent',aRoot,nil,[
    'Before:=1;',
    'Sub.Size:=11;',
    'Middle:=2;',
    'Sub2.Size:=21;',
    'Sub2.Sub.Size:=211;',
    'After:=3;',
    '']);
  finally
    FreeAndNil(aRoot.FSub2.FSub);
    FreeAndNil(aRoot.FSub2);
    FreeAndNil(aRoot.FSub);
    aRoot.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestInterface;
var
  aRoot: TSimpleControl;
  Button1, Label1: TSimpleControlWithInterface;
begin
  aRoot:=TSimpleControl.Create(nil);
  try
    with aRoot do begin
      Name:=CreateRootName(aRoot);
      Button1:=TSimpleControlWithInterface.Create(aRoot);
      with Button1 do begin
        Name:='Button1';
        Parent:=aRoot;
      end;
      Label1:=TSimpleControlWithInterface.Create(aRoot);
      with Label1 do begin
        Name:='Label1';
        Parent:=aRoot;
        Intf:=Button1;
      end;
      Button1.Intf:=Label1;
    end;
    TestWriteDescendant('TestInterface',aRoot,nil,[
    'Button1:=TSimpleControlWithInterface.Create(Self);',
    'Label1:=TSimpleControlWithInterface.Create(Self);',
    'with Button1 do begin',
    '  Name:=''Button1'';',
    '  Intf:=Label1;',
    '  Parent:=Self;',
    'end;',
    'with Label1 do begin',
    '  Name:=''Label1'';',
    '  Intf:=Button1;',
    '  Parent:=Self;',
    'end;',
    '']);
  finally
    aRoot.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestAncestor;

  procedure InitAncestor(C: TSimpleControl);
  var
    Button1: TSimpleControl;
  begin
    C.Tag:=1;
    Button1:=TSimpleControl.Create(C);
    with Button1 do begin
      Name:='Button1';
      Tag:=2;
      OnClick:=@C.OnA;
      Parent:=C;
    end;
  end;

var
  aRoot, Ancestor: TSimpleControl;
begin
  Ancestor:=TSimpleControl.Create(nil);
  aRoot:=TSimpleControl.Create(nil);
  try
    with Ancestor do begin
      Name:='Ancestor';
    end;
    InitAncestor(Ancestor);

    with aRoot do begin
      Name:='Descendant';
    end;
    InitAncestor(aRoot);

    TestWriteDescendant('TestAncestor',aRoot,Ancestor,[
    'with Button1 do begin',
    'end;',
    '']);
  finally
    aRoot.Free;
    Ancestor.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestAncestorChildPos;

  procedure InitAncestor(C: TSimpleControl);
  var
    Button1, Panel2, Button21, Button22: TSimpleControl;
  begin
    C.Tag:=1;
    Button1:=TSimpleControl.Create(C);
    with Button1 do begin
      Name:='Button1';
      Tag:=11;
      Parent:=C;
    end;
    Panel2:=TSimpleControl.Create(C);
    with Panel2 do begin
      Name:='Panel2';
      Tag:=12;
      Parent:=C;
      Button21:=TSimpleControl.Create(C);
      with Button21 do begin
        Name:='Button21';
        Tag:=121;
        Parent:=Panel2;
      end;
      Button22:=TSimpleControl.Create(C);
      with Button22 do begin
        Name:='Button22';
        Tag:=122;
        Parent:=Panel2;
      end;
    end;
  end;

var
  aRoot, Ancestor: TSimpleControl;
begin
  Ancestor:=TSimpleControl.Create(nil);
  aRoot:=TSimpleControl.Create(nil);
  try
    with Ancestor do begin
      Name:='Ancestor';
    end;
    InitAncestor(Ancestor);

    with aRoot do begin
      Name:='Descendant';
    end;
    InitAncestor(aRoot);

    // switch Button21 and Button22
    aRoot.Controls[1].FChildren.Move(0,1);

    // switch Button1 and Panel2
    aRoot.FChildren.Move(0,1);

    TestWriteDescendant('TestAncestorChildPos',aRoot,Ancestor,[
    'with Panel2 do begin',
    '  with Button22 do begin',
    '  end;',
    '  TPasStreamAccess(TComponent(Panel2)).SetChildOrder(Button22,0);',
    '  with Button21 do begin',
    '  end;',
    '  TPasStreamAccess(TComponent(Panel2)).SetChildOrder(Button21,1);',
    'end;',
    'SetChildOrder(Panel2,0);',
    'with Button1 do begin',
    'end;',
    'SetChildOrder(Button1,1);',
    ''],true);
  finally
    aRoot.Free;
    Ancestor.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestWithLookupRootName;

  procedure InitAncestor(C: TSimpleControl);
  var
    Button1, Panel2, Button21, Button22: TSimpleControl;
  begin
    C.Tag:=1;
    Button1:=TSimpleControl.Create(C);
    with Button1 do begin
      Name:='Button1';
      Tag:=11;
      Parent:=C;
    end;
    Panel2:=TSimpleControl.Create(C);
    with Panel2 do begin
      Name:='Panel2';
      Tag:=12;
      Parent:=C;
      Button21:=TSimpleControl.Create(C);
      with Button21 do begin
        Name:='Button21';
        Tag:=121;
        Parent:=Panel2;
      end;
      Button22:=TSimpleControl.Create(C);
      with Button22 do begin
        Name:='Button22';
        Tag:=122;
        Parent:=Panel2;
      end;
    end;
  end;

var
  aRoot, Ancestor, Label1: TSimpleControl;
begin
  Ancestor:=TSimpleControl.Create(nil);
  aRoot:=TSimpleControl.Create(nil);
  try
    with Ancestor do begin
      Name:='Ancestor';
    end;
    InitAncestor(Ancestor);

    with aRoot do begin
      Name:='Descendant';
    end;
    InitAncestor(aRoot);
    aRoot.Controls[0].Next:=aRoot;
    aRoot.Next:=aRoot.Controls[0];
    Label1:=TSimpleControl.Create(aRoot);
    with Label1 do begin
      Name:='Label1';
      Parent:=aRoot;
    end;

    // switch Button21 and Button22
    aRoot.Controls[1].FChildren.Move(0,1);

    // switch Button1 and Panel2
    aRoot.FChildren.Move(0,1);

    Writer.Options:=Writer.Options+[cwpoNoSelf];
    TestWriteDescendant('TestWithLookupRootName',aRoot,Ancestor,[
    '  Label1:=TSimpleControl.Create(Descendant);',
    '  Next:=Button1;',
    '  with Panel2 do begin',
    '    with Button22 do begin',
    '    end;',
    '    TPasStreamAccess(TComponent(Panel2)).SetChildOrder(Button22,0);',
    '    with Button21 do begin',
    '    end;',
    '    TPasStreamAccess(TComponent(Panel2)).SetChildOrder(Button21,1);',
    '  end;',
    '  TPasStreamAccess(TComponent(Descendant)).SetChildOrder(Panel2,0);',
    '  with Button1 do begin',
    '    Next:=Descendant;',
    '  end;',
    '  TPasStreamAccess(TComponent(Descendant)).SetChildOrder(Button1,1);',
    '  with Label1 do begin',
    '    Name:=''Label1'';',
    '    Parent:=Descendant;',
    '  end;',
    ''],true);
  finally
    aRoot.Free;
    Ancestor.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestChildComponents;
var
  aRoot, Button1, Panel1: TSimpleControl;
begin
  aRoot:=TSimpleControl.Create(nil);
  try
    with aRoot do begin
      Name:=CreateRootName(aRoot);
      Tag:=1;
    end;
    Panel1:=TSimpleControl.Create(aRoot);
    with Panel1 do begin
      Name:='Panel1';
      Tag:=2;
      Parent:=aRoot;
      Button1:=TSimpleControl.Create(aRoot);
      with Button1 do begin
        Name:='Button1';
        Tag:=3;
        Parent:=Panel1;
      end;
    end;

    TestWriteDescendant('TestChildComponent',aRoot,nil,[
    'Panel1:=TSimpleControl.Create(Self);',
    'Button1:=TSimpleControl.Create(Self);',
    'Tag:=1;',
    'with Panel1 do begin',
    '  Name:=''Panel1'';',
    '  Tag:=2;',
    '  Parent:=Self;',
    '  with Button1 do begin',
    '    Name:=''Button1'';',
    '    Tag:=3;',
    '    Parent:=Panel1;',
    '  end;',
    'end;',
    '']);
  finally
    aRoot.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestChildComponentsNoWith;
var
  aRoot, Button1, Panel1: TSimpleControl;
begin
  aRoot:=TSimpleControl.Create(nil);
  try
    with aRoot do begin
      Name:='Root';
      Tag:=1;
    end;
    Panel1:=TSimpleControl.Create(aRoot);
    with Panel1 do begin
      Name:='Panel1';
      Tag:=2;
      Parent:=aRoot;
      Button1:=TSimpleControl.Create(aRoot);
      with Button1 do begin
        Name:='Button1';
        Tag:=3;
        Parent:=Panel1;
      end;
    end;

    Writer.Options:=Writer.Options+[cwpoNoWithBlocks];
    TestWriteDescendant('TestChildComponent',aRoot,nil,[
    'Panel1:=TSimpleControl.Create(Self);',
    'Button1:=TSimpleControl.Create(Self);',
    'Tag:=1;',
    '  Panel1.Name:=''Panel1'';',
    '  Panel1.Tag:=2;',
    '  Panel1.Parent:=Self;',
    '    Button1.Name:=''Button1'';',
    '    Button1.Tag:=3;',
    '    Button1.Parent:=Panel1;',
    '']);
  finally
    aRoot.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestForeignReference;
var
  aRoot, Button1, aRoot2, Button2: TSimpleControl;
begin
  aRoot:=TSimpleControl.Create(nil);
  aRoot2:=TSimpleControl.Create(nil);
  try
    with aRoot do begin
      Name:=CreateRootName(aRoot);
      Tag:=11;
    end;
    Button1:=TSimpleControl.Create(aRoot);
    with Button1 do begin
      Name:='Button1';
      Tag:=12;
      Parent:=aRoot;
    end;

    with aRoot2 do begin
      Name:='OtherRoot';
      Tag:=21;
    end;
    Button2:=TSimpleControl.Create(aRoot2);
    with Button2 do begin
      Name:='Button2';
      Tag:=22;
      Parent:=aRoot2;
    end;

    aRoot.Next:=aRoot2;
    Button1.Next:=Button2;

    TestWriteDescendant('TestForeignReference',aRoot,nil,[
    'Button1:=TSimpleControl.Create(Self);',
    'Tag:=11;',
    'Next:=OtherRoot;',
    'with Button1 do begin',
    '  Name:=''Button1'';',
    '  Tag:=12;',
    '  Next:=OtherRoot.Button2;',
    '  Parent:=Self;',
    'end;',
    '']);
  finally
    aRoot.Free;
    aRoot2.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestCollection;
var
  aRoot: TSimpleControlWithCollection;
begin
  aRoot:=TSimpleControlWithCollection.Create(nil);
  try
    with aRoot do begin
      Name:=CreateRootName(aRoot);
      Tag:=1;
      with TSimpleCollectionItem(Items.Add) do begin
        OnClick:=@OnA;
        Sub:=TPersistentSimple.Create;
        Sub.Size:=11;
      end;
      with TSimpleCollectionItem(Items.Add) do begin
        Sub:=TPersistentSimple.Create;
        Sub.Size:=12;
      end;
    end;

    TestWriteDescendant('TestCollection',aRoot,nil,[
    'Tag:=1;',
    'Items.Clear;',
    'with TSimpleCollectionItem(Items.Add) do begin',
    '  Sub.Size:=11;',
    '  OnClick:=@OnA;',
    'end;',
    'with TSimpleCollectionItem(Items.Add) do begin',
    '  Sub.Size:=12;',
    'end;',
    '']);
  finally
    FreeAndNil(aRoot.Items[0].FSub);
    FreeAndNil(aRoot.Items[1].FSub);
    aRoot.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestInline;

  procedure InitFrame(Frame: TSimpleControl);
  var
    FrameButton1: TSimpleControl;
  begin
    with Frame do begin
      Tag:=12;
      FrameButton1:=TSimpleControl.Create(Frame);
      with FrameButton1 do begin
        Name:='FrameButton1';
        Tag:=123;
        Parent:=Frame;
      end;
    end;
  end;

var
  aRoot, Button1, Frame1, AncestorFrame: TSimpleControl;
begin
  // e.g. a form with a frame
  // the form has no ancestor
  // the frame has an ancestor
  aRoot:=TSimpleControl.Create(nil);
  AncestorFrame:=TSimpleControl.Create(nil);
  try
    AncestorFrame.Name:='AncestorFrame';
    InitFrame(AncestorFrame);

    with aRoot do begin
      Name:=CreateRootName(aRoot);
      Tag:=1;
    end;
    Button1:=TSimpleControl.Create(aRoot);
    with Button1 do begin
      Name:='Button1';
      Parent:=aRoot;
    end;
    Frame1:=TSimpleControl.Create(aRoot);
    TAccessComp(TComponent(Frame1)).SetInline(true);
    InitFrame(Frame1);
    with Frame1 do begin
      Name:='Frame1';
      Parent:=aRoot;
    end;

    AddAncestor(Frame1,AncestorFrame);
    TestWriteDescendant('TestInline',aRoot,nil,[
    'Button1:=TSimpleControl.Create(Self);',
    'Frame1:=TSimpleControl.Create(Self);',
    CSPDefaultAccessClass+'(TComponent(Frame1)).SetInline(True);',
    'Tag:=1;',
    'with Button1 do begin',
    '  Name:=''Button1'';',
    '  Parent:=Self;',
    'end;',
    'with Frame1 do begin',
    '  Name:=''Frame1'';',
    '  Parent:=Self;',
    '  with FrameButton1 do begin',
    '  end;',
    'end;',
    ''],true);
  finally
    AncestorFrame.Free;
    aRoot.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestAncestorWithInline;

  procedure InitFrame(Frame: TSimpleControl);
  var
    FrameButton1, FrameButton2: TSimpleControl;
  begin
    with Frame do begin
      Tag:=1;
      FrameButton1:=TSimpleControl.Create(Frame);
      with FrameButton1 do begin
        Name:='FrameButton1';
        Tag:=11;
        Parent:=Frame;
      end;
      FrameButton2:=TSimpleControl.Create(Frame);
      with FrameButton2 do begin
        Name:='FrameButton2';
        Tag:=12;
        Parent:=Frame;
      end;
    end;
  end;

  procedure InitForm(Form: TSimpleControl; out Frame1: TSimpleControl);
  var
    Button1: TSimpleControl;
  begin
    with Form do begin
      // add a button
      Button1:=TSimpleControl.Create(Form);
      with Button1 do begin
        Name:='Button1';
        Tag:=21;
        Parent:=Form;
      end;
      // add a frame
      Frame1:=TSimpleControl.Create(Form);
      TAccessComp(TComponent(Frame1)).SetInline(true);
      InitFrame(Frame1);
      with Frame1 do begin
        Name:='Frame1';
        Tag:=22;
        Parent:=Form;
      end;
    end;
  end;

var
  Frame1, AncestorFrame, AncestorForm, Form,
    Frame2, Label1: TSimpleControl;
begin
  // e.g. a form inherited from with a frame
  AncestorFrame:=nil;
  AncestorForm:=nil;
  Form:=nil;
  try
    AncestorFrame:=TSimpleControl.Create(nil);
    AncestorFrame.Name:='AncestorFrame';
    InitFrame(AncestorFrame);

    AncestorForm:=TSimpleControl.Create(nil);
    AncestorForm.Name:='AncestorForm';
    InitForm(AncestorForm,Frame1);
    AddAncestor(Frame1,AncestorFrame);

    Form:=TSimpleControl.Create(nil);
    Form.Name:='Form';
    InitForm(Form,Frame2);
    Frame2.Tag:=32;
    Frame2.Controls[0].Tag:=421;
    // change Z order of buttons in frame
    Form.FChildren.Move(0,1);
    // change Z order of frame in Form
    Frame2.FChildren.Move(0,1);
    // add a label
    Label1:=TSimpleControl.Create(Form);
    with Label1 do begin
      Name:='Label1';
      Tag:=33;
      Parent:=Form;
    end;

    TestWriteDescendant('TestAncestorWithInline',Form,AncestorForm,[
    'Label1:=TSimpleControl.Create(Self);',
    'with Frame1 do begin',
    '  Tag:=32;',
    '  with FrameButton2 do begin',
    '  end;',
    '  TPasStreamAccess(TComponent(Frame1)).SetChildOrder(Frame1.FrameButton2,0);',
    '  with FrameButton1 do begin',
    '    Tag:=421;',
    '  end;',
    '  TPasStreamAccess(TComponent(Frame1)).SetChildOrder(Frame1.FrameButton1,1);',
    'end;',
    'SetChildOrder(Frame1,0);',
    'with Button1 do begin',
    'end;',
    'SetChildOrder(Button1,1);',
    'with Label1 do begin',
    '  Name:=''Label1'';',
    '  Tag:=33;',
    '  Parent:=Self;',
    'end;',
    ''],true);
  finally
    Form.Free;
    AncestorForm.Free;
    AncestorFrame.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestInlineDescendant;

  procedure InitFrame(Frame: TSimpleControl);
  var
    FrameButton1, FrameButton2: TSimpleControl;
  begin
    with Frame do begin
      Tag:=1;
      FrameButton1:=TSimpleControl.Create(Frame);
      with FrameButton1 do begin
        Name:='FrameButton1';
        Tag:=11;
        Parent:=Frame;
      end;
      FrameButton2:=TSimpleControl.Create(Frame);
      with FrameButton2 do begin
        Name:='FrameButton2';
        Tag:=12;
        Parent:=Frame;
      end;
    end;
  end;

  procedure InitForm(Form: TSimpleControl; out Frame1: TSimpleControl);
  var
    Button1: TSimpleControl;
  begin
    with Form do begin
      // add a button
      Button1:=TSimpleControl.Create(Form);
      with Button1 do begin
        Name:='Button1';
        Tag:=21;
        Parent:=Form;
      end;
      // add a frame
      Frame1:=TSimpleControl.Create(Form);
      TAccessComp(TComponent(Frame1)).SetInline(true);
      InitFrame(Frame1);
      with Frame1 do begin
        Name:='Frame1';
        Tag:=22;
        Parent:=Form;
      end;
    end;
  end;

var
  AncestorFrame, DescendantFrame, Form, Frame: TSimpleControl;
begin
  // e.g. a form inherited from with a frame
  AncestorFrame:=nil;
  DescendantFrame:=nil;
  Form:=nil;
  try
    AncestorFrame:=TSimpleControl.Create(nil);
    AncestorFrame.Name:='AncestorFrame';
    InitFrame(AncestorFrame);

    DescendantFrame:=TSimpleControl.Create(nil);
    DescendantFrame.Name:='DescendantFrame';
    InitFrame(DescendantFrame);
    AddAncestor(DescendantFrame,AncestorFrame);

    Form:=TSimpleControl.Create(nil);
    Form.Name:='Form';
    InitForm(Form,Frame);
    AddAncestor(Frame,DescendantFrame);

    TestWriteDescendant('TestInlineDescendant',Form,nil,[
    'Button1:=TSimpleControl.Create(Self);',
    'Frame1:=TSimpleControl.Create(Self);',
    'TPasStreamAccess(TComponent(Frame1)).SetInline(True);',
    'with Button1 do begin',
    '  Name:=''Button1'';',
    '  Tag:=21;',
    '  Parent:=Self;',
    'end;',
    'with Frame1 do begin',
    '  Name:=''Frame1'';',
    '  Tag:=22;',
    '  Parent:=Self;',
    '  with FrameButton1 do begin',
    '  end;',
    '  with FrameButton2 do begin',
    '  end;',
    'end;',
    ''],true);
  finally
    Form.Free;
    DescendantFrame.Free;
    AncestorFrame.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestDesignInfo;
var
  AComponent: TComponent;
begin
  AComponent:=TComponent.Create(nil);
  try
    with AComponent do begin
      Name:=CreateRootName(AComponent);
      DesignInfo:=12345678;
    end;
    TestWriteDescendant('TestDesignInfo',AComponent,nil,[
    'DesignInfo:=12345678;',
    '']);
  finally
    aComponent.Free;
  end;
end;

procedure TTestCompReaderWriterPas.TestDefineProperties_ListOfStrings;
var
  ARoot: TSimpleControlWithStrings;
  Expected: String;
  Lines2: TStringList;
begin
  ARoot:=TSimpleControlWithStrings.Create(nil);
  Lines2:=nil;
  try
    with ARoot do begin
      Name:=CreateRootName(ARoot);
      Lines:=TStringList.Create;
      Lines.Text:='First'+LineEnding+'Second';
    end;
    Expected:=#7'Strings'#1#6#5'First'#6#6'Second'#0#0;
    TestWriteDescendant('TestDefineProperites_ListOfStrings',ARoot,nil,[
    CSPDefaultExecCustomProc+'(Lines,[#7''Strings''#1#6#5''First''#6#6''Second''#0#0]);',
    '']);

    Lines2:=TStringList.Create;
    ExecCustomCSP(Lines2,[Expected]);
    AssertEquals('read TStrings.Text',ARoot.Lines.Text,Lines2.Text);

    AssertEquals('NeededUnits.Count',1,Writer.NeededUnits.Count);
    AssertEquals('NeededUnits[0]',Writer.ExecCustomProcUnit,Writer.NeededUnits[0]);

  finally
    Lines2.Free;
    FreeAndNil(ARoot.FLines);
    ARoot.Free;
  end;
end;

procedure TTestCompReaderWriterPas.Test_TStrings;
var
  ARoot: TSimpleControlWithStrings;
begin
  ARoot:=TSimpleControlWithStrings.Create(nil);
  try
    with ARoot do begin
      Name:=CreateRootName(ARoot);
      Lines:=TStringList.Create;
      Lines.Text:='First'+LineEnding+'Second';
    end;
    Writer.OnDefineProperties:=@OnDefinePropertiesTStrings;

    TestWriteDescendant('Test_TStrings',ARoot,nil,[
    'with Lines do begin',
    '  Clear;',
    '  Add(''First'');',
    '  Add(''Second'');',
    'end;',
    '']);
  finally
    FreeAndNil(ARoot.FLines);
    ARoot.Free;
  end;
end;

initialization
  RegisterTest(TTestCompReaderWriterPas);
end.

