/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.xpn.xwiki.plugin.feed;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Assert;
import org.jmock.Mock;
import org.jmock.core.Invocation;
import org.jmock.core.stub.CustomStub;
import org.xwiki.display.internal.DisplayConfiguration;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.syntax.Syntax;
import com.sun.syndication.feed.synd.SyndEntry;
import com.sun.syndication.feed.synd.SyndEntryImpl;
import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiConfig;
import com.xpn.xwiki.XWikiContext;
import com.xpn.xwiki.XWikiException;
import com.xpn.xwiki.api.Document;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.objects.classes.BaseClass;
import com.xpn.xwiki.store.XWikiHibernateStore;
import com.xpn.xwiki.store.XWikiHibernateVersioningStore;
import com.xpn.xwiki.store.XWikiStoreInterface;
import com.xpn.xwiki.store.XWikiVersioningStoreInterface;
import com.xpn.xwiki.test.AbstractBridgedXWikiComponentTestCase;
import com.xpn.xwiki.user.api.XWikiRightService;
import com.xpn.xwiki.user.impl.xwiki.XWikiRightServiceImpl;
import com.xpn.xwiki.web.XWikiServletRequestStub;
import com.xpn.xwiki.web.XWikiServletURLFactory;
/**
* Unit tests for {@link SyndEntryDocumentSource}.
*/
public class SyndEntryDocumentSourceTest extends AbstractBridgedXWikiComponentTestCase
{
public static final String INCONSISTENCY = "Inconsistency!";
public static final String POLYMORPHISM_INCONSISTENCY = "Polymorphism inconsistency!";
public static final String ACCESS_RIGHTS_VIOLATED = "Access rights are violated!";
public static final String PARAMETERS_IGNORED = "Parameters are ignored!";
public static final String SVG_MIME_TYPE = "image/svg+xml";
public static final String PNG_MIME_TYPE = "image/png";
public static final String ARTICLE_CLASS_NAME = "XWiki.ArticleClass";
protected SyndEntryDocumentSource source;
protected XWikiDocument doc;
@Override
protected void setUp() throws Exception
{
super.setUp();
mockUp();
getContext().setUser("Condor");
doc = new XWikiDocument(new DocumentReference("Wiki", "MilkyWay", "Fidis"));
doc.setCreator("Condor");
doc.setAuthor("Albatross");
doc.setTitle("Fidis from MilkyWay");
doc.setContent("blah blah blah..");
doc.setSyntax(Syntax.XWIKI_2_1);
initArticleClass();
doc.createNewObject(ARTICLE_CLASS_NAME, getContext());
doc.setStringValue(ARTICLE_CLASS_NAME, "title", "Old story");
doc.setStringValue(ARTICLE_CLASS_NAME, "content", "Once upon a <i>time</i> there was..");
List<String> categories = new ArrayList<String>();
categories.add("News");
categories.add("Information");
doc.setStringListValue(ARTICLE_CLASS_NAME, "category", categories);
getContext().getWiki().saveDocument(doc, getContext());
getContext().setDoc(doc);
// Ensure that no Velocity Templates are going to be used when executing Velocity since otherwise
// the Velocity init would fail (since by default the macros.vm templates wouldn't be found as we're
// not providing it in our unit test resources).
getConfigurationSource().setProperty("xwiki.render.velocity.macrolist", "");
source = new SyndEntryDocumentSource();
}
@Override
protected void registerComponents() throws Exception
{
super.registerComponents();
// Setup display configuration.
Mock mockDisplayConfiguration = registerMockComponent(DisplayConfiguration.class);
mockDisplayConfiguration.stubs().method("getDocumentDisplayerHint").will(returnValue("default"));
mockDisplayConfiguration.stubs().method("getTitleHeadingDepth").will(returnValue(2));
}
private void mockUp() throws Exception
{
final Map<String, XWikiDocument> docs = new HashMap<String, XWikiDocument>();
final XWikiContext context = getContext();
// Set URL/Request
context.setRequest(new XWikiServletRequestStub());
context.setURL(new URL("http://localhost:8080/xwiki/bin/view/MilkyWay/Fidis"));
final XWiki xwiki = new XWiki(new XWikiConfig(), context)
{
@Override
public String getXWikiPreference(String prefname, String defaultValue, XWikiContext context)
{
return defaultValue;
}
};
context.setURLFactory(new XWikiServletURLFactory(new URL("http://www.xwiki.org/"), "xwiki/", "bin/"));
final Mock mockXWikiStore =
mock(XWikiHibernateStore.class, new Class[] {XWiki.class, XWikiContext.class},
new Object[] {xwiki, context});
mockXWikiStore.stubs().method("loadXWikiDoc").will(
new CustomStub("Implements XWikiStoreInterface.loadXWikiDoc")
{
@Override
public Object invoke(Invocation invocation) throws Throwable
{
XWikiDocument shallowDoc = (XWikiDocument) invocation.parameterValues.get(0);
if (docs.containsKey(shallowDoc.getName())) {
return docs.get(shallowDoc.getName());
} else {
return shallowDoc;
}
}
});
mockXWikiStore.stubs().method("saveXWikiDoc").will(
new CustomStub("Implements XWikiStoreInterface.saveXWikiDoc")
{
@Override
public Object invoke(Invocation invocation) throws Throwable
{
XWikiDocument document = (XWikiDocument) invocation.parameterValues.get(0);
document.setNew(false);
document.setStore((XWikiStoreInterface) mockXWikiStore.proxy());
docs.put(document.getName(), document);
return null;
}
});
mockXWikiStore.stubs().method("getTranslationList").will(returnValue(Collections.EMPTY_LIST));
mockXWikiStore.stubs().method("exists").will(returnValue(false));
final Mock mockXWikiVersioningStore =
mock(XWikiHibernateVersioningStore.class, new Class[] {XWiki.class, XWikiContext.class}, new Object[] {
xwiki, context});
mockXWikiVersioningStore.stubs().method("getXWikiDocumentArchive").will(returnValue(null));
mockXWikiVersioningStore.stubs().method("resetRCSArchive").will(returnValue(null));
xwiki.setStore((XWikiStoreInterface) mockXWikiStore.proxy());
xwiki.setVersioningStore((XWikiVersioningStoreInterface) mockXWikiVersioningStore.proxy());
final Mock mockXWikiRightsService = mock(XWikiRightServiceImpl.class, new Class[] {}, new Object[] {});
mockXWikiRightsService.stubs().method("hasAccessLevel").will(
new CustomStub("Implements XWikiRightService.hasAccessLevel")
{
@Override
public Object invoke(Invocation invocation) throws Throwable
{
// String right = (String) invocation.parameterValues.get(0);
String user = (String) invocation.parameterValues.get(1);
// String doc = (String) invocation.parameterValues.get(2);
// we give access to all the users with an even name length
return new Boolean(user.length() % 2 == 0);
}
});
xwiki.setRightService((XWikiRightService) mockXWikiRightsService.proxy());
}
protected BaseClass initArticleClass() throws XWikiException
{
XWikiDocument doc = getContext().getWiki().getDocument(ARTICLE_CLASS_NAME, getContext());
boolean needsUpdate = doc.isNew();
BaseClass bclass = doc.getXClass();
bclass.setName(ARTICLE_CLASS_NAME);
needsUpdate |= bclass.addTextField("title", "Title", 64);
needsUpdate |= bclass.addTextAreaField("content", "Content", 45, 4);
needsUpdate |= bclass.addTextField("category", "Category", 64);
if (needsUpdate) {
getContext().getWiki().saveDocument(doc, getContext());
}
return bclass;
}
protected SyndEntryImpl source(Object obj)
{
return source(obj, Collections.EMPTY_MAP);
}
protected SyndEntryImpl source(Object obj, Map params)
{
SyndEntryImpl entry = new SyndEntryImpl();
try {
source.source(entry, obj, params, getContext());
} catch (Exception e) {
}
return entry;
}
/**
* Computes the sum of lengths of all the text nodes from the given XML fragment.
*
* @param xmlFragment the XML fragment to be parsed
* @return the number of characters in all the text nodes within the given XML fragment
*/
protected int getXMLContentLength(String xmlFragment)
{
return SyndEntryDocumentSource.innerTextLength(SyndEntryDocumentSource.tidy(xmlFragment,
SyndEntryDocumentSource.TIDY_HTML_CONFIG));
}
/**
* Tests if two successive calls of the source method with the same argument have the same result.
*/
public void testSourceConsistency()
{
Assert.assertEquals(INCONSISTENCY, source(doc), source(doc));
}
/**
* Tests if different calls of the source method have the same result when the argument passed points to the same
* document, irrespective of its type: {@link XWikiDocument}, {@link Document}, and {@link String}.
*/
public void testSourcePolymorphism()
{
SyndEntryImpl fromXDoc = source(doc);
SyndEntryImpl fromDoc = source(doc.newDocument(getContext()));
SyndEntryImpl fromFullName = source(doc.getFullName());
Assert.assertEquals(POLYMORPHISM_INCONSISTENCY, fromXDoc, fromDoc);
Assert.assertEquals(POLYMORPHISM_INCONSISTENCY, fromXDoc, fromFullName);
Assert.assertEquals(POLYMORPHISM_INCONSISTENCY, fromDoc, fromFullName);
}
/**
* Tests if the source method obeys the access rights.
*
* @throws XWikiException
*/
public void testSourceAccessRights() throws XWikiException
{
// odd user name length implies no access rights
getContext().setUser("XWiki.Albatross");
try {
source.source(new SyndEntryImpl(), doc, Collections.EMPTY_MAP, getContext());
Assert.fail(ACCESS_RIGHTS_VIOLATED);
} catch (XWikiException expected) {
// we should get an exception
Assert.assertEquals(XWikiException.ERROR_XWIKI_ACCESS_DENIED, expected.getCode());
}
// even user name length implies all access rights
getContext().setUser("Condor");
source.source(new SyndEntryImpl(), doc, Collections.EMPTY_MAP, getContext());
// we shouldn't get an exception
}
/**
* Tests if {@link SyndEntryDocumentSource#CONTENT_TYPE} parameter is used correctly.
*/
public void testSourceContentType()
{
Map instanceParams = new HashMap();
instanceParams.put(SyndEntryDocumentSource.CONTENT_TYPE, SVG_MIME_TYPE);
source.setParams(instanceParams);
Assert.assertEquals(PARAMETERS_IGNORED, SVG_MIME_TYPE, source(doc).getDescription().getType());
Map methodParams = new HashMap();
methodParams.put(SyndEntryDocumentSource.CONTENT_TYPE, PNG_MIME_TYPE);
SyndEntry entry = source(doc, methodParams);
Assert.assertEquals(PARAMETERS_IGNORED, PNG_MIME_TYPE, entry.getDescription().getType());
}
/**
* Tests if {@link SyndEntryDocumentSource#CONTENT_LENGTH} parameter is used correctly when the
* {@link SyndEntryDocumentSource#CONTENT_TYPE} is <i>text/plain</i>.
*/
public void testArticleSourcePlainContentLength()
{
int maxLength = 15;
Map params = new HashMap();
params.put(SyndEntryDocumentSource.CONTENT_TYPE, "text/plain");
params.put(SyndEntryDocumentSource.CONTENT_LENGTH, maxLength);
params.put(SyndEntryDocumentSource.FIELD_DESCRIPTION, ARTICLE_CLASS_NAME + "_content");
source.setParams(params);
doc.setStringValue(ARTICLE_CLASS_NAME, "content", "Somewhere in la Mancha, in a place..");
Assert.assertTrue(doc.display("content", getContext()).length() > maxLength);
int descriptionLength = source(doc).getDescription().getValue().length();
Assert.assertTrue(PARAMETERS_IGNORED, descriptionLength <= maxLength);
}
/**
* Tests if {@link SyndEntryDocumentSource#CONTENT_LENGTH} parameter is used correctly when the
* {@link SyndEntryDocumentSource#CONTENT_TYPE} is <i>text/html</i>.
*/
public void testArticleSourceHTMLContentLength()
{
int maxLength = 16;
Map params = new HashMap();
params.put(SyndEntryDocumentSource.CONTENT_TYPE, "text/html");
params.put(SyndEntryDocumentSource.CONTENT_LENGTH, maxLength);
params.put(SyndEntryDocumentSource.FIELD_DESCRIPTION, ARTICLE_CLASS_NAME + "_content");
doc.setStringValue(ARTICLE_CLASS_NAME, "content",
"Somewhere \n\tin <i>la</i> <a href=\"http://www.mancha.es\"> Mancha</a>, in a place..");
Assert.assertTrue(getXMLContentLength(doc.display("content", getContext())) > maxLength);
String description = source(doc, params).getDescription().getValue();
int descriptionLength = getXMLContentLength(description);
Assert.assertTrue(PARAMETERS_IGNORED, descriptionLength <= maxLength);
}
public void testArticleSourceXMLContentLength()
{
int maxLength = 17;
Map params = new HashMap();
params.put(SyndEntryDocumentSource.CONTENT_TYPE, "text/xml");
params.put(SyndEntryDocumentSource.CONTENT_LENGTH, maxLength);
params.put(SyndEntryDocumentSource.FIELD_DESCRIPTION, ARTICLE_CLASS_NAME + "_content");
doc.setStringValue(ARTICLE_CLASS_NAME, "content",
"<text>Somewhere \n\tin la <region> Mancha</region>, in a place..</text>");
Assert.assertTrue(getXMLContentLength(doc.display("content", getContext())) > maxLength);
String description = source(doc, params).getDescription().getValue();
int descriptionLength = getXMLContentLength(description);
Assert.assertTrue(PARAMETERS_IGNORED, descriptionLength <= maxLength);
}
public void testPreviewContentEncoding()
{
String snippet = "<p>Test ê</p>";
String transformedHTML = SyndEntryDocumentSource.getHTMLPreview(snippet, 10);
Assert.assertEquals(snippet, transformedHTML);
String transformedXML = SyndEntryDocumentSource.getXMLPreview(snippet, 10);
Assert.assertEquals(snippet, transformedXML);
String plainSnippet = " Test Text ê Rest ";
String previewExpected = "Test Text ê";
String transformedPlain = SyndEntryDocumentSource.getPlainPreview(plainSnippet, 12);
Assert.assertEquals(previewExpected, transformedPlain);
}
}