com.addthis.hydra.data.io.TestDiskBackedList2.java Source code

Java tutorial

Introduction

Here is the source code for com.addthis.hydra.data.io.TestDiskBackedList2.java

Source

/*
 * 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.addthis.hydra.data.io;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Random;

import com.addthis.basis.test.SlowTest;
import com.addthis.basis.util.LessBytes;
import com.addthis.basis.util.LessFiles;

import com.addthis.codec.annotations.FieldConfig;
import com.addthis.codec.codables.Codable;
import com.addthis.hydra.data.io.DiskBackedList2.ItemCodec;

import com.google.common.io.Files;

import org.apache.commons.io.output.ByteArrayOutputStream;

import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;

@Category(SlowTest.class)
public class TestDiskBackedList2 {

    private final TestValueCodec codec = new TestValueCodec();

    public static class TestValueCodec implements ItemCodec<TestValue> {

        @Override
        public TestValue decode(byte[] row) throws IOException {
            ByteArrayInputStream in = new ByteArrayInputStream(row);
            TestValue tv = new TestValue();
            tv.value = LessBytes.readLong(in);
            tv.randomBytes = LessBytes.readBytes(in);
            return tv;
        }

        @Override
        public byte[] encode(TestValue row) throws IOException {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            LessBytes.writeLong(row.value, out);
            LessBytes.writeBytes(row.randomBytes, out);
            return out.toByteArray();
        }
    }

    public static final class TestValue implements Codable {

        private static final Random random = new Random(1234);
        @FieldConfig(codable = true)
        private long value;
        @FieldConfig(codable = true)
        private byte[] randomBytes;

        public TestValue() {
        }

        public TestValue(long val) {
            this.value = val;
            this.randomBytes = new byte[random.nextInt() & 0xff];
            random.nextBytes(randomBytes);
        }

        @Override
        public boolean equals(Object o) {
            return (o instanceof TestValue) && ((TestValue) o).value == value;
        }

        @Override
        public String toString() {
            return "TV:" + value + ":" + randomBytes.length;
        }
    }

    @Test
    public void addRemoveSizeTest() throws IOException {
        Random random = new Random(58);
        DiskBackedList2<TestValue> dbl = new DiskBackedList2<>(codec);
        try {
            int numEntries = 1000;
            int[] vals = new int[numEntries];
            for (int i = 0; i < numEntries; i++) {
                vals[i] = random.nextInt(3 * numEntries);
            }
            // Add a bunch of entries and make sure they all get set correctly
            for (int i = 0; i < numEntries; i++) {
                dbl.add(new TestValue(vals[i]));
                Assert.assertEquals(vals[i], dbl.get(i).value);
            }
            // Make sure resulting size is correct
            Assert.assertEquals(numEntries, dbl.size());
            // Remove half of the entries, check if new size is correct
            for (int i = (numEntries / 2); i > 0; i--) {
                dbl.remove(i);
            }
            Assert.assertEquals(numEntries - numEntries / 2, dbl.size());
            dbl.clear();
        } finally {
            LessFiles.deleteDir(dbl.getDirectory());
        }
    }

    @Test
    public void addAtIndexTest() throws IOException {
        List<Integer> ints = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
        DiskBackedList2<TestValue> dbl = new DiskBackedList2<>(codec);
        try {
            // Add a bunch of entries and make sure they all get set correctly
            for (Integer z : ints) {
                dbl.add(new TestValue(z));
            }
            ints.add(2, 100);
            dbl.add(2, new TestValue(100));
            int i = 0;
            for (TestValue tv : dbl) {
                Assert.assertEquals(new TestValue(ints.get(i++)), tv);
            }
            dbl.clear();
        } finally {
            if (dbl != null) {
                LessFiles.deleteDir(dbl.getDirectory());
            }
        }
    }

    @Test
    public void canHoldBigLoadTest() throws IOException {
        DiskBackedList2<TestValue> dbl = new DiskBackedList2<>(codec);
        try {
            int numEntries = 1000000;
            // Add a large number of entries to make sure we don't overflow heap
            for (int i = 0; i < numEntries; i++) {
                dbl.add(new TestValue(i));
            }
            // Make sure resulting size is correct
            Assert.assertEquals(numEntries, dbl.size());
            // Make sure a random read from the middle gives the correct value
            int middleEntry = numEntries / 2;
            TestValue tv = dbl.get(middleEntry);
            Assert.assertEquals(middleEntry, tv.value);
            dbl.clear();
        } finally {
            if (dbl != null) {
                LessFiles.deleteDir(dbl.getDirectory());
            }
        }
    }

    @Test
    public void iteratorTest() throws IOException {
        // Test whether the iterator has the correct set of values and no extras
        DiskBackedList2<TestValue> dbl = new DiskBackedList2<>(codec);
        try {
            int numEntries = 10000;
            for (int i = 0; i < numEntries; i++) {
                dbl.add(new TestValue(i));
            }
            Iterator<TestValue> listIter = dbl.listIterator();
            for (int i = 0; i < numEntries; i++) {
                Assert.assertEquals(new TestValue(i), listIter.next());
            }
            Assert.assertEquals(false, listIter.hasNext());
            dbl.clear();
        } finally {
            if (dbl != null) {
                LessFiles.deleteDir(dbl.getDirectory());
            }
        }
    }

    @Test
    public void sortTest() throws IOException {
        // Manually sort a bunch of values, then make sure dbl.sort reproduces the same list
        DiskBackedList2<TestValue> dbl = new DiskBackedList2<>(codec);
        try {
            Random random = new Random(1984);
            int numEntries = 20000;
            int[] vals = new int[numEntries];
            for (int i = 0; i < numEntries; i++) {
                vals[i] = random.nextInt(3 * numEntries);
            }
            int[] sortedVals = vals.clone();
            Arrays.sort(sortedVals);
            for (int val : vals) {
                dbl.add(new TestValue(val));
            }
            Comparator<TestValue> comp = new Comparator<TestValue>() {
                @Override
                public int compare(TestValue o1, TestValue o2) {
                    return (int) (o1.value - o2.value);
                }
            };
            dbl.sort(comp);
            int i = 0;
            for (TestValue tv : dbl) {
                Assert.assertEquals(sortedVals[i++], tv.value);
            }
            dbl.clear();
        } finally {
            if (dbl != null) {
                LessFiles.deleteDir(dbl.getDirectory());
            }
        }
    }

    @Test
    public void dontExceedMaxDiskSpaceTest() throws IOException {
        System.setProperty("max.total.query.size.bytes", "200");
        DiskBackedList2<TestValue> dbl = new DiskBackedList2<>(codec, 50, Files.createTempDir());
        try {
            boolean failed = false;
            for (int i = 0; i < 100; i++) {
                try {
                    dbl.add(new TestValue(i));
                } catch (RuntimeException ex) {
                    System.out.println(ex);
                    failed = true;
                    break;
                }
            }
            Assert.assertEquals(true, failed);
            System.clearProperty("max.total.query.size.bytes");
            dbl.clear();
        } finally {
            if (dbl != null) {
                LessFiles.deleteDir(dbl.getDirectory());
            }
        }
    }

    @Test
    public void runStressTests() throws IOException {
        // This test can be used to measure sort runtimes
        // stressTest(16000000,10000000);
    }

    public void stressTest(int chunkSize, int numEntries) throws IOException {
        long startTime = System.currentTimeMillis();
        DiskBackedList2<TestValue> dbl = new DiskBackedList2<>(codec, chunkSize, Files.createTempDir());
        try {
            int seed = 58;
            Random random = new Random(seed);
            for (int i = 0; i < numEntries; i++) {
                dbl.add(new TestValue(random.nextInt(3 * numEntries)));
            }
            Comparator<TestValue> comp = new Comparator<TestValue>() {
                @Override
                public int compare(TestValue o1, TestValue o2) {
                    return (int) (o1.value - o2.value);
                }
            };

            dbl.sort(comp);
            long totalTime = System.currentTimeMillis() - startTime;
            System.out
                    .println("chunkSize " + chunkSize + " numEntries " + numEntries + " took " + totalTime + " ms");
            dbl.clear();
        } finally {
            if (dbl != null) {
                LessFiles.deleteDir(dbl.getDirectory());
            }
        }
    }

    @Test
    public void saveToDirectoryTest() throws IOException {
        Random random = new Random(451);
        int numEntries = 100;
        int sizeBytes = 3000;
        File directory = Files.createTempDir();
        try {
            DiskBackedList2<TestValue> dbl = new DiskBackedList2<>(codec, sizeBytes, directory);
            for (int i = 0; i < numEntries; i++) {
                dbl.add(new TestValue(random.nextInt(2101)));
            }
            dbl.saveAllChunksToDirectory();
            DiskBackedList2<TestValue> anotherdbl = new DiskBackedList2<>(codec, sizeBytes, directory);
            anotherdbl.loadAllChunksFromDirectory();
            Assert.assertEquals(dbl.size(), anotherdbl.size());
            for (int i = 0; i < numEntries; i++) {
                Assert.assertEquals(dbl.get(i).value, anotherdbl.get(i).value);
            }
            dbl.clear();
        } finally {
            if (directory != null) {
                LessFiles.deleteDir(directory);
            }
        }
    }
}