Is there a way to write unit tests so that they can be compiled and run both with Delphi and Free Pascal?
There are different unit test frameworks for Delphi and Free Pascal, which causes duplicate work for developers who target both compilers (for example, library and framework developers).
So maybe there is a way, using either the DUnit or the FPCUnit framework and tweak the test case source code (or the framework itself) so that it also works with the other compiler.
So essentially the question is:
which framework (DUnit or FPCUnit) can be compiled with both compilers (Delphi and Free Pascal) with as little modifications as possible?
or
is there a third framework (Thanks to Arnaud for mentioning TSynTest) which works with Delphi and FPC?
See this very nice blog article - just fresh meat about FPCUnit testing.
In short, as far as I know, and if you compare to DUnit:
Most Check*() methods were renamed Assert*();
SetUp / TearDown methods are called per-function in both framework;
Some other thinks may vary.
So, I think it could be easy to let FPCUnit "mimics" DUnit, by creating a small wrapper class over FPCUnit implementation, to have the same exact methods than with DUnit. So you may be able to share code between the two targets, and even re-use existing DUnit tests. Such a wrapper class is IMHO much more convenient that using {$ifdef FPC} as other suggested here. Conditional compilation tends to make code hard to debug, verbose, redundant and should be used only if necessary.
Another potential solution could be to use other tests frameworks. Our small TSynTest classes are lighter, but I'm currently converting the framework to FPC. So the same exact code could be used with both compilers. It has some features (like optional logging with fine profiling, and full stack strace on failure) which I would miss from DUnit / FPCUnit. It does not have a GUI nor a Wizard but honestly, as I programmer I prefer plain text that I can include in my technical release documentation easily to testify that no regression occurred.
Default unit test framework for Free Pascal is FPCUnit, it has the same design as DUnit but different from it in minor details. You can write common unit tests for FPCUnit and DUnit by circumventing the differences by {$IFDEF FPC}. I just tested FPCUnit, it is a usable framework, and blogged about it.
I just whipped up a sample that works in both DUnit (delphi) and FPCUnit (Freepascal equivalent nearest to DUnit, that happens to ship already "in the box" in lazarus 1.0, which includes freepascal 2.6 ):
A trivial bit of IFDEF and you're there.
unit TestUnit1;
{$IFDEF FPC}
{$mode objfpc}{$H+}
{$ENDIF}
interface
uses
Classes,
{$ifdef FPC}
fpcunit, testutils, testregistry,
{$else}
TestFramework,
{$endif}
SysUtils;
type
TTestCase1= class(TTestCase)
published
procedure TestHookUp;
end;
implementation
procedure TTestCase1.TestHookUp;
begin
Self.Check(false,'value');
end;
initialization
RegisterTest(TTestCase1{$ifndef FPC}.Suite{$endif});
end.
Related
Okay, so this is not the best practice situation, but luckily I have an answer, so please bear with me.
I'm working on a rather large Delphi project with lots of external APIs used. Those APIs are plugged in different ways, including units wrapped into IFDEFs in the DPR itself, when they are platform-dependent. Having IFDEFs in DPR is a bad practice - sure, but it is handy to have all the units listed in the project tree to switch between them (rather than switch between projects).
So I was adding another alternative API implementation to the project and as usual - wrapped the existing solution units into the {$IFDEF SOME_API_DLL} clause and a new solution units into another {$IFDEF SOME_API_EXE}.
Anyways, I noticed soon after, that the IDE started to freeze semi-randomly after just 3-5min of coding, which is unbearable toll.. The IDE would hog one CPU (e.g. 13% on 8-core PC). I was able to isolate the cause to be invocation of "autocomplete", "code completion" (Ctrl+Space) and "jump to declaration (Ctrl+Click).
The main question is - what could cause the IDE to freeze on how to resolve this?
I'm using Delphi XE8, but switching to other versions didn't help. Unfortunately newer 10.3+ versions (with claimed rewritten Code Insight) could not be tested/used due to third-party components.
Thanks to the bug being well connected to the new API implementation, I was able to track down the cause. So it appears that IDE does not like DPR files with IFDEFs (old news), but the real bummer seems to be that IDE handles IFDEFs okay only if they wrap only one unit.
The hint to the solution was the Project Tree, which gets auto-updated with units in DPR, but it auto-included only the first units after the IFDEF. Apparently, thats what also throws off the track the auto-completion.
Bad DPR structure that causes IDE to freeze on code-completion:
{$IFDEF EXTAI_API_DLL}
KM_ExtAI_DLL in 'src\KM_ExtAI_DLL.pas',
KM_ExtAI_DLLs in 'src\KM_ExtAI_DLLs.pas',
KM_ExtAI_SharedTypes in 'src\KM_ExtAI_SharedTypes.pas',
KM_ExtAI_SharedInterfaces in 'src\KM_ExtAI_SharedInterfaces.pas',
KM_ExtAIActions in 'src\KM_ExtAIActions.pas',
KM_ExtAIMaster in 'src\KM_ExtAIMaster.pas',
KM_ExtAIStates in 'src\KM_ExtAIStates.pas',
KM_ExtAIUtils in 'src\KM_ExtAIUtils.pas',
{$ENDIF}
Good DPR structure that lets IDE work well:
{$IFDEF EXTAI_API_DLL}KM_ExtAI_DLL in 'src\KM_ExtAI_DLL.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAI_DLLs in 'src\KM_ExtAI_DLLs.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAI_SharedTypes in 'src\KM_ExtAI_SharedTypes.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAI_SharedInterfaces in 'src\KM_ExtAI_SharedInterfaces.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAIActions in 'src\KM_ExtAIActions.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAIMaster in 'src\KM_ExtAIMaster.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAIStates in 'src\KM_ExtAIStates.pas',{$ENDIF}
{$IFDEF EXTAI_API_DLL}KM_ExtAIUtils in 'src\KM_ExtAIUtils.pas',{$ENDIF}
Why can't I uses Generics.Collections or Generics.Default or even just Generics in Lazarus?
Uses
Generics.Collections;
Uses
System.Generics.Default;
Uses
System.Generics.Collections;
Uses
System.Generics.Default,
System.Generics.Collections;
Uses
SysUtils,
Generics;
Cannot find Generics.Collections used by uTest of the Project Inspector.
Cannot find Generics.Defaults used by uTest of the Project Inspector.
Cannot find Generics used by uTest of the Project Inspector.
Lazarus is the IDE for the open-source FreePascal compiler. Neither of them have ANYTHING to do with Delphi. FreePascal is a completely separate Pascal compiler than the one Delphi uses.
FreePascal has a Delphi compatibility mode, and does implement various Delphi units and classes, to help users port existing Delphi code to FreePascal.
But as far as Generics is concerned, FreePascal provides its own Generics syntax and implementation that is different from, and not compatible with, Delphi's Generics (actually, support for Delphi-style Generics was added in FreePascal 2.6, but "still may be not 100% compatible" with Delphi. Also see delphi language features which fpc does not have - Generics Syntax).
Read FreePascal's documentation for more details about its flavor of Generics:
http://wiki.freepascal.org/Generics
http://www.freepascal.org/docs-html/ref/refch8.html
The System.Generics.Default and System.Generics.Collections units are only available in Delphi, they do not exist in FreePascal. However, there is a 3rd party implementation of these units available for FreePascal.
Generics.Collections library (with Generics.Defaults module) has been added to FPC trunk as rtl-generics package in r34229. Latest version of precompiled FPC trunk (with Generics.Collections) for Win32 + Lazarus trunk available at http://newpascal.org . The repository of Generics.Collections ( https://github.com/dathox/generics.collections ) will be still used for maintenance (should be synced often with FPC trunk).
I recomend you to use the Generics.Collections package made by Maciej Izak.
I am trying to modify my Delphi 2010 code to compile in XE7 (and want to retain the ability to compile it in 2010). So in the unit that houses my mainform I added conditional directives. The following works fine in 2010
uses
{$IF CompilerVersion >= 24}System.Actions, {$ELSE}Actnlist,{$IFEND}
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs;
But XE7 automatically adds a System.Actions at the end to create a uses clause that now has System.Actions declared twice (see below), and gives an error message [dcc32 Error] MyForm.pas(10): E2004 Identifier redeclared: 'System.Actions'. Why is XE7 not accepting the unit from within the conditional directive ?
uses
{$IF CompilerVersion >= 24}System.Actions, {$ELSE}Actnlist,{$IFEND}
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
System.Actions; // <- automatically added
As Ken says, the interface uses clause will be modified by the IDE and the processes by which this is achieved are somewhat less than sophisticated (as you have discovered). The same problem affects the project uses clause. Unfortunately this is much harder to avoid in the case of Form/DataModule uses clauses.
You could use a Unit Alias (see David Heffernan's answer) but need to be aware that if you create an alias for a unit that the IDE wishes to add, then the IDE will still add a reference to the required unit since it does not recognise the alias as identifying that required unit. Aliasing to the System unit will avoid this since it is already (implicitly) used by every unit.
Another alternative is to remove all such conditionals from your uses list and instead create place-holder units as required so that the different compilers you wish to use on the project can each be satisfied by the single uses list combined from the list that each IDE insists is required (the IDE won't remove unused units from the uses list, something that is often a complaint but in this case actually helps solve your problem).
In this case, in your Delphi 2010 project create an empty Actions unit:
unit Actions;
interface
implementation
end.
You will of course need to ensure that this unit is not in the project path for your XE7 version of the project.
One way to achieve that would be ensure that the empty Actions.pas unit is not explicitly listed in the DPR uses list, but is placed in a subfolder of your project source (e.g. 'placeholders'). You can then add this subfolder to the project search path for the Delphi 2010 version but not the XE7 version:
\Project Folder
project2010.dpr
project2010.dproj
projectXE7.dpr
projectXE7.dproj
\placeholders
Actions.pas
If you find that you need placeholders for each of the different versions then you will need separate placeholder folders. You might create further version specific subfolders, for example:
\placeholders
\2010
Actions.pas
\XE7
D2010UnitNotPresentInXE7.pas
This sort of structure might be advisable simply from the point of view of creating an auto/self documenting organisation.
Note that this is only required for dealing with unit references in the uses clause of the interface section of Forms (or Frames etc). In non-visual units or in the implementation section, the IDE does not interfere so conditional compilation directives should present no issues there.
The easiest way to fix this is to add a unit alias to your Delphi 2010 project. You'll need to use different .dproj files for your different Delphi versions, but you need to do that anyway.
In the unit aliases settings for the Delphi 2010 project add this:
Actions=System
I'm using System as the alias target because the System unit is automatically included in every Delphi unit and so aliased inclusions are benign. It's the simplest way that I can think of to make the compiler effectively ignore an entry in a uses clause.
Then you can declare your uses clause like this:
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
Actions, Actnlist;
This will compile fine in Delphi 2010, because the alias processing will map Actions onto System. In XE7 you are also fine because there is no alias, and the IDE is satisfied by the presence of the Actions unit and so feels no compulsion to modify the uses clause.
Would there be something wrong with
{$IF CompilerVersion < 24}Actnlist,{$IFEND}
or is this an academic argument?
Addendum...
Then add a dummy System.Actions.dcu containing nothing into your 2010 compile-path.
I'd theorise that the IDE would then insist on inserting uses ... System.Actions, 2010 has what it wants, XE7 has what it wants.
But I don't have XE7 so I can't test it out.
we had the same issue...
The easiest way is to do it that way:
{$IF CompilerVersion < 24}{$ELSE}System.Actions,{$IFEND}
{$IF CompilerVersion >= 24}{$ELSE}Actnlist,{$IFEND}
If you open the file in old IDE's, than you may see an error, which says "unit X" not found, but it will compile fine and no automatic adding is performed.
It looks not so nice, but it works quite well...
Kind regards,
Bernd
Has anyone come across a framework or library for Delphi to simplify the generation of x86 code? I am not looking for an assembler, but rather a framework that abstracts the code generation process above the low level bits and bytes. Ideally I would like to build on top of an existing library or framework rather than hardcode the logic on a case by case basis.
The initial usage will be to generate small code stubs at runtime similar to the way Delphi dispatches SOAP requests. If I cannot find something I will likely roll my own, but I would hate to reinvent the wheel. Something in "C" might me interesting provided the license will permit translation and use in commercial and open source projects.
Update:
Here is some more context: What I am working toward is runtime implementation of interfaces and/or classes as part of a persistence framework. Something sort of like Java annotation driven persistence (JPA/EJB3) except with a distinctly Delphi flavor. The invocation target is a modular/extensible framework which will implement a generalized persistence model. I need to dispatch and hook method calls based on RTTI and an annotation/attribute model (something similar to InstantObjects metadata) in a very dynamic and fluid manner.
Thanks,
David
The more I have thought about your question. I am not sure if all you trying to just do Dynamic Method Invocation. Even though your asking about generating x86 code.
There are several techiniques that do this.
If you know the signature of the method in question you can do it easily by using a
TMethod and setting the method address and data.
procedure TForm8.Button1Click(Sender: TObject);
begin
Showmessage('Hello1');
end;
procedure TForm8.Button2Click(Sender: TObject);
var
M : TMethod;
begin
M.Code := MethodAddress('Button1Click');
M.Data := Self;
TNotifyEvent(M)(self);
end;
If you don't know the method signature you can write the class with {$METHODINFO ON}
Then use the functionality in ObjAuto.pas to invoke the method.
I have an example in my RTTI Presentation code from DelphiLive on how to do that.
According to features of PaxCompiler, you can create stand alone executable files.
Very spectulative answer:
Something like LLVM? I am not sure if it can be used from delphi or not, but you should be able to create dll's wth it.
Logically you would simply generate delphi code, compile to a DLL/BPL by cmdline compiler and then dyn load that one?
Unfortunately Delphi Explorer doesn't come with the cmdline compiler though. And your main binary would also have to be in Delphi Explorer (or at least in D2006 if that is binary compatible enough)
Any mix of Delphi versions (or Free Pascal) will probably not work on the package or HLL level, only at basic procedural DLL level.
I just found an interesting framework that does much of what I was looking for when I originally posted the question. A little late for my purposes, but thought someone else might find this useful:
DAsmJit a Delphi port of the asmjit project
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 8 years ago.
Improve this question
I'm wondering how the few Delphi users here are doing unit testing, if any? Is there anything that integrates with the IDE that you've found works well? If not, what tools are you using and do you have or know of example mini-projects that demonstrate how it all works?
Update:
I forgot to mention that I'm using BDS 2006 Pro, though I occasionally drop into Delphi 7, and of course others may be using other versions.
DUnit is a xUnit type of unit testing framework to be used with win32 Delphi. Since Delphi 2005 DUnit is integrated to a certan point into the IDE. Other DUnit integration tools for the Delphi IDE can be found here. DUnit comes with documentation with examples.
There are some add-ons for DUnit, maybe this is worth a new entry on SO. Two which I can put on the list now are
FastMM4 integration: Unit tests will automatically detect memory leaks (and other things), works with DUnit 9.3 and newer
OpenCTF is a 'component test
framework' based on DUnit, it
creates the tests dynamically for
all components in the project's
forms, frames and datamodules, and
tests them using customized rules (open source)
You could take a look at the unit testing classes available in our SynCommons open source unit. It's used in our Open-Source framework for all regression tests. It's perhaps not the best, but it's worth taking a look at it.
See http://blog.synopse.info/post/2010/07/23/Unit-Testing-light-in-Delphi
In order to implement an unit test, you just declare a new test case by creating a class like this:
type
TTestNumbersAdding = class(TSynTestCase)
published
procedure TestIntegerAdd;
procedure TestDoubleAdd;
end;
procedure TTestNumbersAdding.TestDoubleAdd;
var A,B: double;
i: integer;
begin
for i := 1 to 1000 do
begin
A := Random;
B := Random;
CheckSame(A+B,Adding(A,B));
end;
end;
Then you create a test suit, and run it.
In the up-to-come 1.13 version, there is also a new logging mechanism with stack trace of any raised exception and such, just like MadExcept, using .map file content as source.
It's now used by the unit testing classes, so that any failure will create an entry in the log with the source line, and stack trace:
C:\Dev\lib\SQLite3\exe\TestSQL3.exe 0.0.0.0 (2011-04-13)
Host=Laptop User=MyName CPU=2*0-15-1027 OS=2.3=5.1.2600 Wow64=0 Freq=3579545
TSynLogTest 1.13 2011-04-13 05:40:25
20110413 05402559 fail TTestLowLevelCommon(00B31D70) Low level common: TDynArray "" stack trace 0002FE0B SynCommons.TDynArray.Init (15148) 00036736 SynCommons.Test64K (18206) 0003682F SynCommons.TTestLowLevelCommon._TDynArray (18214) 000E9C94 TestSQL3 (163)
The difference between a test suit without logging and a test suit with logging is only this:
procedure TSynTestsLogged.Failed(const msg: string; aTest: TSynTestCase);
begin
inherited;
with TestCase[fCurrentMethod] do
fLogFile.Log(sllFail,'%: % "%"',
[Ident,TestName[fCurrentMethodIndex],msg],aTest);
end;
The logging mechanism can do much than just log the testing: you can log recursive calls of methods, select the information you want to appear in the logs, profile the application from the customer side, writing published properties, TList or TCollection content as JSON into the log content, and so on...
The first time the .map file is read, a .mab file is created, and will contain all symbol information needed. You can send the .mab file with the .exe to your client, or even embed its content to the .exe. This .mab file is optimized: a .map of 927,984 bytes compresses into a 71,943 .mab file.
So this unit could be recognized as the natural child of DUnit and MadExcept wedding, in pure OpenSource. :)
Additional information is available on our forum. Feel free to ask. Feedback and feature requests are welcome! Works from Delphi 6 up to XE.
DUnit2 is available from http://members.optusnet.com.au/~mcnabp/
DUnit2 is modified more regularly than the original dunit. It also works on Delphi 2009.
Try: http://sourceforge.net/projects/dunit2/ - it moved as the original author Peter McNab passed away several years ago. Still some activity on the dunit mailing list.
There's a new unit testing framework for modern Delphi versions in development: https://github.com/VSoftTechnologies/DUnitX
Usually I create a Unit test project (File->New->Other->Unit Test->Test Project). It contains the stuff I need so it's been good enough so far.
I use delphi 2007 so I don't really know if this is available in 2006.
We do unit testing of all logic code using DUnit and use the code coverage profiler included in AQTime to check that all paths through the code are executed by the tests.
We have two approaches, first we have Dunit tests that are run buy the developers - these make sure that the code that has just been changed still works as before. The other approach is to use CruiseControl.NET to build executables and then run the dunit tests everytime a change is made, to ensure that there are no unintended consequences of the change.
Much of our codebase has no tests, so the automatic tests are a case of continuous development in order to ensure our applications work as we think they should.
We tried to use DUnit with Delphi 5, but it didn't work well. Specially if you are implementing COM interfaces, we found many dependencies to setup all the test infrastructure. I don't know if the test support has improved in newer versions.