-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
a29945b
commit 19a30e5
Showing
13 changed files
with
457 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
/_build | ||
/cover | ||
/deps | ||
erl_crash.dump | ||
*.ez | ||
*.eqc | ||
.eqc-info |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
" make sure the following 2 lines are included in ~/.vimrc | ||
" set exrc | ||
" set secure | ||
|
||
map <leader>t :!mix test %<CR> | ||
map <leader>c :!mix compile && mix dialyzer<CR> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
# Quickcheck Playground | ||
|
||
This project uses QuickCheck Mini from Quviq. | ||
|
||
1. Head over to [QuviQ](http://www.quviq.com/downloads/) and download the _FREE_ version of QuickCheck (Mini). | ||
2. Unzip the folder, and `cd` into it. | ||
3. Run `iex`. | ||
4. Run `:eqc_install.install()` to install QuickCheck Mini. | ||
5. Clone this repo, and run `mix deps.get` | ||
6. Run `mix tests` to run the tests. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,30 @@ | ||
# This file is responsible for configuring your application | ||
# and its dependencies with the aid of the Mix.Config module. | ||
use Mix.Config | ||
|
||
# This configuration is loaded before any dependency and is restricted | ||
# to this project. If another project depends on this project, this | ||
# file won't be loaded nor affect the parent project. For this reason, | ||
# if you want to provide default values for your application for | ||
# 3rd-party users, it should be done in your "mix.exs" file. | ||
|
||
# You can configure for your application as: | ||
# | ||
# config :quickcheck_playground, key: :value | ||
# | ||
# And access this configuration in your application as: | ||
# | ||
# Application.get_env(:quickcheck_playground, :key) | ||
# | ||
# Or configure a 3rd-party app: | ||
# | ||
# config :logger, level: :info | ||
# | ||
|
||
# It is also possible to import configuration files, relative to this | ||
# directory. For example, you can emulate configuration per environment | ||
# by uncommenting the line below and defining dev.exs, test.exs and such. | ||
# Configuration from the imported file will override the ones defined | ||
# here (which is why it is important to import them last). | ||
# | ||
# import_config "#{Mix.env}.exs" |
2 changes: 2 additions & 0 deletions
2
chapter_11/quickcheck_playground/lib/quickcheck_playground.ex
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
defmodule QuickcheckPlayground do | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
defmodule QuickcheckPlayground.Mixfile do | ||
use Mix.Project | ||
|
||
def project do | ||
[app: :quickcheck_playground, | ||
version: "0.0.1", | ||
elixir: "~> 1.2-rc", | ||
build_embedded: Mix.env == :prod, | ||
start_permanent: Mix.env == :prod, | ||
test_pattern: "*_{test,eqc}.exs", | ||
deps: deps] | ||
end | ||
|
||
def application do | ||
[applications: [:logger]] | ||
end | ||
|
||
defp deps do | ||
[ | ||
{:eqc_ex, "~> 1.2.4"} | ||
] | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
%{"eqc_ex": {:hex, :eqc_ex, "1.2.4"}, | ||
"excheck": {:hex, :excheck, "0.3.2"}, | ||
"triq": {:git, "https://github.com/krestenkrab/triq.git", "c7306b8eaea133d52140cb828817efb5e50a3d52", []}} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
defmodule ListEQC do | ||
use ExUnit.Case | ||
use EQC.ExUnit | ||
|
||
@tag numtests: 100 | ||
property "Deleting from a list" do | ||
forall list <- ulist(int) do | ||
implies list != [] do | ||
forall item <- elements(list) do | ||
ensure not item in List.delete(list, item) == true | ||
end | ||
end | ||
end | ||
end | ||
|
||
property "Misconception of deleting from a list" do | ||
:eqc.fails( | ||
forall list <- list(int) do | ||
implies list != [] do | ||
forall item <- elements(list) do | ||
ensure not item in List.delete(list, item) == true | ||
end | ||
end | ||
end | ||
) | ||
end | ||
|
||
property "Deleting an element not in a list leaves the list unchanged" do | ||
forall list <- list(int) do | ||
forall item <- int do | ||
implies not item in list do | ||
ensure list == List.delete(list, item) | ||
end | ||
end | ||
end | ||
end | ||
|
||
property "sorting works" do | ||
forall l <- list(int) do | ||
ensure l |> Enum.sort |> is_sorted == true | ||
end | ||
end | ||
|
||
|
||
# NOTE: Testing properties of reverse | ||
|
||
property "reverse is recursive" do | ||
forall l <- non_empty(list(char)) do | ||
equal Enum.reverse(l), Enum.reverse(tl(l)) ++ [hd(l)] | ||
end | ||
end | ||
|
||
property "reverse is distributive" do | ||
forall {l1, l2} <- {list(char), list(char)} do | ||
ensure Enum.reverse(l1 ++ l2) == Enum.reverse(l2) ++ Enum.reverse(l1) | ||
end | ||
end | ||
|
||
property "reverse is idempotent" do | ||
forall l <- list(char) do | ||
ensure l |> Enum.reverse |> Enum.reverse == l | ||
end | ||
end | ||
|
||
property "reverse is equivalent to the Erlang version" do | ||
forall l <- list(oneof [real, int]) do | ||
ensure Enum.reverse(l) == :lists.reverse(l) | ||
end | ||
end | ||
|
||
def is_sorted([]), do: true | ||
|
||
def is_sorted(list) do | ||
list | ||
|> Enum.zip(tl(list)) | ||
|> Enum.all?(fn {x, y} -> x <= y end) | ||
end | ||
|
||
def equal(x, y) do | ||
when_fail(IO.puts("FAILED ☛ #{inspect(x)} != #{inspect(y)}")) do | ||
x == y | ||
end | ||
end | ||
|
||
# Custom generator to generate unique lists | ||
def ulist(item) do | ||
let l <- list(item) do | ||
l |> Enum.sort |> Enum.uniq | ||
end | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,121 @@ | ||
defmodule MapEQC do | ||
use ExUnit.Case | ||
use EQC.ExUnit | ||
|
||
# NOTE: Strange that this should fail but doesn't. | ||
property "keys are unique (take 1)" do | ||
forall m <- map_1 do | ||
no_duplicates(Map.keys(m)) | ||
end | ||
end | ||
|
||
property "keys are unique (take 2)" do | ||
forall m <- map_2 do | ||
no_duplicates(Map.keys(:eqc_symbolic.eval(m))) | ||
end | ||
end | ||
|
||
property "storing keys and values" do | ||
forall {k, v, m} <- {key, val, map_2} do | ||
map = :eqc_symbolic.eval(m) | ||
lists_equal(model(Map.put(map, k, v)), model_store(k, v, model(map))) | ||
end | ||
end | ||
|
||
property "merging maps is *not* commutative" do | ||
forall {m1, m2} <- {map_2, map_2} do | ||
map_1 = :eqc_symbolic.eval(m1) | ||
map_2 = :eqc_symbolic.eval(m2) | ||
|
||
# NOTE: This will not work, since keys can be overriden! | ||
# Cool that QC finds this out after ~ 79 tests | ||
:eqc.fails( | ||
ensure Map.merge(map_1, map_2) == Map.merge(map_2, map_1) | ||
) | ||
end | ||
end | ||
|
||
property "merging maps retains keys" do | ||
forall {m1, m2} <- {map_2, map_2} do | ||
map_1 = :eqc_symbolic.eval(m1) | ||
map_2 = :eqc_symbolic.eval(m2) | ||
|
||
left_keys = Map.merge(map_1, map_2) |> Map.keys | ||
right_keys = Map.merge(map_2, map_1) |> Map.keys | ||
|
||
equal(left_keys, right_keys) | ||
end | ||
end | ||
|
||
# First version of map generator | ||
# NOTE: there's a recursive call to map_1(). We need to | ||
# use the `lazy` macro here. | ||
def map_1 do | ||
map_gen = lazy do | ||
let {k, v, m} <- {key, val, map_1} do | ||
Map.put(m, k, v) | ||
end | ||
end | ||
|
||
oneof [Map.new, map_gen] | ||
end | ||
|
||
# NOTE: Make sure that the order is right! | ||
# {:call, Map, :put, [key, val, map_2]}] will *not* work! | ||
def map_2 do | ||
lazy do | ||
oneof [{:call, Map, :new, []}, | ||
{:call, Map, :put, [map_2, key, val]}] | ||
end | ||
end | ||
|
||
def no_duplicates(elems) do | ||
left = elems |> Enum.sort | ||
right = elems |> Enum.uniq |> Enum.sort | ||
# equal(:lists.sort(elems), :lists.usort(elems)) | ||
equal(left, right) | ||
end | ||
|
||
def key do | ||
oneof [int, real, atom] | ||
end | ||
|
||
def val do | ||
key | ||
end | ||
|
||
def atom do | ||
elements [:a, :b, :c, true, false, :ok] | ||
end | ||
|
||
def model(map) do | ||
Map.to_list(map) | ||
end | ||
|
||
def model_store(k, v, list) do | ||
case find_index_with_key(k, list) do | ||
{:match, index} -> | ||
List.replace_at(list, index, {k, v}) | ||
_ -> | ||
[{k, v} | list] | ||
end | ||
end | ||
|
||
def find_index_with_key(k, list) do | ||
case Enum.find_index(list, fn({x,_}) -> x == k end) do | ||
nil -> :nomatch | ||
index -> {:match, index} | ||
end | ||
end | ||
|
||
def equal(x, y) do | ||
when_fail(IO.puts("FAILED ☛ #{inspect(x)} != #{inspect(y)}")) do | ||
x == y | ||
end | ||
end | ||
|
||
def lists_equal(x, y) do | ||
equal(Enum.sort(x), Enum.sort(y)) | ||
end | ||
|
||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
defmodule NumbersEQC do | ||
use ExUnit.Case | ||
use EQC.ExUnit | ||
|
||
property "the square of a real number is always positive" do | ||
forall x <- real do | ||
ensure x*x >= 0 | ||
end | ||
end | ||
|
||
property "the square of the square root of a real number is the original number" do | ||
forall x <- pos_real do | ||
sq_root = :math.sqrt(x) | ||
ensure sq_root * sq_root - x < 0.000000000001 | ||
end | ||
end | ||
|
||
def pos_real do | ||
let r <- real do | ||
:erlang.abs(r) | ||
end | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
defmodule OthersEQC do | ||
use ExUnit.Case | ||
use EQC.ExUnit | ||
|
||
property "encoding then decoding a binary gives back the same binary" do | ||
forall s <- binary do | ||
equal s |> Base.encode16 |> Base.decode16!, s | ||
end | ||
end | ||
|
||
def equal(x, y) do | ||
when_fail(IO.puts("FAILED ☛ #{inspect(x)} != #{inspect(y)}")) do | ||
x == y | ||
end | ||
end | ||
|
||
end |
Oops, something went wrong.