пятница, 13 февраля 2015 г.

MindStream. Part 5. Testing

Original in Russian: http://habrahabr.ru/post/248027/

Table of contents

Hello, dear readership.

In this post I want to tell about the changes in our project and also about the technologies and methods we’ve used to achieve our goals.

Now our project looks as follows:



четверг, 5 февраля 2015 г.

Link. Why UML (for Delphi users)

"Delphi is actively used in company "Garant" together with UML. Sometimes someone consider RAD tools and UML-modeling as opposite technologies. Developers and architects in "Garant" do use a combination of UML-modeling, automatic code generation andDelphi - leading RAD tool. Let’s ask them how do they do this and why do they use UML.
Vsevolod Leonov (Seva)- product manager, Embarcadero Technologies.
Alexander Lulin (Alex) - developer lead, Garant.
Maxim Krylov (Max) - project manager, Garant
Company "Garant" is well known in Russia due to its key product - "Garant" system to provide information on Russian laws. Most of the company’s employees are developers. Their key product was started 23 years ago. The system has tens of millions of code lines, written in different programming languages. Today we’ll ask the leads how they have managed to support high quality of the product for this scale, language heterogeneity, production tempo, and requirements on stability. One of the key technologies aside from Delphi (as well as C++)  is UML.
Seva: did you start using UML from the beginning?
Max: No, we didn’t. When we started the system "Garamt", UML was not invented then. In about 1997 one of our young developer (we all were pretty young that time) brought a diskette with one of the first UML modeling tool. We think that was a beginning of UML in our company. But it took years to start an effective use of UML as a full integrated part of the process.
Alex: Moreover, bringing UML into development was long and in some steps. But as the complexity of our software grew, we needed more and more some tool to manage the code and architecture of the system."

More - http://blogs.embarcadero.com/vsevolodleonov/2013/08/12/why-uml-for-delphi-users/

MindStream. How we develop software for FireMonkey. Part 4. Serialization


Table of contents
Original in russian: http://habrahabr.ru/post/245441/

When I became enthusiastic about programming I liked working with files. Actually, the work was mainly to read the inputs and test records. Then I worked with databases and used files less often (except for IniFile sometimes). That is why I was rather interested in serialization.

Today I’ll tell you about how we’ve added serialization to our application, about the difficulties we’ve had and how we’ve overcome them. This material is not new, so it would more likely appeal to beginners. Though, anyone would be able to learn criticize some methods.

How to find out the true size of the memory allocated for by GetMem

Original in russian: http://18delphi.blogspot.ru/2013/04/getmem.html

Haven’t you thought that FreeMem does NOT REQUIRE this size? This means it somehow “knows” it.

It is quite simple:
function _l3MemorySize(aP: Pointer): Longint; 
 
{$IFDEF XE}
const
  {----------------------------Block type flags---------------------------}
  {The lower 3 bits in the dword header of small blocks (4 bits in medium and
   large blocks) are used as flags to indicate the state of the block}
  {Set if the block is not in use}
  IsFreeBlockFlag = 1;
  {Set if this is a medium block}
  IsMediumBlockFlag = 2;
  {Set if it is a medium block being used as a small block pool. Only valid if
   IsMediumBlockFlag is set.}
  IsSmallBlockPoolInUseFlag = 4;
  {Set if it is a large block. Only valid if IsMediumBlockFlag is not set.}
  IsLargeBlockFlag = 4;
  {Is the medium block preceding this block available?}
  PreviousMediumBlockIsFreeFlag = 8;
  {Is this large block segmented? I.e. is it actually built up from more than
   one chunk allocated through VirtualAlloc? (Only used by large blocks.)}
  LargeBlockIsSegmented = 8;
  {The flags masks for small blocks}
  DropSmallFlagsMask = -8;
  ExtractSmallFlagsMask = 7;
  {The flags masks for medium and large blocks}
  DropMediumAndLargeFlagsMask = -16;
  ExtractMediumAndLargeFlagsMask = 15;
{------------------------------Private types------------------------------}
type
 
  {Move procedure type}
  TMoveProc = procedure(const ASource; var ADest; ACount: NativeInt);
 
  {-----------------------Small block structures--------------------------}
 
  {Pointer to the header of a small block pool}
  PSmallBlockPoolHeader = ^TSmallBlockPoolHeader;
 
  {Small block type (Size = 32 bytes for 32-bit, 64 bytes for 64-bit).}
  PSmallBlockType = ^TSmallBlockType;
  TSmallBlockType = record
    {True = Block type is locked}
    BlockTypeLocked: Boolean;
    {Bitmap indicating which of the first 8 medium block groups contain blocks
     of a suitable size for a block pool.}
    AllowedGroupsForBlockPoolBitmap: Byte;
    {The block size for this block type}
    BlockSize: Word;
    {The minimum and optimal size of a small block pool for this block type}
    MinimumBlockPoolSize: Word;
    OptimalBlockPoolSize: Word;
    {The first partially free pool for the given small block. This field must
     be at the same offset as TSmallBlockPoolHeader.NextPartiallyFreePool.}
    NextPartiallyFreePool: PSmallBlockPoolHeader;
    {The last partially free pool for the small block type. This field must
     be at the same offset as TSmallBlockPoolHeader.PreviousPartiallyFreePool.}
    PreviousPartiallyFreePool: PSmallBlockPoolHeader;
    {The offset of the last block that was served sequentially. The field must
     be at the same offset as TSmallBlockPoolHeader.FirstFreeBlock.}
    NextSequentialFeedBlockAddress: Pointer;
    {The last block that can be served sequentially.}
    MaxSequentialFeedBlockAddress: Pointer;
    {The pool that is current being used to serve blocks in sequential order}
    CurrentSequentialFeedPool: PSmallBlockPoolHeader;
{$ifdef UseCustomFixedSizeMoveRoutines}
    {The fixed size move procedure used to move data for this block size when
     it is upsized. When a block is downsized (which usually does not occur
     that often) the variable size move routine is used.}
    UpsizeMoveProcedure: TMoveProc;
{$else}
    Reserved1: Pointer;
{$endif}
{$if SizeOf(Pointer) = 8}
    {Pad to 64 bytes for 64-bit}
    Reserved2: Pointer;
{$ifend}
  end;
 
  {Small block pool (Size = 32 bytes for 32-bit, 48 bytes for 64-bit).}
  TSmallBlockPoolHeader = record
    {BlockType}
    BlockType: PSmallBlockType;
{$if SizeOf(Pointer) <> 8}
    {Align the next fields to the same fields in TSmallBlockType and pad this
     structure to 32 bytes for 32-bit}
    Reserved1: Cardinal;
{$ifend}
    {The next and previous pool that has free blocks of this size. Do not
     change the position of these two fields: They must be at the same offsets
     as the fields in TSmallBlockType of the same name.}
    NextPartiallyFreePool: PSmallBlockPoolHeader;
    PreviousPartiallyFreePool: PSmallBlockPoolHeader;
    {Pointer to the first free block inside this pool. This field must be at
     the same offset as TSmallBlockType.NextSequentialFeedBlockAddress.}
    FirstFreeBlock: Pointer;
    {The number of blocks allocated in this pool.}
    BlocksInUse: Cardinal;
    {Small block pool signature. Used by the leak checking mechanism to
     determine whether a medium block is a small block pool or a regular medium
     block.}
    SmallBlockPoolSignature: Cardinal;
    {The pool pointer and flags of the first block}
    FirstBlockPoolPointerAndFlags: NativeUInt;
  end;
 
  {Small block layout:
   At offset -SizeOf(Pointer) = Flags + address of the small block pool.
   At offset BlockSize - SizeOf(Pointer) = Flags + address of the small block
   pool for the next small block.
  }
 
  {------------------------Medium block structures------------------------}
 
  {The medium block pool from which medium blocks are drawn. Size = 16 bytes
   for 32-bit and 32 bytes for 64-bit.}
  PMediumBlockPoolHeader = ^TMediumBlockPoolHeader;
  TMediumBlockPoolHeader = record
    {Points to the previous and next medium block pools. This circular linked
     list is used to track memory leaks on program shutdown.}
    PreviousMediumBlockPoolHeader: PMediumBlockPoolHeader;
    NextMediumBlockPoolHeader: PMediumBlockPoolHeader;
    {Padding}
    Reserved1: NativeUInt;
    {The block size and flags of the first medium block in the block pool}
    FirstMediumBlockSizeAndFlags: NativeUInt;
  end;
 
  {Medium block layout:
   Offset: -2 * SizeOf(Pointer) = Previous Block Size (only if the previous block is free)
   Offset: -SizeOf(Pointer) = This block size and flags
   Offset: 0 = User data / Previous Free Block (if this block is free)
   Offset: SizeOf(Pointer) = Next Free Block (if this block is free)
   Offset: BlockSize - 2*SizeOf(Pointer) = Size of this block (if this block is free)
   Offset: BlockSize - SizeOf(Pointer) = Size of the next block and flags
 
  {A medium block that is unused}
  PMediumFreeBlock = ^TMediumFreeBlock;
  TMediumFreeBlock = record
    PreviousFreeBlock: PMediumFreeBlock;
    NextFreeBlock: PMediumFreeBlock;
  end;
 
  {-------------------------Large block structures------------------------}
 
  {Large block header record (Size = 16 for 32-bit, 32 for 64-bit)}
  PLargeBlockHeader = ^TLargeBlockHeader;
  TLargeBlockHeader = record
    {Points to the previous and next large blocks. This circular linked
     list is used to track memory leaks on program shutdown.}
    PreviousLargeBlockHeader: PLargeBlockHeader;
    NextLargeBlockHeader: PLargeBlockHeader;
    {The user allocated size of the Large block}
    UserAllocatedSize: NativeUInt;
    {The size of this block plus the flags}
    BlockSizeAndFlags: NativeUInt;
  end;
 
 
{---------------------------Private constants-----------------------------}
const
  {The size of the block header in front of small and medium blocks}
  BlockHeaderSize = SizeOf(Pointer);
  {The size of a small block pool header}
  SmallBlockPoolHeaderSize = SizeOf(TSmallBlockPoolHeader);
  {The size of a medium block pool header}
  MediumBlockPoolHeaderSize = SizeOf(TMediumBlockPoolHeader);
  {The size of the header in front of Large blocks}
  LargeBlockHeaderSize = SizeOf(TLargeBlockHeader);
 
function _l3MemorySize(aP: Pointer): Longint;
var
lBlockHeader: Cardinal;
LPSmallBlockType: PSmallBlockType;
LOldAvailableSize: Cardinal;
begin
if (aP = nil) then
  Result := 0
else
begin
{Get the block header: Is it actually a small block?}
  LBlockHeader := PNativeUInt(PByte(aP) - BlockHeaderSize)^;
  {Is it a small block that is in use?}
  if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag or IsLargeBlockFlag) = 0 then
  begin
    {----------------------------Small block------------------------------}
    {The block header is a pointer to the block pool: Get the block type}
    LPSmallBlockType := PSmallBlockPoolHeader(LBlockHeader).BlockType;
    {Get the available size inside blocks of this type.}
    Result := LPSmallBlockType.BlockSize - BlockHeaderSize;
  end
  else
  begin
    {Is this a medium block or a large block?}
    if LBlockHeader and (IsFreeBlockFlag or IsLargeBlockFlag) = 0 then
    begin
     Result:= (LBlockHeader and DropMediumAndLargeFlagsMask) - BlockHeaderSize;
    end
    else
    begin
      {Is this a valid large block?}
      if LBlockHeader and (IsFreeBlockFlag or IsMediumBlockFlag) = 0 then
      begin
        {-----------------------Large block------------------------------}
        {Get the block header}
        //LBlockHeader := PNativeUInt(PByte(aP) - BlockHeaderSize)^;
        {Subtract the overhead to determine the useable size in the large block.}
        Result := (LBlockHeader and DropMediumAndLargeFlagsMask) - (LargeBlockHeaderSize + BlockHeaderSize);
      end
      else
      begin
        {-----------------------Invalid block------------------------------}
        {Bad pointer: probably an attempt to reallocate a free memory block.}
        Result := 0;
      end;
    end;
  end;
end;
end;
{$ELSE XE}
const
  cThisUsedFlag = 2;
  cPrevFreeFlag = 1;
  cFillerFlag   = Integer($80000000);
  cFlags        = cThisUsedFlag or cPrevFreeFlag or cFillerFlag;
 
type
  PUsed = ^TUsed;
  TUsed = packed record
    sizeFlags: Integer;
  end;//TUsed
 
function _l3MemorySize(aP: Pointer): Longint; 
begin
 if (aP = nil) then
  Result := 0
 else
  Result := PUsed(PAnsiChar(aP)-SizeOf(TUsed)).sizeFlags and not cFlags - sizeof(TUsed);
//  Result := (PLong(Long(aP) - 4)^ AND not cFlags) - 4;
end;
{$EndIf XE}

Why do we need it? You’d ask… So that not to store Capacity. We’re being “penny wise”. May be I will get to the theme “my micro-STL for Delphi”, where it would be highlighted.

Disclaimer. This code was written not only by me, it was taken from standard library. The colleague helped me to make it out. If I infringe somebody’s copyright, I’ll delete this post.

Briefly. About “reverse stability”

Original in russian: http://programmingmindstream.blogspot.ru/2014/08/blog-post_17.html

I’ll go back a long way, to C++ where there’s no operator with (and luckily never will be).

But there are “block variables” and the constructions like int & X = MyClass.MyField.MySubField;).
Let the code be like:

class A
{
 private:
  int X; // - this is a private member of A class
};//A
 
int X = 0; // - это глобальная переменная
 
class B : A
{
 void Dummy ()
 {
  X = 123; // - here the compiler will grumble that “there is a private member 
           //   which can “shade” the global variable” 

 };
};//B

As a result, the code will not be compiled.

How can it be compiled?

What if we try this one:

class A
{
 private:
  int X; // - this is a private member of A class
};//A
 
int X = 0; // - This is Global variable
 
class B : A
{
 void Dummy ()
 {
  ::X = 123; // - here the compiler will not grumble because will “understands” 
             // that X is a GLOBAL variable and ONLY X is
 };
};//B

This strategy of the compiler is the RIGHT one.

If code could be compiled, what would we get?

We’d get this:

class A
{
 protected:
  int X; // - this is a “protected” member of A class
};//A
 
int X = 0; // - this is a global variable
 
class B : A
{
 void Dummy ()
 {
  X = 123; // - WHY! Here X “suddenly” becomes a member of A class 
           // and not a global variable
 };
};//B

And so we get an “unexpected result”.

But! The compiler does not do so.

That is what we call a “reverse stability”.

And the operator with in Delphi does not have the “reverse stability”. It’s a pity.

Let me explain:
type
 TA = class
  public
   Caption : String;
 end;//TA
 
 TB = class
  public
   Caption : String;
   A : TA;
 end;//TB
...
procedure TB.SomeProc;
begin
 with A do
  Caption := '123'; // - This is assigned to Self.A.Caption 
end;

And in this way:
Unit uA;
...
type
 TA = class
  private
   Caption : String;
 end;//TA
...
Unit uB;
...
 TB = class
  public
   Caption : String;
   A : TA;
 end;//TB
...
procedure TB.SomeProc;
begin
 with A do
  Caption := '123'; // - This is assigned to Self.A.Caption 
end;

We’ll say: WOW!

One more thing…

How could we write in C++?
void TB::SomeProc ()
{
 ...
 {
  ...
  std::string & vCap = A.Caption;
  vCap = '123';
  ...
 }
 ...
}

Do you understand why I wrote about the “block variables” and about &? Do you understand why “in this context” C++ does not need operator with.

вторник, 3 февраля 2015 г.

Briefly. About triggering the exceptions

Original in russian: http://programmingmindstream.blogspot.ru/2014/08/blog-post_85.html

I wіll definitely not break new ground, but I will write.

It can be done in this way:

type
 EmyException = class(Exception)
 end;//EmyException
...
if not Condition1 then
 raise EMyException.Create('Some string1');
...
if not Condition2 then
 raise EMyException.Create('Some string2');

Or in this way:

type
 EmyException = class(Exception)
  public
   class procedure Check(aCondition: Boolean; 
                         const aMessage: String);
 end;//EmyException
...
class procedure EmyException.Check(aCondition: Boolean; 
                                   const aMessage: String);
begin
 if not aCondition then
  raise Self.Create(aMessage);
end;
...
EMyException.Check(Condition1, 'Some string1');
...
EMyException.Check(Condition2, 'Some string2');

Both these options seem identical.

But, as for me, the second one is more “tasty” as well as more readable.

In debugging – more useful.

Why useful? Because you can set one break-point to EmyException.Check compared to the bags of them throughout the code.

Perhaps, it is included in standard library on new versions of Delphi. I actually did not found out. But we have been using this approach already for a long time and we like it.

Of course, it can be enhanced and improved.

For example, you could not pass the string but “generate” it inside Check. Generally, there is lots of alternatives.

I will also note that I’ve seen enough people who’ve forgotten raise.
It means they wrote like this:

 Exception.Create('aMessage');

But not like this:

 raise Exception.Create('aMessage');

And about the "expansions”.

For example, you can do like this:

type
 TMyPredicate = reference to function (aData: Integer): Boolean;
 EmyException = class(Exception)
  public
   class procedure Check(aCondition: Boolean; 
                         const aMessage: String); overload;
   class procedure Check(aCondition: TMyPredicate; 
                         const aMessage: String; 
                         aData: Integer); overload;
 end;//EmyException
...
class procedure EmyException.Check(aCondition: Boolean; 
                                   const aMessage: String);
begin
 if not aCondition then
  raise Self.Create(aMessage);
end;

class procedure EmyException.Check(aCondition: TMyPredicate; 
                                   const aMessage: String; 
                                   aData: Integer);
begin
 if not aCondition(aData) then
  raise Self.Create('InvalidData: ' + IntToStr(aData) + aMessage);
end;

...
EMyException.Check(Condition1, 'Some string1');
...
EMyException.Check(
 function (aData: Integer): Boolean; 
 begin 
  Result := IsValid(aData); 
 end;, 
 'Some string2', 
 SomeComplexExpression);

Of course, it is only a “prototype”, not a true working code.

What’s so good of the “prototype”? It is that SomeComplexExpression will be evaluated only once.

Of course, you could do with a “locale variable” – in the simplest cases.

Besides, we can write like this:

var
 SomeLocalData : Integer;

EMyException.Check(
 function (aData: Integer): Boolean; 
 begin 
  Result := (aData = SomeLocalData); 
 end;, 
 'Some string2', 
 SomeComplexExpression);

In response to the commentary: http://programmingmindstream.blogspot.ru/2014/08/blog-post_85.html?showComment=1408566592655#c1216957169742679866

As for “using just a procedure”, we’ve used them too, of course:

function Ht(ID : LongInt) : LongInt;
{var
 nDosError : SmallInt; // Here the code returned by DOS will be entered
 nOperation: SmallInt; // Here the code of the operation causing an error will be entered
 lErrstr : array[0..1000] of AnsiChar;
 lErrstr2 : PAnsiChar;
}
begin
 Result := ID;

 if lNeedStackOut_ErrNum <> 0 then
 begin
  l3System.Stack2Log(Format('HTERROR = %d STACK OUT', [lNeedStackOut_ErrNum]));
  lNeedStackOut_ErrNum := 0;
 end;

{ if ID = -1 then
  lErrstr2 := htExtError(nDosError, nOperation, @lErrstr[0]);
}
 if ID < 0 then
  raise EHtErrors.CreateInt(ID);
end;
....
   Ht(htOpenResults(Masks,ROPEN_READ,@FldArr,FldCount));
....
     Ht(htDeleteRecords(TmpList));
....
     Ht(htOpenResults(ValList,ROPEN_READ,nil,0));

One more thing.

What will happen if we write like this:

type
 EmyException2 = class(EmyException)
 end;//EmyException2

...
EmyException2.Check(aCondition, aMessage);

Exception of which class will trigger: EmyException or EmyException2?

As could be expected, it is EmyException2 :-)

It concerns “why class method and not just function”.

That is in some way an answer to: http://programmingmindstream.blogspot.ru/2014/08/blog-post_85.html?showComment=1408649403323#c8271514992700352647)

By the way, here is an example of what was written at the beginning:

Em3InvalidStreamPos.Check(Self.IsValidPosition,
                          aHeader.f_Name,
                          l_Pos);
Em3InvalidStreamSize.Check(Self.IsValidPosition,
                           aHeader.f_Name,
                           aHeader.f_TOCItemData.rBody.rRealSize);
Em3InvalidStreamPos.Check(Self.IsValidLink,
                          aHeader.f_Name,
                          aHeader.f_TOCItemData.rBody.RTOCBuffRootPosition);
Em3InvalidStreamPos.Check(Self.IsValidLink,
                          aHeader.f_Name,
                          aHeader.f_TOCItemData.rBody.RTOCItemListPosition);
Em3InvalidStreamPos.Check(Self.IsValidLink,
                          aHeader.f_Name,
                          aHeader.f_TOCItemData.RNextPosition);

IsValidPosition and IsValidLink are predicates.

In other words, function (aData: Int64): Boolean of object;

Check in this case looks like this:

type
 TInt64Predicate = function (aData: Int64): Boolean of object;
...
class procedure Em3InvalidStreamData.Check(aCondition: TInt64Predicate;
                                           aName : String;
                                           aData : Int64);
begin
 if not aCondition(aData) then
  raise Self.CreateFmt('Invalid data %d in file %s', [aName, aData]);
end; 
...
Em3InvalidStreamPos = class(Em3InvalidStreamData);

Em3InvalidStreamSize = class(Em3InvalidStreamData);

What else do we need it for?

So that not to make a mess in the function Format and not to debug the raised exception, that will appear but not “tell the truth”.

It is obvious that the example of the code does not look good, but I did not refined it for purpose.

Of course, “tastes differ”, but I like it more than with a “band” of raise.

Try to write with raise and show that it will “be shorter”. I’ll be glad to learn.

By the way, directing of stack to the log file helps to identify the string from which an exception sprang. May be, I will write about it some time.

(Meanwhile here a discussion takes place).

And I will note: we could write something like:

Em3InvalidStreamPos.Check(Self.IsValidPosition,
                          aHeader.f_Name,
                          [l_Pos,
                           aHeader.f_TOCItemData.rBody.rRealSize,
                           aHeader.f_TOCItemData.rBody.RTOCBuffRootPosition,
                           aHeader.f_TOCItemData.rBody.RTOCItemListPosition,
                           aHeader.f_TOCItemData.RNextPosition]);

But I do not do it consciously, since writing is surely shorter and more readable.

But! Using this approach it is more difficult to find out the real source of an error.

I could also write with, I know it. But I personally am an “ideological enemy” of with, especially in Delphi version without “reverse stability”.

Do you understand what "reverse stability" is? Or do I “think up my own terms” again?

Just in case I’ve written this one: Briefly. About “reverse stability”

вторник, 13 января 2015 г.

MindStream. How we develop software for FireMonkey. Part 3.1. Inspired by GUIRunner

Original post in Russian: http://habrahabr.ru/post/241377/ 

Table of contents

Inspired by GUIRunner

I’ve finished my post about how we’ve decided to develop our own GUIRunner for FireMonkey. In the commentaries to the post in one of the social networks Aleksey Timohin has drawn my attention to another framework for testing – DUniX.
I have tried to find the alternative by using the console version but Alexander was implacable. When I entered the repository and saw a finished GUIRunner for FireMonkey I quite drooped my head.

Anyway. After running DUniX for the first time I have written to Alexander: “LOL. There are no ticks there”. So I did not solve the problem in vain. After a more thorough examining I had the impression that a person who coded this form has also “peered” a bit into Original GUIRunner.
In general, I would really be pleased with such a gift near a month ago when I have not yet learned through mistakes. And today I was just interested how another programmer solved this problem. We’ve made a number of almost the same mistakes. I have written in the post about how to connect tests with branches and it ended with me offering to perform refactoring using TDictionary.
Let me remind you of the original:
  l_Test.GUIObject := aNode.Items[l_Index];
  ...
  l_TreeViewItem.Tag := FTests.Add(aTest);
DUnitX developer did in almost the same way. However, he made a wrapper on TTreeViewItem (I will adopt it in the future):
type
  TTestNode = class(TTreeViewItem)
  strict private
    FFullName: String;
    FImage: TImage;
  public
    constructor Create(Owner: TComponent; Text: String; TestFullName: String); reintroduce;
    destructor Destroy; override;
    property FullName: String read FFullName;
    procedure SetResultType(resultType: TTestResultType);
    procedure Reload;
  end;
I connected each test with a branch named after the test.
function TGUIXTestRunner.GetNode(FullName: String): TTreeViewItem;
var
  i: Integer;
begin
  Result := nil;
  i := 0;
  repeat begin
    if (TestTree.ItemByGlobalIndex(i) as TTestNode).FullName = FullName then
      Result := TestTree.ItemByGlobalIndex(i);
    Inc(i);
   end
   until Assigned(Result) or (i >= TestTree.GlobalCount);
end;
What surprised me is the following issue:
  FFailedTests: TDictionary<string>;
Try to guess why do we need key String? That’s right – so that with a help of it we could get to the branch and report on its state after having finished the test. As for me, we’ve overcomplicated things. Class TTreeNode is worth special mentioning. It stores a “link” to the test and a picture that will change the state of the branch. Since this class is inherited from TreeViewItem, this code works successfully:
var
   testNode : TTreeViewItem;  
   ...
   testNode := CreateNode(TestTree, test.Name, test.Fixture.FullName + '.' + test.Name);
   ...

function TGUIXTestRunner.CreateNode(Owner: TComponent; Text: String; TestFullName: String): TTreeViewItem;
begin
  Result := TTestNode.Create(Owner, Text, TestFullName);
end;
...
constructor TTestNode.Create(Owner: TComponent; Text, TestFullName: String);
begin
  inherited Create(Owner);
  Self.Text := Text;
  FFullName := TestFullName;
  FImage := TImage.Create(Owner);
  FImage.Parent := Self;
  {$IFDEF DELPHI_XE6_UP}
  FImage.Align := TAlignLayout.Right;
  {$ELSE}
  FImage.Align := TAlignLayout.alRight;
  {$ENDIF}
  FImage.Bitmap.Create(15, 15);
  FImage.Bitmap.Clear(TAlphaColorRec.Gray);
  FImage.SendToBack;
end;  
In general, DUnitX made a positive impression on me. The framework seems to be far more trust-worthy than its “big brother”. The developers reconsidered and in a way improwed the interface and the architecture. The whole code looks really neat. There are much more commentaries there. I will examine and compare.
Links:
Repository Delphi-Mocks. Need for compile the framework.
Repository DUnitX.

MindStream. How we develop software for FireMonkey. Part 2

Original post in Russian -http://habrahabr.ru/post/234801/

Table of contents

Good day to you. In this article I will continue to tell how we develop software for FireMonkey. Two interesting objects will be added. Both of them will remind us of vector algebra and trigonometry. The methods from OOP that we use will also be shown in the post. A number of lines (that differ from each other only by dashes, dot-and-dash, point-to-point, etc.) that we added were made in the same way as the description of previous primitives. Now it is time to move on to more complex figures (including compound ones). The first primitive to be added is an arrowed line (as an arrow an ordinary triangle of smaller size will serve). To begin with, let us introduce a triangle “looking” to the right. It means we will inherit the ordinary triangle and rewrite for it Polygon method that is responsible for the coordinates of the vertices.
function TmsTriangleDirectionRight.Polygon: TPolygon;
begin
  SetLength(Result, 4);
  Result[0] := TPointF.Create(StartPoint.X - InitialHeight / 2,
                              StartPoint.Y - InitialHeight / 2);
  Result[1] := TPointF.Create(StartPoint.X - InitialHeight / 2,
                              StartPoint.Y + InitialHeight / 2);
  Result[2] := TPointF.Create(StartPoint.X + InitialHeight / 2,
                              StartPoint.Y);
  Result[3] := Result[0];
end;
That is how our triangles look like:



Further, we inherit a so-called “small triangle”:
type
  TmsSmallTriangle = class(TmsTriangleDirectionRight)
  protected
    function FillColor: TAlphaColor; override;
  public
    class function InitialHeight: Single; override;
  end; // TmsSmallTriangle
As you can see, all we have done is overridden functions that are unique for the new triangle. Next, we’ll add a class of an arrowed line inherited from an ordinary line. Only the procedure of drawing the primitive will be overridden in the class, in other words, the line will be drawn by a base class and the triangle is an heir.
procedure TmsLineWithArrow.DoDrawTo(const aCtx: TmsDrawContext);
var
  l_Proxy : TmsShape;
  l_OriginalMatrix: TMatrix;
  l_Matrix: TMatrix;
  l_Angle : Single;
  l_CenterPoint : TPointF;

  l_TextRect : TRectF;
begin
  inherited;
  if (StartPoint <> FinishPoint) then
  begin
    l_OriginalMatrix := aCtx.rCanvas.Matrix;
    try
    l_Proxy := TmsSmallTriangle.Create(FinishPoint);
      try
        // Just yet in order to experiment we specify rotation by 0 degrees
        // to ensure the triangle is drawn correctly
        l_Angle := DegToRad(0);

        l_CenterPoint := TPointF.Create(FinishPoint.X , FinishPoint.Y);

        // We’ve stored the initial matrix 
        l_Matrix := l_OriginalMatrix;
        // We’ve transferred the origin of coordinates in the point where a rotation will be made
        l_Matrix := l_Matrix * TMatrix.CreateTranslation(-l_CenterPoint.X, -l_CenterPoint.Y);
        // The process itself
        l_Matrix := l_Matrix * TMatrix.CreateRotation(l_Angle);
        // We’ve put back the origin of the coordinates
        l_Matrix := l_Matrix * TMatrix.CreateTranslation(l_CenterPoint.X, l_CenterPoint.Y);
        
        // We are applying our space matrix to the canvas
        aCanvas.SetMatrix(l_Matrix);

        // We’re drawing :) 
        l_Proxy.DrawTo(aCanvas, aOrigin);
      finally
        FreeAndNil(l_Proxy);
      end; // try..finally
    finally
      // Since we’ve drawn the required figure we put back the initial matrix to the canvas
      aCanvas.SetMatrix(l_OriginalMatrix);
    end;
  end;//(StartPoint <> FinishPoint)
end;
There is nothing much to explain, everything is already given in the commentaries. Anyway, if you wish to recollect what vector algebra is and how to work with vector graphic (moving, figures rotation and so on) I recommend a remarkable post on Habr on this subject as well as articles here and here.  As shown in the picture, our triangle is now drawn only if we draw a line from left to right:

Now the task becomes more interesting. We have to rotate a triangle perpendicular to the line that has drawn it. In order to do this we’ll introduce method GetArrowAngleRotation calculating the rotation angle. Let us imagine that our line is a hypotenuse of a right-angled triangle; then let’s find an angle with a leg of the triangle. By this angle the triangle will rotate around the line:

function TmsLineWithArrow.GetArrowAngleRotation: Single;
var
  l_ALength, l_CLength, l_AlphaAngle, l_X, l_Y, l_RotationAngle: Single;
  l_PointC: TPointF;
  l_Invert: SmallInt;
begin
  Result := 0;

  // The formula for calculating the distance between two points  
  l_X := (FinishPoint.X - StartPoint.X) * (FinishPoint.X - StartPoint.X);
  l_Y := (FinishPoint.Y - StartPoint.Y) * (FinishPoint.Y - StartPoint.Y);

  // We find the length of the hypotenuse of the right-angled triangle
  l_CLength := sqrt(l_X + l_Y);

  l_PointC := TPointF.Create(FinishPoint.X, StartPoint.Y);

  // The formula for calculating the distance between two points
  l_X := (l_PointC.X - StartPoint.X) * (l_PointC.X - StartPoint.X);
  l_Y := (l_PointC.Y - StartPoint.Y) * (l_PointC.Y - StartPoint.Y);

  // We find the length of the leg
  l_ALength := sqrt(l_X + l_Y);

  // The angle in radians
  l_AlphaAngle := ArcSin(l_ALength / l_CLength);

  l_RotationAngle := 0;
  l_Invert := 1;

  if FinishPoint.X > StartPoint.X then
  begin
    l_RotationAngle := Pi / 2 * 3;
    if FinishPoint.Y > StartPoint.Y then
      l_Invert := -1;
  end
  else
  begin
    l_RotationAngle := Pi / 2;
    if FinishPoint.Y < StartPoint.Y then
      l_Invert := -1;
  end;

  Result := l_Invert * (l_AlphaAngle + l_RotationAngle);
end;

Now our line looks like this:

Next, we’ll add an object responsible for moving of the figures.
We’ll use the following algorithm:
1. We need a method to determine whether a point gets to a specific figure, say ContainsPt, for each figure. Since formulas for calculating the getting are unique for each figure, we’ll use virtual functions.
2. The next method is required to determine which figure the point has got to in case figures cross. Since the figures get on the list as they appear on the form, provided that figures cross, the figure at the beginning of the list is the last to have appeared and is therefore at the top. In fact, there’s a pitfall in this logic, but for now let’s assume it is right and leave the correction for the next post.
3. On the first click in the figure to which we’ve got we have to change its outline or a number of other characteristics.
4. On the second click we have to move the figure to which we’ve got.
The class of the moving will be inherited from a standard figure, but it will store a figure that it moves, and this class is the one to redraw the figure on the second click (I have described the peculiarities of drawing the lines in my previous post).

We implement the methods I’ve described.
1. This method detects whether a point gets to a figure (in our case it is a rectangle):
function TmsRectangle.ContainsPt(const aPoint: TPointF): Boolean;
var
  l_Finish : TPointF;
  l_Rect: TRectF;
begin
  Result := False;
  l_Finish := TPointF.Create(StartPoint.X + InitialWidth,
                             StartPoint.Y + InitialHeight);
  l_Rect := TRectF.Create(StartPoint,l_Finish);
  Result := l_Rect.Contains(aPoint);
end;
2. This method is used to detect which figure we’ve got to:
class function TmsShape.ShapeByPt(const aPoint: TPointF; aList: TmsShapeList): TmsShape;
var
  l_Shape: TmsShape;
  l_Index: Integer;
begin
  Result := nil;
  for l_Index := aList.Count - 1 downto 0 do
  begin
    l_Shape := aList.Items[l_Index];
    if l_Shape.ContainsPt(aPoint) then
    begin
      Result := l_Shape;
      Exit;
    end; // l_Shape.ContainsPt(aPoint)
  end; // for l_Index
end;
3. On the first click in the figure that we’ve got to we have to change its outline or a number of other characteristics. To implement this method we’ll perform a slight refactoring. We introduce a so-called “drawing context”:
type
  TmsDrawContext = record
  public
    rCanvas: TCanvas;
    rOrigin: TPointF;
    rMoving: Boolean; // We define that the currently drawn primitive is moving
    constructor Create(const aCanvas: TCanvas; const aOrigin: TPointF);
  end; // TmsDrawContext
If we specify the figure as “movable” in the drawing context, the drawing will be performed in the different way.
procedure TmsShape.DrawTo(const aCtx: TmsDrawContext);
begin
  aCtx.rCanvas.Fill.Color := FillColor;
  if aCtx.rMoving then
  begin
    aCtx.rCanvas.Stroke.Dash := TStrokeDash.sdDashDot;
    aCtx.rCanvas.Stroke.Color := TAlphaColors.Darkmagenta;
    aCtx.rCanvas.Stroke.Thickness := 4;
  end
  else
  begin
    aCtx.rCanvas.Stroke.Dash := StrokeDash;
    aCtx.rCanvas.Stroke.Color := StrokeColor;
    aCtx.rCanvas.Stroke.Thickness := StrokeThickness;
  end;
  DoDrawTo(aCtx);
end;
4. On the second click we have to move the figure to which we’ve got. To begin with, let’s introduce a factory method responsible for figure constructing (the list of the figures is required so that TmsMover could call all the figures drawn within the current diagram).
class function TmsShape.Make(const aStartPoint: TPointF; aListWithOtherShapes: TmsShapeList): TmsShape;
begin
  Result := Create(aStartPoint);
end;
... 
class function TmsMover.Make(const aStartPoint: TPointF;
                                   aListWithOtherShapes: TmsShapeList): TmsShape;
var
  l_Moving: TmsShape;
begin
  l_Moving := ShapeByPt(aStartPoint, aListWithOtherShapes);
  if (l_Moving <> nil) then
    Result := Create(aStartPoint, aListWithOtherShapes, l_Moving)
  else
    Result := nil;
end;
As a result of using class function we’ve fundamentally separated creating of the moved object and of the other figures. However, this approach has a disadvantage. For instance, we introduced a parameter of creating aListWithOtherShapes which is not required for the other figures at all.
type
  TmsMover = class(TmsShape)
  private
    f_Moving: TmsShape;
    f_ListWithOtherShapes: TmsShapeList;
  protected
    procedure DoDrawTo(const aCtx: TmsDrawContext); override;
    constructor Create(const aStartPoint: TPointF; aListWithOtherShapes: TmsShapeList; aMoving: TmsShape);
  public
    class function Make(const aStartPoint: TPointF; aListWithOtherShapes: TmsShapeList): TmsShape; override;
    class function IsNeedsSecondClick: Boolean; override;
    procedure EndTo(const aFinishPoint: TPointF); override;
  end; // TmsMover

implementation

uses
  msRectangle,
  FMX.Types,
  System.SysUtils;

constructor TmsMover.Create(const aStartPoint: TPointF; aListWithOtherShapes: TmsShapeList; aMoving: TmsShape);
begin
  inherited Create(aStartPoint);
  f_ListWithOtherShapes := aListWithOtherShapes;
  f_Moving := aMoving;
end;

class function TmsMover.Make(const aStartPoint: TPointF;
                                   aListWithOtherShapes: TmsShapeList): TmsShape;
var
  l_Moving: TmsShape;
begin
  l_Moving := ShapeByPt(aStartPoint, aListWithOtherShapes);
  if (l_Moving <> nil) then
    Result := Create(aStartPoint, aListWithOtherShapes, l_Moving)
  else
    Result := nil;
end;

class function TmsMover.IsNeedsSecondClick: Boolean;
begin
  Result := true;
end;

procedure TmsMover.EndTo(const aFinishPoint: TPointF);
begin
  if (f_Moving <> nil) then
    f_Moving.MoveTo(aFinishPoint);
  f_ListWithOtherShapes.Remove(Self);
  // Now I must delete MYSELF since we do not need mover in the general list after it has performed its function
end;

procedure TmsMover.DoDrawTo(const aCtx: TmsDrawContext);
var
  l_Ctx: TmsDrawContext;
begin
  if (f_Moving <> nil) then
  begin
    l_Ctx := aCtx;
    l_Ctx.rMoving := true;
    f_Moving.DrawTo(l_Ctx);
  end; // f_Moving <> nil
end;

initialization

TmsMover.Register;

end.
In the controller we just have to change methods of figures creating:
procedure TmsDiagramm.BeginShape(const aStart: TPointF);
begin
  Assert(CurrentClass <> nil);
  FCurrentAddedShape := CurrentClass.Make(aStart, FShapeList);
  if (FCurrentAddedShape <> nil) then
  begin
    FShapeList.Add(FCurrentAddedShape);
    if not FCurrentAddedShape.IsNeedsSecondClick then
      FCurrentAddedShape := nil;
    Invalidate;
  end; // FCurrentAddedShape <> nil
end;

procedure TmsDiagramm.EndShape(const aFinish: TPointF);
begin
  Assert(CurrentAddedShape <> nil);
  CurrentAddedShape.EndTo(aFinish);
  FCurrentAddedShape := nil;
  Invalidate;
end;
In case of mover a call CurrentAddedShape.EndTo(aFinish) calls another one - MoveTo, i.e. moves the figure, and the redrawing, as shown above, is initiated by the controller:
procedure TmsMover.EndTo(const aFinishPoint: TPointF);
begin
  if (f_Moving <> nil) then
    f_Moving.MoveTo(aFinishPoint);
  f_ListWithOtherShapes.Remove(Self);
  // Now I must delete MYSELF since we do not need mover in the general list after it has performed its function
end;

...

procedure TmsShape.MoveTo(const aFinishPoint: TPointF);
begin
  FStartPoint := aFinishPoint;
end;
Since the controller is responsible for figures behavior, we’ll take the method of checking the “getting to a figure” away to the controller, and while creating the objects we’ll pass a check function:
type
  TmsShapeByPt = function (const aPoint: TPointF): TmsShape of object;
...
class function Make(const aStartPoint: TPointF; aShapeByPt: TmsShapeByPt): TmsShape; override;
...
procedure TmsDiagramm.BeginShape(const aStart: TPointF);
begin
 Assert(CurrentClass <> nil);
 // The call itself
 FCurrentAddedShape := CurrentClass.Make(aStart, Self.ShapeByPt);
 if (FCurrentAddedShape <> nil) then
 begin
  FShapeList.Add(FCurrentAddedShape);
  if not FCurrentAddedShape.IsNeedsSecondClick then
   FCurrentAddedShape := nil;
  Invalidate;
 end;//FCurrentAddedShape <> nil
end;
Since we have to pass two parameters in order to create objects, we define a context of “creating”:
type
  TmsMakeShapeContext = record
  public
    rStartPoint: TPointF;
    rShapeByPt: TmsShapeByPt;
    constructor Create(aStartPoint: TPointF; aShapeByPt: TmsShapeByPt);
  end;//TmsMakeShapeContext
Let’s add interfaces implemented by the controller and also a class of interface object. In the future we’ll realise our own reference counting in it.
type
  TmsInterfacedNonRefcounted = class abstract(TObject)
  protected
    function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
    function _AddRef: Integer; stdcall;
    function _Release: Integer; stdcall;
  end;//TmsInterfacedNonRefcounted

  TmsShape = class;

  ImsShapeByPt = interface
    function ShapeByPt(const aPoint: TPointF): TmsShape;
  end;//ImsShapeByPt

  ImsShapesController = interface
    procedure RemoveShape(aShape: TmsShape);
  end;//ImsShapeRemover
Let’s slightly change TmsMakeShapeContext:
type
  TmsMakeShapeContext = record
  public
    rStartPoint: TPointF;
    rShapesController: ImsShapesController;
    constructor Create(aStartPoint: TPointF; const aShapesController: ImsShapesController);
  end; // TmsMakeShapeContext
I recommend two interesting posts on interfaces and details of working with them in Delphi: 18delphi.blogspot.com/2013/04/iunknown.html  and habrahabr.ru/post/181107/
Let’s ensure that our controller (TmsDiagramm) inherits from TmsInterfacedNonRefcounted and the interfaces. Let us also change one line in BeginShape method. We had the following:
  FCurrentAddedShape := CurrentClass.Make(aStart, Self.ShapeByPt);
And it became like this:
  FCurrentAddedShape := CurrentClass.Make(TmsMakeShapeContext.Create(aStart, Self));
In case of moving, EndTo method will look as follows:
procedure TmsMover.EndTo(const aCtx: TmsEndShapeContext);
begin
  if (f_Moving <> nil) then
    f_Moving.MoveTo(aCtx.rStartPoint);
  f_Moving := nil;
  aCtx.rShapesController.RemoveShape(Self);
  // Now I must delete MYSELF
end;
In the previous post I’ve told about hiding of “unique configurations” (filling color, line width, etc.) in virtual methods that each figure defines independently. For example:
function TmsTriangle.FillColor: TAlphaColor;
begin
 Result := TAlphaColorRec.Green;
end;
We pack all figures configurations in the context:
type
  TmsDrawOptionsContext = record 
  public
    rFillColor: TAlphaColor;
    rStrokeDash: TStrokeDash;
    rStrokeColor: TAlphaColor;
    rStrokeThickness: Single;
    constructor Create(const aCtx: TmsDrawContext);
  end;//TmsDrawOptionsContext
In TmsShape class we make a virtual procedure similarly to the previous example. In the future we’ll easily expand the number of unique figure configurations:
procedure TmsTriangle.TransformDrawOptionsContext(var theCtx: TmsDrawOptionsContext);
begin
  inherited;
  theCtx.rFillColor := TAlphaColorRec.Green;
  theCtx.rStrokeColor := TAlphaColorRec.Blue; 
end;
Using the context we put the logic (do we really draw a mover?) away from the method of drawing and hide it in the record constructor:
constructor TmsDrawOptionsContext.Create(const aCtx: TmsDrawContext);
begin
  rFillColor := TAlphaColorRec.Null;
  if aCtx.rMoving then
  begin
    rStrokeDash := TStrokeDash.sdDashDot;
    rStrokeColor := TAlphaColors.Darkmagenta;
    rStrokeThickness := 4;
  end // aCtx.rMoving
  else
  begin
    rStrokeDash := TStrokeDash.sdSolid;
    rStrokeColor := TAlphaColorRec.Black;
    rStrokeThickness := 1;
  end; // aCtx.rMoving
end;
After that our method of drawing becomes like this:
procedure TmsShape.DrawTo(const aCtx: TmsDrawContext);
var
  l_Ctx: TmsDrawOptionsContext;
begin
  l_Ctx := DrawOptionsContext(aCtx);
  aCtx.rCanvas.Fill.Color := l_Ctx.rFillColor;
  aCtx.rCanvas.Stroke.Dash := l_Ctx.rStrokeDash;
  aCtx.rCanvas.Stroke.Color := l_Ctx.rStrokeColor;
  aCtx.rCanvas.Stroke.Thickness := l_Ctx.rStrokeThickness;
  DoDrawTo(aCtx);
end;

function TmsShape.DrawOptionsContext(const aCtx: TmsDrawContext): TmsDrawOptionsContext;
begin
  Result := TmsDrawOptionsContext.Create(aCtx);
  // We get the configurations for each figure
  TransformDrawOptionsContext(Result);
end;
The only thing left to make our figures movable is to code for each figure ContainsPt method responsible for checking if a point gets to a figure. This is a routine trigonometry; all the formulas can be found on the Internet.


Let’s somewhat make over object registration in the container. Now each class registers itself. Let’s put registration into a separate module.
unit msOurShapes;

interface
uses
  msLine,
  msRectangle,
  msCircle,
  msRoundedRectangle,
  msUseCaseLikeEllipse,
  msTriangle,
  msDashDotLine,
  msDashLine,
  msDotLine,
  msLineWithArrow,
  msTriangleDirectionRight,
  msMover,
  msRegisteredShapes
  ; 
implementation

procedure RegisterOurShapes;
begin
  TmsRegisteredShapes.Instance.Register([
    TmsLine,
    TmsRectangle,
    TmsCircle,
    TmsRoundedRectangle,
    TmsUseCaseLikeEllipse,
    TmsTriangle,
    TmsDashDotLine,
    TmsDashLine,
    TmsDotLine,
    TmsLineWithArrow,
    TmsTriangleDirectionRight,
    TmsMover
   ]);
end;

initialization
 RegisterOurShapes;

end.
Let’s add registration method in the container:
procedure TmsRegisteredShapes.Register(const aShapes: array of RmsShape);
var
  l_Index: Integer;
begin
  for l_Index := Low(aShapes) to High(aShapes) do
    Self.Register(aShapes[l_Index]);
end;

procedure TmsRegisteredShapes.Register(const aValue: RmsShape);
begin
  Assert(f_Registered.IndexOf(aValue) < 0);
  f_Registered.Add(aValue);
end;


In this post we’ve tried to show how to make our life easier by using contexts, interfaces and the fabric method. More details on the fabric method you can find here and here. In the next post we will tell how we’ve “tied” DUnit to FireMonkey. We will also develop a number of tests, some of which will cause an error at once.

Source