Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : */
9 :
10 : #ifdef WNT
11 : # undef ERROR
12 : #endif
13 :
14 : #include <sal/types.h>
15 : #include <cppunit/TestAssert.h>
16 : #include <cppunit/TestFixture.h>
17 : #include <cppunit/extensions/HelperMacros.h>
18 : #include <cppunit/plugin/TestPlugIn.h>
19 :
20 : #include <sal/config.h>
21 : #include <osl/file.hxx>
22 : #include <osl/process.h>
23 : #include <rtl/ustrbuf.hxx>
24 :
25 : #include <cppuhelper/bootstrap.hxx>
26 : #include <comphelper/processfactory.hxx>
27 :
28 : #include <com/sun/star/sheet/DataPilotFieldOrientation.hpp>
29 : #include <com/sun/star/sheet/GeneralFunction.hpp>
30 : #include <com/sun/star/lang/XComponent.hpp>
31 :
32 : #include <i18nlangtag/lang.h>
33 :
34 : #include "svl/zforlist.hxx"
35 : #include "svl/zformat.hxx"
36 : #include "svl/sharedstringpool.hxx"
37 : #include "unotools/syslocale.hxx"
38 :
39 : #include <boost/scoped_ptr.hpp>
40 :
41 : using namespace ::com::sun::star;
42 : using namespace svl;
43 :
44 : namespace {
45 :
46 : class Test : public CppUnit::TestFixture {
47 : public:
48 : Test();
49 : virtual ~Test();
50 :
51 : virtual void setUp() SAL_OVERRIDE;
52 : virtual void tearDown() SAL_OVERRIDE;
53 :
54 : void testNumberFormat();
55 : void testSharedString();
56 : void testSharedStringPool();
57 : void testSharedStringPoolPurge();
58 : void testFdo60915();
59 : void testI116701();
60 :
61 2 : CPPUNIT_TEST_SUITE(Test);
62 1 : CPPUNIT_TEST(testNumberFormat);
63 1 : CPPUNIT_TEST(testSharedString);
64 1 : CPPUNIT_TEST(testSharedStringPool);
65 1 : CPPUNIT_TEST(testSharedStringPoolPurge);
66 1 : CPPUNIT_TEST(testFdo60915);
67 1 : CPPUNIT_TEST(testI116701);
68 2 : CPPUNIT_TEST_SUITE_END();
69 :
70 : private:
71 : uno::Reference< uno::XComponentContext > m_xContext;
72 : void checkPreviewString(SvNumberFormatter& aFormatter,
73 : const OUString& sCode,
74 : double fPreviewNumber,
75 : LanguageType eLang,
76 : OUString& sExpected);
77 : };
78 :
79 6 : Test::Test()
80 : {
81 6 : m_xContext = cppu::defaultBootstrap_InitialComponentContext();
82 :
83 6 : uno::Reference<lang::XMultiComponentFactory> xFactory(m_xContext->getServiceManager());
84 12 : uno::Reference<lang::XMultiServiceFactory> xSM(xFactory, uno::UNO_QUERY_THROW);
85 :
86 : //Without this we're crashing because callees are using
87 : //getProcessServiceFactory. In general those should be removed in favour
88 : //of retaining references to the root ServiceFactory as its passed around
89 12 : comphelper::setProcessServiceFactory(xSM);
90 6 : }
91 :
92 6 : void Test::setUp()
93 : {
94 6 : }
95 :
96 6 : void Test::tearDown()
97 : {
98 6 : }
99 :
100 18 : Test::~Test()
101 : {
102 6 : uno::Reference< lang::XComponent >(m_xContext, uno::UNO_QUERY_THROW)->dispose();
103 12 : }
104 :
105 1 : void Test::testNumberFormat()
106 : {
107 1 : LanguageType eLang = LANGUAGE_ENGLISH_US;
108 :
109 : const char* pNumber[] = {
110 : "General",
111 : "0",
112 : "0.00",
113 : "#,##0",
114 : "#,##0.00",
115 : "#,###.00",
116 : 0
117 1 : };
118 :
119 : const char* pScientific[] = {
120 : "0.00E+000",
121 : "0.00E+00",
122 : 0
123 1 : };
124 :
125 : const char* pPercent[] = {
126 : "0%",
127 : "0.00%",
128 : 0
129 1 : };
130 :
131 : const char* pFraction[] = {
132 : "# \?/\?",
133 : "# \?\?/\?\?",
134 : #if 0
135 : // TODO: Followings aren't in range of NF_FRACTION_START and NF_FRACTION_END
136 : // see enum NfIndexTableOffset in svl/inc/svl/zforlist.hxx
137 : "# \?/4",
138 : "# \?\?/100",
139 : #endif
140 : 0
141 1 : };
142 :
143 : #if 0 // TODO: Find out why on some systems the last two currency format codes differ.
144 : const char* pCurrency[] = {
145 : "$#,##0;[RED]-$#,##0",
146 : "$#,##0.00;[RED]-$#,##0.00",
147 : "#,##0.00 CCC",
148 : "$#,##0.--;[RED]-$#,##0.--",
149 : "$#,##0;-$#,##0",
150 : "$#,##0;-$#,##0",
151 : 0
152 : };
153 : #endif
154 :
155 : #if 0 // TODO: This currently fails
156 : const char* pDate[] = {
157 : "MM/DD/YY",
158 : "MM/DD/YYYY",
159 : "MMM D, YY",
160 : "MMM D, YYYY",
161 : "D. MMM. YYYY",
162 : "MMMM D, YYYY",
163 : "D. MMMM YYYY",
164 : "NN, MMM D, YY",
165 : "NN DD/MMM YY",
166 : "NN, MMMM D, YYYY",
167 : "NNNNMMMM D, YYYY",
168 : "MM-DD",
169 : "YY-MM-DD",
170 : "YYYY-MM-DD",
171 : "MM/YY",
172 : "MMM DD",
173 : "MMMM",
174 : "QQ YY",
175 : "WW",
176 : "MM/DD/YY",
177 : "WW",
178 : 0
179 : };
180 : #endif
181 :
182 : const char* pTime[] = {
183 : "HH:MM",
184 : "HH:MM:SS",
185 : "HH:MM AM/PM",
186 : "HH:MM:SS AM/PM",
187 : "[HH]:MM:SS",
188 : "MM:SS.00",
189 : "[HH]:MM:SS.00",
190 : 0
191 1 : };
192 :
193 : #if 0 // TODO: This currently fails
194 : const char* pDateTime[] = {
195 : "MM/DD/YY HH:MM AM/PM",
196 : "MM/DD/YY HH:MM AM/PM",
197 : 0
198 : };
199 : #endif
200 :
201 : const char* pBoolean[] = {
202 : "BOOLEAN",
203 : 0
204 1 : };
205 :
206 : const char* pText[] = {
207 : "@",
208 : 0
209 1 : };
210 :
211 : struct {
212 : NfIndexTableOffset eStart;
213 : NfIndexTableOffset eEnd;
214 : size_t nSize;
215 : const char** pCodes;
216 : } aTests[] = {
217 : { NF_NUMBER_START, NF_NUMBER_END, 6, pNumber },
218 : { NF_SCIENTIFIC_START, NF_SCIENTIFIC_END, 2, pScientific },
219 : { NF_PERCENT_START, NF_PERCENT_END, 2, pPercent },
220 : { NF_FRACTION_START, NF_FRACTION_END, 2, pFraction },
221 : #if 0 // TODO: Find out why on some systems the last two currency format codes differ.
222 : { NF_CURRENCY_START, NF_CURRENCY_END, 6, pCurrency },
223 : #endif
224 : #if 0 // TODO: This currently fails
225 : { NF_DATE_START, NF_DATE_END, 21, pDate },
226 : #endif
227 : { NF_TIME_START, NF_TIME_END, 7, pTime },
228 : #if 0 // TODO: This currently fails
229 : { NF_DATETIME_START, NF_DATETIME_END, 2, pDateTime },
230 : #endif
231 : { NF_BOOLEAN, NF_BOOLEAN, 1, pBoolean },
232 : { NF_TEXT, NF_TEXT, 1, pText }
233 1 : };
234 :
235 1 : SvNumberFormatter aFormatter(m_xContext, eLang);
236 :
237 8 : for (size_t i = 0; i < SAL_N_ELEMENTS(aTests); ++i)
238 : {
239 7 : size_t nStart = aTests[i].eStart;
240 7 : size_t nEnd = aTests[i].eEnd;
241 :
242 14 : CPPUNIT_ASSERT_MESSAGE("Unexpected number of formats for this category.",
243 7 : (nEnd - nStart + 1) == aTests[i].nSize);
244 :
245 28 : for (size_t j = nStart; j <= nEnd; ++j)
246 : {
247 : sal_uInt32 nIndex =
248 21 : aFormatter.GetFormatIndex(static_cast<NfIndexTableOffset>(j));
249 21 : const SvNumberformat* p = aFormatter.GetEntry(nIndex);
250 :
251 21 : CPPUNIT_ASSERT_MESSAGE("Number format entry is expected, but doesn't exist.", p);
252 21 : OUString aCode = p->GetFormatstring();
253 21 : bool bEqual = aCode.equalsAscii(aTests[i].pCodes[j-nStart]);
254 21 : CPPUNIT_ASSERT_MESSAGE("Unexpected number format code.", bEqual);
255 21 : }
256 : }
257 :
258 : sal_Int32 nPos;
259 1 : short nType = NUMBERFORMAT_DEFINED;
260 : sal_uInt32 nKey;
261 2 : OUString aCode;
262 : // Thai date format (implicit locale).
263 1 : aCode = "[$-1070000]d/mm/yyyy;@";
264 1 : if (!aFormatter.PutEntry(aCode, nPos, nType, nKey))
265 : {
266 0 : CPPUNIT_ASSERT_MESSAGE("failed to insert format code '[$-1070000]d/mm/yyyy;@'", false);
267 : }
268 :
269 : // Thai date format (explicit locale)
270 1 : aCode = "[$-107041E]d/mm/yyyy;@";
271 1 : if (!aFormatter.PutEntry(aCode, nPos, nType, nKey))
272 : {
273 0 : CPPUNIT_ASSERT_MESSAGE("failed to insert format code '[$-107041E]d/mm/yyyy;@'", false);
274 : }
275 :
276 : // Thai date format (using buddhist calendar type).
277 1 : aCode = "[~buddhist]D MMMM YYYY";
278 1 : if (!aFormatter.PutEntry(aCode, nPos, nType, nKey))
279 : {
280 0 : CPPUNIT_ASSERT_MESSAGE("failed to insert format code '[~buddhist]D MMMM YYYY'", false);
281 1 : }
282 1 : }
283 :
284 1 : void Test::testSharedString()
285 : {
286 : // Use shared string as normal, non-shared string, which is allowed.
287 2 : SharedString aSS1("Test"), aSS2("Test");
288 1 : CPPUNIT_ASSERT_MESSAGE("Equality check should return true.", aSS1 == aSS2);
289 2 : SharedString aSS3("test");
290 2 : CPPUNIT_ASSERT_MESSAGE("Equality check is case sensitive.", aSS1 != aSS3);
291 1 : }
292 :
293 1 : void Test::testSharedStringPool()
294 : {
295 1 : SvtSysLocale aSysLocale;
296 2 : svl::SharedStringPool aPool(aSysLocale.GetCharClassPtr());
297 :
298 2 : svl::SharedString p1, p2;
299 1 : p1 = aPool.intern("Andy");
300 1 : p2 = aPool.intern("Andy");
301 1 : CPPUNIT_ASSERT_EQUAL(p1.getData(), p2.getData());
302 :
303 1 : p2 = aPool.intern("Bruce");
304 1 : CPPUNIT_ASSERT_MESSAGE("They must differ.", p1.getData() != p2.getData());
305 :
306 2 : OUString aAndy("Andy");
307 1 : p1 = aPool.intern("Andy");
308 1 : p2 = aPool.intern(aAndy);
309 1 : CPPUNIT_ASSERT_MESSAGE("Identifier shouldn't be NULL.", p1.getData());
310 1 : CPPUNIT_ASSERT_MESSAGE("Identifier shouldn't be NULL.", p2.getData());
311 1 : CPPUNIT_ASSERT_EQUAL(p1.getData(), p2.getData());
312 :
313 : // Test case insensitive string ID's.
314 2 : OUString aAndyLower("andy"), aAndyUpper("ANDY");
315 1 : p1 = aPool.intern(aAndy);
316 1 : p2 = aPool.intern(aAndyLower);
317 1 : CPPUNIT_ASSERT_MESSAGE("Failed to intern strings.", p1.getData() && p2.getData());
318 1 : CPPUNIT_ASSERT_MESSAGE("These two ID's should differ.", p1.getData() != p2.getData());
319 1 : CPPUNIT_ASSERT_MESSAGE("These two ID's should be equal.", p1.getDataIgnoreCase() == p2.getDataIgnoreCase());
320 1 : p2 = aPool.intern(aAndyUpper);
321 1 : CPPUNIT_ASSERT_MESSAGE("Failed to intern string.", p2.getData());
322 1 : CPPUNIT_ASSERT_MESSAGE("These two ID's should differ.", p1.getData() != p2.getData());
323 2 : CPPUNIT_ASSERT_MESSAGE("These two ID's should be equal.", p1.getDataIgnoreCase() == p2.getDataIgnoreCase());
324 1 : }
325 :
326 1 : void Test::testSharedStringPoolPurge()
327 : {
328 1 : SvtSysLocale aSysLocale;
329 2 : svl::SharedStringPool aPool(aSysLocale.GetCharClassPtr());
330 1 : aPool.intern("Andy");
331 1 : aPool.intern("andy");
332 1 : aPool.intern("ANDY");
333 :
334 1 : CPPUNIT_ASSERT_MESSAGE("Wrong string count.", aPool.getCount() == 3);
335 1 : CPPUNIT_ASSERT_MESSAGE("Wrong case insensitive string count.", aPool.getCountIgnoreCase() == 1);
336 :
337 : // Since no string objects referencing the pooled strings exist, purging
338 : // the pool should empty it.
339 1 : aPool.purge();
340 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), aPool.getCount());
341 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), aPool.getCountIgnoreCase());
342 :
343 : // Now, create string objects on the heap.
344 2 : boost::scoped_ptr<OUString> pStr1(new OUString("Andy"));
345 2 : boost::scoped_ptr<OUString> pStr2(new OUString("andy"));
346 2 : boost::scoped_ptr<OUString> pStr3(new OUString("ANDY"));
347 2 : boost::scoped_ptr<OUString> pStr4(new OUString("Bruce"));
348 1 : aPool.intern(*pStr1);
349 1 : aPool.intern(*pStr2);
350 1 : aPool.intern(*pStr3);
351 1 : aPool.intern(*pStr4);
352 :
353 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aPool.getCount());
354 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPool.getCountIgnoreCase());
355 :
356 : // This shouldn't purge anything.
357 1 : aPool.purge();
358 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(4), aPool.getCount());
359 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPool.getCountIgnoreCase());
360 :
361 : // Delete one heap string object, and purge. That should purge one string.
362 1 : pStr1.reset();
363 1 : aPool.purge();
364 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(3), aPool.getCount());
365 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPool.getCountIgnoreCase());
366 :
367 : // Ditto...
368 1 : pStr3.reset();
369 1 : aPool.purge();
370 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPool.getCount());
371 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(2), aPool.getCountIgnoreCase());
372 :
373 : // Again.
374 1 : pStr2.reset();
375 1 : aPool.purge();
376 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPool.getCount());
377 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(1), aPool.getCountIgnoreCase());
378 :
379 : // Delete 'Bruce' and purge.
380 1 : pStr4.reset();
381 1 : aPool.purge();
382 1 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), aPool.getCount());
383 2 : CPPUNIT_ASSERT_EQUAL(static_cast<size_t>(0), aPool.getCountIgnoreCase());
384 1 : }
385 :
386 6 : void Test::checkPreviewString(SvNumberFormatter& aFormatter,
387 : const OUString& sCode,
388 : double fPreviewNumber,
389 : LanguageType eLang,
390 : OUString& sExpected)
391 : {
392 6 : OUString sStr;
393 6 : Color* pColor = 0;
394 6 : Color** ppColor = &pColor;
395 6 : if (!aFormatter.GetPreviewString(sCode, fPreviewNumber, sStr, ppColor, eLang))
396 0 : CPPUNIT_FAIL("GetPreviewString() failed");
397 6 : CPPUNIT_ASSERT_EQUAL(sExpected, sStr);
398 6 : }
399 :
400 1 : void Test::testFdo60915()
401 : {
402 1 : LanguageType eLang = LANGUAGE_THAI;
403 2 : OUString sCode, sExpected;
404 1 : double fPreviewNumber = 36486; // equals 1999-11-22 (2542 B.E.)
405 2 : SvNumberFormatter aFormatter(m_xContext, eLang);
406 : {
407 1 : sCode = "[~buddhist]D/MM/YYYY";
408 1 : sExpected = "22/11/2542";
409 1 : checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
410 : }
411 : {
412 1 : sCode = "[~buddhist]D/MM/YY";
413 1 : sExpected = "22/11/42";
414 1 : checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
415 : }
416 : {
417 1 : sCode = "[NatNum1][$-41E][~buddhist]D/MM/YYYY";
418 : sal_Unicode sTemp[] =
419 : {
420 : 0x0E52, 0x0E52, 0x002F,
421 : 0x0E51, 0x0E51, 0x002F,
422 : 0x0E52, 0x0E55, 0x0E54, 0x0E52
423 1 : };
424 1 : sExpected = OUString(sTemp, SAL_N_ELEMENTS(sTemp));
425 1 : checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
426 : }
427 : {
428 1 : sCode = "[NatNum1][$-41E][~buddhist]D/MM/YY";
429 : sal_Unicode sTemp[] =
430 : {
431 : 0x0E52, 0x0E52, 0x002F,
432 : 0x0E51, 0x0E51, 0x002F,
433 : 0x0E54, 0x0E52
434 1 : };
435 1 : sExpected = OUString(sTemp, SAL_N_ELEMENTS(sTemp));
436 1 : checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
437 1 : }
438 1 : }
439 :
440 : // https://issues.apache.org/ooo/show_bug.cgi?id=116701
441 1 : void Test::testI116701()
442 : {
443 1 : LanguageType eLang = LANGUAGE_CHINESE_TRADITIONAL;
444 2 : OUString sCode, sExpected;
445 1 : double fPreviewNumber = 40573; // equals 30/01/2011
446 2 : SvNumberFormatter aFormatter(m_xContext, eLang);
447 : // DateFormatskey25 in i18npool/source/localedata/data/zh_TW.xml
448 : sal_Unicode CODE1[] =
449 : {
450 : 0x0047, 0x0047, 0x0047, 0x0045, 0x0045, // GGGEE
451 : 0x0022, 0x5E74, 0x0022,
452 : 0x004D, // M
453 : 0x0022, 0x6708, 0x0022,
454 : 0x0044, // D
455 : 0x0022, 0x65E5, 0x0022
456 1 : };
457 1 : sCode = OUString(CODE1, SAL_N_ELEMENTS(CODE1));
458 : sal_Unicode EXPECTED[] =
459 : {
460 : 0x4E2D, 0x83EF, 0x6C11, 0x570B,
461 : 0x0031, 0x0030, 0x0030, // 100
462 : 0x5E74,
463 : 0x0031, // 1
464 : 0x6708,
465 : 0x0033, 0x0030, // 30
466 : 0x65E5
467 1 : };
468 1 : sExpected = OUString(EXPECTED, SAL_N_ELEMENTS(EXPECTED));
469 1 : checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
470 : sal_Unicode CODE2[] =
471 : {
472 : 0x0047, 0x0047, 0x0047, 0x0045, // GGGE
473 : 0x0022, 0x5E74, 0x0022,
474 : 0x004D, // M
475 : 0x0022, 0x6708, 0x0022,
476 : 0x0044, // D
477 : 0x0022, 0x65E5, 0x0022
478 1 : };
479 1 : sCode = OUString(CODE2, SAL_N_ELEMENTS(CODE2));
480 2 : checkPreviewString(aFormatter, sCode, fPreviewNumber, eLang, sExpected);
481 1 : }
482 :
483 1 : CPPUNIT_TEST_SUITE_REGISTRATION(Test);
484 :
485 : }
486 :
487 4 : CPPUNIT_PLUGIN_IMPLEMENT();
488 :
489 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|