├── .gitignore ├── ChangeLog ├── Makefile ├── README.markdown ├── doc └── overview.edoc ├── etap.epm ├── include └── etap.hrl ├── scripts └── collect.erl ├── src ├── Makefile ├── etap.app ├── etap.erl └── overview.edoc ├── support └── include.mk └── t ├── Makefile ├── etap_t_001.erl ├── etap_t_002.erl ├── etap_t_003.erl ├── etap_t_004.erl ├── etap_t_005.erl ├── etap_t_006.erl ├── etap_t_008.erl ├── etap_t_009.erl ├── etap_t_010.erl ├── etap_t_011.erl ├── etap_t_012.erl └── etap_t_013.erl /.gitignore: -------------------------------------------------------------------------------- 1 | ebin 2 | doc 3 | *.swp 4 | *.dump 5 | edoc-info 6 | -------------------------------------------------------------------------------- /ChangeLog: -------------------------------------------------------------------------------- 1 | 2 | 2009-02-10 ngerakines 3 | - Added etap:skip/3 4 | - Adding misc documentation 5 | - Splitting changes list out of etap.erl and into this file. 6 | 7 | 2009-01-23 ngerakines 8 | - Added etap_report module to build pretty HTML code coverage reports 9 | - Updated readme and misc documentation 10 | 11 | 2009-01-21 ngerakines 12 | - Bumpting to 0.3.3 13 | - Updated documentation for the coverage report script. 14 | 15 | 2009-01-12 ngerakines 16 | - Added experimental code coverage support. 17 | 18 | 2009-01-01 ngerakines 19 | - Added etap:skip/1 and etap:skip/2 20 | - Added skip support to etap:plan/1 21 | - Misc code cleanup and documentation 22 | - Bumping to 0.3.2, tagging release 23 | - Adding specs and documentation 24 | 25 | 2008-12-30 ngerakines 26 | - Removing functionality, hurray! 27 | 28 | 2008-12-28 ngerakines 29 | - Added etap:is_greater/3 30 | - Added etap_string module and updated test suite 31 | 32 | 2008-12-26 ngerakines 33 | - Cleaned etap_web:build_request/4 module handling of redirects 34 | - Added functionality to etal_request 35 | 36 | 2008-12-17 Jacob Vorreuter, ngerakines, Jeremy Wall 37 | - Cleaned etap_web:build_request/4 module handling for get/post/put requests 38 | - Documentation updates / cleanup 39 | 40 | 2008-12-17 Jacob Vorreuter, ngerakines, Jeremy Wall 41 | - Fixing bug in etap_request:status_is/2 42 | - Added attribute inspection tests to etap_can 43 | - Added Jacob to credits list 44 | 45 | 2008-12-15 Jeremy Wall, ngerakines 46 | - Added doc targets to build 47 | - Misc build cleanup 48 | 49 | 2008-12-11 ngerakines 50 | - Added etap:diag_time/0 51 | 52 | 2008-12-10 ngerakines 53 | - Adding support for non-get requests in etap_web. 54 | 55 | 2008-12-09 ngerakines 56 | - Added output displaying test run time in seconds. 57 | 58 | 2008-12-01 ngerakines 59 | - Fixed bug in test etap_t_002. 60 | - Minor/Misc code cleanup. 61 | 62 | 2008-11-30 ngerakines 63 | - Fixed by in test results where failed tests weren't being recorded. 64 | - Added warning when planned vs executed tests aren't the same. 65 | - Bumping rev to 0.3.1. 66 | - Added etap:fun_is/3. 67 | - Updated the README 68 | - Added limited support for the dianostic syntax 69 | 70 | 2008-11-28 ngerakines 71 | - Minor documentation and build changes. 72 | - Added etap_process module and updated test suite accordingly. 73 | 74 | 2008-11-27 ngerakines 75 | - Added etap:any/3, etap:none/3 and etap_excecption:throws_ok/3. 76 | - Internal cleanup of etap modules. 77 | 78 | 2008-11-25 ngerakines 79 | - Consolidated test server and plan server. 80 | - Added meta information when creating new plan. 81 | - Added lots of documentation. 82 | - Cleaned up the current test suite. 83 | - Started extending testing capabilities of etap_request. 84 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | LIBDIR=$(shell erl -eval 'io:format("~s~n", [code:lib_dir()])' -s init stop -noshell) 2 | .PHONY: doc 3 | VERSION=0.3.4 4 | 5 | all: 6 | mkdir -p ebin 7 | (cd src;$(MAKE)) 8 | cp -f src/etap.app ebin/ 9 | 10 | doc: 11 | (cd src; $(MAKE) doc) 12 | 13 | test: all 14 | (cd t;$(MAKE)) 15 | (cd t;$(MAKE) test) 16 | 17 | clean: 18 | (cd src;$(MAKE) clean) 19 | (cd t;$(MAKE) clean) 20 | rm -rf cover/ 21 | 22 | package: clean 23 | @mkdir etap-$(VERSION)/ && cp -rf ChangeLog Makefile README.markdown scripts src support t etap-$(VERSION) 24 | @COPYFILE_DISABLE=true tar zcf etap-$(VERSION).tgz etap-$(VERSION) 25 | @rm -rf etap-$(VERSION)/ 26 | 27 | install: 28 | mkdir -p $(prefix)/$(LIBDIR)/etap-$(VERSION)/ebin 29 | for i in ebin/*.beam; do install $$i $(prefix)/$(LIBDIR)/etap-$(VERSION)/$$i ; done 30 | mkdir -p $(prefix)/$(LIBDIR)/etap-$(VERSION)/include 31 | for i in include/*.hrl; do install $$i $(prefix)/$(LIBDIR)/etap-$(VERSION)/$$i ; done 32 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | README 2 | ====== 3 | 4 | etap is a collection of Erlang modules that provide a TAP testing client library. These modules allow developers to create extensive and comprehensive tests covering many aspects of application and module development. This includes simple assertions, exceptions, the application behavior and event web requests. This library was originally written by Jeremy wall. 5 | 6 | As per the TAP wiki: 7 | 8 | > TAP, the Test Anything Protocol, is a simple text-based interface between testing modules in a test harness. TAP started life as part of the test harness for Perl but now has implementations in C/C++, Python, PHP, Perl and probably others by the time you read this. 9 | 10 | These modules are not meant to compete with eunit, but to offer a more general testing facility that isn't provided by eunit. 11 | 12 | http://en.wikipedia.org/wiki/Test_Anything_Protocol 13 | http://testanything.org 14 | 15 | CREATING TESTS 16 | ============== 17 | 18 | A "test" is any number of etap:\* or etap\_\*:\* tests that are part of a test plan. When a plan is created using etap:plan/1, a process is started that tracks the status of the tests executed and handles diagnostic output. 19 | 20 | Consider the following example test plan: 21 | 22 | etap:plan(3), 23 | etap:ok(true, "the 'true' atom is recognized"), 24 | etap:is(1 + 1, 2, "simple math"), 25 | etap:isnt(2 + 2, 5, "some would argue"), 26 | etap:end_tests(). 27 | 28 | For tests that require pattern matching, a macro can be used. 29 | 30 | -include("etap.hrl"). 31 | ... 32 | ?ETAP_MATCH({foo, bar, baz}, {foo, _, _}, "a three-element tuple with foo as the first element"), 33 | 34 | There are a number of utility tests that can be used. The etap:any/3, etap:none/3, etap:fun\_is/3, and etap:expect\_fun/3 use functions to return either 'true' or 'false'. 35 | 36 | Numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9], 37 | FunWithNumbers = fun(X) case X of [1, 2, 3 | _] -> true; _ -> false end end, 38 | etap:fun_is(FunWithNumbers, Numbers, "Match the first three numbers"). 39 | 40 | There are many examples in t/\*.erl. 41 | 42 | BUILD & INSTALL 43 | =============== 44 | 45 | To build this library, from the root directory execute the `make` command. You should also execute the `make test` command to verify that the library functions correctly on your system. If you have the Perl module TAP::Harness you can use it to collect and display test results using the `make prove` target. 46 | 47 | $ make 48 | $ make test 49 | $ make prove 50 | 51 | If you choose to run the `make test` command then please be sure to `make clean` after to remove any of the temporary beam files created by the tests in the `t/` directory. 52 | 53 | The included tests cover the basic functionality of the etap modules. They can also be used as a reference when writing your own tests. 54 | 55 | To install etap you need to create the `etap/ebin/` directory in your current Erlang library and copy all of the .beam files created by the `make` file. 56 | 57 | $ sudo mkdir -p /usr/lib/erlang/lib/etap-0.3.4/ebin 58 | $ make clean && make 59 | $ sudo cp ebin/*.beam /usr/lib/erlang/lib/etap-0.3.4/ebin/ 60 | 61 | The `make dist-src` target can be used to create source distributions for further packaging and deployment. 62 | 63 | USING TAP::Harness 64 | ================== 65 | 66 | The 'TAP::Harness' library can be used to collect TAP output produced by this module. 67 | 68 | $ cpan install TAP::Harness 69 | $ prove t/*.t 70 | $ prove -v t/*.t 71 | 72 | TEST COVERAGE 73 | ============= 74 | 75 | With etap it is possible to test the code coverage of your test suite. To enable code coverage you must set the "COVER" environmental variable and post-compile all of the .coverdata files created by the test suite. 76 | 77 | $ COVER=1 erl -eval 'module:test().' -s init stop ... 78 | OR 79 | $ COVER=1 escript t/*.t 80 | OR 81 | $ COVER=1 prove t/*.t 82 | $ erl 83 | 1> etap_report:create(). 84 | ... 85 | ok 86 | 87 | There are several assumptions made here: 88 | 89 | * All of the modules you are trying to get coverage for reside in the `./ebin/` directory. If this is not the case, the directory can be set using the "COVER\_BIN" environmental variable. 90 | * All of the .beam files analyzed by this code coverage feature are compiled with the +debug\_info flag. 91 | 92 | SUPPORTED FUNCTIONALITY 93 | ======================= 94 | 95 | There are a number of proposals listed on the TAP wiki that are not supported by this library. Please be aware of this when creating your tests. 96 | 97 | * LIMITED SUPPORTED: TAP diagnostic syntax 98 | * LIMITED SUPPORTED: TAP meta information 99 | * LIMITED SUPPORTED: TAP logging syntax 100 | * NOT SUPPORTED: TODO 101 | * SUPPORTED: SKIP 102 | * SUPPORTED: TAP datetime 103 | * SUPPORTED: c0 code coverage 104 | * SUPPORTED: html code coverage reports 105 | 106 | We Need Your Help! 107 | ================== 108 | 109 | Things that can greatly be improved. Please fork this project and contribute. Patches are always welcome. 110 | 111 | * Support for testing multi-node systems and environments. 112 | * OTP behaviors like gen\_server, gen\_fsm and gen\_event 113 | * Things like the error\_logger and sasl 114 | * C1 code coverage reporting and html output 115 | * Documentation 116 | 117 | I've got a project. How can I integrate testing? 118 | ============================================= 119 | 120 | If you haven't been a test-first developer before, now is the best time to start. To integrate etap into your project, you need to do 3 things. 121 | 122 | 1. Install etap onto your development/build/integration system. 123 | 2. Create tests! Start with really simple things like loading modules and gradually build tests into more and more complex and deep functionality. 124 | 3. Run your tests. This is the most important step. Get into the habit of running your test suite before every check-in, after every pull, before packaging, etc. 125 | 126 | CREDITS 127 | ======= 128 | 129 | 2008-2009 Nick Gerakines
130 | 2007-2008 Jeremy Wall
131 | 2008 Jacob Vorreuter 132 | 133 | Special thanks to everyone that has contributed to this project. 134 | 135 | * Paul J. Davis 136 | * Brad Anderson 137 | * Ken Pratt 138 | * Kevin Ilchmann Jørgensen 139 | * Benoit Chesneau 140 | * Adam Kocoloski 141 | * Jayson Vantuyl 142 | -------------------------------------------------------------------------------- /doc/overview.edoc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ngerakines/etap/3d46faf192a4e1436a058c9cfd7830deac72424b/doc/overview.edoc -------------------------------------------------------------------------------- /etap.epm: -------------------------------------------------------------------------------- 1 | []. 2 | -------------------------------------------------------------------------------- /include/etap.hrl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2008-2009 Nick Gerakines 2 | %% 3 | %% Permission is hereby granted, free of charge, to any person 4 | %% obtaining a copy of this software and associated documentation 5 | %% files (the "Software"), to deal in the Software without 6 | %% restriction, including without limitation the rights to use, 7 | %% copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | %% copies of the Software, and to permit persons to whom the 9 | %% Software is furnished to do so, subject to the following 10 | %% conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be 13 | %% included in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | %% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | %% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | %% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | %% OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | %% @spec ?etap_match(Got, Expected, Desc) -> Result 25 | %% Got = any() 26 | %% Expected = any() 27 | %% Desc = string() 28 | %% Result = true | false 29 | %% @doc Assert that a value matches a match spec. 30 | -define(etap_match(Got, Expected, Desc), 31 | etap:expect_fun(fun(XXXXXX) -> case XXXXXX of Expected -> true; _ -> false end end, Got, Desc, ??Expected)). 32 | 33 | %% @spec ?etap_throws_match(F, ErrMatch, Desc) -> Result 34 | %% F = fun() 35 | %% ErrMatch = any() 36 | %% Desc = string() 37 | %% Result = true | false 38 | %% @doc Assert that the exception thrown by a function matches the given exception. 39 | %% Like etap_exception:throws_ok/3, but with pattern matching 40 | -define(etap_throws_match(F, ErrMatch, Desc), 41 | try F() of 42 | _ -> etap:ok(nok, Desc) 43 | catch 44 | _:E -> 45 | ?etap_match(E, ErrMatch, Desc) 46 | end). 47 | -------------------------------------------------------------------------------- /scripts/collect.erl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env escript 2 | %% -*- erlang -*- 3 | %%! -noshell 4 | 5 | %% @doc Takes the coverdata files generated by the cover module and creates 6 | %% reports out of them. Each module analysed by the system generates an 7 | %% X_coverage.txt file. 8 | main(_) -> 9 | [cover:import(File) || File <- filelib:wildcard("*.coverdata")], 10 | lists:foreach( 11 | fun(Module) -> 12 | cover:analyse_to_file( 13 | Module, atom_to_list(Mod) ++ "_coverage.txt", [] 14 | ) 15 | end, 16 | cover:imported_modules() 17 | ). 18 | 19 | -------------------------------------------------------------------------------- /src/Makefile: -------------------------------------------------------------------------------- 1 | include ../support/include.mk 2 | 3 | all: $(EBIN_FILES) 4 | 5 | doc: 6 | mkdir -p $(DOC_DIR) 7 | $(ERL) -noshell -run edoc files $(ERL_SOURCES) -run init stop 8 | mv *.html *.png *.css $(DOC_DIR) 9 | 10 | debug: 11 | $(MAKE) DEBUG=-DDEBUG 12 | 13 | clean: 14 | rm -rf $(EBIN_FILES) 15 | rm -rf edoc-info 16 | rm -rf $(DOC_DIR) 17 | -------------------------------------------------------------------------------- /src/etap.app: -------------------------------------------------------------------------------- 1 | {application, etap, [ 2 | {description, "Erlang Test Anything Protocol"}, 3 | {vsn, "0.3.4"}, 4 | {modules, [etap]}, 5 | {registered, []}, 6 | {applications, [kernel, stdlib]} 7 | ]}. 8 | -------------------------------------------------------------------------------- /src/etap.erl: -------------------------------------------------------------------------------- 1 | %% Copyright (c) 2008-2009 Nick Gerakines 2 | %% 3 | %% Permission is hereby granted, free of charge, to any person 4 | %% obtaining a copy of this software and associated documentation 5 | %% files (the "Software"), to deal in the Software without 6 | %% restriction, including without limitation the rights to use, 7 | %% copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | %% copies of the Software, and to permit persons to whom the 9 | %% Software is furnished to do so, subject to the following 10 | %% conditions: 11 | %% 12 | %% The above copyright notice and this permission notice shall be 13 | %% included in all copies or substantial portions of the Software. 14 | %% 15 | %% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 16 | %% EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 17 | %% OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18 | %% NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 19 | %% HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | %% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21 | %% FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | %% OTHER DEALINGS IN THE SOFTWARE. 23 | %% 24 | %% @author Nick Gerakines [http://socklabs.com/] 25 | %% @author Jeremy Wall 26 | %% @version 0.3.4 27 | %% @copyright 2007-2008 Jeremy Wall, 2008-2009 Nick Gerakines 28 | %% @reference http://testanything.org 29 | %% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol 30 | %% @todo Finish implementing the skip directive. 31 | %% @todo Document the messages handled by this receive loop. 32 | %% @todo Explain in documentation why we use a process to handle test input. 33 | %% @doc etap is a TAP testing module for Erlang components and applications. 34 | %% This module allows developers to test their software using the TAP method. 35 | %% 36 | %%

37 | %% TAP, the Test Anything Protocol, is a simple text-based interface between 38 | %% testing modules in a test harness. TAP started life as part of the test 39 | %% harness for Perl but now has implementations in C/C++, Python, PHP, Perl 40 | %% and probably others by the time you read this. 41 | %%

42 | %% 43 | %% The testing process begins by defining a plan using etap:plan/1, running 44 | %% a number of etap tests and then calling eta:end_tests/0. Please refer to 45 | %% the Erlang modules in the t directory of this project for example tests. 46 | -module(etap). 47 | -vsn("0.3.4"). 48 | 49 | -export([ 50 | ensure_test_server/0, 51 | start_etap_server/0, 52 | test_server/1, 53 | msg/1, msg/2, 54 | diag/1, diag/2, 55 | expectation_mismatch_message/3, 56 | plan/1, 57 | end_tests/0, 58 | not_ok/2, ok/2, is_ok/2, is/3, isnt/3, any/3, none/3, 59 | fun_is/3, expect_fun/3, expect_fun/4, 60 | is_greater/3, 61 | skip/1, skip/2, 62 | datetime/1, 63 | skip/3, 64 | bail/0, bail/1, 65 | test_state/0, failure_count/0 66 | ]). 67 | 68 | -export([ 69 | contains_ok/3, 70 | is_before/4 71 | ]). 72 | 73 | -export([ 74 | is_pid/2, 75 | is_alive/2, 76 | is_mfa/3 77 | ]). 78 | 79 | -export([ 80 | loaded_ok/2, 81 | can_ok/2, can_ok/3, 82 | has_attrib/2, is_attrib/3, 83 | is_behaviour/2 84 | ]). 85 | 86 | -export([ 87 | dies_ok/2, 88 | lives_ok/2, 89 | throws_ok/3 90 | ]). 91 | 92 | 93 | -record(test_state, { 94 | planned = 0, 95 | count = 0, 96 | pass = 0, 97 | fail = 0, 98 | skip = 0, 99 | skip_reason = "" 100 | }). 101 | 102 | %% @spec plan(N) -> Result 103 | %% N = unknown | skip | {skip, string()} | integer() 104 | %% Result = ok 105 | %% @doc Create a test plan and boot strap the test server. 106 | plan(unknown) -> 107 | %ensure_coverage_starts(), 108 | ensure_test_server(), 109 | etap_server ! {self(), plan, unknown}, 110 | ok; 111 | plan(skip) -> 112 | io:format("1..0 # skip~n"); 113 | plan({skip, Reason}) -> 114 | io:format("1..0 # skip ~s~n", [Reason]); 115 | plan(N) when is_integer(N), N > 0 -> 116 | %ensure_coverage_starts(), 117 | ensure_test_server(), 118 | etap_server ! {self(), plan, N}, 119 | ok. 120 | 121 | %% @spec end_tests() -> ok 122 | %% @doc End the current test plan and output test results. 123 | %% @todo This should probably be done in the test_server process. 124 | end_tests() -> 125 | %ensure_coverage_ends(), 126 | case whereis(etap_server) of 127 | undefined -> self() ! true; 128 | _ -> etap_server ! {self(), state} 129 | end, 130 | State = receive {etap, X} -> X end, 131 | if 132 | State#test_state.planned == -1 -> 133 | io:format("1..~p~n", [State#test_state.count]); 134 | true -> 135 | ok 136 | end, 137 | case whereis(etap_server) of 138 | undefined -> ok; 139 | _ -> etap_server ! done, ok 140 | end. 141 | 142 | bail() -> 143 | bail(""). 144 | 145 | bail(Reason) -> 146 | etap_server ! {self(), diag, "Bail out! " ++ Reason}, 147 | %ensure_coverage_ends(), 148 | etap_server ! done, ok, 149 | ok. 150 | 151 | %% @spec test_state() -> Return 152 | %% Return = test_state_record() | {error, string()} 153 | %% @doc Return the current test state 154 | test_state() -> 155 | etap_server ! {self(), state}, 156 | receive 157 | {etap, X} when is_record(X, test_state) -> X 158 | after 1000 -> 159 | {error, "Timed out waiting for etap server reply.~n"} 160 | end. 161 | 162 | %% @spec failure_count() -> Return 163 | %% Return = integer() | {error, string()} 164 | %% @doc Return the current failure count 165 | failure_count() -> 166 | case test_state() of 167 | #test_state{fail=FailureCount} -> FailureCount; 168 | X -> X 169 | end. 170 | 171 | %% @spec msg(S) -> ok 172 | %% S = string() 173 | %% @doc Print a message in the test output. 174 | msg(S) -> etap_server ! {self(), diag, S}, ok. 175 | 176 | %% @spec msg(Format, Data) -> ok 177 | %% Format = atom() | string() | binary() 178 | %% Data = [term()] 179 | %% UnicodeList = [Unicode] 180 | %% Unicode = int() 181 | %% @doc Print a message in the test output. 182 | %% Function arguments are passed through io_lib:format/2. 183 | msg(Format, Data) -> msg(io_lib:format(Format, Data)). 184 | 185 | %% @spec diag(S) -> ok 186 | %% S = string() 187 | %% @doc Print a debug/status message related to the test suite. 188 | diag(S) -> msg("# " ++ S). 189 | 190 | %% @spec diag(Format, Data) -> ok 191 | %% Format = atom() | string() | binary() 192 | %% Data = [term()] 193 | %% UnicodeList = [Unicode] 194 | %% Unicode = int() 195 | %% @doc Print a debug/status message related to the test suite. 196 | %% Function arguments are passed through io_lib:format/2. 197 | diag(Format, Data) -> diag(io_lib:format(Format, Data)). 198 | 199 | %% @spec expectation_mismatch_message(Got, Expected, Desc) -> ok 200 | %% Got = any() 201 | %% Expected = any() 202 | %% Desc = string() 203 | %% @doc Print an expectation mismatch message in the test output. 204 | expectation_mismatch_message(Got, Expected, Desc) -> 205 | msg(" ---"), 206 | msg(" description: ~p", [Desc]), 207 | msg(" found: ~p", [Got]), 208 | msg(" wanted: ~p", [Expected]), 209 | msg(" ..."), 210 | ok. 211 | 212 | % @spec evaluate(Pass, Got, Expected, Desc) -> Result 213 | %% Pass = true | false 214 | %% Got = any() 215 | %% Expected = any() 216 | %% Desc = string() 217 | %% Result = true | false 218 | %% @doc Evaluate a test statement, printing an expectation mismatch message 219 | %% if the test failed. 220 | evaluate(Pass, Got, Expected, Desc) -> 221 | case mk_tap(Pass, Desc) of 222 | false -> 223 | expectation_mismatch_message(Got, Expected, Desc), 224 | false; 225 | true -> 226 | true 227 | end. 228 | 229 | %% @spec ok(Expr, Desc) -> Result 230 | %% Expr = true | false 231 | %% Desc = string() 232 | %% Result = true | false 233 | %% @doc Assert that a statement is true. 234 | ok(Expr, Desc) -> evaluate(Expr == true, Expr, true, Desc). 235 | 236 | %% @spec not_ok(Expr, Desc) -> Result 237 | %% Expr = true | false 238 | %% Desc = string() 239 | %% Result = true | false 240 | %% @doc Assert that a statement is false. 241 | not_ok(Expr, Desc) -> evaluate(Expr == false, Expr, false, Desc). 242 | 243 | %% @spec is_ok(Expr, Desc) -> Result 244 | %% Expr = any() 245 | %% Desc = string() 246 | %% Result = true | false 247 | %% @doc Assert that two values are the same. 248 | is_ok(Expr, Desc) -> evaluate(Expr == ok, Expr, ok, Desc). 249 | 250 | %% @spec is(Got, Expected, Desc) -> Result 251 | %% Got = any() 252 | %% Expected = any() 253 | %% Desc = string() 254 | %% Result = true | false 255 | %% @doc Assert that two values are the same. 256 | is(Got, Expected, Desc) -> evaluate(Got == Expected, Got, Expected, Desc). 257 | 258 | %% @spec isnt(Got, Expected, Desc) -> Result 259 | %% Got = any() 260 | %% Expected = any() 261 | %% Desc = string() 262 | %% Result = true | false 263 | %% @doc Assert that two values are not the same. 264 | isnt(Got, Expected, Desc) -> evaluate(Got /= Expected, Got, Expected, Desc). 265 | 266 | %% @spec is_greater(ValueA, ValueB, Desc) -> Result 267 | %% ValueA = number() 268 | %% ValueB = number() 269 | %% Desc = string() 270 | %% Result = true | false 271 | %% @doc Assert that an integer is greater than another. 272 | is_greater(ValueA, ValueB, Desc) when is_integer(ValueA), is_integer(ValueB) -> 273 | mk_tap(ValueA > ValueB, Desc). 274 | 275 | %% @spec any(Got, Items, Desc) -> Result 276 | %% Got = any() 277 | %% Items = [any()] 278 | %% Desc = string() 279 | %% Result = true | false 280 | %% @doc Assert that an item is in a list. 281 | any(Got, Items, Desc) when is_function(Got) -> 282 | is(lists:any(Got, Items), true, Desc); 283 | any(Got, Items, Desc) -> 284 | is(lists:member(Got, Items), true, Desc). 285 | 286 | %% @spec none(Got, Items, Desc) -> Result 287 | %% Got = any() 288 | %% Items = [any()] 289 | %% Desc = string() 290 | %% Result = true | false 291 | %% @doc Assert that an item is not in a list. 292 | none(Got, Items, Desc) when is_function(Got) -> 293 | is(lists:any(Got, Items), false, Desc); 294 | none(Got, Items, Desc) -> 295 | is(lists:member(Got, Items), false, Desc). 296 | 297 | %% @spec fun_is(Fun, Expected, Desc) -> Result 298 | %% Fun = function() 299 | %% Expected = any() 300 | %% Desc = string() 301 | %% Result = true | false 302 | %% @doc Use an anonymous function to assert a pattern match. 303 | fun_is(Fun, Expected, Desc) when is_function(Fun) -> 304 | is(Fun(Expected), true, Desc). 305 | 306 | %% @spec expect_fun(ExpectFun, Got, Desc) -> Result 307 | %% ExpectFun = function() 308 | %% Got = any() 309 | %% Desc = string() 310 | %% Result = true | false 311 | %% @doc Use an anonymous function to assert a pattern match, using actual 312 | %% value as the argument to the function. 313 | expect_fun(ExpectFun, Got, Desc) -> 314 | evaluate(ExpectFun(Got), Got, ExpectFun, Desc). 315 | 316 | %% @spec expect_fun(ExpectFun, Got, Desc, ExpectStr) -> Result 317 | %% ExpectFun = function() 318 | %% Got = any() 319 | %% Desc = string() 320 | %% ExpectStr = string() 321 | %% Result = true | false 322 | %% @doc Use an anonymous function to assert a pattern match, using actual 323 | %% value as the argument to the function. 324 | expect_fun(ExpectFun, Got, Desc, ExpectStr) -> 325 | evaluate(ExpectFun(Got), Got, ExpectStr, Desc). 326 | 327 | %% @equiv skip(TestFun, "") 328 | skip(TestFun) when is_function(TestFun) -> 329 | skip(TestFun, ""). 330 | 331 | %% @spec skip(TestFun, Reason) -> ok 332 | %% TestFun = function() 333 | %% Reason = string() 334 | %% @doc Skip a test. 335 | skip(TestFun, Reason) when is_function(TestFun), is_list(Reason) -> 336 | begin_skip(Reason), 337 | catch TestFun(), 338 | end_skip(), 339 | ok. 340 | 341 | %% @spec skip(Q, TestFun, Reason) -> ok 342 | %% Q = true | false | function() 343 | %% TestFun = function() 344 | %% Reason = string() 345 | %% @doc Skips a test conditionally. The first argument to this function can 346 | %% either be the 'true' or 'false' atoms or a function that returns 'true' or 347 | %% 'false'. 348 | skip(QFun, TestFun, Reason) when is_function(QFun), is_function(TestFun), is_list(Reason) -> 349 | case QFun() of 350 | true -> begin_skip(Reason), TestFun(), end_skip(); 351 | _ -> TestFun() 352 | end, 353 | ok; 354 | 355 | skip(Q, TestFun, Reason) when is_function(TestFun), is_list(Reason), Q == true -> 356 | begin_skip(Reason), 357 | TestFun(), 358 | end_skip(), 359 | ok; 360 | 361 | skip(_, TestFun, Reason) when is_function(TestFun), is_list(Reason) -> 362 | TestFun(), 363 | ok. 364 | 365 | %% @private 366 | begin_skip(Reason) -> 367 | etap_server ! {self(), begin_skip, Reason}. 368 | 369 | %% @private 370 | end_skip() -> 371 | etap_server ! {self(), end_skip}. 372 | 373 | %% @spec contains_ok(string(), string(), string()) -> true | false 374 | %% @doc Assert that a string is contained in another string. 375 | contains_ok(Source, String, Desc) -> 376 | etap:isnt( 377 | string:str(Source, String), 378 | 0, 379 | Desc 380 | ). 381 | 382 | %% @spec is_before(string(), string(), string(), string()) -> true | false 383 | %% @doc Assert that a string comes before another string within a larger body. 384 | is_before(Source, StringA, StringB, Desc) -> 385 | etap:is_greater( 386 | string:str(Source, StringB), 387 | string:str(Source, StringA), 388 | Desc 389 | ). 390 | 391 | %% @doc Assert that a given variable is a pid. 392 | is_pid(Pid, Desc) when is_pid(Pid) -> etap:ok(true, Desc); 393 | is_pid(_, Desc) -> etap:ok(false, Desc). 394 | 395 | %% @doc Assert that a given process/pid is alive. 396 | is_alive(Pid, Desc) -> 397 | etap:ok(erlang:is_process_alive(Pid), Desc). 398 | 399 | %% @doc Assert that the current function of a pid is a given {M, F, A} tuple. 400 | is_mfa(Pid, MFA, Desc) -> 401 | etap:is({current_function, MFA}, erlang:process_info(Pid, current_function), Desc). 402 | 403 | %% @spec loaded_ok(atom(), string()) -> true | false 404 | %% @doc Assert that a module has been loaded successfully. 405 | loaded_ok(M, Desc) when is_atom(M) -> 406 | etap:fun_is(fun({module, _}) -> true; (_) -> false end, code:load_file(M), Desc). 407 | 408 | %% @spec can_ok(atom(), atom()) -> true | false 409 | %% @doc Assert that a module exports a given function. 410 | can_ok(M, F) when is_atom(M), is_atom(F) -> 411 | Matches = [X || {X, _} <- M:module_info(exports), X == F], 412 | etap:ok(Matches > 0, lists:concat([M, " can ", F])). 413 | 414 | %% @spec can_ok(atom(), atom(), integer()) -> true | false 415 | %% @doc Assert that a module exports a given function with a given arity. 416 | can_ok(M, F, A) when is_atom(M); is_atom(F), is_number(A) -> 417 | Matches = [X || X <- M:module_info(exports), X == {F, A}], 418 | etap:ok(Matches > 0, lists:concat([M, " can ", F, "/", A])). 419 | 420 | %% @spec has_attrib(M, A) -> true | false 421 | %% M = atom() 422 | %% A = atom() 423 | %% @doc Asserts that a module has a given attribute. 424 | has_attrib(M, A) when is_atom(M), is_atom(A) -> 425 | etap:isnt( 426 | proplists:get_value(A, M:module_info(attributes), 'asdlkjasdlkads'), 427 | 'asdlkjasdlkads', 428 | lists:concat([M, " has attribute ", A]) 429 | ). 430 | 431 | %% @spec has_attrib(M, A. V) -> true | false 432 | %% M = atom() 433 | %% A = atom() 434 | %% V = any() 435 | %% @doc Asserts that a module has a given attribute with a given value. 436 | is_attrib(M, A, V) when is_atom(M) andalso is_atom(A) -> 437 | etap:is( 438 | proplists:get_value(A, M:module_info(attributes)), 439 | [V], 440 | lists:concat([M, "'s ", A, " is ", V]) 441 | ). 442 | 443 | %% @spec is_behavior(M, B) -> true | false 444 | %% M = atom() 445 | %% B = atom() 446 | %% @doc Asserts that a given module has a specific behavior. 447 | is_behaviour(M, B) when is_atom(M) andalso is_atom(B) -> 448 | is_attrib(M, behaviour, B). 449 | 450 | %% @doc Assert that an exception is raised when running a given function. 451 | dies_ok(F, Desc) -> 452 | case (catch F()) of 453 | {'EXIT', _} -> etap:ok(true, Desc); 454 | _ -> etap:ok(false, Desc) 455 | end. 456 | 457 | %% @doc Assert that an exception is not raised when running a given function. 458 | lives_ok(F, Desc) -> 459 | etap:is(try_this(F), success, Desc). 460 | 461 | %% @doc Assert that the exception thrown by a function matches the given exception. 462 | throws_ok(F, Exception, Desc) -> 463 | try F() of 464 | _ -> etap:ok(nok, Desc) 465 | catch 466 | _:E -> 467 | etap:is(E, Exception, Desc) 468 | end. 469 | 470 | %% @private 471 | %% @doc Run a function and catch any exceptions. 472 | try_this(F) when is_function(F, 0) -> 473 | try F() of 474 | _ -> success 475 | catch 476 | throw:E -> {throw, E}; 477 | error:E -> {error, E}; 478 | exit:E -> {exit, E} 479 | end. 480 | 481 | %% @private 482 | %% @doc Start the etap_server process if it is not running already. 483 | ensure_test_server() -> 484 | case whereis(etap_server) of 485 | undefined -> 486 | proc_lib:start(?MODULE, start_etap_server,[]); 487 | _ -> 488 | diag("The test server is already running.") 489 | end. 490 | 491 | %% @private 492 | %% @doc Start the etap_server loop and register itself as the etap_server 493 | %% process. 494 | start_etap_server() -> 495 | catch register(etap_server, self()), 496 | proc_lib:init_ack(ok), 497 | etap:test_server(#test_state{ 498 | planned = 0, 499 | count = 0, 500 | pass = 0, 501 | fail = 0, 502 | skip = 0, 503 | skip_reason = "" 504 | }). 505 | 506 | 507 | %% @private 508 | %% @doc The main etap_server receive/run loop. The etap_server receive loop 509 | %% responds to seven messages apperatining to failure or passing of tests. 510 | %% It is also used to initiate the testing process with the {_, plan, _} 511 | %% message that clears the current test state. 512 | test_server(State) -> 513 | NewState = receive 514 | {_From, plan, unknown} -> 515 | io:format("# Current time local ~s~n", [datetime(erlang:localtime())]), 516 | io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]), 517 | State#test_state{ 518 | planned = -1, 519 | count = 0, 520 | pass = 0, 521 | fail = 0, 522 | skip = 0, 523 | skip_reason = "" 524 | }; 525 | {_From, plan, N} -> 526 | io:format("# Current time local ~s~n", [datetime(erlang:localtime())]), 527 | io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]), 528 | io:format("1..~p~n", [N]), 529 | State#test_state{ 530 | planned = N, 531 | count = 0, 532 | pass = 0, 533 | fail = 0, 534 | skip = 0, 535 | skip_reason = "" 536 | }; 537 | {_From, begin_skip, Reason} -> 538 | State#test_state{ 539 | skip = 1, 540 | skip_reason = Reason 541 | }; 542 | {_From, end_skip} -> 543 | State#test_state{ 544 | skip = 0, 545 | skip_reason = "" 546 | }; 547 | {_From, pass, Desc} -> 548 | FullMessage = skip_diag( 549 | " - " ++ Desc, 550 | State#test_state.skip, 551 | State#test_state.skip_reason 552 | ), 553 | io:format("ok ~p ~s~n", [State#test_state.count + 1, FullMessage]), 554 | State#test_state{ 555 | count = State#test_state.count + 1, 556 | pass = State#test_state.pass + 1 557 | }; 558 | 559 | {_From, fail, Desc} -> 560 | FullMessage = skip_diag( 561 | " - " ++ Desc, 562 | State#test_state.skip, 563 | State#test_state.skip_reason 564 | ), 565 | io:format("not ok ~p ~s~n", [State#test_state.count + 1, FullMessage]), 566 | State#test_state{ 567 | count = State#test_state.count + 1, 568 | fail = State#test_state.fail + 1 569 | }; 570 | {From, state} -> 571 | From ! {etap, State}, 572 | State; 573 | {_From, diag, Message} -> 574 | io:format("~s~n", [Message]), 575 | State; 576 | {From, count} -> 577 | From ! {etap, State#test_state.count}, 578 | State; 579 | {From, is_skip} -> 580 | From ! {etap, State#test_state.skip}, 581 | State; 582 | done -> 583 | exit(normal) 584 | end, 585 | test_server(NewState). 586 | 587 | %% @private 588 | %% @doc Process the result of a test and send it to the etap_server process. 589 | mk_tap(Result, Desc) -> 590 | etap_server ! {self(), is_skip}, 591 | IsSkip = receive 592 | {etap, Response} -> Response 593 | end, 594 | case [IsSkip, Result] of 595 | [_, true] -> 596 | etap_server ! {self(), pass, Desc}, 597 | true; 598 | [1, _] -> 599 | etap_server ! {self(), pass, Desc}, 600 | true; 601 | _ -> 602 | etap_server ! {self, fail, Desc}, 603 | false 604 | end. 605 | 606 | %% @private 607 | %% @doc Format a date/time string. 608 | datetime(DateTime) -> 609 | {{Year, Month, Day}, {Hour, Min, Sec}} = DateTime, 610 | io_lib:format("~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B", [Year, Month, Day, Hour, Min, Sec]). 611 | 612 | %% @private 613 | %% @doc Craft an output message taking skip/todo into consideration. 614 | skip_diag(Message, 0, _) -> 615 | Message; 616 | skip_diag(_Message, 1, "") -> 617 | " # SKIP"; 618 | skip_diag(_Message, 1, Reason) -> 619 | " # SKIP : " ++ Reason. 620 | -------------------------------------------------------------------------------- /src/overview.edoc: -------------------------------------------------------------------------------- 1 | 2 | @title A TAP testing client. 3 | @author Nick Gerakines 4 | @author Jeremy Wall 5 | @version 0.3.4 6 | @doc A TAP testing client. 7 | 8 |

etap is a collection of Erlang modules that provide a TAP testing client library. These modules allow developers to create extensive and comprehensive tests covering many aspects of application and module development. This includes simple assertions, exceptions, the application behavior and event web requests. This library was originally written by Jeremy wall.

9 |

As per the TAP wiki:

10 |
TAP, the Test Anything Protocol, is a simple text-based interface between testing modules in a test harness. TAP started life as part of the test harness for Perl but now has implementations in C/C++, Python, PHP, Perl and probably others by the time you read this.
11 |

These modules are not meant to compete with eunit, but to offer a more general testing facility that isn't provides by eunit.

12 |
http://en.wikipedia.org/wiki/Test_Anything_Protocol
13 | http://testanything.org
14 | 
15 | 16 |

CREATING TESTS

17 | 18 |

A test is any number of etap:* or etap_*:* tests that are part of a test plan. When a plan is created using etap:plan/1, a process is started that tracks the status of the tests executed and handles diagnostic output.

19 |

Consider the following example test plan:

20 |
etap:plan(3),
21 | etap:ok(true, "the 'true' atom is recognized"),
22 | etap:is(1 + 1, 2, "simple math"),
23 | etap:isnt(2 + 2, 5, "some would argue"),
24 | etap:end_tests().
25 | 
26 |

At this time, etap does not support pattern matching. To work around this there are a number of utility tests that can be used. The etap:any/3, etap:none/3 and etap:fun_is/3 use functions to return either true or false.

27 |
Numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9],
28 | FunWithNumbers = fun(X) case X of [1, 2, 3 | _] -> true; _ -> false end end,
29 | etap:fun_is(FunWithNumbers, Numbers, "Match the first three numbers").
30 | 
31 | 32 |

There are many examples in t/*.erl.

33 | 34 |

BUILD & INSTALL

35 | 36 |

To build this library, from the root directory execute the make command. You should also execute the make test command to verify that the library functions correctly on your system. If you have the Perl module TAP::Harness you can use it to collect and display test results using the make prove target.

37 | 38 |
$ make
39 | $ make test
40 | $ make prove
41 | 
42 | 43 |

If you choose to run the make test command then please be sure to make clean after to remove any of the temporary beam files created by the tests in the t/ directory.

44 | 45 |

The included tests cover the basic functionality of the etap modules. They can also be used as a reference when writing your own tests.

46 | 47 |

To install etap you need to create the etap/ebin/ directory in your current Erlang library and copy all of the .beam files created by the make file.

48 | 49 |
$ sudo mkdir -p /usr/lib/erlang/lib/etap-0.3.4/ebin
50 | $ make clean && make
51 | $ sudo cp ebin/*.beam /usr/lib/erlang/lib/etap-0.3.4/ebin/
52 | 
53 | 54 |

The make dist-src target can be used to create source distributions for further packaging and deployment.

55 | 56 |

SUPPORTED FUNCTIONALITY

57 | 58 |

There are a number of proposals listed on the TAP wiki that are not supported by this library. Please be aware of this when creating your tests.

59 | 60 |
    61 |
  • LIMITED SUPPORTED: TAP diagnostic syntax
  • 62 |
  • LIMITED SUPPORTED: TAP meta information
  • 63 |
  • LIMITED SUPPORTED: TAP logging syntax
  • 64 |
  • NOT SUPPORTED: Test groups
  • 65 |
  • NOT SUPPORTED: Test blocks
  • 66 |
  • LIMITED SUPPORTED: SKIP
  • 67 |
  • NOT SUPPORTED: TODO
  • 68 |
  • LIMITED SUPPORTED: TAP datetime
  • 69 |
70 | 71 |

CREDITS

72 | 73 |

2008 Nick Gerakines
74 | 2007-2008 Jeremy Wall
75 | 2008 Jacob Vorreuter

76 | -------------------------------------------------------------------------------- /support/include.mk: -------------------------------------------------------------------------------- 1 | ## -*- makefile -*- 2 | 3 | ERL := erl 4 | ERLC := $(ERL)c 5 | 6 | INCLUDE_DIRS := ../include 7 | DOC_DIR := ../doc 8 | EBIN_DIRS := 9 | ERLC_FLAGS := -W $(INCLUDE_DIRS:../%=-I ../%) $(EBIN_DIRS:%=-pa %) 10 | 11 | ifndef no_debug_info 12 | ERLC_FLAGS += +debug_info 13 | endif 14 | 15 | ifdef debug 16 | ERLC_FLAGS += -Ddebug 17 | endif 18 | 19 | EBIN_DIR := ../ebin 20 | EMULATOR := beam 21 | 22 | ERL_SOURCES := $(wildcard *.erl) 23 | ERL_DOCS := $(wildcard *.erl) $(wildcard *.edoc) 24 | ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl) 25 | ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR)) 26 | ERL_OBJECTS_LOCAL := $(ERL_SOURCES:%.erl=./%.$(EMULATOR)) 27 | APP_FILES := $(wildcard *.app) 28 | EBIN_FILES = $(ERL_OBJECTS) $(APP_FILES:%.app=../ebin/%.app) 29 | MODULES = $(ERL_SOURCES:%.erl=%) 30 | 31 | ../ebin/%.app: %.app 32 | cp $< $@ 33 | 34 | $(EBIN_DIR)/%.$(EMULATOR): %.erl 35 | $(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $< 36 | 37 | ./%.$(EMULATOR): %.erl 38 | $(ERLC) $(ERLC_FLAGS) -o . $< 39 | -------------------------------------------------------------------------------- /t/Makefile: -------------------------------------------------------------------------------- 1 | include ../support/include.mk 2 | 3 | all: $(EBIN_FILES) 4 | 5 | clean: 6 | rm -rf $(EBIN_FILES) erl_crash.dump 7 | 8 | test: $(MODULES) 9 | 10 | ./$(MODULES): 11 | @echo "Running tests for $@" 12 | erl -pa ../ebin -run $@ start -run init stop -noshell 13 | -------------------------------------------------------------------------------- /t/etap_t_001.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_001). 2 | -export([start/0]). 3 | 4 | -record(tester, {xx, yy, zz}). 5 | 6 | -include("etap.hrl"). 7 | 8 | start() -> 9 | etap:plan(21), 10 | etap:diag("Test the core etap module and it's functions."), 11 | etap:ok(true, "Proving etap:ok/2 needs true"), 12 | etap:not_ok(false, "Proving etap:not_ok/2 needs false"), 13 | etap:is(1, 1, "Proving etap:is/3 must have 2 equal values"), 14 | etap:isnt(1, 2, "Proving etap:isnt/3 can not have 2 equal values"), 15 | etap:is_greater(3, 1, "Proving 3 is greater than 1."), 16 | etap:is([foo, bar, baz], [foo, bar, baz], "Proving etap:is/3 can use complex structures"), 17 | etap:isnt([foo, bar, baz], [bar, foo, baz], "Proving etap:isnt/3 can use complex structures"), 18 | 19 | Foo = #tester{ xx = 1, yy = 2, zz = 3}, 20 | Bar = #tester{ xx = 4, yy = 5, zz = 6}, 21 | 22 | etap:is(Foo, Foo, "Proving etap:is/3 can use records"), 23 | etap:isnt(Foo, Bar, "Proving etap:isnt/3 can use records"), 24 | etap:any(fun(foo) -> true; (_)-> false end, [foo, bar, baz], "Proving etap:any/3 works with atoms"), 25 | etap:any(fun({_, this}) -> true; (_)-> false end, [{1, none}, {2, all}, {3, this}], "Proving etap:any/3 works with lists of tuples"), 26 | etap:any(fun([{record, value} | _]) -> true; (_)-> false end, [[{something, nothing}, {somewhere, nowhere}], [{name, nick}, {has_family, true}], [{record, value}, {term, atom}]], "Proving etap:none/3 works with simple structures"), 27 | etap:none(fun(the_bitch) -> true; (_)-> false end, lists:seq(1, 99), "Proving etap:none/3 works with simple structures"), 28 | etap:none(fun([{color, red} | _]) -> true; (_)-> false end, [[{something, nothing}, {somewhere, nowhere}], [{name, nick}, {has_family, true}], [{record, value}, {term, atom}]], "Proving etap:none/3 works with simple structures"), 29 | etap:fun_is(fun(Expected) -> case Expected of "abc" ++ _ -> true; _ -> false end end, "abcdefg", "fun_is/3 works with simple lists"), 30 | etap:fun_is(fun(Expected) -> case Expected of {foo, _} -> true; _ -> false end end, {foo, bar}, "fun_is/3 works with simple lists"), 31 | etap:expect_fun(fun(Got) -> case Got of "abc" ++ _ -> true; _ -> false end end, "abcdefg", "expect_fun/3 works with simple lists"), 32 | etap:expect_fun(fun(Got) -> case Got of {foo, _} -> true; _ -> false end end, {foo, bar}, "expect_fun/3 works with simple structures"), 33 | ?etap_match("abcdefg", "abc" ++ _, "?etap_match/3 works with simple lists"), 34 | ?etap_match({foo, bar}, {foo, _}, "?etap_match/3 works with simple structures"), 35 | etap:skip(fun() -> ?etap_match({foo, bar, baz}, {foo, _}, "Example ?etap_match/3 failure") end, "its ok for this to fail"), 36 | etap:end_tests(). 37 | -------------------------------------------------------------------------------- /t/etap_t_002.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_002). 2 | -export([start/0]). 3 | 4 | start() -> 5 | etap:plan(67), 6 | etap:diag("Test the etap_can module."), 7 | test_etap(), 8 | test_etap_application(), 9 | test_etap_can(), 10 | test_etap_exception(), 11 | test_etap_request(), 12 | test_etap_web(), 13 | test_etap_process(), 14 | etap:end_tests(). 15 | 16 | test_etap() -> 17 | etap_can:loaded_ok(etap, "module 'etap' loaded"), 18 | etap_can:can_ok(etap, plan), 19 | etap_can:can_ok(etap, plan, 1), 20 | etap_can:can_ok(etap, end_tests), 21 | etap_can:can_ok(etap, end_tests, 0), 22 | etap_can:can_ok(etap, diag), 23 | etap_can:can_ok(etap, diag, 1), 24 | etap_can:can_ok(etap, ok), 25 | etap_can:can_ok(etap, ok, 2), 26 | etap_can:can_ok(etap, not_ok), 27 | etap_can:can_ok(etap, not_ok, 2), 28 | etap_can:can_ok(etap, is), 29 | etap_can:can_ok(etap, is, 3), 30 | etap_can:can_ok(etap, is_greater), 31 | etap_can:can_ok(etap, is_greater, 3), 32 | etap_can:can_ok(etap, isnt), 33 | etap_can:can_ok(etap, isnt, 3), 34 | etap_can:can_ok(etap, any), 35 | etap_can:can_ok(etap, any, 3), 36 | etap_can:can_ok(etap, none), 37 | etap_can:can_ok(etap, none, 3), 38 | etap_can:can_ok(etap, fun_is), 39 | etap_can:can_ok(etap, fun_is, 3), 40 | ok. 41 | 42 | test_etap_application() -> 43 | etap_can:loaded_ok(etap_application, "module 'etap_application' loaded"), 44 | etap_can:can_ok(etap_application, start_ok), 45 | etap_can:can_ok(etap_application, start_ok, 2), 46 | etap_can:can_ok(etap_application, ensure_loaded), 47 | etap_can:can_ok(etap_application, ensure_loaded, 3), 48 | etap_can:can_ok(etap_application, pg2_group_exists), 49 | etap_can:can_ok(etap_application, pg2_group_exists, 2), 50 | etap_can:can_ok(etap_application, pg2_group_exists), 51 | etap_can:can_ok(etap_application, pg2_group_exists, 2), 52 | etap_can:can_ok(etap_application, load_ok), 53 | etap_can:can_ok(etap_application, load_ok, 2), 54 | ok. 55 | 56 | test_etap_can() -> 57 | etap_can:loaded_ok(etap_can, "module 'etap_can' loaded"), 58 | etap_can:can_ok(etap_can, loaded_ok), 59 | etap_can:can_ok(etap_can, loaded_ok, 2), 60 | etap_can:can_ok(etap_can, can_ok), 61 | etap_can:can_ok(etap_can, can_ok, 2), 62 | etap_can:can_ok(etap_can, can_ok, 3), 63 | etap_can:can_ok(etap_can, has_attrib, 2), 64 | etap_can:can_ok(etap_can, is_attrib, 3), 65 | etap_can:can_ok(etap_can, is_behaviour, 2), 66 | ok. 67 | 68 | test_etap_exception() -> 69 | etap_can:loaded_ok(etap_exception, "module 'etap_exception' loaded"), 70 | etap_can:can_ok(etap_exception, dies_ok), 71 | etap_can:can_ok(etap_exception, dies_ok, 2), 72 | etap_can:can_ok(etap_exception, lives_ok), 73 | etap_can:can_ok(etap_exception, lives_ok, 2), 74 | etap_can:can_ok(etap_exception, throws_ok), 75 | etap_can:can_ok(etap_exception, throws_ok, 3), 76 | ok. 77 | 78 | test_etap_request() -> 79 | etap_can:loaded_ok(etap_request, "module 'etap_request' loaded"), 80 | etap_can:can_ok(etap_request, status_is), 81 | etap_can:can_ok(etap_request, status_is, 3), 82 | ok. 83 | 84 | test_etap_web() -> 85 | etap_can:loaded_ok(etap_web, "module 'etap_web' loaded"), 86 | etap_can:can_ok(etap_web, simple_200), 87 | etap_can:can_ok(etap_web, simple_200, 2), 88 | etap_can:can_ok(etap_web, simple_404), 89 | etap_can:can_ok(etap_web, simple_404, 2), 90 | etap_can:can_ok(etap_web, build_request), 91 | etap_can:can_ok(etap_web, build_request, 4), 92 | ok. 93 | 94 | test_etap_process() -> 95 | etap_can:loaded_ok(etap_process, "module 'etap_process' loaded"), 96 | etap_can:can_ok(etap_process, is_pid), 97 | etap_can:can_ok(etap_process, is_pid, 2), 98 | etap_can:can_ok(etap_process, is_alive), 99 | etap_can:can_ok(etap_process, is_alive, 2), 100 | etap_can:can_ok(etap_process, is_mfa), 101 | etap_can:can_ok(etap_process, is_mfa, 3), 102 | ok. 103 | -------------------------------------------------------------------------------- /t/etap_t_003.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_003). 2 | -export([start/0]). 3 | 4 | start() -> 5 | etap:plan(3), 6 | etap_exception:dies_ok(fun() -> exit("some error") end, "throwing an error dies"), 7 | etap_exception:lives_ok(fun() -> _M = 1 end, "not throwing an error lives"), 8 | etap_exception:throws_ok(fun() -> throw("error_foo") end, "error_foo", "throwing an error dies"), 9 | etap:end_tests(). 10 | -------------------------------------------------------------------------------- /t/etap_t_004.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_004). 2 | -export([start/0, start_loop/1]). 3 | 4 | start() -> 5 | etap:plan(6), 6 | etap_application:load_ok(inets, "Application inets-5.0.13 loads"), 7 | etap_application:start_ok(inets, "Application inets-5.0.13 starts"), 8 | etap:skip(fun() -> 9 | etap_application:ensure_loaded(inets, "5.1", "Application inets-5.1 is loaded and running.") 10 | end, "Versions may be different"), 11 | 12 | spawn(?MODULE, start_loop, [test_group_a]), 13 | timer:sleep(100), 14 | etap_application:pg2_group_exists(test_group_a, "pg2 group test_group_a exists"), 15 | etap_application:pg2_group_doesntexist(test_group_b, "pg2 group test_group_b exists"), 16 | 17 | spawn(?MODULE, start_loop, [test_group_b]), 18 | timer:sleep(100), 19 | etap_application:pg2_group_exists(test_group_b, "pg2 group test_group_b exists"), 20 | 21 | [ Pid ! done || Pid <- pg2:get_members(test_group_a)], 22 | [ Pid ! done || Pid <- pg2:get_members(test_group_b)], 23 | 24 | etap:end_tests(). 25 | 26 | start_loop(GroupName) -> 27 | pg2:create(GroupName), 28 | pg2:join(GroupName, self()), 29 | loop(). 30 | 31 | loop() -> 32 | receive 33 | done -> exit(normal); 34 | _ -> ok 35 | end, 36 | loop(). 37 | -------------------------------------------------------------------------------- /t/etap_t_005.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_005). 2 | -export([start/0]). 3 | 4 | start() -> 5 | etap:plan(4), 6 | etap_application:start_ok(inets, "Application inets starts"), 7 | etap_web:simple_200("http://socklabs.com/", "Web request against http://socklabs.com/ returns a 200"), 8 | etap_web:simple_404("http://socklabs.com/404.html", "Web request against http://socklabs.com/404.html returns a 404"), 9 | 10 | Request = etap_web:build_request(get, "http://socklabs.com/", [], []), 11 | Request:status_is(200, "Web request against http://socklabs.com/ returns a 200"), 12 | 13 | etap:end_tests(). 14 | -------------------------------------------------------------------------------- /t/etap_t_006.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_006). 2 | -export([start/0, start_loop/1]). 3 | 4 | start() -> 5 | etap:plan(3), 6 | 7 | etap_process:is_pid(spawn(?MODULE, start_loop, [test_group_a]), "Spawned process is a pid"), 8 | etap_process:is_alive(spawn(?MODULE, start_loop, [test_group_a]), "Spawned process is alive"), 9 | etap_process:is_mfa(spawn(?MODULE, start_loop, [test_group_a]), {?MODULE, start_loop, 1}, "Spawned process has correct MFA"), 10 | [ Pid ! done || Pid <- pg2:get_members(test_group_a)], 11 | etap:end_tests(). 12 | 13 | start_loop(GroupName) -> 14 | pg2:create(GroupName), 15 | pg2:join(GroupName, self()), 16 | loop(). 17 | 18 | loop() -> 19 | timer:sleep(10000), 20 | receive done -> exit(normal); _ -> ok end, 21 | loop(). 22 | -------------------------------------------------------------------------------- /t/etap_t_008.erl: -------------------------------------------------------------------------------- 1 | -module (etap_t_008). 2 | 3 | -export([start/0]). 4 | 5 | start() -> 6 | etap:plan(3), 7 | etap:diag("probably stupid but I don't know what else to test this against"), 8 | etap_can:has_attrib(kernel, behaviour), 9 | etap_can:is_attrib(kernel, behaviour, supervisor), 10 | etap_can:is_behaviour(kernel, supervisor), 11 | etap:end_tests(). 12 | -------------------------------------------------------------------------------- /t/etap_t_009.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_009). 2 | 3 | -export([start/0]). 4 | 5 | start() -> 6 | etap:plan(2), 7 | Body = "This is a test.", 8 | etap_string:contains_ok(Body, "test", "Proving contains_ok/3."), 9 | etap_string:is_before(Body, "is", "a test", "Proving is_before/4"), 10 | etap:end_tests(). 11 | -------------------------------------------------------------------------------- /t/etap_t_010.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_010). 2 | 3 | -export([start/0]). 4 | 5 | start() -> 6 | etap:plan(2), 7 | etap:skip(fun() -> 8 | etap:ok(false, "This shouldn't be false ...") 9 | end), 10 | etap:skip(fun() -> 11 | etap:ok(false, "This shouldn't be false ...") 12 | end, "Fails for reasons unknown"), 13 | etap:end_tests(). 14 | -------------------------------------------------------------------------------- /t/etap_t_011.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_011). 2 | 3 | -export([start/0]). 4 | 5 | start() -> 6 | etap:plan(skip), 7 | _Skipped = fun() -> 8 | etap:skip(fun() -> 9 | etap:ok(false, "This shouldn't be false ...") 10 | end), 11 | etap:skip(fun() -> 12 | etap:ok(false, "This shouldn't be false ...") 13 | end, "Fails for reasons unknown") 14 | end, 15 | etap:end_tests(). 16 | -------------------------------------------------------------------------------- /t/etap_t_012.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_012). 2 | 3 | -export([start/0]). 4 | 5 | start() -> 6 | etap:plan({skip, "Requires psychic powers"}), 7 | _Skipped = fun() -> 8 | etap:skip(fun() -> 9 | etap:ok(false, "This shouldn't be false ...") 10 | end), 11 | etap:skip(fun() -> 12 | etap:ok(false, "This shouldn't be false ...") 13 | end, "Fails for reasons unknown") 14 | end, 15 | etap:end_tests(). 16 | -------------------------------------------------------------------------------- /t/etap_t_013.erl: -------------------------------------------------------------------------------- 1 | -module(etap_t_013). 2 | -export([start/0]). 3 | 4 | start() -> 5 | etap:plan(unknown), 6 | etap:is(1, 1, "one down"), 7 | etap:is(3, 3, "two down"), 8 | etap:is(4, 4, "three down"), 9 | etap:is(5, 5, "four down"), 10 | etap:end_tests(), 11 | ok. 12 | --------------------------------------------------------------------------------