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