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