Skip to content

Commit

Permalink
Merge pull request #265 from krymtkts:feature/fscheck
Browse files Browse the repository at this point in the history
Replace basic tests with property-based tests.
  • Loading branch information
krymtkts authored Dec 7, 2024
2 parents 9cb71c1 + bf422d0 commit 165b2bf
Showing 1 changed file with 113 additions and 108 deletions.
221 changes: 113 additions & 108 deletions src/pocof.Test/Data.fs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ module PocofTest.Data

open System
open Microsoft.FSharp.Reflection
open System.Collections.Generic

open Xunit
open FsUnitTyped
Expand Down Expand Up @@ -48,7 +49,7 @@ module unwrap =
|> Gen.map (DictionaryEntry >> Entry.Dict)

type EntryPSObject =
static member Double() = psObjectGen |> Arb.fromGen
static member Generate() = psObjectGen |> Arb.fromGen

[<Property(Arbitrary = [| typeof<EntryPSObject> |], EndSize = 1000)>]
let ``should return PSObject sequence.`` (data: Entry list) =
Expand All @@ -64,7 +65,7 @@ module unwrap =
|> Prop.collect (List.length data)

type EntryDictionaryEntry =
static member Double() = dictionaryEntryGen |> Arb.fromGen
static member Generate() = dictionaryEntryGen |> Arb.fromGen

[<Property(Arbitrary = [| typeof<EntryDictionaryEntry> |], EndSize = 1000)>]
let ``should return DictionaryEntry sequence.`` (data: Entry list) =
Expand Down Expand Up @@ -109,137 +110,141 @@ module unwrap =
|> List.length
)

module ``Action fromString`` =
[<Fact>]
let ``should return Error Unknown.`` () =
Action.fromString "XXX" |> shouldEqual (Error "Unknown Action 'XXX'.")
let randomCase (s: string) =
let random = Random()

[<Fact>]
let ``should return Error when AddQuery.`` () =
Action.fromString "AddQuery" |> shouldEqual (Error "Unknown Action 'AddQuery'.")
s
|> Seq.map (fun c ->
if random.Next(2) = 0 then
Char.ToLower(c)
else
Char.ToUpper(c))
|> Seq.toArray
|> String

[<Fact>]
let ``should return known actions excluding AddQuery.`` () =
FSharpType.GetUnionCases(typeof<Action>)
|> Seq.filter (fun a -> a.Name <> "AddQuery")
|> Seq.iter (fun a ->
[ a.Name; String.lower a.Name; String.upper a.Name ]
|> List.map Action.fromString
|> List.iter (shouldEqual (Ok(FSharpValue.MakeUnion(a, [||]) :?> Action))))
let duNames<'U> = FSharpType.GetUnionCases(typeof<'U>) |> Seq.map _.Name

module fromString =
let ``Error Unknown.``<'a> (fromString: string -> 'a) =
shouldFail (fun () -> fromString "Unknown" |> ignore)
let randomCases (name: string) =
[ name; String.lower name; String.upper name; randomCase name ]

let ``known matchers.``<'a> (fromString: string -> 'a) =
FSharpType.GetUnionCases(typeof<'a>)
|> Seq.iter (fun (a: UnionCaseInfo) ->
[ a.Name; String.lower a.Name; String.upper a.Name ]
|> List.map fromString
|> List.iter (shouldEqual (FSharpValue.MakeUnion(a, [||]) :?> 'a)))
let toIgnoreCaseSet (x: string seq) =
HashSet(x, StringComparer.InvariantCultureIgnoreCase)

module ``of Matcher`` =
[<Fact>]
let ``should return Error Unknown.`` () =
``Error Unknown.``<Matcher> Matcher.fromString
let generateStringExclude (exclude: string seq) =
let excludeSet = exclude |> toIgnoreCaseSet

[<Fact>]
let ``should return known matchers.`` () =
``known matchers.``<Matcher> Matcher.fromString
ArbMap.defaults
|> ArbMap.generate<string>
|> Gen.filter (excludeSet.Contains >> not)

module ``of Operator`` =
[<Fact>]
let ``should return Error Unknown.`` () =
``Error Unknown.``<Operator> Operator.fromString
let generateStringFromDu<'DU> (exclude: string seq) =
let excludeSet = exclude |> toIgnoreCaseSet

[<Fact>]
let ``should return known matchers.`` () =
``known matchers.``<Operator> Operator.fromString
ArbMap.defaults
|> ArbMap.generate<string>
|> Gen.filter (excludeSet.Contains >> not)

module ``of Layout`` =
[<Fact>]
let ``should return Error Unknown.`` () =
``Error Unknown.``<Layout> Layout.fromString

[<Fact>]
let ``should return known matchers.`` () =
``known matchers.``<Layout> Layout.fromString

module ``QueryState toString`` =
let queryState (m: Matcher) (o: Operator) : QueryCondition =
{ Matcher = m
Operator = o
CaseSensitive = false
Invert = false }

let caseSensitive (s: QueryCondition) = { s with CaseSensitive = true }
let invert (s: QueryCondition) = { s with Invert = true }
let findDu<'DU> (name: string) =
FSharpType.GetUnionCases(typeof<'DU>)
|> Seq.find (fun a -> a.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
|> fun x -> (FSharpValue.MakeUnion(x, [||]) :?> 'DU)

[<Fact>]
let ``should return eq and`` () =
let actual = queryState Matcher.Eq Operator.And
string actual |> shouldEqual "eq and"

[<Fact>]
let ``should return cne or`` () =
let actual = queryState Matcher.Eq Operator.Or |> caseSensitive |> invert

string actual |> shouldEqual "cne or"
module ``Action fromString`` =
let actionsNames = duNames<Action> |> Seq.filter ((<>) "AddQuery")

[<Fact>]
let ``should return ceq and`` () =
let actual = queryState Matcher.Eq Operator.And |> caseSensitive
type UnknownAction() =
static member Generate() =
Gen.frequency
[ (1, randomCases "AddQuery" |> Gen.elements)
(9, actionsNames |> generateStringExclude) ]
|> Arb.fromGen

string actual |> shouldEqual "ceq and"
[<Property(Arbitrary = [| typeof<UnknownAction> |])>]
let ``should return unknown action error.`` (data: string) =
data
|> Action.fromString
|> shouldEqual (Error $"Unknown Action '{data}'.")
|> Prop.collect data

[<Fact>]
let ``should return ne or`` () =
let actual = queryState Matcher.Eq Operator.Or |> invert
string actual |> shouldEqual "ne or"
type KnownAction() =
static member Generate() =
actionsNames |> Seq.collect randomCases |> Gen.elements |> Arb.fromGen

[<Fact>]
let ``should return like and`` () =
let actual = queryState Matcher.Like Operator.And
string actual |> shouldEqual "like and"
[<Property(Arbitrary = [| typeof<KnownAction> |])>]
let ``should return known actions excluding AddQuery.`` (data: string) =
data
|> Action.fromString
|> shouldEqual (data |> findDu<Action> |> Ok)
|> Prop.collect data

[<Fact>]
let ``should return clike and`` () =
let actual = queryState Matcher.Like Operator.And |> caseSensitive
module fromString =
let ``should fail.``<'DU> (fromString: string -> 'DU) value =
shouldFail (fun () -> fromString value |> ignore)

string actual |> shouldEqual "clike and"
let ``known matchers.``<'DU> (fromString: string -> 'DU) (data: string) =
data |> fromString |> shouldEqual (data |> findDu<'DU>)

[<Fact>]
let ``should return notlike and`` () =
let actual = queryState Matcher.Like Operator.And |> invert
string actual |> shouldEqual "notlike and"
type InvalidDuName<'DU>() =
static member Generate() =
duNames<'DU> |> generateStringExclude |> Arb.fromGen

[<Fact>]
let ``should return cnotlike and`` () =
let actual = queryState Matcher.Like Operator.And |> caseSensitive |> invert
type ValidDuName<'DU>() =
static member Generate() =
duNames<'DU> |> Seq.collect randomCases |> Gen.elements |> Arb.fromGen

string actual |> shouldEqual "cnotlike and"
module ``of Matcher`` =
[<Property(Arbitrary = [| typeof<InvalidDuName<Matcher>> |])>]
let ``should fail when unknown value.`` (data: string) =
data |> ``should fail.`` Matcher.fromString

[<Fact>]
let ``should return cnotmatch or`` () =
let actual = queryState Matcher.Match Operator.Or |> caseSensitive |> invert
[<Property(Arbitrary = [| typeof<ValidDuName<Matcher>> |])>]
let ``should return known matchers.`` (data: string) =
data |> ``known matchers.`` Matcher.fromString |> Prop.collect data

string actual |> shouldEqual "cnotmatch or"
module ``of Operator`` =
[<Property(Arbitrary = [| typeof<InvalidDuName<Operator>> |])>]
let ``should fail when unknown value.`` (data: string) =
data |> ``should fail.`` Operator.fromString

[<Fact>]
let ``should return notmatch or`` () =
let actual = queryState Matcher.Match Operator.Or |> invert
string actual |> shouldEqual "notmatch or"
[<Property(Arbitrary = [| typeof<ValidDuName<Operator>> |])>]
let ``should return known matchers.`` (data: string) =
data |> ``known matchers.`` Operator.fromString |> Prop.collect data

[<Fact>]
let ``should return cmatch or`` () =
let actual = queryState Matcher.Match Operator.Or |> caseSensitive
module ``of Layout`` =
[<Property(Arbitrary = [| typeof<InvalidDuName<Layout>> |])>]
let ``should fail when unknown value.`` (data: string) =
data |> ``should fail.`` Layout.fromString

string actual |> shouldEqual "cmatch or"
[<Property(Arbitrary = [| typeof<ValidDuName<Layout>> |])>]
let ``should return known matchers.`` (data: string) =
data |> ``known matchers.`` Layout.fromString |> Prop.collect data

[<Fact>]
let ``should return match or`` () =
let actual = queryState Matcher.Match Operator.Or
string actual |> shouldEqual "match or"
module ``QueryState toString`` =
type QueryConditionGen() =
static member Generate() =
Gen.map4
(fun m o c i ->
{ Matcher = m
Operator = o
CaseSensitive = c
Invert = i })
(ArbMap.generate<Matcher> ArbMap.defaults)
(ArbMap.generate<Operator> ArbMap.defaults)
(ArbMap.generate<bool> ArbMap.defaults)
(ArbMap.generate<bool> ArbMap.defaults)
|> Arb.fromGen

[<Property(Arbitrary = [| typeof<QueryConditionGen> |])>]
let ``should return correct format.`` (data: QueryCondition) =
let c = if data.CaseSensitive then "c" else ""

let m =
match data.Matcher with
| Matcher.Eq -> if data.Invert then "ne" else "eq"
| Matcher.Like -> $"""{if data.Invert then "not" else ""}like"""
| Matcher.Match -> $"""{if data.Invert then "not" else ""}match"""

data |> string |> shouldEqual $"{c}{m} {data.Operator}" |> Prop.collect data

module initConfig =
[<Fact>]
Expand Down

0 comments on commit 165b2bf

Please sign in to comment.