From 4e5a796e504d3c23fd24c8b285a568ab8cc50003 Mon Sep 17 00:00:00 2001 From: "Justin C. Miller" Date: Sat, 12 Feb 2022 21:30:14 -0800 Subject: [PATCH] [test_runner] Add test_runner program This change introduces test_runner, which runs unit or integration tests and then tells the kernel to exit QEMU with a status code indicating the number of failed tests. The test_runner program is not loaded by default. Use the test manifest to enable it: ./configure --manifest=assets/manifests/test.yml A number of tests from the old src/tests have moved over. More to come, as well as moving code from testapp before getting rid of it. The test.sh script has been repurposed to be a "headless" version of qemu.sh for running tests, and it exits with the appropriate exit code. (Though ./qemu.sh gained the ability to exit with the correct exit code as well.) Exit codes from kernel panics have been updated so that the bash scripts should exit with code 127. --- assets/manifests/test.yaml | 1 + qemu.sh | 5 +- src/kernel/panic.serial/main.cpp | 4 +- src/kernel/syscalls/system.cpp | 2 +- src/tests/constexpr_hash.cpp | 21 --- src/tests/linked_list.cpp | 154 ------------------ src/tests/map.cpp | 95 ----------- src/user/test_runner/main.cpp | 12 ++ src/user/test_runner/test_case.cpp | 32 ++++ src/user/test_runner/test_case.h | 83 ++++++++++ src/user/test_runner/test_rng.h | 30 ++++ src/user/test_runner/test_runner.module | 15 ++ src/user/test_runner/tests/constexpr_hash.cpp | 24 +++ .../test_runner/tests/container_helpers.h | 13 ++ src/user/test_runner/tests/linked_list.cpp | 143 ++++++++++++++++ src/user/test_runner/tests/map.cpp | 98 +++++++++++ src/user/test_runner/tests/vector.cpp | 29 ++++ test.sh | 94 ++++++++++- 18 files changed, 575 insertions(+), 280 deletions(-) delete mode 100644 src/tests/constexpr_hash.cpp delete mode 100644 src/tests/linked_list.cpp delete mode 100644 src/tests/map.cpp create mode 100644 src/user/test_runner/main.cpp create mode 100644 src/user/test_runner/test_case.cpp create mode 100644 src/user/test_runner/test_case.h create mode 100644 src/user/test_runner/test_rng.h create mode 100644 src/user/test_runner/test_runner.module create mode 100644 src/user/test_runner/tests/constexpr_hash.cpp create mode 100644 src/user/test_runner/tests/container_helpers.h create mode 100644 src/user/test_runner/tests/linked_list.cpp create mode 100644 src/user/test_runner/tests/map.cpp create mode 100644 src/user/test_runner/tests/vector.cpp diff --git a/assets/manifests/test.yaml b/assets/manifests/test.yaml index 55cb4f7..a09b3c0 100644 --- a/assets/manifests/test.yaml +++ b/assets/manifests/test.yaml @@ -6,3 +6,4 @@ programs: target: kernel flags: panic - name: drv.uart + - name: test_runner diff --git a/qemu.sh b/qemu.sh index 346f83d..5985438 100755 --- a/qemu.sh +++ b/qemu.sh @@ -80,7 +80,7 @@ elif [[ $DESKTOP_SESSION = "i3" ]]; then fi fi -exec qemu-system-x86_64 \ +qemu-system-x86_64 \ -drive "if=pflash,format=raw,readonly,file=${assets}/ovmf/x64/ovmf_code.fd" \ -drive "if=pflash,format=raw,file=${build}/ovmf_vars.fd" \ -drive "format=raw,file=${build}/jsix.img" \ @@ -93,3 +93,6 @@ exec qemu-system-x86_64 \ -M q35 \ -no-reboot \ $isaexit $log $gfx $vga $kvm $debug + +((result = ($? >> 1) - 1)) +exit $result diff --git a/src/kernel/panic.serial/main.cpp b/src/kernel/panic.serial/main.cpp index e865f22..2c91a15 100644 --- a/src/kernel/panic.serial/main.cpp +++ b/src/kernel/panic.serial/main.cpp @@ -63,8 +63,8 @@ void panic_handler(const cpu_state *regs) if (__atomic_sub_fetch(&remaining, 1, order) == 0) { // No remaining CPUs, if we're running on QEMU, // tell it to exit - constexpr uint32_t exit_code = 0; - asm ( "out %0, %1" :: "a"(exit_code), "Nd"(0xf4) ); + constexpr uint32_t exit_code = 255; + asm ( "outl %%eax, %%dx" :: "a"(exit_code), "d"(0xf4) ); } while (1) asm ("hlt"); diff --git a/src/kernel/syscalls/system.cpp b/src/kernel/syscalls/system.cpp index 7f1d5ed..47a7060 100644 --- a/src/kernel/syscalls/system.cpp +++ b/src/kernel/syscalls/system.cpp @@ -40,7 +40,7 @@ noop() test_finish(uint32_t exit_code) { // Tell QEMU to exit - asm ( "out %0, %1" :: "a"(exit_code), "Nd"(0xf4) ); + asm ( "out %0, %1" :: "a"(exit_code+1), "Nd"(0xf4) ); while (1) asm ("hlt"); } diff --git a/src/tests/constexpr_hash.cpp b/src/tests/constexpr_hash.cpp deleted file mode 100644 index 534647e..0000000 --- a/src/tests/constexpr_hash.cpp +++ /dev/null @@ -1,21 +0,0 @@ -#include "kutil/constexpr_hash.h" -#include "catch.hpp" - -using namespace kutil; - -TEST_CASE( "constexpr hash", "[hash]" ) -{ - const unsigned hash1 = static_cast("hash1!"_h); - CHECK(hash1 == 210); - - const unsigned hash2 = static_cast("hash1!"_h); - CHECK(hash1 == hash2); - - const unsigned hash3 = static_cast("not hash1!"_h); - CHECK(hash1 != hash3); - CHECK(hash3 == 37); - - const unsigned hash4 = static_cast("another thing that's longer"_h); - CHECK(hash1 != hash4); - CHECK(hash4 == 212); -} diff --git a/src/tests/linked_list.cpp b/src/tests/linked_list.cpp deleted file mode 100644 index 4b30c81..0000000 --- a/src/tests/linked_list.cpp +++ /dev/null @@ -1,154 +0,0 @@ -#include -#include -#include -#include -#include -#include "kutil/linked_list.h" -#include "catch.hpp" -#include "container_helpers.h" - -using namespace kutil; - -const int test_list_size = 100; - -template -class ListVectorCompare : - public Catch::MatcherBase>> -{ -public: - using item = list_node; - using vector = std::vector; - - ListVectorCompare(const linked_list &list, bool reversed) : - m_list(list), m_reverse(reversed) {} - - virtual bool match (vector const& vec) const override - { - size_t index = m_reverse ? vec.size() - 1 : 0; - for (const T *i : m_list) { - if (i != &vec[index]) return false; - index += m_reverse ? -1 : 1; - } - return true; - } - - virtual std::string describe() const override - { - return "is the same as the given linked list"; - } - -private: - const linked_list &m_list; - bool m_reverse; -}; - -template -class IsSorted : - public Catch::MatcherBase> -{ -public: - using item = list_node; - using list = linked_list; - - IsSorted() {} - - virtual bool match (list const& l) const override - { - int big = std::numeric_limits::min(); - for (const T *i : l) { - if (i->value < big) return false; - big = i->value; - } - return true; - } - - virtual std::string describe() const override - { - return "is sorted"; - } -}; - -template -class ListContainsMatcher : - public Catch::MatcherBase> -{ -public: - using item = list_node; - using list = linked_list; - - ListContainsMatcher(const item &needle) : m_needle(needle) {} - - virtual bool match (list const& l) const override - { - for (const T *i : l) - if (i == &m_needle) return true; - return false; - } - - virtual std::string describe() const override - { - return "contains the given item"; - } - - const item &m_needle; -}; - -template -ListVectorCompare IsSameAsList(const linked_list &list, bool reversed = false) -{ - return ListVectorCompare(list, reversed); -} - -template -ListContainsMatcher ListContains(const list_node &item) -{ - return ListContainsMatcher(item); -} - -TEST_CASE( "Linked list tests", "[containers] [list]" ) -{ - linked_list ulist; - - int value = 0; - std::vector> unsortables(test_list_size); - for (auto &i : unsortables) { - i.value = value++; - ulist.push_back(&i); - } - CHECK( ulist.length() == test_list_size ); - CHECK_THAT( unsortables, IsSameAsList(ulist) ); - - linked_list ulist_reversed; - - for (auto &i : unsortables) { - ulist.remove(&i); - ulist_reversed.push_front(&i); - } - - CHECK( ulist_reversed.length() == test_list_size ); - CHECK_THAT( unsortables, IsSameAsList(ulist_reversed, true) ); - - auto &removed = unsortables[test_list_size / 2]; - ulist_reversed.remove(&removed); - CHECK( ulist_reversed.length() == test_list_size - 1 ); - CHECK_THAT( ulist_reversed, !ListContains(removed) ); -} - -TEST_CASE( "Sorted list tests", "[containers] [list]" ) -{ - using clock = std::chrono::system_clock; - unsigned seed = clock::now().time_since_epoch().count(); - std::default_random_engine rng(seed); - std::uniform_int_distribution gen(1, 1000); - - linked_list slist; - CHECK( slist.length() == 0 ); - - std::vector> sortables(test_list_size); - for (auto &i : sortables) { - i.value = gen(rng); - slist.sorted_insert(&i); - } - CHECK( slist.length() == test_list_size ); - CHECK_THAT( slist, IsSorted() ); -} diff --git a/src/tests/map.cpp b/src/tests/map.cpp deleted file mode 100644 index 41eb418..0000000 --- a/src/tests/map.cpp +++ /dev/null @@ -1,95 +0,0 @@ -#include "kutil/map.h" -#include "catch.hpp" - -using Catch::rng; -std::uniform_int_distribution distrib {0, 10000}; - -TEST_CASE( "map insertion", "[containers] [map]" ) -{ - std::vector ints; - for (int i = 0; i < 1000; ++i) - ints.push_back(i); - - size_t sizes[] = {1, 2, 3, 5, 100}; - for (size_t s : sizes) { - kutil::map v; - std::shuffle(ints.begin(), ints.end(), rng()); - - for (int i = 0; i < s; ++i) { - v.insert(ints[i], ints[i]); - } - - for (int i = 0; i < s; ++i) { - int *p = v.find(ints[i]); - CAPTURE( s ); - CAPTURE( i ); - CAPTURE( ints[i] ); - CAPTURE( kutil::hash(ints[i]) ); - CHECK( p ); - CHECK( *p == ints[i] ); - } - } -} - -TEST_CASE( "map deletion", "[containers] [map]" ) -{ - std::vector ints; - for (int i = 0; i < 1000; ++i) - ints.push_back(i); - - size_t sizes[] = {1, 2, 3, 5, 100}; - for (size_t s : sizes) { - kutil::map v; - std::shuffle(ints.begin(), ints.end(), rng()); - - for (int i = 0; i < s; ++i) { - v.insert(ints[i], ints[i]); - } - - for (int i = 0; i < s; i += 2) { - v.erase(ints[i]); - } - - for (int i = 0; i < s; ++i) { - int *p = v.find(ints[i]); - CAPTURE( s ); - CAPTURE( i ); - CAPTURE( ints[i] ); - CAPTURE( kutil::hash(ints[i]) ); - if ( i%2 ) - CHECK( p ); - else - CHECK( !p ); - } - } -} - -TEST_CASE( "map with pointer vals", "[containers] [map]" ) -{ - kutil::map v; - int is[4] = { 0, 0, 0, 0 }; - for (int i = 0; i < 4; ++i) - v.insert(i*7, &is[i]); - - for (int i = 0; i < 4; ++i) { - int *p = v.find(i*7); - CHECK( p == &is[i] ); - } - - CHECK( v.find(3) == nullptr ); -} - -TEST_CASE( "map with uint64_t keys", "[containers] [map]" ) -{ - kutil::map v; - int is[4] = { 2, 3, 5, 7 }; - for (uint64_t i = 0; i < 4; ++i) - v.insert(i+1, is[i]); - - for (uint64_t i = 0; i < 4; ++i) { - int *p = v.find(i+1); - CHECK( *p == is[i] ); - } - - CHECK( v.find(30) == nullptr ); -} diff --git a/src/user/test_runner/main.cpp b/src/user/test_runner/main.cpp new file mode 100644 index 0000000..ed49c54 --- /dev/null +++ b/src/user/test_runner/main.cpp @@ -0,0 +1,12 @@ +#include +#include + +#include "test_case.h" + +extern "C" +int main() +{ + size_t failures = test::registry::run_all_tests(); + j6_test_finish(failures); // never actually returns + return 0; +} diff --git a/src/user/test_runner/test_case.cpp b/src/user/test_runner/test_case.cpp new file mode 100644 index 0000000..87fffbe --- /dev/null +++ b/src/user/test_runner/test_case.cpp @@ -0,0 +1,32 @@ +#include "test_case.h" + +namespace test { + +util::vector registry::m_tests; + +void +fixture::_log_failure(const char *test_name, const char *message, + const char *function, const char *file, uint64_t line) +{ + // TODO: output results + ++_test_failure_count; +} + +void +registry::register_test_case(fixture &test) +{ + m_tests.append(&test); +} + +size_t +registry::run_all_tests() +{ + size_t failures = 0; + for (auto *test : m_tests) { + test->test_execute(); + failures += test->_test_failure_count; + } + return failures; +} + +} // namespace test diff --git a/src/user/test_runner/test_case.h b/src/user/test_runner/test_case.h new file mode 100644 index 0000000..d57ed34 --- /dev/null +++ b/src/user/test_runner/test_case.h @@ -0,0 +1,83 @@ +#pragma once +/// \file test_case.h +/// Test case definition and helpers + +#include +#include + +namespace test { + +class fixture +{ +public: + virtual void setup() {} + virtual void teardown() {} + virtual void test_execute() = 0; + +protected: + void _log_failure( + const char *test_name, + const char *message, + const char *function = __builtin_FUNCTION(), + const char *file = __builtin_FILE(), + uint64_t line = __builtin_LINE()); + +private: + friend class registry; + size_t _test_failure_count = 0; +}; + +class registry +{ +public: + static void register_test_case(fixture &test); + static size_t run_all_tests(); + +private: + static util::vector m_tests; +}; + +template +class registrar +{ +public: + registrar() { registry::register_test_case(m_test); } + +private: + T m_test; +}; + +template +struct comparator_negate; + +template +struct comparator +{ + virtual bool operator()(const Args&... opts) const = 0; + virtual comparator_negate operator!() const { return comparator_negate {*this}; } +}; + +template +struct comparator_negate : + public comparator +{ + comparator_negate(const comparator &in) : inner {in} {} + virtual bool operator()(const Args&... opts) const override { return !inner(opts...); } + const comparator &inner; +}; + +} // namespace test + +#define TEST_CASE(fixture, name) namespace { \ + struct test_case_ ## name : fixture { \ + static constexpr const char *test_name = #fixture ":" #name;\ + void test_execute(); \ + }; \ + test::registrar name ## _auto_registrar; \ +} \ +void test_case_ ## name::test_execute() + +#define CHECK(expr, message) do { if (!(expr)) {_log_failure(test_name,message);} } while (0) +#define REQUIRE(expr, message) do { if (!(expr)) {_log_failure(test_name,message); return;} } while (0) +#define CHECK_BARE(expr) do { if (!(expr)) {_log_failure(test_name,#expr);} } while (0) +#define CHECK_THAT(subject, checker) do { if (!(checker)(subject)) {_log_failure(test_name,#subject #checker);} } while (0) diff --git a/src/user/test_runner/test_rng.h b/src/user/test_runner/test_rng.h new file mode 100644 index 0000000..9ec174a --- /dev/null +++ b/src/user/test_runner/test_rng.h @@ -0,0 +1,30 @@ +#pragma once +/// \file test_rng.h +/// Simple xorshift-based psuedorandom number generator for tests + +#include + +namespace test { + +class rng +{ +public: + using result_type = uint64_t; + + rng(uint64_t seed = 1) : a(seed) {} + + uint64_t operator()() { + a ^= a << 13; + a ^= a >> 7; + a ^= a << 17; + return a; + } + + constexpr static uint64_t max() { return UINT64_MAX; } + constexpr static uint64_t min() { return 0; } + +private: + uint64_t a; +}; + +} // namespace test diff --git a/src/user/test_runner/test_runner.module b/src/user/test_runner/test_runner.module new file mode 100644 index 0000000..a5df984 --- /dev/null +++ b/src/user/test_runner/test_runner.module @@ -0,0 +1,15 @@ +# vim: ft=python + +module("test_runner", + targets = [ "user" ], + deps = [ "libc", "util" ], + description = "Unit test runner", + sources = [ + "main.cpp", + "test_case.cpp", + + "tests/constexpr_hash.cpp", + "tests/linked_list.cpp", + "tests/map.cpp", + "tests/vector.cpp", + ]) diff --git a/src/user/test_runner/tests/constexpr_hash.cpp b/src/user/test_runner/tests/constexpr_hash.cpp new file mode 100644 index 0000000..cd31052 --- /dev/null +++ b/src/user/test_runner/tests/constexpr_hash.cpp @@ -0,0 +1,24 @@ +#include +#include "test_case.h" + +class hash_tests : + public test::fixture +{ +}; + +TEST_CASE( hash_tests, equality_test ) +{ + const unsigned hash1 = static_cast("hash1!"_h); + CHECK( hash1 == 210, "hash gave unexpected value"); + + const unsigned hash2 = static_cast("hash1!"_h); + CHECK(hash1 == hash2, "hashes of equal strings should be equal"); + + const unsigned hash3 = static_cast("not hash1!"_h); + CHECK(hash1 != hash3, "hashes of different strings should not be equal"); + CHECK(hash3 == 37, "hash gave unexpected value"); + + const unsigned hash4 = static_cast("another thing that's longer"_h); + CHECK(hash1 != hash4, "hashes of different strings should not be equal"); + CHECK(hash4 == 212, "hash gave unexpected value"); +} diff --git a/src/user/test_runner/tests/container_helpers.h b/src/user/test_runner/tests/container_helpers.h new file mode 100644 index 0000000..2f18713 --- /dev/null +++ b/src/user/test_runner/tests/container_helpers.h @@ -0,0 +1,13 @@ +#pragma once + +struct unsortableT { + unsigned long value; +}; + +struct sortableT { + unsigned long value; + unsigned long compare(const sortableT &other) const { + return value > other.value ? 1 : value == other.value ? 0 : -1; + } +}; + diff --git a/src/user/test_runner/tests/linked_list.cpp b/src/user/test_runner/tests/linked_list.cpp new file mode 100644 index 0000000..785f767 --- /dev/null +++ b/src/user/test_runner/tests/linked_list.cpp @@ -0,0 +1,143 @@ +#include +#include + +#include + +#include "container_helpers.h" +#include "test_case.h" +#include "test_rng.h" + +const int test_list_size = 100; + +struct linked_list_tests : + public test::fixture +{ +}; + +template +class list_vector_comparator : + public test::comparator> const&> +{ +public: + using item = util::list_node; + using vector = std::vector; + + list_vector_comparator(const util::linked_list &list, bool reversed) : + m_list(list), m_reverse(reversed) {} + + virtual bool operator()(vector const& vec) const override + { + size_t index = m_reverse ? vec.size() - 1 : 0; + for (const T *i : m_list) { + if (i != &vec[index]) return false; + index += m_reverse ? -1 : 1; + } + return true; + } + +private: + const util::linked_list &m_list; + bool m_reverse; +}; + +template +class is_sorted : + public test::comparator const&> +{ +public: + using item = util::list_node; + using list = util::linked_list; + + is_sorted() {} + + virtual bool operator()(list const& l) const override + { + int big = std::numeric_limits::min(); + for (const T *i : l) { + if (i->value < big) return false; + big = i->value; + } + return true; + } +}; + +template +class list_contains_comparator : + public test::comparator> +{ +public: + using item = util::list_node; + using list = util::linked_list; + + list_contains_comparator(const item &needle) : m_needle(needle) {} + + virtual bool operator()(list const& l) const override + { + for (const T *i : l) + if (i == &m_needle) return true; + return false; + } + + const item &m_needle; +}; + +template +list_vector_comparator same_as(const util::linked_list &list, bool reversed = false) +{ + return list_vector_comparator(list, reversed); +} + +template +list_contains_comparator contains(const util::list_node &item) +{ + return list_contains_comparator(item); +} + +TEST_CASE( linked_list_tests, unsorted ) +{ + util::linked_list ulist; + + int value = 0; + std::vector> unsortables(test_list_size); + for (auto &i : unsortables) { + i.value = value++; + ulist.push_back(&i); + } + CHECK_BARE( ulist.length() == test_list_size ); + CHECK_THAT( unsortables, same_as(ulist) ); + + util::linked_list ulist_reversed; + + for (auto &i : unsortables) { + ulist.remove(&i); + ulist_reversed.push_front(&i); + } + + CHECK_BARE( ulist_reversed.length() == test_list_size ); + + //CHECK_THAT( unsortables, IsSameAsList(ulist_reversed, true) ); + + auto &removed = unsortables[test_list_size / 2]; + ulist_reversed.remove(&removed); + CHECK( ulist_reversed.length() == test_list_size - 1, + "remove() did not make size 1 less" ); + + CHECK_THAT( ulist_reversed, !contains(removed) ); +} + +TEST_CASE( linked_list_tests, sorted ) +{ + test::rng rng {12345}; + std::uniform_int_distribution gen(1, 1000); + + util::linked_list slist; + CHECK( slist.length() == 0, "Newly constructed list should be empty" ); + + std::vector> sortables(test_list_size); + for (auto &i : sortables) { + i.value = gen(rng); + slist.sorted_insert(&i); + } + CHECK_BARE( slist.length() == test_list_size ); + // CHECK_THAT( slist, is_sorted() ); +} diff --git a/src/user/test_runner/tests/map.cpp b/src/user/test_runner/tests/map.cpp new file mode 100644 index 0000000..509d5b1 --- /dev/null +++ b/src/user/test_runner/tests/map.cpp @@ -0,0 +1,98 @@ +#include +#include + +#include "test_case.h" +#include "test_rng.h" + +struct map_tests : + public test::fixture +{ +}; + +TEST_CASE( map_tests, insert ) +{ + test::rng rng {12345}; + + std::vector ints; + for (int i = 0; i < 1000; ++i) + ints.push_back(i); + + size_t sizes[] = {1, 2, 10, 50, 100, 100}; + for (size_t s : sizes) { + util::map v; + std::shuffle(ints.begin(), ints.end(), rng); + + for (int i = 0; i < s; ++i) { + v.insert(ints[i], ints[i]); + } + + for (int i = 0; i < s; ++i) { + int *p = v.find(ints[i]); + CHECK( p, "Map did not have expected key" ); + CHECK( *p == ints[i], "Map did not have expected value" ); + } + } +} + +TEST_CASE( map_tests, deletion ) +{ + test::rng rng {12345}; + + std::vector ints; + for (int i = 0; i < 1000; ++i) + ints.push_back(i); + + size_t sizes[] = {1, 2, 3, 5, 100}; + for (size_t s : sizes) { + util::map v; + std::shuffle(ints.begin(), ints.end(), rng); + + for (int i = 0; i < s; ++i) { + v.insert(ints[i], ints[i]); + } + + for (int i = 0; i < s; i += 2) { + v.erase(ints[i]); + } + + for (int i = 0; i < s; ++i) { + int *p = v.find(ints[i]); + if ( i%2 ) + CHECK( p, "Expected map item did not exist" ); + else + CHECK( !p, "Deleted item should not exist" ); + } + } +} + +TEST_CASE( map_tests, pointer_vals ) +{ + util::map v; + int is[4] = { 0, 0, 0, 0 }; + for (int i = 0; i < 4; ++i) + v.insert(i*7, &is[i]); + + for (int i = 0; i < 4; ++i) { + int *p = v.find(i*7); + CHECK( p, "Expected pointer did not exist" ); + CHECK( p == &is[i], "Expected pointer was not correct" ); + } + + CHECK( v.find(3) == nullptr, "Expected empty slot exists" ); +} + +TEST_CASE( map_tests, uint64_keys ) +{ + util::map v; + int is[4] = { 2, 3, 5, 7 }; + for (uint64_t i = 0; i < 4; ++i) + v.insert(i+1, is[i]); + + for (uint64_t i = 0; i < 4; ++i) { + int *p = v.find(i+1); + CHECK( p, "Expected integer did not exist" ); + CHECK( *p == is[i], "Expected integer was not correct" ); + } + + CHECK( v.find(30) == nullptr, "Expected missing intger was found" ); +} diff --git a/src/user/test_runner/tests/vector.cpp b/src/user/test_runner/tests/vector.cpp new file mode 100644 index 0000000..a32cee4 --- /dev/null +++ b/src/user/test_runner/tests/vector.cpp @@ -0,0 +1,29 @@ +#include +#include + +#include "container_helpers.h" +#include "test_case.h" +#include "test_rng.h" + +struct vector_tests : + public test::fixture +{ +}; + +TEST_CASE( vector_tests, sorted_test ) +{ + test::rng rng {12345}; + + util::vector v; + + int sizes[] = {1, 2, 3, 5, 100}; + for (int s : sizes) { + for (int i = 0; i < s; ++i) { + sortableT t { rng() }; + v.sorted_insert(t); + } + + for (int i = 1; i < s; ++i) + CHECK( v[i].value >= v[i-1].value, "v is not sorted" ); + } +} diff --git a/test.sh b/test.sh index f9d8e53..9df3385 100755 --- a/test.sh +++ b/test.sh @@ -1,10 +1,92 @@ #!/usr/bin/env bash -set -o errexit +build="$(dirname $0)/build" +assets="$(dirname $0)/assets" +vga="-vga none" +log="" +kvm="" +cpu="Broadwell,+pdpe1gb,-check" +smp=4 +mem=2048 +clean="" -ROOT=$(realpath $(dirname $0)) -BUILD="${ROOT}/build" -TESTS="${BUILD}/native/tests" +while true; do + case "$1" in + -c | --clean) + clean="yes" + shift + ;; + -v | --vga) + vga="" + shift + ;; + -k | --kvm) + kvm="-enable-kvm" + cpu="host" + shift + ;; + -c | --cpus) + smp=$2 + shift 2 + ;; + -m | --memory) + mem=$2 + shift 2 + ;; + -l | --log) + log="-d mmu,int,guest_errors -D jsix.log" + shift + ;; + *) + if [ -d "$1" ]; then + build="$1" + shift + fi + break + ;; + esac +done -ninja -C "${BUILD}" "${TESTS}" -"${TESTS}" $* +if [[ ! -c /dev/kvm ]]; then + kvm="" +fi + +if [[ -n $clean ]]; then + ninja -C "${build}" -t clean +fi + +if ! ninja -C "${build}"; then + exit 255 +fi + +qemu-system-x86_64 \ + -drive "if=pflash,format=raw,readonly,file=${assets}/ovmf/x64/ovmf_code.fd" \ + -drive "if=pflash,format=raw,file=${build}/ovmf_vars.fd" \ + -drive "format=raw,file=${build}/jsix.img" \ + -serial "file:${build}/qemu_test_com1.txt" \ + -debugcon "file:${build}/qemu_debugcon.txt" \ + -smp "${smp}" \ + -m $mem \ + -cpu "${cpu}" \ + -M q35 \ + -no-reboot \ + -nographic \ + -device isa-debug-exit,iobase=0xf4,iosize=0x04 \ + $log $vga $kvm $debug > "${build}/qemu_test_out.txt" + +result=$? +if [[ $result -eq 0 ]]; then + echo "Somehow exited with status 0" > /dev/stderr + exit 1 +fi + +if [[ $result -eq 1 ]]; then + echo "QEMU returned an error status" > /dev/stderr + exit 1 +fi + +((result = ($result >> 1) - 1)) +if [[ $result -gt 0 ]]; then + cat "${build}/qemu_test_com1.txt" +fi +exit $result