--- /dev/null
+/*
+ * Copyright (c) 2017 Brocade Communications Systems, Inc. and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+package org.opendaylight.controller.cluster.io;
+
+import static org.junit.Assert.assertArrayEquals;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import com.google.common.base.Stopwatch;
+import com.google.common.util.concurrent.Uninterruptibles;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Unit tests for FileBackedOutputStream.
+ *
+ * @author Thomas Pantelis
+ */
+public class FileBackedOutputStreamTest {
+ private static final Logger LOG = LoggerFactory.getLogger(FileBackedOutputStreamTest.class);
+ private static final String TEMP_DIR = "target/FileBackedOutputStreamTest";
+
+ @BeforeClass
+ public static void staticSetup() {
+ File dir = new File(TEMP_DIR);
+ if (!dir.exists() && !dir.mkdirs()) {
+ throw new RuntimeException("Failed to create temp dir " + TEMP_DIR);
+ }
+ }
+
+ @AfterClass
+ public static void staticCleanup() {
+ deleteTempFiles();
+ deleteFile(TEMP_DIR);
+ }
+
+ @Before
+ public void setup() {
+ deleteTempFiles();
+ FileBackedOutputStream.REFERENCE_CACHE.clear();
+ }
+
+ @After
+ public void cleanup() {
+ deleteTempFiles();
+ }
+
+ @Test
+ public void testFileThresholdNotReached() throws IOException {
+ LOG.info("testFileThresholdNotReached starting");
+ try (FileBackedOutputStream fbos = new FileBackedOutputStream(10, TEMP_DIR)) {
+ byte[] bytes = new byte[]{1, 2, 3, 4, 5, 6, 7, 8, 9};
+ fbos.write(bytes[0]);
+ fbos.write(bytes, 1, bytes.length - 1);
+
+ assertEquals("getCount", bytes.length, fbos.getCount());
+ assertNull("Found unexpected temp file", findTempFileName());
+ assertEquals("Size", bytes.length, fbos.asByteSource().size());
+
+ // Read bytes twice.
+ assertArrayEquals("Read bytes", bytes, fbos.asByteSource().read());
+ assertArrayEquals("Read bytes", bytes, fbos.asByteSource().read());
+
+ assertEquals("Reference cache size", 0, FileBackedOutputStream.REFERENCE_CACHE.size());
+
+ fbos.cleanup();
+ }
+
+ LOG.info("testFileThresholdNotReached ending");
+ }
+
+ @Test
+ public void testFileThresholdReachedWithWriteBytes() throws IOException {
+ LOG.info("testFileThresholdReachedWithWriteBytes starting");
+ try (FileBackedOutputStream fbos = new FileBackedOutputStream(10, TEMP_DIR)) {
+ byte[] bytes = new byte[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
+ fbos.write(bytes[0]);
+ fbos.write(bytes, 1, 11);
+
+ String tempFileName = findTempFileName();
+ assertNotNull("Expected temp file created", tempFileName);
+
+ fbos.write(bytes[12]);
+ fbos.write(bytes, 13, bytes.length - 13);
+
+ assertEquals("Temp file", tempFileName, findTempFileName());
+ assertEquals("Size", bytes.length, fbos.asByteSource().size());
+
+ InputStream inputStream = fbos.asByteSource().openStream();
+
+ assertArrayEquals("Read bytes", bytes, fbos.asByteSource().read());
+
+ byte[] inBytes = new byte[bytes.length];
+ assertEquals("# bytes read", bytes.length, inputStream.read(inBytes));
+ assertArrayEquals("Read InputStream", bytes, inBytes);
+ assertEquals("End of stream", -1, inputStream.read());
+
+ inputStream.close();
+
+ assertEquals("Reference cache size", 1, FileBackedOutputStream.REFERENCE_CACHE.size());
+
+ fbos.cleanup();
+
+ assertEquals("Reference cache size", 0, FileBackedOutputStream.REFERENCE_CACHE.size());
+
+ assertNull("Found unexpected temp file", findTempFileName());
+ }
+
+ LOG.info("testFileThresholdReachedWithWriteBytes ending");
+ }
+
+ @Test
+ public void testFileThresholdReachedWithWriteByte() throws IOException {
+ LOG.info("testFileThresholdReachedWithWriteByte starting");
+ try (FileBackedOutputStream fbos = new FileBackedOutputStream(2, TEMP_DIR)) {
+ byte[] bytes = new byte[]{0, 1, 2};
+ fbos.write(bytes[0]);
+ fbos.write(bytes[1]);
+
+ assertNull("Found unexpected temp file", findTempFileName());
+
+ fbos.write(bytes[2]);
+ fbos.flush();
+
+ assertNotNull("Expected temp file created", findTempFileName());
+
+ assertEquals("Size", bytes.length, fbos.asByteSource().size());
+ assertArrayEquals("Read bytes", bytes, fbos.asByteSource().read());
+ }
+
+ LOG.info("testFileThresholdReachedWithWriteByte ending");
+ }
+
+ @Test(expected = IOException.class)
+ public void testWriteAfterAsByteSource() throws IOException {
+ LOG.info("testWriteAfterAsByteSource starting");
+ try (FileBackedOutputStream fbos = new FileBackedOutputStream(3, TEMP_DIR)) {
+ byte[] bytes = new byte[]{0, 1, 2};
+ fbos.write(bytes);
+
+ assertNull("Found unexpected temp file", findTempFileName());
+ assertEquals("Size", bytes.length, fbos.asByteSource().size());
+
+ // Should throw IOException after call to asByteSource.
+ fbos.write(1);
+ }
+ }
+
+ @Test
+ public void testTempFileDeletedOnGC() throws IOException {
+ LOG.info("testTempFileDeletedOnGC starting");
+
+ FileBackedOutputStream fbos = null;
+ try {
+ fbos = new FileBackedOutputStream(1, TEMP_DIR);
+ fbos.write(new byte[] {0, 1});
+ assertNotNull("Expected temp file created", findTempFileName());
+ } finally {
+ if (fbos != null) {
+ fbos.close();
+ }
+ fbos = null;
+ }
+
+ Stopwatch sw = Stopwatch.createStarted();
+ while (sw.elapsed(TimeUnit.SECONDS) <= 20) {
+ System.gc();
+ if (findTempFileName() == null) {
+ return;
+ }
+ Uninterruptibles.sleepUninterruptibly(50, TimeUnit.MILLISECONDS);
+ }
+
+ fail("Temp file was not deleted");
+ }
+
+ private static String findTempFileName() {
+ String[] files = new File(TEMP_DIR).list();
+ assertNotNull(files);
+ assertTrue("Found more than one temp file: " + Arrays.toString(files), files.length < 2);
+ return files.length == 1 ? files[0] : null;
+ }
+
+ private static boolean deleteFile(String file) {
+ return new File(file).delete();
+ }
+
+ private static void deleteTempFiles() {
+ String[] files = new File(TEMP_DIR).list();
+ if (files != null) {
+ for (String file: files) {
+ deleteFile(TEMP_DIR + File.separator + file);
+ }
+ }
+ }
+}