Using Pex4Fun to explore Pex behavior
using System;
using Microsoft.Pex.Framework;
using Microsoft.Pex.Framework.Settings;
[PexClass]
public class Program
{
[PexMethod(TestEmissionFilter=PexTestEmissionFilter.All)]
public static string testMethod(int x, int y)
{
PexAssume.IsTrue(y >= 0);//replace here with your assumption
//... enter your code under test here
//if (x == 10000) throw new Exception();
PexAssert.IsTrue(y >= 0);//replace here with your assertion
return PexSymbolicValue.GetPathConditionString();
}
}
Note: You can replace the API call of PexSymbolicValue.GetPathConditionString() with another API call defined on the PexSymbolicValue class. Please refer the MSDN documentation for this class. For excample, you can use PexSymbolicValue.ToRawString<T>(T) or PexSymbolicValue.ToString<T>(T) to turn a value into a string that represents the symbolic value. For example, you can turn the Puzzle method below
using System;
using Microsoft.Pex.Framework;
using Microsoft.Pex.Framework.Settings;
[PexClass]
public class Program
{
[PexMethod(TestEmissionFilter=PexTestEmissionFilter.All)]
public static int Puzzle(int x, int y)
{
//... the original puzzle code here
ret = ...;
return ret;
}
}
into the rewritten Puzzle method below with its return type replaced with string, and its return value of the method to be the combination of the path condition and the return value's symbolic expression:
using System;
using Microsoft.Pex.Framework;
using Microsoft.Pex.Framework.Settings;
[PexClass]
public class Program
{
[PexMethod(TestEmissionFilter=PexTestEmissionFilter.All)]
public static string Puzzle(int x, int y)
{
//... the original puzzle code here
ret = ...;
return "PC: " + PexSymbolicValue.GetPathConditionString() + " RET: " + PexSymbolicValue.ToString<int>(ret);
}
}
Even better, you can print the PUT name, PUT argument values, path condition, and return values' symbolic expression into a file with an example as below. First, you apply Pex on the the original PUT to generate CUTs from the PUT. Second, you replace the original PUT with the following revised PUT and run the generated CUTs (which invokes the new revised PUT). In this case, you don't need to change the return type of the PUT. In addition, you may use the same trick to construct a original PUT that includes comparing the return of the student solution puzzle and the return of the secret puzzle (see Section 4.3 of the ICSE 2016 SEET paper), and then replace such PUT with the revised one below.
using System;
using System.IO;
using Microsoft.Pex.Framework;
using Microsoft.Pex.Framework.Settings;
[PexClass]
public class Program
{
[PexMethod(TestEmissionFilter=PexTestEmissionFilter.All)]
public static int Puzzle(int x, int y)
{
//... the original puzzle code here
ret = ...;
string path = @"c:\temp\MyTest.txt"; //please make sure this file exists in your file system
using (StreamWriter sw = File.AppendText(path))
{ sw.WriteLine("Puzzle(" + x + "," + y + ") PC:" + PexSymbolicValue.GetPathConditionString() + " RET =" + PexSymbolicValue.ToString<int>(ret));
}
return ret;
}
}
=======
General debugging tips
Useful tips
Known issues (not resolved in Pex) and ways to get around
Fitnex
Fitnex usage can be found at:
http://social.msdn.microsoft.com/Forums/en-US/pex/thread/e523461a-9372-46b9-af45-faa26690cedc/
http://go4answers.webhost4life.com/Example/achieve-certain-branch-node-asap-48849.aspx
Let's consider the following example to see when Fitnex won't work.
void int m(int[] a)
{
int count = 0;
for (int i = 0; i < a.length; i++)
{
if (a[i] > 0}
count ++;
}
if (count > 1000)
return -1;
return count;
}
Fitnex will NOT help cover the branch "if (count > 1000)". The reason is that count is not symbolic but a constant. Therefore, this branch is not flippable. Fitnex or any other Pex strategies would prioritize only the flippable branching nodes.
To fix this issue, you could replace the line of "int count = 0;" as
int count = PexChoose.Value<int>("count");
PexAssume.IsTrue(count == 0);
In this way, count is turned symbolic and then "if (count > 1000)" is flippable.
It seems that the latest version of Pex doesn't allow us to specify to use only Fitnex strategy. So the default Pex search strategy would be a combination of the Fitnex strategy and other strategies. So in the example below, you don't see that Pex would solely aggressively increase the loop bound or flip to the true branch in the loop body. In addition, when Pex flips the loop bound, it is not increment by 1 but by some increasing integer values.
Making constant inputs symbolic:
I found an API call, PexSymbolicValue.Internal.LoopVar<T>(string,T);
This method makes a variable of type T symbolic.
See the adjusted test case:
[PexMethod]
public void PuzzleTestThrowsPexAssertFailedException321(){
int input = PexSymbolicValue.Internal.LoopVar<int>("xPut", 4);
this.PuzzleTest(input); }
The output is as expected, a sequence of predicates for the PCs