/*
* Copyright (c) 2008-2014 MongoDB, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mongodb.gridfs;
import category.Slow;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.DatabaseTestCase;
import com.mongodb.MongoException;
import org.bson.types.ObjectId;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.Scanner;
import static java.nio.charset.Charset.defaultCharset;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class GridFSTest extends DatabaseTestCase {
private GridFS gridFS;
@Before
public void setUp() {
super.setUp();
gridFS = new GridFS(database);
}
@Test
public void testSmall() throws Exception {
testInOut("this is a simple test");
}
@Test
public void testBig() throws Exception {
int target = GridFS.DEFAULT_CHUNKSIZE * 3;
StringBuilder buf = new StringBuilder(target);
while (buf.length() < target) {
buf.append("asdasdkjasldkjasldjlasjdlajsdljasldjlasjdlkasjdlaskjdlaskjdlsakjdlaskjdasldjsad");
}
String s = buf.toString();
testInOut(s);
}
void testOutStream(final String s) throws Exception {
int[] start = getCurrentCollectionCounts();
GridFSInputFile in = gridFS.createFile();
OutputStream writeStream = in.getOutputStream();
writeStream.write(s.getBytes(defaultCharset()), 0, s.length());
writeStream.close();
GridFSDBFile out = gridFS.findOne(new BasicDBObject("_id", in.getId()));
assert (out.getId().equals(in.getId()));
assert (out.getChunkSize() == (long) GridFS.DEFAULT_CHUNKSIZE);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
out.writeTo(bout);
String outString = new String(bout.toByteArray(), defaultCharset());
assert (outString.equals(s));
out.remove();
int[] end = getCurrentCollectionCounts();
assertEquals(start[0], end[0]);
assertEquals(start[1], end[1]);
}
@Test
public void testOutStreamSmall() throws Exception {
testOutStream("this is a simple test");
}
@Test
public void testOutStreamBig() throws Exception {
int target = (int) (GridFS.DEFAULT_CHUNKSIZE * 3.5);
StringBuilder buf = new StringBuilder(target);
while (buf.length() < target) {
buf.append("asdasdkjasldkjasldjlasjdlajsdljasldjlasjdlkasjdlaskjdlaskjdlsakjdlaskjdasldjsad");
}
String s = buf.toString();
testOutStream(s);
}
@Test
public void testOutStreamBigAligned() throws Exception {
int target = (GridFS.DEFAULT_CHUNKSIZE * 4);
StringBuilder buf = new StringBuilder(target);
while (buf.length() < target) {
buf.append("a");
}
String s = buf.toString();
testOutStream(s);
}
@Test
public void testCreateFileWithFile() throws Exception {
URI fileURI = GridFSTest.class.getResource("/GridFSLegacy/GridFSTestFile.txt").toURI();
GridFSInputFile in = gridFS.createFile(new File(fileURI));
in.save();
String expectedString = new Scanner(new File(fileURI)).useDelimiter("\\Z").next();
GridFSDBFile out = gridFS.findOne(new BasicDBObject("_id", in.getId()));
ByteArrayOutputStream bout = new ByteArrayOutputStream();
out.writeTo(bout);
String outString = new String(bout.toByteArray(), defaultCharset()).trim();
assertEquals(expectedString, outString);
}
@Test
public void testMetadata() throws Exception {
GridFSInputFile in = gridFS.createFile("foo".getBytes(defaultCharset()));
in.put("meta", 5);
in.save();
GridFSDBFile out = gridFS.findOne(new BasicDBObject("_id", in.getId()));
assertTrue(out.get("meta").equals(5));
}
@Test
public void testFind() throws Exception {
GridFSInputFile in = gridFS.createFile(new ByteArrayInputStream("foo".getBytes(defaultCharset())), "testFind");
in.save();
assertNotNull(gridFS.find((ObjectId) in.getId()));
assertNotNull(gridFS.findOne((ObjectId) in.getId()));
assertNotNull(gridFS.findOne("testFind"));
assertNotNull(gridFS.findOne(new BasicDBObject("_id", in.getId())));
}
@Test
public void testBadChunkSize() throws Exception {
byte[] randomBytes = new byte[256];
GridFSInputFile inputFile = gridFS.createFile(randomBytes);
inputFile.setFilename("bad_chunk_size.bin");
try {
inputFile.save(0);
fail("should have received an exception about a chunk size being zero");
} catch (MongoException e) {
//We expect this exception to complain about the chunksize
assertTrue(e.toString().contains("chunkSize must be greater than zero"));
}
}
@Test
@Category(Slow.class)
public void testMultipleChunks() throws Exception {
int fileSize = 1024 * 128;
byte[] randomBytes = new byte[fileSize];
for (int idx = 0; idx < fileSize; ++idx) {
randomBytes[idx] = (byte) (256 * Math.random());
}
GridFSInputFile inputFile = gridFS.createFile(randomBytes);
inputFile.setFilename("bad_chunk_size.bin");
//For good measure let's save and restore the bytes
inputFile.save(1024);
GridFSDBFile savedFile = gridFS.findOne(new BasicDBObject("_id", inputFile.getId()));
ByteArrayOutputStream savedFileByteStream = new ByteArrayOutputStream();
savedFile.writeTo(savedFileByteStream);
byte[] savedFileBytes = savedFileByteStream.toByteArray();
assertArrayEquals(randomBytes, savedFileBytes);
}
@Test
@Category(Slow.class)
public void getBigChunkSize() throws Exception {
GridFSInputFile file = gridFS.createFile("512kb_bucket");
file.setChunkSize(file.getChunkSize() * 2);
OutputStream os = file.getOutputStream();
for (int i = 0; i < 1024; i++) {
os.write(new byte[GridFS.DEFAULT_CHUNKSIZE / 1024 + 1]);
}
os.close();
}
@Test
public void testInputStreamSkipping() throws Exception {
//int chunkSize = 5;
int chunkSize = GridFS.DEFAULT_CHUNKSIZE;
int fileSize = (int) (7.25 * chunkSize);
byte[] fileBytes = new byte[fileSize];
for (int idx = 0; idx < fileSize; ++idx) {
fileBytes[idx] = (byte) (idx % 251);
}
//Don't want chunks to be aligned at byte position 0
GridFSInputFile inputFile = gridFS.createFile(fileBytes);
inputFile.setFilename("input_stream_skipping.bin");
inputFile.save(chunkSize);
GridFSDBFile savedFile = gridFS.findOne(new BasicDBObject("_id", inputFile.getId()));
InputStream inputStream = savedFile.getInputStream();
//Quick run-through, make sure the file is as expected
for (int idx = 0; idx < fileSize; ++idx) {
assertEquals((byte) (idx % 251), (byte) inputStream.read());
}
inputStream = savedFile.getInputStream();
long skipped = inputStream.skip(1);
assertEquals(1, skipped);
int position = 1;
assertEquals((byte) (position++ % 251), (byte) inputStream.read());
skipped = inputStream.skip(chunkSize);
assertEquals(chunkSize, skipped);
position += chunkSize;
assertEquals((byte) (position++ % 251), (byte) inputStream.read());
skipped = inputStream.skip(-1);
assertEquals(0, skipped);
skipped = inputStream.skip(0);
assertEquals(0, skipped);
skipped = inputStream.skip(3 * chunkSize);
assertEquals(3 * chunkSize, skipped);
position += 3 * chunkSize;
assertEquals((byte) (position++ % 251), (byte) inputStream.read());
//Make sure skipping works when we skip to an exact chunk boundary
long toSkip = inputStream.available();
skipped = inputStream.skip(toSkip);
assertEquals(toSkip, skipped);
position += toSkip;
assertEquals((byte) (position++ % 251), (byte) inputStream.read());
skipped = inputStream.skip(2 * fileSize);
assertEquals(fileSize - position, skipped);
assertEquals(-1, inputStream.read());
}
@Test
public void testCustomFileID() throws IOException {
// given
int id = 1;
gridFS.remove(new BasicDBObject("_id", id));
int chunkSize = 10;
int fileSize = (int) (3.25 * chunkSize);
byte[] fileBytes = new byte[fileSize];
for (int idx = 0; idx < fileSize; ++idx) {
fileBytes[idx] = (byte) (idx % 251);
}
// when
GridFSInputFile inputFile = gridFS.createFile(fileBytes);
inputFile.setId(id);
inputFile.setFilename("custom_file_id.bin");
inputFile.save(chunkSize);
assertEquals(id, inputFile.getId());
// then
GridFSDBFile savedFile = gridFS.findOne(new BasicDBObject("_id", id));
InputStream inputStream = savedFile.getInputStream();
for (int idx = 0; idx < fileSize; ++idx) {
assertEquals((byte) (idx % 251), (byte) inputStream.read());
}
// finally
gridFS.remove(new BasicDBObject("_id", id));
}
void testInOut(final String s) throws Exception {
int[] start = getCurrentCollectionCounts();
GridFSInputFile in = gridFS.createFile(s.getBytes(defaultCharset()));
in.save();
GridFSDBFile out = gridFS.findOne(new BasicDBObject("_id", in.getId()));
assert (out.getId().equals(in.getId()));
assert (out.getChunkSize() == (long) GridFS.DEFAULT_CHUNKSIZE);
ByteArrayOutputStream bout = new ByteArrayOutputStream();
out.writeTo(bout);
String outString = new String(bout.toByteArray(), defaultCharset());
assert (outString.equals(s));
out.remove();
int[] end = getCurrentCollectionCounts();
assertEquals(start[0], end[0]);
assertEquals(start[1], end[1]);
}
int[] getCurrentCollectionCounts() {
int[] i = new int[2];
i[0] = gridFS.getFilesCollection().find().count();
i[1] = gridFS.getChunksCollection().find().count();
return i;
}
@Test(expected = IllegalArgumentException.class)
public void testRemoveWhenObjectIdIsNull() {
ObjectId objectId = null;
gridFS.remove(objectId);
}
@Test(expected = IllegalArgumentException.class)
public void testRemoveWhenFileNameIsNull() {
String fileName = null;
gridFS.remove(fileName);
}
@Test(expected = IllegalArgumentException.class)
public void testRemoveWhenQueryIsNull() {
DBObject dbObjectQuery = null;
gridFS.remove(dbObjectQuery);
}
}