Skip to Content

tag

Tag Archives: AutoFixture

Extending AutoFixture with custom ISpecimenBuilder

At times AutoFixture out-of-a-box behavior and capabilities, fall to short when we require more customized test data. Such was my case. In previous post [click] I  mentioned that I prefer not to provide objects with invalid data, as a matter of fact, it’s not only a case of personal preference. If we declare array of certain length via MarshalAsAttribute, and then set different one, will end up with exception. Thus the idea of generating test data based on MarshalAsAttribute, since we already have access to it, shouldn’t be a problem. One more thing worth mentioning. At some point I’ll be mocking ADS client, since CI build server won’t have access to actual TwinCAT runtime. Default AutoFixture behavior throws exception if we try to generate structure with default constructor only, if we only go with suggested SupportMutableValueTypesCustomization we’ll end up with structure which has all fields set with default values, isn’t the thing we need, is it?

Implementing ISpecimenBuilder

First thing we ought to do is implementing ISpecimenBuilder and it’s quite simple as it only has one method.

let MarshalAsTypeBuilder =
  {
    new ISpecimenBuilder with
    member self.Create(request, context) =
      
      match request with
        | : ? Type as t when 
          t.IsValueType 
          && t.IsPrimitive |> not
          && Array.contains t 
            [|
              typedefof<string>;
              typedefof<decimal>;
              typedefof<DateTime>;
              typedefof<DateTimeOffset>;
              typedefof<TimeSpan>
            |] |> not ->
              let instance = Activator.CreateInstance(t)
              instance
        | _ -> new NoSpecimen() :> obj
  }

What we’re doing here is verifying if the current context is type of our interest. If not we fall back to default behavior on line 20. As you may noticed we still only have an instance with default values.

Providing data for struct fields

before we proceed, let’s take a look on code compiled from F#.

[<StructLayout(LayoutKind.Sequential, Pack=1)>]
type StringTestStruct =
  struct
    [<MarshalAs(UnmanagedType.ByValTStr, SizeConst=81)>]
    val string80Var:string
    [<MarshalAs(UnmanagedType.ByValTStr, SizeConst=2)>]
    val string1Var:string
    [<MarshalAs(UnmanagedType.ByValTStr, SizeConst=257)>]
    val string256Var:string
  end

[CompilationMapping(SourceConstructFlags.ObjectType)]
[Serializable]
public struct StringTestStruct : IEquatable<Tests.StringTestStruct>, IStructuralEquatable, IComparable<Tests.StringTestStruct>, IComparable, IStructuralComparable
{
    internal string string80Var@;

    internal string string1Var@;

    internal string string256Var@;

    [CompilationMapping(SourceConstructFlags.Field, 1)]
    public string string1Var
    {
        get
        {
            return this.string1Var@;
        }
    }

    [CompilationMapping(SourceConstructFlags.Field, 2)]
    public string string256Var
    {
        get
        {
            return this.string256Var@;
        }
    }

    [CompilationMapping(SourceConstructFlags.Field, 0)]
    public string string80Var
    {
        get
        {
            return this.string80Var@;
        }
    }
}

F# hides struct fields behind properties. Properties itself are of no interest for us as they do not store MarshalAsAttribute metadata, thus we’ll be iterating over non-public instance fields.

let instance = Activator.CreateInstance(t)

t.GetFields(BindingFlags.Instance ||| BindingFlags.NonPublic ||| BindingFlags.Public)
|> Array.map (fun fi -> fi,context.Resolve(fi))
|> Array.iter (fun (fi, value) -> 
  fi.SetValue(instance, value)
)
instance

At this point we have are supplied with struct instance with default AutoFixture data, which do not correspond to MarshalAsAttribute data.

Assuring valid data

As said in beginning, we have to provide data for arrays and strings accordingly to information we provided via MarshalAsAttribute.

Thus we’ll have to override default behavior for those as well.

Strings not longer that SizeConst

| : ? FieldInfo as fi when fi.FieldType = typedefof<string> ->
  let attr = fi.GetMarshalAsAttribute()
  match attr with
    | Some(maa) when maa.Value = UnmanagedType.ByValTStr && maa.SizeConst > 0 ->
        let result = context.Resolve(typedefof<string>).ToString()
        result.Substring(0,Math.Min(result.Length,maa.SizeConst-1)) :> obj
    | _ -> new NoSpecimen() :> obj

if field is string, and it has MarshalAsAttribute we use AutoFixture default string generator, only difference is, if it’s longer than expected we take substring of it, so it’s length would correspond to SizeConst

–1 is used only due to ADS string null terminator.

Arrays length equal to SizeConst

| : ? FieldInfo as fi when fi.FieldType.IsArray -> 
  let attr = fi.GetMarshalAsAttribute()
  match attr with
    | Some(maa) -> 
      let result = Array.CreateInstance(fi.FieldType.GetElementType(), maa.SizeConst)
      Array.Copy(Array.init maa.SizeConst (fun _ -> context.Resolve(fi.FieldType.GetElementType())), result,maa.SizeConst)
      result :> obj
    | _ -> new NoSpecimen() :> obj

again we override default behavior, this time first we create array with length based on SizeConst, then we fill with values generated with AutoFixture (that includes our overrides, if we have nested types)

Accessing MarshalAsAttribute

While playing with AutoFixture to verify if F# Records can be marshalled as nicely as Structs, to my surprise I noticed MarshalAsAttribute cannot be accessed by standard GetCustomAttributes method. Turns out that it’s one of pseudo custom attributes that are treated differently by compiler.

Why would I need to access MarshalAsAttribute?

AutoFixture by default generates test data for string as memberName+GUID, which is nice for most cases, but if a filed is marked as having 10chars in string, why provide with incorrect data?

type SampleRecord = {
  [<field: MarshalAs(UnmanagedType.I1)>]boolVar:BOOL
  byteVar: BYTE
  dwordVar: DWORD
  lrealVar: LREAL
  [<field: MarshalAs(UnmanagedType.ByValTStr, SizeConst=81)>]
  string80Var: string
  [<field: MarshalAs(UnmanagedType.ByValTStr, SizeConst=11)>]
  string10Var: string
}

Possible solution would be usage of additional attribute like [<field: StringLength(10)>], but I’m not a big fan of providing same information, over and over again, if I already did it once, plus sometimes I’m stubborn, well, quite often if I’m positive something can be done the way I want it.

How to access MarshalAsAttribute?

If you look into .Net Reference Source you’ll find internal static GetCustomAttribute(RuntimeFieldInfo field) method, which returns the very same MarshalAsAttribute as we annotated our field with. Few pretty self-explanatory lines of code and we’re home.

module Extensions

open System.Reflection
open System.Runtime.InteropServices

let maa = typedefof<MarshalAsAttribute>
  
let GetCustomAttribute = 
  maa.GetMethods(BindingFlags.Static ||| BindingFlags.NonPublic) 
  |> List.ofArray 
  |> List.tryFind (fun mi -> 
    let parameters = mi.GetParameters()
    mi.Name = "GetCustomAttribute" 
    && parameters.Length = 1 
    && parameters.[0].ParameterType.Name = "RuntimeFieldInfo"
    )


type FieldInfo with
  member self.GetMarshalAsAttribute () =
    match GetCustomAttribute with
      | Some(gcaMi) ->
        let attr = gcaMi.Invoke(null, [| self |])
        Some(attr :?> MarshalAsAttribute)
      | None -> None

F# ADS Toolkit | Tools review

Why F#?

For the development of ADS Toolkit I have considered two .NET based languages, F# due to Type Providers, and Nemrele due to powerful metaprogramming capabilities. Unfortunately Nemerle, which is great language, I strongly encourage you to try it, lacks mature support in Visual Studio.

Let’s focus on few F# features that’ll be useful for this particular project.

Type aliases

As mentioned in previous post, TwinCAT implements IEC 61131-3 specification, thus data types naming is different from the one we know from .NET world.  Type aliases are a little thing, yet they make communication a lot simpler, due to allowing both parties (.NET Dev and PLC Dev) usage of same naming convention. Besides, it’s easier, and faster, to say double word instead of  32bit unsigned integer.

Units of Measure and strong typing

Yet another little thing, but it makes values less abstract, plus it makes types safe, if by any chance we’ll by trying to add, or compare,  A (current) to V (voltage) we’ll end up with compile time error. Also, typical solution involves precise measurement thus F# lack of dynamic casting between numeric is another plus.

Type providers

An F# type provider is a component that provides types, properties, and methods for use in your program. – MSDN

I want to skip as much manual work as possible when creating software, call me lazy, thus major part of this project is providing types, required to communicate with PLC, based on *.tpy file generated by TwinCAT.

F# itself

On a first few projects in area of Industrial Automation I used C# exclusively, but recently, say one year ago, I decided to take my chances and try F# for the part of solution responsible for communication with PLC, LIN (Local Interconnect Network – an Automotive network) systems and third party web service. Since that time I cannot imagine myself using non-functional language for such appliances. Time required for development was at least cut 40%, moreover I wasn’t distracted by expressive C-like syntax. As a side note, code metrics is way shorter, yet it’s not a valid criterion to judge software.

Testing

AppVeyor

I always wanted to give those fancy hosted CI build servers a shot. I picked AppVeyor as it’s free for OSS projects (same as Travis-CI), comes with nice badges (again, same as Travis-CI) and is Windows based. Later, if I get to the point at which I replace Beckhoff’s TwinCAT.Ads.dll with own, fully managed .NET client, I’ll be testing it on mono/linux.

FoQ, AutoFixture, xUnit, FsUnit

I admit, I used none of them before, only nUnit, MSTest and JustMock, no wonder I want to try something different, although JustMock has some nice features. DSP is great moment to try new tools, and I’m curious if xUnit is as much extensible as it is described, if AutoFixture saves as much time as its authors boast, and moreover, how all of them play together.

Why I’ll need mocking

As said earlier, I’ll be using AppVeyor, hosted CI build server. It means I can forget about testing against actual TwinCAT instance, whether VM or actual PLC, adding routes to running TwinCAT instance can sometimes be pain in the ass, and I don’t want to check how it can be done on AppVeyor, it probably can’t be done anyway. I’m going to wrap TcAdsClient in  my class (and mock it for test). It’s always good idea to create wrapper for TcAdsClient anyway, as AdsException does not contain information regarding on which particular symbolic/handle operation has failed.