All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.akubraproject.tck.TCKTestSuite Maven / Gradle / Ivy

There is a newer version: 0.4.0
Show newest version
/* $HeadURL: https://fedora-commons.org/svn/root/akubra/tags/akubra-0.3/akubra-tck/src/main/java/org/akubraproject/tck/TCKTestSuite.java $
 * $Id: TCKTestSuite.java 2821 2010-02-20 17:24:57Z cwilper $
 *
 * Copyright (c) 2009-2010 DuraSpace
 * http://duraspace.org
 *
 * In collaboration with Topaz Inc.
 * http://www.topazproject.org
 *
 * 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 org.akubraproject.tck;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;

import javax.transaction.Transaction;

import org.testng.annotations.Test;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;

import org.akubraproject.Blob;
import org.akubraproject.BlobStore;
import org.akubraproject.BlobStoreConnection;
import org.akubraproject.DuplicateBlobException;
import org.akubraproject.UnsupportedIdException;
import org.akubraproject.MissingBlobException;

/**
 * A suite of unit tests for Akubra stores.
 *
 * 

The tests are assigned, and depend on, various groups. All tests here depend on (directly or * indirectly) the group "init", and there is one dummy test in that group; all tests that * manipulate blobs (create, delete, modify, move) are in the "manipulatesBlobs" group; there is * one test in the "post" group, and that test depends on the "manipulatesBlobs" to ensure it is * run after all blob manipulations are completed. Additional there are three groups that represent * the entity they're testing: "store", "connection", and "blob". * *

Subclasses of this must implement at least {@link #getInvalidId getInvalidId}. Additionally, * many stores will probably want to override {@link #createId createId} and {@link #getPrefixFor * getPrefixFor} in order to create URI's which are understood by the store. * *

For stores that don't support all modify operations, various flags can be passed to the * constructor indicating what is supported. This works fine for the transactional, id-gen, and * move-to flags; however, create, delete, and list-ids are needed throughout the tests. For * stores that don't support one or more of these latter flags you must also override one or more * of {@link #createBlob(BlobStoreConnection, Blob, String) createBlob} and {@link * #deleteBlob(BlobStoreConnection, Blob) deleteBlob} to create and delete the underlying * resources, and {@link #assertNoBlobs(String) assertNoBlobs} and {@link * #listBlobs(BlobStoreConnection, String, URI[]) listBlobs} for verifying things got cleaned up * properly. * * @author Ronald Tschalär */ @SuppressWarnings("PMD.TooManyStaticImports") public abstract class TCKTestSuite extends AbstractTests { protected final URI storeId; protected final boolean isIdGenSupp; protected final boolean isListIdsSupp; protected final boolean isOutputSupp; protected final boolean isDeleteSupp; protected final boolean isMoveToSupp; protected final boolean isSyncSupp; /** * Create a new test suite instance. This assumes a fully functional store, i.e. one that * supports creation, deletion, moving, and listing of blobs. * * @param store the store to test * @param storeId the store's expected id * @param isTransactional if the store is transactional * @param isIdGenSupp true if id generation (getBlob(null, ...)) is supported */ protected TCKTestSuite(BlobStore store, URI storeId, boolean isTransactional, boolean isIdGenSupp) { this(store, storeId, isTransactional, isIdGenSupp, true, true, true, true, true); } /** * Create a new test suite instance. * * @param store the store to test * @param storeId the store's expected id * @param isTransactional if the store is transactional * @param isIdGenSupp true if id generation (getBlob(null, ...)) is supported * @param isListIdsSupp true if con.listBlobIds() is supported * @param isOutputSupp true if Blob.openOutputStream() is supported * @param isDeleteSupp true if Blob.delete() is supported * @param isMoveToSupp true if Blob.moveTo() is supported * @param isSyncSupp true if con.sync() is supported */ protected TCKTestSuite(BlobStore store, URI storeId, boolean isTransactional, boolean isIdGenSupp, boolean isListIdsSupp, boolean isOutputSupp, boolean isDeleteSupp, boolean isMoveToSupp, boolean isSyncSupp) { super(store, isTransactional); this.storeId = storeId; this.isIdGenSupp = isIdGenSupp; this.isListIdsSupp = isListIdsSupp; this.isOutputSupp = isOutputSupp; this.isDeleteSupp = isDeleteSupp; this.isMoveToSupp = isMoveToSupp; this.isSyncSupp = isSyncSupp; } /** * Create an id for the given name. Usually this will incorporate the given name as part of the * URI itself (e.g. by appending it to a fixed prefix), but for stores that can't do this (e.g. * a read-only store) they can return an arbitrary URI. This must return different URI's for * different names and the same URI for the same name, at least for the duration of each test. * *

The default implementation returns URI.create("urn:akubra-tck:" + name). * * @param name the URI's "name" * @return the URI * @see #getPrefixFor */ protected URI createId(String name) { return URI.create("urn:akubra-tck:" + name); } /** * Get the URI prefix for the given name. This is used by the tests to list all blobs created by * the test in order to verify things got cleaned up. The name passed in here will be * a prefix of the names passed to previous {@link #createId} calls. The expectation is that if * createId is overriden then so must this method. In any case the implementation of * this method must figure out an appropriate prefix to return that will include all created id's * (in the current test). * *

The default implementation returns "urn:akubra-tck:" + name. * * @param name the URI's "name" * @return the prefix */ protected String getPrefixFor(String name) { return "urn:akubra-tck:" + name; } /** * @return an invalid id (for id validation test), or null to skip the tests (e.g. if all id's * are valid) */ protected abstract URI getInvalidId(); /** * @param uri the uri for which to get a list of aliases * @return an array of aliases, or null if getCanonicalId() returns null */ protected abstract URI[] getAliases(URI uri); @Test(groups={ "init" }) public void testInit() { } /* * BlobStore Tests */ /** * Store id should be what it was initialized with. */ @Test(groups={ "store" }) public void testGetId() { assertEquals(store.getId(), storeId); } /** * Request to open a connection without a transaction. */ @Test(groups={ "store" }, dependsOnGroups={ "init" }) public void testOpenConnectionNoTransaction() { try { BlobStoreConnection con = store.openConnection(null, null); if (isTransactional) fail("Did not get expected IOException initializing store without a transaction"); assertNotNull(con, "Null connection returned from openConnection(null)"); assertSame(con.getBlobStore(), store); } catch (IOException ioe) { if (!isTransactional) fail("Got unexpected IOException initializing store without a transaction", ioe); } } /** * Request to open a connection with a transaction. */ @Test(groups={ "store" }, dependsOnGroups={ "init" }) public void testOpenConnectionWithTransaction() throws Exception { tm.begin(); BlobStoreConnection con = null; try { con = store.openConnection(tm.getTransaction(), null); if (!isTransactional) fail("Did not get expected UnsupportedOperationException while initializing store with " + "a transaction"); assertNotNull(con, "Null connection returned from openConnection(txn)"); assertSame(con.getBlobStore(), store); } catch (UnsupportedOperationException uoe) { if (isTransactional) fail("Got unexpected UnsupportedOperationException initializing store with a transaction", uoe); } finally { tm.rollback(); if (con != null) con.close(); } } /* * BlobStoreConnection Tests */ /** * Test closing a connection. */ @Test(groups={ "connection", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testCloseConnection() throws Exception { final URI id = createId("blobCloseConn1"); // no operations, close then commit runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { } }, true); // no operations, close then roll back runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { } }, false); // one operation, close then commit runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id, null); createBlob(con, b, null); } }, true); // one operation, close then roll back runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id, ""); deleteBlob(con, b); } }, false); // clean up if (isTransactional) deleteBlob(id, "", true); else getBlob(id, null, true); // special transactional tests if (!isTransactional) return; // no operations, commit then close tm.begin(); BlobStoreConnection con = store.openConnection(tm.getTransaction(), null); tm.commit(); con.close(); assertTrue(con.isClosed()); // no operations, roll back then close tm.begin(); con = store.openConnection(tm.getTransaction(), null); tm.rollback(); con.close(); assertTrue(con.isClosed()); // one operation, commit then close tm.begin(); con = store.openConnection(tm.getTransaction(), null); Blob b = getBlob(con, id, null); createBlob(con, b, null); tm.commit(); con.close(); assertTrue(con.isClosed()); // one operation, roll back then close tm.begin(); con = store.openConnection(tm.getTransaction(), null); b = getBlob(con, id, ""); deleteBlob(con, b); tm.rollback(); con.close(); assertTrue(con.isClosed()); // clean up deleteBlob(id, "", true); assertNoBlobs(getPrefixFor("blobCloseConn")); } /** * Test a closed connection. */ @Test(groups={ "connection" }, dependsOnGroups={ "init" }) public void testClosedConnection() throws Exception { final URI id1 = createId("blobClosedConn1"); final URI id2 = createId("blobClosedConn2"); // test con.isClosed() and idempotence of con.close() runTests(new Action() { public void run(Transaction txn) throws Exception { BlobStoreConnection con = store.openConnection(txn, null); assertFalse(con.isClosed()); con.close(); assertTrue(con.isClosed()); con.close(); assertTrue(con.isClosed()); con.close(); assertTrue(con.isClosed()); } }); // test connection operations on a closed connection runTests(new Action() { public void run(Transaction txn) throws Exception { final BlobStoreConnection con = store.openConnection(txn, null); for (int idx = 0; idx < 3; idx++) { con.close(); assertTrue(con.isClosed()); assertEquals(con.getBlobStore(), store); shouldFail(new ERunnable() { @Override public void erun() throws Exception { con.getBlob(id1, null); } }, IllegalStateException.class, null); shouldFail(new ERunnable() { @Override public void erun() throws Exception { con.getBlob(null, null); } }, IllegalStateException.class, null); shouldFail(new ERunnable() { @Override public void erun() throws Exception { con.getBlob(new ByteArrayInputStream(new byte[0]), -1, null); } }, IllegalStateException.class, null); if (isListIdsSupp) { shouldFail(new ERunnable() { @Override public void erun() throws Exception { con.listBlobIds("foo"); } }, IllegalStateException.class, null); shouldFail(new ERunnable() { @Override public void erun() throws Exception { con.listBlobIds(null); } }, IllegalStateException.class, null); } if (isSyncSupp) { shouldFail(new ERunnable() { @Override public void erun() throws Exception { con.sync(); } }, IllegalStateException.class, null); } } } }); // test non-existent blob operations on a closed connection runTests(new Action() { public void run(Transaction txn) throws Exception { final BlobStoreConnection con = store.openConnection(txn, null); final Blob b = getBlob(con, id1, false); for (int idx = 0; idx < 3; idx++) { con.close(); assertTrue(con.isClosed()); assertEquals(b.getConnection(), con); assertEquals(b.getId(), id1); if (isOutputSupp) { shouldFail(new ERunnable() { @Override public void erun() throws Exception { b.openOutputStream(-1, true); } }, IllegalStateException.class, null); } shouldFail(new ERunnable() { @Override public void erun() throws Exception { b.exists(); } }, IllegalStateException.class, null); } } }); // test existing blob operations on a closed connection runTests(new Action() { public void run(Transaction txn) throws Exception { final BlobStoreConnection con = store.openConnection(txn, null); final Blob b = getBlob(con, id1, false); final Blob b2 = getBlob(con, id2, false); createBlob(con, b, "foo"); for (int idx = 0; idx < 3; idx++) { con.close(); assertTrue(con.isClosed()); assertEquals(b.getConnection(), con); assertEquals(b.getId(), id1); shouldFail(new ERunnable() { @Override public void erun() throws Exception { b.openInputStream(); } }, IllegalStateException.class, null); if (isOutputSupp) { shouldFail(new ERunnable() { @Override public void erun() throws Exception { b.openOutputStream(-1, true); } }, IllegalStateException.class, null); } shouldFail(new ERunnable() { @Override public void erun() throws Exception { b.getSize(); } }, IllegalStateException.class, null); shouldFail(new ERunnable() { @Override public void erun() throws Exception { b.exists(); } }, IllegalStateException.class, null); if (isDeleteSupp) { shouldFail(new ERunnable() { @Override public void erun() throws Exception { b.delete(); } }, IllegalStateException.class, null); } if (isMoveToSupp) { shouldFail(new ERunnable() { @Override public void erun() throws Exception { b.moveTo(b2.getId(), null); } }, IllegalStateException.class, null); } } } }); // clean up deleteBlob(id1, "foo", true); assertNoBlobs(getPrefixFor("blobClosedConn")); } /** * Test listing blobs. */ @Test(groups={ "connection", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testListBlobs() throws Exception { // check if list-ids is supported if (!isListIdsSupp) { shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { con.listBlobIds(null); } }, UnsupportedOperationException.class, null); return; } // run the tests final URI id1 = createId("blobBasicList1"); final URI id2 = createId("blobBasicList2"); listBlobs(getPrefixFor("blobBasicList"), new URI[] { }); listBlobs(getPrefixFor("blobBasicLisT"), new URI[] { }); listBlobs(getPrefixFor("blobBasicList2"), new URI[] { }); createBlob(id1, "hello", true); listBlobs(getPrefixFor("blobBasicList"), new URI[] { id1 }); listBlobs(getPrefixFor("blobBasicLisT"), new URI[] { }); listBlobs(getPrefixFor("blobBasicList2"), new URI[] { }); createBlob(id2, "bye", true); listBlobs(getPrefixFor("blobBasicList"), new URI[] { id1, id2 }); listBlobs(getPrefixFor("blobBasicLisT"), new URI[] { }); listBlobs(getPrefixFor("blobBasicList2"), new URI[] { id2 }); deleteBlob(id1, "hello", true); listBlobs(getPrefixFor("blobBasicList"), new URI[] { id2 }); listBlobs(getPrefixFor("blobBasicLisT"), new URI[] { }); listBlobs(getPrefixFor("blobBasicList2"), new URI[] { id2 }); deleteBlob(id2, "bye", true); listBlobs(getPrefixFor("blobBasicList"), new URI[] { }); listBlobs(getPrefixFor("blobBasicLisT"), new URI[] { }); listBlobs(getPrefixFor("blobBasicList2"), new URI[] { }); // test that blobs created/deleted as part of the current txn are properly shown runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b1 = getBlob(con, id1, null); Blob b2 = getBlob(con, id2, null); listBlobs(con, getPrefixFor("blobBasicList"), new URI[] { }); listBlobs(con, getPrefixFor("blobBasicLisT"), new URI[] { }); listBlobs(con, getPrefixFor("blobBasicList2"), new URI[] { }); createBlob(con, b1, "quibledyqwak"); listBlobs(con, getPrefixFor("blobBasicList"), new URI[] { id1 }); listBlobs(con, getPrefixFor("blobBasicLisT"), new URI[] { }); listBlobs(con, getPrefixFor("blobBasicList1"), new URI[] { id1 }); createBlob(con, b2, "waflebleeblegorm"); listBlobs(con, getPrefixFor("blobBasicList"), new URI[] { id1, id2 }); listBlobs(con, getPrefixFor("blobBasicLisT"), new URI[] { }); listBlobs(con, getPrefixFor("blobBasicList2"), new URI[] { id2 }); deleteBlob(con, b1); listBlobs(con, getPrefixFor("blobBasicList"), new URI[] { id2 }); listBlobs(con, getPrefixFor("blobBasicLisT"), new URI[] { }); listBlobs(con, getPrefixFor("blobBasicList2"), new URI[] { id2 }); deleteBlob(con, b2); listBlobs(con, getPrefixFor("blobBasicList"), new URI[] { }); listBlobs(con, getPrefixFor("blobBasicLisT"), new URI[] { }); listBlobs(con, getPrefixFor("blobBasicList1"), new URI[] { }); } }); } /** * Test sync. */ @Test(groups={ "connection", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testSync() throws Exception { // check if sync is supported if (!isSyncSupp) { shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { con.sync(); } }, UnsupportedOperationException.class, null); return; } // nothing really to test, other than that it doesn't throw an exception final URI id1 = createId("blobConSync1"); final URI id2 = createId("blobConSync2"); runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { con.sync(); } }); runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id1, null); createBlob(con, b, "foos"); con.sync(); deleteBlob(con, b); } }); runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id1, null); createBlob(con, b, "foos"); deleteBlob(con, b); con.sync(); } }); runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id1, null); createBlob(con, b, "foos"); b = moveBlob(con, b, id2, "foos"); con.sync(); deleteBlob(con, b); } }); assertNoBlobs(getPrefixFor("blobConSync")); } /* * Blob tests. */ /** * Test id validation. */ @Test(groups={ "blob", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testIdValidation() throws Exception { // valid should work URI valId = createId("blobValidId"); createBlob(valId, null, true); deleteBlob(valId, "", true); // invalid should not final URI invId = getInvalidId(); if (invId != null) { shouldFail(new ERunnable() { @Override public void erun() throws Exception { createBlob(invId, null, true); } }, UnsupportedIdException.class, invId); } } /** * Test id generation. */ @Test(groups={ "blob", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testIdGeneration() throws Exception { // check if id-gen is supported if (!isIdGenSupp) { shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { getBlob(con, null, false); } }, UnsupportedOperationException.class, null); return; } // null id should work runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, null, false); if (b.exists()) deleteBlob(con, b); } }, false); } /** * Test canonical-id. */ @Test(groups={ "blob", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testCanonicalId() throws Exception { // set up final URI id = createId("blobCanonicalId"); final URI[] aliases = getAliases(id); // test runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { if (aliases == null) { Blob b = getBlob(con, id, false); assertNull(b.getCanonicalId()); } else { for (URI alias : aliases) { Blob b = getBlob(con, alias, false); assertEquals(b.getCanonicalId(), id); } } } }); } /** * Test non-existent blob. */ @Test(groups={ "blob", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testNonExistentBlob() throws Exception { final URI id = createId("blobNonExistent1"); runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id, false); assertEquals(b.getConnection(), con); assertEquals(b.getId(), id); } }); shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { getBlob(con, id, false).getSize(); } }, MissingBlobException.class, id); shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { getBlob(con, id, false).openInputStream().close(); } }, MissingBlobException.class, id); } /** * Test con.getBlob(InputStream, ...). */ @Test(groups={ "blob", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testBlobFromStream() throws Exception { // check if id-gen is supported if (!isIdGenSupp) { shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { con.getBlob(new ByteArrayInputStream(new byte[0]), -1, null); } }, UnsupportedOperationException.class, null); return; } // null stream should fail shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { con.getBlob(null, -1, null); } }, NullPointerException.class, null); // basic input-stream should work testBlobFromStream("", -1); testBlobFromStream("", 0); testBlobFromStream("", 10); testBlobFromStream("Tyrant, remember?", -1); testBlobFromStream("Tyrant, remember?", 0); testBlobFromStream("Tyrant, remember?", 10); testBlobFromStream("Tyrant, remember?", 17); testBlobFromStream("Tyrant, remember?", 27); } protected void testBlobFromStream(final String body, final long estSize) throws Exception { runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = con.getBlob(new ByteArrayInputStream(body.getBytes("UTF-8")), estSize, null); assertTrue(b.exists()); assertTrue(con.getBlob(b.getId(), null).exists()); assertEquals(getBody(b), body); assertEquals(getBody(con.getBlob(b.getId(), null)), body); deleteBlob(con, b); } }); } /** * Test id validation. */ @Test(groups={ "blob", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testDelete() throws Exception { // set up final URI id1 = createId("blobDelete1"); createBlob(id1, null, true); // check if delete is supported if (!isDeleteSupp) { shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id1, ""); b.delete(); } }, UnsupportedOperationException.class, null); getBlob(id1, "", true); // make sure it's still exists deleteBlob(id1, "", true); return; } // delete of an existing blob deleteBlob(id1, "", true); // delete of a non-existent blob should succeed deleteBlob(id1, null, true); // clean up assertNoBlobs(getPrefixFor("blobDelete")); } /** * Test openInputStream. */ @Test(groups={ "blob", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testInputStream() throws Exception { // set up final URI id1 = createId("blobInputStream1"); // openInputStream on non-existent blob shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id1, false); b.openInputStream().close(); } }, MissingBlobException.class, id1); // openInputStream on existing blob runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { String body = "For the love of God, Montresor"; Blob b = getBlob(con, id1, null); createBlob(con, b, body); // read partial body byte[] buf = new byte[100]; InputStream is = b.openInputStream(); int got = is.read(buf, 0, 10); assertTrue(got > 0 && got <= 10, "Invalid number of bytes read: " + got); int len = got; is.close(); assertEquals(new String(buf, 0, len, "UTF-8"), body.substring(0, len)); is = b.openInputStream(); got = 0; while (got < len) { int skipped = (int) is.skip(len - got); assertTrue(skipped > 0 && skipped <= (len - got), "Invalid number of bytes skipped: " + skipped); got += skipped; } got = is.read(buf, len, 2); assertTrue(got > 0 && got <= 2, "Invalid number of bytes read: " + got); len += got; got = is.read(buf, len, 5); assertTrue(got > 0 && got <= 5, "Invalid number of bytes read: " + got); len += got; is.close(); assertEquals(new String(buf, 0, len, "UTF-8"), body.substring(0, len)); // read whole body assertEquals(getBody(b), body); deleteBlob(con, b); } }); // clean up assertNoBlobs(getPrefixFor("blobInputStream")); } /** * Test openOutputStream. */ @Test(groups={ "blob", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testOutputStream() throws Exception { // check if create is supported if (!isOutputSupp) { shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, createId("blobOutputStream1"), false); b.openOutputStream(-1, true).close(); } }, UnsupportedOperationException.class, null); return; } // set up final URI id1 = createId("blobOutputStream1"); // 0 length blob, no write testOutputStream(id1, null, "a", -1); testOutputStream(id1, null, "a", 0); testOutputStream(id1, null, "a", 20); // 0 length blob, empty write testOutputStream(id1, "", "a", -1); testOutputStream(id1, "", "a", 0); testOutputStream(id1, "", "a", 20); // >0 length blob testOutputStream(id1, "foo bar", "a", -1); testOutputStream(id1, "foo bar", "a", 0); testOutputStream(id1, "foo bar", "a", 5); testOutputStream(id1, "foo bar", "a", 7); testOutputStream(id1, "foo bar", "a", 17); // clean up assertNoBlobs(getPrefixFor("blobOutputStream")); } protected void testOutputStream(final URI id, final String body, final String body2, final long estSize) throws Exception { runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { final Blob b = getBlob(con, id, null); testOutputStream(b, body, estSize, true); // test create testOutputStream(b, body2, estSize, true); // test overwrite deleteBlob(con, b); testOutputStream(b, body, estSize, false); // test create shouldFail(new ERunnable() { // test !overwrite @Override public void erun() throws Exception { testOutputStream(b, body2, estSize, false); } }, DuplicateBlobException.class, id); assertEquals(getBody(b), body != null ? body : ""); deleteBlob(con, b); } }); } protected void testOutputStream(Blob b, String body, long estSize, boolean overwrite) throws Exception { if (body != null) { setBody(b, body, estSize, overwrite); assertEquals(getBody(b), body); } else { b.openOutputStream(estSize, overwrite).close(); assertEquals(getBody(b), ""); } } /** * Test changing a blob's value. */ @Test(groups={ "blob", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testBlobUpdate() throws Exception { final URI id = createId("blobBlobUpdate1"); runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id, null); createBlob(con, b, null); setBlob(con, b, "value1"); setBlob(con, b, "value2"); setBlob(con, b, "value3"); } }, true); runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id, "value3"); setBlob(con, b, "value4"); setBlob(con, b, "value5"); } }, true); deleteBlob(id, "value5", true); getBlob(id, null, true); assertNoBlobs(getPrefixFor("blobBlobUpdate")); } /** * Test move. */ @Test(groups={ "blob", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testMoveTo() throws Exception { // check if move is supported if (!isMoveToSupp) { shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, createId("blobMoveTo1"), false); b.moveTo(b.getId(), null); } }, UnsupportedOperationException.class, null); return; } // set up final URI id1 = createId("blobMoveTo1"); final URI id2 = createId("blobMoveTo2"); final URI id3 = createId("blobMoveTo3"); final URI id4 = createId("blobMoveTo4"); createBlob(id1, "foo", true); createBlob(id4, "bar", true); // move blob from id1 to id2 renameBlob(id1, id2, "foo", true); // move from non-existent blob should fail shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob ob = getBlob(con, id1, false); ob.moveTo(id3, null); } }, MissingBlobException.class, id1); getBlob(id1, null, true); getBlob(id3, null, true); // move to existing blob should fail shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob ob = getBlob(con, id2, "foo"); ob.moveTo(id4, null); } }, DuplicateBlobException.class, id4); getBlob(id2, "foo", true); getBlob(id4, "bar", true); // move a non-existent blob onto itself should fail shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id1, false); b.moveTo(id1, null); } }, MissingBlobException.class, id1); getBlob(id1, null, true); // move an existing blob onto itself should fail shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id2, "foo"); b.moveTo(id2, null); } }, DuplicateBlobException.class, id2); getBlob(id2, "foo", true); // move to null if (!isIdGenSupp) { // move to null should fail shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id2, "foo"); b.moveTo(null, null); } }, UnsupportedOperationException.class, null); getBlob(id2, "foo", true); } else { // null id should work runTests(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob b = getBlob(con, id2, "foo"); Blob b2 = b.moveTo(null, null); // undo for other tests b2.moveTo(id2, null); } }, false); } // move to incompatible blob should fail final URI inv = getInvalidId(); if (inv != null) { shouldFail(new ConAction() { public void run(BlobStoreConnection con) throws Exception { Blob ob = getBlob(con, id2, "foo"); ob.moveTo(inv, null); } }, UnsupportedIdException.class, inv); } getBlob(id2, "foo", true); // clean up deleteBlob(id2, "foo", true); deleteBlob(id4, "bar", true); assertNoBlobs(getPrefixFor("blobMoveTo")); } /* * Transaction tests. */ /** * Basic create, get, rename, delete. */ @Test(groups={ "transaction", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testBasicCommit() throws Exception { if (!isTransactional) return; URI id = createId("blobBasicCommit1"); createBlob(id, "hello", true); getBlob(id, "hello", true); if (isMoveToSupp) { URI id2 = createId("blobBasicCommit2"); renameBlob(id, id2, "hello", true); getBlob(id, null, true); getBlob(id2, "hello", true); id = id2; } setBlob(id, "bye bye", true); getBlob(id, "bye bye", true); deleteBlob(id, "bye bye", true); getBlob(id, null, true); assertNoBlobs(getPrefixFor("blobBasicCommit")); } /** * Basic create, get, rename, delete with rollbacks. */ @Test(groups={ "transaction", "manipulatesBlobs" }, dependsOnGroups={ "init" }) public void testBasicRollback() throws Exception { if (!isTransactional) return; URI id = createId("blobBasicRollback1"); // roll back a create createBlob(id, "hello", false); getBlob(id, null, false); // create, roll back a rename createBlob(id, "hello", true); getBlob(id, "hello", true); if (isMoveToSupp) { URI id2 = createId("blobBasicRollback2"); renameBlob(id, id2, "hello", false); getBlob(id2, null, true); } getBlob(id, "hello", true); // update and roll back setBlob(id, "bye bye", false); getBlob(id, "hello", true); // roll back a delete deleteBlob(id, "hello", false); getBlob(id, "hello", true); // delete deleteBlob(id, "hello", true); getBlob(id, null, true); assertNoBlobs(getPrefixFor("blobBasicRollback")); } /** * Test that things get cleaned up. This runs after all other tests that create or otherwise * manipulate blobs. */ @Test(groups={ "post" }, dependsOnGroups={ "manipulatesBlobs" }) public void testCleanup() throws Exception { assertNoBlobs(null); assertNoBlobs(""); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy