So that e.g. parseDump() in case of an incorrect xpath does not just plain give an equality assertion failure, which on its own is confusing. Change-Id: Ib9fc2b4f6720b5c472773afbe8dc18cd8961465c
691 lines
28 KiB
C++
691 lines
28 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
/*
|
|
* This file is part of the LibreOffice project.
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
|
|
#include <com/sun/star/frame/Desktop.hpp>
|
|
#include <com/sun/star/packages/zip/ZipFileAccess.hpp>
|
|
#include <com/sun/star/style/XStyleFamiliesSupplier.hpp>
|
|
#include <com/sun/star/style/XAutoStylesSupplier.hpp>
|
|
#include <com/sun/star/style/XAutoStyleFamily.hpp>
|
|
#include <com/sun/star/text/XPageCursor.hpp>
|
|
#include <com/sun/star/text/XTextDocument.hpp>
|
|
#include <com/sun/star/text/XTextRange.hpp>
|
|
#include <com/sun/star/text/XTextTable.hpp>
|
|
#include <com/sun/star/text/XTextViewCursorSupplier.hpp>
|
|
#include <com/sun/star/table/XCell.hpp>
|
|
#include <com/sun/star/table/BorderLine2.hpp>
|
|
|
|
#include <test/bootstrapfixture.hxx>
|
|
#include <unotest/macros_test.hxx>
|
|
#include <unotools/ucbstreamhelper.hxx>
|
|
#include <rtl/strbuf.hxx>
|
|
#include <rtl/ustrbuf.hxx>
|
|
#include <comphelper/processfactory.hxx>
|
|
#include <unotools/tempfile.hxx>
|
|
|
|
#include <unotxdoc.hxx>
|
|
#include <docsh.hxx>
|
|
#include <doc.hxx>
|
|
#include <rootfrm.hxx>
|
|
|
|
#include <libxml/xmlwriter.h>
|
|
#include <libxml/xpath.h>
|
|
#include <libxml/xpathInternals.h>
|
|
#include <libxml/parserInternals.h>
|
|
|
|
using namespace com::sun::star;
|
|
|
|
#define DEFAULT_STYLE "Default Style"
|
|
#define EMU_TO_MM100(EMU) (EMU / 360)
|
|
|
|
/**
|
|
* Macro to declare a new test (with full round-trip. To test
|
|
* import only use the DECLARE_SW_IMPORT_TEST macro instead).
|
|
* In order to add a new test, one only needs to use this macro
|
|
* and then specify the test content, like this:
|
|
*
|
|
* DECLARE_SW_ROUNDTRIP_TEST(MyTest, "myfilename.docx", Test)
|
|
* {
|
|
* CPPUNIT_ASSERT_EQUAL(blabla);
|
|
* }
|
|
*
|
|
*/
|
|
#define DECLARE_SW_ROUNDTRIP_TEST(TestName, filename, BaseClass) \
|
|
class TestName : public BaseClass { \
|
|
public:\
|
|
CPPUNIT_TEST_SUITE(TestName); \
|
|
CPPUNIT_TEST(Import); \
|
|
CPPUNIT_TEST(Import_Export_Import); \
|
|
CPPUNIT_TEST_SUITE_END(); \
|
|
\
|
|
void Import() { \
|
|
executeImportTest(filename);\
|
|
}\
|
|
void Import_Export_Import() {\
|
|
executeImportExportImportTest(filename);\
|
|
}\
|
|
void verify();\
|
|
}; \
|
|
CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
|
|
void TestName::verify()
|
|
|
|
#define DECLARE_SW_IMPORT_TEST(TestName, filename, BaseClass) \
|
|
class TestName : public BaseClass { \
|
|
public:\
|
|
CPPUNIT_TEST_SUITE(TestName); \
|
|
CPPUNIT_TEST(Import); \
|
|
CPPUNIT_TEST_SUITE_END(); \
|
|
\
|
|
void Import() { \
|
|
executeImportTest(filename);\
|
|
}\
|
|
void verify();\
|
|
}; \
|
|
CPPUNIT_TEST_SUITE_REGISTRATION(TestName); \
|
|
void TestName::verify()
|
|
|
|
/// Base class for filter tests loading or roundtriping a document, then asserting the document model.
|
|
class SwModelTestBase : public test::BootstrapFixture, public unotest::MacrosTest
|
|
{
|
|
public:
|
|
SwModelTestBase(const char* pTestDocumentPath = "", const char* pFilter = "")
|
|
: mpXmlBuffer(0),
|
|
mpTestDocumentPath(pTestDocumentPath),
|
|
mpFilter(pFilter),
|
|
m_nStartTime(0),
|
|
m_bExported(false)
|
|
{
|
|
}
|
|
|
|
~SwModelTestBase()
|
|
{
|
|
}
|
|
|
|
virtual void setUp()
|
|
{
|
|
test::BootstrapFixture::setUp();
|
|
|
|
mxDesktop.set( com::sun::star::frame::Desktop::create(comphelper::getComponentContext(getMultiServiceFactory())) );
|
|
}
|
|
|
|
virtual void tearDown()
|
|
{
|
|
if (mxComponent.is())
|
|
mxComponent->dispose();
|
|
|
|
test::BootstrapFixture::tearDown();
|
|
}
|
|
|
|
protected:
|
|
/**
|
|
* Helper func used by each unit test to test the 'import' code.
|
|
* (Loads the requested file and then calls 'verify' method)
|
|
*/
|
|
void executeImportTest(const char* filename)
|
|
{
|
|
// If the testcase is stored in some other format, it's pointless to test.
|
|
if (mustTestImportOf(filename))
|
|
{
|
|
header();
|
|
preTest(filename);
|
|
load(mpTestDocumentPath, filename);
|
|
postTest(filename);
|
|
verify();
|
|
finish();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Helper func used by each unit test to test the 'export' code.
|
|
* (Loads the requested file, save it to temp file, load the
|
|
* temp file and then calls 'verify' method)
|
|
*/
|
|
void executeImportExportImportTest(const char* filename)
|
|
{
|
|
header();
|
|
preTest(filename);
|
|
load(mpTestDocumentPath, filename);
|
|
reload(mpFilter, filename);
|
|
postTest(filename);
|
|
verify();
|
|
finish();
|
|
}
|
|
|
|
/**
|
|
* Function overloaded by unit test. See DECLARE_SW_*_TEST macros
|
|
*/
|
|
virtual void verify()
|
|
{
|
|
CPPUNIT_FAIL( "verify method must be overridden" );
|
|
}
|
|
|
|
/**
|
|
* Override this function if interested in skipping import test for this file
|
|
*/
|
|
virtual bool mustTestImportOf(const char* /* filename */) const
|
|
{
|
|
return true;
|
|
}
|
|
/**
|
|
* Override this function if some special filename-specific setup is needed
|
|
*/
|
|
virtual void preTest(const char* /*filename*/)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Override this function if some special filename-specific teardown is needed
|
|
*/
|
|
virtual void postTest(const char* /*filename*/)
|
|
{
|
|
}
|
|
|
|
/**
|
|
* Override this function if not calcing layout is needed
|
|
*/
|
|
virtual bool mustCalcLayoutOf(const char* /*filename*/)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
void dumpLayout()
|
|
{
|
|
// create the xml writer
|
|
mpXmlBuffer = xmlBufferCreate();
|
|
xmlTextWriterPtr pXmlWriter = xmlNewTextWriterMemory(mpXmlBuffer, 0);
|
|
xmlTextWriterStartDocument(pXmlWriter, NULL, NULL, NULL);
|
|
|
|
// create the dump
|
|
SwXTextDocument* pTxtDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
|
|
SwDoc* pDoc = pTxtDoc->GetDocShell()->GetDoc();
|
|
SwRootFrm* pLayout = pDoc->GetCurrentLayout();
|
|
pLayout->dumpAsXml(pXmlWriter);
|
|
|
|
// delete xml writer
|
|
xmlTextWriterEndDocument(pXmlWriter);
|
|
xmlFreeTextWriter(pXmlWriter);
|
|
}
|
|
|
|
void calcLayout()
|
|
{
|
|
SwXTextDocument* pTxtDoc = dynamic_cast<SwXTextDocument *>(mxComponent.get());
|
|
SwDoc* pDoc = pTxtDoc->GetDocShell()->GetDoc();
|
|
pDoc->GetCurrentViewShell()->CalcLayout();
|
|
}
|
|
|
|
protected:
|
|
/// Get the length of the whole document.
|
|
int getLength()
|
|
{
|
|
uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(), uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
|
|
OUStringBuffer aBuf;
|
|
while (xParaEnum->hasMoreElements())
|
|
{
|
|
uno::Reference<container::XEnumerationAccess> xRangeEnumAccess(xParaEnum->nextElement(), uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumeration> xRangeEnum = xRangeEnumAccess->createEnumeration();
|
|
while (xRangeEnum->hasMoreElements())
|
|
{
|
|
uno::Reference<text::XTextRange> xRange(xRangeEnum->nextElement(), uno::UNO_QUERY);
|
|
aBuf.append(xRange->getString());
|
|
}
|
|
}
|
|
return aBuf.getLength();
|
|
}
|
|
|
|
/// Get a family of styles, see com.sun.star.style.StyleFamilies for possible values.
|
|
uno::Reference<container::XNameAccess> getStyles(const OUString& aFamily)
|
|
{
|
|
uno::Reference<style::XStyleFamiliesSupplier> xStyleFamiliesSupplier(mxComponent, uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamilies(xStyleFamiliesSupplier->getStyleFamilies(), uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xStyleFamily(xStyleFamilies->getByName(aFamily), uno::UNO_QUERY);
|
|
return xStyleFamily;
|
|
}
|
|
|
|
/// Get a family of auto styles, see com.sun.star.style.StyleFamilies for possible values.
|
|
uno::Reference<style::XAutoStyleFamily> getAutoStyles(const OUString& aFamily)
|
|
{
|
|
uno::Reference< style::XAutoStylesSupplier > xAutoStylesSupplier(mxComponent, uno::UNO_QUERY);
|
|
uno::Reference< style::XAutoStyles > xAutoStyles(xAutoStylesSupplier->getAutoStyles());
|
|
uno::Reference< style::XAutoStyleFamily > xAutoStyleFamily(xAutoStyles->getByName(aFamily), uno::UNO_QUERY);
|
|
return xAutoStyleFamily;
|
|
}
|
|
|
|
/**
|
|
* Extract a value from the layout dump using an XPath expression and an attribute name.
|
|
*
|
|
* If the attribute is omitted, the text of the node is returned.
|
|
*/
|
|
OUString parseDump(const OString& aXPath, const OString& aAttribute = OString())
|
|
{
|
|
if (!mpXmlBuffer)
|
|
dumpLayout();
|
|
|
|
xmlDocPtr pXmlDoc = xmlParseMemory((const char*)xmlBufferContent(mpXmlBuffer), xmlBufferLength(mpXmlBuffer));;
|
|
|
|
xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pXmlDoc);
|
|
xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(aXPath.getStr()), pXmlXpathCtx);
|
|
xmlNodeSetPtr pXmlNodes = pXmlXpathObj->nodesetval;
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("parsing dump failed", 1, xmlXPathNodeSetGetLength(pXmlNodes));
|
|
xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
|
|
OUString aRet;
|
|
if (aAttribute.getLength())
|
|
aRet = OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(aAttribute.getStr())));
|
|
else
|
|
aRet = OUString::createFromAscii((const char*)XML_GET_CONTENT(pXmlNode));
|
|
|
|
xmlFreeDoc(pXmlDoc);
|
|
|
|
return aRet;
|
|
}
|
|
|
|
template< typename T >
|
|
T getProperty( const uno::Any& obj, const OUString& name ) const
|
|
{
|
|
uno::Reference< beans::XPropertySet > properties( obj, uno::UNO_QUERY_THROW );
|
|
T data = T();
|
|
properties->getPropertyValue( name ) >>= data;
|
|
return data;
|
|
}
|
|
|
|
template< typename T >
|
|
T getProperty( const uno::Reference< uno::XInterface >& obj, const OUString& name ) const
|
|
{
|
|
uno::Reference< beans::XPropertySet > properties( obj, uno::UNO_QUERY_THROW );
|
|
T data = T();
|
|
properties->getPropertyValue( name ) >>= data;
|
|
return data;
|
|
}
|
|
|
|
bool hasProperty(const uno::Reference<uno::XInterface>& obj, const OUString& name) const
|
|
{
|
|
uno::Reference<beans::XPropertySet> properties(obj, uno::UNO_QUERY_THROW);
|
|
return properties->getPropertySetInfo()->hasPropertyByName(name);
|
|
}
|
|
|
|
/// Get number of paragraphs of the document.
|
|
int getParagraphs()
|
|
{
|
|
uno::Reference<text::XTextDocument> xTextDocument(mxComponent, uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumerationAccess> xParaEnumAccess(xTextDocument->getText(), uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumeration> xParaEnum = xParaEnumAccess->createEnumeration();
|
|
int nRet = 0;
|
|
while (xParaEnum->hasMoreElements())
|
|
{
|
|
xParaEnum->nextElement();
|
|
nRet++;
|
|
}
|
|
return nRet;
|
|
}
|
|
|
|
uno::Reference<text::XTextContent> getParagraphOrTable(int number, uno::Reference<text::XText> xText = uno::Reference<text::XText>()) const
|
|
{
|
|
uno::Reference<container::XEnumerationAccess> paraEnumAccess;
|
|
if (xText.is())
|
|
paraEnumAccess.set(xText, uno::UNO_QUERY);
|
|
else
|
|
{
|
|
uno::Reference<text::XTextDocument> textDocument(mxComponent, uno::UNO_QUERY);
|
|
paraEnumAccess.set(textDocument->getText(), uno::UNO_QUERY);
|
|
}
|
|
uno::Reference<container::XEnumeration> paraEnum = paraEnumAccess->createEnumeration();
|
|
for( int i = 1;
|
|
i < number;
|
|
++i )
|
|
paraEnum->nextElement();
|
|
uno::Reference< text::XTextContent> const xElem(paraEnum->nextElement(),
|
|
uno::UNO_QUERY_THROW);
|
|
return xElem;
|
|
}
|
|
|
|
// Get paragraph (counted from 1), optionally check it contains the given text.
|
|
uno::Reference< text::XTextRange > getParagraph( int number, const OUString& content = OUString() ) const
|
|
{
|
|
uno::Reference<text::XTextRange> const xParagraph(
|
|
getParagraphOrTable(number), uno::UNO_QUERY_THROW);
|
|
if( !content.isEmpty())
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE( "paragraph does not have expected content", content, xParagraph->getString());
|
|
return xParagraph;
|
|
}
|
|
|
|
uno::Reference<text::XTextRange> getParagraphOfText(int number, uno::Reference<text::XText> xText, const OUString& content = OUString()) const
|
|
{
|
|
uno::Reference<text::XTextRange> const xParagraph(getParagraphOrTable(number, xText), uno::UNO_QUERY_THROW);
|
|
if (!content.isEmpty())
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE( "paragraph does not contain expected content", content, xParagraph->getString());
|
|
return xParagraph;
|
|
}
|
|
|
|
/// Get run (counted from 1) of a paragraph, optionally check it contains the given text.
|
|
uno::Reference<text::XTextRange> getRun(uno::Reference<text::XTextRange> xParagraph, int number, const OUString& content = OUString()) const
|
|
{
|
|
uno::Reference<container::XEnumerationAccess> xRunEnumAccess(xParagraph, uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumeration> xRunEnum = xRunEnumAccess->createEnumeration();
|
|
for (int i = 1; i < number; ++i)
|
|
xRunEnum->nextElement();
|
|
uno::Reference<text::XTextRange> xRun(xRunEnum->nextElement(), uno::UNO_QUERY);
|
|
if( !content.isEmpty())
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE( "run does not contain expected content", content, xRun->getString());
|
|
return xRun;
|
|
}
|
|
|
|
/// Get math formula string of a run.
|
|
OUString getFormula(uno::Reference<text::XTextRange> xRun) const
|
|
{
|
|
uno::Reference<container::XContentEnumerationAccess> xContentEnumAccess(xRun, uno::UNO_QUERY);
|
|
uno::Reference<container::XEnumeration> xContentEnum(xContentEnumAccess->createContentEnumeration(""), uno::UNO_QUERY);
|
|
uno::Reference<beans::XPropertySet> xFormula(xContentEnum->nextElement(), uno::UNO_QUERY);
|
|
return getProperty<OUString>(getProperty< uno::Reference<beans::XPropertySet> >(xFormula, "Model"), "Formula");
|
|
}
|
|
|
|
/// get cell of a table; table can be retrieved with getParagraphOrTable
|
|
uno::Reference<table::XCell> getCell(
|
|
uno::Reference<uno::XInterface> const& xTableIfc,
|
|
OUString const& rCell, OUString const& rContent = OUString())
|
|
{
|
|
uno::Reference<text::XTextTable> const xTable(xTableIfc,
|
|
uno::UNO_QUERY_THROW);
|
|
uno::Reference<table::XCell> const xCell(
|
|
xTable->getCellByName(rCell), uno::UNO_SET_THROW);
|
|
if (!rContent.isEmpty())
|
|
{
|
|
uno::Reference<text::XText> const xCellText(xCell,
|
|
uno::UNO_QUERY_THROW);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("cell does not contain expected content", rContent, xCellText->getString());
|
|
}
|
|
return xCell;
|
|
}
|
|
|
|
/// Get shape (counted from 1)
|
|
uno::Reference<drawing::XShape> getShape(int number)
|
|
{
|
|
uno::Reference<drawing::XDrawPageSupplier> xDrawPageSupplier(mxComponent, uno::UNO_QUERY);
|
|
uno::Reference<drawing::XDrawPage> xDrawPage = xDrawPageSupplier->getDrawPage();
|
|
uno::Reference<drawing::XShape> xShape(xDrawPage->getByIndex(number - 1), uno::UNO_QUERY);
|
|
return xShape;
|
|
}
|
|
|
|
/// Get TextFrame by name
|
|
uno::Reference<drawing::XShape> getTextFrameByName(const OUString& aName)
|
|
{
|
|
uno::Reference<text::XTextFramesSupplier> xTextFramesSupplier(mxComponent, uno::UNO_QUERY);
|
|
uno::Reference<container::XNameAccess> xNameAccess(xTextFramesSupplier->getTextFrames(), uno::UNO_QUERY);
|
|
uno::Reference<drawing::XShape> xShape(xNameAccess->getByName(aName), uno::UNO_QUERY);
|
|
return xShape;
|
|
}
|
|
|
|
void header()
|
|
{
|
|
std::cout << "File tested,Execution Time (ms)" << std::endl;
|
|
}
|
|
|
|
void load(const char* pDir, const char* pName)
|
|
{
|
|
if (mxComponent.is())
|
|
mxComponent->dispose();
|
|
// Output name early, so in the case of a hang, the name of the hanging input file is visible.
|
|
std::cout << pName << ",";
|
|
m_nStartTime = osl_getGlobalTimer();
|
|
mxComponent = loadFromDesktop(getURLFromSrc(pDir) + OUString::createFromAscii(pName), "com.sun.star.text.TextDocument");
|
|
if (mustCalcLayoutOf(pName))
|
|
calcLayout();
|
|
}
|
|
|
|
void reload(const char* pFilter, const char* filename)
|
|
{
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
uno::Sequence<beans::PropertyValue> aArgs(1);
|
|
aArgs[0].Name = "FilterName";
|
|
OUString aFilterName = OUString::createFromAscii(pFilter);
|
|
aArgs[0].Value <<= aFilterName;
|
|
m_aTempFile.EnableKillingFile();
|
|
xStorable->storeToURL(m_aTempFile.GetURL(), aArgs);
|
|
uno::Reference<lang::XComponent> xComponent(xStorable, uno::UNO_QUERY);
|
|
xComponent->dispose();
|
|
m_bExported = true;
|
|
mxComponent = loadFromDesktop(m_aTempFile.GetURL(), "com.sun.star.text.TextDocument");
|
|
if(aFilterName == "Office Open XML Text")
|
|
{
|
|
// too many validation errors right now
|
|
// validate(m_aTempFile.GetFileName(), test::OOXML);
|
|
}
|
|
else if(aFilterName == "writer8")
|
|
{
|
|
// still a few validation errors
|
|
// validate(m_aTempFile.GetFileName(), test::ODF);
|
|
}
|
|
|
|
if (mpXmlBuffer)
|
|
{
|
|
xmlBufferFree(mpXmlBuffer);
|
|
mpXmlBuffer = 0;
|
|
}
|
|
if (mustCalcLayoutOf(filename))
|
|
calcLayout();
|
|
}
|
|
|
|
/// Save the loaded document to a tempfile. Can be used to check the resulting docx/odt directly as a ZIP file.
|
|
void save(const OUString& aFilter, utl::TempFile& rTempFile)
|
|
{
|
|
rTempFile.EnableKillingFile();
|
|
uno::Reference<frame::XStorable> xStorable(mxComponent, uno::UNO_QUERY);
|
|
uno::Sequence<beans::PropertyValue> aFilterArgs(1);
|
|
aFilterArgs[0].Name = "FilterName";
|
|
aFilterArgs[0].Value <<= aFilter;
|
|
xStorable->storeToURL(rTempFile.GetURL(), aFilterArgs);
|
|
}
|
|
|
|
void finish()
|
|
{
|
|
sal_uInt32 nEndTime = osl_getGlobalTimer();
|
|
std::cout << (nEndTime - m_nStartTime) << std::endl;
|
|
if (mpXmlBuffer)
|
|
{
|
|
xmlBufferFree(mpXmlBuffer);
|
|
mpXmlBuffer = 0;
|
|
}
|
|
}
|
|
|
|
/// Get page count.
|
|
int getPages()
|
|
{
|
|
uno::Reference<frame::XModel> xModel(mxComponent, uno::UNO_QUERY);
|
|
uno::Reference<text::XTextViewCursorSupplier> xTextViewCursorSupplier(xModel->getCurrentController(), uno::UNO_QUERY);
|
|
uno::Reference<text::XPageCursor> xCursor(xTextViewCursorSupplier->getViewCursor(), uno::UNO_QUERY);
|
|
xCursor->jumpToLastPage();
|
|
return xCursor->getPage();
|
|
}
|
|
|
|
/**
|
|
* Given that some problem doesn't affect the result in the importer, we
|
|
* test the resulting file directly, by opening the zip file, parsing an
|
|
* xml stream, and asserting an XPath expression. This method returns the
|
|
* xml stream, so that you can do the asserting.
|
|
*/
|
|
xmlDocPtr parseExport(const OUString& rStreamName = OUString("word/document.xml"))
|
|
{
|
|
if (!m_bExported)
|
|
return 0;
|
|
|
|
// Read the XML stream we're interested in.
|
|
uno::Reference<packages::zip::XZipFileAccess2> xNameAccess = packages::zip::ZipFileAccess::createWithURL(comphelper::getComponentContext(m_xSFactory), m_aTempFile.GetURL());
|
|
uno::Reference<io::XInputStream> xInputStream(xNameAccess->getByName(rStreamName), uno::UNO_QUERY);
|
|
boost::shared_ptr<SvStream> pStream(utl::UcbStreamHelper::CreateStream(xInputStream, true));
|
|
pStream->Seek(STREAM_SEEK_TO_END);
|
|
sal_Size nSize = pStream->Tell();
|
|
pStream->Seek(0);
|
|
OStringBuffer aDocument(nSize);
|
|
char ch;
|
|
for (sal_Size i = 0; i < nSize; ++i)
|
|
{
|
|
pStream->ReadChar( ch );
|
|
aDocument.append(ch);
|
|
}
|
|
|
|
// Parse the XML.
|
|
return xmlParseMemory((const char*)aDocument.getStr(), aDocument.getLength());
|
|
}
|
|
|
|
/**
|
|
* Helper method to return nodes represented by rXPath.
|
|
*/
|
|
xmlNodeSetPtr getXPathNode(xmlDocPtr pXmlDoc, const OString& rXPath)
|
|
{
|
|
xmlXPathContextPtr pXmlXpathCtx = xmlXPathNewContext(pXmlDoc);
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w"), BAD_CAST("http://schemas.openxmlformats.org/wordprocessingml/2006/main"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("v"), BAD_CAST("urn:schemas-microsoft-com:vml"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("mc"), BAD_CAST("http://schemas.openxmlformats.org/markup-compatibility/2006"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wps"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordprocessingShape"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wpg"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordprocessingGroup"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wp"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/wordprocessingDrawing"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("wp14"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordprocessingDrawing"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("a"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/main"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("pic"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/picture"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("rels"), BAD_CAST("http://schemas.openxmlformats.org/package/2006/relationships"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("w14"), BAD_CAST("http://schemas.microsoft.com/office/word/2010/wordml"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("m"), BAD_CAST("http://schemas.openxmlformats.org/officeDocument/2006/math"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("ContentType"), BAD_CAST("http://schemas.openxmlformats.org/package/2006/content-types"));
|
|
xmlXPathRegisterNs(pXmlXpathCtx, BAD_CAST("lc"), BAD_CAST("http://schemas.openxmlformats.org/drawingml/2006/lockedCanvas"));
|
|
xmlXPathObjectPtr pXmlXpathObj = xmlXPathEvalExpression(BAD_CAST(rXPath.getStr()), pXmlXpathCtx);
|
|
return pXmlXpathObj->nodesetval;
|
|
}
|
|
|
|
/**
|
|
* Same as the assertXPath(), but don't assert: return the string instead.
|
|
*/
|
|
OUString getXPath(xmlDocPtr pXmlDoc, const OString& rXPath, const OString& rAttribute)
|
|
{
|
|
xmlNodeSetPtr pXmlNodes = getXPathNode(pXmlDoc, rXPath);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("XPath '" + rXPath + "' number of nodes is incorrect").getStr(),
|
|
1, xmlXPathNodeSetGetLength(pXmlNodes));
|
|
if (rAttribute.isEmpty())
|
|
return OUString();
|
|
xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
|
|
return OUString::createFromAscii((const char*)xmlGetProp(pXmlNode, BAD_CAST(rAttribute.getStr())));
|
|
}
|
|
|
|
/**
|
|
* Assert that rXPath exists, and returns exactly one node.
|
|
* In case rAttribute is provided, the rXPath's attribute's value must
|
|
* equal to the rExpected value.
|
|
*/
|
|
void assertXPath(xmlDocPtr pXmlDoc, const OString& rXPath, const OString& rAttribute = OString(), const OUString& rExpectedValue = OUString())
|
|
{
|
|
OUString aValue = getXPath(pXmlDoc, rXPath, rAttribute);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("Attribute '" + rAttribute + "' of '" + rXPath + "' incorrect value.").getStr(),
|
|
rExpectedValue, aValue);
|
|
}
|
|
|
|
/**
|
|
* Assert that rXPath exists, and returns exactly nNumberOfNodes nodes.
|
|
* Useful for checking that we do _not_ export some node (nNumberOfNodes == 0).
|
|
*/
|
|
void assertXPath(xmlDocPtr pXmlDoc, const OString& rXPath, int nNumberOfNodes)
|
|
{
|
|
xmlNodeSetPtr pXmlNodes = getXPathNode(pXmlDoc, rXPath);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("XPath '" + rXPath + "' number of nodes is incorrect").getStr(),
|
|
nNumberOfNodes, xmlXPathNodeSetGetLength(pXmlNodes));
|
|
}
|
|
|
|
/**
|
|
* Assert that rXPath exists, and its content equals rContent.
|
|
*/
|
|
void assertXPathContent(xmlDocPtr pXmlDoc, const OString& rXPath, const OUString& rContent)
|
|
{
|
|
xmlNodeSetPtr pXmlNodes = getXPathNode(pXmlDoc, rXPath);
|
|
|
|
CPPUNIT_ASSERT_MESSAGE(OString("XPath '" + rXPath + "' not found").getStr(),
|
|
xmlXPathNodeSetGetLength(pXmlNodes) > 0);
|
|
|
|
xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
|
|
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE("XPath contents of child does not match", rContent,
|
|
OUString::createFromAscii((const char*)((pXmlNode->children[0]).content)));
|
|
}
|
|
|
|
/**
|
|
* Assert that rXPath exists, and has exactly nNumberOfChildNodes child nodes.
|
|
* Useful for checking that we do have a no child nodes to a specific node (nNumberOfChildNodes == 0).
|
|
*/
|
|
void assertXPathChildren(xmlDocPtr pXmlDoc, const OString& rXPath, int nNumberOfChildNodes)
|
|
{
|
|
xmlNodeSetPtr pXmlNodes = getXPathNode(pXmlDoc, rXPath);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("XPath '" + rXPath + "' number of nodes is incorrect").getStr(),
|
|
1, xmlXPathNodeSetGetLength(pXmlNodes));
|
|
xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("XPath '" + rXPath + "' number of child-nodes is incorrect").getStr(),
|
|
nNumberOfChildNodes, (int)xmlChildElementCount(pXmlNode));
|
|
}
|
|
|
|
/**
|
|
* Get the position of the child named rName of the parent node specified by rXPath.
|
|
* Useful for checking relative order of elements.
|
|
*/
|
|
int getXPathPosition(xmlDocPtr pXmlDoc, const OString& rXPath, const OUString& rChildName)
|
|
{
|
|
xmlNodeSetPtr pXmlNodes = getXPathNode(pXmlDoc, rXPath);
|
|
CPPUNIT_ASSERT_EQUAL_MESSAGE(OString("XPath '" + rXPath + "' number of nodes is incorrect").getStr(),
|
|
1,
|
|
xmlXPathNodeSetGetLength(pXmlNodes));
|
|
xmlNodePtr pXmlNode = pXmlNodes->nodeTab[0];
|
|
int nRet = 0;
|
|
for (xmlNodePtr pChild = pXmlNode->children; pChild; pChild = pChild->next)
|
|
{
|
|
if (OUString::createFromAscii((const char*)pChild->name) == rChildName)
|
|
break;
|
|
++nRet;
|
|
}
|
|
return nRet;
|
|
}
|
|
|
|
uno::Reference<lang::XComponent> mxComponent;
|
|
xmlBufferPtr mpXmlBuffer;
|
|
const char* mpTestDocumentPath;
|
|
const char* mpFilter;
|
|
|
|
template< typename T >
|
|
struct MethodEntry
|
|
{
|
|
const char* pName;
|
|
void (T::*pMethod)();
|
|
};
|
|
sal_uInt32 m_nStartTime;
|
|
utl::TempFile m_aTempFile;
|
|
bool m_bExported; ///< Does m_aTempFile already contain something useful?
|
|
};
|
|
|
|
/**
|
|
* Test whether the expected and actual borderline parameters are equal
|
|
* and assert if not.
|
|
*
|
|
* @param[in] rExpected expected borderline object
|
|
* @param[in] rActual actual borderline object
|
|
* @param[in] rSourceLine line from where the assertion is called
|
|
* Note: This method is the implementatition of CPPUNIT_ASSERT_BORDER_EQUAL, so
|
|
* use that macro instead.
|
|
**/
|
|
inline void assertBorderEqual(
|
|
const table::BorderLine2& rExpected, const table::BorderLine2& rActual,
|
|
const CppUnit::SourceLine& rSourceLine )
|
|
{
|
|
CPPUNIT_NS::assertEquals<util::Color>( rExpected.Color, rActual.Color, rSourceLine, "different Color" );
|
|
CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.InnerLineWidth, rActual.InnerLineWidth, rSourceLine, "different InnerLineWidth" );
|
|
CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.OuterLineWidth, rActual.OuterLineWidth, rSourceLine, "different OuterLineWidth" );
|
|
CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.LineDistance, rActual.LineDistance, rSourceLine, "different LineDistance" );
|
|
CPPUNIT_NS::assertEquals<sal_Int16>( rExpected.LineStyle, rActual.LineStyle, rSourceLine, "different LineStyle" );
|
|
CPPUNIT_NS::assertEquals<sal_Int32>( rExpected.LineWidth, rActual.LineWidth, rSourceLine, "different LineWidth" );
|
|
}
|
|
|
|
#define CPPUNIT_ASSERT_BORDER_EQUAL(aExpected, aActual) \
|
|
assertBorderEqual( aExpected, aActual, CPPUNIT_SOURCELINE() ) \
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|