Welcome to MSDN Blogs Sign in | Join | Help

Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

Fault Injection is the act of artificially changing the behavior of an existing executable code to simulate various faults. FI is very useful for validation of error handling code paths and for improving code coverage.

There are several types of fault injection. In runtime fault injection, the fault injecting test modifies the execution logic of the application under test (AUT), by injecting faults, triggered by specific runtime conditions. One could for example implement a FI test with the following semantic:

Throw an out-of-memory (OOM) exception, whenever the application calls method CreateWidget of class WidgetManager.

The FI terminology is as follows:

  • AUT (application under test) – this is the tested application, in which faults are being injected;
  • Fault Rule – The fault rule is a central construct in an FI test that determines WHEN faults get triggered and WHAT TYPES of faults get triggered. A fault rule consists of:
    • a Method Signature, determining the method where the fault will be injected;
    • a Fault Condition, determining when the specific fault should be triggered (e.g. every Nth call)
    • a Fault -- Determines the type of fault (e.g. throwing an exception, returning a specific value, etc.) that occurs when the fault condition is met.
  • Fault Session – The fault session is a collection of fault rules that are applied to a given AUT.

TestApi provides a simple, but powerful runtime fault injection API for injecting faults in managed code. The API was originally designed and implemented by Bill Liu et al from the “Essential Business Server” team, and adapted to TestApi by Sam Terilli from our WPF XAML team. The following content provides a quick introduction to the API.

Sample AUT

Following is the code of a trivial AUT that we will use for demonstration purposes:

//
// This is a sample application used for demonstration purposes.
//
using System;

class MyApplication
{
    static void Main(string[] args)
    {
        int a = 2;
        int b = 3;

        for (int i = 0; i < 10; i++)
        {
            Console.WriteLine("{0}) {1} + {2} = {3}", i, a, b, Sum(a, b));
        }
    }

    private static int Sum(int a, int b)
    {
        return a + b;
    }
}

The result of running this application is of course:

> MyApplication.exe
0) 2 + 3 = 5
1) 2 + 3 = 5
2) 2 + 3 = 5
3) 2 + 3 = 5
4) 2 + 3 = 5
5) 2 + 3 = 5
6) 2 + 3 = 5
7) 2 + 3 = 5
8) 2 + 3 = 5
9) 2 + 3 = 5

A Simple Fault Injection Test

Now, let’s try to inject a fault in the AUT. Let’s assume that we want to modify the return value of Sum. Here’s how we can accomplish that:

// 
// Simple fault injection test
//
using System;
using System.Diagnostics;
using Microsoft.Test.FaultInjection;

public class FaultInjectionTest 
{ 
    public static void Main() 
    { 
        // 
        // Set up a fault rule to return –1000 the second time Sum is called.
        //
        string method = "MyApplication.Sum(int,int)";
        ICondition condition = BuiltInConditions.TriggerOnNthCall(2);
        IFault fault = BuiltInFaults.ReturnValueFault(-1000);
        FaultRule rule = new FaultRule(method, condition, fault);            

        //
        // Establish a session, injecting the faults defined by the fault rule(s)
        //
        FaultSession session = new FaultSession(rule);
        ProcessStartInfo psi = session.GetProcessStartInfo(@".\MyApplication.exe"); 

        //
        // Launch the target process and observe faults
        //
        Process p = Process.Start(psi);        
        p.WaitForExit();
    }
}

Fairly straightforward. Upon running the test (which will itself spawn the AUT) we observe the following output:

> FaultInjectionTest.exe
0) 2 + 3 = 5
1) 2 + 3 = -1000
2) 2 + 3 = 5
3) 2 + 3 = 5
4) 2 + 3 = 5
5) 2 + 3 = 5
6) 2 + 3 = 5
7) 2 + 3 = 5
8) 2 + 3 = 5
9) 2 + 3 = 5

As intended, we injected a runtime fault in MyApplication.exe, which resulted in Sum returning –1000 the second time it got called.

Under The Covers

Under the covers, the managed code Fault Injection API uses the CLR profiling API to modify the prologue of the intercepted method at runtime in order to inject the desired fault. The injected prologue instructions essentially call a method in the library, which then dispatches the call to the specified fault.

Because faults are injected at runtime, the code of the original application is not modified in any way. There is a certain performance degradation, which depends on the number of the injected faults.

The fault injection API provides a variety of built-in conditions and faults (in the BuiltInConditions and BuiltInFaults classes respectively). Users of the API can also create custom conditions and faults (by implementing the ICondition and IFault interfaces respectively). The API also provides a set of classes that expose the ability to fine tune and monitor the injected faults.

In addition, the API provides a facility to set “global faults”, which is useful for server application testing, where one application typically consists of and recycles many different processes.

I have attached the sample above, which should get you up and running with fault injection.

 

 

Published Wednesday, November 25, 2009 12:47 AM by ivom
Attachment(s): FaultInjectionSample.zip

Comment Notification

If you would like to receive an email when updates are made to this post, please register here

Subscribe to this post's comments using RSS

Comments

Wednesday, February 24, 2010 11:44 AM by Eddie

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

I am trying to use the FaultInjection libraries to simulate data access failures.

Is this proper method signature to get the ExecuteNonQuery() method on DbCommand?

       Dim method = "System.Data.Common.DbCommand.ExecuteNonQuery()"

Thanks.

Wednesday, February 24, 2010 2:16 PM by ivom

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

Yep, this should work.

Let me know if you have trouble and we will help.

Monday, March 01, 2010 3:33 PM by David

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

I had success when using the fault injection libraries on some sample code I had written, but now that I'm trying to use it against our intended target code it doesn't seem to be working.

I want to use the ReturnValueFault on an internal static method which returns an int. The method is within an internal sealed class with the SecuritySafeCritical attribute set.

I'm creating the fault rule like this:

FaultRule rule = new FaultRule("static FullNamespace.ClassName.GetDeviceId(uint, IntPtr, IntPtr, IntPtr)", BuiltInConditions.TriggerOnEveryCall, BuiltInFaults.ReturnValueFault(1000));

I also tried prepending out to IntPtr as these are specified as out parameters in the method signature. I didn't have success either way. Am I doing something wrong here?

My other thought is that this class and method are in a library/dll, and not within the exe code itself which is the process I'm starting a fault injection session for.

Any help is greatly appreciated. So far I've been very happy with the low cost of entry for using this API and the good documentation.

Monday, March 01, 2010 11:11 PM by bill

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

Hi David, can you try to replace "IntPtr" with "System.IntPtr" in the parameters list of the method? the data type of the parameter need to be fully qualified name too.

let me know if it still doesn't work.

Wednesday, March 03, 2010 3:46 PM by David

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

I tried System.IntPtr and had no luck here either. Is it an issue if the binary I am trying to inject into is signed?

Thursday, March 04, 2010 11:46 PM by bill

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

no, the issue is not related to the sign. i noticed that you mention about 'out parameter'. please make sure that 'out' is also part of the method signature. i.e your method signature should be:

static FullNamespace.ClassName.GetDeviceId(uint, out System.IntPtr,out System.IntPtr,out System.IntPtr)

i wrote a dummy app with the same method signature above, and the fault injection api works for me without problem. if this still doesn't work for you. most likely reason here is that the method signature still not correct somehow, i.e it does not match what clr expecting in the runtime). a few thing you can duble check:

1. make sure the method got invoked.

2. make sure the method signature is correct or no typo (i often see people has typo in the method signature by mistakes).

3. in the %temp% folder, there should have some log, check if there are some error.

Friday, March 05, 2010 8:03 AM by David

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

I have tried with the out parameter and also it did not work. I coulnd't find any logs in %temp% folder. I realized today that the AUT is a native program that runs the code from the managed assemblies by hosting a CLR runtime. Is this my problem, is there a way to still use TestApi fault injection or will I have to use some native fault injection library instead?

Friday, March 05, 2010 12:53 PM by bill

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

ha. that's the problem. when i saw the IntPtr parameter at the first place, i suspected that your aut might be using navtive code or p/invoke stuff which is out scope of this tool that targets to managed code only.  in you case, you have to use native fault injection tool or windows API hooker stuff.

good luck.

Friday, March 05, 2010 8:11 PM by David

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

Darn, this would have been the perfect solution. Thanks for your help!

Tuesday, March 09, 2010 12:47 PM by Eddie

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

I am just getting around to responding to Ivan's response to my original comment.

Ivan, I am unable to get ExecuteNonQuery() to throw an exception for me. Here is all my code from within a (slightly verbose) unit test:

       Dim method = "System.Data.Common.DbCommand.ExecuteNonQuery()"

       'Dim condition = BuiltInConditions.TriggerOnNthCall(2)

       Dim condition = BuiltInConditions.TriggerOnEveryCall

       Const ExceptionMessage As String = "This is a fault-injected exception."

       Dim fault = BuiltInFaults.ThrowExceptionFault(New DataException(ExceptionMessage))

       Dim faultRule = New FaultRule(method, condition, fault)

       Dim s = New FaultSession(faultRule)

       Dim rp = TestDataFactory.CreateYrtReinsurancePolicy(PolicyNumberFactory.CreateUnique())

       Dim isExceptionThrown As Boolean

       Try

           rp.Save()

       Catch ex As DataException

           If ex.Message.Equals(ExceptionMessage) Then

               isExceptionThrown = True

           End If

       End Try

       Assert.IsTrue(isExceptionThrown)

Note that I gave up on the NthCall and went with EveryCall just to see if I could get it to work.

Any ideas?

Tuesday, March 16, 2010 10:24 AM by bill

# re: Introduction to TestApi – Part 5: Managed Code Fault Injection APIs

if this is your complete version of the code, the fault injection won't work, because i only see you set FaultSession, but not call Launch method. referring to the document/samples, in order to have fault injection works, you have to provide an executable which execute your target API (ExecuteNonQuery, in your case), and you set the fault injection rule and call launch method to launch your executable.

I have a private version to better support unit test, but probably take a while to release publicly. for now, this is the high level work flow (suppose you want to inject fault into function Foo()).

1. write an app to call Foo().

2. set fault injeciton rule and session, as you did above.

4. call Launch method to run your app. and when your app call Foo(), the fault will be injected.

let me know if you have any questions.

Leave a Comment

(required) 
required 
(required) 

  
Enter Code Here: Required
 
Page view tracker