Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
*.swp
.DS_Store
tmp
*~
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# xErlang

Exercism exercises in Erlang
Exercism exercises in Erlang.

For each test there is a directory, which contains two files, e.g., in the `bob` dir
there is `bob_test.erl` and `example.erl`. The example file define a module with the
name `bob`, so that we can use it to check that the tests are running as they should.
The users of exercism will create their own `bob.erl` file and `bob_test` will test it.

## Contributing Guide

Expand Down
34 changes: 34 additions & 0 deletions _test/check-exercises.escript
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#! /usr/bin/env escript

main( [] ) ->
Examples = filelib:wildcard( "*/example.erl" ),
Modules = [{X, compile(X)} || X <- Examples],
[compile_tests(X) || X <- Modules],
Results = [run_tests(X) || X <- Modules],
erlang:halt( erlang:length([X || X <- Results, X =:= ok]) );
main( _ ) -> usage().



compile( File ) ->
Compile = compile:file( File, [binary, return_errors] ),
{compile, File, {ok, Module, Binary}} = {compile, File, Compile},
Load = code:load_binary( Module, File, Binary ),
{load, Module, Load} = {load, Module, Load},
{Module, Binary}.


compile_tests( {Example, {Example_module, _Binary}} ) ->
Filename = erlang:atom_to_list(Example_module) ++ "_tests.erl",
Filepath = filename:join( [filename:dirname(Example), Filename] ),
compile( Filepath ).


run_tests( {_Example, {Module, _Binary}} ) ->
io:fwrite( "~p: ", [Module] ),
eunit:test( Module ).


usage() ->
io:fwrite( "Usage: ~s~n", [escript:script_name()] ),
io:fwrite( "~s will compile and run Erlang examples and test cases in sub directories of where it is started.~n", [escript:script_name()] ).
25 changes: 25 additions & 0 deletions accumulate/accumulate_tests.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
-module( accumulate_tests ).

-include_lib("eunit/include/eunit.hrl").

accumulate_squares_test() ->
Fn = fun(Number) -> Number * Number end,
Ls = [1, 2, 3],
?assertEqual([1, 4, 9], accumulate:accumulate(Fn, Ls)).

accumulate_upcases_test() ->
Fn = fun(Word) -> string:to_upper(Word) end,
Ls = string:tokens("hello world", " "),
?assertEqual(["HELLO", "WORLD"], accumulate:accumulate(Fn, Ls)).

accumulate_reversed_strings_test() ->
Fn = fun(Word) -> lists:reverse(Word) end,
Ls = string:tokens("the quick brown fox etc", " "),
?assertEqual(["eht", "kciuq", "nworb", "xof", "cte"], accumulate:accumulate(Fn, Ls)).

accumulate_recursively_test() ->
Chars = string:tokens("a b c", " "),
Nums = string:tokens("1 2 3", " "),
Fn = fun(Char) -> [Char ++ Num || Num <- Nums] end,
?assertEqual([["a1", "a2", "a3"], ["b1", "b2", "b3"], ["c1", "c2", "c3"]], accumulate:accumulate(Fn, Chars)).

49 changes: 49 additions & 0 deletions allergies/allergies_test.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
% To run tests:
% elc *.erl
% erl -noshell -eval "eunit:test(allergies, [verbose])" -s init stop
%

-module(allergies_test).

-include_lib("eunit/include/eunit.hrl").

no_allergies_at_all_test() ->
?assertEqual(allergies:allergies(0), []).

allergic_to_just_eggs_test() ->
?assertEqual(allergies:allergies(1), ['eggs']).

allergic_to_just_peanuts_test() ->
?assertEqual(allergies:allergies(2), ['peanuts']).

allergic_to_just_strawberries_test() ->
?assertEqual(allergies:allergies(8), ['strawberries']).

allergic_to_eggs_and_peanuts_test() ->
?assertEqual(allergies:allergies(3), ['eggs', 'peanuts']).

allergic_to_more_than_eggs_but_not_peanuts_test() ->
?assertEqual(allergies:allergies(5), ['eggs', 'shellfish']).

allergic_to_lots_of_stuff_test() ->
?assertEqual(allergies:allergies(248), ['strawberries', 'tomatoes', 'chocolate',
'pollen', 'cats']).

allergic_to_everything_test() ->
?assertEqual(allergies:allergies(255),['eggs', 'peanuts', 'shellfish', 'strawberries',
'tomatoes', 'chocolate', 'pollen', 'cats']).

no_allergies_means_not_allergic_test() ->
?assertNot(allergies:isAllergicTo('peanuts', 0)),
?assertNot(allergies:isAllergicTo('cats', 0)),
?assertNot(allergies:isAllergicTo('strawberries', 0)).

is_allergic_to_eggs_test() ->
?assert(allergies:isAllergicTo('eggs', 1)).

allergic_to_eggs_and_other_stuff_test() ->
?assert(allergies:isAllergicTo('eggs', 5)).

ignore_non_allergen_score_parts_test() ->
?assertEqual(allergies:allergies(509), ['eggs', 'shellfish', 'strawberries',
'tomatoes', 'chocolate', 'pollen', 'cats']).
2 changes: 1 addition & 1 deletion allergies/allergies_tests.erl
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
% erl -noshell -eval "eunit:test(allergies, [verbose])" -s init stop
%

-module(allergies_tests).
-module( allergies_tests ).

-include_lib("eunit/include/eunit.hrl").

Expand Down
48 changes: 48 additions & 0 deletions anagram/anagram_tests.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-module( anagram_tests ).
-include_lib("eunit/include/eunit.hrl").

no_matches_test() ->
?assertEqual(
anagram:find("diaper", ["hello", "world", "zombies", "pants"]),
[]).

detect_simple_anagram_test() ->
?assertEqual(
anagram:find("ant", ["tan", "stand", "at"]),
["tan"]).

does_not_confuse_different_duplicates_test() ->
?assertEqual(
anagram:find("galea", ["eagle"]),
[]).

eliminate_anagram_subsets_test() ->
?assertEqual(
anagram:find("good", ["dog", "goody"]),
[]).

detect_anagram_test() ->
?assertEqual(
anagram:find("listen", ["enlists", "google", "inlets", "banana"]),
["inlets"]).

multiple_anagrams_test() ->
?assertEqual(
anagram:find("allergy", ["gallery", "ballerina", "regally", "clergy",
"largely", "leading"]),
["gallery", "regally", "largely"]).

case_insensitive_subject_test() ->
?assertEqual(
anagram:find("Orchestra", ["cashregister", "carthorse", "radishes"]),
["carthorse"]).

case_insensitive_candidate_test() ->
?assertEqual(
anagram:find("orchestra", ["cashregister", "Carthorse", "radishes"]),
["Carthorse"]).

does_not_detect_a_word_as_its_own_anagram_test() ->
?assertEqual(
anagram:find("corn", ["corn", "dark", "Corn", "rank", "CORN", "cron", "park"]),
["cron"]).
95 changes: 95 additions & 0 deletions bank-account/bank_account_tests.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
-module(bank_account_tests).
-include_lib("eunit/include/eunit.hrl").


create_test() ->
Bank_account = bank_account:create(),
?assert(bank_account:balance( Bank_account ) =:= 0).

close_account_test() ->
Bank_account = bank_account:create(),
bank_account:deposit( Bank_account, 1 ),
Amount = bank_account:close( Bank_account ),
?assert(Amount =:= 1),
?assertError(function_clause, bank_account:balance( Bank_account )).

deposit_test() ->
Bank_account = bank_account:create(),
bank_account:deposit( Bank_account, 1 ),
?assert(bank_account:balance( Bank_account ) =:= 1).

deposit_fail_test() ->
Bank_account = bank_account:create(),
bank_account:deposit( Bank_account, -1 ),
?assert(bank_account:balance( Bank_account ) =:= 0).

deposit_many_test() ->
Bank_account = bank_account:create(),
[erlang:spawn( fun () -> bank_account:deposit(Bank_account, X) end ) || X <- lists:seq(1, 10)],
First = bank_account:balance( Bank_account ),
timer:sleep( 100 ),
Last = bank_account:balance( Bank_account ),
?assert(First < 55),
?assert(Last =:= 55).

withdraw_test() ->
Bank_account = bank_account:create(),
bank_account:deposit( Bank_account, 10 ),
Amount = bank_account:withdraw( Bank_account, 1 ),
?assert(Amount =:= 1),
?assert(bank_account:balance( Bank_account ) =:= 9).

withdraw_fail_test() ->
Bank_account = bank_account:create(),
bank_account:deposit( Bank_account, 10 ),
Amount = bank_account:withdraw( Bank_account, -1 ),
?assert(Amount =:= 0),
?assert(bank_account:balance( Bank_account ) =:= 10).

withdraw_excessive_test() ->
Bank_account = bank_account:create(),
bank_account:deposit( Bank_account, 10 ),
Amount = bank_account:withdraw( Bank_account, 20 ),
?assert(Amount =:= 10),
?assert(bank_account:balance( Bank_account ) =:= 0).

withdraw_many_test() ->
Bank_account = bank_account:create(),
bank_account:deposit(Bank_account, 55 ),
[erlang:spawn( fun () -> bank_account:withdraw(Bank_account, X) end ) || X <- lists:seq(1, 10)],
First = bank_account:balance( Bank_account ),
timer:sleep( 100 ),
Last = bank_account:balance( Bank_account ),
?assert(First > 0),
?assert(Last =:= 0).

charge_test() ->
Bank_account = bank_account:create(),
bank_account:deposit( Bank_account, 10 ),
Amount = bank_account:charge( Bank_account, 2 ),
?assert(Amount =:= 2),
?assert(bank_account:balance( Bank_account ) =:= 8).

charge_fail_test() ->
Bank_account = bank_account:create(),
bank_account:deposit( Bank_account, 10 ),
Amount = bank_account:charge( Bank_account, -2 ),
?assert(Amount =:= 0),
?assert(bank_account:balance( Bank_account ) =:= 10).

charge_excessive_test() ->
Bank_account = bank_account:create(),
bank_account:deposit( Bank_account, 10 ),
Amount = bank_account:charge( Bank_account, 20 ),
?assert(Amount =:= 0),
?assert(bank_account:balance( Bank_account ) =:= 10).

charge_many_test() ->
Bank_account = bank_account:create(),
bank_account:deposit(Bank_account, 55 ),
[erlang:spawn( fun () -> bank_account:charge(Bank_account, 10) end ) || _X <- lists:seq(1, 10)],
First = bank_account:balance( Bank_account ),
timer:sleep( 100 ),
Last = bank_account:balance( Bank_account ),
?assert(First > 0),
?assert(Last =:= 5).
48 changes: 48 additions & 0 deletions bank-account/example.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
-module(bank_account).

-export( [balance/1, charge/2, close/1, create/0, deposit/2, withdraw/2] ).

balance( Pid ) -> call( erlang:is_process_alive(Pid), Pid, balance, 0 ).

charge( Pid, Amount ) when Amount > 0 -> call( erlang:is_process_alive(Pid), Pid, charge, Amount );
charge( _Pid, _Amount ) -> 0.

close( Pid ) -> call( erlang:is_process_alive(Pid), Pid, close, 0 ).

create() -> erlang:spawn( fun () -> loop(0) end ).

deposit( Pid, Amount ) when Amount > 0 -> Pid ! {deposit, Amount};
deposit( _Pid, _Amount ) -> ok.

withdraw( Pid, Amount ) when Amount > 0 -> call( erlang:is_process_alive(Pid), Pid, withdraw, Amount );
withdraw( _Pid, _Amount ) -> 0.



call( true, Pid, Request, Argument ) ->
Pid ! {Request, Argument, erlang:self()},
receive
{Request, Answer} -> Answer
end.

loop( Balance ) ->
receive
{balance, _Argument, Pid} ->
Pid ! {balance, Balance},
loop( Balance );
{charge, Amount, Pid} ->
Charge = loop_charge( Balance, Amount ),
Pid ! {charge, Charge},
loop( Balance - Charge );
{close, _Argument, Pid} ->
Pid ! {close, Balance};
{deposit, Amount} ->
loop( Balance + Amount );
{withdraw, Amount, Pid} ->
Withdraw = erlang:min( Balance, Amount ),
Pid ! {withdraw, Withdraw},
loop( Balance - Withdraw )
end.

loop_charge( Balance, Amount ) when Balance >= Amount -> Amount;
loop_charge( _Balance, _Amount ) -> 0.
50 changes: 50 additions & 0 deletions beer-song/beer_song_tests.erl
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
-module( beer_song_tests ).
-include_lib("eunit/include/eunit.hrl").

verse_test() ->
compareNestedLists(beer_song:verse(8),
"8 bottles of beer on the wall, 8 bottles of beer.\n"
"Take it down and pass it around, 7 bottles of beer on the wall.\n").

verse_0_test() ->
compareNestedLists(beer_song:verse(0),
"No more bottles of beer on the wall, no more bottles of beer.\n"
"Go to the store and buy some more, 99 bottles of beer on the wall.\n").

verse_1_test() ->
compareNestedLists(beer_song:verse(1),
"1 bottle of beer on the wall, 1 bottle of beer.\n"
"Take it down and pass it around, no more bottles of beer on the wall.\n").

verse_2_test() ->
compareNestedLists(beer_song:verse(2),
"2 bottles of beer on the wall, 2 bottles of beer.\n"
"Take it down and pass it around, 1 bottle of beer on the wall.\n").

singing_several_verses_test() ->
compareNestedLists(beer_song:sing(8, 6),
"8 bottles of beer on the wall, 8 bottles of beer.\n"
"Take it down and pass it around, 7 bottles of beer on the wall.\n\n"

"7 bottles of beer on the wall, 7 bottles of beer.\n"
"Take it down and pass it around, 6 bottles of beer on the wall.\n\n"

"6 bottles of beer on the wall, 6 bottles of beer.\n"
"Take it down and pass it around, 5 bottles of beer on the wall.\n\n").

sing_all_the_rest_of_the_verses_test() ->
compareNestedLists(beer_song:sing(3),
"3 bottles of beer on the wall, 3 bottles of beer.\n"
"Take it down and pass it around, 2 bottles of beer on the wall.\n\n"

"2 bottles of beer on the wall, 2 bottles of beer.\n"
"Take it down and pass it around, 1 bottle of beer on the wall.\n\n"

"1 bottle of beer on the wall, 1 bottle of beer.\n"
"Take it down and pass it around, no more bottles of beer on the wall.\n\n"

"No more bottles of beer on the wall, no more bottles of beer.\n"
"Go to the store and buy some more, 99 bottles of beer on the wall.\n\n").

compareNestedLists(Response, Expected) ->
?assertEqual(lists:flatten(Response), lists:flatten(Expected)).
Binary file added bin/configlet-darwin-386
Binary file not shown.
Binary file added bin/configlet-darwin-amd64
Binary file not shown.
Binary file added bin/configlet-linux-386
Binary file not shown.
Binary file added bin/configlet-linux-amd64
Binary file not shown.
Binary file added bin/configlet-windows-386.exe
Binary file not shown.
Binary file added bin/configlet-windows-amd64.exe
Binary file not shown.
Loading