I’m trying to write property based tests for a Reverse Polish Notation calculator.
I’ve implemented the RPN such that it will return an error tuple if you try to push a operator to the stack when the stack has less than two numbers.
defmodule ReversePolishNotation do
@moduledoc """
Documentation for ReversePolishNotation.
"""
def push(list, number) when number |> is_number do
{:ok, [number | list]}
end
@operators [:+, :/, :-, :*]
def push([x, y | list], op) when op in @operators do
result = apply(Kernel, op, [x, y])
{:ok, [result | list]}
end
def push(list, input) do
{:error, list, {:unexpected_input, input}}
end
end
Now I want to test the happy path of pushing an operator to a stack (list) of numbers, where the list has at least two numbers in it.
since number() |> list() |> non_empty()
will shrink to a one element list it does not work for me.
so I tried to to use the such_that
macro to generate lists of numbers with length(l) > 1
but that ends with {:error, :cant_generate}
My test and generator
property "can push operator to a list with minimum 2 numbers" do
forall {list, operator} <- {list_min_two(), operator()} do
{:ok, _new_list} = RPN.push(list, operator)
end
end
# Generators
def operator() do
oneof([:+, :-, :/, :*])
end
def list_min_two() do
such_that l <- non_empty(list(number())), when: length(l) > 1
end
Maybe @alfert has some insight?