update credits
[LibreOffice.git] / sw / qa / core / uwriter.cxx
blob249f8385bc0780ce1e02dbb07c05b9d00c76299c
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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 */
10 #include <sal/config.h>
11 #include <test/bootstrapfixture.hxx>
13 #include <rtl/strbuf.hxx>
14 #include <osl/file.hxx>
16 #include <com/sun/star/i18n/TransliterationModulesExtra.hpp>
18 #include <comphelper/processfactory.hxx>
19 #include <tools/urlobj.hxx>
20 #include <unotools/tempfile.hxx>
21 #include <unotools/transliterationwrapper.hxx>
23 #include <editeng/langitem.hxx>
24 #include <editeng/charhiddenitem.hxx>
26 #include <sfx2/app.hxx>
27 #include <sfx2/docfilt.hxx>
28 #include <sfx2/docfile.hxx>
29 #include <sfx2/sfxmodelfactory.hxx>
31 #include <xmloff/odffields.hxx>
33 #include "breakit.hxx"
34 #include "doc.hxx"
35 #include "docsh.hxx"
36 #include "docstat.hxx"
37 #include "docufld.hxx"
38 #include "fmtanchr.hxx"
39 #include "init.hxx"
40 #include "ndtxt.hxx"
41 #include "shellio.hxx"
42 #include "shellres.hxx"
43 #include "swcrsr.hxx"
44 #include "swscanner.hxx"
45 #include "swmodule.hxx"
46 #include "swtypes.hxx"
47 #include "fmtftn.hxx"
48 #include "fmtrfmrk.hxx"
49 #include "fmtfld.hxx"
50 #include "redline.hxx"
51 #include "docary.hxx"
52 #include "modeltoviewhelper.hxx"
53 #include "scriptinfo.hxx"
55 SV_DECL_REF(SwDocShell)
56 SV_IMPL_REF(SwDocShell)
58 using namespace ::com::sun::star;
60 /* Implementation of Swdoc-Test class */
62 class SwDocTest : public test::BootstrapFixture
64 public:
65 virtual void setUp();
66 virtual void tearDown();
68 void randomTest();
69 void testPageDescName();
70 void testFileNameFields();
71 void testDocStat();
72 void testModelToViewHelper();
73 void testSwScanner();
74 void testUserPerceivedCharCount();
75 void testGraphicAnchorDeletion();
76 void testFdo57938();
77 void testFdo59573();
78 void testTransliterate();
80 CPPUNIT_TEST_SUITE(SwDocTest);
81 CPPUNIT_TEST(testTransliterate);
82 CPPUNIT_TEST(randomTest);
83 CPPUNIT_TEST(testPageDescName);
84 CPPUNIT_TEST(testFileNameFields);
85 CPPUNIT_TEST(testDocStat);
86 CPPUNIT_TEST(testModelToViewHelper);
87 CPPUNIT_TEST(testSwScanner);
88 CPPUNIT_TEST(testUserPerceivedCharCount);
89 CPPUNIT_TEST(testGraphicAnchorDeletion);
90 CPPUNIT_TEST(testFdo57938);
91 CPPUNIT_TEST(testFdo59573);
92 CPPUNIT_TEST_SUITE_END();
94 private:
95 SwDoc *m_pDoc;
96 SwDocShellRef m_xDocShRef;
99 void SwDocTest::testPageDescName()
101 ShellResource aShellResources;
103 std::vector<OUString> aResults;
105 //These names must be unique for each different combination, otherwise
106 //duplicate page description names may exist, which will causes lookup
107 //by name to be incorrect, and so the corresponding export to .odt
108 aResults.push_back(aShellResources.GetPageDescName(1, ShellResource::NORMAL_PAGE));
109 aResults.push_back(aShellResources.GetPageDescName(1, ShellResource::FIRST_PAGE));
110 aResults.push_back(aShellResources.GetPageDescName(1, ShellResource::FOLLOW_PAGE));
112 std::sort(aResults.begin(), aResults.end());
113 aResults.erase(std::unique(aResults.begin(), aResults.end()), aResults.end());
115 CPPUNIT_ASSERT_MESSAGE("GetPageDescName results must be unique", aResults.size() == 3);
118 //See https://bugs.freedesktop.org/show_bug.cgi?id=32463
119 void SwDocTest::testFileNameFields()
121 //Here's a file name with some chars in it that will be %% encoded, when expanding
122 //SwFileNameFields we want to restore the original readable filename
123 utl::TempFile aTempFile(OUString("demo [name]"));
124 aTempFile.EnableKillingFile();
126 INetURLObject aTempFileURL(aTempFile.GetURL());
127 String sFileURL = aTempFileURL.GetMainURL(INetURLObject::NO_DECODE);
128 SfxMedium aDstMed(sFileURL, STREAM_STD_READWRITE);
130 SfxFilter aFilter(
131 OUString("Text"),
132 OUString(), 0, 0, OUString(), 0, OUString(),
133 OUString("TEXT"), OUString() );
134 aDstMed.SetFilter(&aFilter);
136 m_xDocShRef->DoSaveAs(aDstMed);
137 m_xDocShRef->DoSaveCompleted(&aDstMed);
139 const INetURLObject &rUrlObj = m_xDocShRef->GetMedium()->GetURLObject();
141 SwFileNameFieldType aNameField(m_pDoc);
144 OUString sResult(aNameField.Expand(FF_NAME));
145 OUString sExpected(rUrlObj.getName(INetURLObject::LAST_SEGMENT,
146 true,INetURLObject::DECODE_WITH_CHARSET));
147 CPPUNIT_ASSERT_MESSAGE("Expected Readable FileName", sResult == sExpected);
151 OUString sResult(aNameField.Expand(FF_PATHNAME));
152 OUString sExpected(rUrlObj.GetFull());
153 CPPUNIT_ASSERT_MESSAGE("Expected Readable FileName", sResult == sExpected);
157 OUString sResult(aNameField.Expand(FF_PATH));
158 INetURLObject aTemp(rUrlObj);
159 aTemp.removeSegment();
160 OUString sExpected(aTemp.PathToFileName());
161 CPPUNIT_ASSERT_MESSAGE("Expected Readable FileName", sResult == sExpected);
165 OUString sResult(aNameField.Expand(FF_NAME_NOEXT));
166 OUString sExpected(rUrlObj.getName(INetURLObject::LAST_SEGMENT,
167 true,INetURLObject::DECODE_WITH_CHARSET));
168 //Chop off .tmp
169 sExpected = sExpected.copy(0, sExpected.getLength() - 4);
170 CPPUNIT_ASSERT_MESSAGE("Expected Readable FileName", sResult == sExpected);
173 m_xDocShRef->DoInitNew(0);
176 //See http://lists.freedesktop.org/archives/libreoffice/2011-August/016666.html
177 //Remove unnecessary parameter to IDocumentStatistics::UpdateDocStat for
178 //motivation
179 void SwDocTest::testDocStat()
181 CPPUNIT_ASSERT_MESSAGE("Expected initial 0 count", m_pDoc->GetDocStat().nChar == 0);
183 SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
184 SwPaM aPaM(aIdx);
186 OUString sText("Hello World");
187 m_pDoc->InsertString(aPaM, sText);
189 CPPUNIT_ASSERT_MESSAGE("Should still be non-updated 0 count", m_pDoc->GetDocStat().nChar == 0);
191 SwDocStat aDocStat = m_pDoc->GetUpdatedDocStat();
192 sal_uLong nLen = static_cast<sal_uLong>(sText.getLength());
194 CPPUNIT_ASSERT_MESSAGE("Should now have updated count", aDocStat.nChar == nLen);
196 CPPUNIT_ASSERT_MESSAGE("And cache is updated too", m_pDoc->GetDocStat().nChar == nLen);
199 //For UI character counts we should follow UAX#29 and display the user
200 //perceived characters, not the number of codepoints, nor the number of code
201 //units http://unicode.org/reports/tr29/
202 void SwDocTest::testUserPerceivedCharCount()
204 SwBreakIt *pBreakIter = SwBreakIt::Get();
206 //Grapheme example, two different unicode code-points perceived by the user as a single
207 //glyph
208 const sal_Unicode ALEF_QAMATS [] = { 0x05D0, 0x05B8 };
209 OUString sALEF_QAMATS(ALEF_QAMATS, SAL_N_ELEMENTS(ALEF_QAMATS));
210 sal_Int32 nGraphemeCount = pBreakIter->getGraphemeCount(sALEF_QAMATS);
211 CPPUNIT_ASSERT_MESSAGE("Grapheme Count should be 1", nGraphemeCount == 1);
213 //Surrogate pair example, one single unicode code-point (U+1D11E)
214 //represented as two code units in UTF-16
215 const sal_Unicode GCLEF[] = { 0xD834, 0xDD1E };
216 OUString sGCLEF(GCLEF, SAL_N_ELEMENTS(GCLEF));
217 sal_Int32 nCount = pBreakIter->getGraphemeCount(sGCLEF);
218 CPPUNIT_ASSERT_MESSAGE("Surrogate Pair should be counted as single character", nCount == 1);
221 void SwDocTest::testModelToViewHelper()
223 SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
224 SwPaM aPaM(aIdx);
227 SwFmtFtn aFtn;
228 aFtn.SetNumStr(OUString("foo"));
230 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
231 m_pDoc->InsertString(aPaM, OUString("AAAAA BBBBB "));
232 SwTxtNode* pTxtNode = aPaM.GetNode()->GetTxtNode();
233 xub_StrLen nPos = aPaM.GetPoint()->nContent.GetIndex();
234 pTxtNode->InsertItem(aFtn, nPos, nPos);
235 m_pDoc->InsertString(aPaM, OUString(" CCCCC "));
236 nPos = aPaM.GetPoint()->nContent.GetIndex();
237 pTxtNode->InsertItem(aFtn, nPos, nPos);
238 m_pDoc->InsertString(aPaM, OUString(" DDDDD"));
239 CPPUNIT_ASSERT(pTxtNode->GetTxt().getLength() == (4*5) + 5 + 2);
241 //set start of selection to first B
242 aPaM.GetPoint()->nContent.Assign(aPaM.GetCntntNode(), 6);
243 aPaM.SetMark();
244 //set end of selection to last C
245 aPaM.GetPoint()->nContent.Assign(aPaM.GetCntntNode(), 14);
246 //set character attribute hidden on range
247 SvxCharHiddenItem aHidden(true, RES_CHRATR_HIDDEN);
248 m_pDoc->InsertPoolItem(aPaM, aHidden, 0 );
250 //turn on red-lining and show changes
251 m_pDoc->SetRedlineMode(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_DELETE|nsRedlineMode_t::REDLINE_SHOW_INSERT);
252 CPPUNIT_ASSERT_MESSAGE("redlining should be on", m_pDoc->IsRedlineOn());
253 CPPUNIT_ASSERT_MESSAGE("redlines should be visible", IDocumentRedlineAccess::IsShowChanges(m_pDoc->GetRedlineMode()));
255 //set start of selection to last A
256 aPaM.GetPoint()->nContent.Assign(aPaM.GetCntntNode(), 4);
257 aPaM.SetMark();
258 //set end of selection to second last B
259 aPaM.GetPoint()->nContent.Assign(aPaM.GetCntntNode(), 9);
260 m_pDoc->DeleteAndJoin(aPaM); //redline-aware deletion api
263 ModelToViewHelper aModelToViewHelper(*pTxtNode, PASSTHROUGH);
264 OUString sViewText = aModelToViewHelper.getViewText();
265 OUString sModelText = pTxtNode->GetTxt();
266 CPPUNIT_ASSERT(sViewText == sModelText);
270 ModelToViewHelper aModelToViewHelper(*pTxtNode, EXPANDFIELDS);
271 OUString sViewText = aModelToViewHelper.getViewText();
272 CPPUNIT_ASSERT(sViewText == "AAAAA BBBBB foo CCCCC foo DDDDD");
276 ModelToViewHelper aModelToViewHelper(*pTxtNode, HIDEINVISIBLE);
277 OUString sViewText = aModelToViewHelper.getViewText();
278 OUStringBuffer aBuffer;
279 aBuffer.append("AAAAA CCCCC ");
280 aBuffer.append(CH_TXTATR_BREAKWORD);
281 aBuffer.append(" DDDDD");
282 CPPUNIT_ASSERT(sViewText == aBuffer.makeStringAndClear());
286 ModelToViewHelper aModelToViewHelper(*pTxtNode, HIDEREDLINED);
287 OUString sViewText = aModelToViewHelper.getViewText();
288 OUStringBuffer aBuffer;
289 aBuffer.append("AAAABB ");
290 aBuffer.append(CH_TXTATR_BREAKWORD);
291 aBuffer.append(" CCCCC ");
292 aBuffer.append(CH_TXTATR_BREAKWORD);
293 aBuffer.append(" DDDDD");
294 CPPUNIT_ASSERT(sViewText == aBuffer.makeStringAndClear());
298 ModelToViewHelper aModelToViewHelper(*pTxtNode, EXPANDFIELDS | HIDEINVISIBLE);
299 OUString sViewText = aModelToViewHelper.getViewText();
300 CPPUNIT_ASSERT(sViewText == "AAAAA CCCCC foo DDDDD");
304 ModelToViewHelper aModelToViewHelper(*pTxtNode, EXPANDFIELDS | HIDEREDLINED);
305 OUString sViewText = aModelToViewHelper.getViewText();
306 CPPUNIT_ASSERT(sViewText == "AAAABB foo CCCCC foo DDDDD");
310 ModelToViewHelper aModelToViewHelper(*pTxtNode, HIDEINVISIBLE | HIDEREDLINED);
311 OUString sViewText = aModelToViewHelper.getViewText();
312 OUStringBuffer aBuffer;
313 aBuffer.append("AAAACCCCC ");
314 aBuffer.append(CH_TXTATR_BREAKWORD);
315 aBuffer.append(" DDDDD");
316 CPPUNIT_ASSERT(sViewText == aBuffer.makeStringAndClear());
320 ModelToViewHelper aModelToViewHelper(*pTxtNode, EXPANDFIELDS | HIDEINVISIBLE | HIDEREDLINED);
321 OUString sViewText = aModelToViewHelper.getViewText();
322 CPPUNIT_ASSERT(sViewText == "AAAACCCCC foo DDDDD");
327 void SwDocTest::testSwScanner()
329 SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
330 SwPaM aPaM(aIdx);
332 SwTxtNode* pTxtNode = aPaM.GetNode()->GetTxtNode();
334 CPPUNIT_ASSERT_MESSAGE("Has Text Node", pTxtNode);
336 //See https://bugs.freedesktop.org/show_bug.cgi?id=40449
337 //See https://bugs.freedesktop.org/show_bug.cgi?id=39365
338 //Use a temporary OUString as the arg, as that's the trouble behind
339 //fdo#40449 and fdo#39365
341 SwScanner aScanner(*pTxtNode,
342 OUString("Hello World"),
343 0, ModelToViewHelper(), i18n::WordType::DICTIONARY_WORD, 0,
344 RTL_CONSTASCII_LENGTH("Hello World"));
346 bool bFirstOk = aScanner.NextWord();
347 CPPUNIT_ASSERT_MESSAGE("First Token", bFirstOk);
348 const OUString &rHello = aScanner.GetWord();
349 CPPUNIT_ASSERT_MESSAGE("Should be Hello",
350 rHello == "Hello");
352 bool bSecondOk = aScanner.NextWord();
353 CPPUNIT_ASSERT_MESSAGE("Second Token", bSecondOk);
354 const OUString &rWorld = aScanner.GetWord();
355 CPPUNIT_ASSERT_MESSAGE("Should be World",
356 rWorld == "World");
359 //See https://www.libreoffice.org/bugzilla/show_bug.cgi?id=45271
361 const sal_Unicode IDEOGRAPHICFULLSTOP_D[] = { 0x3002, 'D' };
363 m_pDoc->InsertString(aPaM, OUString(IDEOGRAPHICFULLSTOP_D,
364 SAL_N_ELEMENTS(IDEOGRAPHICFULLSTOP_D)));
366 SvxLanguageItem aCJKLangItem( LANGUAGE_CHINESE_SIMPLIFIED, RES_CHRATR_CJK_LANGUAGE );
367 SvxLanguageItem aWestLangItem( LANGUAGE_ENGLISH_US, RES_CHRATR_LANGUAGE );
368 m_pDoc->InsertPoolItem(aPaM, aCJKLangItem, 0 );
369 m_pDoc->InsertPoolItem(aPaM, aWestLangItem, 0 );
371 SwDocStat aDocStat;
372 pTxtNode = aPaM.GetNode()->GetTxtNode();
373 pTxtNode->CountWords(aDocStat, 0, SAL_N_ELEMENTS(IDEOGRAPHICFULLSTOP_D));
375 CPPUNIT_ASSERT_MESSAGE("Should be 2", aDocStat.nChar == 2);
376 CPPUNIT_ASSERT_MESSAGE("Should be 2", aDocStat.nCharExcludingSpaces == 2);
379 const sal_Unicode test[] =
381 0x3053, 0x306E, 0x65E5, 0x672C, 0x8A9E, 0x306F, 0x6B63, 0x3057,
382 0x304F, 0x6570, 0x3048, 0x3089, 0x308C, 0x308B, 0x3067, 0x3057,
383 0x3087, 0x3046, 0x304B, 0x3002, 0x0041, 0x006E, 0x0064, 0x0020,
384 0x006C, 0x0065, 0x0074, 0x0027, 0x0073, 0x0020, 0x0074, 0x0068,
385 0x0072, 0x006F, 0x0077, 0x0020, 0x0073, 0x006F, 0x006D, 0x0065,
386 0x0020, 0x0045, 0x006E, 0x0067, 0x006C, 0x0069, 0x0073, 0x0068,
387 0x0020, 0x0069, 0x006E, 0x0020, 0x0074, 0x006F, 0x0020, 0x006D,
388 0x0061, 0x006B, 0x0065, 0x0020, 0x0069, 0x0074, 0x0020, 0x0069,
389 0x006E, 0x0074, 0x0065, 0x0072, 0x0065, 0x0073, 0x0074, 0x0069,
390 0x006E, 0x0067, 0x002E, 0x0020, 0x0020, 0x305D, 0x3057, 0x3066,
391 0x3001, 0x307E, 0x305F, 0x65E5, 0x672C, 0x8A9E, 0x3000, 0x3000,
392 0x3067, 0x3082, 0x4ECA, 0x56DE, 0x306F, 0x7A7A, 0x767D, 0x3092,
393 0x3000, 0x3000, 0x5165, 0x308C, 0x307E, 0x3057, 0x305F, 0x3002,
394 0x0020, 0x0020, 0x0053, 0x006F, 0x0020, 0x0068, 0x006F, 0x0077,
395 0x0020, 0x0064, 0x006F, 0x0065, 0x0073, 0x0020, 0x0074, 0x0068,
396 0x0069, 0x0073, 0x0020, 0x0064, 0x006F, 0x003F, 0x0020, 0x0020
398 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
399 m_pDoc->InsertString(aPaM, OUString(test,
400 SAL_N_ELEMENTS(test)));
402 SvxLanguageItem aCJKLangItem( LANGUAGE_JAPANESE, RES_CHRATR_CJK_LANGUAGE );
403 SvxLanguageItem aWestLangItem( LANGUAGE_ENGLISH_US, RES_CHRATR_LANGUAGE );
404 m_pDoc->InsertPoolItem(aPaM, aCJKLangItem, 0 );
405 m_pDoc->InsertPoolItem(aPaM, aWestLangItem, 0 );
407 SwDocStat aDocStat;
408 pTxtNode = aPaM.GetNode()->GetTxtNode();
409 pTxtNode->CountWords(aDocStat, 0, SAL_N_ELEMENTS(test));
410 CPPUNIT_ASSERT_MESSAGE("58 words", aDocStat.nWord == 58);
411 CPPUNIT_ASSERT_MESSAGE("43 Asian characters and Korean syllables", aDocStat.nAsianWord == 43);
412 CPPUNIT_ASSERT_MESSAGE("105 non-whitespace chars", aDocStat.nCharExcludingSpaces == 105);
413 CPPUNIT_ASSERT_MESSAGE("128 characters", aDocStat.nChar == 128);
416 //See https://issues.apache.org/ooo/show_bug.cgi?id=89042
417 //See https://bugs.freedesktop.org/show_bug.cgi?id=53399
419 SwDocStat aDocStat;
421 const sal_Unicode aShouldBeThree[] = {
422 0x0053, 0x0068, 0x006F, 0x0075, 0x006C, 0x0064, 0x0020,
423 0x2018, 0x0062, 0x0065, 0x0020, 0x0074, 0x0068, 0x0072,
424 0x0065, 0x0065, 0x2019
427 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
428 m_pDoc->InsertString(aPaM, OUString(aShouldBeThree, SAL_N_ELEMENTS(aShouldBeThree)));
429 pTxtNode = aPaM.GetNode()->GetTxtNode();
430 pTxtNode->CountWords(aDocStat, 0, SAL_N_ELEMENTS(aShouldBeThree));
431 CPPUNIT_ASSERT_MESSAGE("Should be 3", aDocStat.nWord == 3);
433 const sal_Unicode aShouldBeFive[] = {
434 // f r e n c h space
435 0x0046, 0x0072, 0x0065, 0x006E, 0x0063, 0x0068, 0x0020,
436 // << nbsp s a v o i
437 0x00AB, 0x00A0, 0x0073, 0x0061, 0x0076, 0x006F, 0x0069,
438 // r nnbsp c a l c u
439 0x0072, 0x202f, 0x0063, 0x0061, 0x006C, 0x0063, 0x0075,
440 // l e r idspace >>
441 0x006C, 0x0065, 0x0072, 0x3000, 0x00BB
444 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
445 m_pDoc->InsertString(aPaM, OUString(aShouldBeFive, SAL_N_ELEMENTS(aShouldBeFive)));
446 pTxtNode = aPaM.GetNode()->GetTxtNode();
447 aDocStat.Reset();
448 pTxtNode->CountWords(aDocStat, 0, SAL_N_ELEMENTS(aShouldBeFive));
449 CPPUNIT_ASSERT_MESSAGE("Should be 5", aDocStat.nWord == 5);
452 //See https://bugs.freedesktop.org/show_bug.cgi?id=49629
454 SwDocStat aDocStat;
456 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
457 m_pDoc->InsertString(aPaM, OUString("Apple"));
458 pTxtNode = aPaM.GetNode()->GetTxtNode();
459 xub_StrLen nPos = aPaM.GetPoint()->nContent.GetIndex();
460 SwFmtFtn aFtn;
461 aFtn.SetNumStr(OUString("banana"));
462 SwTxtAttr* pTA = pTxtNode->InsertItem(aFtn, nPos, nPos);
463 CPPUNIT_ASSERT(pTA);
464 CPPUNIT_ASSERT(pTxtNode->Len() == 6); //Apple + 0x02
465 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
466 CPPUNIT_ASSERT(aDocStat.nWord == 1);
467 CPPUNIT_ASSERT_MESSAGE("footnote should be expanded", aDocStat.nChar == 11);
469 xub_StrLen nNextPos = aPaM.GetPoint()->nContent.GetIndex();
470 CPPUNIT_ASSERT(nNextPos == nPos+1);
471 SwFmtRefMark aRef(OUString("refmark"));
472 pTA = pTxtNode->InsertItem(aRef, nNextPos, nNextPos);
473 CPPUNIT_ASSERT(pTA);
475 aDocStat.Reset();
476 pTxtNode->SetWordCountDirty(true);
477 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
478 CPPUNIT_ASSERT(aDocStat.nWord == 1);
479 CPPUNIT_ASSERT_MESSAGE("refmark anchor should not be counted", aDocStat.nChar == 11);
481 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
482 m_pDoc->InsertString(aPaM, OUString("Apple"));
484 DateTime aDate(DateTime::SYSTEM);
485 SwPostItField aPostIt(
486 (SwPostItFieldType*)m_pDoc->GetSysFldType(RES_POSTITFLD), OUString("An Author"),
487 OUString("Some Text"), OUString("Initials"), OUString("Name"), aDate );
488 m_pDoc->InsertPoolItem(aPaM, SwFmtFld(aPostIt), 0);
490 m_pDoc->InsertString(aPaM, OUString("Apple"));
491 pTxtNode = aPaM.GetNode()->GetTxtNode();
492 aDocStat.Reset();
493 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
494 CPPUNIT_ASSERT(aDocStat.nWord == 1);
495 CPPUNIT_ASSERT_MESSAGE("postit anchor should effectively not exist", aDocStat.nChar == 10);
496 CPPUNIT_ASSERT(pTxtNode->Len() == 11);
498 aDocStat.Reset();
501 //See https://bugs.freedesktop.org/show_bug.cgi?id=46757
503 SwDocStat aDocStat;
505 const char aString[] = "Lorem ipsum";
506 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
507 m_pDoc->InsertString(aPaM, OUString(aString));
508 pTxtNode = aPaM.GetNode()->GetTxtNode();
509 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
510 CPPUNIT_ASSERT_EQUAL(aDocStat.nWord, static_cast<sal_uLong>(2));
512 //turn on red-lining and show changes
513 m_pDoc->SetRedlineMode(nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_DELETE|nsRedlineMode_t::REDLINE_SHOW_INSERT);
514 CPPUNIT_ASSERT_MESSAGE("redlining should be on", m_pDoc->IsRedlineOn());
515 CPPUNIT_ASSERT_MESSAGE("redlines should be visible", IDocumentRedlineAccess::IsShowChanges(m_pDoc->GetRedlineMode()));
517 //delete everything except the first word
518 aPaM.SetMark(); //set start of selection to current pos
519 aPaM.GetPoint()->nContent.Assign(aPaM.GetCntntNode(), 5); //set end of selection to fifth char of current node
520 m_pDoc->DeleteAndJoin(aPaM); //redline-aware deletion api
521 //"real underlying text should be the same"
522 CPPUNIT_ASSERT_EQUAL(pTxtNode->GetTxt(), OUString(aString));
524 aDocStat.Reset();
525 pTxtNode->SetWordCountDirty(true);
526 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len()); //but word-counting the text should only count the non-deleted text
527 CPPUNIT_ASSERT_EQUAL(aDocStat.nWord, static_cast<sal_uLong>(1));
529 pTxtNode->SetWordCountDirty(true);
531 //keep red-lining on but hide changes
532 m_pDoc->SetRedlineMode(nsRedlineMode_t::REDLINE_ON);
533 CPPUNIT_ASSERT_MESSAGE("redlining should be still on", m_pDoc->IsRedlineOn());
534 CPPUNIT_ASSERT_MESSAGE("redlines should be invisible", !IDocumentRedlineAccess::IsShowChanges(m_pDoc->GetRedlineMode()));
536 aDocStat.Reset();
537 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len()); //but word-counting the text should only count the non-deleted text
538 CPPUNIT_ASSERT_EQUAL(aDocStat.nWord, static_cast<sal_uLong>(1));
540 OUString sLorem = pTxtNode->GetTxt();
541 CPPUNIT_ASSERT(sLorem == "Lorem");
543 const SwRedlineTbl& rTbl = m_pDoc->GetRedlineTbl();
545 SwNodes& rNds = m_pDoc->GetNodes();
546 CPPUNIT_ASSERT(rTbl.size() == 1);
548 SwNodeIndex* pNodeIdx = rTbl[0]->GetContentIdx();
549 CPPUNIT_ASSERT(pNodeIdx);
551 pTxtNode = rNds[ pNodeIdx->GetIndex() + 1 ]->GetTxtNode(); //first deleted txtnode
552 CPPUNIT_ASSERT(pTxtNode);
554 OUString sIpsum = pTxtNode->GetTxt();
555 CPPUNIT_ASSERT(sIpsum == " ipsum");
557 aDocStat.Reset();
558 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len()); //word-counting the text should only count the non-deleted text, and this whole chunk should be ignored
559 CPPUNIT_ASSERT_EQUAL(aDocStat.nWord, static_cast<sal_uLong>(0));
560 CPPUNIT_ASSERT_EQUAL(aDocStat.nChar, static_cast<sal_uLong>(0));
563 //See https://bugs.freedesktop.org/show_bug.cgi?id=38983
565 SwDocStat aDocStat;
567 OUString sTemplate("ThisXis a test.");
569 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
570 m_pDoc->InsertString(aPaM, sTemplate.replace('X', ' '));
571 pTxtNode = aPaM.GetNode()->GetTxtNode();
572 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
573 CPPUNIT_ASSERT(aDocStat.nWord == 4 &&
574 aDocStat.nCharExcludingSpaces == 12 &&
575 aDocStat.nChar == 15);
576 aDocStat.Reset();
578 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
579 m_pDoc->InsertString(aPaM, sTemplate.replaceAll(OUString('X'), OUString(" = ")));
580 pTxtNode = aPaM.GetNode()->GetTxtNode();
581 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
582 CPPUNIT_ASSERT(aDocStat.nWord == 5 &&
583 aDocStat.nCharExcludingSpaces == 13 &&
584 aDocStat.nChar == 17);
585 aDocStat.Reset();
587 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
588 m_pDoc->InsertString(aPaM, sTemplate.replaceAll(OUString('X'), OUString(" _ ")));
589 pTxtNode = aPaM.GetNode()->GetTxtNode();
590 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
591 CPPUNIT_ASSERT(aDocStat.nWord == 5 &&
592 aDocStat.nCharExcludingSpaces == 13 &&
593 aDocStat.nChar == 17);
594 aDocStat.Reset();
596 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
597 m_pDoc->InsertString(aPaM, sTemplate.replaceAll(OUString('X'), OUString(" -- ")));
598 pTxtNode = aPaM.GetNode()->GetTxtNode();
599 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
600 CPPUNIT_ASSERT(aDocStat.nWord == 5 &&
601 aDocStat.nCharExcludingSpaces == 14 &&
602 aDocStat.nChar == 18);
603 aDocStat.Reset();
605 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
606 m_pDoc->InsertString(aPaM, sTemplate.replace('X', '_'));
607 pTxtNode = aPaM.GetNode()->GetTxtNode();
608 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
609 CPPUNIT_ASSERT(aDocStat.nWord == 3 &&
610 aDocStat.nCharExcludingSpaces == 13 &&
611 aDocStat.nChar == 15);
612 aDocStat.Reset();
614 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
615 m_pDoc->InsertString(aPaM, sTemplate.replace('X', '-'));
616 pTxtNode = aPaM.GetNode()->GetTxtNode();
617 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
618 CPPUNIT_ASSERT(aDocStat.nWord == 3 &&
619 aDocStat.nCharExcludingSpaces == 13 &&
620 aDocStat.nChar == 15);
621 aDocStat.Reset();
623 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
624 m_pDoc->InsertString(aPaM, sTemplate.replace('X', 0x2012));
625 pTxtNode = aPaM.GetNode()->GetTxtNode();
626 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
627 CPPUNIT_ASSERT(aDocStat.nWord == 3 &&
628 aDocStat.nCharExcludingSpaces == 13 &&
629 aDocStat.nChar == 15);
630 aDocStat.Reset();
632 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
633 m_pDoc->InsertString(aPaM, sTemplate.replace('X', 0x2015));
634 pTxtNode = aPaM.GetNode()->GetTxtNode();
635 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
636 CPPUNIT_ASSERT(aDocStat.nWord == 3 &&
637 aDocStat.nCharExcludingSpaces == 13 &&
638 aDocStat.nChar == 15);
639 aDocStat.Reset();
641 //But default configuration should, msword-alike treak emdash
642 //and endash as word separators for word-counting
643 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
644 m_pDoc->InsertString(aPaM, sTemplate.replace('X', 0x2013));
645 pTxtNode = aPaM.GetNode()->GetTxtNode();
646 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
647 CPPUNIT_ASSERT(aDocStat.nWord == 4 &&
648 aDocStat.nCharExcludingSpaces == 13 &&
649 aDocStat.nChar == 15);
650 aDocStat.Reset();
652 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
653 m_pDoc->InsertString(aPaM, sTemplate.replace('X', 0x2014));
654 pTxtNode = aPaM.GetNode()->GetTxtNode();
655 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
656 CPPUNIT_ASSERT(aDocStat.nWord == 4 &&
657 aDocStat.nCharExcludingSpaces == 13 &&
658 aDocStat.nChar == 15);
659 aDocStat.Reset();
661 const sal_Unicode aChunk[] = {' ', 0x2013, ' '};
662 OUString sChunk(aChunk, SAL_N_ELEMENTS(aChunk));
663 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
664 m_pDoc->InsertString(aPaM, sTemplate.replaceAll(OUString('X'), sChunk));
665 pTxtNode = aPaM.GetNode()->GetTxtNode();
666 pTxtNode->CountWords(aDocStat, 0, pTxtNode->Len());
667 CPPUNIT_ASSERT(aDocStat.nWord == 4 &&
668 aDocStat.nCharExcludingSpaces == 13 &&
669 aDocStat.nChar == 17);
670 aDocStat.Reset();
674 //See https://bugs.freedesktop.org/show_bug.cgi?id=40599
675 void SwDocTest::testGraphicAnchorDeletion()
677 CPPUNIT_ASSERT_MESSAGE("Expected initial 0 count", m_pDoc->GetDocStat().nChar == 0);
679 SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
680 SwPaM aPaM(aIdx);
682 m_pDoc->InsertString(aPaM, OUString("Paragraph 1"));
683 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
685 m_pDoc->InsertString(aPaM, OUString("graphic anchor>><<graphic anchor"));
686 SwNodeIndex nPara2 = aPaM.GetPoint()->nNode;
687 m_pDoc->AppendTxtNode(*aPaM.GetPoint());
689 m_pDoc->InsertString(aPaM, OUString("Paragraph 3"));
691 aPaM.GetPoint()->nNode = nPara2;
692 aPaM.GetPoint()->nContent.Assign(aPaM.GetCntntNode(), RTL_CONSTASCII_LENGTH("graphic anchor>>"));
694 //Insert a graphic at X of >>X<< in paragraph 2
695 SfxItemSet aFlySet(m_pDoc->GetAttrPool(), RES_FRMATR_BEGIN, RES_FRMATR_END-1);
696 SwFmtAnchor aAnchor(FLY_AS_CHAR);
697 aAnchor.SetAnchor(aPaM.GetPoint());
698 aFlySet.Put(aAnchor);
699 SwFlyFrmFmt *pFrame = m_pDoc->Insert(aPaM, OUString(), OUString(), NULL, &aFlySet, NULL, NULL);
700 CPPUNIT_ASSERT_MESSAGE("Expected frame", pFrame != NULL);
702 CPPUNIT_ASSERT_MESSAGE("Should be 1 graphic", m_pDoc->GetFlyCount(FLYCNTTYPE_GRF) == 1);
704 //Delete >X<
705 aPaM.GetPoint()->nNode = nPara2;
706 aPaM.GetPoint()->nContent.Assign(aPaM.GetCntntNode(),
707 RTL_CONSTASCII_LENGTH("graphic anchor>><")+1);
708 aPaM.SetMark();
709 aPaM.GetPoint()->nNode = nPara2;
710 aPaM.GetPoint()->nContent.Assign(aPaM.GetCntntNode(), RTL_CONSTASCII_LENGTH("graphic anchor>"));
711 m_pDoc->DeleteRange(aPaM);
713 #ifdef DEBUG_AS_HTML
715 SvFileStream aPasteDebug(OUString("cppunitDEBUG.html"), STREAM_WRITE|STREAM_TRUNC);
716 WriterRef xWrt;
717 GetHTMLWriter( String(), String(), xWrt );
718 SwWriter aDbgWrt( aPasteDebug, *m_pDoc );
719 aDbgWrt.Write( xWrt );
721 #endif
723 CPPUNIT_ASSERT_MESSAGE("Should be 0 graphics", m_pDoc->GetFlyCount(FLYCNTTYPE_GRF) == 0);
725 //Now, if instead we swap FLY_AS_CHAR (inline graphic) to FLY_AT_CHAR (anchored to character)
726 //and repeat the above, graphic is *not* deleted, i.e. it belongs to the paragraph, not the
727 //range to which its anchored, which is annoying.
730 static int
731 getRand(int modulus)
733 if (modulus <= 0)
734 return 0;
735 return rand() % modulus;
738 static OUString
739 getRandString()
741 OUString aText("AAAAA BBBB CCC DD E \n");
742 int s = getRand(aText.getLength());
743 int j = getRand(aText.getLength() - s);
744 OUString aRet(aText.copy(s, j));
745 if (!getRand(5))
746 aRet += OUString(sal_Unicode('\n'));
747 // fprintf (stderr, "rand string '%s'\n", OUStringToOString(aRet, RTL_TEXTENCODING_UTF8).getStr());
748 return aRet;
751 static SwPosition
752 getRandomPosition(SwDoc *pDoc, int /* nOffset */)
754 const SwPosition aPos(pDoc->GetNodes().GetEndOfContent());
755 sal_uLong nNodes = aPos.nNode.GetNode().GetIndex() - aPos.nNode.GetNode().StartOfSectionIndex();
756 sal_uLong n = (rand() * nNodes) / RAND_MAX;
757 SwPaM pam(aPos);
758 for (sal_uLong i = 0; i < n; ++i) {
759 pam.Move(fnMoveBackward, fnGoNode);
761 return *pam.GetPoint();
764 void SwDocTest::randomTest()
766 CPPUNIT_ASSERT_MESSAGE("SwDoc::IsRedlineOn()", !m_pDoc->IsRedlineOn());
767 RedlineMode_t modes[] = {
768 nsRedlineMode_t::REDLINE_ON,
769 nsRedlineMode_t::REDLINE_SHOW_MASK,
770 nsRedlineMode_t::REDLINE_NONE,
771 nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_MASK,
772 nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_IGNORE,
773 nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_IGNORE | nsRedlineMode_t::REDLINE_SHOW_MASK,
774 nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_INSERT,
775 nsRedlineMode_t::REDLINE_ON | nsRedlineMode_t::REDLINE_SHOW_DELETE
777 static const char *authors[] = {
778 "Jim", "Bob", "JimBobina", "Helga", "Gertrude", "Spagna", "Hurtleweed"
781 for( sal_uInt16 rlm = 0; rlm < SAL_N_ELEMENTS(modes); rlm++ )
783 m_pDoc->ClearDoc();
785 // setup redlining
786 m_pDoc->SetRedlineMode(modes[rlm]);
787 SW_MOD()->SetRedlineAuthor(OUString::createFromAscii(authors[0]));
789 for( int i = 0; i < 2000; i++ )
791 SwPaM aPam(m_pDoc->GetNodes());
792 SwCursor aCrs(getRandomPosition(m_pDoc, i/20), 0, false);
793 aCrs.SetMark();
795 switch (getRand (i < 50 ? 3 : 6)) {
796 // insert ops first
797 case 0: {
798 if (!m_pDoc->InsertString(aCrs, getRandString())) {
799 // fprintf (stderr, "failed to insert string !\n");
801 break;
803 case 1:
804 break;
805 case 2: { // switch author
806 int a = getRand(SAL_N_ELEMENTS(authors));
807 SW_MOD()->SetRedlineAuthor(OUString::createFromAscii(authors[a]));
808 break;
811 // movement / deletion ops later
812 case 3: // deletion
813 switch (getRand(6)) {
814 case 0:
815 m_pDoc->DelFullPara(aCrs);
816 break;
817 case 1:
818 m_pDoc->DeleteRange(aCrs);
819 break;
820 case 2:
821 m_pDoc->DeleteAndJoin(aCrs, !!getRand(1));
822 break;
823 case 3:
824 default:
825 m_pDoc->Overwrite(aCrs, getRandString());
826 break;
828 break;
829 case 4: { // movement
830 IDocumentContentOperations::SwMoveFlags nFlags =
831 (IDocumentContentOperations::SwMoveFlags)
832 (getRand(1) ? // FIXME: puterb this more ?
833 IDocumentContentOperations::DOC_MOVEDEFAULT :
834 IDocumentContentOperations::DOC_MOVEALLFLYS |
835 IDocumentContentOperations::DOC_CREATEUNDOOBJ |
836 IDocumentContentOperations::DOC_MOVEREDLINES |
837 IDocumentContentOperations::DOC_NO_DELFRMS);
838 SwPosition aTo(getRandomPosition(m_pDoc, i/10));
839 m_pDoc->MoveRange(aCrs, aTo, nFlags);
840 break;
843 case 5:
844 break;
846 // undo / redo ?
847 default:
848 break;
852 // Debug / verify the produced document has real content
853 #if 0
854 OStringBuffer aBuffer("nodes-");
855 aBuffer.append(sal_Int32(rlm));
856 aBuffer.append(".xml");
858 xmlTextWriterPtr writer;
859 writer = xmlNewTextWriterFilename( aBuffer.makeStringAndClear().getStr(), 0 );
860 xmlTextWriterStartDocument( writer, NULL, NULL, NULL );
861 m_pDoc->dumpAsXml(writer);
862 xmlTextWriterEndDocument( writer );
863 xmlFreeTextWriter( writer );
864 #endif
868 void SwDocTest::testFdo57938()
870 SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
871 SwPaM aPaM(aIdx);
873 // Insert "atest" and create a fieldmark around "test".
874 OUString aTest("atest");
875 m_pDoc->InsertString(aPaM, aTest);
876 aPaM.SetMark();
877 aPaM.GetPoint()->nContent = 1;
878 IDocumentMarkAccess* pMarksAccess = m_pDoc->getIDocumentMarkAccess();
879 pMarksAccess->makeFieldBookmark(aPaM, "", ODF_COMMENTRANGE);
880 aPaM.GetPoint()->nContent = 0;
881 aPaM.GetMark()->nContent = 1;
882 // The problem was that "a" was considered read-only, so could not be deleted.
883 CPPUNIT_ASSERT_EQUAL(false, aPaM.HasReadonlySel(false));
886 void SwDocTest::testFdo59573()
888 SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
889 SwPaM aPaM(aIdx);
891 // Insert "abc" and create a fieldmark around "b".
892 OUString aTest("abc");
893 m_pDoc->InsertString(aPaM, aTest);
894 aPaM.SetMark();
895 aPaM.GetPoint()->nContent = 1;
896 aPaM.GetMark()->nContent = 2;
897 IDocumentMarkAccess* pMarksAccess = m_pDoc->getIDocumentMarkAccess();
898 pMarksAccess->makeFieldBookmark(aPaM, "", ODF_COMMENTRANGE);
899 aPaM.GetPoint()->nContent = 4;
900 aPaM.GetMark()->nContent = 4;
901 // The problem was that the position after the fieldmark end and before the
902 // annotation anchor wasn't read-only.
903 CPPUNIT_ASSERT_EQUAL(true, aPaM.HasReadonlySel(false));
906 static OUString
907 translitTest(SwDoc & rDoc, SwPaM & rPaM, sal_uInt32 const nType)
909 utl::TransliterationWrapper aTrans(
910 ::comphelper::getProcessComponentContext(), nType);
911 rDoc.TransliterateText(rPaM, aTrans);
912 return rPaM.GetTxt();
915 void SwDocTest::testTransliterate()
917 // just some simple test to see if it's totally broken
918 SwNodeIndex aIdx(m_pDoc->GetNodes().GetEndOfContent(), -1);
919 SwPaM aPaM(aIdx);
920 m_pDoc->InsertString(aPaM, OUString("foobar"));
921 aPaM.SetMark();
922 aPaM.GetPoint()->nContent = 0;
923 CPPUNIT_ASSERT_EQUAL(String("foobar"), aPaM.GetTxt());
925 CPPUNIT_ASSERT_EQUAL(OUString("FOOBAR"),
926 translitTest(*m_pDoc, aPaM,
927 i18n::TransliterationModules_LOWERCASE_UPPERCASE));
928 CPPUNIT_ASSERT_EQUAL(OUString("Foobar"),
929 translitTest(*m_pDoc, aPaM,
930 i18n::TransliterationModulesExtra::TITLE_CASE));
931 CPPUNIT_ASSERT_EQUAL(OUString("fOOBAR"),
932 translitTest(*m_pDoc, aPaM,
933 i18n::TransliterationModulesExtra::TOGGLE_CASE));
934 CPPUNIT_ASSERT_EQUAL(OUString("foobar"),
935 translitTest(*m_pDoc, aPaM,
936 i18n::TransliterationModules_UPPERCASE_LOWERCASE));
937 CPPUNIT_ASSERT_EQUAL(OUString("Foobar"),
938 translitTest(*m_pDoc, aPaM,
939 i18n::TransliterationModulesExtra::SENTENCE_CASE));
940 CPPUNIT_ASSERT_EQUAL(OUString("Foobar"),
941 translitTest(*m_pDoc, aPaM,
942 i18n::TransliterationModules_HIRAGANA_KATAKANA));
946 void SwDocTest::setUp()
948 BootstrapFixture::setUp();
950 SwGlobals::ensure();
951 m_pDoc = new SwDoc;
952 m_xDocShRef = new SwDocShell(m_pDoc, SFX_CREATE_MODE_EMBEDDED);
953 m_xDocShRef->DoInitNew(0);
956 void SwDocTest::tearDown()
958 m_xDocShRef.Clear();
959 delete m_pDoc;
961 BootstrapFixture::tearDown();
964 CPPUNIT_TEST_SUITE_REGISTRATION(SwDocTest);
966 CPPUNIT_PLUGIN_IMPLEMENT();
968 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */