1 ///////////////////////////////////////////////////////////////////////////////
2 // Name:        tests/log/logtest.cpp
3 // Purpose:     wxLog unit test
4 // Author:      Vadim Zeitlin
5 // Created:     2009-07-07
6 // Copyright:   (c) 2009 Vadim Zeitlin <vadim@wxwidgets.org>
7 ///////////////////////////////////////////////////////////////////////////////
8 
9 // ----------------------------------------------------------------------------
10 // headers
11 // ----------------------------------------------------------------------------
12 
13 #include "testprec.h"
14 
15 #ifdef __BORLANDC__
16     #pragma hdrstop
17 #endif
18 
19 #ifndef WX_PRECOMP
20     #include "wx/log.h"
21     #include "wx/filefn.h"
22 #endif // WX_PRECOMP
23 
24 #include "wx/scopeguard.h"
25 
26 #ifdef __WINDOWS__
27     #include "wx/msw/wrapwin.h"
28 #else
29     #include <errno.h>
30 #endif
31 
32 #if WXWIN_COMPATIBILITY_2_8
33     // we override deprecated DoLog() and DoLogString() in this test, suppress
34     // warnings about it
35     #if wxCHECK_VISUALC_VERSION(7)
36         #pragma warning(disable: 4996)
37     #endif // VC++ 7+
38 #endif // WXWIN_COMPATIBILITY_2_8
39 
40 // all calls to wxLogXXX() functions from this file will use this log component
41 #define wxLOG_COMPONENT "test"
42 
43 // ----------------------------------------------------------------------------
44 // test loggers
45 // ----------------------------------------------------------------------------
46 
47 // base class for all test loggers which simply store all logged messages for
48 // future examination in the test code
49 class TestLogBase : public wxLog
50 {
51 public:
TestLogBase()52     TestLogBase() { }
53 
GetLog(wxLogLevel level) const54     const wxString& GetLog(wxLogLevel level) const
55     {
56         return m_logs[level];
57     }
58 
GetInfo(wxLogLevel level) const59     const wxLogRecordInfo& GetInfo(wxLogLevel level) const
60     {
61         return m_logsInfo[level];
62     }
63 
Clear()64     void Clear()
65     {
66         for ( unsigned n = 0; n < WXSIZEOF(m_logs); n++ )
67         {
68             m_logs[n].clear();
69             m_logsInfo[n] = wxLogRecordInfo();
70         }
71     }
72 
73 protected:
74     wxString m_logs[wxLOG_Trace + 1];
75     wxLogRecordInfo m_logsInfo[wxLOG_Trace + 1];
76 
77     wxDECLARE_NO_COPY_CLASS(TestLogBase);
78 };
79 
80 // simple log sink which just stores the messages logged for each level
81 class TestLog : public TestLogBase
82 {
83 public:
TestLog()84     TestLog() { }
85 
86 protected:
DoLogRecord(wxLogLevel level,const wxString & msg,const wxLogRecordInfo & info)87     virtual void DoLogRecord(wxLogLevel level,
88                              const wxString& msg,
89                              const wxLogRecordInfo& info)
90     {
91         m_logs[level] = msg;
92         m_logsInfo[level] = info;
93     }
94 
95 private:
96     wxDECLARE_NO_COPY_CLASS(TestLog);
97 };
98 
99 #if WXWIN_COMPATIBILITY_2_8
100 
101 // log sink overriding the old DoLogXXX() functions should still work too
102 
103 // this one overrides DoLog(char*)
104 class CompatTestLog : public TestLogBase
105 {
106 public:
CompatTestLog()107     CompatTestLog() { }
108 
109 protected:
DoLog(wxLogLevel level,const char * str,time_t WXUNUSED (t))110     virtual void DoLog(wxLogLevel level, const char *str, time_t WXUNUSED(t))
111     {
112         m_logs[level] = str;
113     }
114 
115     // get rid of the warning about hiding the other overload
DoLog(wxLogLevel WXUNUSED (level),const wchar_t * WXUNUSED (str),time_t WXUNUSED (t))116     virtual void DoLog(wxLogLevel WXUNUSED(level),
117                        const wchar_t *WXUNUSED(str),
118                        time_t WXUNUSED(t))
119     {
120     }
121 
122 private:
123     wxDECLARE_NO_COPY_CLASS(CompatTestLog);
124 };
125 
126 // and this one overload DoLogString(wchar_t*)
127 class CompatTestLog2 : public wxLog
128 {
129 public:
CompatTestLog2()130     CompatTestLog2() { }
131 
Get() const132     const wxString& Get() const { return m_msg; }
133 
134 protected:
DoLogString(const wchar_t * msg,time_t WXUNUSED (t))135     virtual void DoLogString(const wchar_t *msg, time_t WXUNUSED(t))
136     {
137         m_msg = msg;
138     }
139 
140     // get rid of the warning
DoLogString(const char * WXUNUSED (msg),time_t WXUNUSED (t))141     virtual void DoLogString(const char *WXUNUSED(msg), time_t WXUNUSED(t))
142     {
143     }
144 
145 private:
146     wxString m_msg;
147 
148     wxDECLARE_NO_COPY_CLASS(CompatTestLog2);
149 };
150 
151 #endif // WXWIN_COMPATIBILITY_2_8
152 
153 // ----------------------------------------------------------------------------
154 // test class
155 // ----------------------------------------------------------------------------
156 
157 class LogTestCase : public CppUnit::TestCase
158 {
159 public:
LogTestCase()160     LogTestCase() { }
161 
162     virtual void setUp();
163     virtual void tearDown();
164 
165 private:
166     CPPUNIT_TEST_SUITE( LogTestCase );
167         CPPUNIT_TEST( Functions );
168         CPPUNIT_TEST( Null );
169         CPPUNIT_TEST( Component );
170 #if wxDEBUG_LEVEL
171         CPPUNIT_TEST( Trace );
172 #endif // wxDEBUG_LEVEL
173 #if WXWIN_COMPATIBILITY_2_8
174         CPPUNIT_TEST( CompatLogger );
175         CPPUNIT_TEST( CompatLogger2 );
176 #endif // WXWIN_COMPATIBILITY_2_8
177         CPPUNIT_TEST( SysError );
178         CPPUNIT_TEST( NoWarnings );
179     CPPUNIT_TEST_SUITE_END();
180 
181     void Functions();
182     void Null();
183     void Component();
184 #if wxDEBUG_LEVEL
185     void Trace();
186 #endif // wxDEBUG_LEVEL
187 #if WXWIN_COMPATIBILITY_2_8
188     void CompatLogger();
189     void CompatLogger2();
190 #endif // WXWIN_COMPATIBILITY_2_8
191     void SysError();
192     void NoWarnings();
193 
194     TestLog *m_log;
195     wxLog *m_logOld;
196     bool m_logWasEnabled;
197 
198     wxDECLARE_NO_COPY_CLASS(LogTestCase);
199 };
200 
201 // register in the unnamed registry so that these tests are run by default
202 CPPUNIT_TEST_SUITE_REGISTRATION( LogTestCase );
203 
204 // also include in its own registry so that these tests can be run alone
205 CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( LogTestCase, "LogTestCase" );
206 
setUp()207 void LogTestCase::setUp()
208 {
209     m_logOld = wxLog::SetActiveTarget(m_log = new TestLog);
210     m_logWasEnabled = wxLog::EnableLogging();
211 }
212 
tearDown()213 void LogTestCase::tearDown()
214 {
215     delete wxLog::SetActiveTarget(m_logOld);
216     wxLog::EnableLogging(m_logWasEnabled);
217 }
218 
Functions()219 void LogTestCase::Functions()
220 {
221     wxLogMessage("Message");
222     CPPUNIT_ASSERT_EQUAL( "Message", m_log->GetLog(wxLOG_Message) );
223 
224     wxLogError("Error %d", 17);
225     CPPUNIT_ASSERT_EQUAL( "Error 17", m_log->GetLog(wxLOG_Error) );
226 
227     wxLogDebug("Debug");
228 #if wxDEBUG_LEVEL
229     CPPUNIT_ASSERT_EQUAL( "Debug", m_log->GetLog(wxLOG_Debug) );
230 #else
231     CPPUNIT_ASSERT_EQUAL( "", m_log->GetLog(wxLOG_Debug) );
232 #endif
233 }
234 
Null()235 void LogTestCase::Null()
236 {
237     {
238         wxLogNull noLog;
239         wxLogWarning("%s warning", "Not important");
240 
241         CPPUNIT_ASSERT_EQUAL( "", m_log->GetLog(wxLOG_Warning) );
242     }
243 
244     wxLogWarning("%s warning", "Important");
245     CPPUNIT_ASSERT_EQUAL( "Important warning", m_log->GetLog(wxLOG_Warning) );
246 }
247 
Component()248 void LogTestCase::Component()
249 {
250     wxLogMessage("Message");
251     CPPUNIT_ASSERT_EQUAL( wxLOG_COMPONENT,
252                           m_log->GetInfo(wxLOG_Message).component );
253 
254     // completely disable logging for this component
255     wxLog::SetComponentLevel("test/ignore", wxLOG_FatalError);
256 
257     // but enable it for one of its subcomponents
258     wxLog::SetComponentLevel("test/ignore/not", wxLOG_Max);
259 
260     #undef wxLOG_COMPONENT
261     #define wxLOG_COMPONENT "test/ignore"
262 
263     // this shouldn't be output as this component is ignored
264     wxLogError("Error");
265     CPPUNIT_ASSERT_EQUAL( "", m_log->GetLog(wxLOG_Error) );
266 
267     // and so are its subcomponents
268     #undef wxLOG_COMPONENT
269     #define wxLOG_COMPONENT "test/ignore/sub/subsub"
270     wxLogError("Error");
271     CPPUNIT_ASSERT_EQUAL( "", m_log->GetLog(wxLOG_Error) );
272 
273     // but one subcomponent is not
274     #undef wxLOG_COMPONENT
275     #define wxLOG_COMPONENT "test/ignore/not"
276     wxLogError("Error");
277     CPPUNIT_ASSERT_EQUAL( "Error", m_log->GetLog(wxLOG_Error) );
278 
279     // restore the original value
280     #undef wxLOG_COMPONENT
281     #define wxLOG_COMPONENT "test"
282 }
283 
284 #if wxDEBUG_LEVEL
285 
286 namespace
287 {
288 
289 const char *TEST_MASK = "test";
290 
291 // this is a test vararg function (a real one, not a variadic-template-like as
292 // wxVLogTrace(), so care should be taken with its arguments)
TraceTest(const char * format,...)293 void TraceTest(const char *format, ...)
294 {
295     va_list argptr;
296     va_start(argptr, format);
297     wxVLogTrace(TEST_MASK, format, argptr);
298     va_end(argptr);
299 }
300 
301 } // anonymous namespace
302 
Trace()303 void LogTestCase::Trace()
304 {
305     // we use wxLogTrace() or wxVLogTrace() from inside TraceTest()
306     // interchangeably here, it shouldn't make any difference
307 
308     wxLogTrace(TEST_MASK, "Not shown");
309     CPPUNIT_ASSERT_EQUAL( "", m_log->GetLog(wxLOG_Trace) );
310 
311     wxLog::AddTraceMask(TEST_MASK);
312     TraceTest("Shown");
313     CPPUNIT_ASSERT_EQUAL( wxString::Format("(%s) Shown", TEST_MASK),
314                           m_log->GetLog(wxLOG_Trace) );
315 
316     wxLog::RemoveTraceMask(TEST_MASK);
317     m_log->Clear();
318 
319     TraceTest("Not shown again");
320     CPPUNIT_ASSERT_EQUAL( "", m_log->GetLog(wxLOG_Trace) );
321 }
322 
323 #endif // wxDEBUG_LEVEL
324 
325 #if WXWIN_COMPATIBILITY_2_8
326 
CompatLogger()327 void LogTestCase::CompatLogger()
328 {
329     CompatTestLog log;
330     wxLog * const logOld = wxLog::SetActiveTarget(&log);
331     wxON_BLOCK_EXIT1( wxLog::SetActiveTarget, logOld );
332 
333     wxLogError("Old error");
334     CPPUNIT_ASSERT_EQUAL( "Old error", log.GetLog(wxLOG_Error) );
335 }
336 
CompatLogger2()337 void LogTestCase::CompatLogger2()
338 {
339     CompatTestLog2 log;
340     wxLog * const logOld = wxLog::SetActiveTarget(&log);
341     wxON_BLOCK_EXIT1( wxLog::SetActiveTarget, logOld );
342 
343     wxLogWarning("Old warning");
344     CPPUNIT_ASSERT_EQUAL( "Old warning", log.Get() );
345 }
346 
347 #endif // WXWIN_COMPATIBILITY_2_8
348 
SysError()349 void LogTestCase::SysError()
350 {
351     wxString s;
352 
353     wxLogSysError(17, "Error");
354     CPPUNIT_ASSERT( m_log->GetLog(wxLOG_Error).StartsWith("Error (", &s) );
355     WX_ASSERT_MESSAGE( ("Error message is \"(%s\"", s), s.StartsWith("error 17") );
356 
357     // Try to ensure that the system error is 0.
358 #ifdef __WINDOWS__
359     ::SetLastError(0);
360 #else
361     errno = 0;
362 #endif
363 
364     wxLogSysError("Success");
365     CPPUNIT_ASSERT( m_log->GetLog(wxLOG_Error).StartsWith("Success (", &s) );
366     WX_ASSERT_MESSAGE( ("Error message is \"(%s\"", s), s.StartsWith("error 0") );
367 
368     wxOpen("no-such-file", 0, 0);
369     wxLogSysError("Not found");
370     CPPUNIT_ASSERT( m_log->GetLog(wxLOG_Error).StartsWith("Not found (", &s) );
371     WX_ASSERT_MESSAGE( ("Error message is \"(%s\"", s), s.StartsWith("error 2") );
372 }
373 
NoWarnings()374 void LogTestCase::NoWarnings()
375 {
376     // Check that "else" branch is [not] taken as expected and that this code
377     // compiles without warnings (which used to not be the case).
378 
379     bool b = wxFalse;
380     if ( b )
381         wxLogError("Not logged");
382     else
383         b = !b;
384 
385     CPPUNIT_ASSERT( b );
386 
387     if ( b )
388         wxLogError("If");
389     else
390         CPPUNIT_FAIL("Should not be taken");
391 
392     CPPUNIT_ASSERT_EQUAL( "If", m_log->GetLog(wxLOG_Error) );
393 }
394