Skip to content
Akira Sugiura edited this page Dec 30, 2015 · 7 revisions

In this page, there are the method signatures that are supported by Prig and APIs that are explained in the other samples.

Table of Content




Definition of word

Prig Assembly is the assembly that indicates to enable target method you want to replace. Prig Type and Prig Type Introducer will be defined in the assembly.

This assembly will be outputted into $(TargetDir) of the project that the assembly is added, and its naming convention is <assembly name>.<runtime version>.v<assembly version>.dll.

To add a Prig Assembly, right click Reference of the project and select Add Prig Assembly menu, or execute Add-PrigAssembly in the Package Manager Console. See also README.md and Package Manager Console Powershell Reference.

Stub Settings File is the XML file that lists the information of replaceable methods to define into Prig Assembly.

This file will be outputted into $(ProjectDir) of the project that the file is added, and its naming convention is <assembly name>.<runtime version>.v<assembly version>.prig.

This file is added with the same timing that the Prig Assembly is added. Also, you can start Prig Setup Session with right clicking this file, and you can remove the Prig Assembly from here. See also README.md and Package Manager Console Powershell Reference.

Prig Setup Session is the special PowerShell session to add Prig Assembly, to analyze target assembly and so on. This session can start from right click Stub Settings File and select Edit Prig Indirection Settings, or execute Start-PrigSetup command in the Package Manager Console.

See also README.md and Package Manager Console Powershell Reference.

Indirection Stub Setting is the each information to specify the replacement target method that will be written into the Stub Settings File. Specifically, it is the each add tag that is listed between <stubs>...</stubs> tag in Stub Settings File.

See also README.md and Package Manager Console Powershell Reference.

Indirection Delegate is the delegate that indicates the interface of replacement target method. Prig provides the following Indirection Delegate by default:

Name Return Parameters
IndirectionAction void Max 16, the all are normal
IndirectionOutAction void Max 16, the last one is out; the others are normal
IndirectionOutOutAction void Max 16, the last two are out; the others are normal
IndirectionRefAction void Max 16, the last one is ref; the others are normal
IndirectionRefRefAction void Max 16, the last two are ref; the others are normal
IndirectionRefThisAction void Max 16, the first one is ref; the others are normal
IndirectionRefThisOutAction void Max 16, the first one is ref, the last one is out; the others are normal
IndirectionRefThisOutOutAction void Max 16, the first one is ref, the last two are out; the others are normal
IndirectionRefThisRefAction void Max 16, the first one is ref, the last one is ref; the others are normal
IndirectionRefThisRefRefAction void Max 16, the first one is ref, the last two are ref; the others are normal
IndirectionFunc TResult Max 16, the all are normal
IndirectionOutFunc TResult Max 16, the last one is out; the others are normal
IndirectionOutOutFunc TResult Max 16, the last two are out; the others are normal
IndirectionRefFunc TResult Max 16, the last one is ref; the others are normal
IndirectionRefRefFunc TResult Max 16, the last two are ref; the others are normal
IndirectionRefThisFunc TResult Max 16, the first one is ref; the others are normal
IndirectionRefThisOutFunc TResult Max 16, the first one is ref, the last one is out; the others are normal
IndirectionRefThisOutOutFunc TResult Max 16, the first one is ref, the last two are out; the others are normal
IndirectionRefThisRefFunc TResult Max 16, the first one is ref, the last one is ref; the others are normal
IndirectionRefThisRefRefFunc TResult Max 16, the first one is ref, the last two are ref; the others are normal
Work object Unlimited, but only use against non-public signature method

These delegates will be selected by the following different condition:

  • When there are any non-public type onto the signature:
    Always Work will be selected. If the method is a static member, the delegate parameter args will be set with same order as the parameters of the original method. If the method is an instance member, the args will be set as that this is 0th, the others are same as the original method in spite of whether the declaring type is a class or a struct.
  • Otherwise:
    • A static method:
      The same signature as the original method will be selected.
      For example, if the method is DateTime.Now, IndirectionFunc<TResult> will be selected, and it will be instantiated as IndirectionFunc<DateTime>.
    • A instance method:
      • When the declaring type is a class:
        The type of this is passed as the first parameter, and the others are same as the original method.
        For example, if the method is Exception.InternalToString, IndirectionFunc<T1, TResult> will be selected, and it will be instantiated as IndirectionFunc<Exception, string>.
      • When the declaring type is a struct:
        The type of ref this is passed as the first parameter, and the others are same as the original method.
        For example, if the method is Nullable<T>..ctor, IndirectionRefThisAction<TRef1, T1> will be selected, and it will be instantiated as IndirectionRefThisAction<T?, T>.

Against the methods that don't apply to the above, you have to prepare Indirection Delegate yourself. For detail, please see Replace Three or More out/ref Parameter Method.

Prig Type is a stub type to collect Indirection Stub, and Prig Type Introducer is the type that defines the identifier list to specify the target method. Both types will be defined into Prig Assembly. Their naming convention is similar to Microsoft Fakes's, but there is also a little different:

  • The namespace that the Prig Types are located is the original namespace + .Prig.
    For example, the Prig Types for the types under System are located at the namespace System.Prig.
  • The prefix of the Prig Type is P(no conditions) or PProxy(specified instance of a class).
    For example, the Prig Type for System.DateTime is System.Prig.PDateTime. However, the Prig Type PProxyDateTime isn't generated, because it is a structure. The Prig Type for System.Net.HttpWebRequest is System.Net.Prig.PHttpWebRequest. Also, System.Net.Prig.PProxyHttpWebRequest is generated, because it is a class.
  • The prefix of the Prig Type Introducer is OfP(no conditions) or OfPProxy(specified instance of a class).
    The difference between a class and a structure is same as Prig Type.

See also Code generation, compilation, and naming conventions in Microsoft Fakes.

Indirection Stub is a dummy method body for replacement target method. This is defined into Prig Type. Its naming convention is same as Microsoft Fakes's.

See also Code generation, compilation, and naming conventions in Microsoft Fakes.

Features

public static DateTime Now { get; } of System.DateTime in mscorlib

In mscorlib's Prig Setup Session, execute the following command:

PS> $TargetReferencedAssembly.GetTypes() | ? { $_.name -eq 'datetime' } | pfind -m 'get_now' | pget | clip

Paste the result to the Stub Settings File and build the project. Then, you can write like this:

PDateTime.NowGet().Body = () => new DateTime(2013, 12, 13, 11, 00, 00);

static Foo() of Foo in StaticMockingMigration

In StaticMockingMigration's Prig Setup Session, execute the following command:

PS> $TargetReferencedAssembly.GetTypes() | ? { $_.name -eq 'foo' } | pfind -m '\.cc' | pget | clip

Paste the result to the Stub Settings File and build the project. Then, you can write like this:

PFoo.StaticConstructor().Body = () => { };

public static T Exchange<T>(ref T location1, T value) where T : class of System.Threading.Interlocked in mscorlib

In mscorlib's Prig Setup Session, execute the following command:

PS> $TargetReferencedAssembly.GetTypes() | ? { $_.name -match 'interlock' } | pfind -m ' exchange\[T\]' | pget | clip

Paste the result to the Stub Settings File and build the project. Then, you can write like this:

PInterlocked.ExchangeOfTTRefT<MyData>().Body = (ref MyData location1, MyData value) =>
{
    location1 = value;
    return new MyData(42);
};

public static bool IsCompatibleKey(object key) of UntestableLibrary.ULDictionary<TKey, TValue> in UntestableLibrary

In UntestableLibrary's Prig Setup Session, execute the following command:

PS> $TargetReferencedAssembly.GetTypes() | ? { $_.name -match 'dict' } | pfind -m 'is' | pget | clip

Paste the result to the Stub Settings File and build the project. Then, you can write like this:

PULDictionary<int, string>.IsCompatibleKeyObject().Body = key => key is string;

internal unsafe static bool TryParse(string s, DateTimeFormatInfo dtfi, DateTimeStyles styles, ref DateTimeResult result) of System.DateTimeParse in mscorlib

In mscorlib's Prig Setup Session, execute the following command:

PS> $TargetReferencedAssembly.GetTypes() | ? { $_.name -eq 'datetimeparse' } | pfind -m 'tryparse\(.*result' | pget | clip

Paste the result to the Stub Settings File and build the project. Then, you can write like this:

var dateTimeResult = typeof(DateTime).Assembly.GetTypes().First(_ => _.Name == "DateTimeResult");
var expected = Activator.CreateInstance(dateTimeResult);
PDateTimeParse.TryParseStringDateTimeFormatInfoDateTimeStylesDateTimeResultRef().Body = args => { args[3] = expected; return true; };

internal virtual string InternalToString() of System.Exception in mscorlib

In mscorlib's Prig Setup Session, execute the following command:

PS> $TargetReferencedAssembly.GetTypes() | ? { $_.name -eq 'exception' } | pfind -m 'internalt' | pget | clip

Paste the result to the Stub Settings File and build the project. Then, you can write like this for all instance:

PException.InternalToString().Body = @this => "にゃんぱすー";

or specified instance:

var proxy = new PProxyException();
proxy.InternalToString().Body = @this => "にゃんぱすー";
// and pass `proxy` to the API that you want to mock.

public override long Seek(long offset, SeekOrigin loc) of System.IO.MemoryStream in mscorlib

In mscorlib's Prig Setup Session, execute the following command:

PS> $TargetReferencedAssembly.GetTypes() | ? { $_.name -eq 'memorystream' } | pfind -m ' seek' | pget | clip

Paste the result to the Stub Settings File and build the project. Then, you can write like this for all instance:

PMemoryStream.SeekInt64SeekOrigin().Body = (@this, offset, loc) => 42L;

or specified instance:

var proxy = new PProxyMemoryStream();
proxy.SeekInt64SeekOrigin().Body = (@this, offset, loc) => 42L;
// and pass `proxy` to the API that you want to mock.

public void Add(T item) of System.Collections.Generic.List<T> in mscorlib

In mscorlib's Prig Setup Session, execute the following command:

PS> $TargetReferencedAssembly.GetTypes() | ? { $_.name -eq 'list`1' } | pfind -m ' add\(' | pget | clip

Paste the result to the Stub Settings File and build the project. Then, you can write like this for all instance:

var actual = default(int);
PList<int>.AddT().Body = (@this, item) => actual = item;

or specified instance:

var proxy = new PProxyList<int>();
proxy.AddT().Body = (@this, item) => actual = item;
// and pass `proxy` to the API that you want to mock.

public Nullable(T value) of System.Nullable<T> in mscorlib

In mscorlib's Prig Setup Session, execute the following command:

PS> $TargetReferencedAssembly.GetTypes() | ? { $_.name -eq 'nullable`1' } | pfind -m '\.ctor' | pget | clip

Paste the result to the Stub Settings File and build the project. Then, you can write like this:

var actualValue = 0;
PNullable<int>.ConstructorT().Body = (ref Nullable<int> @this, int value) =>
{
    actualValue = value;
};

internal override EraInfo[] CalEraInfo of System.Globalization.JapaneseLunisolarCalendar in mscorlib

In mscorlib's Prig Setup Session, execute the following command:

PS> $TargetReferencedAssembly.GetTypes() | ? { $_.name -match 'japanesel' } | pfind -m 'get_cal' | pget | clip

Paste the result to the Stub Settings File and build the project. Then, you can write like this for all instance:

var expected = default(Array);
MakeCalEraInfoTestData(out expected);
PJapaneseLunisolarCalendar.CalEraInfoGet().Body = args => expected;

or specified instance:

var proxy = new PProxyJapaneseLunisolarCalendar();
proxy.CalEraInfoGet().Body = args => expected;
// and pass `proxy` to the API that you want to mock.

See also README.md, Generics and Non-public Method Replacement.

To call original method, you have to call the method through IndirectionsContext.ExecuteOriginal. For example, against public virtual IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) of System.IO.Stream, like this:

PStream.BeginReadByteArrayInt32Int32AsyncCallbackObject().Body = (@this, _buffer, offset, count, callback, state) => 
    IndirectionsContext.ExecuteOriginal(() => @this.BeginRead(_buffer, offset, 42, callback, state));

In the case for the constructor of a class, you have to reuse the this that is passed as first parameter. So, you have to use the reflection API. For example, against public Random(int Seed) of System.Random, like this:

var seeds = new HashSet<int>();
PRandom.ConstructorInt32().Body = (@this, seed) =>
{
    IndirectionsContext.ExecuteOriginal(() =>
    {
        var ctor = typeof(Random).GetConstructor(new[] { typeof(int) });
        ctor.Invoke(@this, new object[] { seed });
    });
    seeds.Add(seed);
};

In the case for the constructor of a struct, you can replace the ref this that is passed as first parameter. For example, against public Nullable(T value) of System.Nullable<T>, like this:

var actualValue = 0;
PNullable<int>.ConstructorT().Body = (ref Nullable<int> @this, int value) =>
{
    actualValue = value;
    @this = IndirectionsContext.ExecuteOriginal(() => new Nullable<int>(value));
};

See also Calling Original Method.

Default Behavior can be set in the following levels:

Note that any generic types/methods are excluded as shown in the API. This is a limitation by current design.

All methods that are defined in the Stub Settings File

To change all methods that are defined in the Stub Settings File, you can use IndirectionsContext. The following example changes the default behavior to return default value:

IndirectionsContext.
    ExcludeGeneric().
    DefaultBehavior = IndirectionBehaviors.DefaultValue;

All methods that are defined in the Prig Type

To change all methods that are defined in the Prig Type, you can use the Prig Type. The following example changes the default behavior of System.Environment excluding some members to throw NotImplementedException:

PEnvironment.
    ExcludeGeneric().
    Exclude(PEnvironment.CurrentManagedThreadIdGet()).
    Exclude(PEnvironment.OSVersionGet()).
    DefaultBehavior = IndirectionBehaviors.NotImplemented;

All methods that are defined in the Prig Type for a specified instance

To change all methods that are defined in the Prig Type for a specified instance, you can use the Prig Type for a specified instance. The following example changes only the default behavior of public void Add(T item) of a specified instance of List<int> to revert to original behavior:

var proxy = new PProxyList<int>();
proxy.
    ExcludeGeneric().
    IncludeAddT().
    DefaultBehavior = IndirectionBehaviors.Fallthrough;
// and pass `proxy` to the API that you want to mock.

See also Default Behavior.

Clone this wiki locally