The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,861 other subscribers

Archive for the ‘Event’ Category

A DUnit Folder Iterator Extension – The Art of Delphi Programming

Posted by jpluimers on 2021/03/17

Reminder to self: experiment with [WayBack] A DUnit Folder Iterator Extension – The Art of Delphi Programming.

This could be extended to virtual folders, allowing many integration tests to be run depending on some configuration hierarchy defined in a folder structure.

–jeroen

Posted in Conference Topics, Conferences, Delphi, Development, DUnit, Event, Software Development, Unit Testing | Leave a Comment »

Unittesting: why would you want OAPT, and how you might end up with a combined OAPT and OOPT.

Posted by jpluimers on 2021/03/09

In unit testing, a lot has been written on OAPT, most commonly known as One Assert Per Test.

Given a basic unit test structured the way [WayBack] Martin Fowler: GivenWhenThen posted in 2013 (basically identical, though easier to remember for most people than the the 3A’s pattern Arrange-Act-Assert from the 2005 article [WayBack] thinking-out-loud: Principles for Test-Driven Development):

Feature: User trades stocks
  Scenario: User requests a sell before close of trading
    Given I have 100 shares of MSFT stock
       And I have 150 shares of APPL stock
       And the time is before close of trading

    When I ask to sell 20 shares of MSFT stock
     
     Then I should have 80 shares of MSFT stock
      And I should have 150 shares of APPL stock
      And a sell order for 20 shares of MSFT stock should have been executed

Neither GivenWhenThen, nor ArrangeActAssert indicate there should be One Assert Per Test, however, having multiple asserts in the Then phase, might make it harder to figure out which asserts of a test passed, and which one(s) failed.

Other tests than unit tests

Note we are talking about unit tests here. There is a lot of confusion on what these are, and I have regular discussions with teams about calling tests unit tests, when in fact they are not.

More on this in the last section of this blog post, which quotes from these pages:

Asserts outside the Then phase

When putting asserts outside the Then phase, or even having multiple When-Then phases in one test, then you get into a whole different area of testing.

There are various ways to handle those situations, covered very well in [WayBack] Multiple Asserts Are OK – Industrial Logic: Some people suggest we should restrict ourselves to a single assertion per test. Are multiple asserts in a test ever OK?

  1. Run-on test
  2. Missing method or object
  3. De-duplication of similar tests
  4. Confirming setup
  5. Probe with multiple assertions

Here, “Run-on test” and “Confirming setup” definitely are bad, “Missing method or object” indicates some refactoring needs to take place, “De-duplication of tests” is OK if you factor out the set of assertions into one that provides a meaningful failure message, and “Probe with multiple assertions” can be OK, but should be closely inspected.

OAPT effects

So when applying OAPT, it is usually centered around phrasing the Then into one assert, or splitting the various Then parts that need to be true, in separate assertions that reside in separate methods.

Combining things in one assert, often is problematic, because upon test failure, most frameworks will not provide enough details to track back which part of the combined assert was violated.

Hence when following OAPT, tests are often split by introducing an extra method that holds the Given and When phases:

Method: Given portfolio of 100 MSFT and 150 AAPL, When selling 20 MSFT
   Given I have 100 shares of MSFT stock 
      And I have 150 shares of APPL stock
      And the time is before close of trading
   When I ask to sell 20 shares of MSFT stock 

Feature: User trades stocks
  Scenario: User requests a sell before close of trading, non-sold stock is unchanged
    Call method Given portfolio of 100 MSFT and 150 AAPL, When selling 20 MSFT
    Then I should have 150 shares of APPL stock

Feature: User trades stocks
   Scenario: User requests a sell before close of trading, sold stock is gone from portfolio 
      Call method Given portfolio of 100 MSFT and 150 AAPL, When selling 20 MSFT
      Then I should have 80 shares of MSFT stock

Feature: User trades stocks
   Scenario: User requests a sell before close of trading, sell order has been executed
   Call method Given portfolio of 100 MSFT and 150 AAPL, When selling 20 MSFT
   Then a sell order for 20 shares of MSFT stock should have been executed

Other test frameworks can make the distinction by for instance:

  • automatically doing the split (and showing which actual clauses of the Then part fail),
  • have a short-hand to tag separate Then clauses to a single scenario,
  • allow for one assert to not skip the rest of the asserts (in essence allowing to report multiple failures per test)
  • allow for deep comparisons (so instead of comparing many leaf attributes of two structures, you can compare the full structure in one go)

There is a lot to read about OAPT, some of which specify frameworks that make it easier to have one test method, having parameterised asserts, so the test runner can show the results of each assert in a separate result.

Some reading material:

Multiple asserts ain’t bad

There is an interesting study about having multiple asserts per test [WayBack] What Do the Asserts in a Unit Test Tell Us About Code Quality? (CSMR2013) which presents the results from the paper [WayBack] What Do The Asserts in a Unit Test Tell Us About Code Quality? A Study on Open Source and Industrial Projects

The conclusion is a two part one, of which both might surprise you:

  1. the number of asserts does not tell us anything about the production code quality;
  2. however, the number of asserted objects does.

What this basically means is that you should have one Unit-Under-Test.

This is why I would likely split the very first test that has 3 clauses under the Then part, into two tests, each centered around one Unit-Under-Test:

  • The portfolio expectations
  • The sell order expectation

OAPT, but still having multiple asserts in one method

One of the drawbacks of having multiple methods for one conceptual part pf a method, each having a different assert is the proliferation of methods.

What if you could have all these assertions in one method, but still run them as separate tests?

This is where a library lika

Repeat: we are talking unit-tests

If you are doing other tests than unit tests, then the above are still good guidelines, but might not all apply.

To get a better understanding on unit tests, read [WayBack] Unit Testing Lessons in Ruby, Java and .NET – The Art Of Unit Testing – Definition of a Unit Test

A good unit test is:

  • Able to be fully automated
  • Has full control over all the pieces running (Use mocks or stubs to achieve this isolation when needed)
  • Can be run in any order  if part of many other tests
  • Runs in memory (no DB or File access, for example)
  • Consistently returns the same result (You always run the same test, so no random numbers, for example. save those for integration or range tests)
  • Runs fast
  • Tests a single logical concept in the system
  • Readable
  • Maintainable
  • Trustworthy (when you see its result, you don’t need to debug the code just to be sure)

I consider any test that doesn’t live up to all these as an integration test and put it in its own “integration tests” project.

and [WayBack] Unit Testing Lessons in Ruby, Java and .NET – The Art Of Unit Testing – Test Review Guidelines

How to do test reviews

Related: Definition of a unit test

Summary

Test Reviews (like code reviews, but on tests) can offer you the best process for teaching and improving the quality of your code and your unit tests while implementing unit testing into your organization. Review EVERY piece of unit testing code, and use the following points as a simple check list of things to watch out for. (you’ll find this is mainly useful when working in statically types languages such as Java or C#).

You can find these guidelines and more on the last page of the book. (but this page contains additions not found in the book)

Readability

  • Make sure setup and teardown methods are not abused. It’s better to use factory methods for readability  (p. 188, 214)
  • Make sure the test tests one thing only (p. 179)
  • Check for good and consistent naming conventions (p. 210-211)
  • Make sure that only meaningful assert messages are used, or none at all (meaningful test names are better) (p. 212)
  • Make sure asserts are separated from actions (different lines). (p. 214)
  • Make sure tests don’t use magic strings and values as inputs. use the simplest inputs possible to prove your point.
  • Make sure there is consistency in location of tests. make it easy to find related tests for a method, or a class, or a project.

Maintainability

  • Make sure tests are isolated from each other and repeatable (p. 191)
  • Make sure that testing private or protected methods is not the norm (public is always better) (P. 182)
  • Make sure tests are not over-specified (p. 205)
  • Make sure that state-based testing is preferred over using interaction testing (p. 83)
  • Make sure strict mocks are used as little as possible (leads to over specification and fragile tests) (p. 106)
  • Make sure there is no more than one mock per test (p. 94)
  • Make sure tests do not mix mocks and regular asserts in the same test (testing multiple things)
  • Make sure that tests ‘verify’ mock calls only on the single mock object in the test, and not on all the fake objects in the tests (the rest are stubs, this leads to over specification and fragile tests) (p.123)
  • Make sure the test verifies only on a single call to a mock object. Verifying multiple calls on a mock object is either over specification or testing multiple things.
  • Make sure that only in very rare cases a mock is also used as a stub to return a value in the same test (p. 84)

Trust

  • Make sure the test does not contain logic or dynamic values (p. 178)
  • Check coverage by playing with values (booleans or consts) (p. 180)
  • Make sure unit tests are separated from integration tests (p. 180)
  • Make sure tests don’t use things that keep changing in a unit test (like DateTime.Now ). Use fixed values.
  • Make sure tests don’t assert with expected values that are created dynamically – you might be repeating production code.

–jeroen

Posted in Agile, Conference Topics, Conferences, Development, Event, Software Development, Unit Testing | Leave a Comment »

Short Delphi tip: ensuring RTTI for classes is included

Posted by jpluimers on 2021/03/04

When using RTTI in Delphi, you really want the RTTI to be available.

The compiler includes RTTI for classes, as soon as it found that a class is touched by code that will be executed.

So in order to include RTTI for classes into the executable, you have to ensure you touch the class.

Basically there are two tricks for that.

  1. A small one step process which incurs a tiny bit of string overhead:
    class function TObject.ClassName: string;
    begin
      Result := UTF8ToString(_PShortStr(PPointer(PByte(Self) + vmtClassName)^)^);
    end;
    • Touch the [WayBack] ClassName class function for each class, for instance in an initialization section or registration method, like this:
      TMyClass.ClassName;
      TMyOtherClass.ClassName;
  2. A small two step process
    1. Create a method like this: procedure EnsureRttiIsAvailable(const Classes: array of TClass); begin end;
    2. Pass the classes to it like this:
      EnsureRttiIsAvailable([TMyClass, TMyOtherClass]);

I like the second solution more, as it clearly states the intent.

The first trick is calling a function without using the result. This is a Pascal construct that looks odd, but is perfectly valid to use: basically you discard the result.

–jeroen

Posted in Conference Topics, Conferences, Delphi, Development, Event, Software Development, Undocumented Delphi | Leave a Comment »

Delphi: quickly failing in use-after free scenarios

Posted by jpluimers on 2021/03/03

Two tricks that can help in use-after-free scenarios.

Call ScanMemoryPoolForCorruptions often

One of the scenarios of use after free, is that memory blocks get corrupted.

FastMM4 normall checks this at process end using the CheckBlocksOnShutdown method (in FastMM4.pas when writing this private at line 11156), but you can also do this process manually using the ScanMemoryPoolForCorruptions method (also in FastMM4.pas, but public at line L1356).

You can automate this process by setting the FullDebugModeScanMemoryPoolBeforeEveryOperation flag to True while in FullDebugMode as you see in the quoted code blocks below.

Note that calling ScanMemoryPoolForCorruptions between allocations might reveal wild pointer dereferences between allocations.

  - Added a global variable "FullDebugModeScanMemoryPoolBeforeEveryOperation".
    When this variable is set to true and FullDebugMode is enabled, then the
    entire memory pool is checked for consistency before every GetMem, FreeMem
    and ReallocMem operation. An "Out of Memory" error is raised if a
    corruption is found (and this variable is set to false to prevent recursive
    errors). This obviously incurs a massive performance hit, so enable it only
    when hunting for elusive memory corruption bugs. (Thanks to Marcus Mönnig.)

  {If this variable is set to true and FullDebugMode is enabled, then the
   entire memory pool is checked for consistency before every memory
   operation. Note that this incurs a massive performance hit on top of
   the already significant FullDebugMode overhead, so enable this option
   only when absolutely necessary.}
  FullDebugModeScanMemoryPoolBeforeEveryOperation: Boolean = False;

Call any virtual method on an instance reference

A quick way to test use-after free scenarios is to call a virtual method on an instance.

Virtual methods mean that the Virtual Method Table needs to be used as a starting point, so any nil pointer will get dereferenced.

Two simple methods that you can call, which have no side effects, except for referencing memory, and are virtual on [WayBack] TObject are [WayBack] GetHashCode and [WayBack] ToString. Both methods got added in Delphi 2009, and now support 64-bit and 32-bit compilers are below.

If you use use these in addition to FastMM4 clearing memory, and FastMM4 redirecting virtual methods of freed objects, you have a good chance of catching use-after free.

Without FastMM, they are also of good help, especially when the freed memory has since then been overwritten by new usage. FastMM4 is a lot more strict about this, so definitely recommended.

Calling these two methods help you to quickly fail with an EAccessViolation [WayBack] in use-after-free scenarios.

More on the virtual method table is for instance in [WayBack] Hallvard’s Blog: Method calls compiler implementation.

Read the rest of this entry »

Posted in Conference Topics, Conferences, Delphi, Development, Event, FastMM, Software Development, Undocumented Delphi | Leave a Comment »

Delphi compile time assertions

Posted by jpluimers on 2021/02/24

My post on Delphi intrinsic functions that evaluate to consts as a step up to Delphi compile time assertions.

This is a corner case of Delphi language use, which can come in very handy when your code is changed in the future, and you want to be prepared to ensure that some changes do not violate some predefined boundaries.

Hopefully a future post will elaborate a bit more on actual usage, but for now, lets first show some examples, then some other languages that have a richer set of compile time assertions.

My original goal was to see if I could come up with a mechanism that allowed for better validation of generic types because Delphi generic constraints – still – are quite limited: Delphi Constraints in Generics – RAD Studio XE documentation wiki, so limiting or verifying the aspects of the concrete type often cannot be done by constraints.

C# had a similar limitation for constraining to enum, which finally got added some 13 years after adding generics, in 2018: [WayBack] Unmanaged, delegate and enum type constraints – C# 7.3 in Rider and ReSharper – .NET Tools Blog.NET Tools Blog.

Let’s start simple:

const
  // forbidden const values to check compile time assert:
  A = 0;
  B = 1;
  C = -1;
  // The below expressions all each generate a "[dcc32 Error] E2098 Division by zero" (so multiple errors in one compile)
  // Asserting at compile time using boolean expressions:
  BooleanAssertAIsNotZero =  1 div Ord(A <> 0);
  BooleanAssertBIsNotOne = 1 div Ord(B <> 1);
  // Asserting at compile time using numeric expressions:
  AssertAIsNotZero = 1 div A;
  AssertBIsNotOne =  1 div (B - 1);
  AssertBIsNotAbsOne =  1 div (Abs(B) - 1);
  AssertCIsNotAbsOne =  1 div (Abs(C) - 1);

This is all centered around generating a compile time error "[dcc32 Error] E2098 Division by zero", of which multiple can occur in one compile go (after compilation, the cursor focus will be at the first error) and which has been in the language for a very long time [WayBack] E2098: Division by zero.

The conversion of Boolean to Integer is done using Ord, a very powerful compile time intrinsic that evaluates to a constant.

You can use this for other intrinsics as well, for example:

type
  TDigits = 0..9;

const
  DigitsAreInteger = GetTypeKind(TDigits) = tkInteger;
  DigitsAreIntegerIsTrue = 1 div Ord(DigitsAreInteger);
  DigitsAreEnumeration = GetTypeKind(TDigits) = tkEnumeration; // compiles fine
  DigitsAreEnumerationIsTrue = 1 div Ord(DigitsAreEnumeration); // [dcc32 Error] E2098 Division by zero

The above learns that integer subranges are not enumerations, but stay integers.

You can now extend this to check longer boolean expressions, for instance to check if a record size matches certain criteria. For this we create records having zero to four bytes in size (yes, you can have empty record in Delphi, it in fact the only data structure that can be zero bytes in length, though the documentation [WayBack] Structured Types: record types does not state this is in fact possible ), then validate the sizes:

type
  TRecord0 = record
  end;

  TRecord1 = packed record
    FByte0: Byte;
  end;

  TRecord2 = packed record
    FByte0: Byte;
    FByte1: Byte;
  end;

  TRecord3 = packed record
    FByte0: Byte;
    FByte1: Byte;
    FByte2: Byte;
  end;

  TRecord4 = packed record
    FByte0: Byte;
    FByte1: Byte;
    FByte2: Byte;
    FByte3: Byte;
  end;

const
  AssertTRecord0SizeOf0 = 1 div Ord(SizeOf(TRecord0) = 0); // When expression is false: [dcc32 Error] E2098 Division by zero
  AssertTRecord0SizeOf1 = 1 div Ord(SizeOf(TRecord1) = 1); // When expression is false: [dcc32 Error] E2098 Division by zero
  AssertTRecord0SizeOf2 = 1 div Ord(SizeOf(TRecord2) = 2); // When expression is false: [dcc32 Error] E2098 Division by zero
  AssertTRecord0SizeOf3 = 1 div Ord(SizeOf(TRecord3) = 3); // When expression is false: [dcc32 Error] E2098 Division by zero
  AssertTRecord0SizeOf4 = 1 div Ord(SizeOf(TRecord4) = 4); // When expression is false: [dcc32 Error] E2098 Division by zero
  AssertTRecord0SizeOfMultipleOf4 = 1 div Ord(SizeOf(TRecord0) mod 4 = 0); // When expression is false: [dcc32 Error] E2098 Division by zero
  AssertTRecord4SizeOfMultipleOf4 = 1 div Ord(SizeOf(TRecord4) mod 4 = 0); // When expression is false: [dcc32 Error] E2098 Division by zero
  AssertTRecord0SizeOfMultipleOf4AndGreaterThan0 = 1 div Ord((SizeOf(TRecord0) mod 4 = 0) and (SizeOf(TRecord0) > 0)); // When expression is false: [dcc32 Error] E2098 Division by zero
  AssertTRecord4SizeOfMultipleOf4AndGreaterThan0 = 1 div Ord((SizeOf(TRecord4) mod 4 = 0) and (SizeOf(TRecord4) > 0)); // When expression is false: [dcc32 Error] E2098 Division by zero

That’s how far I got in my first experiments using this mechanism. Hopefully it gave you some inspiration too, so I welcome any usages you made with it.

Inline use of intrinsics can lead to no generated code at all

Since Delphi has no macro language, you cannot create your own intrinsic functions that evaluate to const. You could use a pre-processor though, as described in [WayBack] How to write Delphi compile-time functions – Stack Overflow.

The answer by Johan there however mentions clever use of in-line functions that do not generate any code at all (so effectively evaluate to a const). More on that in a future post.

Compile time assertions in other languages

Many languages support a form of [WayBack] Compile-time calculation – Rosetta Code. If such a language can errors out on compiling such a calculation, then you can have compile time assertions.

Compile time assertions are very much used in C and C++, where they are often called static assertions. Often they depend on macros, but C11 (C standard revision 11) has it built-in.

Since I also do quite a bit of .NET: [WayBack] Can C# Provide a static_assert? – Stack Overflow

Some links on how they work in C and C++, and what you can do with them:

–jeroen

Posted in .NET, C, C#, C++, Conference Topics, Conferences, Delphi, Development, Event, Software Development | Leave a Comment »

Unittesting FizzBuzz

Posted by jpluimers on 2021/02/23

Keep a version history of how you approach the below TDD driven approach, then discuss it with one of your co-workers.

Note there is no “right” approach, though probably you will experience that some environments and approaches may lead to code that is better, for instance because it is:

  • easier to explain
  • shorter
  • more performant

The above 3 might points bite each other (;

Based on FizzBuzz: One Simple Interview Question – YouTube.

Write initial tests

Write unit tests for this unit under test in pseudo code:

type OutputMethod: method(string Value)
class FizzBuzzGame:
    method Construct(OutputMethod Value)
    method Process(int Value)

Unit test that captures Output with a certain set of Values, then tests for the four possible combinations:

  • number
  • Fizz
  • Buzz
  • FizzBuzz

and also ensures that the passed OutputMethod is being called once for each call to Process.

Extend the tests

Now extend the tests to cover the below multiples and all the permutations caused by the longer list. Think carefully about the permutations.

  • 3 -> Fizz
  • 5 -> Buzz
  • 7 -> Fuzz
  • 11 -> Bizz
  • 13 -> Biff

Write the unit under test

The least tricky bit should be this step.

Be sure to test the solution with your co-workers as well.

Non positive numbers.

Did you think about negative numbers?

Did you think about the number zero?

What would you do about them in the tests and unit under test?

–jeroen

Read the rest of this entry »

Posted in Conference Topics, Conferences, Development, Event, Software Development | Leave a Comment »

Delphi intrinsic functions that evaluate to consts

Posted by jpluimers on 2021/02/23

A long time ago, I wondered Are these really Windows compiler unsupported Delphi Intrinsic Routines about [WayBack/Archive.is] Delphi Intrinsic Routines – RAD Studio.

Today, I limited the documented intrinsic list to the constant intrinsic functions:

const
  _Abs = System.Abs(1);
  _Chr = System.Chr(1);
  _Concat = System.Concat(1);
  _Hi = System.Hi(1);
  _High = System.High(1);
  _Length = System.Length('');
  _Lo = System.Lo(1);
  _Low = System.Low(1);
  _Odd = System.Odd(1);
  _Ord = System.Ord(1);
  _Pi = System.Pi();
  _Pred = System.Pred(1);
  _Ptr = System.Ptr(1);
  _Round = System.Round(1);
  _SizeOf = System.SizeOf(1);
  _Sqr = System.Sqr(1);
  _Succ = System.Succ(1);
  _Swap = System.Swap(1);
  _Trunc = System.Trunc(1);

The limited table is below the fold.

There is also a set of undocumented generic intrinsics that I wrote about in Source: Delphi Compiler Intrinsics can help you collapse generated code for generics a lot.

Of the those undocumented functions, these are constant intrinsic functions:

const // undocumented compiler intrinsics
  _Default = Default(Integer);
  _IsManagedType = IsManagedType(1);
  _GetTypeKind = GetTypeKind(1);
  _IsConstValue = IsConstValue(1);

–jeroen

Read the rest of this entry »

Posted in Conference Topics, Conferences, Delphi, Development, Event, Software Development, Undocumented Delphi | 2 Comments »

Delphi: not all lists need to be generic

Posted by jpluimers on 2021/02/18

Lots of Delphi programmers made, or are making the move, of classic Delphi based containers like TObjectList into generic containers like TList<T>.

A while ago, I got into a project that needed to extend lifetime of some objects. Virtually all of them were interface based, and most of the code was from the non-Unicode era, and most of the developers there had a strong background in that era, so they started fiddling with TList, found it hard, then thought “maybe TList<IInterface>” where will help.

The problem however, is that Delphi has no IList<T>. For that, you have to go to the Spring4D library.

Then I sat down with them, and proposed to use an instance good old TInterfacedList of which the context was maintained in an IInterfacedList field.

Back in the days where Delphi did not support non-generic types, TInterfacedList was the only built-in way to store interface references, and the Collection Classes framework by Ray Lischner were the only ways to do that in a more structured way (as they were based on interfaces, an idiom that Embarcadero should have used for their generic collections as well; Spring4D did, so use those collection classes and interfaces whenever possible as they are way more versatile than the Delphi built-in ones)

Back to using TInferfacedList, as it can still be useful today in:

unit InterfacesHolderUnit;

interface

uses
  DebuggableInterfacedObjectUnit, System.Classes;

type
  IInterfacesHolder = interface
    procedure Add(const aReference: IInterface);
  end;

  TInterfacesHolder = class(TInterfacedObject, IInterfacesHolder)
  strict private
    FInterfaces: IInterfaceList;
  public
    constructor Create();
    procedure Add(const aReference: IInterface);
  end;

implementation

{ TInterfacesHolder }

procedure TInterfacesHolder.Add(const aReference: IInterface);
begin
   FInterfaces.Add(aReference);
end;

constructor TInterfacesHolder.Create();
begin
   inherited Create();
   FInterfaces := TInterfaceList.Create();
end;

end.

and some tests:

Read the rest of this entry »

Posted in Conference Topics, Conferences, Delphi, Development, Event, Software Development | 1 Comment »

DUnit testing code that should raise a specific exception

Posted by jpluimers on 2021/02/17

A while back, I was writing some code to demonstrate a few inner workings of TInterfacedObject, interface reference counting and mixing object references with interface references.

One way to show this is through a test case that expects a certain exception to happen, but I forgot how to do that in DUnit. Luckily this pointed me on the right track: [WayBack] delphi – CheckException only accepts 0-parameter methods; how do I test that other methods throw exceptions? – Stack Overflow.

The solution shows that DUnit has had support for something similar as DUnitX: now has a WillRaiseAttribute to ease defining tests around code that should throw exceptions for a very long time (I think this was introduced around Delphi 2005).

You can do it in a property way:

unit InterfacedObjectTestCaseUnit;

interface

uses
  TestFramework;

type
  TDebuggableInterfacedObjectTestCase = class(TTestCase)
  published
    procedure System_TInterfacedObject_Free_Before_RefCount_Should_Raise_EInvalidPointer();
  end;

implementation

uses
  System.SysUtils,

procedure TInterfacedObjectTestCase.System_TInterfacedObject_Free_Before_RefCount_Should_Raise_EInvalidPointer();
var
  ObjectReference:    System.TInterfacedObject;
  InterfaceReference: IInterface;
begin
  ObjectReference    := System.TInterfacedObject.Create();
  InterfaceReference := ObjectReference;
  ExpectedException  := System.SysUtils.EInvalidPointer;
  ObjectReference.Free(); // this should raise an exception in System.TInterfacedObject.BeforeDestruction, as it checks the RefCount to be zero
  // the below is optional; should not be reached. If it is reached, it will fail earlier than the encompassing `RunTest` method would
  ExpectedException := nil; // or `StopExpectingException();`
end;

end.

or in a method way for an exception that happens in the current method:

procedure TInterfacedObjectTestCase.System_TInterfacedObject_Free_Before_RefCount_Should_Raise_EInvalidPointer();
var
  ObjectReference:    System.TInterfacedObject;
  InterfaceReference: IInterface;
begin
  ObjectReference    := System.TInterfacedObject.Create();
  InterfaceReference := ObjectReference;
  StartExpectingException(System.SysUtils.EInvalidPointer);
  ObjectReference.Free(); // this should raise an exception in System.TInterfacedObject.BeforeDestruction, as it checks the RefCount to be zero
  // the below is optional; should not be reached. If it is reached, it will fail earlier than the encompassing `RunTest` method would
  StopExpectingException();
end;

The alternative using CheckException that will raise earlier, but also tests the results of a complete method which also has to be parameterless:

procedure TDebuggableInterfacedObjectTestCase.System_TInterfacedObject_Free_Before_RefCount();
var
  ObjectReference:    System.TInterfacedObject;
  InterfaceReference: IInterface;
begin
  ObjectReference    := System.TInterfacedObject.Create();
  InterfaceReference := ObjectReference;
  ObjectReference.Free(); // this should raise an exception in System.TInterfacedObject.BeforeDestruction, as it checks the RefCount to be zero
end;

procedure TDebuggableInterfacedObjectTestCase.System_TInterfacedObject_Free_Before_RefCount_Should_Raise_EInvalidPointer_TTestMethod_Based();
begin
  CheckException(System_TInterfacedObject_Free_Before_RefCount, System.SysUtils.EInvalidPointer);
end;

So I wrote a class helper based on TProc that allows you to test an anonymous method which usually has more fine grained testing potential.

Because of type compatibility, you have to call the inherited version of CheckException inside the new one:

unit TestCaseHelperUnit;

interface

uses
  System.SysUtils,
  TestFramework;

type
  TTestCaseHelper = class helper for TTestCase
  public
    procedure CheckException(const AProc: TProc; const AExceptionClass: TClass; const msg: string = '');
  end;

implementation

type
  TTestCaseInvoker = class
  strict private
    FProc: TProc;
  public
    constructor Create(const AProc: TProc);
    procedure Execute();
  end;

{ TTestCaseInvoker }

constructor TTestCaseInvoker.Create(const AProc: TProc);
begin
  inherited Create();
  FProc := AProc;
end;

procedure TTestCaseInvoker.Execute();
begin
  if Assigned(FProc) then
    FProc();
end;

procedure TTestCaseHelper.CheckException(const AProc: TProc; const AExceptionClass: TClass; const msg: string = '');
var
  TestCaseInvoker: TTestCaseInvoker;
begin
  TestCaseInvoker := TTestCaseInvoker.Create(AProc);
  try
    inherited CheckException(TestCaseInvoker.Execute, AExceptionClass, msg); // `inherited`, to avoid stack overflow because `TProc` is compatible with `TTestMethod`
  finally
    TestCaseInvoker.Free();
  end;
end;

end.

The test then becomes this:

procedure TDebuggableInterfacedObjectTestCase.System_TInterfacedObject_Free_Before_RefCount_Should_Raise_EInvalidPointer_TProc_Based();
var
  ObjectReference:    System.TInterfacedObject;
  InterfaceReference: IInterface;
begin
  ObjectReference    := System.TInterfacedObject.Create();
  InterfaceReference := ObjectReference;
  CheckException(procedure ()
  begin
    ObjectReference.Free(); // this should raise an exception in System.TInterfacedObject.BeforeDestruction, as it checks the RefCount to be zero
  end,
  System.SysUtils.EInvalidPointer);
end;

DUnit code snippets

Read the rest of this entry »

Posted in Agile, Conference Topics, Conferences, Delphi, Development, Event, Software Development, Unit Testing | Leave a Comment »

Checking expiration dates for your certbot certificates

Posted by jpluimers on 2021/02/16

I have these two little aliases in my toolbox:

alias "certbot-check-all-by-file=bash <(curl -fsSL https://raw.githubusercontent.com/srvrco/checkssl/master/checkssl) --location /etc/letsencrypt/live"
alias "certbot-check-all-by-config=bash <(curl -fsSL https://raw.githubusercontent.com/srvrco/checkssl/master/checkssl) --server ISPconfig"

First a big fat warning

do not run just any script downloaded through curl. Plenty of reasons why this is dangerous:

  1. [WayBack] Detecting the use of “curl | bash” server side | Application Security
  2. [WayBack] One way “curl pipe sh” install scripts can be dangerous [proof of concept] / Jordan Eldredge: script content differs depending on user agent
  3. [WayBack] sean cassidy : Don’t Pipe to your Shell: scripts having different behaviour when executed partially
  4. [WayBack] Why using curl | sudo sh is not advised? – Stack Overflow:

    You can proof your scripts against partial execution by putting the whole thing into the body of a function, and executing that function on the last line. If you know a script is defined like that, it’s exactly as secure as downloading and then executing some installer.

The first three can mostly prevented by using your own fork of the script repository, then checking each modification of the script, combined with ensuring your fork location does not throw tricks 1 or 2 on you.

That’s why I run the above alias only from a checkssl fork which I can inspect.

Back to the alias

The aliases use quite a few tricks:

  1. Having curl download a command minimising
    curl -fsSL https://raw.githubusercontent.com/srvrco/checkssl/master/checkssl

    Via: [WayBack] The missing package manager for macOS (or Linux) — The missing package manager for macOS (or Linux)

  2. Running that command through bash as if the download were a file by wrapping wrapping it in parenthesis and a less than sign .
    bash <(curl -fsSL https://raw.githubusercontent.com/srvrco/checkssl/master/checkssl)

    Via: [WayBack] linux – Execute bash script from URL – Stack Overflow

  3. Passing arguments to the bash command by appending this to the command just like from the regular command-line:
    bash <(curl -fsSL https://raw.githubusercontent.com/srvrco/checkssl/master/checkssl) --location /etc/letsencrypt/live
  4. An alias [WayBack] with double-quotes around the whole statement:
    alias "certbot-check-all-by-file=bash <(curl -fsSL https://raw.githubusercontent.com/srvrco/checkssl/master/checkssl) --location /etc/letsencrypt/live"
  5. Either use the certbot configuration file or apache2 (via ISPconfig as the apache2 parameter value is not yet supported) domain configuration:
    alias "certbot-check-all-by-file=bash <(curl -fsSL https://raw.githubusercontent.com/srvrco/checkssl/master/checkssl) --location /etc/letsencrypt/live"
    alias "certbot-check-all-by-config=bash <(curl -fsSL https://raw.githubusercontent.com/srvrco/checkssl/master/checkssl) --server ISPconfig"

Using source instead of bash

Note that an alternative alias is this one:

alias "certbot-check-all-by-file=(source <(curl -s https://raw.githubusercontent.com/srvrco/checkssl/master/checkssl) --location /etc/letsencrypt/live)"

However, that needs an extra set of parenthesis, otherwise you will get bumped out of your current shell.

The reason is that bash runs in a [WayBack] subshell, whereas [WayBack] source (and the equivalent [WayBack] “dot” command .) runs in the current shell, but the script performs a gracefull_exit or error_exit which end in an exit [WayBack] terminating the current shell.

The [WayBack] () parenthesis around the source command ensure it runs in a sub-shell.

In this case, you can still pass the --location /etc/letsencrypt/live parameters, as bash is the only shell allowing this: [WayBack] source – Passing variables to a bash script when sourcing it – Unix & Linux Stack Exchange.

References

Related [WayBack] Advanced Bash-Scripting Guide topics:

Related cURL options from [WayBack] curl – How To Use:

  • -f: [WayBack] -f, --fail

    (HTTP) Fail silently (no output at all) on server errors. This is mostly done to better enable scripts etc to better deal with failed attempts. In normal cases when an HTTP server fails to deliver a document, it returns an HTML document stating so (which often also describes why and more). This flag will prevent curl from outputting that and return error 22.

    This method is not fail-safe and there are occasions where non-successful response codes will slip through, especially when authentication is involved (response codes 401 and 407).

  • -s: [WayBack] -s, --silent:

    Silent or quiet mode. Don’t show progress meter or error messages. Makes Curl mute. It will still output the data you ask for, potentially even to the terminal/stdout unless you redirect it.

    Use -S, –show-error in addition to this option to disable progress meter but still show error messages.

    See also -v, –verbose and –stderr.

  • -S: [WayBack] -S, --show-errors:

    When used with -s, –silent, it makes curl show an error message if it fails.

  • -L: [WayBack] -L, --location:

    (HTTP) If the server reports that the requested page has moved to a different location (indicated with a Location: header and a 3XX response code), this option will make curl redo the request on the new place. If used together with -i, –include or -I, –head, headers from all requested pages will be shown. When authentication is used, curl only sends its credentials to the initial host. If a redirect takes curl to a different host, it won’t be able to intercept the user+password. See also –location-trusted on how to change this. You can limit the amount of redirects to follow by using the –max-redirs option.

    When curl follows a redirect and the request is not a plain GET (for example POST or PUT), it will do the following request with a GET if the HTTP response was 301, 302, or 303. If the response code was any other 3xx code, curl will re-send the following request using the same unmodified method.

    You can tell curl to not change the non-GET request method to GET after a 30x response by using the dedicated options for that: –post301–post302 and –post303.

Reminders

Reminder to self: see if JSON output is viable. This commit might help.

–jeroen

Posted in bash, Conference Topics, Conferences, Development, Event, Scripting, Software Development | Leave a Comment »