Felipe97/llama-cpp-compiled
01.1k
1#pragma once2 3#include "common.h"4 5#include <chrono>6#include <exception>7#include <iostream>8#include <string>9#include <regex>10#include <vector>11 12struct testing {13 std::ostream &out;14 std::vector<std::string> stack;15 std::regex filter;16 bool filter_tests = false;17 bool throw_exception = false;18 bool verbose = false;19 int tests = 0;20 int assertions = 0;21 int failures = 0;22 int unnamed = 0;23 int exceptions = 0;24 int skipped = 0;25 26 // set by skip(), read by the innermost test()27 bool skip_current = false;28 std::string skip_reason;29 30 static constexpr std::size_t status_column = 80;31 32 explicit testing(std::ostream &os = std::cout) : out(os) {}33 34 std::string indent() const {35 if (stack.empty()) {36 return "";37 }38 return std::string((stack.size() - 1) * 2, ' ');39 }40 41 std::string full_name() const {42 return string_join(stack, ".");43 }44 45 void log(const std::string & msg) {46 if (verbose) {47 out << indent() << " " << msg << "\n";48 }49 }50 51 void set_filter(const std::string & re) {52 filter = std::regex(re);53 filter_tests = true;54 }55 56 bool should_run() const {57 if (filter_tests) {58 if (!std::regex_match(full_name(), filter)) {59 return false;60 }61 }62 return true;63 }64 65 template <typename F>66 void run_with_exceptions(F &&f, const char *ctx) {67 try {68 f();69 } catch (const std::exception &e) {70 ++failures;71 ++exceptions;72 out << indent() << "UNHANDLED EXCEPTION (" << ctx << "): " << e.what() << "\n";73 if (throw_exception) {74 throw;75 }76 } catch (...) {77 ++failures;78 ++exceptions;79 out << indent() << "UNHANDLED EXCEPTION (" << ctx << "): unknown\n";80 if (throw_exception) {81 throw;82 }83 }84 }85 86 void skip(const std::string &reason = "") {87 skip_current = true;88 skip_reason = reason;89 }90 91 void print_result(const std::string &label, int new_failures, int new_assertions, const std::string &extra = "", bool was_skipped = false) const {92 std::string line = indent() + label;93 94 std::string details;95 if (new_assertions > 0) {96 if (new_failures == 0) {97 details = std::to_string(new_assertions) + " assertion(s)";98 } else {99 details = std::to_string(new_failures) + " of " +100 std::to_string(new_assertions) + " assertion(s) failed";101 }102 }103 if (!extra.empty()) {104 if (!details.empty()) {105 details += ", ";106 }107 details += extra;108 }109 110 if (!details.empty()) {111 line += " (" + details + ")";112 }113 114 std::string status = new_failures != 0 ? "[FAIL]" : (was_skipped ? "[SKIP]" : "[PASS]");115 116 if (line.size() + 1 < status_column) {117 line.append(status_column - line.size(), ' ');118 } else {119 line.push_back(' ');120 }121 122 out << line << status << "\n";123 }124 125 template <typename F>126 void test(const std::string &name, F f) {127 stack.push_back(name);128 if (!should_run()) {129 stack.pop_back();130 return;131 }132 133 ++tests;134 out << indent() << name << "\n";135 136 int before_failures = failures;137 int before_assertions = assertions;138 139 // do not let a skipped subtest also mark its parent as skipped140 bool outer_skip = skip_current;141 std::string outer_skip_reason = skip_reason;142 skip_current = false;143 skip_reason.clear();144 145 run_with_exceptions([&] { f(*this); }, "test");146 147 int new_failures = failures - before_failures;148 int new_assertions = assertions - before_assertions;149 150 bool was_skipped = skip_current && new_failures == 0;151 if (was_skipped) {152 ++skipped;153 }154 155 print_result(name, new_failures, new_assertions, was_skipped ? skip_reason : "", was_skipped);156 157 skip_current = outer_skip;158 skip_reason = outer_skip_reason;159 160 stack.pop_back();161 }162 163 template <typename F>164 void test(F f) {165 test("test #" + std::to_string(++unnamed), f);166 }167 168 template <typename F>169 void bench(const std::string &name, F f, int iterations = 100) {170 stack.push_back(name);171 if (!should_run()) {172 stack.pop_back();173 return;174 }175 176 ++tests;177 out << indent() << "[bench] " << name << "\n";178 179 int before_failures = failures;180 int before_assertions = assertions;181 182 using clock = std::chrono::high_resolution_clock;183 184 std::chrono::microseconds duration(0);185 186 run_with_exceptions([&] {187 for (auto i = 0; i < iterations; i++) {188 auto start = clock::now();189 f();190 duration += std::chrono::duration_cast<std::chrono::microseconds>(clock::now() - start);191 }192 }, "bench");193 194 auto avg_elapsed = duration.count() / iterations;195 auto avg_elapsed_s = std::chrono::duration_cast<std::chrono::duration<double>>(duration).count() / iterations;196 auto rate = (avg_elapsed_s > 0.0) ? (1.0 / avg_elapsed_s) : 0.0;197 198 int new_failures = failures - before_failures;199 int new_assertions = assertions - before_assertions;200 201 std::string extra =202 "n=" + std::to_string(iterations) +203 " avg=" + std::to_string(avg_elapsed) + "us" +204 " rate=" + std::to_string(int(rate)) + "/s";205 206 print_result("[bench] " + name, new_failures, new_assertions, extra);207 208 stack.pop_back();209 }210 211 template <typename F>212 void bench(F f, int iterations = 100) {213 bench("bench #" + std::to_string(++unnamed), f, iterations);214 }215 216 // Assertions217 bool assert_true(bool cond) {218 return assert_true("", cond);219 }220 221 bool assert_true(const std::string &msg, bool cond) {222 ++assertions;223 if (!cond) {224 ++failures;225 out << indent() << "ASSERTION FAILED";226 if (!msg.empty()) {227 out << " : " << msg;228 }229 out << "\n";230 return false;231 }232 return true;233 }234 235 template <typename A, typename B>236 bool assert_equal(const A &expected, const B &actual) {237 return assert_equal("", expected, actual);238 }239 240 template <typename A, typename B>241 bool assert_equal(const std::string &msg, const A &expected, const B &actual) {242 ++assertions;243 if (!(actual == expected)) {244 ++failures;245 out << indent() << "ASSERT EQUAL FAILED";246 if (!msg.empty()) {247 out << " : " << msg;248 }249 out << "\n";250 251 out << indent() << " expected: " << expected << "\n";252 out << indent() << " actual : " << actual << "\n";253 return false;254 }255 return true;256 }257 258 // Print summary and return an exit code259 int summary() const {260 out << "\n";261 out << "tests : " << tests << "\n";262 out << "assertions : " << assertions << "\n";263 out << "failures : " << failures << "\n";264 out << "exceptions : " << exceptions << "\n";265 out << "skipped : " << skipped << "\n";266 return failures == 0 ? 0 : 1;267 }268};269 