libtcspc C++ API
Streaming TCSPC and time tag data processing
Loading...
Searching...
No Matches
test_utils.hpp
1/*
2 * This file is part of libtcspc
3 * Copyright 2019-2026 Board of Regents of the University of Wisconsin System
4 * SPDX-License-Identifier: MIT
5 */
6
7#pragma once
8
9#include "bucket.hpp"
10#include "common.hpp"
11#include "context.hpp"
12#include "data_types.hpp"
13#include "errors.hpp"
14#include "introspect.hpp"
15#include "processor.hpp"
16#include "type_list.hpp"
17#include "variant_event.hpp"
18#include "vector_queue.hpp"
19
20#include <algorithm>
21#include <any>
22#include <array>
23#include <cassert>
24#include <cstdint>
25#include <cstdio>
26#include <initializer_list>
27#include <limits>
28#include <memory>
29#include <ostream>
30#include <span>
31#include <sstream>
32#include <stdexcept>
33#include <string>
34#include <type_traits>
35#include <typeinfo>
36#include <utility>
37#include <variant>
38#include <vector>
39
40namespace tcspc {
41
42namespace internal {
43
44template <typename EventList> class sink_events {
45 public:
46 [[nodiscard]] auto introspect_node() const -> processor_info {
47 return processor_info(this, "sink_events");
48 }
49
50 [[nodiscard]] auto introspect_graph() const -> processor_graph {
51 return processor_graph().push_entry_point(this);
52 }
53
54 template <typename E>
55 requires convertible_to_type_list_member<std::remove_cvref_t<E>,
56 EventList>
57 void handle(E &&event) {
58 [[maybe_unused]] std::remove_reference_t<E> const e =
59 std::forward<E>(event);
60 }
61
62 void flush() {}
63};
64
65} // namespace internal
66
81template <typename... Event> auto sink_events() {
82 return internal::sink_events<type_list<Event...>>();
83}
84
99template <typename EventList> auto sink_event_list() {
100 return internal::sink_events<EventList>();
101}
102
114
116inline auto operator<<(std::ostream &stream, feed_as cat) -> std::ostream & {
117 switch (cat) {
119 return stream << "feed_as::const_lvalue";
120 case feed_as::rvalue:
121 return stream << "feed_as::rvalue";
122 }
123 internal::unreachable();
124}
125
143
145inline auto operator<<(std::ostream &stream, emitted_as cat)
146 -> std::ostream & {
147 switch (cat) {
149 return stream << "emitted_as::any_allowed";
151 return stream << "emitted_as::same_as_fed";
153 return stream << "emitted_as::always_lvalue";
155 return stream << "emitted_as::always_rvalue";
156 }
157 internal::unreachable();
158}
159
160namespace internal {
161
162// Value category observed by capture_output.
163enum class emitted_value_category {
164 const_lvalue,
165 nonconst_lvalue,
166 const_rvalue,
167 nonconst_rvalue,
168};
169
170inline void check_value_category(feed_as feed_cat, emitted_as expected,
171 emitted_value_category actual) {
172 if (actual == emitted_value_category::nonconst_lvalue)
173 throw std::logic_error("non-const lvalue event not allowed");
174
175 if (expected == emitted_as::same_as_fed) {
176 switch (feed_cat) {
178 expected = emitted_as::always_lvalue;
179 break;
180 case feed_as::rvalue:
181 expected = emitted_as::always_rvalue;
182 break;
183 }
184 }
185
186 switch (expected) {
188 break;
190 unreachable();
192 if (actual == emitted_value_category::nonconst_rvalue)
193 throw std::logic_error("expected lvalue event, found rvalue");
194 break;
196 if (actual != emitted_value_category::nonconst_rvalue)
197 throw std::logic_error("expected rvalue event, found lvalue");
198 break;
199 }
200}
201
202inline auto operator<<(std::ostream &stream, emitted_value_category cat)
203 -> std::ostream & {
204 switch (cat) {
205 using e = emitted_value_category;
206 case e::const_lvalue:
207 return stream << "const &";
208 case e::nonconst_lvalue:
209 return stream << "&";
210 case e::const_rvalue:
211 return stream << "const &&";
212 case e::nonconst_rvalue:
213 return stream << "&&";
214 }
215 unreachable();
216}
217
218template <typename EventList>
219using recorded_event =
220 std::pair<emitted_value_category, variant_event<EventList>>;
221
222template <typename EventList>
223auto operator<<(std::ostream &stream, recorded_event<EventList> const &pair)
224 -> std::ostream & {
225 return stream << pair.second << ' ' << pair.first;
226}
227
228// Polymorphic function set for type-erasing capture_output_access.
229class capture_output_access_impl_base {
230 public:
231 virtual ~capture_output_access_impl_base() = default;
232 // Returned std::any contains std::vector<recorded_event<EventList>>.
233 [[nodiscard]] virtual auto peek_events() const -> std::any = 0;
234 virtual void pop_event() = 0;
235 [[nodiscard]] virtual auto is_empty() const -> bool = 0;
236 [[nodiscard]] virtual auto is_flushed() const -> bool = 0;
237 virtual void set_up_to_throw(std::size_t count, bool use_error) = 0;
238 [[nodiscard]] virtual auto events_as_string() const -> std::string = 0;
239};
240
241} // namespace internal
242
252class capture_output_access {
253 std::unique_ptr<internal::capture_output_access_impl_base> impl;
254
255 template <typename EventList>
256 auto peek_events() const
257 -> std::vector<internal::recorded_event<EventList>> {
258 return std::any_cast<std::vector<internal::recorded_event<EventList>>>(
259 impl->peek_events());
260 }
261
262 public:
264 explicit capture_output_access(
265 std::unique_ptr<internal::capture_output_access_impl_base>
266 implementation)
267 : impl(std::move(implementation)) {}
268
273 void check_ready_for_input(std::string const &input) const {
274 if (not impl->is_empty()) {
275 throw std::logic_error(
276 "cannot accept input (" + input +
277 "): recorded output events remain unchecked:" +
278 impl->events_as_string());
279 }
280 if (impl->is_flushed()) {
281 throw std::logic_error("cannot accept input (" + input +
282 "): output has been flushed");
283 }
284 }
285
305 template <typename Event, typename EventList>
306 auto pop(feed_as feeder_value_category, emitted_as value_category)
307 -> Event {
309 auto const events = peek_events<EventList>();
310 try {
311 if (events.empty())
312 throw std::logic_error("missing event");
313 check_value_category(feeder_value_category, value_category,
314 events.front().first);
315 auto const *event = std::get_if<Event>(&events.front().second);
316 if (event == nullptr)
317 throw std::logic_error("type mismatch");
318 impl->pop_event();
319 return *event;
320 } catch (std::logic_error const &exc) {
321 std::ostringstream stream;
322 stream << "event pop failed: " << exc.what() << '\n';
323 stream << "expected recorded output event of type "
324 << std::string(typeid(Event).name()) << " ("
325 << feeder_value_category << ", " << value_category
326 << ") but found";
327 if (events.empty()) {
328 stream << " no events";
329 } else {
330 stream << ':';
331 for (auto const &e : events)
332 stream << '\n' << e;
333 }
334 throw std::logic_error(stream.str());
335 }
336 }
337
362 template <typename Event, typename EventList>
363 auto check(feed_as feeder_value_category, emitted_as value_category,
364 Event const &expected_event) -> bool {
366 auto events = peek_events<EventList>();
367 try {
368 if (events.empty())
369 throw std::logic_error("missing event");
370 check_value_category(feeder_value_category, value_category,
371 events.front().first);
372 auto const *event = std::get_if<Event>(&events.front().second);
373 if (event == nullptr)
374 throw std::logic_error("type mismatch");
375 if (*event != expected_event)
376 throw std::logic_error("value mismatch");
377 impl->pop_event();
378 return true;
379 } catch (std::logic_error const &exc) {
380 std::ostringstream stream;
381 stream << "event check failed: " << exc.what() << '\n';
382 stream << "expected recorded output event " << expected_event
383 << " (" << feeder_value_category << ", " << value_category
384 << ") but found";
385 if (events.empty()) {
386 stream << " no events";
387 } else {
388 stream << ':';
389 for (auto const &e : events)
390 stream << '\n' << e;
391 }
392 throw std::logic_error(stream.str());
393 }
394 }
395
407 auto check_not_flushed() -> bool {
408 if (not impl->is_empty()) {
409 throw std::logic_error(
410 "expected no recorded output events but found:" +
411 impl->events_as_string());
412 }
413 if (impl->is_flushed()) {
414 throw std::logic_error(
415 "expected output unflushed but found flushed");
416 }
417 return true;
418 }
419
431 auto check_flushed() -> bool {
432 if (not impl->is_empty()) {
433 throw std::logic_error(
434 "expected no recorded output events but found:" +
435 impl->events_as_string());
436 }
437 if (not impl->is_flushed()) {
438 throw std::logic_error(
439 "expected output flushed but found unflushed");
440 }
441 return true;
442 }
443
450 void throw_error_on_next(std::size_t count = 0) {
451 impl->set_up_to_throw(count, true);
452 }
453
460 void throw_end_processing_on_next(std::size_t count = 0) {
461 impl->set_up_to_throw(count, false);
462 }
463
468 impl->set_up_to_throw(std::numeric_limits<std::size_t>::max(), true);
469 }
470
475 impl->set_up_to_throw(std::numeric_limits<std::size_t>::max(), false);
476 }
477};
478
488template <typename EventList> class capture_output_checker {
490
491 feed_as feeder_valcat;
492
493 public:
498 explicit capture_output_checker(feed_as feeder_value_category,
500 : acc(std::move(access)), feeder_valcat(feeder_value_category) {}
501
506 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-member-init)
507 explicit capture_output_checker(feed_as feeder_value_category,
508 std::shared_ptr<context> context,
509 std::string const &name)
511 feeder_value_category,
512 context->access<capture_output_access>(name)) {}
513
520 template <typename Event> auto pop() -> Event {
522 }
523
535 template <typename Event> auto pop(emitted_as value_category) -> Event {
537 return acc.pop<Event, EventList>(feeder_valcat, value_category);
538 }
539
546 template <typename Event> auto check(Event const &expected_event) -> bool {
547 return check(emitted_as::any_allowed, expected_event);
548 }
549
569 template <typename Event>
570 auto check(emitted_as value_category, Event const &expected_event)
571 -> bool {
573 return acc.check<Event, EventList>(feeder_valcat, value_category,
574 expected_event);
575 }
576
588 auto check_not_flushed() -> bool { return acc.check_not_flushed(); }
589
601 auto check_flushed() -> bool { return acc.check_flushed(); }
602
609 void throw_error_on_next(std::size_t count = 0) {
610 acc.throw_error_on_next(count);
611 }
612
619 void throw_end_processing_on_next(std::size_t count = 0) {
620 acc.throw_end_processing_on_next(count);
621 }
622
626 void throw_error_on_flush() { acc.throw_error_on_flush(); }
627
632 acc.throw_end_processing_on_flush();
633 }
634};
635
636namespace internal {
637
638template <typename EventList> class capture_output {
639 vector_queue<recorded_event<EventList>> output;
640 bool flushed = false;
641 std::size_t error_in = std::numeric_limits<std::size_t>::max();
642 std::size_t end_in = std::numeric_limits<std::size_t>::max();
643 bool error_on_flush = false;
644 bool end_on_flush = false;
645
646 access_tracker<capture_output_access> trk;
647
648 struct access_impl : capture_output_access_impl_base {
649 capture_output *self;
650
651 explicit access_impl(capture_output *self) : self(self) {}
652
653 [[nodiscard]] auto peek_events() const -> std::any final {
654 return self->peek();
655 }
656 void pop_event() final { self->output.pop(); }
657 [[nodiscard]] auto is_empty() const -> bool final {
658 return self->output.empty();
659 }
660 [[nodiscard]] auto is_flushed() const -> bool final {
661 return self->flushed;
662 }
663 void set_up_to_throw(std::size_t count, bool use_error) final {
664 self->set_up_to_throw(count, use_error);
665 }
666 [[nodiscard]] auto events_as_string() const -> std::string final {
667 return self->events_as_string();
668 }
669 };
670
671 auto peek() const {
672 std::vector<recorded_event<EventList>> ret;
673 ret.reserve(output.size());
674 // Cannot use std::copy because vector_queue lacks iterators.
675 output.for_each([&ret](auto const &pair) { ret.push_back(pair); });
676 return ret;
677 }
678
679 [[nodiscard]] auto events_as_string() const -> std::string {
680 std::ostringstream stream;
681 output.for_each([&](auto const &event) { stream << '\n' << event; });
682 return stream.str();
683 }
684
685 void set_up_to_throw(std::size_t count, bool use_error) {
686 if (count == std::numeric_limits<std::size_t>::max()) {
687 if (use_error)
688 error_on_flush = true;
689 else
690 end_on_flush = true;
691 } else {
692 if (use_error)
693 error_in = count;
694 else
695 end_in = count;
696 }
697 }
698
699 public:
700 explicit capture_output(access_tracker<capture_output_access> &&tracker)
701 : trk(std::move(tracker)) {
702 trk.register_access_factory([](auto &tracker) {
703 auto *self =
704 LIBTCSPC_OBJECT_FROM_TRACKER(capture_output, trk, tracker);
705 return capture_output_access(std::make_unique<access_impl>(self));
706 });
707 }
708
709 [[nodiscard]] auto introspect_node() const -> processor_info {
710 return processor_info(this, "capture_output");
711 }
712
713 [[nodiscard]] auto introspect_graph() const -> processor_graph {
714 return processor_graph().push_entry_point(this);
715 }
716
717 template <typename Event>
718 requires type_list_member<std::remove_cvref_t<Event>, EventList>
719 void handle(Event &&event) {
720 static constexpr auto valcat = [] {
721 if constexpr (std::is_lvalue_reference_v<Event>) {
722 if constexpr (std::is_const_v<std::remove_reference_t<Event>>)
723 return emitted_value_category::const_lvalue;
724 else
725 return emitted_value_category::nonconst_lvalue;
726 } else {
727 if constexpr (std::is_const_v<Event>)
728 return emitted_value_category::const_rvalue;
729 else
730 return emitted_value_category::nonconst_rvalue;
731 }
732 }();
733 assert(not flushed);
734 if (error_in == 0)
735 throw test_error("test error upon event");
736 output.push(std::pair{valcat, std::forward<Event>(event)});
737 if (end_in == 0)
738 throw end_of_processing("test end-of-stream upon event");
739 --error_in;
740 --end_in;
741 }
742
743 void flush() {
744 assert(not flushed);
745 if (error_on_flush) {
746 throw test_error("test error upon flush");
747 }
748 flushed = true;
749 if (end_on_flush)
750 throw end_of_processing("test end-of-stream upon flush");
751 }
752};
753
754// Specialization for empty event list.
755template <> class capture_output<type_list<>> {
756 bool flushed = false;
757 bool error_on_flush = false;
758 bool end_on_flush = false;
759 access_tracker<capture_output_access> trk;
760
761 struct access_impl : capture_output_access_impl_base {
762 capture_output *self;
763
764 explicit access_impl(capture_output *self) : self(self) {}
765
766 [[noreturn]] static void not_allowed() {
767 throw std::logic_error(
768 "operation not allowed on capture_output with empty event list");
769 }
770
771 [[nodiscard]] auto peek_events() const -> std::any final {
772 not_allowed();
773 }
774 void pop_event() final { not_allowed(); }
775 [[nodiscard]] auto is_empty() const -> bool final { return true; }
776 [[nodiscard]] auto is_flushed() const -> bool final {
777 return self->flushed;
778 }
779 void set_up_to_throw(std::size_t /* count */,
780 bool /* use_error */) final {
781 not_allowed();
782 }
783 [[nodiscard]] auto events_as_string() const -> std::string final {
784 not_allowed();
785 }
786 };
787
788 void set_up_to_throw(std::size_t count, bool use_error) {
789 if (count == std::numeric_limits<std::size_t>::max()) {
790 if (use_error)
791 error_on_flush = true;
792 else
793 end_on_flush = true;
794 }
795 }
796
797 public:
798 explicit capture_output(access_tracker<capture_output_access> &&tracker)
799 : trk(std::move(tracker)) {
800 trk.register_access_factory([](auto &tracker) {
801 auto *self =
802 LIBTCSPC_OBJECT_FROM_TRACKER(capture_output, trk, tracker);
803 return capture_output_access(std::make_unique<access_impl>(self));
804 });
805 }
806
807 [[nodiscard]] auto introspect_node() const -> processor_info {
808 return processor_info(this, "capture_output");
809 }
810
811 [[nodiscard]] auto introspect_graph() const -> processor_graph {
812 return processor_graph().push_entry_point(this);
813 }
814
815 void flush() {
816 assert(not flushed);
817 if (error_on_flush) {
818 throw test_error("test error upon flush");
819 }
820 flushed = true;
821 if (end_on_flush)
822 throw end_of_processing("test end-of-stream upon flush");
823 }
824};
825
826template <typename Downstream> class feed_input {
827 static_assert(processor<Downstream>);
828
829 std::vector<std::pair<std::shared_ptr<context>, std::string>>
830 outputs_to_check; // (context, name)
831 feed_as refmode = feed_as::const_lvalue;
832 Downstream downstream;
833
834 void check_outputs_ready(std::string const &input) {
835 if (outputs_to_check.empty())
836 throw std::logic_error(
837 "feed_input has no registered capture_output to check");
838 for (auto &[context, name] : outputs_to_check)
839 context->template access<capture_output_access>(name)
840 .check_ready_for_input(input);
841 }
842
843 public:
844 explicit feed_input(Downstream downstream)
845 : downstream(std::move(downstream)) {}
846
847 explicit feed_input(feed_as mode, Downstream downstream)
848 : refmode(mode), downstream(std::move(downstream)) {}
849
850 [[nodiscard]] auto introspect_node() const -> processor_info {
851 return processor_info(this, "feed_input");
852 }
853
854 [[nodiscard]] auto introspect_graph() const -> processor_graph {
855 return downstream.introspect_graph().push_entry_point(this);
856 }
857
858 void require_output_checked(std::shared_ptr<context> context,
859 std::string name) {
860 context->access<capture_output_access>(name); // Fail early.
861 outputs_to_check.emplace_back(std::move(context), std::move(name));
862 }
863
864 template <typename Event>
865 requires handler_for<Downstream, std::remove_cvref_t<Event>>
866 void handle(Event &&event) {
867 check_outputs_ready("event of type " +
868 std::string(typeid(event).name()));
869
870 if (refmode == feed_as::const_lvalue) {
871 downstream.handle(static_cast<Event const &>(event));
872 } else if constexpr (std::is_lvalue_reference_v<Event>) {
873 std::remove_cvref_t<Event> copy(event);
874 downstream.handle(std::move(copy));
875 } else {
876 downstream.handle(std::forward<Event>(event));
877 }
878 }
879
880 void flush() {
881 check_outputs_ready("flush");
882 downstream.flush();
883 }
884};
885
886} // namespace internal
887
913template <typename EventList>
915 return internal::capture_output<EventList>(std::move(tracker));
916}
917
952template <typename Downstream>
953auto feed_input(feed_as value_category, Downstream downstream) {
954 return internal::feed_input<Downstream>(value_category,
955 std::move(downstream));
956}
957
965template <int N> struct empty_test_event {
967 friend auto operator==(empty_test_event<N> const &lhs,
968 empty_test_event<N> const &rhs) noexcept
969 -> bool = default;
970
972 friend auto operator<<(std::ostream &strm,
973 empty_test_event<N> const & /* e */)
974 -> std::ostream & {
975 return strm << "empty_test_event<" << N << ">";
976 }
977};
978
988template <int N, typename DataTypes = default_data_types>
991 typename DataTypes::abstime_type abstime;
992
994 friend auto operator==(time_tagged_test_event const &lhs,
995 time_tagged_test_event const &rhs) noexcept
996 -> bool = default;
997
999 friend auto operator<<(std::ostream &strm, time_tagged_test_event const &e)
1000 -> std::ostream & {
1001 return strm << "time_tagged_test_event<" << N << ">{" << e.abstime
1002 << "}";
1003 }
1004};
1005
1014template <typename T>
1015auto test_bucket(std::initializer_list<T> il) -> bucket<T> {
1016 using U = std::remove_cv_t<T>;
1017 struct test_storage {
1018 std::vector<U> v;
1019 };
1020 auto storage = test_storage{std::vector<U>(il.begin(), il.end())};
1021 return bucket<T>(std::span(storage.v), std::move(storage));
1022}
1023
1031template <typename T> auto test_bucket(std::span<T> s) -> bucket<T> {
1032 using U = std::remove_cv_t<T>;
1033 struct test_storage {
1034 std::vector<U> v;
1035 };
1036 auto storage = test_storage{std::vector<U>(s.begin(), s.end())};
1037 return bucket<T>(std::span(storage.v), std::move(storage));
1038}
1039
1052template <typename T> class test_bucket_source : public bucket_source<T> {
1053 std::shared_ptr<bucket_source<T>> src;
1054 T value;
1055 std::size_t count = 0;
1056
1057 explicit test_bucket_source(
1058 std::shared_ptr<bucket_source<T>> backing_source, T fill_value)
1059 : src(std::move(backing_source)), value(std::move(fill_value)) {}
1060
1061 public:
1063 static auto create(std::shared_ptr<bucket_source<T>> backing_source,
1064 T fill_value)
1065 -> std::shared_ptr<test_bucket_source<T>> {
1066 return std::shared_ptr<test_bucket_source<T>>(new test_bucket_source(
1067 std::move(backing_source), std::move(fill_value)));
1068 }
1069
1071 auto bucket_of_size(std::size_t size) -> bucket<T> override {
1072 auto b = src->bucket_of_size(size);
1073 std::fill(b.begin(), b.end(), value);
1074 ++count;
1075 return b;
1076 }
1077
1079 [[nodiscard]] auto bucket_count() const noexcept -> std::size_t {
1080 return count;
1081 }
1082
1084 [[nodiscard]] auto supports_shared_views() const noexcept
1085 -> bool override {
1086 return src->supports_shared_views();
1087 }
1088
1090 [[nodiscard]] auto shared_view_of(bucket<T> const &bkt)
1091 -> bucket<T const> override {
1092 return src->shared_view_of(bkt);
1093 }
1094};
1095
1117template <typename Event>
1118inline auto
1119from_reversed_bytes(std::array<std::uint8_t, sizeof(Event)> bytes) noexcept {
1120 static_assert(std::is_trivial_v<Event>);
1121 auto const srcspan = std::as_bytes(std::span(&bytes, 1));
1122 Event ret{};
1123 auto const retspan = std::as_writable_bytes(std::span(&ret, 1));
1124 std::reverse_copy(srcspan.begin(), srcspan.end(), retspan.begin());
1125 return ret;
1126}
1127
1128} // namespace tcspc
Tracker that mediates access to objects via a tcspc::context.
Definition context.hpp:39
Value-semantic container for array data allowing use of custom storage.
Definition bucket.hpp:110
Access for tcspc::capture_output() processors.
Definition test_utils.hpp:252
auto check_not_flushed() -> bool
Check that no recorded output events remain but the output has not been flushed.
Definition test_utils.hpp:407
void throw_error_on_next(std::size_t count=0)
Arrange to throw tcspc::test_error on receiving the given number of events.
Definition test_utils.hpp:450
void throw_end_processing_on_next(std::size_t count=0)
Arrange to throw tcspc::end_of_processing on receiving the given number of events.
Definition test_utils.hpp:460
void check_ready_for_input(std::string const &input) const
Check if ready for input; normally used internally by tcspc::feed_input().
Definition test_utils.hpp:273
auto check(feed_as feeder_value_category, emitted_as value_category, Event const &expected_event) -> bool
Check that the next recorded output event matches with the given one.
Definition test_utils.hpp:363
auto check_flushed() -> bool
Check that no recorded output events remain and the output has been flushed.
Definition test_utils.hpp:431
void throw_end_processing_on_flush()
Arrange to throw tcspc::end_of_processing on receiving a flush.
Definition test_utils.hpp:474
auto pop(feed_as feeder_value_category, emitted_as value_category) -> Event
Retrieve the next recorded output event.
Definition test_utils.hpp:306
void throw_error_on_flush()
Arrange to throw tcspc::test_error on receiving a flush.
Definition test_utils.hpp:467
auto check(Event const &expected_event) -> bool
Check that the next recorded output event matches with the given event, disregarding value category.
Definition test_utils.hpp:546
void throw_end_processing_on_next(std::size_t count=0)
Arrange to throw tcspc::end_of_processing on receiving the given number of events.
Definition test_utils.hpp:619
auto check_flushed() -> bool
Check that no recorded output events remain and the output has been flushed.
Definition test_utils.hpp:601
auto check(emitted_as value_category, Event const &expected_event) -> bool
Check that the next recorded output event matches with the given event and value category.
Definition test_utils.hpp:570
capture_output_checker(feed_as feeder_value_category, std::shared_ptr< context > context, std::string const &name)
Construct from a context, tracker name of tcspc::capture_output processor, and feeder's value categor...
Definition test_utils.hpp:507
auto pop(emitted_as value_category) -> Event
Retrieve the next recorded output event, checking its value category.
Definition test_utils.hpp:535
void throw_end_processing_on_flush()
Arrange to throw tcspc::end_of_processing on receiving a flush.
Definition test_utils.hpp:631
void throw_error_on_next(std::size_t count=0)
Arrange to throw tcspc::test_error on receiving the given number of events.
Definition test_utils.hpp:609
capture_output_checker(feed_as feeder_value_category, capture_output_access access)
Construct from a tcspc::capture_output_access, with the feeder's value category.
Definition test_utils.hpp:498
auto pop() -> Event
Retrieve the next recorded output event, disregarding value category.
Definition test_utils.hpp:520
void throw_error_on_flush()
Arrange to throw tcspc::test_error on receiving a flush.
Definition test_utils.hpp:626
auto check_not_flushed() -> bool
Check that no recorded output events remain but the output has not been flushed.
Definition test_utils.hpp:588
Context for enabling access to objects after they have been incorporated into a processing graph.
Definition context.hpp:113
auto supports_shared_views() const noexcept -> bool override
Implements sharable bucket source requirement.
Definition test_utils.hpp:1084
auto bucket_of_size(std::size_t size) -> bucket< T > override
Implements bucket source requirement.
Definition test_utils.hpp:1071
static auto create(std::shared_ptr< bucket_source< T > > backing_source, T fill_value) -> std::shared_ptr< test_bucket_source< T > >
Create an instance.
Definition test_utils.hpp:1063
auto bucket_count() const noexcept -> std::size_t
Return the number of buckets created so far.
Definition test_utils.hpp:1079
auto shared_view_of(bucket< T > const &bkt) -> bucket< T const > override
Implements sharable bucket source requirement.
Definition test_utils.hpp:1090
Concept that is satisfied when a type is contained in a type list.
Definition type_list.hpp:158
#define LIBTCSPC_OBJECT_FROM_TRACKER(obj_type, tracker_field_name, tracker)
Recover the object address from a tcspc::access_tracker embedded in the object.
Definition context.hpp:253
auto test_bucket(std::initializer_list< T > il) -> bucket< T >
Create an ad-hoc tcspc::bucket<T> for testing, from a list of values.
Definition test_utils.hpp:1015
emitted_as
Value category to check emitted events against.
Definition test_utils.hpp:133
feed_as
Value category used to feed an event via tcspc::feed_input.
Definition test_utils.hpp:108
auto from_reversed_bytes(std::array< std::uint8_t, sizeof(Event)> bytes) noexcept
Bit-cast an array of bytes to an event after reversing the order.
Definition test_utils.hpp:1119
@ always_rvalue
Require non-const rvalue.
Definition test_utils.hpp:141
@ any_allowed
Require const lvalue or rvalue, or non-const rvalue.
Definition test_utils.hpp:135
@ same_as_fed
Require the same category as the events being fed.
Definition test_utils.hpp:137
@ always_lvalue
Require const lvalue.
Definition test_utils.hpp:139
@ rvalue
Feed as non-const rvalue.
Definition test_utils.hpp:112
@ const_lvalue
Feed as const lvalue.
Definition test_utils.hpp:110
auto count(access_tracker< count_access > &&tracker, Downstream downstream)
Create a processor that counts events of a given type.
Definition count.hpp:313
auto sink_events()
Create a processor that ignores only specific event types.
Definition test_utils.hpp:81
auto sink_event_list()
Create a processor that ignores only specific event types.
Definition test_utils.hpp:99
auto feed_input(feed_as value_category, Downstream downstream)
Create a source for feeding test input to a processor under test.
Definition test_utils.hpp:953
auto capture_output(access_tracker< capture_output_access > &&tracker)
Create a sink that records the output of a processor under test.
Definition test_utils.hpp:914
libtcspc namespace.
Definition acquire.hpp:29
Abstract base class for polymorphic bucket sources.
Definition bucket.hpp:505
Empty event for testing.
Definition test_utils.hpp:965
friend auto operator<<(std::ostream &strm, empty_test_event< N > const &) -> std::ostream &
Stream insertion operator.
Definition test_utils.hpp:972
friend auto operator==(empty_test_event< N > const &lhs, empty_test_event< N > const &rhs) noexcept -> bool=default
Equality comparison operator.
Timestamped event for testing.
Definition test_utils.hpp:989
friend auto operator<<(std::ostream &strm, time_tagged_test_event const &e) -> std::ostream &
Stream insertion operator.
Definition test_utils.hpp:999
friend auto operator==(time_tagged_test_event const &lhs, time_tagged_test_event const &rhs) noexcept -> bool=default
Equality comparison operator.
DataTypes::abstime_type abstime
Timestamp.
Definition test_utils.hpp:991
Compile-time representation of a list of types.
Definition type_list.hpp:38