Implements XCopyableBrowseNode on the ScriptFile class so that all script providers based on the single-file provider can support copying. Change-Id: I14aa057ac432ff80e4a4841fbe5c19be082aa099 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/208161 Tested-by: Jenkins Reviewed-by: Neil Roberts <nroberts@libreoffice.org>
457 lines
18 KiB
C++
457 lines
18 KiB
C++
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
|
||
/*
|
||
* 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/beans/XPropertySet.hpp>
|
||
#include <com/sun/star/container/XContentEnumerationAccess.hpp>
|
||
#include <com/sun/star/lang/XSingleComponentFactory.hpp>
|
||
#include <com/sun/star/script/browse/BrowseNodeTypes.hpp>
|
||
#include <com/sun/star/script/browse/XBrowseNode.hpp>
|
||
#include <com/sun/star/script/browse/XCopyableBrowseNode.hpp>
|
||
#include <com/sun/star/script/browse/XCreatableBrowseNode.hpp>
|
||
#include <com/sun/star/script/browse/XEditableBrowseNode.hpp>
|
||
#include <com/sun/star/script/provider/ScriptURIHelper.hpp>
|
||
#include <com/sun/star/script/provider/XScriptProvider.hpp>
|
||
#include <com/sun/star/script/provider/theMasterScriptProviderFactory.hpp>
|
||
#include <com/sun/star/text/XText.hpp>
|
||
#include <com/sun/star/text/XTextDocument.hpp>
|
||
#include <com/sun/star/ucb/SimpleFileAccess.hpp>
|
||
#include <com/sun/star/ucb/UniversalContentBroker.hpp>
|
||
#include <test/unoapi_test.hxx>
|
||
|
||
namespace
|
||
{
|
||
class JsProvTest : public UnoApiTest
|
||
{
|
||
public:
|
||
JsProvTest()
|
||
: UnoApiTest(u"/scripting/qa/extras"_ustr)
|
||
{
|
||
}
|
||
|
||
void setUp() override;
|
||
void tearDown() override;
|
||
|
||
private:
|
||
css::uno::Reference<css::ucb::XSimpleFileAccess3> m_xFileAccess;
|
||
css::uno::Reference<css::script::provider::XScriptURIHelper> m_xUriHelper;
|
||
css::uno::Reference<css::script::provider::XScriptProvider> m_xScriptProvider;
|
||
|
||
css::uno::Reference<css::script::provider::XScriptProvider> createScriptProvider();
|
||
void createDummyMacro(const OUString& sDirectoryUri);
|
||
|
||
void testEditableCreatable();
|
||
void testCreate();
|
||
void testRun();
|
||
void testUnrelatedFile();
|
||
void testFolder();
|
||
void testCopyable();
|
||
|
||
CPPUNIT_TEST_SUITE(JsProvTest);
|
||
CPPUNIT_TEST(testEditableCreatable);
|
||
CPPUNIT_TEST(testCreate);
|
||
CPPUNIT_TEST(testRun);
|
||
CPPUNIT_TEST(testUnrelatedFile);
|
||
CPPUNIT_TEST(testFolder);
|
||
CPPUNIT_TEST(testCopyable);
|
||
CPPUNIT_TEST_SUITE_END();
|
||
};
|
||
|
||
void JsProvTest::setUp()
|
||
{
|
||
UnoApiTest::setUp();
|
||
|
||
m_xFileAccess = css::ucb::SimpleFileAccess::create(m_xContext);
|
||
|
||
// The ExpandContentProvider is needed for ScriptURIHelper to work
|
||
css::uno::Reference<css::ucb::XUniversalContentBroker> xUcb
|
||
= css::ucb::UniversalContentBroker::create(m_xContext);
|
||
css::uno::Reference<css::ucb::XContentProvider> xExpandProvider(
|
||
m_xSFactory->createInstance(u"com.sun.star.ucb.ExpandContentProvider"_ustr),
|
||
css::uno::UNO_QUERY_THROW);
|
||
xUcb->registerContentProvider(xExpandProvider, u"vnd.sun.star.expand"_ustr, true);
|
||
|
||
m_xUriHelper = css::script::provider::ScriptURIHelper::create(getComponentContext(),
|
||
u"JavaScript"_ustr, u"user"_ustr);
|
||
|
||
m_xScriptProvider = createScriptProvider();
|
||
}
|
||
|
||
void JsProvTest::tearDown()
|
||
{
|
||
m_xScriptProvider.clear();
|
||
|
||
// Delete the JavaScript scripting directory so that each test can have a clean slate
|
||
if (m_xFileAccess->isFolder(m_xUriHelper->getRootStorageURI()))
|
||
m_xFileAccess->kill(m_xUriHelper->getRootStorageURI());
|
||
|
||
m_xFileAccess.clear();
|
||
m_xUriHelper.clear();
|
||
|
||
UnoApiTest::tearDown();
|
||
}
|
||
|
||
css::uno::Reference<css::script::provider::XScriptProvider> JsProvTest::createScriptProvider()
|
||
{
|
||
// Create the script provider by searching for a factory with the right implementation in case
|
||
// there are multiple JavaScript providers
|
||
css::uno::Reference<css::container::XContentEnumerationAccess> xEnumAccess(
|
||
m_xFactory, css::uno::UNO_QUERY_THROW);
|
||
css::uno::Reference<css::container::XEnumeration> xEnum = xEnumAccess->createContentEnumeration(
|
||
"com.sun.star.script.provider.ScriptProviderForJavaScript");
|
||
|
||
while (xEnum->hasMoreElements())
|
||
{
|
||
css::uno::Reference<css::lang::XSingleComponentFactory> xFactory(xEnum->nextElement(),
|
||
css::uno::UNO_QUERY_THROW);
|
||
css::uno::Reference<css::lang::XServiceInfo> xServiceInfo(xFactory,
|
||
css::uno::UNO_QUERY_THROW);
|
||
|
||
if (xServiceInfo->getImplementationName()
|
||
!= "com.sun.star.singleprov.ScriptProviderForJavaScript")
|
||
continue;
|
||
|
||
css::uno::Sequence<css::uno::Any> aArgs(1);
|
||
aArgs.getArray()[0] <<= u"user"_ustr;
|
||
|
||
css::uno::Reference<css::script::provider::XScriptProvider> xProvider(
|
||
xFactory->createInstanceWithArgumentsAndContext(aArgs, m_xContext),
|
||
css::uno::UNO_QUERY_THROW);
|
||
|
||
return xProvider;
|
||
}
|
||
|
||
CPPUNIT_FAIL("Couldn’t find com.sun.star.singleprov.ScriptProviderForJavaScript "
|
||
"implementation");
|
||
}
|
||
|
||
bool isEditable(const css::uno::Reference<css::script::browse::XBrowseNode>& xNode)
|
||
{
|
||
css::uno::Reference<css::script::browse::XEditableBrowseNode> xEditable(xNode,
|
||
css::uno::UNO_QUERY);
|
||
|
||
return xEditable.is() && xEditable->isEditableNode();
|
||
}
|
||
|
||
bool isCreatable(const css::uno::Reference<css::script::browse::XBrowseNode>& xNode)
|
||
{
|
||
css::uno::Reference<css::script::browse::XCreatableBrowseNode> xCreatable(xNode,
|
||
css::uno::UNO_QUERY);
|
||
|
||
return xCreatable.is() && xCreatable->isCreatableNode();
|
||
}
|
||
|
||
void JsProvTest::testEditableCreatable()
|
||
{
|
||
// Create a dummy macro in the script directory
|
||
createDummyMacro(m_xUriHelper->getRootStorageURI() + "/MyScript.js");
|
||
|
||
// The root node should be creatable but not editable
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xRootNode(m_xScriptProvider,
|
||
css::uno::UNO_QUERY_THROW);
|
||
CPPUNIT_ASSERT(isCreatable(xRootNode));
|
||
CPPUNIT_ASSERT(!isEditable(xRootNode));
|
||
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xBrowseNode(m_xScriptProvider,
|
||
css::uno::UNO_QUERY_THROW);
|
||
|
||
// The root node should be a container
|
||
CPPUNIT_ASSERT_EQUAL(css::script::browse::BrowseNodeTypes::CONTAINER, xBrowseNode->getType());
|
||
|
||
CPPUNIT_ASSERT_EQUAL(u"JavaScript"_ustr, xBrowseNode->getName());
|
||
|
||
// The root node should have exactly one child which is the script we created above
|
||
CPPUNIT_ASSERT(xBrowseNode->hasChildNodes());
|
||
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xBrowseNode->getChildNodes().getLength());
|
||
|
||
xBrowseNode = xBrowseNode->getChildNodes()[0];
|
||
|
||
// The next level down should be a container node to represent the module
|
||
CPPUNIT_ASSERT_EQUAL(css::script::browse::BrowseNodeTypes::CONTAINER, xBrowseNode->getType());
|
||
|
||
CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xBrowseNode->getName());
|
||
|
||
// The module should be editable but not creatable
|
||
CPPUNIT_ASSERT(!isCreatable(xBrowseNode));
|
||
CPPUNIT_ASSERT(isEditable(xBrowseNode));
|
||
|
||
// The module should have exactly one child to represent the macro
|
||
CPPUNIT_ASSERT(xBrowseNode->hasChildNodes());
|
||
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xBrowseNode->getChildNodes().getLength());
|
||
|
||
xBrowseNode = xBrowseNode->getChildNodes()[0];
|
||
|
||
// Finally we should be on a node representing the actual macro
|
||
CPPUNIT_ASSERT_EQUAL(css::script::browse::BrowseNodeTypes::SCRIPT, xBrowseNode->getType());
|
||
|
||
CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xBrowseNode->getName());
|
||
|
||
// The macro should be editable but not creatable
|
||
CPPUNIT_ASSERT(!isCreatable(xBrowseNode));
|
||
CPPUNIT_ASSERT(isEditable(xBrowseNode));
|
||
|
||
// The macro should have no children
|
||
CPPUNIT_ASSERT(!xBrowseNode->hasChildNodes());
|
||
CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xBrowseNode->getChildNodes().getLength());
|
||
|
||
// Test the macro’s URL
|
||
css::uno::Reference<css::beans::XPropertySet> xPropertySet(xBrowseNode,
|
||
css::uno::UNO_QUERY_THROW);
|
||
OUString sMacroUri;
|
||
bool bConvertResult = xPropertySet->getPropertyValue("URI") >>= sMacroUri;
|
||
CPPUNIT_ASSERT(bConvertResult);
|
||
CPPUNIT_ASSERT_EQUAL(u"vnd.sun.star.script:MyScript.js?language=JavaScript&location=user"_ustr,
|
||
sMacroUri);
|
||
}
|
||
|
||
void JsProvTest::testCreate()
|
||
{
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xBrowseNode(m_xScriptProvider,
|
||
css::uno::UNO_QUERY_THROW);
|
||
// The root node should start by having no children
|
||
CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xBrowseNode->getChildNodes().getLength());
|
||
|
||
// The root node should be creatable
|
||
CPPUNIT_ASSERT(isCreatable(xBrowseNode));
|
||
|
||
// Invoke the create method
|
||
css::uno::Reference<css::script::browse::XCreatableBrowseNode> xCreatable(
|
||
xBrowseNode, css::uno::UNO_QUERY_THROW);
|
||
xCreatable->createNode(u"My Script"_ustr);
|
||
|
||
// That should have created the file
|
||
CPPUNIT_ASSERT(m_xFileAccess->exists(m_xUriHelper->getRootStorageURI() + "/My Script.js"));
|
||
|
||
// The file should now appear as a child of the root node
|
||
CPPUNIT_ASSERT(xBrowseNode->hasChildNodes());
|
||
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xBrowseNode->getChildNodes().getLength());
|
||
|
||
CPPUNIT_ASSERT_EQUAL(u"My Script"_ustr, xBrowseNode->getChildNodes()[0]->getName());
|
||
}
|
||
|
||
void JsProvTest::createDummyMacro(const OUString& sDirectoryUri)
|
||
{
|
||
// Create a dummy macro in the given directory
|
||
css::uno::Reference<css::io::XOutputStream> xOutput
|
||
= m_xFileAccess->openFileWrite(sDirectoryUri);
|
||
|
||
// Make the script insert some text in all of the writer documents
|
||
static constexpr OString sSource
|
||
= "let context = uno.componentContext;\n"
|
||
"let desktop = uno.idl.com.sun.star.frame.theDesktop(context);\n"
|
||
"let enumeration = desktop.getComponents().createEnumeration();\n"
|
||
"while (enumeration.hasMoreElements())\n"
|
||
"{\n"
|
||
" let model = enumeration.nextElement();\n"
|
||
" model.getText().getEnd().setString(\"jsprovtest\");\n"
|
||
"}\n"_ostr;
|
||
|
||
css::uno::Sequence<sal_Int8> aSource(reinterpret_cast<const sal_Int8*>(sSource.getStr()),
|
||
sSource.getLength());
|
||
xOutput->writeBytes(aSource);
|
||
|
||
xOutput->closeOutput();
|
||
}
|
||
|
||
void JsProvTest::testRun()
|
||
{
|
||
createDummyMacro(m_xUriHelper->getRootStorageURI() + "/MyScript.js");
|
||
|
||
// Get the macro as a property set
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xBrowseNode(m_xScriptProvider,
|
||
css::uno::UNO_QUERY_THROW);
|
||
css::uno::Reference<css::beans::XPropertySet> xPropertySet(
|
||
xBrowseNode->getChildNodes()[0]->getChildNodes()[0], css::uno::UNO_QUERY_THROW);
|
||
|
||
// Get the XScript
|
||
OUString sScriptUri;
|
||
bool bConvertResult = xPropertySet->getPropertyValue("URI") >>= sScriptUri;
|
||
CPPUNIT_ASSERT(bConvertResult);
|
||
css::uno::Reference<css::script::provider::XScript> xScript
|
||
= m_xScriptProvider->getScript(sScriptUri);
|
||
CPPUNIT_ASSERT(xScript.is());
|
||
|
||
// Create a document to run the script on
|
||
loadFromURL(u"private:factory/swriter"_ustr);
|
||
|
||
// Execute it
|
||
css::uno::Sequence<css::uno::Any> aScriptParams;
|
||
css::uno::Sequence<sal_Int16> aOutParamIndex;
|
||
css::uno::Sequence<css::uno::Any> aOutParam;
|
||
xScript->invoke(aScriptParams, aOutParamIndex, aOutParam);
|
||
|
||
// Check that the text was inserted into our test document
|
||
css::uno::Reference<css::text::XTextDocument> xTextDocument(mxComponent,
|
||
css::uno::UNO_QUERY_THROW);
|
||
CPPUNIT_ASSERT_EQUAL(u"jsprovtest"_ustr, xTextDocument->getText()->getString());
|
||
}
|
||
|
||
void JsProvTest::testUnrelatedFile()
|
||
{
|
||
// Create a file with a name that shouldn’t be picked up by the JavaScript provider
|
||
OUString sFileUri = m_xUriHelper->getRootStorageURI() + "/MyScript.py";
|
||
m_xFileAccess->openFileWrite(sFileUri);
|
||
CPPUNIT_ASSERT(m_xFileAccess->exists(sFileUri));
|
||
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xBrowseNode(m_xScriptProvider,
|
||
css::uno::UNO_QUERY_THROW);
|
||
|
||
// The root node should have no children
|
||
CPPUNIT_ASSERT_EQUAL(sal_Int32(0), xBrowseNode->getChildNodes().getLength());
|
||
}
|
||
|
||
void JsProvTest::testFolder()
|
||
{
|
||
// Create a file that is nested in subfolders
|
||
m_xFileAccess->openFileWrite(m_xUriHelper->getRootStorageURI()
|
||
+ "/very/nested/file/MyScript.js");
|
||
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xBrowseNode(m_xScriptProvider,
|
||
css::uno::UNO_QUERY_THROW);
|
||
|
||
std::array<OUString, 3> aParts = { u"very"_ustr, u"nested"_ustr, u"file"_ustr };
|
||
|
||
for (const auto& sPart : aParts)
|
||
{
|
||
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xBrowseNode->getChildNodes().getLength());
|
||
|
||
xBrowseNode = xBrowseNode->getChildNodes()[0];
|
||
|
||
CPPUNIT_ASSERT_EQUAL(css::script::browse::BrowseNodeTypes::CONTAINER,
|
||
xBrowseNode->getType());
|
||
CPPUNIT_ASSERT_EQUAL(sPart, xBrowseNode->getName());
|
||
|
||
// Folders should be creatable but not editable
|
||
CPPUNIT_ASSERT(isCreatable(xBrowseNode));
|
||
CPPUNIT_ASSERT(!isEditable(xBrowseNode));
|
||
}
|
||
|
||
CPPUNIT_ASSERT_EQUAL(sal_Int32(1), xBrowseNode->getChildNodes().getLength());
|
||
xBrowseNode = xBrowseNode->getChildNodes()[0];
|
||
CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xBrowseNode->getName());
|
||
}
|
||
|
||
bool isCopyableNode(const css::uno::Reference<css::script::browse::XBrowseNode>& xNode)
|
||
{
|
||
css::uno::Reference<css::script::browse::XCopyableBrowseNode> xCopyable(xNode,
|
||
css::uno::UNO_QUERY);
|
||
|
||
return xCopyable.is() && xCopyable->isCopyableNode();
|
||
}
|
||
|
||
void JsProvTest::testCopyable()
|
||
{
|
||
// Create a dummy macro in a subfolder of the script directory
|
||
m_xFileAccess->createFolder(m_xUriHelper->getRootStorageURI() + "/SrcDir");
|
||
createDummyMacro(m_xUriHelper->getRootStorageURI() + "/SrcDir/MyScript.js");
|
||
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xRootNode(m_xScriptProvider,
|
||
css::uno::UNO_QUERY_THROW);
|
||
|
||
// The root node shouldn’t be copyable
|
||
CPPUNIT_ASSERT(!isCopyableNode(xRootNode));
|
||
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xLibrary = xRootNode->getChildNodes()[0];
|
||
CPPUNIT_ASSERT_EQUAL(u"SrcDir"_ustr, xLibrary->getName());
|
||
|
||
// The library shouldn’t be copyable
|
||
CPPUNIT_ASSERT(!isCopyableNode(xLibrary));
|
||
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xFile = xLibrary->getChildNodes()[0];
|
||
CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xFile->getName());
|
||
|
||
// File nodes are copyable
|
||
CPPUNIT_ASSERT(isCopyableNode(xFile));
|
||
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xMacro = xFile->getChildNodes()[0];
|
||
CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xMacro->getName());
|
||
|
||
// Macro nodes are not copyable
|
||
CPPUNIT_ASSERT(!isCopyableNode(xMacro));
|
||
|
||
css::uno::Reference<css::script::browse::XCopyableBrowseNode> xCopyable(
|
||
xFile, css::uno::UNO_QUERY_THROW);
|
||
|
||
// We can copy to the root level of the JavaScript hierarchy
|
||
CPPUNIT_ASSERT(xCopyable->nodeCanBeCopiedTo(xRootNode));
|
||
// We can’t copy to the same directory that the file is already in
|
||
CPPUNIT_ASSERT(!xCopyable->nodeCanBeCopiedTo(xLibrary));
|
||
// We can’t copy to a file
|
||
CPPUNIT_ASSERT(!xCopyable->nodeCanBeCopiedTo(xFile));
|
||
// or a macro
|
||
CPPUNIT_ASSERT(!xCopyable->nodeCanBeCopiedTo(xMacro));
|
||
|
||
// We shouldn’t be able to copy to any other language providers
|
||
css::uno::Reference<css::script::provider::XScriptProviderFactory> xMasterProviderFactory
|
||
= css::script::provider::theMasterScriptProviderFactory::get(m_xContext);
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xMasterProvider(
|
||
xMasterProviderFactory->createScriptProvider(css::uno::Any(u"user"_ustr)),
|
||
css::uno::UNO_QUERY_THROW);
|
||
for (const auto& xChild : xMasterProvider->getChildNodes())
|
||
{
|
||
if (xChild->getName() == u"JavaScript"_ustr)
|
||
continue;
|
||
CPPUNIT_ASSERT(!xCopyable->nodeCanBeCopiedTo(xChild));
|
||
}
|
||
|
||
// Make a new library that we can copy to
|
||
m_xFileAccess->createFolder(m_xUriHelper->getRootStorageURI() + "/DestDir");
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xOtherLibrary;
|
||
for (const auto& xChild : xRootNode->getChildNodes())
|
||
{
|
||
if (xChild->getName() == u"DestDir"_ustr)
|
||
{
|
||
xOtherLibrary = xChild;
|
||
break;
|
||
}
|
||
}
|
||
CPPUNIT_ASSERT(xOtherLibrary.is());
|
||
|
||
// We should be able to copy into the new library
|
||
CPPUNIT_ASSERT(xCopyable->nodeCanBeCopiedTo(xOtherLibrary));
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xCopiedNode
|
||
= xCopyable->copyNode(xOtherLibrary);
|
||
|
||
CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xCopiedNode->getName());
|
||
|
||
css::uno::Reference<css::script::browse::XBrowseNode> xCopiedMacro
|
||
= xCopiedNode->getChildNodes()[0];
|
||
CPPUNIT_ASSERT_EQUAL(u"MyScript"_ustr, xCopiedMacro->getName());
|
||
|
||
// Get the URI of the copied macro
|
||
css::uno::Reference<css::beans::XPropertySet> xPropertySet(xCopiedMacro,
|
||
css::uno::UNO_QUERY_THROW);
|
||
css::uno::Any xURI = xPropertySet->getPropertyValue(u"URI"_ustr);
|
||
OUString sURI;
|
||
CPPUNIT_ASSERT(xURI >>= sURI);
|
||
|
||
// The URI should contain the new library name
|
||
CPPUNIT_ASSERT(sURI.indexOf(":DestDir|") != -1);
|
||
|
||
// Create a document to run the script on
|
||
loadFromURL(u"private:factory/swriter"_ustr);
|
||
|
||
// Execute it
|
||
css::uno::Sequence<css::uno::Any> aScriptParams;
|
||
css::uno::Sequence<sal_Int16> aOutParamIndex;
|
||
css::uno::Sequence<css::uno::Any> aOutParam;
|
||
m_xScriptProvider->getScript(sURI)->invoke(aScriptParams, aOutParamIndex, aOutParam);
|
||
|
||
// Check that the text was inserted into our test document
|
||
css::uno::Reference<css::text::XTextDocument> xTextDocument(mxComponent,
|
||
css::uno::UNO_QUERY_THROW);
|
||
CPPUNIT_ASSERT_EQUAL(u"jsprovtest"_ustr, xTextDocument->getText()->getString());
|
||
}
|
||
|
||
CPPUNIT_TEST_SUITE_REGISTRATION(JsProvTest);
|
||
|
||
} // namespace
|
||
CPPUNIT_PLUGIN_IMPLEMENT();
|
||
|
||
/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
|