KiCad PCB EDA Suite
Loading...
Searching...
No Matches
unit_test_utils.h
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright The KiCad Developers, see AUTHORS.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <https://www.gnu.org/licenses/>.
18 */
19
20#pragma once
21
22#define BOOST_NO_AUTO_PTR
23
24#include <boost/test/unit_test.hpp>
25
27
28#include <cstdint>
29#include <functional>
30#include <optional>
31#include <set>
32#include <vector>
33
34#include <wx/gdicmn.h>
35#include <wx/log.h>
36#include <wx/string.h>
37
38#include <wx_log_utils.h>
39
40
41template<class T>
43{
44 PRINTABLE_OPT( const std::optional<T>& aOpt ) : m_Opt( aOpt ){};
45 PRINTABLE_OPT( const T& aVal ) : m_Opt( aVal ){};
46
47 std::optional<T> m_Opt;
48};
49
50
54#define KI_CHECK_OPT_EQUAL( lhs, rhs ) \
55 BOOST_CHECK_EQUAL( PRINTABLE_OPT( lhs ), PRINTABLE_OPT( rhs ) )
56
57
58template <class T>
59inline std::ostream& operator<<( std::ostream& aOs, const PRINTABLE_OPT<T>& aOptional )
60{
61 if( aOptional.m_Opt.has_value() )
62 aOs << *aOptional.m_Opt;
63 else
64 aOs << "nullopt";
65
66 return aOs;
67}
68
69
70template <class L, class R>
71inline bool operator==( const PRINTABLE_OPT<L>& aLhs, const PRINTABLE_OPT<R>& aRhs )
72{
73 if( !aLhs.m_Opt.has_value() && !aRhs.m_Opt.has_value() )
74 return true; // both nullopt
75
76 return aLhs.m_Opt.has_value() && aRhs.m_Opt.has_value() && *aLhs.m_Opt == *aRhs.m_Opt;
77}
78
79
80template <class L, class R>
81inline bool operator!=( const PRINTABLE_OPT<L>& aLhs, const PRINTABLE_OPT<R>& aRhs )
82{
83 return !( aLhs == aRhs );
84}
85
86
87// boost_test_print_type has to be in the same namespace as the printed type
88namespace std
89{
90
94template <typename T>
95std::ostream& boost_test_print_type( std::ostream& os, std::vector<T> const& aVec )
96{
97 os << "std::vector size " << aVec.size() << " [";
98
99 for( const auto& i : aVec )
100 {
101 os << "\n " << i;
102 }
103
104 os << "]";
105 return os;
106}
107
111template <typename K, typename V>
112std::ostream& boost_test_print_type( std::ostream& os, std::map<K, V> const& aMap )
113{
114 os << "std::map size " << aMap.size() << " [";
115
116 for( const auto& [key, value] : aMap )
117 {
118 os << "\n " << key << " = " << value;
119 }
120
121 os << "]";
122 return os;
123}
124
128template <typename K, typename V>
129std::ostream& boost_test_print_type( std::ostream& os, std::pair<K, V> const& aPair )
130{
131 os << "[" << aPair.first << ", " << aPair.second << "]";
132 return os;
133}
134
138template <typename T>
139std::ostream& boost_test_print_type( std::ostream& os, std::set<T> const& aSet )
140{
141 os << "set size " << aSet.size() << " [";
142 for( const auto& i : aSet )
143 {
144 os << "\n " << i;
145 }
146
147 os << "]";
148 return os;
149}
150
151} // namespace std
152
153
154//-----------------------------------------------------------------------------+
155// Boost.Test printing helpers for wx types / wide string literals
156//-----------------------------------------------------------------------------+
157
158// C++20 removed operator<<(ostream&, const wchar_t*) (P1423R3), which breaks wxString streaming
159// via implicit wchar_t* conversion in Boost.Test's lazy_ostream (used by BOOST_TEST_CONTEXT,
160// BOOST_TEST_MESSAGE, BOOST_CHECK_MESSAGE, and BOOST_FAIL).
161inline std::ostream& boost_test_print_type( std::ostream& os, const wxString& v )
162{
163#if wxUSE_UNICODE
164 os << v.ToUTF8().data();
165#else
166 os << v.c_str();
167#endif
168 return os;
169}
170
171
172// Wide string literal arrays
173template <std::size_t N>
174std::ostream& boost_test_print_type( std::ostream& os, const wchar_t ( &ws )[N] )
175{
176 wxString tmp( ws );
177#if wxUSE_UNICODE
178 os << tmp.ToUTF8().data();
179#else
180 os << tmp;
181#endif
182 return os;
183}
184
185
186namespace boost { namespace unit_test
187{
194template<> struct is_forward_iterable<wxString> : public mpl::false_ {};
195
196}} // namespace boost::unit_test
197
198
199namespace boost { namespace test_tools { namespace tt_detail {
200
201template<std::size_t N>
202struct print_log_value<wchar_t[ N ]>
203{
204 void operator()( std::ostream& os, const wchar_t (&ws)[ N ] )
205 {
206 wxString tmp( ws );
207#if wxUSE_UNICODE
208 os << tmp.ToUTF8().data();
209#else
210 os << tmp;
211#endif
212 }
213};
214
215}}} // namespace boost::test_tools::tt_detail
216
217
222std::ostream& boost_test_print_type( std::ostream& os, wxPoint const& aVec );
223
224namespace KI_TEST
225{
226
227template <typename EXP_CONT> using EXP_OBJ = typename EXP_CONT::value_type;
228template <typename FOUND_CONT> using FOUND_OBJ = typename FOUND_CONT::value_type;
229
246template <typename EXP_OBJ, typename FOUND_OBJ>
247using MATCH_PRED = std::function<bool( const EXP_OBJ&, const FOUND_OBJ& )>;
248
282template <typename EXP_CONT, typename FOUND_CONT, typename MATCH_PRED>
283void CheckUnorderedMatches( const EXP_CONT& aExpected, const FOUND_CONT& aFound,
284 MATCH_PRED aMatchPredicate )
285{
286 using EXP_OBJ = typename EXP_CONT::value_type;
287
288 // set of object we've already found
289 std::set<const EXP_OBJ*> matched;
290
291 // fill the set of object that match
292 for( const auto& found : aFound )
293 {
294 for( const auto& expected : aExpected )
295 {
296 if( aMatchPredicate( expected, found ) )
297 {
298 matched.insert( &expected );
299 break;
300 }
301 }
302 }
303
304 // first check every expected object was "found"
305 for( const EXP_OBJ& exp : aExpected )
306 {
307 BOOST_CHECK_MESSAGE( matched.count( &exp ) > 0, "Expected item was not found. Expected: \n"
308 << exp );
309 }
310
311 // check every "found" object was expected
312 for( const EXP_OBJ* found : matched )
313 {
314 const bool was_expected = std::find_if( aExpected.begin(), aExpected.end(),
315 [found]( const EXP_OBJ& aObj )
316 {
317 return &aObj == found;
318 } ) != aExpected.end();
319
320 BOOST_CHECK_MESSAGE( was_expected, "Found item was not expected. Found: \n" << *found );
321 }
322}
323
324
328template <typename T>
329bool CollectionHasNoDuplicates( const T& aCollection )
330{
331 T sorted = aCollection;
332 std::sort( sorted.begin(), sorted.end() );
333
334 return std::adjacent_find( sorted.begin(), sorted.end() ) == sorted.end();
335}
336
337
345{
346 std::string m_CaseName;
347
348 friend std::ostream& operator<<( std::ostream& os, const NAMED_CASE& aCase )
349 {
350 os << aCase.m_CaseName;
351 return os;
352 }
353};
354
355
364#if wxDEBUG_LEVEL > 0
365#define CHECK_WX_ASSERT( STATEMENT ) BOOST_CHECK_THROW( STATEMENT, KI_TEST::WX_ASSERT_ERROR );
366#else
367#define CHECK_WX_ASSERT( STATEMENT )
368#endif
369
370
375class COUNTING_WXLOG : public wxLog
376{
377public:
378 COUNTING_WXLOG( wxLogLevelValues aMaxLevel ) :
379 m_maxLevel( aMaxLevel )
380 {
381 }
382
383 unsigned GetCount() const { return m_count; }
384
385protected:
386 void DoLogRecord( wxLogLevel aLevel, const wxString&, const wxLogRecordInfo& ) override
387 {
388 if( aLevel <= m_maxLevel )
389 m_count++;
390 }
391
392private:
393 wxLogLevelValues m_maxLevel;
394 unsigned m_count = 0;
395};
396
397
405{
406public:
414 SCOPED_COUNTING_WXLOG( unsigned* aLogCount, wxLogLevelValues aMaxLevel = wxLOG_Error ) :
416 m_logger( aMaxLevel ),
417 m_logCountRef( aLogCount )
418 {
419 }
420
422 {
423 // Update the caller's log count reference with the number of logs recorded
424 if( m_logCountRef )
425 *m_logCountRef = m_logger.GetCount();
426 }
427
428 /*
429 * Gets the current count of matching log messages.
430 */
431 unsigned GetCount() const { return m_logger.GetCount(); }
432
433private:
435 unsigned* m_logCountRef;
436};
437
438
447std::string GetEeschemaTestDataDir();
448
449std::string GetTestDataRootDir();
450
459std::vector<uint8_t> LoadBinaryData( const std::string& aFilePath, std::optional<size_t> aLoadBytes = std::nullopt );
460
474std::string LoadStringData( const wxString& aPath );
475
483bool CanDoDisplayTests();
484
485} // namespace KI_TEST
Counts error-level records so a failed save can be checked for reports beyond the one it throws.
COUNTING_WXLOG(wxLogLevelValues aMaxLevel)
wxLogLevelValues m_maxLevel
unsigned GetCount() const
void DoLogRecord(wxLogLevel aLevel, const wxString &, const wxLogRecordInfo &) override
SCOPED_COUNTING_WXLOG(unsigned *aLogCount, wxLogLevelValues aMaxLevel=wxLOG_Error)
SCOPED_WXLOG_TARGET(wxLog *aTarget)
Test utilities for timestamps.
std::string GetTestDataRootDir()
std::vector< uint8_t > LoadBinaryData(const std::string &aFilePath, std::optional< size_t > aLoadBytes=std::nullopt)
Load the contents of a file into a vector of bytes.
void CheckUnorderedMatches(const EXP_CONT &aExpected, const FOUND_CONT &aFound, MATCH_PRED aMatchPredicate)
Check that a container of "found" objects matches a container of "expected" objects.
typename FOUND_CONT::value_type FOUND_OBJ
std::string LoadStringData(const wxString &aPath)
Load the contents of a file into a string.
std::function< bool(const EXP_OBJ &, const FOUND_OBJ &)> MATCH_PRED
A match predicate: check that a "found" object is equivalent to or represents an "expected" object,...
std::string GetEeschemaTestDataDir()
Get the configured location of Eeschema test data.
typename EXP_CONT::value_type EXP_OBJ
bool CollectionHasNoDuplicates(const T &aCollection)
Predicate to check a collection has no duplicate elements.
bool CanDoDisplayTests()
Some tests on some platforms require a display connection to run.
STL namespace.
std::ostream & boost_test_print_type(std::ostream &os, std::vector< T > const &aVec)
Boost print helper for generic vectors.
A named data-driven test case.
friend std::ostream & operator<<(std::ostream &os, const NAMED_CASE &aCase)
std::optional< T > m_Opt
PRINTABLE_OPT(const T &aVal)
PRINTABLE_OPT(const std::optional< T > &aOpt)
void operator()(std::ostream &os, const wchar_t(&ws)[N])
VECTOR3I expected(15, 30, 45)
bool operator!=(const PRINTABLE_OPT< L > &aLhs, const PRINTABLE_OPT< R > &aRhs)
bool operator==(const PRINTABLE_OPT< L > &aLhs, const PRINTABLE_OPT< R > &aRhs)
std::ostream & operator<<(std::ostream &aOs, const PRINTABLE_OPT< T > &aOptional)
std::ostream & boost_test_print_type(std::ostream &os, const wxString &v)