Coder Social home page Coder Social logo

swiftcheck's Introduction

Carthage compatible Build Status Gitter chat

SwiftCheck

QuickCheck for Swift.

For those already familiar with the Haskell library, check out the source. For everybody else, see the Tutorial Playground for a beginner-level introduction to the major concepts and use-cases of this library.

Introduction

SwiftCheck is a testing library that automatically generates random data for testing of program properties. A property is a particular facet of an algorithm or data structure that must be invariant under a given set of input data, basically an XCTAssert on steroids. Where before all we could do was define methods prefixed by test and assert, SwiftCheck allows program properties and tests to be treated like data.

To define a program property the forAll quantifier is used with a type signature like (A, B, C, ... Z) -> Testable where A : Arbitrary, B : Arbitrary ... Z : Arbitrary. SwiftCheck implements the Arbitrary protocol for most Swift Standard Library types and implements the Testable protocol for Bool and several other related types. For example, if we wanted to test the property that every Integer is equal to itself, we would express it as such:

func testAll() {
    // 'property' notation allows us to name our tests.  This becomes important
    // when they fail and SwiftCheck reports it in the console.
    property("Integer Equality is Reflexive") <- forAll { (i : Int) in
        return i == i
    }
}

For a less contrived example, here is a program property that tests whether Array identity holds under double reversal:

property("The reverse of the reverse of an array is that array") <- forAll { (xs : [Int]) in
    // This property is using a number of SwiftCheck's more interesting 
    // features.  `^&&^` is the conjunction operator for properties that turns
    // both properties into a larger property that only holds when both sub-properties
    // hold.  `<?>` is the labelling operator allowing us to name each sub-part
    // in output generated by SwiftCheck.  For example, this property reports:
    //
    // *** Passed 100 tests
    // (100% , Right identity, Left identity)
    return
        (xs.reversed().reversed() == xs) <?> "Left identity"
        ^&&^
        (xs == xs.reversed().reversed()) <?> "Right identity"
}

Because SwiftCheck doesn't require tests to return Bool, just Testable, we can produce tests for complex properties with ease:

property("Shrunken lists of integers always contain [] or [0]") <- forAll { (l : [Int]) in
    // Here we use the Implication Operator `==>` to define a precondition for
    // this test.  If the precondition fails the test is discarded.  If it holds
    // the test proceeds.
    return (!l.isEmpty && l != [0]) ==> {
        let ls = self.shrinkArbitrary(l)
        return (ls.filter({ $0 == [] || $0 == [0] }).count >= 1)
    }
}

Properties can even depend on other properties:

property("Gen.one(of:) multiple generators picks only given generators") <- forAll { (n1 : Int, n2 : Int) in
    let g1 = Gen.pure(n1)
    let g2 = Gen.pure(n2)
    // Here we give `forAll` an explicit generator.  Before SwiftCheck was using
    // the types of variables involved in the property to create an implicit
    // Generator behind the scenes.
    return forAll(Gen.one(of: [g1, g2])) { $0 == n1 || $0 == n2 }
}

All you have to figure out is what to test. SwiftCheck will handle the rest.

Shrinking

What makes QuickCheck unique is the notion of shrinking test cases. When fuzz testing with arbitrary data, rather than simply halt on a failing test, SwiftCheck will begin whittling the data that causes the test to fail down to a minimal counterexample.

For example, the following function uses the Sieve of Eratosthenes to generate a list of primes less than some n:

/// The Sieve of Eratosthenes:
///
/// To find all the prime numbers less than or equal to a given integer n:
///    - let l = [2...n]
///    - let p = 2
///    - for i in [(2 * p) through n by p] {
///          mark l[i]
///      }
///    - Remaining indices of unmarked numbers are primes
func sieve(_ n : Int) -> [Int] {
    if n <= 1 {
        return []
    }

    var marked : [Bool] = (0...n).map { _ in false }
    marked[0] = true
    marked[1] = true

    for p in 2..<n {
        for i in stride(from: 2 * p, to: n, by: p) {
            marked[i] = true
        }
    }

    var primes : [Int] = []
    for (t, i) in zip(marked, 0...n) {
        if !t {
            primes.append(i)
        }
    }
    return primes
}

/// Short and sweet check if a number is prime by enumerating from 2...⌈√(x)⌉ and checking 
/// for a nonzero modulus.
func isPrime(n : Int) -> Bool {
    if n == 0 || n == 1 {
        return false
    } else if n == 2 {
        return true
    }
    
    let max = Int(ceil(sqrt(Double(n))))
    for i in 2...max {
        if n % i == 0 {
            return false
        }
    }
    return true
}

We would like to test whether our sieve works properly, so we run it through SwiftCheck with the following property:

import SwiftCheck

property("All Prime") <- forAll { (n : Int) in
    return sieve(n).filter(isPrime) == sieve(n)
}

Which produces the following in our testing log:

Test Case '-[SwiftCheckTests.PrimeSpec testAll]' started.
*** Failed! Falsifiable (after 10 tests):
4

Indicating that our sieve has failed on the input number 4. A quick look back at the comments describing the sieve reveals the mistake immediately:

- for i in stride(from: 2 * p, to: n, by: p) {
+ for i in stride(from: 2 * p, through: n, by: p) {

Running SwiftCheck again reports a successful sieve of all 100 random cases:

*** Passed 100 tests

Custom Types

SwiftCheck implements random generation for most of the types in the Swift Standard Library. Any custom types that wish to take part in testing must conform to the included Arbitrary protocol. For the majority of types, this means providing a custom means of generating random data and shrinking down to an empty array.

For example:

import SwiftCheck
 
public struct ArbitraryFoo {
    let x : Int
    let y : Int

    public var description : String {
        return "Arbitrary Foo!"
    }
}

extension ArbitraryFoo : Arbitrary {
    public static var arbitrary : Gen<ArbitraryFoo> {
        return Gen<(Int, Int)>.zip(Int.arbitrary, Int.arbitrary).map(ArbitraryFoo.init)
    }
}

class SimpleSpec : XCTestCase {
    func testAll() {
        property("ArbitraryFoo Properties are Reflexive") <- forAll { (i : ArbitraryFoo) in
            return i.x == i.x && i.y == i.y
        }
    }
}

There's also a Gen.compose method which allows you to procedurally compose values from multiple generators to construct instances of a type:

public static var arbitrary : Gen<MyClass> {
    return Gen<MyClass>.compose { c in
        return MyClass(
            // Use the nullary method to get an `arbitrary` value.
            a: c.generate(),

            // or pass a custom generator
            b: c.generate(Bool.suchThat { $0 == false }),

            // .. and so on, for as many values and types as you need.
            c: c.generate(), ...
        )
    }
}

Gen.compose can also be used with types that can only be customized with setters:

public struct ArbitraryMutableFoo : Arbitrary {
    var a: Int8
    var b: Int16
    
    public init() {
        a = 0
        b = 0
    }
    
    public static var arbitrary: Gen<ArbitraryMutableFoo> {
        return Gen.compose { c in
            var foo = ArbitraryMutableFoo()
            foo.a = c.generate()
            foo.b = c.generate()
            return foo
        }
    }
}

For everything else, SwiftCheck defines a number of combinators to make working with custom generators as simple as possible:

let onlyEven = Int.arbitrary.suchThat { $0 % 2 == 0 }

let vowels = Gen.fromElements(of: [ "A", "E", "I", "O", "U" ])

let randomHexValue = Gen<UInt>.choose((0, 15))

let uppers = Gen<Character>.fromElements(in: "A"..."Z")
let lowers = Gen<Character>.fromElements(in: "a"..."z")
let numbers = Gen<Character>.fromElements(in: "0"..."9")
 
/// This generator will generate `.none` 1/4 of the time and an arbitrary
/// `.some` 3/4 of the time
let weightedOptionals = Gen<Int?>.frequency([
    (1, Gen<Int?>.pure(nil)),
    (3, Int.arbitrary.map(Optional.some))
])

For instances of many complex or "real world" generators, see ComplexSpec.swift.

System Requirements

SwiftCheck supports OS X 10.9+ and iOS 7.0+.

Setup

SwiftCheck can be included one of two ways:

Using The Swift Package Manager

  • Add SwiftCheck to your Package.swift file's dependencies section:
.package(url: "https://github.com/typelift/SwiftCheck.git", from: "0.8.1")

Using Carthage

  • Add SwiftCheck to your Cartfile
  • Run carthage update
  • Drag the relevant copy of SwiftCheck into your project.
  • Expand the Link Binary With Libraries phase
  • Click the + and add SwiftCheck
  • Click the + at the top left corner to add a Copy Files build phase
  • Set the directory to Frameworks
  • Click the + and add SwiftCheck

Using CocoaPods

  • Add our Pod to your podfile.
  • Run $ pod install in your project directory.

Framework

  • Drag SwiftCheck.xcodeproj into your project tree as a subproject
  • Under your project's Build Phases, expand Target Dependencies
  • Click the + and add SwiftCheck
  • Expand the Link Binary With Libraries phase
  • Click the + and add SwiftCheck
  • Click the + at the top left corner to add a Copy Files build phase
  • Set the directory to Frameworks
  • Click the + and add SwiftCheck

License

SwiftCheck is released under the MIT license.

swiftcheck's People

Contributors

adamkuipers avatar akashivskyy avatar bgerstle avatar bkase avatar brentleyjones avatar broomburgo avatar codafi avatar danj-stripe avatar dreymonde avatar dylanlukes avatar euandreh avatar filblue avatar gfontenot avatar grigorye avatar griotspeak avatar ikesyo avatar kouky avatar lilyball avatar martinhoeller avatar mpurland avatar pthariensflame avatar rabbitinspace avatar robrix avatar ryanbooker avatar samritchie avatar sebastiangrail avatar stnamco avatar tcldr avatar yusukehosonuma avatar zeta611 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swiftcheck's Issues

Assert in Failing Propositions

Not sure if we should fail immediately, fail once, or offer the option for both. But there has to be some way of asserting a failing proposition.

TupleOf Modifier Type

Hi,
I am unable to compose tuples with the ArrayOf modifier so:

forAll { (ids: ArrayOf<(ArbitraryID, ArbitraryID)>) ...

which causes compiler error

Type (ArbitraryID, ArbitraryID) does not conform to protocol Arbitrary

Should I attempt a TupleOf modifier type or is there a smarter way to go about this?

cheers,

Daniel

Fix the Array Shrinker

It's time for an intervention. Actually, it was time for an intervention 8 GB of ARC-induced list allocations ago.

Move to Swiftz

The Basis is no longer a viable platform to build out these kinds of libraries.

A modifier for pointer values

Manually managed primitives are unsafe; because we can't rely on user deallocating anything, make a Modifier type that acts as a smart pointer to do it for them.

XCTest reports successes even when erroring

Changing the ArrayOf modifiers nest test to return false fails, as it should:

*** Failed! Proposition: ArrayOf modifiers nest
Falsifiable (after 1 test):
[]

However, XCTest believes it has succeeded:

Test Case '-[SwiftCheckTests.ModifierSpec testModifiers]' passed (0.131 seconds).

Examples (from docs) fail to compile in Swift 2 (Xcode 7 beta 6)

A few examples from the documentation do not compile in Xcode 7 beta 6:

let vowels = Gen.elements(["A", "E", "I", "O", "U" ])

Generates the error: Argument for generic parameter "A" could not be inferred.

Also:

let weightedOptionals = Gen.frequency([
    (1, Gen.pure(OptionalOf(Optional<A>.None))),
    (3, liftM({ OptionalOf(Optional<A>.Some($0)) })(m1: Int.arbitrary))
])

Gives: Use of undeclared type 'A'.

The latter can be fixed up by changing A to Int (for example).

Case causes Xcode to crash

The following property is causing Xcode to crash (latest pull from master and using Xcode 7 GM):

property("generates arbitrary Int") <- forAll(Gen<Int>.choose((Int.min, Int.max))) { (i: Int) in
    return i >= Int.min && i <= Int.max
}

It looks like a BAD_INSTRUCTION but Xcode crashes quickly, so I only have an instant to see it.

Range on Gen<Int> is not very wide...

When running a simple test (contrived):

    property("generates arbitrary Int") <- forAll { (i: Int) in
        logger.info("int \(i)")
        return i == i
    }

Rather surprised that the output range is from in the approximate range of -77 to 90.

I would expect to have fairly wide range over the entire size of an Int (that is, -2,147,483,648 to 2,147,483,647, at least... larger on 64 bit I think).

I know I can write my own ("ArbitraryLargeInt" or something)... but it seems to me that the default behavior should be to supply values across the native range of the type in question.

Rejigger Testing Syntax

It's a huge pain, what we have right now, that you get a proposition back as a variable and explicitly have to QuickCheck it. What if, instead, we handled that for the user. Perhaps something like this:

quickCheck["Refl"] = forAll { (x : Int) in
    return x == x
}

In global scope so we bypass the XCTest'ing mechanism altogether. It also allows us to take a page from lagrangian and mix source and tests in the same file. All we have to do is just make testing a NOP when in release.

Only problem is Swift does these kinds of things lazily. I'm not sure how lazily, but I don't take it as a good sign that I appear to be the first one to have thought of this.

Bring Back the Operators

So we're in a quandary with Swift. If we want to keep the operators (and we really do, I'm tired of having to .bind things), we have to define them to the exact specification Swiftz and any-other user library uses. Any change of associativity, any change of precedence, and Swift invents 2 billion ambiguous operator declarations. Within TypeLift this is viable, but we cannot guarantee compatibility with other libraries and that seriously worries me.

Wanted: Some way to reproduce results

I just had a test failure (where I couldn't see the counterexample, see #115), but I can't reproduce it. It would be great if test failures would print the seed and if there was a trivial way of plugging that seed back into the test to reproduce that case.

SwiftCheck fails to build as Carthage dependency

I checked out SwiftCheck/swift-develop and it builds without issue as a standalone Xcode project. When I include Swiftx, which includes SwiftCheck, the build fails (A shell task failed with exit code 65) but I am unsure of the cause. The only error that I can find in the log is

~/Development/TonalKit/Carthage/Checkouts/Swiftx/Carthage/Checkouts/SwiftCheck/SwiftCheck/Test.swift:489:28: error: cannot invoke 'flatMap' with an argument list of type '((_) -> _)'
        let strings = s.collected.flatMap({ l in Array(l).map({ "," + $0.0 }).filter({ !$0.isEmpty }) })
                                  ^
~/Development/TonalKit/Carthage/Checkouts/Swiftx/Carthage/Checkouts/SwiftCheck/SwiftCheck/Test.swift:489:28: note: overloads for 'flatMap' exist with these partially matching parameter lists: (@noescape (Self.Generator.Element) -> S), (@noescape (Self.Generator.Element) -> T?)
        let strings = s.collected.flatMap({ l in Array(l).map({ "," + $0.0 }).filter({ !$0.isEmpty }) })

Though it seems to keep building past that and then stop on Rose.swift without explicitly providing an error.

Existential Quantification

Using the equivalence

∃ {x} . P x ≡ ¬  {x} . ¬ P x

Define the inverse of a property. From there, negate on the inside and outside and define existential quantification.

Confusing swift-2.0 compiler messages

Gen definitions often cause confusing compiler messages, e.g:

let attributeList : Gen<String> = 
    idStatement.proliferateNonEmpty().fmap { (s:[String]) in s.flatMap { $0 } }`

causes:
Error: Cannot convert value of type 'Gen<[_Element]>' (aka 'Gen<Array<Character>>') to specified type 'Gen<String>'

the documentation is excellent, but expanding the section handling swift-2.0's quirks would be awesome!

Remove (or reduce) dependency on Swiftz

I'd love to use SwiftCheck to do the property based testing for Runes. However, since both Runes and Swiftz define (slightly different) operators for map, I'm not able to. This is a bummer, since it seems like Swiftz is an internal dependency of SwiftCheck, and this is one place where that dependency is leaking out.

I filed a radar (22676864) about this same issue on Apple's side after a conversation with Joe Groff, but it also seems like it can be mitigated by reducing the dependency on Swiftz to what is absolutely necessary. In this case, by not pulling the operator definitions (which don't appear to be used in SwiftCheck, based on my search results) into SwiftCheck, both of our libraries would play nicely together.

An Applicator For Property Combinators

Having to write

property["foo"] = verbose(forAll { //
})

Is kind of ridiculous when we could instead be doing this

property["foo"] = forAll { //
} $ verbosely

Or this

property["foo"] = verbose $ forAll { //
}

Flipping the Combinators isn't hard, it's the operator we'd have to define that troubles me. There are too many interpretations of apply among the Swift community (I should know, I've written at least 2 of them). I don't want to tread on toes, nor do I want to define yet another random operator and deepen the DSL.

Halp?

Gen returning values outside of range

I have a short test that generates email addresses, and then feeds them to our UI. The UI then validates them against certain rules.

The problem is, the generators seem to be generating invalid data sets (meaning, Strings that they should not be generating). The rules do not include, for example, spaces and control characters. They also specify that the email should always contain a name, hostname, @, and a TLD.

But the tests generate data that fails on the following types of values:

09:54PM [Info] [default] [main] [TestEmailValidationRules.swift:147] spec() > generated '7_B.dt'
09:54PM [Info] [default] [main] [TestEmailValidationRules.swift:147] spec() > generated '[email protected]'
09:54PM [Info] [default] [main] [TestEmailValidationRules.swift:147] spec() > generated '[email protected]'
09:54PM [Info] [default] [main] [TestEmailValidationRules.swift:147] spec() > generated '.dA'
09:54PM [Info] [default] [main] [TestEmailValidationRules.swift:147] spec() > generated '.d
'

Here's the test case:

func wrap3(l : String) -> String -> String -> String -> String -> String {
    return { m in { m2 in { m3 in { r in l + m + m2 + m3 + r } } } }
}

fit("handle randomly generated addresses with Unicode") {
    let upperCase : Gen<Character>= Gen<Character>.fromElementsIn("A"..."Z")
    let lowerCase : Gen<Character> = Gen<Character>.fromElementsIn("a"..."z")
    let numeric : Gen<Character> = Gen<Character>.fromElementsIn("0"..."9")
    let alpha: Gen<Character> = Gen<Character>.oneOf([upperCase, lowerCase])
    let alphanumeric: Gen<Character> = Gen<Character>.oneOf([alpha, numeric])
    let latin: Gen<Character> = Gen<Character>.fromElementsIn("\u{00C0}"..."\u{02B8}")
    let allowedSymbols = Gen<Character>.fromElementsOf(["%", "+", "-", "_", "."])
    let special = Gen<Character>.fromElementsOf(["!", "#", "$", "%", "&", "'", "*", "+", "-", "/", "=", "?", "^", "_", "`", "{", "|", "}", "~", "."])

    let localEmail = Gen<Character>.oneOf([
        alphanumeric,
        allowedSymbols
        ]).proliferateNonEmpty().suchThat({ $0.count > 2 && $0.count < 25 }).fmap(String.init)

    let hostname = Gen<Character>.oneOf([
        alpha
        ]).proliferateNonEmpty().suchThat({ $0.count < 10 }).fmap(String.init)

    let tld = lower.proliferateNonEmpty().suchThat({ $0.count > 1 && $0.count < 4 }).fmap(String.init)

    let emailGen = wrap3 <^> localEmail <*> Gen.pure("@") <*> hostname <*> Gen.pure(".") <*> tld

    property("Unicode email addresses validate correctly") <- forAll(emailGen) { (e : String) in
        logger.info("generated '\(e)'")

            f.text = e
        return f.isValid
    }
}

Cocoapod?

Please? ;-)

Seriously, this would be a great pod.

What's the impact of SE-0002 (Removing currying `func` declaration syntax)?

https://github.com/apple/swift-evolution/blob/master/proposals/0002-remove-currying.md
I'm assuming this will be implemented in Swift 3.

Just curious if this has any impact at all.
Looking at the Tutorial.xcplayground, I'm guessing this function:

// Concatenates 5 strings together in order.
func glue5(l : String)(m : String)(m2 : String)(m3 : String)(r : String) -> String {
    return l + m + m2 + m3 + r
}

would need to be altered to something like this (?):

func glue5a(l: String) -> (String) -> (String) -> (String) -> (String) -> String {
    return { (m: String) -> (String) -> (String) -> (String) -> String in
            { (m2: String) -> (String) -> (String) -> String in
                { (m3: String) -> (String) -> String in
                    { (r: String) -> String in
                        return l + m + m2 + m3 + r
                    }
                }
            }
    }
}

Ugh.

Double random generator is not working correctly.

I made CGPoint extension for Arbitrary protocol.

extension CGPoint : Arbitrary {
  public static var creator: CGFloat -> CGFloat -> CGPoint {
    return { x in { y in CGPoint(x: x, y:  y) } }
  }
  public static var arbitrary: Gen<CGPoint> {
    let cgFloatGen = Double.arbitrary.fmap() { CGFloat($0) }
    return CGPoint.creator <^> cgFloatGen <*> cgFloatGen
  }
}

When I run simple test with it

func testCGPoint() {
  property("Dummy point printer") <-
    forAll { (p : CGPoint) in
      self.pointPrinter(p)
      return true
  }
}
func pointPrinter(p: CGPoint) {
  let s = NSString(format: "X: %012.3f; Y: %012.3f", p.x, p.y)
  print(s)
}

I got this printed:

X: 00000000.000; Y: 00000000.000
X: -0009825.713; Y: -0009825.713
X: -0019653.323; Y: -0019653.323
X: -0029480.934; Y: -0029480.934
X: -0039308.544; Y: -0039308.544
X: -0049136.154; Y: -0049136.154
X: -0058963.764; Y: -0058963.764
X: -0068791.374; Y: -0068791.374
X: -0078618.985; Y: -0078618.985
X: -0088446.595; Y: -0088446.595
X: -0098274.205; Y: -0098274.205
X: -0108101.815; Y: -0108101.815
X: -0117929.425; Y: -0117929.425
X: -0127757.035; Y: -0127757.035
X: -0137584.646; Y: -0137584.646
X: -0147412.256; Y: -0147412.256
X: -0157239.866; Y: -0157239.866
X: -0167067.476; Y: -0167067.476
X: -0176895.086; Y: -0176895.086
X: -0186722.697; Y: -0186722.697
X: -0196550.307; Y: -0196550.307
X: -0206377.917; Y: -0206377.917
X: -0216205.527; Y: -0216205.527
X: -0226033.137; Y: -0226033.137
X: -0235860.748; Y: -0235860.748
X: -0245688.358; Y: -0245688.358
X: -0255515.968; Y: -0255515.968
.................
.................
X: -0904138.240; Y: -0904138.240
X: -0913965.850; Y: -0913965.850
X: -0923793.460; Y: -0923793.460
X: -0933621.071; Y: -0933621.071
X: -0943448.681; Y: -0943448.681
X: -0953276.291; Y: -0953276.291
X: -0963103.901; Y: -0963103.901
X: -0972931.511; Y: -0972931.511
*** Passed 100 tests

So, firstly, all numbers (excluding first) are negative. Second, same x coordinate and y. (It passes fine p.x == p.y property)
After that, I thought, is it me being dumb or is it a bug? So, I wrote test for double.

func testDouble() {
  property("Dummy double printer") <-
    forAll { (d : Double) in
      let s = NSString(format: "F: %012.3f;", d)
      print(s)
      return true
  }
}

And got this:

D: 00000000.000;
D: -0006286.371;
D: -0012573.149;
D: -0018859.926;
D: -0025146.704;
D: -0031433.481;
D: -0037720.258;
D: -0044007.036;
D: -0050293.813;
D: -0056580.591;
D: -0062867.368;
D: -0069154.145;
D: -0075440.923;
D: -0081727.700;
D: -0088014.478;
D: -0094301.255;
D: -0100588.033;
D: -0106874.810;
D: -0113161.587;
D: -0119448.365;
D: -0125735.142;
D: -0132021.920;
D: -0138308.697;
D: -0144595.475;
D: -0150882.252;
...............
...............
D: -0572096.340;
D: -0578383.117;
D: -0584669.895;
D: -0590956.672;
D: -0597243.449;
D: -0603530.227;
D: -0609817.004;
D: -0616103.782;
D: -0622390.559;

So, again, all numbers are negative, and vary just a bit (is it just adding about -6287?). It is not expected from random double generator.
To investigate about pairs of double being same I wrote this:

public struct IntPair: Arbitrary {
  let a: Int
  let b: Int
  public static var arbitrary: Gen<IntPair> {
    return Int.arbitrary.bind()
      { a in Int.arbitrary.fmap() { b in IntPair.init(a: a, b: b) } }
  }
}
func testPairs() {
  property("Dummy IntPair printer") <-
    forAll { (p: IntPair) in
      print("A: \(p.a);\t B: \(p.b)")
      return true
  }
}

And I got

A: 0;    B: 0
A: 1;    B: 0
A: 0;    B: 0
A: -2;   B: 1
.........
.........
A: 82;   B: -90
A: 15;   B: -89
A: -12;  B: -26
A: 85;   B: 45
A: 92;   B: 76
A: 74;   B: 5
*** Passed 100 tests

Here everything seems to be OK. (All numbers are less than 100, but may be this is supposed to happen)
So, as I understood, problem here with Double generator.

Report on which sub-property failed

I may be doing something wrong, but when I use sub properties and one of them fails, all I get in the output are the inputs used and the name of the parent property. For example:

property("equtatable") <- forAll {
    (a: Int, b: Int) in

    let reflexive = EquatableTestUtilities.checkReflexive { Constant(a) } <?> "reflexive"
    let symmetric = EquatableTestUtilities.checkSymmetric { Constant(a) } <?> "symmetric"
    let transitive = EquatableTestUtilities.checkTransitive { Constant(a) } <?> "transitive"
    let notEquate = (a != b) ==> {
        EquatableTestUtilities.checkNotEquate(
            { Constant(b) },
            { Constant(b) }
        )
    }() <?> "not-equate"

    return reflexive ^&&^ symmetric ^&&^ transitive ^&&^ notEquate
}

will just say that "equatable" failed, instead of also telling me that "not-equate" failed:

*** Failed! Proposition: equtatable
Falsifiable (after 1 test):
0
1
*** Passed 0 tests

If I get it to go past 1 test, it looks even weirder, saying 100% of the sub-properties passed?:

*** Failed! Proposition: equtatable
Falsifiable (after 2 tests and 2 shrinks):
-2
0
*** Passed 1 tests
(100%, not-equate, transitive, symmetric, reflexive)

Basically, I feel it would be really valuable to know which sub-properties have failed. Sometimes custom arbitrary types are expensive to compute and it's easier to do many sub-properties off the same random values.

Carthage can’t build SwiftCheck-iOS scheme

It looks like the SwiftCheck-iOS scheme has the test target configured to build for the Analyze & Run actions. This causes build failures in carthage - “error: module 'SwiftCheck' was not compiled for testing”

Ambiguity?

Just tried the latest pull from the Swift develop branch.

This:

    static let username = Gen<Character>.oneOf([
        alphanumeric,
        allowedSymbols,
        latin
        ]).proliferateNonEmpty().suchThat({ $0.count > 2 && $0.count < 25 }).fmap(String.init)

Is generating a compiler error about Type of expression is ambiguous without more context (but it worked as of a few days ago). Intentional change?

Gen.chooseNum() aka proliferateSized(min, max)

It would be great to have an ability to build generators that generate a specific (but random) range. For example, taking from my Scala code:

def unicodeGenerator(generator: Gen[Char] = Gen.alphaChar, minimum: Int = 5, maximum: Int = 20): Gen[String] = Gen.chooseNum(minimum, maximum).flatMap { n =>
    Gen.buildableOfN[String, Char](n, generator)
}

The above generates a random series of Unicode between 5 and 20 characters. Another expression that's pretty useful:

val shortCodeGenerator: Gen[String] = Gen.chooseNum(3, 10).flatMap { n =>
    Gen.sequence[String, Char](List.fill(n)(Gen.alphaNumChar))
}

In this latter example, I'm creating an alphanumeric code that is 3 to 10 characters in length.

It would be very handy to be able to specify a range as in proliferateSized(min, max).

Provide the name of passed tests too?

ScalaCheck currently does this by default, but I suspect it's just an extra bit of fluff we'd be dumping to stdout rather than a proper feature for most people. Especially once #38 gets merged this will become less important.

Implement Fun

Need something more concrete that ArrowOf at the moment. Might even be prudent to deprecate it now.

Add `ListOf` Modifier

Since we can't yet quantify over Array<A : Arbitrary>, there may as well be a ListOf modifier that generates arbitrary lists for us.

Bring back `once`.

I disabled exhaustiveness checks for some reason and I need them back. Bad Past-Me, Bad!

Laz(ier)y Properties

Testable things made of blocks have to be executed at least once when they're bound to their respective properties. We can fix this with a lazy combinator.

Practical guide to using SwiftCheck with business logic?

Forgive the obtuse title – I'm looking for resources that might help me with using property-testing on larger parts of my app than "check that my sorting algorithm works" e.g.

  • verifying that adding and deleting objects from a database behaves correctly
  • verifying that user input to a view model is correctly handled

etc.

In short, I'm trying to get a feel for "Real World SwiftCheck" (or QuickCheck, for that matter). I'm also interested in how I can apply SwiftCheck to less functionally oriented code to achieve useful results.

For me, I'd consider this issue closed when I can see any or all of:

  • examples of SwiftCheck being used in an application
  • blog posts/tutorials
  • book recommendations for the analogous QuickCheck style frameworks in other languages

Thanks!

Real world examples badly needed in documentation

It would be incredibly helpful to see a few "real world" examples in the documentation. Things that we are trying to piece together...

  1. Generating 100 random email addresses (with Unicode!)
  2. Generating 100 randomized IPv6 address
  3. Generating 100 random first, middle, last name combinations
  4. Generating 100 random phone numbers (with country code and variable format: ##-####-### etc.)
  5. Generating a random phrase of pseudo-latin N words long
  6. Generate a binary data stream N bytes long

Error calling curried `forAll`

property("curried forAll") <- 
    forAll(Gen.pure("a"))(Gen.pure("b")) { (lhs: String, rhs: String) in 
        return lhs != rhs
    }

causes this error:
Cannot invoke 'forAll' with an argument list of type '(Gen<String>, (String, String) -> Bool)'

Am I calling this correctly? - I couldn't find any examples in SwiftCheck

Unroll Shrinker Loop

It's clear Swift won't make the necessary optimizations, so I figure introducing a little mutable state locally be an overall win.

Strange log output "falsifiable" even though tests pass

This test:

let p = CheckerArguments(replay: Optional.Some((standardRNG, 10)), maxAllowableSuccessfulTests: 100, maxAllowableDiscardedTests: 100, maxTestCaseSize: 1000)

property("does not generate negatives", arguments: p) <- forAll(Gen<UInt>.choose((0, 1000000))) { (i: UInt) in
    return i < 0
}.expectFailure

Generates this log output:

Test Case '-[GlimpulseTests.TestGenerators SwiftCheck__testing_basic_generators__changes_the_property_parameters_UserszbeckmanProjectsGlimpulseiOSprojectGlimpulseGlimpulseTestsTestGeneratorsswift_116]' started.
+++ OK, failed as expected. Proposition: does not generate negatives
Falsifiable (after 1 test and 20 shrinks):
0

The test does pass – but it's confusing to see what looks like a failure in the output. Clearly, it's not actually a falsifiable test with the value zero, but the output shows that it is...

CheckerArgument for minTests

Is it possible to use CheckerArguments to specify a minimum number of positive tests? I've been playing with it, e.g,

CheckerArguments(replay: Optional.Some((standardRNG, 10)), maxAllowableSuccessfulTests: 200, maxAllowableDiscardedTests: 100, maxTestCaseSize: 1000)

It seems we have control of the maximum parameters, but not minimum.

But the most common need we have is to increase the number of tests from 100. For example, we have some patterns that require a pretty wide range of testing before we can be "reasonably certain" that all conditions are passed. We'd like to be able to specify 1,000 test cases for these.

Other situations in which having more than 100 passing test cases make sense:

  1. Performance. Quite often we need to run more than 100 cases in order to obtain a reasonable baseline on performance checking.
  2. Volume oriented testing. For instance, does the database create 10,000 records without failing?
  3. Large data sets. Already mentioned: Situations where failure is only detectable on the order of 1 in a 1,000 cases.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.