char-fork/llama.cpp

git clone https://git.t4t.associates/char-fork/llama.cpp

Xuan-Son Nguyenchat: refactor handling supports_string_content / supports_typed_content (#27130)b94041a98

char/aliceai-t5-moe
7.4 KiB268 linesraw
1#pragma once
2
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 skipped
140        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    // Assertions
217    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 code
259    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};