1 // Copyright (c) 2006, International Business Machines
2 // Corporation and others.  All Rights Reserved.
3 // This code is licensed under the terms of the Eclipse Public License (EPL).
4 
5 /*
6   A brief test of CoinMessageHandler. Tests that we can print basic messages,
7   and checks that we can handle messages with parts. Does a few more subtle
8   tests involving cloning and handlers with no associated messages.
9 
10   Does not attempt to test any of the various enbryonic features.
11 */
12 
13 #include "CoinPragma.hpp"
14 #include "CoinMessageHandler.hpp"
15 #include <cstring>
16 
17 namespace { // begin file-local namespace
18 
19 /*
20   Define a set of test messages.
21 */
22 enum COIN_TestMessage
23 { COIN_TST_NOFIELDS,
24   COIN_TST_INT,
25   COIN_TST_DBL,
26   COIN_TST_DBLFMT,
27   COIN_TST_CHAR,
28   COIN_TST_STRING,
29   COIN_TST_MULTIPART,
30   COIN_TST_NOCODES,
31   COIN_TST_END
32 } ;
33 
34 /*
35   Convenient structure for doing static initialisation. Essentially, we want
36   something that'll allow us to set up an array of structures with the
37   information required by CoinOneMessage. Then we can easily walk the array,
38   create a CoinOneMessage structure for each message, and add each message to
39   a CoinMessages structure.
40 */
41 typedef struct
42 { COIN_TestMessage internalNumber;
43   int externalNumber;
44   char detail;
45   const char * message;
46 } MsgDefn ;
47 
48 MsgDefn us_tstmsgs[] =
49 { {COIN_TST_NOFIELDS,1,1,"This message has no parts and no fields."},
50   {COIN_TST_INT,3,1,"This message has an integer field: (%d)"},
51   {COIN_TST_DBL,4,1,"This message has a double field: (%g)"},
52   {COIN_TST_DBLFMT,4,1,
53       "This message has an explicit precision .3: (%.3g)"},
54   {COIN_TST_CHAR,5,1,"This message has a char field: (%c)"},
55   {COIN_TST_STRING,6,1,"This message has a string field: (%s)"},
56   {COIN_TST_MULTIPART,7,1,
57     "Prefix%? Part 1%? Part 2 with integer in hex %#.4x%? Part 3%? suffix."},
58   {COIN_TST_NOCODES,8,1,""},
59   {COIN_TST_END,7,1,"This is the dummy end marker."}
60 } ;
61 
62 
63 /*
64   Tests that don't use formatted messages.
65 */
testsWithoutMessages(int & errs)66 void testsWithoutMessages (int &errs)
67 
68 { CoinMessageHandler hdl ;
69 /*
70   format_ must be null in order for on-the-fly message construction to work
71   properly.
72 */
73   hdl.message()
74     << "This should print if the constructor sets format_ to null."
75     << CoinMessageEol ;
76 /*
77   Log level should have no effect by default, so set it to 0 to prove the
78   point.
79 */
80   hdl.message()
81     << "By default, the log level has no effect for on-the-fly messages."
82     << CoinMessageEol ;
83   hdl.setLogLevel(0) ;
84   if (hdl.logLevel() != 0)
85   { std::cout
86       << "Cannot set/get log level of 0!" << std::endl ;
87     errs++ ; }
88   hdl.message()
89     << "Log level is now" << hdl.logLevel() << "." << CoinMessageEol ;
90 /*
91   But we can specify a log level and it will be effective. What's more, each
92   message is completely independent, so the default behaviour should return
93   after an explicit log level suppressed printing.
94 */
95   hdl.message()
96     << "But we can specify a log level and have it take effect."
97     << CoinMessageEol ;
98   hdl.message(1)
99     << "This message should not print!" << CoinMessageEol ;
100   hdl.message()
101     << "If you saw a message that said 'should not print', there's a problem."
102     << CoinMessageEol ;
103 /*
104   This next sequence exposed a subtle bug in cloning. Failure here may well
105   cause a core dump. Here's the scenario: Since we haven't used any messages,
106   rhs.format_ is null and rhs.currentMessage_ is set to the default for
107   CoinOneMessage, which sets the format string to the null string "\0".
108   Cloning assumed that rhs.format_ was a pointer into the format string from
109   currentMessage_, and proceeded to set up format_ in the clone to be a
110   pointer into the cloned currentMessage_, allowing for the fact that we
111   might be somewhere in the middle of the message at the time of cloning.
112   Net result was that format_ was non-null in the clone, but god only knows
113   where it pointed to. When the code tried to write to *format_, the result
114   was a core dump.
115 */
116   hdl.message()
117     << "A core dump here indicates a cloning failure." << CoinMessageEol ;
118   CoinMessageHandler hdlClone(hdl) ;
119   hdlClone.message()
120     << "This should print if cloning sets format_ to null."
121     << CoinMessageEol ;
122 
123   return ; }
124 
125 
126 /*
127   Basic functionality for printing messages. Check that supported parameter
128   types work, and that we can selectively suppress portions of a message.
129 */
basicTestsWithMessages(const CoinMessages & testMessages,int & errs)130 void basicTestsWithMessages (const CoinMessages &testMessages, int &errs)
131 {
132   CoinMessageHandler hdl ;
133   hdl.setLogLevel(1) ;
134   if (hdl.logLevel() != 1)
135   { std::cout
136       << "Cannot set/get log level of 1!" << std::endl ;
137     errs++ ; }
138 /*
139   Simple tests of one piece messages.
140 */
141   hdl.message(COIN_TST_NOFIELDS,testMessages) << CoinMessageEol ;
142   hdl.message(COIN_TST_INT,testMessages) << 42 << CoinMessageEol ;
143   hdl.message(COIN_TST_DBL,testMessages) << (42.42+1.0/7.0) << CoinMessageEol ;
144   std::cout << "Changing to 10 significant digits." << std::endl ;
145   int savePrecision = hdl.precision() ;
146   hdl.setPrecision(10) ;
147   hdl.message(COIN_TST_DBL,testMessages) << (42.42+1.0/7.0) << CoinMessageEol ;
148   std::cout
149     << "And back to " << savePrecision
150     << " significant digits." << std::endl ;
151   hdl.setPrecision(savePrecision) ;
152   hdl.message(COIN_TST_DBL,testMessages) << (42.42+1.0/7.0) << CoinMessageEol ;
153   hdl.message(COIN_TST_DBLFMT,testMessages)
154        << (42.42+1.0/7.0) << CoinMessageEol ;
155 
156   hdl.message(COIN_TST_CHAR,testMessages) << 'w' << CoinMessageEol ;
157   hdl.message(COIN_TST_STRING,testMessages) << "forty-two" << CoinMessageEol ;
158 /*
159   A multipart message, consisting of prefix, three optional parts, and a
160   suffix. Note that we need four calls to printing() in order to process the
161   four `%?' codes.
162 */
163   hdl.message(COIN_TST_MULTIPART,testMessages) ;
164   hdl.printing(true) ;
165   hdl.printing(true) << 42 ;
166   hdl.printing(true) ;
167   hdl.printing(true) << CoinMessageEol ;
168   hdl.message(COIN_TST_MULTIPART,testMessages) ;
169   hdl.printing(false) ;
170   hdl.printing(false) << 42 ;
171   hdl.printing(false) ;
172   hdl.printing(true) << CoinMessageEol ;
173   hdl.message(COIN_TST_MULTIPART,testMessages) ;
174   hdl.printing(true) ;
175   hdl.printing(false) << 42 ;
176   hdl.printing(false) ;
177   hdl.printing(true) << CoinMessageEol ;
178   hdl.message(COIN_TST_MULTIPART,testMessages) ;
179   hdl.printing(false) ;
180   hdl.printing(true) << 42 ;
181   hdl.printing(false) ;
182   hdl.printing(true) << CoinMessageEol ;
183   hdl.message(COIN_TST_MULTIPART,testMessages) ;
184   hdl.printing(false) ;
185   hdl.printing(false) << 42 ;
186   hdl.printing(true) ;
187   hdl.printing(true) << CoinMessageEol ;
188 /*
189   Construct a message from scratch given an empty message. Parameters are
190   printed with default format codes.
191 */
192   hdl.message(COIN_TST_NOCODES,testMessages) ;
193   hdl.message() << "Standardised prefix, free form remainder:" ;
194   hdl.message() << "An int" << 42
195    << "A double" << 4.2
196    << "a new line" << CoinMessageNewline
197    << "and done." << CoinMessageEol ;
198 /*
199   Construct a message from scratch given nothing at all. hdl.finish is
200   equivalent to CoinMessageEol (more accurately, processing CoinMessagEol
201   consists of a call to finish).
202 */
203   hdl.message() << "No standardised prefix, free form reminder: integer ("
204     << 42 << ")." ;
205   hdl.finish() ;
206 /*
207   And the transition mechanism, where we just dump the string we're given.
208   It's not possible to augment this message, as printStatus_ is set to 2,
209   which prevents the various operator<< methods from contributing to the
210   output buffer, with the exception of operator<<(CoinMessageMarker).
211 */
212   hdl.message(27,"Tran","Transition message.",'I') << CoinMessageEol ;
213 
214   return ; }
215 
216 
217 /*
218   More difficult tests. Clone a handler in mid-message. Why? Because we can.
219 */
advTestsWithMessages(const CoinMessages & testMessages,int & errs)220 void advTestsWithMessages (const CoinMessages &testMessages, int &errs)
221 {
222   CoinMessageHandler hdl ;
223 /*
224   A multipart message, consisting of prefix, three optional parts, and a
225   suffix. Note that we need four calls to printing() in order to process the
226   four `%?' codes.
227 */
228   hdl.message() << "Trying a clone in mid-message." << CoinMessageEol ;
229 
230   hdl.message(COIN_TST_MULTIPART,testMessages) ;
231   hdl.printing(true) ;
232   hdl.printing(true) ;
233   CoinMessageHandler hdl2 ;
234   hdl2 = hdl ;
235   hdl2.message() << 42 ;
236   hdl2.printing(true) ;
237   hdl2.printing(true) << CoinMessageEol ;
238 
239   hdl.message() << 0x42 ;
240   hdl.printing(false) ;
241   hdl.printing(false) << CoinMessageEol ;
242 
243   hdl.message()
244     << "The second copy should be missing Part 3 and suffix."
245     << CoinMessageEol ;
246 
247   return ; }
248 
249 } // end file-local namespace
250 
CoinMessageHandlerUnitTest()251 bool CoinMessageHandlerUnitTest ()
252 
253 { int errs = 0 ;
254 
255 /*
256   Create a CoinMessages object to hold our messages.
257 */
258   CoinMessages testMessages(sizeof(us_tstmsgs)/sizeof(MsgDefn)) ;
259   strcpy(testMessages.source_,"Test") ;
260 /*
261   Load with messages. This involves creating successive messages
262   (CoinOneMessage) and loading them into the array. This is the usual copy
263   operation; client is responsible for disposing of the original message
264   (accomplished here by keeping the CoinOneMessage internal to the loop
265   body).
266 */
267   MsgDefn *msgDef = us_tstmsgs ;
268   while (msgDef->internalNumber != COIN_TST_END)
269   { CoinOneMessage msg(msgDef->externalNumber,msgDef->detail,msgDef->message) ;
270     testMessages.addMessage(msgDef->internalNumber,msg) ;
271     msgDef++ ; }
272 /*
273   Run some tests on a message handler without messages.
274 */
275   testsWithoutMessages(errs) ;
276 /*
277   Basic tests with messages.
278 */
279   basicTestsWithMessages(testMessages,errs) ;
280 /*
281   Advanced tests with messages.
282 */
283   advTestsWithMessages(testMessages,errs) ;
284 /*
285   Did we make it without error?
286 */
287   if (errs)
288   { std::cout
289       << "ERROR! CoinMessageHandlerTest reports "
290       << errs << " errors." << std::endl ; }
291   else
292   { std::cout
293       << "CoinMessageHandlerTest completed without error." << std::endl ; }
294 
295   return (errs == 0) ; }
296