1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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/.
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"
36 #include "docstat.hxx"
37 #include "docufld.hxx"
38 #include "fmtanchr.hxx"
41 #include "shellio.hxx"
42 #include "shellres.hxx"
44 #include "swscanner.hxx"
45 #include "swmodule.hxx"
46 #include "swtypes.hxx"
48 #include "fmtrfmrk.hxx"
50 #include "redline.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
66 virtual void tearDown();
69 void testPageDescName();
70 void testFileNameFields();
72 void testModelToViewHelper();
74 void testUserPerceivedCharCount();
75 void testGraphicAnchorDeletion();
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();
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
);
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
));
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
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);
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
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);
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);
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);
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);
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",
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",
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 );
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 );
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
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
[] = {
435 0x0046, 0x0072, 0x0065, 0x006E, 0x0063, 0x0068, 0x0020,
437 0x00AB, 0x00A0, 0x0073, 0x0061, 0x0076, 0x006F, 0x0069,
439 0x0072, 0x202f, 0x0063, 0x0061, 0x006C, 0x0063, 0x0075,
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();
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
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();
461 aFtn
.SetNumStr(OUString("banana"));
462 SwTxtAttr
* pTA
= pTxtNode
->InsertItem(aFtn
, nPos
, nPos
);
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
);
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();
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);
501 //See https://bugs.freedesktop.org/show_bug.cgi?id=46757
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
));
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()));
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");
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
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
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);
705 aPaM
.GetPoint()->nNode
= nPara2
;
706 aPaM
.GetPoint()->nContent
.Assign(aPaM
.GetCntntNode(),
707 RTL_CONSTASCII_LENGTH("graphic anchor>><")+1);
709 aPaM
.GetPoint()->nNode
= nPara2
;
710 aPaM
.GetPoint()->nContent
.Assign(aPaM
.GetCntntNode(), RTL_CONSTASCII_LENGTH("graphic anchor>"));
711 m_pDoc
->DeleteRange(aPaM
);
715 SvFileStream
aPasteDebug(OUString("cppunitDEBUG.html"), STREAM_WRITE
|STREAM_TRUNC
);
717 GetHTMLWriter( String(), String(), xWrt
);
718 SwWriter
aDbgWrt( aPasteDebug
, *m_pDoc
);
719 aDbgWrt
.Write( xWrt
);
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.
735 return rand() % modulus
;
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
));
746 aRet
+= OUString(sal_Unicode('\n'));
747 // fprintf (stderr, "rand string '%s'\n", OUStringToOString(aRet, RTL_TEXTENCODING_UTF8).getStr());
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
;
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
++ )
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);
795 switch (getRand (i
< 50 ? 3 : 6)) {
798 if (!m_pDoc
->InsertString(aCrs
, getRandString())) {
799 // fprintf (stderr, "failed to insert string !\n");
805 case 2: { // switch author
806 int a
= getRand(SAL_N_ELEMENTS(authors
));
807 SW_MOD()->SetRedlineAuthor(OUString::createFromAscii(authors
[a
]));
811 // movement / deletion ops later
813 switch (getRand(6)) {
815 m_pDoc
->DelFullPara(aCrs
);
818 m_pDoc
->DeleteRange(aCrs
);
821 m_pDoc
->DeleteAndJoin(aCrs
, !!getRand(1));
825 m_pDoc
->Overwrite(aCrs
, getRandString());
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
);
852 // Debug / verify the produced document has real content
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
);
868 void SwDocTest::testFdo57938()
870 SwNodeIndex
aIdx(m_pDoc
->GetNodes().GetEndOfContent(), -1);
873 // Insert "atest" and create a fieldmark around "test".
874 OUString
aTest("atest");
875 m_pDoc
->InsertString(aPaM
, aTest
);
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);
891 // Insert "abc" and create a fieldmark around "b".
892 OUString
aTest("abc");
893 m_pDoc
->InsertString(aPaM
, aTest
);
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));
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);
920 m_pDoc
->InsertString(aPaM
, OUString("foobar"));
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();
952 m_xDocShRef
= new SwDocShell(m_pDoc
, SFX_CREATE_MODE_EMBEDDED
);
953 m_xDocShRef
->DoInitNew(0);
956 void SwDocTest::tearDown()
961 BootstrapFixture::tearDown();
964 CPPUNIT_TEST_SUITE_REGISTRATION(SwDocTest
);
966 CPPUNIT_PLUGIN_IMPLEMENT();
968 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */