/* * Copyright 2012-2016 the original author or authors. * * 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.glowroot.agent.embedded.util; import java.io.File; import java.io.IOException; import java.io.OutputStreamWriter; import java.io.RandomAccessFile; import java.io.Writer; import org.junit.After; import org.junit.Before; import org.junit.Test; import static org.assertj.core.api.Assertions.assertThat; public class CappedDatabaseOutputStreamTest { private static final int BLOCK_HEADER_SIZE = 8; private File tempFile; private CappedDatabaseOutputStream cappedOut; private RandomAccessFile in; @Before public void onBefore() throws IOException { tempFile = File.createTempFile("glowroot-test-", ".capped.txt"); cappedOut = new CappedDatabaseOutputStream(tempFile, 10); in = new RandomAccessFile(tempFile, "r"); } @After public void onAfter() throws IOException { cappedOut.close(); in.close(); tempFile.delete(); } @Test public void shouldWrite() throws IOException { // given Writer out = new OutputStreamWriter(cappedOut); String text = "0123456789"; // when cappedOut.startBlock(); out.write(text); out.flush(); long cappedId = cappedOut.endBlock(); cappedOut.sync(); // then assertWrite(text, cappedId); } @Test public void shouldWriteUsingByteArray() throws IOException { // given String text = "0123456789"; // when cappedOut.startBlock(); cappedOut.write(text.getBytes()); cappedOut.flush(); long cappedId = cappedOut.endBlock(); cappedOut.sync(); // then assertWrite(text, cappedId); } @Test public void shouldWriteUsingSingleBytes() throws IOException { // when cappedOut.startBlock(); cappedOut.write('0'); cappedOut.write('1'); cappedOut.write('2'); cappedOut.write('3'); cappedOut.write('4'); cappedOut.write('5'); cappedOut.write('6'); cappedOut.write('7'); cappedOut.write('8'); cappedOut.write('9'); cappedOut.flush(); long cappedId = cappedOut.endBlock(); cappedOut.sync(); // then assertWrite("0123456789", cappedId); } @Test public void shouldWrap() throws IOException { // given Writer out = new OutputStreamWriter(cappedOut); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 600; i++) { sb.append("0123456789"); } String text = sb.toString(); cappedOut.startBlock(); out.write(text); out.flush(); cappedOut.endBlock(); // when out = new OutputStreamWriter(cappedOut); cappedOut.startBlock(); out.write(text); out.flush(); long cappedId = cappedOut.endBlock(); // then assertThat(cappedId).isEqualTo(6000 + BLOCK_HEADER_SIZE); long currIndex = in.readLong(); int cappedDatabaseSizeKb = in.readInt(); long lastCompactionBaseIndex = in.readLong(); assertThat(currIndex).isEqualTo(12000 + 2 * BLOCK_HEADER_SIZE); assertThat(cappedDatabaseSizeKb).isEqualTo(10); assertThat(lastCompactionBaseIndex).isEqualTo(0); in.seek(CappedDatabaseOutputStream.HEADER_SKIP_BYTES + 6000 + BLOCK_HEADER_SIZE); long blockSize = in.readLong(); assertThat(blockSize).isEqualTo(6000); byte[] bytes = new byte[(int) blockSize]; int remaining = 10240 - 6000 - 2 * BLOCK_HEADER_SIZE; in.readFully(bytes, 0, remaining); in.seek(CappedDatabaseOutputStream.HEADER_SKIP_BYTES); in.readFully(bytes, remaining, 6000 - remaining); String content = new String(bytes); assertThat(content).isEqualTo(text); } @Test public void shouldWrapAndKeepGoing() throws IOException { // given Writer out = new OutputStreamWriter(cappedOut); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 600; i++) { sb.append("0123456789"); } String text = sb.toString(); cappedOut.startBlock(); out.write(text); out.flush(); cappedOut.endBlock(); cappedOut.startBlock(); out.write(text); out.flush(); cappedOut.endBlock(); // when out = new OutputStreamWriter(cappedOut); cappedOut.startBlock(); out.write(text); out.flush(); long cappedId = cappedOut.endBlock(); // then assertThat(cappedId).isEqualTo(12000 + 2 * BLOCK_HEADER_SIZE); long currIndex = in.readLong(); int cappedDatabaseSizeKb = in.readInt(); long lastCompactionBaseIndex = in.readLong(); assertThat(currIndex).isEqualTo(18000 + 3 * BLOCK_HEADER_SIZE); assertThat(cappedDatabaseSizeKb).isEqualTo(10); assertThat(lastCompactionBaseIndex).isEqualTo(0); int totalOfFirstTwoBlocks = 2 * (6000 + BLOCK_HEADER_SIZE); in.seek(CappedDatabaseOutputStream.HEADER_SKIP_BYTES + totalOfFirstTwoBlocks - 10240); long blockSize = in.readLong(); assertThat(blockSize).isEqualTo(6000); byte[] bytes = new byte[(int) blockSize]; in.readFully(bytes, 0, bytes.length); String content = new String(bytes); assertThat(content).isEqualTo(text); } @Test public void shouldWrapAndResize() throws IOException { // given Writer out = new OutputStreamWriter(cappedOut); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 600; i++) { sb.append("0123456789"); } String text = sb.toString(); cappedOut.startBlock(); out.write(text); out.flush(); cappedOut.endBlock(); cappedOut.startBlock(); out.write(text); out.flush(); long cappedId = cappedOut.endBlock(); // when // have to close in before resizing in.close(); cappedOut.resize(20); in = new RandomAccessFile(tempFile, "r"); // then assertThat(cappedId).isEqualTo(6000 + BLOCK_HEADER_SIZE); long currIndex = in.readLong(); int cappedDatabaseSizeKb = in.readInt(); long lastCompactionBaseIndex = in.readLong(); assertThat(currIndex).isEqualTo(12000 + 2 * BLOCK_HEADER_SIZE); assertThat(cappedDatabaseSizeKb).isEqualTo(20); int total = 2 * (6000 + BLOCK_HEADER_SIZE); assertThat(lastCompactionBaseIndex).isEqualTo(total - 10240); int totalOfFirstBlock = 6000 + BLOCK_HEADER_SIZE; in.seek(CappedDatabaseOutputStream.HEADER_SKIP_BYTES + 10240 - totalOfFirstBlock); long blockSize = in.readLong(); assertThat(blockSize).isEqualTo(6000); byte[] bytes = new byte[(int) blockSize]; in.readFully(bytes, 0, 6000); String content = new String(bytes); assertThat(content).isEqualTo(text); } @Test public void shouldWrapAndResizeVerySmall() throws IOException { // given Writer out = new OutputStreamWriter(cappedOut); StringBuilder sb = new StringBuilder(); for (int i = 0; i < 60; i++) { sb.append("0123456789"); } String text = sb.toString(); for (int i = 0; i < 9; i++) { cappedOut.startBlock(); out.write(text); out.flush(); cappedOut.endBlock(); } cappedOut.startBlock(); out.write(text); out.flush(); long cappedId = cappedOut.endBlock(); // when // have to close in before resizing in.close(); cappedOut.resize(1); in = new RandomAccessFile(tempFile, "r"); // then assertThat(cappedId).isEqualTo(9 * (600 + BLOCK_HEADER_SIZE)); long currIndex = in.readLong(); int cappedDatabaseSizeKb = in.readInt(); long lastCompactionBaseIndex = in.readLong(); assertThat(currIndex).isEqualTo(10 * (600 + BLOCK_HEADER_SIZE)); assertThat(cappedDatabaseSizeKb).isEqualTo(1); int total = 10 * (600 + BLOCK_HEADER_SIZE); assertThat(lastCompactionBaseIndex).isEqualTo(total - 1024); in.seek(CappedDatabaseOutputStream.HEADER_SKIP_BYTES + 416); long blockSize = in.readLong(); assertThat(blockSize).isEqualTo(600); byte[] bytes = new byte[(int) blockSize]; in.readFully(bytes, 0, 600); String content = new String(bytes); assertThat(content).isEqualTo(text); } @Test public void shouldWrapWithoutEnoughSpaceAtEndForContiguousBlockHeader() throws IOException { // given String text = "0123456789"; cappedOut.startBlock(); int numBytesToWrite = 10240 - BLOCK_HEADER_SIZE - 1; for (int i = 0; i < numBytesToWrite; i++) { cappedOut.write(0); } cappedOut.flush(); cappedOut.endBlock(); // when Writer out = new OutputStreamWriter(cappedOut); out = new OutputStreamWriter(cappedOut); cappedOut.startBlock(); out.write(text); out.flush(); long cappedId = cappedOut.endBlock(); // then assertThat(cappedId).isEqualTo(10240); long currIndex = in.readLong(); int cappedDatabaseSizeKb = in.readInt(); long lastCompactionBaseIndex = in.readLong(); assertThat(currIndex).isEqualTo(10240 + BLOCK_HEADER_SIZE + text.length()); assertThat(cappedDatabaseSizeKb).isEqualTo(10); assertThat(lastCompactionBaseIndex).isEqualTo(0); in.seek(CappedDatabaseOutputStream.HEADER_SKIP_BYTES); long blockSize = in.readLong(); assertThat(blockSize).isEqualTo(text.length()); byte[] bytes = new byte[(int) blockSize]; in.readFully(bytes, 0, text.length()); String content = new String(bytes); assertThat(content).isEqualTo(text); } private void assertWrite(String text, long cappedId) throws IOException { assertThat(cappedId).isEqualTo(0); long currIndex = in.readLong(); int cappedDatabaseSizeKb = in.readInt(); long lastCompactionBaseIndex = in.readLong(); assertThat(currIndex).isEqualTo(10 + BLOCK_HEADER_SIZE); assertThat(cappedDatabaseSizeKb).isEqualTo(10); assertThat(lastCompactionBaseIndex).isEqualTo(0); long blockSize = in.readLong(); assertThat(blockSize).isEqualTo(10); byte[] bytes = new byte[(int) blockSize]; in.readFully(bytes, 0, bytes.length); String content = new String(bytes); assertThat(content).isEqualTo(text); } }