QuickCheck, SmallCheck & Reach: Automated Testing in Haskell. Tom Shackell

Similar documents
Testing. Wouter Swierstra and Alejandro Serrano. Advanced functional programming - Lecture 2. [Faculty of Science Information and Computing Sciences]

Informatics 1 Functional Programming Lecture 11. Data Representation. Don Sannella University of Edinburgh

PROGRAMMING IN HASKELL. CS Chapter 6 - Recursive Functions

Shell CSCE 314 TAMU. Functions continued

Data Structures. Datatype. Data structure. Today: Two examples. A model of something that we want to represent in our program

Software System Design and Implementation

Informatics 1 Functional Programming Lecture 12. Data Abstraction. Don Sannella University of Edinburgh

Introduction to Programming: Lecture 6

How does ML deal with +?

Advanced features of Functional Programming (Haskell)

Monads and all that I. Monads. John Hughes Chalmers University/Quviq AB

Source-Based Trace Exploration Work in Progress

INTRODUCTION TO HASKELL

Haskell Introduction Lists Other Structures Data Structures. Haskell Introduction. Mark Snyder

SmallCheck and Lazy SmallCheck

Week 5 Tutorial Structural Induction

Haskell Overview II (2A) Young Won Lim 8/9/16

Logic - CM0845 Introduction to Haskell

QuickCheck: Beyond the Basics

Random Testing in PVS

Haskell Types, Classes, and Functions, Currying, and Polymorphism

Quick announcement. Midterm date is Wednesday Oct 24, 11-12pm.

1 The smallest free number

Lecture 19: Functions, Types and Data Structures in Haskell

Programming in Haskell Aug-Nov 2015

Papers we love - Utrecht

Haskell An Introduction

Reasoning about programs. Chapter 9 of Thompson

Module Title: Informatics 1 Functional Programming (first sitting) Exam Diet (Dec/April/Aug): December 2014 Brief notes on answers:

Static Contract Checking for Haskell

PROGRAMMING IN HASKELL. Chapter 5 - List Comprehensions

Haskell Overview III (3A) Young Won Lim 10/4/16

Advanced Topics in Programming Languages Lecture 2 - Introduction to Haskell

Some Advanced ML Features

Automatic Testing of Operation Invariance

Haskell: From Basic to Advanced. Part 2 Type Classes, Laziness, IO, Modules

Property-Based Testing for Coq. Cătălin Hrițcu

Programming in Haskell Aug-Nov 2015

From Types to Contracts

Introduction to ML. Mooly Sagiv. Cornell CS 3110 Data Structures and Functional Programming

Delayed Expressions Fall 2017 Discussion 9: November 8, Iterables and Iterators. For Loops. Other Iterable Uses

CPM: A Declarative Package Manager with Semantic Versioning

Stacks and queues (chapters 6.6, 15.1, 15.5)

Advanced Type System Features Tom Schrijvers. Leuven Haskell User Group

Haskell Overview II (2A) Young Won Lim 8/23/16

Informatics 1 Functional Programming Lecture 5. Function properties. Don Sannella University of Edinburgh

Introduction to Programming, Aug-Dec 2006

Extended Static Checking for Haskell (ESC/Haskell)

Imperative languages

Signature Inference for Functional Property Discovery

Background Type Classes (1B) Young Won Lim 6/14/18

Introduction to ML. Mooly Sagiv. Cornell CS 3110 Data Structures and Functional Programming

Programming Languages Fall 2013

Monad Background (3A) Young Won Lim 11/18/17

Haske k ll An introduction to Functional functional programming using Haskell Purely Lazy Example: QuickSort in Java Example: QuickSort in Haskell

CS 320: Concepts of Programming Languages

CSCE 314 TAMU Fall CSCE 314: Programming Languages Dr. Flemming Andersen. Haskell Functions

An introduction to functional programming. July 23, 2010

An introduction introduction to functional functional programming programming using usin Haskell

Combining Static and Dynamic Contract Checking for Curry

Tuples. CMSC 330: Organization of Programming Languages. Examples With Tuples. Another Example

CSC312 Principles of Programming Languages : Functional Programming Language. Copyright 2006 The McGraw-Hill Companies, Inc.

DerivingVia or, How to Turn Hand-Written Instances into an Anti-Pattern

Functional Programming and Haskell

Functional Logic Programming Language Curry

INFOB3TC Solutions for the Exam

The Caesar Cipher Informatics 1 Functional Programming: Tutorial 3

Introduction to Functional Programming in Haskell 1 / 56

Background. CMSC 330: Organization of Programming Languages. Useful Information on OCaml language. Dialects of ML. ML (Meta Language) Standard ML

TESTING WEB SERVICES WITH WEBDRIVER AND QUICKCHECK

Type Processing by Constraint Reasoning

Quiz Stuff. Use a full sheet of 8½x11" paper. (Half sheet? Half credit!) ...

It is better to have 100 functions operate one one data structure, than 10 functions on 10 data structures. A. Perlis

The List Datatype. CSc 372. Comparative Programming Languages. 6 : Haskell Lists. Department of Computer Science University of Arizona

Functional Programming in Haskell Part I : Basics

Principles of Programming Languages

Introduction to ML. Mooly Sagiv. Cornell CS 3110 Data Structures and Functional Programming

Programming with Universes, Generically

Programming Language Concepts, CS2104 Lecture 7

CS 457/557: Functional Languages

Programming with Math and Logic

CS 11 Haskell track: lecture 1

Lecture 8: Summary of Haskell course + Type Level Programming

CSci 450: Org. of Programming Languages Overloading and Type Classes

Foundations of Computation

GADTs. GADTs in Haskell

A general introduction to Functional Programming using Haskell

Programming Paradigms Written Exam (6 CPs)

Instances for Free* Neil Mitchell (* Postage and packaging charges may apply)

Practical Haskell. An introduction to functional programming. July 21, Practical Haskell. Juan Pedro Villa-Isaza. Introduction.

Optimising Functional Programming Languages. Max Bolingbroke, Cambridge University CPRG Lectures 2010

A Second Look At ML. Chapter Seven Modern Programming Languages, 2nd ed. 1

Programming Language Concepts: Lecture 14

The Haskell HOP: Higher-order Programming

Programming Languages 3. Definition and Proof by Induction

Solution sheet 1. Introduction. Exercise 1 - Types of values. Exercise 2 - Constructors

Informatics 1 Functional Programming Lecture 4. Lists and Recursion. Don Sannella University of Edinburgh

Type families and data kinds

Speculate: Discovering Conditional Equations and Inequalities about Black-Box Functions by Reasoning from Test Results

CSCE 314 Programming Languages

Transcription:

QuickCheck, SmallCheck & Reach: Automated Testing in Haskell By Tom Shackell

A Brief Introduction to Haskell Haskell is a purely functional language. Based on the idea of evaluation of mathematical functions rather than on manipulating state. Heavy emphasis on 'functions' and data-types. Higher-order functions. Passing functions to other functions. Very powerful static type system including typeinference.

Some Simple Examples... A program for calculating factorials: fac :: Int -> Int fac 0 = 1 fac n = n * fac (n - 1) And for calculating the length of a list: length :: [a] -> Int length [] = 0 length (x:xs) = 1 + length xs

Data Types A data type for colours data Colour = Red Green Blue For binary trees data Tree a = Empty Node a (Tree a) (Tree a)

Higher Order Functions A function to apply some function to every element of a list: map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = f x : map f xs for example map (*2) [3,5,9] = [6,10,18]

Class Polymorphism A function to tested whether a list contains a particular element: elem :: Eq a => a -> [a] -> Bool elem x [] = False elem x (y:ys) = x == y elem x ys

Classes The previous example makes use of the Eq class. class Eq a where (==) :: a -> a -> Bool (/=) :: a -> a -> Bool x /= y = not (x == y) We can define instances of this class to describe how to compare any data type for equality. instance Eq Colour where Red == Red = True Green == Green = True Blue == Blue = True _ == _ = False

QuickCheck

QuickCheck Developed by Koen Claessen and John Hughes. Based on the idea of specifying properties that the programmer expects to be true. These properties are then tested with a large number of random inputs. Properties are expressed in Haskell using combinators defined in the QuickCheck library.

Simple QuickCheck Properties We can express a property such as: prop_revrev :: [Int] -> Bool prop_revrev xs = reverse (reverse xs) == xs We can then ask QuickCheck to verify this property. Main> quickcheck prop_revrev OK: Passed 100 tests Or perhaps it fails. Falsifiable, after 12 tests: [3,5,1]

Conditionals We can specify conditional properties. prop_maxle :: Int -> Int -> Property prop_maxle x y = x <= y ==> max x y == y prop_ordinsert :: Int -> [Int] -> Property prop_ordinsert x xs = ordered xs ==> ordered (insert x xs) This second property has a problem...

Trivial Cases The condition in prop_ordinsert is quite restrictive. Most lists generated randomly are not ordered. Of those that are, shorter lists are a lot more likely that long ones. Our test function will likely only test very small cases.

Generators In fact it's better to define this property using a 'generator'. prop_ordinsert :: Int -> Property prop_ordinsert x = forall orderedlist $ \ xs -> ordered (insert x xs) The generator orderedlist generates a random ordered list of numbers.

Generator Instances We can tell QuickCheck how to generate random data of any type by providing an instance of the arbitrary class. class Arbitrary a where arbitrary :: Gen a for example instance Arbitrary Colour where arbitrary = elements [ Red, Green, Blue ]

Generator Functions We can also define generator functions such as orderedlist orderedlist :: (Arbitrary a, Ord a) => Gen [a] orderedlist = do xs <- arbitrary return (sort xs)

How QuickCheck is implemented QuickCheck is implemented as a Haskell library imported into the program rather than an external tool. quickcheck is a Haskell function that takes a function of any number of arguments. It generates random values for the arguments, passes them to the function and observes the result. Internally the implementation relies on some quite clever use of Haskell's class system.

QuickCheck Used for random testing. Easy to use as it's a standard Haskell library. Generally works best for simple data structures. Works best for properties with simple preconditions. Writing generators can be time consuming and tedious.

SmallCheck

SmallCheck Developed by Colin Runciman at York. Design is similar to QuickCheck. However the focus is on finding small counter examples through exhaustive search of the space. Uses a depth-bound on the size of data generated to control search space explosion.

Usage SmallCheck is used in a very similar way to QuickCheck prop_revrev :: [Bool] -> Bool prop_revrev xs = reverse (reverse xs) == xs Main> smallcheck 5 prop_revrev Completed 63 tests without failure Here we've checked all lists of Bools with length less than or equal to 5.

Existentials SmallCheck introduces the idea of existential quantification. prop_isprefix :: [Bool] -> [Bool] -> Property prop_isprefix xs ys = isprefix xs ys ==> exists $ \ zs -> ys == xs ++ zs SmallCheck will search exhaustively for a zs that matches the criteria specified.

SmallCheck Implemented in much the same way as QuickCheck but with exhaustive searching. Has issues with large search spaces. Still requires user to write generators.

Reach

Reach Developed by Matt Naylor and Colin Runciman Is based on the idea of trying to find an input that causes a target expression to be evaluated. Very much based around the idea of avoiding having to write 'generators'. Is implemented using constraint solving and Functional Logic Programming constructs. Like SmallCheck, makes use of a depth-bound.

Target Expressions In Reach the user specifies a target expression that they would like to be executed. This is done using the target function. f xs ys = if ordered xs && ordered ys then target (xs ++ ys) else... Reach will then search for an input to the function f that causes target to be evaluated.

Property Testing This can be used to test properties just like in QuickCheck or SmallCheck. prop_revrev :: [Int] -> Bool prop_revrev xs = reverse (reverse xs) == xs main xs = refute (prop_revrev xs) refute True = True refute False = target False Reach will search for an input that makes the property false.

Constraint Solving Reach does not use exhaustive search as in SmallCheck. Instead it uses constraint solving. At the top level the main function is evaluated with unbound logical variables as the arguments. Evaluation proceeds as normal Haskell but instead of inspecting data structures computation introduces constraints. Evaluation finishes when the target is reached or the depth-bound exceeded.

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) main a {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) lte a (S Z) {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) lte Z (S Z) lte a (S Z) {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) lte Z (S Z) {a=z} lte a (S Z) {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) Target not reached! True {a=z} lte a (S Z) {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) (S Z) doesn't match Z lte a (S Z) {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) lte (S b) (S Z) {a=s b, b =?} lte a (S Z) {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) lte (S lte x) b Z (S{a=S Z) {x=s b, b y, = y?} =?} lte a (S Z) {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) lte Z Z {a=s b, b = Z} lte (S lte x) b Z (S{a=S Z) {x=s b, b y, = y?} =?} lte a (S Z) {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) Target not reached! True {a = S b, b = Z} lte (S lte x) b Z (S{a=S Z) {x=s b, b y, = y?} =?} lte a (S Z) {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) lte (S c) Z {a=s b, b = S c, c =?} lte (S lte x) b Z (S{a=S Z) {x=s b, b y, = y?} =?} lte a (S Z) {a=?} Stack

data Nat = Z S Nat lte Z _ = True lte (S _) Z = target False lte (S x) (S y) = lte x y main a = lte a (S Z) Target reached! Answer found: a = S b, b = S c, c =? a = S (S?) target False {a=s b, b = S c, c =?} lte (S lte x) b Z (S{a=S Z) {x=s b, b y, = y?} =?} lte a (S Z) {a=?} Stack

Reach Can be used for many of the same problems as SmallCheck. Is often much more efficient than SmallCheck, especially in the presence of complex antecedents. No need to write 'generators' which can be a big win in certain applications. Implemented as an external tool rather than a Haskell library.

Conclusion Variety of tools for automated testing in Haskell. The ideas from QuickCheck have been used in several other languages (Erlang, Scheme, Lisp, Python, Ruby, SML). Matt Naylor working on SparseCheck a version of Reach used as a Haskell library.