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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
name: main
on:
pull_request:
branches: [ main ]
jobs:
pre_commit_run:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: pre-commit/action@v3.0.0
unit_tests:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- run: sudo apt-get install clang-19 -y
- run: cmake -S . -B build && cmake --build build
- run: ./build/willow_test
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
iris.log
build/
build/
.gdb_history
.cache
10 changes: 1 addition & 9 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
---
exclude: ^include|^build
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v6.0.0
Expand All @@ -16,19 +17,10 @@ repos:
hooks:
- id: clang-format
exclude: ".json"
- repo: local
hooks:
- id: clang-tidy
name: clang-tidy
language: system
entry: clang-tidy -p build/compile_commands.json -extra-arg=-std=c++23
files: cpp
exclude: "^build/"
- repo: https://github.com/cmake-lint/cmake-lint
rev: 1.4.3
hooks:
- id: cmakelint
exclude: "^include/"
- repo: https://github.com/asottile/reorder-python-imports
rev: v3.16.0
hooks:
Expand Down
16 changes: 16 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
cmake_minimum_required(VERSION 3.30)
set(CMAKE_CXX_COMPILER "/usr/bin/clang++")
project(willow-test LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 23)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

add_compile_options(-g)
add_compile_options(-Wall)
add_compile_options(-Wextra)
add_compile_options(-Wconversion)
add_compile_options(-Wimplicit-fallthrough)

add_subdirectory(src/willow)
add_executable(willow_test src/main.cpp)
target_link_libraries(willow_test PRIVATE willow)
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
# Willow

A unit testing library for modern c++23
A unit testing library for modern c++23
21 changes: 21 additions & 0 deletions src/main.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#include "tests/test_reporters.h"
#include "tests/test_test.h"
#include "tests/test_willow.h"
#include "willow/reporters.h"
#include "willow/willow.h"

auto main() -> int {
Willow::PreCommitReporter reporter = {};

return Willow::runTests(
{
{"test_runTests", test_runTests},
{"test_alert", test_alert},
{"test_toString", test_toString},
{"test_Test_Operator()", test_Test_Operator},
{"PreCommitReporter::print", TestPreCommitReporter::test_print},
{"PreCommitReporter::cleanup", TestPreCommitReporter::test_cleanup},
{"PreCommitReporter::highlight", TestPreCommitReporter::test_highlight},
},
reporter);
}
13 changes: 13 additions & 0 deletions src/tests/testReporter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
#ifndef WILLOW_TEST_SILENT_REPORTER_H
#define WILLOW_TEST_SILENT_REPORTER_H

#include "willow/reporters.h"

class SilentReporter : public Willow::Reporter {
public:
constexpr SilentReporter() {}
constexpr inline auto print([[maybe_unused]] const Willow::Test& test) -> void {}
constexpr inline auto cleanup() -> void {}
};

#endif // WILLOW_TEST_SILENT_REPORTER_H
14 changes: 14 additions & 0 deletions src/tests/test_reporters.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#include "willow/reporters.h"

// PreCommitReporter
class TestPreCommitReporter : public Willow::PreCommitReporter {
public:
static constexpr auto test_print([[maybe_unused]] Willow::Test* test) -> int {
// NOTE: WE can test the logic and what's held in `results`
return 0;
}

static constexpr auto test_cleanup([[maybe_unused]] Willow::Test* test) -> int { return 0; }

static constexpr auto test_highlight([[maybe_unused]] Willow::Test* test) -> int { return 0; }
};
30 changes: 30 additions & 0 deletions src/tests/test_test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "willow/test.h"

constexpr auto test_toString([[maybe_unused]] Willow::Test* test) -> int {
if (Willow::toString(Willow::Status::None) != "None") {
return 1;
}
if (Willow::toString(Willow::Status::Pass) != "Passed") {
return 2;
}
if (Willow::toString(Willow::Status::Fail) != "Failed") {
return 3;
}
if (Willow::toString(Willow::Status::Skip) != "Skipped") {
return 4;
}
return 0;
}

namespace FixtureFuncs {
constexpr auto op_bracket([[maybe_unused]] Willow::Test* t) -> int {
return 42;
}
} // namespace FixtureFuncs

constexpr auto test_Test_Operator([[maybe_unused]] Willow::Test* test) -> int {
Willow::Test t = {"t", FixtureFuncs::op_bracket};
t();

return !(t.retcode == 42);
}
32 changes: 32 additions & 0 deletions src/tests/test_willow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#include "testReporter.h"
#include "willow/willow.h"

namespace FixtureFuncs {
constexpr auto pass([[maybe_unused]] Willow::Test* t) -> int {
return 0;
}
constexpr auto fail([[maybe_unused]] Willow::Test* t) -> int {
return 1;
}
}; // namespace FixtureFuncs

constexpr auto test_runTests([[maybe_unused]] Willow::Test* test) -> int {
SilentReporter r = {};
int ret = Willow::runTests(
{{"pass", FixtureFuncs::pass},
{"fail", FixtureFuncs::fail},
{"skip", FixtureFuncs::fail, Willow::Status::Skip}},
r);

return !(ret == 1);
}

constexpr auto test_alert([[maybe_unused]] Willow::Test* test) -> int {
Willow::Test t {};
Willow::alert(&t, "fail");

if (t.msg.has_value() && t.msg.value() == "fail") {
return 0;
}
return 1;
}
7 changes: 7 additions & 0 deletions src/willow/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
add_library(willow STATIC
willow.cpp
)

target_include_directories(willow PUBLIC
${CMAKE_CURRENT_SOURCE_DIR}/..
)
129 changes: 129 additions & 0 deletions src/willow/reporters.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#ifndef WILLOW_REPORTER_H
#define WILLOW_REPORTER_H

#include <print>

#include "test.h"

namespace Willow {
class Reporter {
public:
virtual constexpr auto print(const Test& test) -> void = 0;
virtual constexpr auto cleanup() -> void = 0;
virtual constexpr ~Reporter() = default;
};

class DefaultReporter : public Reporter {
private:
int test_count = 0;

public:
inline constexpr auto print(const Test& test) -> void {
std::println("[{}] {}\t{}", ++test_count, test.name, toString(test.status));
}

inline constexpr auto cleanup() -> void {}
};

// A reporter that displays based on the output from pre-commit
class PreCommitReporter : public Reporter {
protected:
struct Results {
int pass = 0;
int fail = 0;
int skip = 0;
};

const std::size_t screen_len = 80;
Results results = {};

public:
inline constexpr auto print(const Test& test) -> void {
const std::size_t name_len = test.name.size();
const std::size_t status_len = toString(test.status).size();
const std::string ansi = highlight(test.status);

std::println(
"{}{}{}{}\x1b[0m", test.name,
std::string(screen_len - (name_len + status_len), '.'), ansi,
toString(test.status));

if (test.status == Status::Fail) {
results.fail++;
std::println("\x1b[31m\tReturn code: {}\x1b[0m", test.retcode);
if (test.msg.has_value()) {
std::println("\x1b[31m\t{}\x1b[0m", test.msg.value());
}

} else if (test.status == Status::Skip) {
results.skip++;
if (test.msg.has_value()) {
std::println("\x1b[33m\t{}\x1b[0m", test.msg.value());
}
} else {
results.pass++;
}
}

inline constexpr auto cleanup() -> void {
Status final =
(results.fail ? Status::Fail : (results.skip ? Status::Skip : Status::Pass));

auto make_bar = [&](std::size_t len) { return std::string((len - 2) / 2, '='); };

switch (final) {
case Status::Pass: {
std::string msg = "All tests passed!";
std::println(
"\x1b[32m{} {} {}\x1b[0m", make_bar((screen_len - msg.size()) + 1), msg,
make_bar(screen_len - msg.size()));
break;
}

case Status::Skip: {
std::string msg = std::to_string(results.pass) + " tests passed (" +
std::to_string(results.skip) + " skipped)";
std::println(
"\x1b[33m{} {} {}\x1b[0m", make_bar(screen_len - msg.size()), msg,
make_bar(screen_len - msg.size()));
break;
}

case Status::Fail: {
std::string msg = std::to_string(results.fail) + " tests failed, (" +
std::to_string(results.pass) + " passed)";
std::println(
"\x1b[31m{} {} {}\x1b[0m", make_bar(screen_len - msg.size()), msg,
make_bar(screen_len - msg.size()));
break;
}

case Status::None:
default:
break;
}
}

inline constexpr auto highlight(const Status& st) -> std::string {
switch (st) {
case Status::None:
return "\x1b[0m";
break;
case Status::Pass:
return "\x1b[42m";
break;
case Status::Fail:
return "\x1b[41m";
break;
case Status::Skip:
return "\x1b[43m";
break;
}

return "";
}
};

} // namespace Willow

#endif // WILLOW_REPORTER_H
48 changes: 48 additions & 0 deletions src/willow/test.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
#ifndef WILLOW_TEST_H
#define WILLOW_TEST_H

#include <string>

namespace Willow {
// forward declaration for type alias
struct Test;
using TestFn = int (*)(Test*);

enum class Status { None, Pass, Fail, Skip };

constexpr auto toString(const Status& st) -> std::string {
switch (st) {
case Status::None:
return "None";
break;
case Status::Pass:
return "Passed";
break;
case Status::Fail:
return "Failed";
break;
case Status::Skip:
return "Skipped";
break;
};

return "";
}

struct Test {
std::string name;
TestFn fn;
int retcode = 0;
std::optional<std::string> msg = std::nullopt;
Status status = Status::None;

constexpr Test() : name(""), fn(NULL) {}
constexpr Test(std::string given_name, TestFn f) : name {given_name}, fn {f} {}
constexpr Test(std::string given_name, TestFn f, Status st)
: name {given_name}, fn {f}, status {st} {}

auto operator()() -> void { retcode = fn(this); }
};
}; // namespace Willow

#endif // WILLOW_TEST_H
8 changes: 8 additions & 0 deletions src/willow/willow.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "willow.h"

#include <algorithm>

#include "reporters.h"
#include "test.h"

namespace Willow {} // namespace Willow
Loading
Loading