1 /*
2  * Copyright (C) 2010 Tommi Maekitalo
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * As a special exception, you may use this file as part of a free
10  * software library without restriction. Specifically, if other files
11  * instantiate templates or use macros or inline functions from this
12  * file, or you compile this file and link it with other files to
13  * produce an executable, this file does not by itself cause the
14  * resulting executable to be covered by the GNU General Public
15  * License. This exception does not however invalidate any other
16  * reasons why the executable file might be covered by the GNU Library
17  * General Public License.
18  *
19  * This library is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * Lesser General Public License for more details.
23  *
24  * You should have received a copy of the GNU Lesser General Public
25  * License along with this library; if not, write to the Free Software
26  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
27  */
28 
29 #include "cxxtools/convert.h"
30 #include "cxxtools/unit/testsuite.h"
31 #include "cxxtools/unit/registertest.h"
32 #include "cxxtools/string.h"
33 #include "cxxtools/log.h"
34 #include <limits>
35 #include <string.h>
36 
37 log_define("cxxtools.test.convert")
38 
39 namespace
40 {
nearBy(double v1,double v2,double e=1e-6)41     bool nearBy(double v1, double v2, double e = 1e-6)
42     {
43         double q = v1 / v2;
44         return q > 1 - e && q < 1 + e;
45     }
46 }
47 
48 class ConvertTest : public cxxtools::unit::TestSuite
49 {
50     public:
ConvertTest()51         ConvertTest()
52         : cxxtools::unit::TestSuite("convert")
53         {
54             registerMethod("successTest", *this, &ConvertTest::successTest);
55             registerMethod("failTest", *this, &ConvertTest::failTest);
56             registerMethod("nanTest", *this, &ConvertTest::nanTest);
57             registerMethod("infTest", *this, &ConvertTest::infTest);
58             registerMethod("emptyTest", *this, &ConvertTest::emptyTest);
59             registerMethod("floatTest", *this, &ConvertTest::floatTest);
60         }
61 
successTest()62         void successTest()
63         {
64           std::string s(" -15 ");
65           int n = 0;
66           CXXTOOLS_UNIT_ASSERT_NOTHROW(n = cxxtools::convert<int>(s));
67           CXXTOOLS_UNIT_ASSERT_EQUALS(n, -15);
68 
69           cxxtools::String S(L" -42 ");
70           CXXTOOLS_UNIT_ASSERT_NOTHROW(n = cxxtools::convert<int>(S));
71           CXXTOOLS_UNIT_ASSERT_EQUALS(n, -42);
72 
73         }
74 
failTest()75         void failTest()
76         {
77           std::string s(" -15 a");
78           CXXTOOLS_UNIT_ASSERT_THROW(cxxtools::convert<int>(s), cxxtools::ConversionError);
79 
80           cxxtools::String S(L" -42 a");
81           CXXTOOLS_UNIT_ASSERT_THROW(cxxtools::convert<int>(S), cxxtools::ConversionError);
82 
83         }
84 
nanTest()85         void nanTest()
86         {
87           // test string to number
88 
89           // test nan
90 
91           double d = cxxtools::convert<double>(std::string("NaN"));
92           CXXTOOLS_UNIT_ASSERT(d != d);
93 
94           float f = cxxtools::convert<float>(std::string("NaN"));
95           CXXTOOLS_UNIT_ASSERT(f != f);
96 
97           d = cxxtools::convert<double>(cxxtools::String(L"NaN"));
98           CXXTOOLS_UNIT_ASSERT(d != d);
99 
100           f = cxxtools::convert<float>(cxxtools::String(L"NaN"));
101           CXXTOOLS_UNIT_ASSERT(f != f);
102 
103           // test quiet nan
104 
105           d = cxxtools::convert<double>(std::string("NaNQ"));
106           CXXTOOLS_UNIT_ASSERT(d != d);
107 
108           f = cxxtools::convert<float>(std::string("NaNQ"));
109           CXXTOOLS_UNIT_ASSERT(f != f);
110 
111           d = cxxtools::convert<double>(cxxtools::String(L"NaNQ"));
112           CXXTOOLS_UNIT_ASSERT(d != d);
113 
114           f = cxxtools::convert<float>(cxxtools::String(L"NaNQ"));
115           CXXTOOLS_UNIT_ASSERT(f != f);
116 
117           // test signaling nan
118 
119           d = cxxtools::convert<double>(std::string("NaNS"));
120           CXXTOOLS_UNIT_ASSERT(d != d);
121 
122           f = cxxtools::convert<float>(std::string("NaNS"));
123           CXXTOOLS_UNIT_ASSERT(f != f);
124 
125           d = cxxtools::convert<double>(cxxtools::String(L"NaNS"));
126           CXXTOOLS_UNIT_ASSERT(d != d);
127 
128           f = cxxtools::convert<float>(cxxtools::String(L"NaNS"));
129           CXXTOOLS_UNIT_ASSERT(f != f);
130 
131           CXXTOOLS_UNIT_ASSERT_THROW(cxxtools::convert<float>(cxxtools::String(L"NaNF")), cxxtools::ConversionError);
132 
133           // test number to string
134 
135           std::string s = cxxtools::convert<std::string>(d);
136           CXXTOOLS_UNIT_ASSERT_EQUALS(s, "nan");
137 
138           s = cxxtools::convert<std::string>(f);
139           CXXTOOLS_UNIT_ASSERT_EQUALS(s, "nan");
140 
141           cxxtools::String ss = cxxtools::convert<cxxtools::String>(d);
142           CXXTOOLS_UNIT_ASSERT_EQUALS(ss.narrow(), "nan");
143 
144           ss = cxxtools::convert<cxxtools::String>(f);
145           CXXTOOLS_UNIT_ASSERT_EQUALS(ss.narrow(), "nan");
146 
147         }
148 
infTest()149         void infTest()
150         {
151           // test string to number
152 
153           double d = cxxtools::convert<double>(std::string("inf"));
154           CXXTOOLS_UNIT_ASSERT_EQUALS(d, std::numeric_limits<double>::infinity());
155 
156           d = cxxtools::convert<double>(std::string("infinity"));
157           CXXTOOLS_UNIT_ASSERT_EQUALS(d, std::numeric_limits<double>::infinity());
158 
159           float f = cxxtools::convert<float>(std::string("inf"));
160           CXXTOOLS_UNIT_ASSERT_EQUALS(d, std::numeric_limits<float>::infinity());
161 
162           d = cxxtools::convert<double>(cxxtools::String(L"iNf"));
163           CXXTOOLS_UNIT_ASSERT_EQUALS(d, std::numeric_limits<double>::infinity());
164 
165           f = cxxtools::convert<float>(cxxtools::String(L"inF"));
166           CXXTOOLS_UNIT_ASSERT_EQUALS(d, std::numeric_limits<float>::infinity());
167 
168           // test number to string
169 
170           std::string s = cxxtools::convert<std::string>(d);
171           CXXTOOLS_UNIT_ASSERT_EQUALS(s, "inf");
172 
173           s = cxxtools::convert<std::string>(f);
174           CXXTOOLS_UNIT_ASSERT(strcasecmp(s.c_str(), "inf") == 0);
175 
176           cxxtools::String ss = cxxtools::convert<cxxtools::String>(d);
177           CXXTOOLS_UNIT_ASSERT(strcasecmp(ss.narrow().c_str(), "inf") == 0);
178 
179           ss = cxxtools::convert<cxxtools::String>(f);
180           CXXTOOLS_UNIT_ASSERT(strcasecmp(ss.narrow().c_str(), "inf") == 0);
181 
182           // negative inf
183 
184           // string to number
185           d = cxxtools::convert<double>(std::string("-inf"));
186           CXXTOOLS_UNIT_ASSERT_EQUALS(d, -std::numeric_limits<double>::infinity());
187 
188           f = cxxtools::convert<float>(std::string("-inf"));
189           CXXTOOLS_UNIT_ASSERT_EQUALS(d, -std::numeric_limits<float>::infinity());
190 
191           d = cxxtools::convert<double>(cxxtools::String(L"-iNf"));
192           CXXTOOLS_UNIT_ASSERT_EQUALS(d, -std::numeric_limits<double>::infinity());
193 
194           f = cxxtools::convert<float>(cxxtools::String(L"-INF"));
195           CXXTOOLS_UNIT_ASSERT_EQUALS(d, -std::numeric_limits<float>::infinity());
196 
197           // test number to string
198           s = cxxtools::convert<std::string>(d);
199           CXXTOOLS_UNIT_ASSERT_EQUALS(s, "-inf");
200 
201           s = cxxtools::convert<std::string>(f);
202           CXXTOOLS_UNIT_ASSERT(strcasecmp(s.c_str(), "-inf") == 0);
203 
204           ss = cxxtools::convert<cxxtools::String>(d);
205           CXXTOOLS_UNIT_ASSERT(strcasecmp(ss.narrow().c_str(), "-inf") == 0);
206 
207           ss = cxxtools::convert<cxxtools::String>(f);
208           CXXTOOLS_UNIT_ASSERT(strcasecmp(ss.narrow().c_str(), "-inf") == 0);
209 
210         }
211 
emptyTest()212         void emptyTest()
213         {
214           std::string emptyString;
215           CXXTOOLS_UNIT_ASSERT_THROW(cxxtools::convert<int>(std::string()), cxxtools::ConversionError);
216           CXXTOOLS_UNIT_ASSERT_THROW(cxxtools::convert<int>(cxxtools::String()), cxxtools::ConversionError);
217           CXXTOOLS_UNIT_ASSERT_THROW(cxxtools::convert<unsigned>(std::string()), cxxtools::ConversionError);
218           CXXTOOLS_UNIT_ASSERT_THROW(cxxtools::convert<unsigned>(cxxtools::String()), cxxtools::ConversionError);
219           CXXTOOLS_UNIT_ASSERT_THROW(cxxtools::convert<double>(std::string()), cxxtools::ConversionError);
220           CXXTOOLS_UNIT_ASSERT_THROW(cxxtools::convert<double>(cxxtools::String()), cxxtools::ConversionError);
221         }
222 
t(double d)223         void t(double d)
224         {
225           std::string s = cxxtools::convert<std::string>(d);
226           double r = cxxtools::convert<double>(s);
227           if (r == 0)
228           {
229             CXXTOOLS_UNIT_ASSERT_EQUALS(d, 0);
230           }
231           else
232           {
233             double q = d / r;
234             log_debug("d=" << d << " s=\"" << s << "\" r=" << r << " q=" << q);
235             if (q < 0.999999999999 || q > 1.0000000000001)
236             {
237               CXXTOOLS_UNIT_ASSERT_EQUALS(d, r);
238             }
239           }
240         }
241 
floatTest()242         void floatTest()
243         {
244           double d;
245 
246           d = cxxtools::convert<double>("1.5");
247           CXXTOOLS_UNIT_ASSERT_EQUALS(d, 1.5);
248 
249           d = cxxtools::convert<double>(" -345.75 ");
250           CXXTOOLS_UNIT_ASSERT_EQUALS(d, -345.75);
251 
252           d = cxxtools::convert<double>("\n1e6\r");
253           CXXTOOLS_UNIT_ASSERT_EQUALS(d, 1e6);
254 
255           d = cxxtools::convert<double>("7.0e4");
256           CXXTOOLS_UNIT_ASSERT_EQUALS(d, 7e4);
257 
258           d = cxxtools::convert<double>("-2e-3");
259           CXXTOOLS_UNIT_ASSERT(nearBy(d, -2e-3));
260 
261           d = cxxtools::convert<double>("-8E-5");
262           CXXTOOLS_UNIT_ASSERT(nearBy(d, -8e-5));
263 
264           d = cxxtools::convert<double>("-3.0e-12");
265           CXXTOOLS_UNIT_ASSERT(nearBy(d, -3e-12));
266 
267           d = cxxtools::convert<double>("-8.5E-23");
268           CXXTOOLS_UNIT_ASSERT(nearBy(d, -8.5e-23));
269 
270           t(3.141592653579893);
271           t(0.314);
272           t(0.0314);
273           t(0.00123);
274           t(123456789.55555555);
275           t(0);
276           t(1);
277           t(1.4567e17);
278           t(12345);
279           t(1.4567e-17);
280           t(0.2);
281           t(12);
282         }
283 
284 };
285 
286 cxxtools::unit::RegisterTest<ConvertTest> register_ConvertTest;
287