Skip to content

Commit

Permalink
Make PropertyAttribute include parameter types in the list of generat…
Browse files Browse the repository at this point in the history
…or type classes. Add C# test project (with test for this PropertyAttribute change).
  • Loading branch information
fsoikin committed Jun 8, 2015
1 parent b6a9342 commit c902648
Show file tree
Hide file tree
Showing 7 changed files with 200 additions and 10 deletions.
2 changes: 2 additions & 0 deletions FsCheck-mono.sln
Expand Up @@ -37,6 +37,8 @@ Project("{4925A630-B079-445d-BCD4-3A9C94FE9307}") = "FsCheck.NUnit", "src\FsChec
EndProject
Project("{4925A630-B079-445d-BCD4-3A9C94FE9307}") = "FsCheck.NUnit.Addin", "src\FsCheck.NUnit.Addin\FsCheck.NUnit.Addin.fsproj", "{BF24DE38-C529-4638-BE50-A937EA3E6876}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FsCheck.CS.Test", "tests\FsCheck.CS.Test\FsCheck.CS.Test.csproj", "{47C30E3A-4E89-45E9-84A1-116F9DC1CEF4}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{6B36173B-D4E0-4CF4-840D-3F3016653F85}"
ProjectSection(SolutionItems) = preProject
Docs\content\index.fsx = Docs\content\index.fsx
Expand Down
7 changes: 7 additions & 0 deletions FsCheck.sln
Expand Up @@ -10,6 +10,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".paket", ".paket", "{0B8832
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{3162219D-7729-49C4-849A-9CCD889798DB}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
build.fsx = build.fsx
Contributors.txt = Contributors.txt
FsCheck Release Notes.md = FsCheck Release Notes.md
Expand Down Expand Up @@ -68,6 +69,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FsCheck.XUnit.CSharpExample
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSharp.DocSnippets", "docs\csharp\CSharp.DocSnippets.csproj", "{6AB84678-E55F-4387-A040-67312735D089}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FsCheck.CS.Test", "tests\FsCheck.CS.Test\FsCheck.CS.Test.csproj", "{47C30E3A-4E89-45E9-84A1-116F9DC1CEF4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -122,6 +125,10 @@ Global
{6AB84678-E55F-4387-A040-67312735D089}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AB84678-E55F-4387-A040-67312735D089}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AB84678-E55F-4387-A040-67312735D089}.Release|Any CPU.Build.0 = Release|Any CPU
{47C30E3A-4E89-45E9-84A1-116F9DC1CEF4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{47C30E3A-4E89-45E9-84A1-116F9DC1CEF4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{47C30E3A-4E89-45E9-84A1-116F9DC1CEF4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{47C30E3A-4E89-45E9-84A1-116F9DC1CEF4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
46 changes: 36 additions & 10 deletions src/FsCheck.Xunit/PropertyAttribute.fs
Expand Up @@ -47,6 +47,7 @@ type PropertyAttribute() =
let mutable verbose = false
let mutable quietOnSuccess = false
let mutable arbitrary = Config.Default.Arbitrary |> List.toArray

///If set, the seed to use to start testing. Allows reproduction of previous runs. You can just paste
///the tuple from the output window, e.g. 12344,12312 or (123,123).
member x.Replay with get() = match replay with None -> String.Empty | Some (Random.StdGen (x,y)) -> sprintf "%A" (x,y)
Expand All @@ -69,7 +70,7 @@ type PropertyAttribute() =
member x.Verbose with get() = verbose and set(v) = verbose <- v
///The Arbitrary instances to use for this test method. The Arbitrary instances
///are merged in back to front order i.e. instances for the same generated type
//at the front of the array will override those at the back.
///at the front of the array will override those at the back.
member x.Arbitrary with get() = arbitrary and set(v) = arbitrary <- v
///If set, suppresses the output from the test if the test is successful. This can be useful when running tests
///with TestDriven.net, because TestDriven.net pops up the Output window in Visual Studio if a test fails; thus,
Expand All @@ -79,18 +80,43 @@ type PropertyAttribute() =
///test failures.
member x.QuietOnSuccess with get() = quietOnSuccess and set(v) = quietOnSuccess <- v

member this.discoverArbitraries (methodInfo: IMethodInfo) =
let arbitrariesFromParameters =
let hasStaticArbMethod (t: Type) =
t.GetMethods( Reflection.BindingFlags.Static ||| Reflection.BindingFlags.Public )
|> Seq.exists (fun m -> m.ReturnType.IsGenericType && m.ReturnType.GetGenericTypeDefinition() = typedefof<Arbitrary<_>>)

let unwrapType (t: Type) =
if t.IsArray then t.GetElementType()
else if
t.IsGenericType &&
typeof<System.Collections.IEnumerable>.IsAssignableFrom( t ) &&
t.GenericTypeArguments.Length = 1
then t.GenericTypeArguments.[0]
else t

methodInfo.MethodInfo.GetParameters()
|> Seq.map (fun p -> unwrapType p.ParameterType)
|> Seq.filter (fun t -> not <| t.Namespace.StartsWith( "System.") ) // Heuristic to cut off system types
|> Seq.filter hasStaticArbMethod

let arbitrariesFromDeclaringType =
methodInfo.Class.Type
|> Seq.unfold (fun t -> if t <> null then Some(t,t.DeclaringType) else None)
|> Seq.map (fun t -> t.GetCustomAttributes(typeof<ArbitraryAttribute>, true))
|> Seq.filter (fun attr -> attr.Length = 1)
|> Seq.collect (fun attr -> (attr.[0] :?> ArbitraryAttribute).Arbitrary)

arbitrariesFromDeclaringType
|> Seq.append this.Arbitrary
|> Seq.append arbitrariesFromParameters
|> Seq.toList

override this.EnumerateTestCommands(methodInfo:IMethodInfo) :seq<ITestCommand> =
{ new TestCommand(methodInfo, null, 0) with
override x.Execute(testClass:obj) : MethodResult =
let xunitRunner = XunitRunner()
let arbitraries =
methodInfo.Class.Type
|> Seq.unfold (fun t -> if t <> null then Some(t,t.DeclaringType) else None)
|> Seq.map (fun t -> t.GetCustomAttributes(typeof<ArbitraryAttribute>, true))
|> Seq.filter (fun attr -> attr.Length = 1)
|> Seq.collect (fun attr -> (attr.[0] :?> ArbitraryAttribute).Arbitrary)
|> Seq.append this.Arbitrary
|> Seq.toList

let config =
{Config.Default with
Replay = this.ReplayStdGen
Expand All @@ -100,7 +126,7 @@ type PropertyAttribute() =
EndSize = this.EndSize
Every = if this.Verbose then Config.Verbose.Every else Config.Quick.Every
EveryShrink = if this.Verbose then Config.Verbose.EveryShrink else Config.Quick.EveryShrink
Arbitrary = arbitraries
Arbitrary = this.discoverArbitraries methodInfo
Runner = xunitRunner
}
Check.Method(config, methodInfo.MethodInfo,?target=if testClass <> null then Some testClass else None)
Expand Down
110 changes: 110 additions & 0 deletions tests/FsCheck.CS.Test/FsCheck.CS.Test.csproj
@@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{47C30E3A-4E89-45E9-84A1-116F9DC1CEF4}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FsCheck.Test</RootNamespace>
<AssemblyName>FsCheck.CS.Test</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
<Reference Include="FSharp.Core, Version=4.3.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<Private>True</Private>
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="PropertyAttributeTest.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<None Include="paket.references" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\FsCheck.Xunit\FsCheck.Xunit.fsproj">
<Project>{26a87ed0-9836-45f6-a88e-603da8fcd856}</Project>
<Name>FsCheck.Xunit</Name>
</ProjectReference>
<ProjectReference Include="..\..\src\FsCheck\FsCheck.fsproj">
<Project>{32aa3276-bba1-41ce-b78a-706b8454ec8d}</Project>
<Name>FsCheck</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
<Service Include="{82A7F48D-3B50-4B1E-B82E-3ADA8210C358}" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
<Choose>
<When Condition="($(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v2.0' Or $(TargetFrameworkVersion) == 'v3.0' Or $(TargetFrameworkVersion) == 'v3.5' Or $(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.1' Or $(TargetFrameworkVersion) == 'v4.5.2' Or $(TargetFrameworkVersion) == 'v4.5.3')) Or ($(TargetFrameworkIdentifier) == 'MonoAndroid') Or ($(TargetFrameworkIdentifier) == 'MonoTouch')">
<ItemGroup>
<Reference Include="xunit">
<HintPath>..\..\packages\xunit\lib\net20\xunit.dll</HintPath>
<Private>True</Private>
<Paket>True</Paket>
</Reference>
</ItemGroup>
</When>
</Choose>
<Choose>
<When Condition="$(TargetFrameworkIdentifier) == '.NETCore'">
<PropertyGroup>
<__paket__xunit_runner_visualstudio_props>win8\xunit.runner.visualstudio</__paket__xunit_runner_visualstudio_props>
<__paket__xunit_runner_visualstudio_targets>win8\xunit.runner.visualstudio</__paket__xunit_runner_visualstudio_targets>
</PropertyGroup>
</When>
<When Condition="$(TargetFrameworkIdentifier) == 'WindowsPhoneApp'">
<PropertyGroup>
<__paket__xunit_runner_visualstudio_props>wpa81\xunit.runner.visualstudio</__paket__xunit_runner_visualstudio_props>
<__paket__xunit_runner_visualstudio_targets>wpa81\xunit.runner.visualstudio</__paket__xunit_runner_visualstudio_targets>
</PropertyGroup>
</When>
<When Condition="($(TargetFrameworkIdentifier) == '.NETFramework' And ($(TargetFrameworkVersion) == 'v2.0' Or $(TargetFrameworkVersion) == 'v3.0' Or $(TargetFrameworkVersion) == 'v3.5' Or $(TargetFrameworkVersion) == 'v4.0' Or $(TargetFrameworkVersion) == 'v4.5' Or $(TargetFrameworkVersion) == 'v4.5.1' Or $(TargetFrameworkVersion) == 'v4.5.2' Or $(TargetFrameworkVersion) == 'v4.5.3')) Or ($(TargetFrameworkIdentifier) == 'MonoAndroid') Or ($(TargetFrameworkIdentifier) == 'MonoTouch')">
<PropertyGroup>
<__paket__xunit_runner_visualstudio_props>net20\xunit.runner.visualstudio</__paket__xunit_runner_visualstudio_props>
</PropertyGroup>
</When>
<When Condition="($(TargetFrameworkIdentifier) == 'WindowsPhone' And ($(TargetFrameworkVersion) == 'v8.0' Or $(TargetFrameworkVersion) == 'v8.1')) Or ($(TargetFrameworkProfile) == 'Profile7') Or ($(TargetFrameworkProfile) == 'Profile31') Or ($(TargetFrameworkProfile) == 'Profile32') Or ($(TargetFrameworkProfile) == 'Profile44') Or ($(TargetFrameworkProfile) == 'Profile49') Or ($(TargetFrameworkProfile) == 'Profile78') Or ($(TargetFrameworkProfile) == 'Profile84') Or ($(TargetFrameworkProfile) == 'Profile111') Or ($(TargetFrameworkProfile) == 'Profile151') Or ($(TargetFrameworkProfile) == 'Profile157') Or ($(TargetFrameworkProfile) == 'Profile259')">
<PropertyGroup>
<__paket__xunit_runner_visualstudio_props>portable-net45+aspnetcore50+win+wpa81+wp80+monotouch+monoandroid\xunit.runner.visualstudio</__paket__xunit_runner_visualstudio_props>
</PropertyGroup>
</When>
</Choose>
<Import Project="..\..\packages\xunit.runner.visualstudio\build\$(__paket__xunit_runner_visualstudio_props).props" Condition="Exists('..\..\packages\xunit.runner.visualstudio\build\$(__paket__xunit_runner_visualstudio_props).props')" Label="Paket" />
<Import Project="..\..\packages\xunit.runner.visualstudio\build\$(__paket__xunit_runner_visualstudio_targets).targets" Condition="Exists('..\..\packages\xunit.runner.visualstudio\build\$(__paket__xunit_runner_visualstudio_targets).targets')" Label="Paket" />
</Project>
5 changes: 5 additions & 0 deletions tests/FsCheck.CS.Test/Properties/AssemblyInfo.cs
@@ -0,0 +1,5 @@
using System.Reflection;

[assembly: AssemblyTitle( "FsCheck.Test.CS" )]
[assembly: AssemblyDescription( "FsCheck is a tool for testing .NET programs automatically using randomly generated test cases." )]
[assembly: AssemblyProduct( "FsCheck" )]
38 changes: 38 additions & 0 deletions tests/FsCheck.CS.Test/PropertyAttributeTest.cs
@@ -0,0 +1,38 @@
using System.Collections.Generic;
using System.Linq;
using FsCheck.Xunit;
using Xunit;

namespace FsCheck.Test
{
public class PropertyAttributeTest {

[Property]
public void automatically_discover_Arbitrary_instances_from_parameter_types(
SomeType arg1, AnotherType arg2, SomeType[] arg3, List<AnotherType> arg4,
int primitiveArg, string anotherPrimitiveArg )
{
// Trivial checks here.
// The whole test is to see if FsCheck finds SomeType.MakeArb() and AnotherType.MakeArb()
// and uses them to construct values for parameters.
Assert.Equal( 42, arg1.X );
Assert.Equal( "Bowties are cool", arg2.Y );
Assert.True( arg3.All( a => a.X == 42 ) );
Assert.True( arg4.All( a => a.Y == "Bowties are cool" ) );
}

public class SomeType
{
public int X;

public static Arbitrary<SomeType> MakeArb() { return Gen.Constant( new SomeType { X = 42 } ).ToArbitrary(); }
}

public class AnotherType
{
public string Y;

public static Arbitrary<AnotherType> MakeArb() { return Gen.Constant( new AnotherType { Y = "Bowties are cool" } ).ToArbitrary(); }
}
}
}
2 changes: 2 additions & 0 deletions tests/FsCheck.CS.Test/paket.references
@@ -0,0 +1,2 @@
xunit
xunit.runner.visualstudio

0 comments on commit c902648

Please sign in to comment.