Search code examples
unit-testingdelphidelphi-10-seattledunitx

DUnitX Conversion from string to array of integer


My question is similar to this one, but instead of a set, I'd like to pass an array of integers.

Per example:

[TestCase('TestAdd','[1,2,3,4];[1,2,3,4]',';')]
procedure TestAdd(aValue, aResult: array of integer);

Modifying the DUnitX.Utils seems like the cleanest approach, but I'm not sure how to go about doing the conversion. My main question is, how will it know that I need specifically an array of integers? Is there something in the PTypeInfo that I can take advantage of?


Solution

  • You cannot use an open array because the RTTI is missing code to invoke methods with such arguments (which are implemented by actually two arguments under the hood). So you need to change the signature to use TArray<T> or any other dynamic array type.

    Then the modification to DUnitX.Utils is really easy - just add this function to the Conversions array for tkUString->tkDynArray (any possible optimization is left as an exercise for the reader):

    function ConvStr2DynArray(const ASource: TValue; ATarget: PTypeInfo; out AResult: TValue): Boolean;
    var
      s: string;
      values: TStringDynArray;
      i: Integer;
      p: Pointer;
      v1, v2: TValue;
      elType: PTypeInfo;
    begin
      s := ASource.AsString;
      if StartsStr('[', s) and EndsStr(']', s) then
        s := Copy(s, 2, Length(s) - 2);
      values := SplitString(s, ',');
      i := Length(values);
      p := nil;
      DynArraySetLength(p, ATarget, 1, @i);
      TValue.MakeWithoutCopy(@p, ATarget, AResult);
      elType := ATarget.TypeData.DynArrElType^;
      for i := 0 to High(values) do
      begin
        v1 := TValue.FromString(values[i]);
        if not v1.TryConvert(elType, v2) then
          Exit(False);
        AResult.SetArrayElement(i, v2);
      end;
      Result := True;
    end;