1 | // Copyright (C) 2000, International Business Machines |
---|
2 | // Corporation and others. All Rights Reserved. |
---|
3 | |
---|
4 | #include "CoinPragma.hpp" |
---|
5 | |
---|
6 | #include "OsiConfig.h" |
---|
7 | |
---|
8 | #ifdef NDEBUG |
---|
9 | #undef NDEBUG |
---|
10 | #endif |
---|
11 | |
---|
12 | #include <cassert> |
---|
13 | #include <cstdio> |
---|
14 | #include <iostream> |
---|
15 | #include <map> |
---|
16 | |
---|
17 | #include "OsiUnitTests.hpp" |
---|
18 | |
---|
19 | #include "CoinError.hpp" |
---|
20 | |
---|
21 | #include "OsiCbcSolverInterface.hpp" |
---|
22 | |
---|
23 | namespace { |
---|
24 | |
---|
25 | // Display message on stdout and stderr. Flush cout buffer before printing the |
---|
26 | // message, so that output comes out in order in spite of buffered cout. |
---|
27 | |
---|
28 | void testingMessage( const char * const msg ) |
---|
29 | { |
---|
30 | std::cout.flush() ; |
---|
31 | std::cerr <<msg; |
---|
32 | //cout <<endl <<"*****************************************" |
---|
33 | // <<endl <<msg <<endl; |
---|
34 | } |
---|
35 | |
---|
36 | |
---|
37 | /* |
---|
38 | Utility routine to process command line parameters. An unrecognised parameter |
---|
39 | will trigger the help message and a return value of false. |
---|
40 | |
---|
41 | This should be replaced with the one of the standard CoinUtils parameter |
---|
42 | mechanisms. |
---|
43 | */ |
---|
44 | bool processParameters (int argc, const char **argv, |
---|
45 | std::map<std::string,std::string> &parms) |
---|
46 | |
---|
47 | { |
---|
48 | assert(argc >= 1); |
---|
49 | assert(argv != NULL); |
---|
50 | /* |
---|
51 | Initialise the parameter keywords. |
---|
52 | */ |
---|
53 | std::set<std::string> definedKeyWords; |
---|
54 | definedKeyWords.insert("-cerr2cout"); |
---|
55 | definedKeyWords.insert("-mpsDir"); |
---|
56 | definedKeyWords.insert("-netlibDir"); |
---|
57 | definedKeyWords.insert("-testOsiSolverInterface"); |
---|
58 | definedKeyWords.insert("-nobuf"); |
---|
59 | /* |
---|
60 | Set default values for data directories. |
---|
61 | */ |
---|
62 | const char dirsep = CoinFindDirSeparator() ; |
---|
63 | std::string pathTmp ; |
---|
64 | |
---|
65 | pathTmp = ".." ; |
---|
66 | pathTmp += dirsep ; |
---|
67 | pathTmp += ".." ; |
---|
68 | pathTmp += dirsep ; |
---|
69 | pathTmp += "Data" ; |
---|
70 | pathTmp += dirsep ; |
---|
71 | |
---|
72 | parms["-mpsDir"] = pathTmp + "Sample" ; |
---|
73 | parms["-netlibDir"] = pathTmp + "Netlib" ; |
---|
74 | |
---|
75 | /* |
---|
76 | Read the command line parameters and fill a map of parameter keys and |
---|
77 | associated data. The parser allows for parameters which are only a keyword, |
---|
78 | or parameters of the form keyword=value (no spaces). |
---|
79 | */ |
---|
80 | for (int i = 1 ; i < argc ; i++) |
---|
81 | { std::string parm(argv[i]) ; |
---|
82 | std::string key,value ; |
---|
83 | std::string::size_type eqPos = parm.find('='); |
---|
84 | |
---|
85 | if (eqPos == std::string::npos) |
---|
86 | { key = parm ; } |
---|
87 | else |
---|
88 | { key = parm.substr(0,eqPos) ; |
---|
89 | value = parm.substr(eqPos+1) ; } |
---|
90 | /* |
---|
91 | Is the specifed key valid? |
---|
92 | */ |
---|
93 | if (definedKeyWords.find(key) == definedKeyWords.end()) |
---|
94 | { std::cerr << "Undefined parameter \"" << key << "\"." << std::endl ; |
---|
95 | std::cerr |
---|
96 | << "Usage: " << argv[0] |
---|
97 | << " [-nobuf] [-mpsDir=V1] [-netlibDir=V2] " |
---|
98 | << "[-testOsiSolverInterface] " << std::endl ; |
---|
99 | std::cerr << " where:" << std::endl ; |
---|
100 | std::cerr |
---|
101 | << " " |
---|
102 | << "-cerr2cout: redirect cerr to cout; sometimes useful." << std::endl |
---|
103 | << "\t" << "to synchronise cout & cerr." << std::endl ; |
---|
104 | std::cerr |
---|
105 | << " " |
---|
106 | << "-mpsDir: directory containing mps test files." << std::endl |
---|
107 | << "\t" << "Default value V1=\"../../Data/Sample\"" << std::endl ; |
---|
108 | std::cerr |
---|
109 | << " " |
---|
110 | << "-netlibDir: directory containing netlib files." << std::endl |
---|
111 | << "\t" << "Default value V2=\"../../Data/Netlib\"" << std::endl ; |
---|
112 | std::cerr |
---|
113 | << " " |
---|
114 | << "-testOsiSolverInterface: " |
---|
115 | << "run each OSI on the netlib problem set." << std::endl |
---|
116 | << "\t" |
---|
117 | << "Default is to not run the netlib problem set." << std::endl ; |
---|
118 | std::cerr |
---|
119 | << " " |
---|
120 | << "-nobuf: use unbuffered output." << std::endl |
---|
121 | << "\t" << "Default is buffered output." << std::endl ; |
---|
122 | |
---|
123 | return (false) ; } |
---|
124 | /* |
---|
125 | Valid keyword; stash the value for later reference. |
---|
126 | */ |
---|
127 | parms[key]=value ; } |
---|
128 | /* |
---|
129 | Tack the directory separator onto the data directories so we don't have to |
---|
130 | worry about it later. |
---|
131 | */ |
---|
132 | parms["-mpsDir"] += dirsep ; |
---|
133 | parms["-netlibDir"] += dirsep ; |
---|
134 | /* |
---|
135 | Did the user request unbuffered i/o? It seems we need to go after this |
---|
136 | through stdio --- using pubsetbuf(0,0) on the C++ streams has no |
---|
137 | discernible affect. Nor, for that matter, did setting the unitbuf flag on |
---|
138 | the streams. Why? At a guess, sync_with_stdio connects the streams to the |
---|
139 | stdio buffers, and the C++ side isn't programmed to change them? |
---|
140 | */ |
---|
141 | if (parms.find("-nobuf") != parms.end()) |
---|
142 | { // std::streambuf *coutBuf, *cerrBuf ; |
---|
143 | // coutBuf = std::cout.rdbuf() ; |
---|
144 | // coutBuf->pubsetbuf(0,0) ; |
---|
145 | // cerrBuf = std::cerr.rdbuf() ; |
---|
146 | // cerrBuf->pubsetbuf(0,0) ; |
---|
147 | setbuf(stderr,0) ; |
---|
148 | setbuf(stdout,0) ; } |
---|
149 | /* |
---|
150 | Did the user request a redirect for cerr? This must occur before any i/o is |
---|
151 | performed. |
---|
152 | */ |
---|
153 | if (parms.find("-cerr2cout") != parms.end()) |
---|
154 | { std::cerr.rdbuf(std::cout.rdbuf()) ; } |
---|
155 | |
---|
156 | return (true) ; } |
---|
157 | |
---|
158 | |
---|
159 | } // end file-local namespace |
---|
160 | |
---|
161 | |
---|
162 | |
---|
163 | //---------------------------------------------------------------- |
---|
164 | // unitTest [-nobuf] [-mpsDir=V1] [-netlibDir=V2] [-testOsiSolverInterface] |
---|
165 | // |
---|
166 | // where: |
---|
167 | // -nobuf: remove buffering on cout (stdout); useful to keep cout and cerr |
---|
168 | // messages synchronised when redirecting output to a file or pipe. |
---|
169 | // -mpsDir: directory containing mps test files |
---|
170 | // Default value V1="../../Data/Sample" |
---|
171 | // -netlibDir: directory containing netlib files |
---|
172 | // Default value V2="../../Data/Netlib" |
---|
173 | // -testOsiSolverInterface |
---|
174 | // If specified, then OsiSolveInterface::unitTest |
---|
175 | // is skipped over and not run. |
---|
176 | // |
---|
177 | // All parameters are optional. |
---|
178 | //---------------------------------------------------------------- |
---|
179 | |
---|
180 | int main (int argc, const char *argv[]) |
---|
181 | |
---|
182 | { int totalErrCnt = 0; |
---|
183 | |
---|
184 | /* |
---|
185 | Start off with various bits of initialisation that don't really belong |
---|
186 | anywhere else. |
---|
187 | |
---|
188 | First off, synchronise C++ stream i/o with C stdio. This makes debugging |
---|
189 | output a bit more comprehensible. It still suffers from interleave of cout |
---|
190 | (stdout) and cerr (stderr), but -nobuf deals with that. |
---|
191 | */ |
---|
192 | std::ios::sync_with_stdio() ; |
---|
193 | /* |
---|
194 | Suppress an popup window that Windows shows in response to a crash. See |
---|
195 | note at head of file. |
---|
196 | */ |
---|
197 | WindowsErrorPopupBlocker(); |
---|
198 | |
---|
199 | /* |
---|
200 | Process command line parameters. |
---|
201 | */ |
---|
202 | std::map<std::string,std::string> parms ; |
---|
203 | |
---|
204 | if (processParameters(argc,argv,parms) == false) |
---|
205 | { return (1) ; } |
---|
206 | |
---|
207 | std::string mpsDir = parms["-mpsDir"] ; |
---|
208 | std::string netlibDir = parms["-netlibDir"] ; |
---|
209 | |
---|
210 | try { |
---|
211 | /* |
---|
212 | Test Osi{Row,Col}Cut routines. |
---|
213 | */ |
---|
214 | { |
---|
215 | OsiCbcSolverInterface cbcSi; |
---|
216 | testingMessage( "Testing OsiRowCut with OsiCbcSolverInterface\n" ); |
---|
217 | OsiRowCutUnitTest(&cbcSi,mpsDir); |
---|
218 | } |
---|
219 | { |
---|
220 | OsiCbcSolverInterface cbcSi; |
---|
221 | testingMessage( "Testing OsiColCut with OsiCbcSolverInterface\n" ); |
---|
222 | OsiColCutUnitTest(&cbcSi,mpsDir); |
---|
223 | } |
---|
224 | { |
---|
225 | OsiCbcSolverInterface cbcSi; |
---|
226 | testingMessage( "Testing OsiRowCutDebugger with OsiCbcSolverInterface\n" ); |
---|
227 | OsiRowCutDebuggerUnitTest(&cbcSi,mpsDir); |
---|
228 | } |
---|
229 | |
---|
230 | /* |
---|
231 | Run the OsiXXX class test. It's up to the OsiCbc implementor |
---|
232 | to decide whether or not to run OsiSolverInterfaceCommonUnitTest. Arguably |
---|
233 | this should be required. |
---|
234 | */ |
---|
235 | testingMessage( "Testing OsiCbcSolverInterface\n" ); |
---|
236 | OsiCbcSolverInterfaceUnitTest(mpsDir,netlibDir); |
---|
237 | |
---|
238 | /* |
---|
239 | We have run the specialised unit test. Check now to see if we need to |
---|
240 | run through the Netlib problems. |
---|
241 | */ |
---|
242 | if (parms.find("-testOsiSolverInterface") != parms.end()) |
---|
243 | { |
---|
244 | // Create vector of solver interfaces |
---|
245 | std::vector<OsiSolverInterface*> vecSi(1, new OsiCbcSolverInterface); |
---|
246 | |
---|
247 | testingMessage( "Testing OsiSolverInterface on Netlib problems.\n" ); |
---|
248 | OsiSolverInterfaceMpsUnitTest(vecSi,netlibDir); |
---|
249 | |
---|
250 | delete vecSi[0]; |
---|
251 | } |
---|
252 | else { |
---|
253 | testingMessage( "***Skipped Testing of OsiCbcSolverInterface on Netlib problems***\n" ); |
---|
254 | testingMessage( "***use -testOsiSolverInterface to run them.***\n" ); |
---|
255 | } |
---|
256 | } catch (CoinError& error) { |
---|
257 | std::cout.flush(); |
---|
258 | std::cerr << "Caught CoinError exception: "; |
---|
259 | error.print(true); |
---|
260 | totalErrCnt += 1; |
---|
261 | } |
---|
262 | /* |
---|
263 | We're done. Report on the results. |
---|
264 | */ |
---|
265 | if (totalErrCnt) |
---|
266 | { std::cout.flush() ; |
---|
267 | std::cerr |
---|
268 | << "Tests completed with " << totalErrCnt << " errors." << std::endl ; |
---|
269 | } else |
---|
270 | { testingMessage("All tests completed successfully\n") ; } |
---|
271 | return totalErrCnt; |
---|
272 | } |
---|