HART  0.2.0
High level Audio Regression and Testing
Loading...
Searching...
No Matches
hart_matcher_function.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <functional>
4#include <ostream>
5
8#include "matchers/hart_matcher.hpp"
9
10namespace hart
11{
12
13/// @brief Matcher defined by a user-provided function
14/// @details This matcher allows defining custom matching logic using a callable object
15/// (e.g. a lambda, function, or functor), instead of creating a dedicated matcher class.
16/// The function can have one of the following signatures:
17/// @code
18/// Condition (const AudioBuffer<SampleType>& output);
19/// Condition (const AudioBuffer<SampleType>& input, const AudioBuffer<SampleType>& output);
20/// @endcode
21///
22/// Example:
23/// @code
24/// [] (const AudioBuffer<SampleType>& output) { return HART_LESS_THAN (crestFactorDb (output), 3_dB); }
25/// @endcode
26///
27/// Return value should be a hart::Condition object, which you can create using one of the built-in macros,
28/// like HART_FLOAT_EQUAL(), HART_LESS_THAN() etc. For the full list of available macros, see @ref Conditions.
29///
30/// The function may also return a regular boolean value, like so:
31/// @code
32/// [] (const AudioBuffer<SampleType>& output) { return crestFactorDb (output) < 3_dB; }
33/// @endcode
34///
35/// In this case result will still be implicitly converted to Condition. However, it is encouraged to use proper
36/// Condition macros, since returning a plain `bool` won't give you detailed traceback info if the test fails.
37///
38/// The matcher operates on the full rendered buffer and does not support
39/// per-block evaluation.
40/// @ingroup Matchers
41template<typename SampleType>
43 public Matcher<SampleType, MatcherFunction<SampleType>>
44{
45public:
46
47 /// @brief Creates a matcher from a function that compares input and output
48 /// @param matcherFunction A callable with signature:
49 /// @code
50 /// Condition (const AudioBuffer<SampleType>& input,
51 /// const AudioBuffer<SampleType>& output)
52 /// @endcode
53 /// It should return `true` if the output satisfies the expected condition, `false` otherwise
54 /// @param label Optional human-readable label used in failure reports
55 MatcherFunction (std::function <Condition (const AudioBuffer<SampleType>&, const AudioBuffer<SampleType>&)> matcherFunction, const std::string& label = {}) :
56 m_matcherFunctionForInputAndOutput (std::move (matcherFunction)),
57 m_label (label)
58 {
59 }
60
61 /// @brief Constructs a matcher from a function that inspects only the output
62 /// @param matcherFunction A callable with signature:
63 /// @code
64 /// Condition (const AudioBuffer<SampleType>& output)
65 /// @endcode
66 /// It should return `true` if the output satisfies the expected condition, `false` otherwise
67 /// @param label Optional human-readable label used in failure reports
68 MatcherFunction (std::function <Condition (const AudioBuffer<SampleType>&)> matcherFunction, const std::string& label = {}) :
69 m_matcherFunctionForOutputOnly (std::move (matcherFunction)),
70 m_label (label)
71 {
72 }
73
74 bool match (AnalysisContext<SampleType> context) override
75 {
76 if (m_matcherFunctionForOutputOnly != nullptr)
77 {
78 const AudioBuffer<SampleType>& observedOutputAudio = context.outputAudio();
79
80 m_condition = std::move (m_matcherFunctionForOutputOnly (observedOutputAudio));
81 return m_condition.getResult();
82 }
83
84 if (m_matcherFunctionForInputAndOutput != nullptr)
85 {
86 const AudioBuffer<SampleType>& inputAudio = context.inputAudio();
87 const AudioBuffer<SampleType>& observedOutputAudio = context.outputAudio();
88
89 m_condition = std::move (m_matcherFunctionForInputAndOutput (inputAudio, observedOutputAudio));
90 return m_condition.getResult();
91 }
92
93 HART_THROW_OR_RETURN (hart::NullPointerError, "Matcher function is a nullptr!", false);
94 }
95
97 {
99 details.frame = 0;
100 details.channel = 0;
101 std::ostringstream descriptionStream;
102
103 if (m_condition.hasDetailedMetadata())
104 {
105 hassert (m_condition.getLine() >= 0);
106 hassert (m_condition.getFile() != nullptr);
107 hassert (m_condition.getFile()[0] != '\0');
108
109 descriptionStream
110 << "Location: " << m_condition.getFile()
111 << ':' << m_condition.getLine()
112 << "\nCondition: ";
113 m_condition.representWithTokens (descriptionStream);
114 descriptionStream << "\nEvaluated: ";
115 m_condition.representWithStringRepresentations (descriptionStream);
116 }
117 else
118 {
119 descriptionStream << "Matcher function (" << (m_label.empty() ? "no label" : m_label) << ") has evaluated to false";
120 }
121
122 details.description = descriptionStream.str();
123 return details;
124 }
125
126 void represent (std::ostream& stream) const override
127 {
128 stream << "MatcherFunction (<function>, \"" << m_label << "\")";
129 }
130
131 bool canOperatePerBlock() const override { return false;}
132 void prepare (double /* sampleRateHz */, size_t /* numInputChannels */, size_t /* numOutputChannels */, size_t /* maxBlockSizeFrames */) override {}
133
134private:
135 const std::function <Condition (const AudioBuffer<SampleType>&, const AudioBuffer<SampleType>&)> m_matcherFunctionForInputAndOutput = nullptr;
136 const std::function <Condition (const AudioBuffer<SampleType>&)> m_matcherFunctionForOutputOnly = nullptr;
137 const std::string m_label;
138 Condition m_condition;
139};
140
142
143} // namespace hart
Contains audio-related artefacts useful for analysis by matchers.
Container for audio data.
A class representing some condition.
Matcher defined by a user-provided function.
MatcherFunction(std::function< Condition(const AudioBuffer< SampleType > &, const AudioBuffer< SampleType > &)> matcherFunction, const std::string &label={})
Creates a matcher from a function that compares input and output.
void represent(std::ostream &stream) const override
Makes a text representation of this Matcher for test failure outputs.
void prepare(double, size_t, size_t, size_t) override
Prepare for processing It is guaranteed that all subsequent process() calls will be in line with the ...
bool match(AnalysisContext< SampleType > context) override
Tells the host if the piece of audio satisfies Matcher's condition or not.
bool canOperatePerBlock() const override
Tells the host if it can operate on a block-by-block basis.
virtual MatcherFailureDetails getFailureDetails() const override
Returns a description of why the match has failed.
MatcherFunction(std::function< Condition(const AudioBuffer< SampleType > &)> matcherFunction, const std::string &label={})
Constructs a matcher from a function that inspects only the output.
Base for audio matchers.
Thrown when a nullptr could be handled gracefully.
#define hassert(condition)
Triggers a HartAssertException if the condition is false
#define HART_THROW_OR_RETURN(ExceptionType, message, returnValue)
Throws an exception if HART_DO_NOT_THROW_EXCEPTIONS is set, prints a message and returns a specified ...
#define HART_MATCHER_DECLARE_ALIASES_FOR(ClassName)
size_t channel
Index of channel at which the failure was detected.
std::string description
Readable description of why the match has failed.
size_t frame
Index of frame at which the match has failed.