KiCad PCB EDA Suite
Loading...
Searching...
No Matches
test_reporting.cpp
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 modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation, either version 3 of the License, or (at your
9 * option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * 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#include <boost/test/unit_test.hpp>
21#include <reporter.h>
23#include <wx/string.h>
24#include <wx/textctrl.h>
25#include <wx/statusbr.h>
26#include <wx/log.h>
27#include <vector>
28#include <sstream>
29#include <thread>
30
31// Mock TEST_REPORTER to test base REPORTER functionality
32class TEST_REPORTER : public REPORTER
33{
34public:
36
37 REPORTER& Report( const wxString& aText, SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
38 {
39 REPORTER::Report( aText, aSeverity ); // Call base to update severity mask
40 m_messages.push_back( std::make_pair( aText, aSeverity ) );
41 m_hasMessage = true;
43 return *this;
44 }
45
46 bool HasMessage() const override
47 {
48 return m_hasMessage;
49 }
50
51 void Clear() override
52 {
54 m_messages.clear();
55 m_hasMessage = false;
57 }
58
59 const std::vector<std::pair<wxString, SEVERITY>>& GetMessages() const { return m_messages; }
60 int GetMessageCount() const { return m_messageCount; }
61
62private:
63 std::vector<std::pair<wxString, SEVERITY>> m_messages;
66};
67
68// Mock wxTextCtrl for testing
70{
71public:
73 void AppendText( const wxString& text ) { m_content += text; }
74 void SetValue( const wxString& text ) { m_content = text; }
75 wxString GetValue() const { return m_content; }
76 bool IsEmpty() const { return m_content.IsEmpty(); }
77 void Clear() { m_content.Clear(); }
78
79private:
80 wxString m_content;
81};
82
83// Mock wxStatusBar for testing
85{
86public:
87 MockStatusBar() { m_fields.resize( 3 ); } // Default 3 fields
88 void SetStatusText( const wxString& text, int field = 0 )
89 {
90 if( field >= 0 && field < (int)m_fields.size() )
91 m_fields[field] = text;
92 }
93 wxString GetStatusText( int field = 0 ) const
94 {
95 if( field >= 0 && field < (int)m_fields.size() )
96 return m_fields[field];
97 return wxEmptyString;
98 }
99
100private:
101 std::vector<wxString> m_fields;
102};
103
104BOOST_AUTO_TEST_SUITE( ReporterTests )
105
106BOOST_AUTO_TEST_CASE( BaseReporter_Constructor )
107{
109
110 BOOST_CHECK( !reporter.HasMessage() );
111 BOOST_CHECK( reporter.GetUnits() == EDA_UNITS::MM );
112 BOOST_CHECK( !reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO ) );
113}
114
115BOOST_AUTO_TEST_CASE( BaseReporter_SingleReport )
116{
118
119 reporter.Report( wxT("Test message"), RPT_SEVERITY_INFO );
120
121 BOOST_CHECK( reporter.HasMessage() );
122 BOOST_CHECK_EQUAL( reporter.GetMessageCount(), 1 );
123 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO ) );
124
125 const auto& messages = reporter.GetMessages();
126 BOOST_CHECK_EQUAL( messages.size(), 1 );
127 BOOST_CHECK_EQUAL( messages[0].first, wxString("Test message") );
128 BOOST_CHECK_EQUAL( messages[0].second, RPT_SEVERITY_INFO );
129}
130
131BOOST_AUTO_TEST_CASE( BaseReporter_MultipleReports )
132{
134
135 reporter.Report( wxT("Info message"), RPT_SEVERITY_INFO );
136 reporter.Report( wxT("Warning message"), RPT_SEVERITY_WARNING );
137 reporter.Report( wxT("Error message"), RPT_SEVERITY_ERROR );
138
139 BOOST_CHECK( reporter.HasMessage() );
140 BOOST_CHECK_EQUAL( reporter.GetMessageCount(), 3 );
141
142 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO ) );
143 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_WARNING ) );
144 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) );
145 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO | RPT_SEVERITY_WARNING ) );
146}
147
148BOOST_AUTO_TEST_CASE( BaseReporter_ReportTail )
149{
151
152 reporter.ReportTail( wxT("Tail message"), RPT_SEVERITY_INFO );
153
154 BOOST_CHECK( reporter.HasMessage() );
155 BOOST_CHECK_EQUAL( reporter.GetMessageCount(), 1 );
156
157 const auto& messages = reporter.GetMessages();
158 BOOST_CHECK_EQUAL( messages[0].first, wxString("Tail message") );
159 BOOST_CHECK_EQUAL( messages[0].second, RPT_SEVERITY_INFO );
160}
161
162BOOST_AUTO_TEST_CASE( BaseReporter_ReportHead )
163{
165
166 reporter.ReportHead( wxT("Head message"), RPT_SEVERITY_WARNING );
167
168 BOOST_CHECK( reporter.HasMessage() );
169 BOOST_CHECK_EQUAL( reporter.GetMessageCount(), 1 );
170
171 const auto& messages = reporter.GetMessages();
172 BOOST_CHECK_EQUAL( messages[0].first, wxString("Head message") );
173 BOOST_CHECK_EQUAL( messages[0].second, RPT_SEVERITY_WARNING );
174}
175
176BOOST_AUTO_TEST_CASE( BaseReporter_OperatorLeftShift )
177{
179
180 reporter << wxT("Stream message");
181
182 BOOST_CHECK( reporter.HasMessage() );
183 BOOST_CHECK_EQUAL( reporter.GetMessageCount(), 1 );
184
185 const auto& messages = reporter.GetMessages();
186 BOOST_CHECK_EQUAL( messages[0].first, wxString("Stream message") );
187 BOOST_CHECK_EQUAL( messages[0].second, RPT_SEVERITY_UNDEFINED );
188}
189
190BOOST_AUTO_TEST_CASE( BaseReporter_CharPointerReport )
191{
193
194 reporter.Report( "C-style string", RPT_SEVERITY_ERROR );
195
196 BOOST_CHECK( reporter.HasMessage() );
197 BOOST_CHECK_EQUAL( reporter.GetMessageCount(), 1 );
198 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) );
199}
200
201BOOST_AUTO_TEST_CASE( BaseReporter_Clear )
202{
204
205 reporter.Report( wxT("Message 1"), RPT_SEVERITY_INFO );
206 reporter.Report( wxT("Message 2"), RPT_SEVERITY_WARNING );
207
208 BOOST_CHECK( reporter.HasMessage() );
209 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO ) );
210 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_WARNING ) );
211
212 reporter.Clear();
213
214 BOOST_CHECK( !reporter.HasMessage() );
215 BOOST_CHECK( !reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO ) );
216 BOOST_CHECK( !reporter.HasMessageOfSeverity( RPT_SEVERITY_WARNING ) );
217 BOOST_CHECK_EQUAL( reporter.GetMessageCount(), 0 );
218}
219
220BOOST_AUTO_TEST_CASE( BaseReporter_SeverityMask )
221{
223
224 // Test individual severities
225 reporter.Report( wxT("Info"), RPT_SEVERITY_INFO );
226 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO ) );
227 BOOST_CHECK( !reporter.HasMessageOfSeverity( RPT_SEVERITY_WARNING ) );
228
229 reporter.Report( wxT("Warning"), RPT_SEVERITY_WARNING );
230 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO ) );
231 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_WARNING ) );
232 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO | RPT_SEVERITY_WARNING ) );
233
234 // Test combined mask
235 BOOST_CHECK( !reporter.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) );
236 BOOST_CHECK( !reporter.HasMessageOfSeverity( RPT_SEVERITY_ACTION ) );
237}
238
239BOOST_AUTO_TEST_CASE( BaseReporter_EmptyMessage )
240{
242
243 reporter.Report( wxT(""), RPT_SEVERITY_INFO );
244
245 BOOST_CHECK( reporter.HasMessage() );
246 BOOST_CHECK_EQUAL( reporter.GetMessageCount(), 1 );
247
248 const auto& messages = reporter.GetMessages();
249 BOOST_CHECK_EQUAL( messages[0].first, wxString("") );
250}
251
252BOOST_AUTO_TEST_CASE( BaseReporter_LongMessage )
253{
255 wxString longMessage( 10000, 'A' );
256
257 reporter.Report( longMessage, RPT_SEVERITY_INFO );
258
259 BOOST_CHECK( reporter.HasMessage() );
260 BOOST_CHECK_EQUAL( reporter.GetMessageCount(), 1 );
261
262 const auto& messages = reporter.GetMessages();
263 BOOST_CHECK_EQUAL( messages[0].first, longMessage );
264}
265
266BOOST_AUTO_TEST_CASE( WxStringReporter_BasicFunctionality )
267{
269
270 BOOST_CHECK( !reporter.HasMessage() );
271
272 reporter.Report( wxT("Test message"), RPT_SEVERITY_INFO );
273
274 BOOST_CHECK( reporter.HasMessage() );
275 BOOST_CHECK( !reporter.GetMessages().IsEmpty() );
276 BOOST_CHECK( reporter.GetMessages().Contains( wxT("Test message") ) );
277}
278
279BOOST_AUTO_TEST_CASE( WxStringReporter_MultipleMessages )
280{
282
283 reporter.Report( wxT("Message 1"), RPT_SEVERITY_INFO );
284 reporter.Report( wxT("Message 2"), RPT_SEVERITY_WARNING );
285
286 BOOST_CHECK( reporter.HasMessage() );
287
288 const wxString& messages = reporter.GetMessages();
289 BOOST_CHECK( messages.Contains( wxT("Message 1") ) );
290 BOOST_CHECK( messages.Contains( wxT("Message 2") ) );
291}
292
293BOOST_AUTO_TEST_CASE( WxStringReporter_Clear )
294{
296
297 reporter.Report( wxT("Test message"), RPT_SEVERITY_INFO );
298 BOOST_CHECK( reporter.HasMessage() );
299
300 reporter.Clear();
301
302 BOOST_CHECK( !reporter.HasMessage() );
303 BOOST_CHECK( reporter.GetMessages().IsEmpty() );
304}
305
306BOOST_AUTO_TEST_CASE( WxStringReporter_SeverityMask )
307{
309
310 reporter.Report( wxT("Info"), RPT_SEVERITY_INFO );
311 reporter.Report( wxT("Warning"), RPT_SEVERITY_WARNING );
312
313 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO ) );
314 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_WARNING ) );
315 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO | RPT_SEVERITY_WARNING ) );
316 BOOST_CHECK( !reporter.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) );
317}
318
319BOOST_AUTO_TEST_CASE( WxStringReporter_OperatorChaining )
320{
322
323 reporter.Report( wxT("First"), RPT_SEVERITY_INFO )
324 .Report( wxT("Second"), RPT_SEVERITY_WARNING )
325 << wxT("Third");
326
327 BOOST_CHECK( reporter.HasMessage() );
328 const wxString& messages = reporter.GetMessages();
329 BOOST_CHECK( messages.Contains( wxT("First") ) );
330 BOOST_CHECK( messages.Contains( wxT("Second") ) );
331 BOOST_CHECK( messages.Contains( wxT("Third") ) );
332}
333
334BOOST_AUTO_TEST_CASE( NullReporter_Singleton )
335{
338
339 BOOST_CHECK_EQUAL( &reporter1, &reporter2 );
340}
341
342BOOST_AUTO_TEST_CASE( NullReporter_SeverityMask )
343{
345
346 reporter.Report( wxT("Info"), RPT_SEVERITY_INFO );
347 reporter.Report( wxT("Warning"), RPT_SEVERITY_WARNING );
348
349 // NULL_REPORTER should track severity mask even though it doesn't store messages
350 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_INFO ) );
351 BOOST_CHECK( reporter.HasMessageOfSeverity( RPT_SEVERITY_WARNING ) );
352}
353
354BOOST_AUTO_TEST_CASE( CliReporter_Singleton )
355{
358
359 BOOST_CHECK_EQUAL( &reporter1, &reporter2 );
360}
361
362BOOST_AUTO_TEST_CASE( CliReporter_VerboseDefault )
363{
365
366 // Default should be non-verbose
367 BOOST_CHECK( !reporter.GetVerbose() );
368}
369
370BOOST_AUTO_TEST_CASE( CliReporter_VerboseToggle )
371{
373 bool originalState = reporter.GetVerbose();
374
375 // Toggle verbose on
376 reporter.SetVerbose( true );
377 BOOST_CHECK( reporter.GetVerbose() );
378
379 // Toggle verbose off
380 reporter.SetVerbose( false );
381 BOOST_CHECK( !reporter.GetVerbose() );
382
383 // Restore original state
384 reporter.SetVerbose( originalState );
385}
386
387BOOST_AUTO_TEST_CASE( StdoutReporter_Singleton )
388{
391
392 BOOST_CHECK_EQUAL( &reporter1, &reporter2 );
393}
394
395BOOST_AUTO_TEST_CASE( WxLogReporter_Singleton )
396{
399
400 BOOST_CHECK_EQUAL( &reporter1, &reporter2 );
401}
402
403// Note: WX_TEXT_CTRL_REPORTER and STATUSBAR_REPORTER tests would require
404// actual wxWidgets objects or more sophisticated mocking
405
406BOOST_AUTO_TEST_CASE( ReporterInterface_Polymorphism )
407{
408 std::vector<std::unique_ptr<REPORTER>> reporters;
409 reporters.push_back( std::make_unique<WX_STRING_REPORTER>() );
410 reporters.push_back( std::make_unique<TEST_REPORTER>() );
411
412 for( auto& reporter : reporters )
413 {
414 reporter->Report( wxT( "Polymorphic test" ), RPT_SEVERITY_INFO );
415
416 BOOST_CHECK( reporter->HasMessage() );
417 }
418}
419
420BOOST_AUTO_TEST_CASE( ReporterInterface_ChainedOperations )
421{
423
424 // Test method chaining
425 REPORTER& result = reporter.Report( wxT("First"), RPT_SEVERITY_INFO )
426 .ReportHead( wxT("Head"), RPT_SEVERITY_WARNING )
427 .ReportTail( wxT("Tail"), RPT_SEVERITY_ERROR );
428
429 BOOST_CHECK_EQUAL( &result, &reporter ); // Should return reference to self
430 BOOST_CHECK( reporter.HasMessage() );
431
432 const wxString& messages = reporter.GetMessages();
433 BOOST_CHECK( messages.Contains( wxT("First") ) );
434 BOOST_CHECK( messages.Contains( wxT("Head") ) );
435 BOOST_CHECK( messages.Contains( wxT("Tail") ) );
436}
437
438BOOST_AUTO_TEST_CASE( ReporterInterface_DefaultSeverity )
439{
441
442 // Test default severity parameter
443 reporter.Report( wxT("Default severity") );
444
445 const auto& messages = reporter.GetMessages();
446 BOOST_CHECK_EQUAL( messages[0].second, RPT_SEVERITY_UNDEFINED );
447}
448
449BOOST_AUTO_TEST_CASE( ReporterInterface_UnitsDefault )
450{
452
453 BOOST_CHECK( reporter.GetUnits() == EDA_UNITS::MM );
454}
455
456BOOST_AUTO_TEST_CASE( CliProgressReporter_Singleton )
457{
460
461 BOOST_CHECK_EQUAL( &reporter1, &reporter2 );
462}
463
464BOOST_AUTO_TEST_CASE( CliProgressReporter_VerboseDefault )
465{
467
468 // Default should be non-verbose
469 BOOST_CHECK( !reporter.GetVerbose() );
470}
471
472BOOST_AUTO_TEST_CASE( CliProgressReporter_VerboseToggle )
473{
475 bool originalState = reporter.GetVerbose();
476
477 // Toggle verbose on
478 reporter.SetVerbose( true );
479 BOOST_CHECK( reporter.GetVerbose() );
480
481 // Toggle verbose off
482 reporter.SetVerbose( false );
483 BOOST_CHECK( !reporter.GetVerbose() );
484
485 // Restore original state
486 reporter.SetVerbose( originalState );
487}
488
489BOOST_AUTO_TEST_CASE( SyncReporter_ForwardsEveryEntryPoint )
490{
491 TEST_REPORTER sink;
492 SYNC_REPORTER sync( sink );
493
494 sync.Report( wxT( "body" ), RPT_SEVERITY_INFO );
495 sync.ReportHead( wxT( "head" ), RPT_SEVERITY_WARNING );
496 sync.ReportTail( wxT( "tail" ), RPT_SEVERITY_ERROR );
497
498 // Everything must land on the wrapped reporter, and the queries must reflect its state rather
499 // than the wrapper's unused base mask.
501 BOOST_CHECK( sync.HasMessage() );
502 BOOST_CHECK( sync.HasMessageOfSeverity( RPT_SEVERITY_INFO ) );
503 BOOST_CHECK( sync.HasMessageOfSeverity( RPT_SEVERITY_WARNING ) );
504 BOOST_CHECK( sync.HasMessageOfSeverity( RPT_SEVERITY_ERROR ) );
505
506 sync.Clear();
507
508 BOOST_CHECK( !sink.HasMessage() );
509 BOOST_CHECK( !sync.HasMessage() );
511}
512
513BOOST_AUTO_TEST_CASE( SyncReporter_SerializesConcurrentReports )
514{
515 // The wrapped reporter is not thread-safe (plain std::vector + counter). SYNC_REPORTER must
516 // serialize access so concurrent workers neither lose updates nor corrupt it. Without the
517 // wrapper's lock this races and the final count is wrong (or it crashes under a sanitizer).
518 TEST_REPORTER sink;
519 SYNC_REPORTER sync( sink );
520
521 constexpr int threadCount = 8;
522 constexpr int perThread = 2000;
523
524 std::vector<std::thread> workers;
525 workers.reserve( threadCount );
526
527 for( int t = 0; t < threadCount; ++t )
528 {
529 workers.emplace_back(
530 [&sync]()
531 {
532 for( int i = 0; i < perThread; ++i )
533 sync.Report( wxT( "concurrent" ), RPT_SEVERITY_INFO );
534 } );
535 }
536
537 for( std::thread& worker : workers )
538 worker.join();
539
540 BOOST_CHECK_EQUAL( sink.GetMessageCount(), threadCount * perThread );
541}
542
Reporter forwarding messages to stdout or stderr as appropriate.
static CLI_PROGRESS_REPORTER & GetInstance()
Reporter forwarding messages to stdout or stderr as appropriate.
Definition reporter.h:270
static CLI_REPORTER & GetInstance()
Definition reporter.cpp:216
void SetStatusText(const wxString &text, int field=0)
wxString GetStatusText(int field=0) const
std::vector< wxString > m_fields
bool IsEmpty() const
void AppendText(const wxString &text)
void SetValue(const wxString &text)
wxString GetValue() const
static REPORTER & GetInstance()
Definition reporter.cpp:179
A pure virtual class used to derive REPORTER objects from.
Definition reporter.h:72
virtual void Clear()
Definition reporter.h:152
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition reporter.h:101
REPORTER()
Definition reporter.h:74
static REPORTER & GetInstance()
Definition reporter.cpp:246
A thread-safe REPORTER wrapper that serializes forwarding to an underlying reporter.
Definition reporter.h:171
bool HasMessage() const override
Returns true if any messages were reported.
Definition reporter.cpp:136
REPORTER & ReportHead(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Places the report at the beginning of the list for objects that support ordering.
Definition reporter.cpp:119
bool HasMessageOfSeverity(int aSeverityMask) const override
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition reporter.cpp:144
REPORTER & ReportTail(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Places the report at the end of the list, for objects that support report ordering.
Definition reporter.cpp:110
void Clear() override
Definition reporter.cpp:128
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
Definition reporter.cpp:101
std::vector< std::pair< wxString, SEVERITY > > m_messages
const std::vector< std::pair< wxString, SEVERITY > > & GetMessages() const
void Clear() override
int GetMessageCount() const
bool HasMessage() const override
Returns true if any messages were reported.
REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED) override
Report a string with a given severity.
static REPORTER & GetInstance()
Definition reporter.cpp:279
A wrapper for reporting to a wxString object.
Definition reporter.h:225
SEVERITY
@ RPT_SEVERITY_WARNING
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
@ RPT_SEVERITY_INFO
@ RPT_SEVERITY_ACTION
BOOST_AUTO_TEST_CASE(HorizontalAlignment)
BOOST_AUTO_TEST_SUITE(CadstarPartParser)
BOOST_AUTO_TEST_SUITE_END()
IbisParser parser & reporter
BOOST_AUTO_TEST_CASE(BaseReporter_Constructor)
wxString result
Test unit parsing edge cases and error handling.
BOOST_CHECK_EQUAL(result, "25.4")