Saturday, 11 October 2014

Unfolding Mysteries: Chicken or Egg?

Recently I faced an interesting problem while refactoring legacy code. To understand the problem better, lets take a look at the following class diagram.











The structure is quite simple. There is a class for each object and a corresponding container class. The container class has an attribute named "item" which is responsible for returning object of the class it contains. e.g. The "item" attribute of TContainerA returns reference to TObjectA and so on.

The task in hand is to simplify the implementation using generics. The solution is simple. Here's how it looks like.

  TBaseContainer<T: TBaseObject> = class
  private
    function GetItem(AIndex: Integer): T;
  public
    property Items[AIndex: Integer]: T read GetItem;
  end;

As soon as I made above changes, it broke the derived container classes. The compiler started complaining about the TBaseContainer class.

[dcc32 Error] ContainerA.pas(29): E2003 Undeclared identifier: 'TBaseContainer'
[dcc32 Error] ContainerA.pas(29): E2021 Class type required

Again, the fix is simple. We just need to pass correct parameter to the TBaseContainer class as shown in below snippet. Also, the container specific implementation of "item" attribute is removed as it will be taken care by the TBaseContainer class. This works fantastic as long as our classes are really as simple as shown here.

  TContainerA = class(TBaseContainer<TObjectA>)
  //...
  end;

Do you smell Chicken or the Egg?

Not really? Consider a situation where there are 20+ container classes. And those are bit complex ones. For instance, each container class has its own enumerator that also needs to be parameterized (using generics) as we did for the container class. Of course, there are big chunks of code changes and the whole task affects large number of files. Also, it is desirable to keep the changes atomic due to the complexity and amount of work it requires. But, with above approach, that doesn't seems to be an option. The real problem is - all changes must be done in a one go or not at all. We cannot leave one or the other class behind.

Can we leave the TBaseContainer class as-is and derive a new intermediate class from the TBaseContainer class which contains the generics stuff.

  TCustomContainer<T: TBaseObject> = class(TBaseContainer)
  private
    function GetItem(AIndex: Integer): T;
  public
    property Items[AIndex: Integer]: T read GetItem;
  end;

Compile the code and no complaints this time since no one is using the TCustomContainer class we just created. Also the ambition of keeping the changes atomic is possible. Each concrete container can start using the TCustomContainer class at its own pace.

  TContainerA = class(TCustomContainer<TObjectA>)
  //uses new TCustomContainer class
  end;

  TContainerB = class(TBaseContainer)
  ...  //legacy code
  end;

The above solution works in almost all Object Oriented Languages supporting generics or parameterized types. Where's the mystery by the way?

Let's rename the TCustomContainer to TBaseContainer and see what happens. Essentially, what we are doing is creating a new class with the same name as its base class. The expectation is compile time error since both classes are declared in the same unit.

[dcc32 Error] BaseContainer.pas(24): E2004 Identifier redeclared: 'TBaseContainer'

Delphi surprises this time. It reports no error even if the derived class name is same as its base class. Within a unit, Delphi does not allow declaration of an identifiers which is already declared with the same name. So what's going on?

Let's hold on the curiosity for a moment, and and try to use the generic version of TBaseContainer with our concrete containers.

  TContainerA = class(TBaseContainer<TObjectA>)
  //uses generic version of TBaseContainer
  end;

  TContainerB = class(TBaseContainer)
  ...  //legacy code
  end;

Again, no complaints! However, without explanation, it tells what it is doing. Both TContainerA and TContainerB classes are derived from the TBaseContainer class. The former uses the generic version whereas the later uses non-generic version.

What is happening inside is - when a class is parameterized with generics, Delphi gives it an internal name which differentiates it from the classes declared with different number of parameters or no parameters. It means the TBaseContainer and TBaseContainer<T> are having different internal names and Delphi complier treats them unique.

Given that, following is all legal within same unit.

  TTest = class  //internal name => 'TTest'
  end;

  TTest<T> = class  //internal name => 'TTest`1'
  end;

  TTest<T1, T2> = class  //internal name => 'TTest`2'
  end;

  TTest1 = class  //internal name => 'TTest1'
  end;

  TTest1<T> = class(TTest1)  //internal name => 'TTest1`1'
  end;

  TTest2<T> = class  //internal name => 'TTest2`1'
  end;

  TTest2 = class(TTest2<TObject>)  //internal name => 'TTest2'
  end;

Can't we can say TTest, TTest1 and TTest2 classes are overloaded?

Monday, 14 July 2014

Unfolding Mysteries: Part 1

All Windows users must have seen the message dialog screen many number of times. It is used to show information, a warning, an error message or ask for confirmation to certain actions which may result in loss of data or irreversible state such as formatting the disc drive etc.









As a Delphi developer we know that there is a MessageDlg function defined in the Dialogs.pas unit that displays a message dialog in the center of the screen with the message text we provide. We also know that it returns the value of the button user has selected. 

Consider a scenario where user is making changes to an employee record and they hit the Close button by mistake. Since the changes may not have been saved in the database, we don't want the user to lose their changes. A typical solution is to ask the user if the record is found dirty. 

if Employee.Modified then
begin
  if MessageDlg('Employee details has been changed. Do you want to abandon changes?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    AbandonChanges //Throw away changes 
  else
    SaveChanges; //Save changes
end;

The approach looks acceptable and it works as expected. So what's the mystery? I encourage you to give it a try.

Run it again, make changes to an employee record and hit the Close button. The confirmation dialog appears. Now click the small "x" button located on the top right corner of the confirmation dialog. If you like the keyboard, press the Esc key. 

Surprised with the results? It goes against our intention and updates the employee record. Imagine the impact if the transaction is much more complex than above and has cascade effects. What if it is irreversible like formatting a disc or deleting a file permanently? No user will expect a system to function against them when they hit the "x" button or the Esc key. 

Who's the culprit? Yes, it is the MessageDlg who plays the odd. Unexpectedly, it returns mrCancel (Huh?) even if it is not a valid choice we've given. It is the third choice user can always make. I have seen plenty of examples ignoring this fact. 

A better solution is to rewrite the message text and change our focus. 

if Employee.Modified then
begin
  if MessageDlg('Employee details has been changed. Do you want to save changes?', mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    SaveChanges; //Save changes 
  else
    AbandonChanges //Throw away changes
end;

This approach solves the problem with an assumption. The apparently hidden choice "Cancel" is treated as "No". Moreover, it also touches the UX element and changes the focus towards "Saving" rather than "Abandoning" the transaction. However, it still leaves us with a problem. If user wishes to go back to the state where they were before clicking the Close button, it is impossible.

To fulfill the user's wish, system should entertain the Cancel request more diligently. Also, from the UX perspective, it is important to show the Cancel button as valid choice. This makes user aware of their choices.

Sunday, 29 June 2014

DUnit: I know you know me. Let's get to know each other better.

The DUnit Integration with TeamCity is great. This post taking it further to get most out of TeamCity. 

We know that when a build fails, we can always visit the build log and will get to know what went wrong. This is sufficient for a developer or a build engineer to investigate and fix the issue.

However as a developer, I would be more happy if it can tell me exactly what tests are failed. Logs are OK but those are verbose; and personally, I would reserve it to be the last option. So what's the other option?

TeamCity is our friend. What about letting it know more about our test results? Let's see










This is exactly what we need. Moreover, now we have a new Tests tab showing nice test report with filtering, sorting and export options. 





Of course, there is more than what's shown here; and I would leave the reader to discover those fascinating offerings of TeamCity. For now lets focus on how to get there. 

TeamCity provides various options to enhance the experience with the build system. Such as service messages, plugins, build features etc. Among these, the easiest one is XML report processing which takes almost same amount of time as it takes to read this post.

Before we enable this feature, we need to tweak the DUnit a little bit to produce an XML report TeamCity understands. The solution is VSoft's XMLRunner for DUnit. It produces an XML report in NUnit format. The source code is available under Apache license with example. 

All we need to do now is to enable the build feature. Select the project > Edit Configuration Settings > Select Build Features option.














Click the Add build feature button and choose XML report processing. Report type being NUnit and set monitoring rules. 



Click Save and you're done! We have a full fledged DUnit integration.

Wednesday, 25 June 2014

DUnit Integration with TeamCity

TeamCity supports number of testing frameworks out of the box. What about DUnit..?
Well, it is not supported out-of-the-box. When I searched around, one solution I found is to configure the test project to use the console runner and TeamCity would be happy. My CI server is now running like a charm. 





But wait, TeamCity should not have reported Success as there are failed tests I wrote intentionally. Let's take a look at the Build Log.




















Seems like TeamCity does not understand the language DUnit speaks. So we need to tell TeamCity to report the build status as failure when one of the test fails. 








Finally, it is red. Cool!


What's next? More fun with TeamCity and DUnit?