In a first-in, first-out (FIFO) queue, elements are consumed from the queue in the same order as they arrive to the queue. Test-driven development (TDD) is a software development process that relies on the repetition of a very short development cycle where the developer:
You will now use TDD to implement a FIFO queue in Erlang.
Erlang comes with a unit test framework named EUnit.
In module-8-erlang/src/fifo.erl
you find the skeleton code for a FIFO module.
In the true spirit of TDD, the tests should be written before the implementation. Therefore you are provided with a number of EUnit test cases. The test cases captures design decisions.
From the Linux shell:
$ rm ebin/fifo.beam
$ make
You will see a number of warnings:
src/fifo.erl:26: Warning: variable 'X' is unused
src/fifo.erl:35: Warning: variable 'In' is unused
src/fifo.erl:37: Warning: variable 'H' is unused
src/fifo.erl:37: Warning: variable 'In' is unused
src/fifo.erl:37: Warning: variable 'T' is unused
Warnings - but success!
From the Linux shell, run EUnit in verbose mode:
$ make testv_fifo
EUnit will now run all the test cases and report the results.
======================== EUnit ========================
module 'fifo'
fifo:60: new_test_...ok
fifo:61: new_test_...ok
fifo:62: new_test_...ok
fifo: push_test...ok
fifo: push_pop_test...*failed*
in function fifo:pop/1 (src/fifo.erl, line 41)
called as pop(tbi)
in call from fifo:'-push_pop_test/0-fun-0-'/0 (src/fifo.erl, line 84)
**error:function_clause
output:<<"">>
undefined
*** test generator fifo:size_test_/0 failed ***
**in function fifo:push/2 (src/fifo.erl, line 32)
called as push(tbi,bar)
in call from fifo:f1/0 (src/fifo.erl, line 88)
in call from fifo:size_test_/0 (src/fifo.erl, line 91)
**error:function_clause
=======================================================
Failed: 1. Skipped: 0. Passed: 4.
One or more tests were cancelled.
From the EUnit report we see that four tests passed with status ok
.
The first test that failed was the push_pop_test()
which terminated with reason:
::function_clause
. This means that no matching function clause is found when
evaluating a function call.
Read here for more
information about the various exit reasons.
To investigate this further we will test the fifo
module manually from the
Erlang shell:
erlang> F1 = fifo:new().
{fifo,[],[]}
A new fifo was created. The new fifo is represented by a 3-tuple where the first element is the atom fifo and the second and third elements are empty lists. Now, lets try to push a value to the new fifo:
erlang> F2 = fifo:push(F1, a).
tbi
Aha! When pushing the atom a
to the fifo F1
, the atom tbi
is returned. What
happens if we use the value of F2
in a pop:
erlang> F3 = fifo:pop(F2).
** exception error: no function clause matching fifo:pop(tbi) (src/fifo.erl,
line 33)
When we try to pop, we call fifo:pop(tbi)
and there is no function clause
matching. In other words, the function fifo:pop/1
is not defined when called
with the atom tbi
as argument. The problem is that fifo:push/2
returns the atom
tbi
.
Todo
Change the implementation of fifo:push/2
according to the comments in the
source.
Your task is to make all test pass by making necessary changes to the
implementation of fifo:pop/1
and fifo:push/2
.
Small steps
It is often recommended to make a few small changes, then compile and run the tests again.
Additional tests from the Erlang shell
In addition to the EUnit test cases, it is often very helpful to test the functions in a module manually from the Erlang shell.
EDoc lets you write the
documentation of an Erlang program as comments in the source code itself, using
tags on the form @Name ...
. A source file does not have to contain tags for EDoc
to generate its documentation, but without tags the result will only contain the
basic available information that can be extracted from the module.
Todo
Provide proper descriptions for all @doc
tags.
Generate new html doc files:
$ make doc
Now you can reload the web page and see the new information added to the html
documentation page for fifo.erl
. To open the documentation from scratch:
$ ./help
u may also use the Makefile to print the url to the documentation index page to the terminal:
$ make doc_url
Erlang is a dynamically typed language. Still, it comes with a notation for declaring sets of Erlang terms to form a particular type, effectively forming a specific sub-type of the set of all Erlang terms. EDoc will detect any type declarations and include this information in the generated documentation.
Here you can read more how to declare types and function specifications using EDoc.
Todo
Use -spec
type declarations to add type declarations to fifo:push/2
and
fifo:pop/1
.
Hints
Look at the type declarations provided for fifo:new/0
, fifo:empty/1
and
fifo:size/1
. You may also look at the type declarations for the functions in
tutorial.erl
.