packages feed

tinycheck-0.1.0.0: README.md

# tinycheck

A lightweight, **deterministic** property testing library for Haskell.

Instead of generating random inputs, `tinycheck` enumerates test cases from a
canonical, fairly-interleaved ordering.  Tests are reproducible, require no
seeds, and cover small values first — no shrinking required.

## Quick start

```haskell
import Test.Tasty
import Test.Tasty.TinyCheck

main :: IO ()
main = defaultMain $ testGroup "my suite"
  [ testProperty "reverse . reverse == id" $
      \(xs :: [Int]) -> reverse (reverse xs) == xs
  , testProperty "abs x >= 0" $
      \(x :: Int) -> abs x >= 0
  ]
```

Run with `cabal test`.

## How it works

The core type is `TestCases a` — a newtype over `[a]` whose `Semigroup`,
`Applicative`, and `Monad` instances use **fair interleaving** instead of
concatenation and cartesian product.

```
TestCases [1,2,3] <> TestCases [10,20,30]  ==  TestCases [1,10,2,20,3,30]
```

With infinite generators this ensures neither side is starved:

```
(Left <$> TestCases [1..]) <> (Right <$> TestCases [1..])
  ==  TestCases [Left 1, Right 1, Left 2, Right 2, ...]
```

`Applicative` interleaves function-argument pairs, so all parts of the input
space are explored immediately rather than exhausting one argument before
moving to the next.

Use `interleaveN` to interleave any number of generators fairly:

```
interleaveN [TestCases [1,2,3], TestCases [10,20,30], TestCases [100,200,300]]
  ==  TestCases [1,10,100, 2,20,200, 3,30,300]
```

## Defining generators

Implement `Arbitrary` for your types, or derive it via `Generically`:

```haskell
{-# LANGUAGE DeriveGeneric #-}
import GHC.Generics (Generic, Generically (..))
import Data.TestCases (Arbitrary)

data Colour = Red | Green | Blue
  deriving stock (Show, Generic)
  deriving (Arbitrary) via Generically Colour
```

Newtype wrappers are provided for common patterns:

| Wrapper | Suitable for |
|---|---|
| `SignedArbitrary` | `Num` + `Enum` (e.g. `Int`, `Integer`) |
| `BoundedArbitrary` | `Bounded` + `Enum` (e.g. `Bool`, `Word8`) |
| `RealFracArbitrary` | `Fractional` + `Enum` (e.g. `Float`, `Double`) |
| ... | ... |

## Preconditions

Use `==>` to skip inputs that don't satisfy a precondition:

```haskell
testProperty "n > 0 implies n * 2 > 0" $
  \(n :: Int) -> (n > 0) ==> property (n * 2 > 0)
```

# Development

## Modules

| Module | Purpose |
|---|---|
| `Data.TestCases` | Core `TestCases` type, `Arbitrary`, `CoArbitrary` |
| `Test.Tasty.TinyCheck` | Tasty integration (`testProperty`, `testPropertyWith`, …) |

### `examples/`

Two standalone examples for defining `Arbitrary` instances for your own types are provided here.

## AI usage

The central pieces are developed by a human, @turion.
Many details (tasty integration, boilerplate, test cases, longer docs) have been generated by AI
(Github Copilot with Claude Opus & Sonnet 4.6) and are thoroughly checked by myself.