/*
* 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 org.xwiki.store.legacy.store.internal;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.List;
import org.xwiki.store.legacy.doc.internal.FilesystemAttachmentContent;
import com.xpn.xwiki.doc.XWikiAttachment;
import com.xpn.xwiki.doc.XWikiAttachmentArchive;
import com.xpn.xwiki.doc.XWikiAttachmentContent;
import com.xpn.xwiki.doc.XWikiDocument;
import com.xpn.xwiki.store.AttachmentVersioningStore;
import com.xpn.xwiki.store.XWikiHibernateStore;
import com.xpn.xwiki.web.Utils;
import com.xpn.xwiki.XWiki;
import com.xpn.xwiki.XWikiContext;
import org.apache.commons.io.IOUtils;
import org.hibernate.Session;
import org.jmock.Expectations;
import org.jmock.api.Invocation;
import org.jmock.lib.action.CustomAction;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.xwiki.model.internal.reference.PathStringEntityReferenceSerializer;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.store.filesystem.internal.DefaultFilesystemStoreTools;
import org.xwiki.store.filesystem.internal.FilesystemStoreTools;
import org.xwiki.store.locks.dummy.internal.DummyLockProvider;
/**
* Tests for FilesystemAttachmentStore.
*
* @version $Id: fa69852ea671c1b2d0fe4e7a2c935dc61b723e33 $
* @since 3.0M2
*/
public class FilesystemAttachmentStoreTest extends AbstractFilesystemAttachmentStoreTest
{
private static final String HELLO = "Hello World";
private static final byte[] HELLO_BYTES;
private static final InputStream HELLO_STREAM;
private XWikiContext mockContext;
private XWikiAttachment mockAttach;
private FilesystemAttachmentStore attachStore;
private FilesystemStoreTools fileTools;
private AttachmentVersioningStore mockAttachVersionStore;
private XWikiAttachmentArchive mockArchive;
private XWikiHibernateStore mockHibernate;
private Session mockHibernateSession;
private XWikiDocument doc;
/**
* The file which will hold content for this attachment.
*/
private File storeFile;
/**
* The dir in /tmp/ which we use as our sandbox.
*/
private File storageLocation;
static {
try {
HELLO_BYTES = HELLO.getBytes("UTF-8");
HELLO_STREAM = new ByteArrayInputStream(HELLO_BYTES);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("No UTF-8!!");
}
}
@Before
public void setUp() throws Exception
{
super.setUp();
getMockery().setImposteriser(ClassImposteriser.INSTANCE);
Utils.setComponentManager(this.getComponentManager());
this.mockContext = getMockery().mock(XWikiContext.class);
final XWiki mockXWiki = getMockery().mock(XWiki.class);
this.mockHibernate = getMockery().mock(XWikiHibernateStore.class);
final XWikiAttachmentContent mockDirtyContent =
getMockery().mock(XWikiAttachmentContent.class);
this.mockAttachVersionStore = getMockery().mock(AttachmentVersioningStore.class);
this.mockArchive = getMockery().mock(XWikiAttachmentArchive.class);
this.mockHibernateSession = getMockery().mock(Session.class);
this.doc = new XWikiDocument(new DocumentReference("xwiki", "Main", "WebHome"));
this.mockAttach = getMockery().mock(XWikiAttachment.class);
getMockery().checking(new Expectations() {{
allowing(mockContext).getWiki(); will(returnValue(mockXWiki));
allowing(mockXWiki).getStore(); will(returnValue(mockHibernate));
allowing(mockXWiki).getHibernateStore(); will(returnValue(mockHibernate));
allowing(mockHibernate).checkHibernate(mockContext);
allowing(mockHibernate).beginTransaction(mockContext);
allowing(mockHibernate).getSession(mockContext); will(returnValue(mockHibernateSession));
allowing(mockXWiki).getAttachmentVersioningStore(); will(returnValue(mockAttachVersionStore));
allowing(mockAttachVersionStore).saveArchive(mockArchive, mockContext, false);
allowing(mockAttach).getContentInputStream(mockContext); will(returnValue(HELLO_STREAM));
allowing(mockAttach).getDoc(); will(returnValue(doc));
allowing(mockAttach).getFilename(); will(returnValue("file.name"));
allowing(mockAttach).updateContentArchive(mockContext);
allowing(mockAttach).getAttachment_archive(); will(returnValue(mockArchive));
allowing(mockAttach).getAttachment_content(); will(returnValue(mockDirtyContent));
allowing(mockAttach).isContentDirty(); will(returnValue(true));
allowing(mockDirtyContent).isContentDirty(); will(returnValue(true));
}});
final File tmpDir = new File(System.getProperty("java.io.tmpdir"));
this.storageLocation = new File(tmpDir, "test-storage-location");
this.fileTools =
new DefaultFilesystemStoreTools(new PathStringEntityReferenceSerializer(),
storageLocation,
new DummyLockProvider());
this.attachStore = new FilesystemAttachmentStore(fileTools);
this.storeFile =
this.fileTools.getAttachmentFileProvider(this.mockAttach).getAttachmentContentFile();
HELLO_STREAM.reset();
}
@After
public void tearDown() throws IOException
{
resursiveDelete(this.storageLocation);
}
@Test
public void saveContentTest() throws Exception
{
final File storeFile =
this.fileTools.getAttachmentFileProvider(this.mockAttach).getAttachmentContentFile();
Assert.assertFalse(this.storeFile.exists());
this.attachStore.saveAttachmentContent(this.mockAttach, false, this.mockContext, false);
Assert.assertTrue("The attachment file was not created.", this.storeFile.exists());
final InputStream is = new FileInputStream(storeFile);
final ByteArrayOutputStream os = new ByteArrayOutputStream();
IOUtils.copy(is, os);
is.close();
byte[] array = os.toByteArray();
Assert.assertEquals("The attachment file contained the wrong content",
HELLO,
new String(array, "UTF-8"));
}
@Test
public void saveTwoOfSameAttachmentInOneTransactionTest() throws Exception
{
final File storeFile =
this.fileTools.getAttachmentFileProvider(this.mockAttach).getAttachmentContentFile();
Assert.assertFalse(this.storeFile.exists());
final List<XWikiAttachment> attachments = new ArrayList<XWikiAttachment>();
attachments.add(this.mockAttach);
attachments.add(this.mockAttach);
this.attachStore.saveAttachmentsContent(attachments, this.doc, false, this.mockContext, false);
Assert.assertTrue("The attachment file was not created.", this.storeFile.exists());
final InputStream is = new FileInputStream(storeFile);
final ByteArrayOutputStream os = new ByteArrayOutputStream();
IOUtils.copy(is, os);
is.close();
byte[] array = os.toByteArray();
Assert.assertEquals("The attachment file contained the wrong content",
HELLO,
new String(array, "UTF-8"));
}
@Test
public void loadContentTest() throws Exception
{
this.storeFile.getParentFile().mkdirs();
OutputStream os = new FileOutputStream(this.storeFile, false);
IOUtils.copy(HELLO_STREAM, os);
os.close();
getMockery().checking(new Expectations() {{
oneOf(mockAttach).setAttachment_content(with(any(FilesystemAttachmentContent.class)));
will(new CustomAction("Check to make sure the attachment content is correct.")
{
public Object invoke(final Invocation invoc)
{
final FilesystemAttachmentContent content =
(FilesystemAttachmentContent) invoc.getParameter(0);
try {
final ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy(content.getContentInputStream(), baos);
final String output = new String(baos.toByteArray(), "UTF-8");
Assert.assertEquals("Not the same attachment content.", HELLO, output);
return null;
} catch (IOException e) {
throw new RuntimeException("Exception getting attachment content.", e);
}
}
});
}});
this.attachStore.loadAttachmentContent(this.mockAttach, this.mockContext, false);
}
@Test
public void deleteAttachmentTest() throws Exception
{
getMockery().checking(new Expectations() {{
oneOf(mockAttachVersionStore).deleteArchive(mockAttach, mockContext, false);
exactly(2).of(mockHibernateSession).delete(with(any(Object.class)));
}});
this.createFile();
this.attachStore.deleteXWikiAttachment(this.mockAttach, false, this.mockContext, false);
Assert.assertFalse("The attachment file was not deleted.", this.storeFile.exists());
}
@Test
public void documentUpdateOnDeleteTest() throws Exception
{
final List<XWikiAttachment> attachList = new ArrayList<XWikiAttachment>();
attachList.add(this.mockAttach);
this.doc.setAttachmentList(attachList);
getMockery().checking(new Expectations() {{
oneOf(mockAttachVersionStore).deleteArchive(mockAttach, mockContext, false);
exactly(2).of(mockHibernateSession).delete(with(any(Object.class)));
oneOf(mockHibernate).saveXWikiDoc(doc, mockContext, false);
will(new CustomAction("Make sure the attachment has been removed from the list.")
{
public Object invoke(final Invocation invoc)
{
final XWikiDocument document = (XWikiDocument) invoc.getParameter(0);
Assert.assertTrue("Attachment was not removed from the list.",
document.getAttachmentList().size() == 0);
return null;
}
});
}});
this.createFile();
this.attachStore.deleteXWikiAttachment(this.mockAttach, true, this.mockContext, false);
}
@Test
public void documentUpdateOnSaveTest() throws Exception
{
getMockery().checking(new Expectations() {{
oneOf(mockHibernate).saveXWikiDoc(doc, mockContext, false);
}});
this.attachStore.saveAttachmentContent(this.mockAttach, true, this.mockContext, false);
}
/* -------------------- Helpers -------------------- */
private void createFile() throws IOException
{
this.storeFile.getParentFile().mkdirs();
OutputStream os = new FileOutputStream(this.storeFile, false);
IOUtils.copy(HELLO_STREAM, os);
os.close();
Assert.assertTrue("The attachment file not created for the test.", this.storeFile.exists());
}
private static void resursiveDelete(final File toDelete) throws IOException
{
if (toDelete == null || !toDelete.exists()) {
return;
}
if (toDelete.isDirectory()) {
final File[] children = toDelete.listFiles();
for (int i = 0; i < children.length; i++) {
resursiveDelete(children[i]);
}
}
toDelete.delete();
}
}