mirror of
https://github.com/Bareflank/hypervisor
synced 2026-08-17 06:23:04 -04:00
This patch: - Completes all of the unit tests for the VMM - Completes all of the unit tests for the syscall library - Compleses all of the unit tests for the runtime library - Completes all of the unit tests for the lib library - Fixes a number of bugs identified during testing.
285 lines
12 KiB
C++
285 lines
12 KiB
C++
/// @copyright
|
|
/// Copyright (C) 2020 Assured Information Security, Inc.
|
|
///
|
|
/// @copyright
|
|
/// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
/// of this software and associated documentation files (the "Software"), to deal
|
|
/// in the Software without restriction, including without limitation the rights
|
|
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
/// copies of the Software, and to permit persons to whom the Software is
|
|
/// furnished to do so, subject to the following conditions:
|
|
///
|
|
/// @copyright
|
|
/// The above copyright notice and this permission notice shall be included in
|
|
/// all copies or substantial portions of the Software.
|
|
///
|
|
/// @copyright
|
|
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
/// SOFTWARE.
|
|
|
|
// #include "../../src/<name>.hpp"
|
|
|
|
#include <bsl/convert.hpp>
|
|
#include <bsl/safe_integral.hpp>
|
|
#include <bsl/ut.hpp>
|
|
|
|
namespace example
|
|
{
|
|
/// <!-- description -->
|
|
/// @brief Simple function for testing non-constexpr functions.
|
|
///
|
|
/// <!-- inputs/outputs -->
|
|
/// @param val a value to test
|
|
/// @return Returns true if val is correct
|
|
///
|
|
[[nodiscard]] auto
|
|
runtime_only_function_that_knows_all(bsl::safe_umx const &val) noexcept -> bool
|
|
{
|
|
constexpr auto the_answer_to_everything{42_umx};
|
|
return val == the_answer_to_everything;
|
|
}
|
|
|
|
/// <!-- description -->
|
|
/// @brief Used to execute the actual checks. We put the checks in this
|
|
/// function so that we can validate the tests both at compile-time
|
|
/// and at run-time. If a bsl::ut_check fails, the tests will either
|
|
/// fail fast at run-time, or will produce a compile-time error.
|
|
///
|
|
/// <!-- inputs/outputs -->
|
|
/// @return Always returns bsl::exit_success.
|
|
///
|
|
[[nodiscard]] constexpr auto
|
|
tests() noexcept -> bsl::exit_code
|
|
{
|
|
/// NOTE:
|
|
/// - The tests() function is executed both at compile-time and at
|
|
/// runtime. This is done using the following at the bottom of this
|
|
/// file.
|
|
///
|
|
/// static_assert(example::tests() == bsl::ut_success());
|
|
/// return example::tests();
|
|
///
|
|
/// - Anything executed in a static_assert is executed at compile-time
|
|
/// while the other call is your typical runtime call.
|
|
///
|
|
|
|
/// NOTE:
|
|
/// - BSL unit tests use behavioral driven unit testing.
|
|
/// https://en.wikipedia.org/wiki/Behavior-driven_development
|
|
///
|
|
/// - All of these calls are syntactic sugar, meaning they do
|
|
/// absolutely nothing, except to ensure you are setting up your
|
|
/// tests using behavioral driven methods. The actual work is
|
|
/// performed by the following:
|
|
/// - bsl::ut_check() - used inside bsl::ut_then, and is used to
|
|
/// verify the results of your unit test
|
|
/// - bsl::ut_required_step() - used inside of bsl::ut_when, and
|
|
/// is used to verify that your unit test's set up is correct.
|
|
///
|
|
|
|
bsl::ut_scenario{"this is what I am testing"} = [&]() noexcept {
|
|
bsl::ut_given{"given the following"} = [&]() noexcept {
|
|
/// add variables here
|
|
bsl::ut_when{"when we do the following"} = [&]() noexcept {
|
|
/// add function calls here
|
|
bsl::ut_then{"we expect the following"} = [&]() noexcept {
|
|
/// add results checking here
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
/// NOTE:
|
|
/// - There are a couple of different ways to do this. Once way
|
|
/// is to group tests under the same scenario as follows:
|
|
///
|
|
|
|
bsl::ut_scenario{"verify +="} = [&]() noexcept {
|
|
bsl::ut_given{} = [&]() noexcept {
|
|
constexpr auto data1{42_umx};
|
|
bsl::safe_umx mut_data2{};
|
|
bsl::ut_when{} = [&]() noexcept {
|
|
mut_data2 += data1;
|
|
bsl::ut_then{"adds correctly"} = [&]() noexcept {
|
|
bsl::ut_check(mut_data2.checked() == data1);
|
|
};
|
|
};
|
|
};
|
|
|
|
bsl::ut_given_at_runtime{} = [&]() noexcept {
|
|
constexpr auto data1{42_umx};
|
|
auto mut_data2{bsl::safe_umx::failure()};
|
|
bsl::ut_when{} = [&]() noexcept {
|
|
mut_data2 += data1;
|
|
bsl::ut_then{"preserves the error flag"} = [&]() noexcept {
|
|
bsl::ut_check(mut_data2.is_invalid());
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
/// NOTE:
|
|
/// - Another way is to have one scenario for each test as follows.
|
|
/// Which way you choose if up to you, there likely isn't a right
|
|
/// way here. One advantage with this approach is each description
|
|
/// is a bit more helpful in determining what you are actually
|
|
/// changing as sometimes, the changes between each test can be
|
|
/// really hard to see, especially when each test is big and you
|
|
/// are only changing one thing (or even a single character)
|
|
///
|
|
|
|
bsl::ut_scenario{"verify += adds correctly"} = [&]() noexcept {
|
|
bsl::ut_given{} = [&]() noexcept {
|
|
constexpr auto data1{42_umx};
|
|
bsl::safe_umx mut_data2{};
|
|
bsl::ut_when{} = [&]() noexcept {
|
|
mut_data2 += data1;
|
|
bsl::ut_then{} = [&]() noexcept {
|
|
bsl::ut_check(mut_data2.checked() == data1);
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
bsl::ut_scenario{"verify += preserves the error flag"} = [&]() noexcept {
|
|
bsl::ut_given_at_runtime{} = [&]() noexcept {
|
|
constexpr auto data1{42_umx};
|
|
auto mut_data2{bsl::safe_umx::failure()};
|
|
bsl::ut_when{} = [&]() noexcept {
|
|
mut_data2 += data1;
|
|
bsl::ut_then{} = [&]() noexcept {
|
|
bsl::ut_check(mut_data2.is_invalid());
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
/// NOTE:
|
|
/// - If you have to unit test something that does not support
|
|
/// constexpr, you can use this pattern. This should be avoided
|
|
/// whenever possible. These tests will only be verified at
|
|
/// runtime as they are excluded from compile-time verification.
|
|
/// - Also not how bsl::ut_when was removed. This is because there
|
|
/// was no additional action to take.
|
|
///
|
|
|
|
bsl::ut_scenario{"test something at runtime only"} = [&]() noexcept {
|
|
bsl::ut_given_at_runtime{} = [&]() noexcept {
|
|
constexpr auto val{42_umx};
|
|
bsl::ut_then{} = [&]() noexcept {
|
|
bsl::ut_check(runtime_only_function_that_knows_all(val));
|
|
};
|
|
};
|
|
|
|
bsl::ut_given_at_runtime{} = [&]() noexcept {
|
|
constexpr auto val{23_umx};
|
|
bsl::ut_then{} = [&]() noexcept {
|
|
bsl::ut_check(!runtime_only_function_that_knows_all(val));
|
|
};
|
|
};
|
|
};
|
|
|
|
/// NOTE:
|
|
/// - There are also times when you need to set up a test and ensure
|
|
/// that specific steps in the test's set up succeed. Otherwise, the
|
|
/// test itself might not be valid, could cause a crach, etc. When
|
|
/// this is needed, you can use the bsl::ut_required_step() function.
|
|
/// - This function is identical to bsl::ut_check(). It just has a
|
|
/// different name and is intended to be used in the bsl::ut_when()
|
|
/// block, and has a different name just to help with readability.
|
|
///
|
|
|
|
bsl::ut_scenario{"verify += preserves the error flag"} = [&]() noexcept {
|
|
bsl::ut_given_at_runtime{} = [&]() noexcept {
|
|
constexpr auto data1{42_umx};
|
|
auto mut_data2{bsl::safe_umx::failure()};
|
|
bsl::ut_when{} = [&]() noexcept {
|
|
bsl::ut_required_step(mut_data2.is_invalid());
|
|
mut_data2 += data1;
|
|
bsl::ut_then{} = [&]() noexcept {
|
|
bsl::ut_check(mut_data2.is_invalid());
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
/// NOTE:
|
|
/// - Finally, some tests will use the same variables, but check
|
|
/// different conditions. When you do this, we don't want to have
|
|
/// to manually reset a specific variable for each test. If the
|
|
/// type is copyable, we can use the following pattern, otherwise
|
|
/// you should create a new variable for each test to ensure you
|
|
/// have a clean slate as we did in the tests above.
|
|
/// - Note that the key difference here is that we are not passing
|
|
/// by reference but instead by value which performs a copy, but
|
|
/// when we do this, we need to add the mutable keyword.
|
|
///
|
|
|
|
bsl::ut_scenario{"verify two different conditions"} = [&]() noexcept {
|
|
bsl::ut_given{} = [&]() noexcept {
|
|
constexpr auto data1{42_umx};
|
|
bsl::safe_umx mut_data2{};
|
|
bsl::ut_when{} = [&, mut_data2]() mutable noexcept {
|
|
mut_data2 += data1;
|
|
bsl::ut_then{} = [&, mut_data2]() noexcept {
|
|
bsl::ut_check(mut_data2.checked() == data1);
|
|
};
|
|
};
|
|
|
|
bsl::ut_when{} = [&, mut_data2]() mutable noexcept {
|
|
mut_data2 += (data1 * data1);
|
|
bsl::ut_then{} = [&, mut_data2]() noexcept {
|
|
bsl::ut_check(mut_data2.checked() == (data1 * data1).checked());
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
/// NOTE:
|
|
/// - The following provides a basic block that you can cut/paste
|
|
/// for each test as needed. Don't forget that you have the following
|
|
/// make targets when you compile with CODECOV as the build target,
|
|
/// which will help you check for coverage.
|
|
/// - make codecov-grcov (puts the results in ${CMAKE_SOURCE_DIR}/grcov)
|
|
/// - make codecov-genhtml (Linux only, puts the results in ${CMAKE_SOURCE_DIR}/genhtml)
|
|
/// - make codecov-upload (uploads coverage reports to Codecov if you set your token)
|
|
///
|
|
/// - Good luck!!!
|
|
///
|
|
|
|
bsl::ut_scenario{"description"} = [&]() noexcept {
|
|
bsl::ut_given{} = [&]() noexcept {
|
|
bsl::ut_when{} = [&]() noexcept {
|
|
bsl::ut_required_step(true);
|
|
bsl::ut_then{} = [&]() noexcept {
|
|
bsl::ut_check(true);
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
return bsl::ut_success();
|
|
}
|
|
}
|
|
|
|
/// <!-- description -->
|
|
/// @brief Main function for this unit test. If a call to bsl::ut_check() fails
|
|
/// the application will fast fail. If all calls to bsl::ut_check() pass, this
|
|
/// function will successfully return with bsl::exit_success.
|
|
///
|
|
/// <!-- inputs/outputs -->
|
|
/// @return Always returns bsl::exit_success.
|
|
///
|
|
[[nodiscard]] auto
|
|
main() noexcept -> bsl::exit_code
|
|
{
|
|
bsl::enable_color();
|
|
|
|
static_assert(example::tests() == bsl::ut_success());
|
|
return example::tests();
|
|
}
|