summaryrefslogtreecommitdiffstats
path: root/comm/third_party/botan/doc/dev_ref/test_framework.rst
diff options
context:
space:
mode:
Diffstat (limited to 'comm/third_party/botan/doc/dev_ref/test_framework.rst')
-rw-r--r--comm/third_party/botan/doc/dev_ref/test_framework.rst314
1 files changed, 314 insertions, 0 deletions
diff --git a/comm/third_party/botan/doc/dev_ref/test_framework.rst b/comm/third_party/botan/doc/dev_ref/test_framework.rst
new file mode 100644
index 0000000000..241c51bd1a
--- /dev/null
+++ b/comm/third_party/botan/doc/dev_ref/test_framework.rst
@@ -0,0 +1,314 @@
+Test Framework
+================
+
+Botan uses a custom-built test framework. Some portions of it are
+quite similar to assertion-based test frameworks such as Catch or
+Gtest, but it also includes many features which are well suited for
+testing cryptographic algorithms.
+
+The intent is that the test framework and the test suite evolve
+symbiotically; as a general rule of thumb if a new function would make
+the implementation of just two distinct tests simpler, it is worth
+adding to the framework on the assumption it will prove useful again.
+Feel free to propose changes to the test system.
+
+When writing a new test, there are three key classes that are used,
+namely ``Test``, ``Test::Result``, and ``Text_Based_Test``. A ``Test``
+(or ``Test_Based_Test``) runs and returns one or more ``Test::Result``.
+
+Namespaces in Test
+-------------------
+
+The test code lives in a distinct namespace (``Botan_Tests``) and all
+code in the tests which calls into the library should use the
+namespace prefix ``Botan::`` rather than a ``using namespace``
+declaration. This makes it easier to see where the test is actually
+invoking the library, and makes it easier to reuse test code for
+applications.
+
+Test Data
+-----------
+
+The test framework is heavily data driven. As of this writing, there
+is about 1 Mib of test code and 17 MiB of test data. For most (though
+certainly not all) tests, it is better to add a data file representing
+the input and outputs, and run the tests over it. Data driven tests
+make adding or editing tests easier, for example by writing scripts
+which produce new test data and output it in the expected format.
+
+Test
+--------
+
+.. cpp:class:: Test
+
+ .. cpp:function:: virtual std::vector<Test::Result> run() = 0
+
+ This is the key function of a ``Test``: it executes and returns a
+ list of results. Almost all other functions on ``Test`` are
+ static functions which just serve as helper functions for ``run``.
+
+ .. cpp:function:: static std::string read_data_file(const std::string& path)
+
+ Return the contents of a data file and return it as a string.
+
+ .. cpp:function:: static std::vector<uint8_t> read_binary_data_file(const std::string& path)
+
+ Return the contents of a data file and return it as a vector of
+ bytes.
+
+ .. cpp:function:: static std::string data_file(const std::string& what)
+
+ An alternative to ``read_data_file`` and ``read_binary_file``,
+ use only as a last result, typically for library APIs which
+ themselves accept a filename rather than a data blob.
+
+ .. cpp:function:: static bool run_long_tests() const
+
+ Returns true if the user gave option ``--run-long-tests``. Use
+ this to gate particularly time-intensive tests.
+
+ .. cpp:function:: static Botan::RandomNumberGenerator& rng()
+
+ Returns a reference to a fast, not cryptographically secure
+ random number generator. It is deterministicly seeded with the
+ seed logged by the test runner, so it is possible to reproduce
+ results in "random" tests.
+
+Tests are registered using the macro ``BOTAN_REGISTER_TEST`` which
+takes 2 arguments: the name of the test and the name of the test class.
+For example given a ``Test`` instance named ``MyTest``, use::
+
+ BOTAN_REGISTER_TEST("mytest", MyTest);
+
+All test names should contain only lowercase letters, numbers, and
+underscore.
+
+Test::Result
+-------------
+
+.. cpp:class:: Test::Result
+
+ A ``Test::Result`` records one or more tests on a particular topic
+ (say "AES-128/CBC" or "ASN.1 date parsing"). Most of the test functions
+ return true or false if the test was successful or not; this allows
+ performing conditional blocks as a result of earlier tests::
+
+ if(result.test_eq("first value", produced, expected))
+ {
+ // further tests that rely on the initial test being correct
+ }
+
+ Only the most commonly used functions on ``Test::Result`` are documented here,
+ see the header ``tests.h`` for more.
+
+ .. cpp:function:: Test::Result(const std::string& who)
+
+ Create a test report on a particular topic. This will be displayed in the
+ test results.
+
+ .. cpp:function:: bool test_success()
+
+ Report a test that was successful.
+
+ .. cpp:function:: bool test_success(const std::string& note)
+
+ Report a test that was successful, including some comment.
+
+ .. cpp:function:: bool test_failure(const std::string& err)
+
+ Report a test failure of some kind. The error string will be logged.
+
+ .. cpp:function:: bool test_failure(const std::string& what, const std::string& error)
+
+ Report a test failure of some kind, with a description of what failed and
+ what the error was.
+
+ .. cpp:function:: void test_failure(const std::string& what, const uint8_t buf[], size_t buf_len)
+
+ Report a test failure due to some particular input, which is provided as
+ arguments. Normally this is only used if the test was using some
+ randomized input which unexpectedly failed, since if the input is
+ hardcoded or from a file it is easier to just reference the test number.
+
+ .. cpp:function:: bool test_eq(const std::string& what, const std::string& produced, const std::string& expected)
+
+ Compare to strings for equality.
+
+ .. cpp:function:: bool test_ne(const std::string& what, const std::string& produced, const std::string& expected)
+
+ Compare to strings for non-equality.
+
+ .. cpp:function:: bool test_eq(const char* producer, const std::string& what, \
+ const uint8_t produced[], size_t produced_len, \
+ const uint8_t expected[], size_t expected_len)
+
+ Compare two arrays for equality.
+
+ .. cpp:function:: bool test_ne(const char* producer, const std::string& what, \
+ const uint8_t produced[], size_t produced_len, \
+ const uint8_t expected[], size_t expected_len)
+
+ Compare two arrays for non-equality.
+
+ .. cpp:function:: bool test_eq(const std::string& producer, const std::string& what, \
+ const std::vector<uint8_t>& produced, \
+ const std::vector<uint8_t>& expected)
+
+ Compare two vectors for equality.
+
+ .. cpp:function:: bool test_ne(const std::string& producer, const std::string& what, \
+ const std::vector<uint8_t>& produced, \
+ const std::vector<uint8_t>& expected)
+
+ Compare two vectors for non-equality.
+
+ .. cpp:function:: bool confirm(const std::string& what, bool expr)
+
+ Test that some expression evaluates to ``true``.
+
+ .. cpp:function:: template<typename T> bool test_not_null(const std::string& what, T* ptr)
+
+ Verify that the pointer is not null.
+
+ .. cpp:function:: bool test_lt(const std::string& what, size_t produced, size_t expected)
+
+ Test that ``produced`` < ``expected``.
+
+ .. cpp:function:: bool test_lte(const std::string& what, size_t produced, size_t expected)
+
+ Test that ``produced`` <= ``expected``.
+
+ .. cpp:function:: bool test_gt(const std::string& what, size_t produced, size_t expected)
+
+ Test that ``produced`` > ``expected``.
+
+ .. cpp:function:: bool test_gte(const std::string& what, size_t produced, size_t expected)
+
+ Test that ``produced`` >= ``expected``.
+
+ .. cpp:function:: bool test_throws(const std::string& what, std::function<void ()> fn)
+
+ Call a function and verify it throws an exception of some kind.
+
+ .. cpp:function:: bool test_throws(const std::string& what, const std::string& expected, std::function<void ()> fn)
+
+ Call a function and verify it throws an exception of some kind
+ and that the exception message exactly equals ``expected``.
+
+Text_Based_Test
+-----------------
+
+A ``Text_Based_Text`` runs tests that are produced from a text file
+with a particular format which looks somewhat like an INI-file::
+
+ # Comments begin with # and continue to end of line
+ [Header]
+ # Test 1
+ Key1 = Value1
+ Key2 = Value2
+
+ # Test 2
+ Key1 = Value1
+ Key2 = Value2
+
+.. cpp:class:: VarMap
+
+ An object of this type is passed to each invocation of the text-based test.
+ It is used to access the test variables. All access takes a key, which is
+ one of the strings which was passed to the constructor of ``Text_Based_Text``.
+ Accesses are either required (``get_req_foo``), in which case an exception is
+ throwing if the key is not set, or optional (``get_opt_foo``) in which case
+ the test provides a default value which is returned if the key was not set
+ for this particular instance of the test.
+
+ .. cpp:function:: std::vector<uint8_t> get_req_bin(const std::string& key) const
+
+ Return a required binary string. The input is assumed to be hex encoded.
+
+ .. cpp:function:: std::vector<uint8_t> get_opt_bin(const std::string& key) const
+
+ Return an optional binary string. The input is assumed to be hex encoded.
+
+ .. cpp:function:: std::vector<std::vector<uint8_t>> get_req_bin_list(const std::string& key) const
+
+ .. cpp:function:: Botan::BigInt get_req_bn(const std::string& key) const
+
+ Return a required BigInt. The input can be decimal or (with "0x" prefix) hex encoded.
+
+ .. cpp:function:: Botan::BigInt get_opt_bn(const std::string& key, const Botan::BigInt& def_value) const
+
+ Return an optional BigInt. The input can be decimal or (with "0x" prefix) hex encoded.
+
+ .. cpp:function:: std::string get_req_str(const std::string& key) const
+
+ Return a required text string.
+
+ .. cpp:function:: std::string get_opt_str(const std::string& key, const std::string& def_value) const
+
+ Return an optional text string.
+
+ .. cpp:function:: size_t get_req_sz(const std::string& key) const
+
+ Return a required integer. The input should be decimal.
+
+ .. cpp:function:: size_t get_opt_sz(const std::string& key, const size_t def_value) const
+
+ Return an optional integer. The input should be decimal.
+
+.. cpp:class:: Text_Based_Test : public Test
+
+ .. cpp:function:: Text_Based_Test(const std::string& input_file, \
+ const std::string& required_keys, \
+ const std::string& optional_keys = "")
+
+ This constructor is
+
+ .. note::
+ The final element of required_keys is the "output key", that is
+ the key which signifies the boundary between one test and the next.
+ When this key is seen, ``run_one_test`` will be invoked. In the
+ test input file, this key must always appear least for any particular
+ test. All the other keys may appear in any order.
+
+ .. cpp:function:: Test::Result run_one_test(const std::string& header, \
+ const VarMap& vars)
+
+ Runs a single test and returns the result of it. The ``header``
+ parameter gives the value (if any) set in a ``[Header]`` block.
+ This can be useful to distinguish several types of tests within a
+ single file, for example "[Valid]" and "[Invalid]".
+
+ .. cpp:function:: bool clear_between_callbacks() const
+
+ By default this function returns ``false``. If it returns
+ ``true``, then when processing the data in the file, variables
+ are not cleared between tests. This can be useful when several
+ tests all use some common parameters.
+
+Test Runner
+-------------
+
+If you are simply writing a new test there should be no need to modify
+the runner, however it can be useful to be aware of its abilities.
+
+The runner can run tests concurrently across many cores. By default single
+threaded execution is used, but you can use ``--test-threads`` option to
+specify the number of threads to use. If you use ``--test-threads=0`` then
+the runner will probe the number of active CPUs and use that (but limited
+to at most 16). If you want to run across many cores on a large machine,
+explicitly specify a thread count. The speedup is close to linear.
+
+The RNG used in the tests is deterministic, and the seed is logged for each
+execution. You can cause the random sequence to repeat using ``--drbg-seed``
+option.
+
+.. note::
+ Currently the RNG is seeded just once at the start of execution. So you
+ must run the exact same sequence of tests as the original test run in
+ order to get reproducible results.
+
+If you are trying to track down a bug that happens only occasionally, two very
+useful options are ``--test-runs`` and ``--abort-on-first-fail``. The first
+takes an integer and runs the specified test cases that many times. The second
+causes abort to be called on the very first failed test. This is sometimes
+useful when tracing a memory corruption bug.