de.zib.scalaris.TransactionSingleOpTest.java Source code

Java tutorial

Introduction

Here is the source code for de.zib.scalaris.TransactionSingleOpTest.java

Source

/**
 *  Copyright 2007-2011 Zuse Institute Berlin
 *
 *   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 de.zib.scalaris;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;

import org.apache.commons.codec.binary.Base64;
import org.junit.Test;

import com.ericsson.otp.erlang.OtpErlangObject;

import de.zib.scalaris.TransactionSingleOp.RequestList;
import de.zib.scalaris.TransactionSingleOp.ResultList;
import de.zib.scalaris.operations.ReadOp;
import de.zib.scalaris.operations.ReadRandomFromListOp;
import de.zib.scalaris.operations.WriteOp;

/**
 * Unit test for the {@link TransactionSingleOp} class.
 *
 * @author Nico Kruber, kruber@zib.de
 * @version 3.2
 * @since 2.0
 */
public class TransactionSingleOpTest {
    private final static long testTime = System.currentTimeMillis();

    private final static String[] testData = { "ahz2ieSh", "wooPhu8u", "quai9ooK", "Oquae4ee", "Airier1a",
            "Boh3ohv5", "ahD3Saog", "EM5ooc4i", "Epahrai8", "laVahta7", "phoo6Ahj", "Igh9eepa", "aCh4Lah6",
            "ooT0ath5", "uuzau4Ie", "Iup6mae6",
            //        "xie7iSie", "ail8yeeP", "ooZ4eesi", "Ahn7ohph", "Ohy5moo6", "xooSh9Oo", "ieb6eeS7", "Thooqu9h",
            //        "eideeC9u", "phois3Ie", "EimaiJ2p", "sha6ahR1", "Pheih3za", "bai4eeXe", "rai0aB7j", "xahXoox6",
            //        "Xah4Okeg", "cieG8Yae", "Pe9Ohwoo", "Eehig6ph", "Xe7rooy6", "waY2iifu", "kemi8AhY", "Che7ain8",
            //        "ohw6seiY", "aegh1oBa", "thoh9IeG", "Kee0xuwu", "Gohng8ee", "thoh9Chi", "aa4ahQuu", "Iesh5uge",
            //        "Ahzeil8n", "ieyep5Oh", "xah3IXee", "Eefa5qui", "kai8Muuf", "seeCe0mu", "cooqua5Y", "Ci3ahF6z",
            //        "ot0xaiNu", "aewael8K", "aev3feeM", "Fei7ua5t", "aeCa6oph", "ag2Aelei", "Shah1Pho", "ePhieb0N",
            //        "Uqu7Phup", "ahBi8voh", "oon3aeQu", "Koopa0nu", "xi0quohT", "Oog4aiph", "Aip2ag5D", "tirai7Ae",
            //        "gi0yoePh", "uay7yeeX", "aeb6ahC1", "OoJeic2a", "ieViom1y", "di0eeLai", "Taec2phe", "ID2cheiD",
            //        "oi6ahR5M", "quaiGi8W", "ne1ohLuJ", "DeD0eeng", "yah8Ahng", "ohCee2ie", "ecu1aDai", "oJeijah4",
            //        "Goo9Una1", "Aiph3Phi", "Ieph0ce5", "ooL6cae7", "nai0io1H", "Oop2ahn8", "ifaxae7O", "NeHai1ae",
            //        "Ao8ooj6a", "hi9EiPhi", "aeTh9eiP", "ao8cheiH", "Yieg3sha", "mah7cu2D", "Uo5wiegi", "Oowei0ya",
            //        "efeiDee7", "Oliese6y", "eiSh1hoh", "Joh6hoh9", "zib6Ooqu", "eejiJie4", "lahZ3aeg", "keiRai1d",
            //        "Fei0aewe", "aeS8aboh", "hae3ohKe", "Een9ohQu", "AiYeeh7o", "Yaihah4s", "ood4Giez", "Oumai7te",
            //        "hae2kahY", "afieGh4v", "Ush0boo0", "Ekootee5", "Ya8iz6Ie", "Poh6dich", "Eirae4Ah", "pai8Eeme",
            //        "uNah7dae", "yo3hahCh", "teiTh7yo", "zoMa5Cuv", "ThiQu5ax", "eChi5caa", "ii9ujoiV", "ge7Iekui",
            "sai2aiTa", "ohKi9rie", "ei2ioChu", "aaNgah9y", "ooJai1Ie", "shoh0oH9", "Ool4Ahya", "poh0IeYa",
            "Uquoo0Il", "eiGh4Oop", "ooMa0ufe", "zee6Zooc", "ohhao4Ah", "Uweekek5", "aePoos9I", "eiJ9noor",
            "phoong1E", "ianieL2h", "An7ohs4T", "Eiwoeku3", "sheiS3ao", "nei5Thiw", "uL5iewai", "ohFoh9Ae" };

    static {
        // determine good/bad nodes:
        final ConnectionFactory cf = ConnectionFactory.getInstance();
        cf.testAllNodes();
        // set not to automatically try reconnects (auto-retries prevent ConnectionException tests from working):
        ((DefaultConnectionPolicy) cf.getConnectionPolicy()).setMaxRetries(0);
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#TransactionSingleOp()}.
     * @throws ConnectionException
     */
    @Test
    public void testTransactionSingleOp1() throws ConnectionException {
        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.closeConnection();
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#TransactionSingleOp(Connection)}.
     * @throws ConnectionException
     */
    @Test
    public void testTransactionSingleOp2() throws ConnectionException {
        final TransactionSingleOp conn = new TransactionSingleOp(
                ConnectionFactory.getInstance().createConnection("test"));
        conn.closeConnection();
    }

    /**
     * Test method for {@link TransactionSingleOp#closeConnection()} trying to
     * close the connection twice.
     *
     * @throws UnknownException
     * @throws ConnectionException
     */
    @Test
    public void testDoubleClose() throws ConnectionException {
        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.closeConnection();
        conn.closeConnection();
    }

    /**
     * Test method for {@link TransactionSingleOp#read(String)}.
     *
     * @throws NotFoundException
     * @throws UnknownException
     * @throws ConnectionException
     */
    @Test(expected = NotFoundException.class)
    public void testRead_NotFound() throws ConnectionException, UnknownException, NotFoundException {
        final String key = "_Read_NotFound";
        final TransactionSingleOp conn = new TransactionSingleOp();
        try {
            conn.read(testTime + key);
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for {@link TransactionSingleOp#read(String)} with a closed connection.
     *
     * @throws NotFoundException
     * @throws UnknownException
     * @throws ConnectionException
     */
    @Test(expected = ConnectionException.class)
    public void testRead_NotConnected() throws ConnectionException, UnknownException, NotFoundException {
        final String key = "_Read_NotConnected";
        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.closeConnection();
        conn.read(testTime + key);
    }

    /**
     * Test method for {@link TransactionSingleOp#write(String, String)} with a
     * closed connection.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     */
    @Test(expected = ConnectionException.class)
    public void testWriteString_NotConnected()
            throws ConnectionException, UnknownException, NotFoundException, AbortException {
        final String key = "_WriteString_NotConnected";
        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.closeConnection();
        conn.write(testTime + key, testData[0]);
    }

    /**
     * Test method for {@link TransactionSingleOp#write(String, String)} and
     * {@link TransactionSingleOp#read(String)}. Writes strings and uses a
     * distinct key for each value. Tries to read the data afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws EmptyListException
     */
    @Test
    public void testWriteString1()
            throws ConnectionException, UnknownException, NotFoundException, AbortException, EmptyListException {
        final String key = "_WriteString1_";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            for (int i = 0; i < testData.length; ++i) {
                conn.write(testTime + key + i, testData[i]);
            }

            // now try to read the data:
            for (int i = 0; i < testData.length; ++i) {
                final String actual = conn.read(testTime + key + i).stringValue();
                assertEquals(testData[i], actual);
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for {@link TransactionSingleOp#write(String, String)} and
     * {@link TransactionSingleOp#read(String)}. Writes strings and uses a
     * single key for all the values. Tries to read the data afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     */
    @Test
    public void testWriteString2() throws ConnectionException, UnknownException, NotFoundException, AbortException {
        final String key = "_WriteString2";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            for (final String element : testData) {
                conn.write(testTime + key, element);
            }

            // now try to read the data:
            final String actual = conn.read(testTime + key).stringValue();
            assertEquals(testData[testData.length - 1], actual);
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for {@link TransactionSingleOp#write(String, Integer)} with a
     * closed connection.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     */
    @Test(expected = ConnectionException.class)
    public void testWriteNumber_NotConnected()
            throws ConnectionException, UnknownException, NotFoundException, AbortException {
        final String key = "_WriteString_NotConnected";
        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.closeConnection();
        conn.write(testTime + key, 0);
    }

    /**
     * Test method for {@link TransactionSingleOp#write(String, Integer)},
     * {@link TransactionSingleOp#read(String)} and {@link ReadRandomFromListOp}
     * . Writes integers and uses a distinct key for each value. Tries to read
     * the data afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws EmptyListException
     */
    @Test
    public void testWriteNumber1()
            throws ConnectionException, UnknownException, NotFoundException, AbortException, EmptyListException {
        final String key = "_WriteString1_";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            for (int i = 0; i < testData.length; ++i) {
                conn.write(testTime + key + i, i);
            }

            // now try to read the data:
            for (int i = 0; i < testData.length; ++i) {
                final int actual = conn.read(testTime + key + i).intValue();
                assertEquals(i, actual);

                final ReadRandomFromListOp op = new ReadRandomFromListOp(testTime + key + i);
                final ResultList resultList = conn.req_list(op);
                assertEquals(op, resultList.get(0));
                try {
                    final ReadRandomFromListOp.Result randResult = op.processResultSingle();
                    assertTrue("Trying to read random list entry from non-list should fail: " + randResult, false);
                } catch (final NotAListException e) {
                    // ok
                }
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for {@link TransactionSingleOp#write(String, Integer)},
     * {@link TransactionSingleOp#read(String)} and {@link ReadRandomFromListOp}
     * . Writes strings and uses a single key for all the values. Tries to read
     * the data afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws EmptyListException
     */
    @Test
    public void testWriteNumber2()
            throws ConnectionException, UnknownException, NotFoundException, AbortException, EmptyListException {
        final String key = "_WriteString2";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            for (int i = 0; i < testData.length; ++i) {
                conn.write(testTime + key, i);
            }

            // now try to read the data:
            final int actual = conn.read(testTime + key).intValue();
            assertEquals(testData.length - 1, actual);

            final ReadRandomFromListOp op = new ReadRandomFromListOp(testTime + key);
            final ResultList resultList = conn.req_list(op);
            assertEquals(op, resultList.get(0));
            try {
                final ReadRandomFromListOp.Result randResult = op.processResultSingle();
                assertTrue("Trying to read random list entry from non-list should fail: " + randResult, false);
            } catch (final NotAListException e) {
                // ok
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for {@link TransactionSingleOp#write(String, List)} with a
     * closed connection.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     *
     * @since 3.2
     */
    @Test(expected = ConnectionException.class)
    public void testWriteList_NotConnected()
            throws ConnectionException, UnknownException, NotFoundException, AbortException {
        final String key = "_WriteList_NotConnected";
        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.closeConnection();
        final ArrayList<String> list = new ArrayList<String>();
        list.add(testData[0]);
        list.add(testData[1]);
        conn.write(testTime + key, list);
    }

    /**
     * Test method for {@link TransactionSingleOp#write(String, List)},
     * {@link TransactionSingleOp#read(String)} and {@link ReadRandomFromListOp}
     * . Writes lists and uses a distinct key for each value. Tries to read the
     * data afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws NotAListException
     * @throws EmptyListException
     *
     * @since 3.2
     */
    @Test
    public void testWriteList1() throws ConnectionException, UnknownException, NotFoundException, AbortException,
            EmptyListException, NotAListException {
        final String key = "_WriteList1_";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            for (int i = 0; i < (testData.length - 1); ++i) {
                final ArrayList<String> list = new ArrayList<String>();
                if ((i % 2) == 0) {
                    list.add(testData[i]);
                    list.add(testData[i + 1]);
                }
                conn.write(testTime + key + i, list);
            }

            // now try to read the data:

            for (int i = 0; i < (testData.length - 1); ++i) {
                final ArrayList<String> expected = new ArrayList<String>();
                if ((i % 2) == 0) {
                    expected.add(testData[i]);
                    expected.add(testData[i + 1]);
                }
                final List<String> actual = conn.read(testTime + key + i).stringListValue();
                assertEquals(expected, actual);

                final ReadRandomFromListOp op = new ReadRandomFromListOp(testTime + key + i);
                final ResultList resultList = conn.req_list(op);
                assertEquals(op, resultList.get(0));
                try {
                    final ReadRandomFromListOp.Result randResult = op.processResultSingle();
                    if ((i % 2) == 0) {
                        assertEquals(expected.size(), randResult.listLength);
                        assertTrue(expected.toString() + ".contains(" + randResult.randomElement + ")",
                                expected.contains(randResult.randomElement.stringValue()));
                    } else {
                        assertTrue("Trying to read random list entry from an empty list should fail: " + randResult,
                                false);
                    }
                } catch (final EmptyListException e) {
                    if ((i % 2) == 0) {
                        assertTrue("Got EmptyListException on non-empty list: " + e, false);
                    }
                }
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for {@link TransactionSingleOp#write(String, List)},
     * {@link TransactionSingleOp#read(String)} and {@link ReadRandomFromListOp}
     * . Writes lists and uses a single key for all the values. Tries to read
     * the data afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws NotAListException
     * @throws EmptyListException
     *
     * @since 3.2
     */
    @Test
    public void testWriteList2() throws ConnectionException, UnknownException, NotFoundException, AbortException,
            EmptyListException, NotAListException {
        final String key = "_WriteList2";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            final List<String> list = new ArrayList<String>();
            for (int i = 0; i < (testData.length - 1); i += 2) {
                list.clear();
                list.add(testData[i]);
                list.add(testData[i + 1]);
                conn.write(testTime + key, list);
            }

            // now try to read the data:

            final List<String> actual = conn.read(testTime + key).stringListValue();
            final List<String> expected = list;
            assertEquals(expected, actual);

            final ReadRandomFromListOp op = new ReadRandomFromListOp(testTime + key);
            final ResultList resultList = conn.req_list(op);
            assertEquals(op, resultList.get(0));
            final ReadRandomFromListOp.Result randResult = op.processResultSingle();
            assertEquals(expected.size(), randResult.listLength);
            assertTrue(expected.toString() + ".contains(" + randResult.randomElement + ")",
                    expected.contains(randResult.randomElement.stringValue()));
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#testAndSet(String, String, List)}
     * with a closed connection.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws KeyChangedException
     *
     * @since 3.2
     */
    @Test(expected = ConnectionException.class)
    public void testTestAndSetList_NotConnected()
            throws ConnectionException, UnknownException, NotFoundException, AbortException, KeyChangedException {
        final String key = "_TestAndSetList_NotConnected";
        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.closeConnection();
        final ArrayList<String> list = new ArrayList<String>();
        list.add(testData[0]);
        list.add(testData[1]);
        conn.testAndSet(testTime + key, "fail", list);
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#testAndSet(String, String, List)}.
     * Tries test_and_set with a non-existing key.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws KeyChangedException
     *
     * @since 3.2
     */
    @Test(expected = NotFoundException.class)
    public void testTestAndSetList_NotFound()
            throws ConnectionException, UnknownException, NotFoundException, AbortException, KeyChangedException {
        final String key = "_TestAndSetList_NotFound";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            final ArrayList<String> list = new ArrayList<String>();
            list.add(testData[0]);
            list.add(testData[1]);
            conn.testAndSet(testTime + key, "fail", list);
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#testAndSet(String, List, List)},
     * {@link TransactionSingleOp#read(String)}
     * and {@link TransactionSingleOp#write(String, List)}.
     * Writes a list and tries to overwrite it using test_and_set
     * knowing the correct old value. Tries to read the data afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws KeyChangedException
     *
     * @since 3.9
     */
    @Test
    public void testTestAndSetList1a()
            throws ConnectionException, UnknownException, NotFoundException, AbortException, KeyChangedException {
        final String key = "_TestAndSetList1a";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            // first write all values:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                final ArrayList<String> list = new ArrayList<String>();
                list.add(testData[i]);
                list.add(testData[i + 1]);
                conn.write(testTime + key + i, list);
            }

            // now try to overwrite them using test_and_set:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                final ArrayList<String> old_list = new ArrayList<String>();
                old_list.add(testData[i]);
                old_list.add(testData[i + 1]);
                final ArrayList<String> new_list = new ArrayList<String>();
                new_list.add(testData[i + 1]);
                new_list.add(testData[i]);
                conn.testAndSet(testTime + key + i, old_list, new_list);
            }

            // now try to read the data:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                final List<String> actual = conn.read(testTime + key + i).stringListValue();
                final ArrayList<String> expected = new ArrayList<String>();
                expected.add(testData[i + 1]);
                expected.add(testData[i]);
                assertEquals(expected, actual);
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#testAndSet(String, Object, List)},
     * {@link TransactionSingleOp#read(String)}
     * and {@link TransactionSingleOp#write(String, List)}.
     * Writes a list and tries to overwrite it using test_and_set
     * knowing the wrong old value. Tries to read the data afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     *
     * @since 3.9
     */
    @Test
    public void testTestAndSetList1b()
            throws ConnectionException, UnknownException, NotFoundException, AbortException {
        final String key = "_TestAndSetList1b";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            // first write all values:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                final ArrayList<String> list = new ArrayList<String>();
                list.add(testData[i]);
                list.add(testData[i + 1]);
                conn.write(testTime + key + i, list);
            }

            // now try to overwrite them using test_and_set:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                final int new_value = 1;
                try {
                    conn.testAndSet(testTime + key + i, "fail", new_value);
                    // a key changed exception must be thrown
                    assertTrue(false);
                } catch (final KeyChangedException e) {
                    final ArrayList<String> expected = new ArrayList<String>();
                    expected.add(testData[i]);
                    expected.add(testData[i + 1]);
                    assertEquals(expected, e.getOldValue().stringListValue());
                }
            }

            // now try to read the data:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                final List<String> actual = conn.read(testTime + key + i).stringListValue();
                final ArrayList<String> expected = new ArrayList<String>();
                expected.add(testData[i]);
                expected.add(testData[i + 1]);
                assertEquals(expected, actual);
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#testAndSet(String, List, List)},
     * {@link TransactionSingleOp#read(String)} and
     * {@link TransactionSingleOp#write(String, List)}. Writes a list and tries
     * to overwrite it using test_and_set knowing the correct old value and
     * using a single key for all the values. Tries to read the data afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws KeyChangedException
     *
     * @since 3.9
     */
    @Test
    public void testTestAndSetList2a()
            throws ConnectionException, UnknownException, NotFoundException, AbortException, KeyChangedException {
        final String key = "_TestAndSetList2a";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            // first write all values:
            final ArrayList<String> list = new ArrayList<String>();
            list.add(testData[0]);
            list.add(testData[1]);
            conn.write(testTime + key, list);

            // now try to overwrite them using test_and_set:
            for (int i = 1; i < (testData.length - 1); ++i) {
                final ArrayList<String> old_list = new ArrayList<String>();
                old_list.add(testData[i - 1]);
                old_list.add(testData[i]);
                final ArrayList<String> new_list = new ArrayList<String>();
                new_list.add(testData[i]);
                new_list.add(testData[i + 1]);
                conn.testAndSet(testTime + key, old_list, new_list);
            }

            // now try to read the data:
            final List<String> actual = conn.read(testTime + key).stringListValue();
            final ArrayList<String> expected = new ArrayList<String>();
            expected.add(testData[testData.length - 2]);
            expected.add(testData[testData.length - 1]);
            assertEquals(expected, actual);
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#testAndSet(String, Object, List)},
     * {@link TransactionSingleOp#read(String)} and
     * {@link TransactionSingleOp#write(String, List)}.
     * Writes a list and tries to overwrite it using test_and_set knowing the
     * wrong old value and using a single key for all the values. Tries to read
     * the data afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     *
     * @since 3.9
     */
    @Test
    public void testTestAndSetList2b()
            throws ConnectionException, UnknownException, NotFoundException, AbortException {
        final String key = "_TestAndSetList2b";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            // first write all values:
            final ArrayList<String> list = new ArrayList<String>();
            list.add(testData[0]);
            list.add(testData[1]);
            conn.write(testTime + key, list);

            // now try to overwrite them using test_and_set:
            for (int i = 0; i < (testData.length - 1); ++i) {
                final int new_value = 1;
                try {
                    conn.testAndSet(testTime + key, "fail", new_value);
                    // a key changed exception must be thrown
                    assertTrue(false);
                } catch (final KeyChangedException e) {
                    final ArrayList<String> expected = new ArrayList<String>();
                    expected.add(testData[0]);
                    expected.add(testData[1]);
                    assertEquals(expected, e.getOldValue().stringListValue());
                }
            }

            // now try to read the data:
            final List<String> actual = conn.read(testTime + key).stringListValue();
            final ArrayList<String> expected = list;
            assertEquals(expected, actual);
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#testAndSet(String, String, String)}
     * with a closed connection.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws KeyChangedException
     *
     * @since 2.7
     */
    @Test(expected = ConnectionException.class)
    public void testTestAndSetString_NotConnected()
            throws ConnectionException, UnknownException, NotFoundException, AbortException, KeyChangedException {
        final String key = "_TestAndSetString_NotConnected";
        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.closeConnection();
        conn.testAndSet(testTime + key, testData[0], testData[1]);
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#testAndSet(String, String, String)}.
     * Tries test_and_set with a non-existing key.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws KeyChangedException
     *
     * @since 2.7
     */
    @Test(expected = NotFoundException.class)
    public void testTestAndSetString_NotFound()
            throws ConnectionException, UnknownException, NotFoundException, AbortException, KeyChangedException {
        final String key = "_TestAndSetString_NotFound";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            conn.testAndSet(testTime + key, testData[0], testData[1]);
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#testAndSet(String, String, String)},
     * {@link TransactionSingleOp#read(String)}
     * and {@link TransactionSingleOp#write(String, Object)}.
     * Writes a string and tries to overwrite it using test_and_set
     * knowing the correct old value. Tries to read the string afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     * @throws KeyChangedException
     *
     * @since 2.7
     */
    @Test
    public void testTestAndSetString1()
            throws ConnectionException, UnknownException, NotFoundException, AbortException, KeyChangedException {
        final String key = "_TestAndSetString1";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            // first write all values:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                conn.write(testTime + key + i, testData[i]);
            }

            // now try to overwrite them using test_and_set:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                conn.testAndSet(testTime + key + i, testData[i], testData[i + 1]);
            }

            // now try to read the data:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                assertEquals(testData[i + 1], conn.read(testTime + key + i).stringValue());
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for
     * {@link TransactionSingleOp#testAndSet(String, String, String)},
     * {@link TransactionSingleOp#read(String)}
     * and {@link TransactionSingleOp#write(String, Object)}.
     * Writes a string and tries to overwrite it using test_and_set
     * knowing the wrong old value. Tries to read the string afterwards.
     *
     * @throws UnknownException
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws AbortException
     *
     * @since 2.7
     */
    @Test
    public void testTestAndSetString2()
            throws ConnectionException, UnknownException, NotFoundException, AbortException {
        final String key = "_TestAndSetString2";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            // first write all values:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                conn.write(testTime + key + i, testData[i]);
            }

            // now try to overwrite them using test_and_set:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                try {
                    conn.testAndSet(testTime + key + i, testData[i + 1], "fail");
                    // a key changed exception must be thrown
                    assertTrue(false);
                } catch (final KeyChangedException e) {
                    assertEquals(testData[i], e.getOldValue().stringValue());
                }
            }

            // now try to read the data:
            for (int i = 0; i < (testData.length - 1); i += 2) {
                assertEquals(testData[i], conn.read(testTime + key + i).stringValue());
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for {@link TransactionSingleOp#req_list(RequestList)} with an
     * empty request list.
     *
     * @throws ConnectionException
     * @throws UnknownException
     * @throws AbortException
     */
    @Test
    public void testReqList_Empty() throws ConnectionException, UnknownException, AbortException {
        final TransactionSingleOp t = new TransactionSingleOp();
        try {
            t.req_list(new RequestList());
        } finally {
            t.closeConnection();
        }
    }

    /**
     * Test method for {@link TransactionSingleOp#req_list(RequestList)} with a
     * mixed request list.
     *
     * @throws ConnectionException
     * @throws UnknownException
     * @throws AbortException
     * @throws NotFoundException
     */
    @Test
    public void testReqList1() throws ConnectionException, UnknownException, AbortException, NotFoundException {
        final String key = "_ReqList1_";
        final TransactionSingleOp conn = new TransactionSingleOp();

        try {
            final RequestList readRequests = new RequestList();
            final RequestList firstWriteRequests = new RequestList();
            final RequestList writeRequests = new RequestList();
            for (int i = 0; i < testData.length; ++i) {
                if ((i % 2) == 0) {
                    firstWriteRequests.addOp(new WriteOp(testTime + key + i, "first_" + testData[i]));
                }
                writeRequests.addOp(new WriteOp(testTime + key + i, "second_" + testData[i]));
                readRequests.addOp(new ReadOp(testTime + key + i));
            }

            ResultList results = conn.req_list(firstWriteRequests);
            // evaluate the first write results:
            for (int i = 0; i < firstWriteRequests.size(); ++i) {
                results.processWriteAt(i);
            }

            results = conn.req_list(readRequests);
            assertEquals(readRequests.size(), results.size());
            // now evaluate the read results:
            for (int i = 0; i < readRequests.size(); ++i) {
                if ((i % 2) == 0) {
                    final String actual = results.processReadAt(i).stringValue();
                    assertEquals("first_" + testData[i], actual);
                } else {
                    try {
                        final OtpErlangObject result = results.processReadAt(i).value();
                        // a not found exception must be thrown
                        assertTrue("Expected a NotFoundException, got: " + result.toString(), false);
                    } catch (final NotFoundException e) {
                    }
                }
            }

            results = conn.req_list(writeRequests);
            assertEquals(writeRequests.size(), results.size());

            // now evaluate the write results:
            for (int i = 0; i < writeRequests.size(); ++i) {
                results.processWriteAt(i);
            }

            // once again test reads - now all reads should be successful
            results = conn.req_list(readRequests);
            assertEquals(readRequests.size(), results.size());

            // now evaluate the read results:
            for (int i = 0; i < readRequests.size(); ++i) {
                final String actual = results.processReadAt(i).stringValue();
                assertEquals("second_" + testData[i], actual);
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Tests how long it takes to read a large list without compression.
     *
     * @throws ConnectionException
     * @throws UnknownException
     * @throws AbortException
     * @throws NotFoundException
     */
    @Test
    public void testReadLargeList1()
            throws ConnectionException, UnknownException, AbortException, NotFoundException {
        testReadLargeList(false, "_ReadLargeList1");
    }

    /**
     * Tests how long it takes to read a large list with compression.
     *
     * @throws ConnectionException
     * @throws UnknownException
     * @throws AbortException
     * @throws NotFoundException
     */
    @Test
    public void testReadLargeList2()
            throws ConnectionException, UnknownException, AbortException, NotFoundException {
        testReadLargeList(true, "_ReadLargeList2");
    }

    /**
     * Tests how long it takes to read a large list with or without compression.
     *
     * @param compressed
     *            whether to compress or not
     * @param key
     *            the key to append to the {@link #testTime}
     *
     * @throws ConnectionException
     * @throws AbortException
     * @throws UnknownException
     * @throws ClassCastException
     * @throws NotFoundException
     */
    protected void testReadLargeList(final boolean compressed, final String key)
            throws ConnectionException, AbortException, UnknownException, ClassCastException, NotFoundException {
        final ArrayList<String> expected = new ArrayList<String>(testData.length * 100);
        for (int i = 0; i < 100; ++i) {
            expected.addAll(Arrays.asList(testData));
        }

        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.setCompressed(compressed);
        conn.write(testTime + key, expected);

        try {
            for (int i = 0; i < 500; ++i) {
                final List<String> actual = conn.read(testTime + key).stringListValue();
                assertEquals(expected, actual);
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Tests how long it takes to read a large string without compression.
     *
     * @throws ConnectionException
     * @throws UnknownException
     * @throws AbortException
     * @throws NotFoundException
     * @throws IOException
     */
    @Test
    public void testReadLargeString1()
            throws ConnectionException, UnknownException, AbortException, NotFoundException, IOException {
        testReadLargeString(1, "_testReadLargeString1");
    }

    /**
     * Tests how long it takes to read a large string with compression.
     *
     * @throws ConnectionException
     * @throws UnknownException
     * @throws AbortException
     * @throws NotFoundException
     * @throws IOException
     */
    @Test
    public void testReadLargeString2()
            throws ConnectionException, UnknownException, AbortException, NotFoundException, IOException {
        testReadLargeString(2, "_testReadLargeString2");
    }

    /**
     * Tests how long it takes to read a large string with manual compression,
     * then encoding the string in base64.
     *
     * @throws ConnectionException
     * @throws UnknownException
     * @throws AbortException
     * @throws NotFoundException
     * @throws IOException
     */
    @Test
    public void testReadLargeString3()
            throws ConnectionException, UnknownException, AbortException, NotFoundException, IOException {
        testReadLargeString(3, "_testReadLargeString3");
    }

    /**
     * Tests how long it takes to read a large string with manual compression,
     * then encoding the string in base64 and then using automatic compression.
     *
     * @throws ConnectionException
     * @throws UnknownException
     * @throws AbortException
     * @throws NotFoundException
     * @throws IOException
     */
    @Test
    public void testReadLargeString4()
            throws ConnectionException, UnknownException, AbortException, NotFoundException, IOException {
        testReadLargeString(4, "_testReadLargeString4");
    }

    /**
     * Tests how long it takes to read a large string with different compression
     * schemes.
     *
     * @param compressed
     *            how to compress
     * @param key
     *            the key to append to the {@link #testTime}
     *
     * @throws ConnectionException
     * @throws UnknownException
     * @throws AbortException
     * @throws NotFoundException
     * @throws IOException
     */
    protected void testReadLargeString(final int compression, final String key)
            throws ConnectionException, UnknownException, AbortException, NotFoundException, IOException {
        final StringBuilder sb = new StringBuilder(testData.length * 8 * 100);
        for (int i = 0; i < 100; ++i) {
            for (final String data : testData) {
                sb.append(data);
            }
        }
        final String expected = sb.toString();

        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.setCompressed(true);
        switch (compression) {
        case 1:
            conn.setCompressed(false);
        case 2:
            conn.write(testTime + key, expected);
            break;
        case 3:
            conn.setCompressed(false);
        case 4:
            final ByteArrayOutputStream bos = new ByteArrayOutputStream();
            final GZIPOutputStream gos = new GZIPOutputStream(bos);
            gos.write(expected.getBytes("UTF-8"));
            gos.flush();
            gos.close();
            conn.write(testTime + key, new Base64(0).encodeToString(bos.toByteArray()));
            break;
        default:
            return;
        }

        try {
            for (int i = 0; i < 500; ++i) {
                String actual = conn.read(testTime + key).stringValue();
                if (compression >= 3) {
                    final byte[] packed = new Base64(0).decode(actual);
                    final ByteArrayOutputStream unpacked = new ByteArrayOutputStream();
                    final ByteArrayInputStream bis = new ByteArrayInputStream(packed);
                    final GZIPInputStream gis = new GZIPInputStream(bis);
                    final byte[] bbuf = new byte[256];
                    int read = 0;
                    while ((read = gis.read(bbuf)) >= 0) {
                        unpacked.write(bbuf, 0, read);
                    }
                    gis.close();
                    actual = new String(unpacked.toString("UTF-8"));
                }
                assertEquals(expected, actual);
            }
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for {@link ReadRandomFromListOp}.
     *
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws EmptyListException
     * @throws NotAListException
     * @throws UnknownException
     */
    @Test(expected = NotFoundException.class)
    public void testReadRandomFromList_NotFound()
            throws ConnectionException, NotFoundException, EmptyListException, NotAListException, UnknownException {
        final String key = "_ReadRandomFromList_NotFound";
        final TransactionSingleOp conn = new TransactionSingleOp();
        try {
            final ReadRandomFromListOp op = new ReadRandomFromListOp(testTime + key);
            final ResultList resultList = conn.req_list(op);
            assertEquals(op, resultList.get(0));
            op.processResultSingle();
        } finally {
            conn.closeConnection();
        }
    }

    /**
     * Test method for {@link TransactionSingleOp#read(String)} with a closed connection.
     *
     * @throws ConnectionException
     * @throws NotFoundException
     * @throws EmptyListException
     * @throws NotAListException
     * @throws UnknownException
     */
    @Test(expected = ConnectionException.class)
    public void testReadRandomFromListOp_NotConnected()
            throws ConnectionException, NotFoundException, EmptyListException, NotAListException, UnknownException {
        final String key = "_ReadRandomFromListOp_NotConnected";
        final TransactionSingleOp conn = new TransactionSingleOp();
        conn.closeConnection();
        final ReadRandomFromListOp op = new ReadRandomFromListOp(testTime + key);
        final ResultList resultList = conn.req_list(op);
        assertEquals(op, resultList.get(0));
        op.processResultSingle();
    }
}