Extending

The testing framework here only provides base testing facilities: Assertions for the types in Standard, code timers, and possibly more. For types not in Standard, extensions to the framework must be provided. This is because Ada has a very sophisticated type system; not just intrinsic types, but also not built by inheritance like class libraries. This framework attempts to make testing non-standard types as easy as possible.

Extension packages belong as child packages to the package they would be testing. So testing extensions for Mathematics should be named Mathematics.Testing. This provides a logical naming structure, but also, access to the private parts of the package, so that they may be tested while remaining private.

To extend this framework, you combine the Boolean comparison operators for your type, with the failure tracking and logging conventions used. This looks like the following:

procedure Is_Equal(Statement : in Wide_Wide_String; Result, Expected : in Custom_Type) is
begin
  if Result = Effected then
    Pass;
  else
    Fail;
  end if;
  Put(Statement & " -> ");
  Put(Result'Wide_Wide_Image(Result) & " = ");
  Put(Result'Wide_Wide_Image(Expected));
  New_Line;
end Is_Equal;

Use will be something like this, assuming no additional parameters:

Is_Equal("1 + 2", 1 + 2, 3);

Procedure testing is also supported, but due to certain complexities, is not provided in any generic way. These are easy to write however. Something like the following will work:

procedure Passes(Statement : in Wide_Wide_String; Call : access Procedure(Self : in out Object_Type); Self : in out Object_Type) is
begin
    Call.all(Self);
    Pass;
    Put_Line(Statement & " → Passes");
exception
    when others =>
        Fail;
        Put_Line(Statement & " → Fails");
end Passes;

Use will then be:

Passes("Reciprocal(2)", Reciprocal'access, 2);

If you are familiar with languages supporting introspection, this might seem more verbose than you are familiar with. Indeed it is. However this seems to be the best solution given the language used. It is useful to keep in mind these are only ever written once, and are so templateable a tool to generate these will likely be written in time.

To explain a few specifics. Statement is the code being executed as a string, and will be the same code used to produce Result. This is necessary because Ada lacks introspection. Expected is what Result should be. The specific type of assertion (is equal, in this case) is performed immediately. Pass and Fail are simple procedures which begin logging the results, each marking the beginning of the line with the result, while also recording the amount of failures. After this, it is just a matter of writing the rest of the assertion details to the log.

In many cases it is desirable to provide additional parameters; this is in fact done in many other libraries. For example, fractional numeric types have an optional tolerance value with defaults to 0.0, for specifying acceptable margin of error for things like approximation algorithms. As another example, container types often have a dump parameter which if true prints the container as text after the operation being tested takes place.