Java tutorial
/* * Copyright 2014 Jin Kwon. * * 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.github.jinahya.verbose.codec; import java.io.IOException; import static java.lang.Runtime.getRuntime; import java.lang.invoke.MethodHandles; import java.nio.ByteBuffer; import java.nio.channels.FileChannel; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.Charset; import static java.nio.charset.StandardCharsets.UTF_8; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.StandardOpenOption; import static java.util.Objects.requireNonNull; import static java.util.concurrent.ThreadLocalRandom.current; import java.util.logging.Logger; import org.apache.commons.lang3.RandomStringUtils; import static org.testng.Assert.assertEquals; import org.testng.SkipException; import org.testng.annotations.BeforeClass; import org.testng.annotations.Test; /** * * @author Jin Kwon * @param <E> * @param <D> */ public abstract class BinaryCodecTest<E extends BinaryEncoder, D extends BinaryDecoder> { /** * logger. */ private static final Logger logger = Logger.getLogger(MethodHandles.lookup().lookupClass().getCanonicalName()); /** * Creates a new instance. * * @param encoderType * @param decoderType * * @see BinaryEncoderFactory#newInstance(java.lang.Class) * @see BinaryDecoderFactory#newInstance(java.lang.Class) */ public BinaryCodecTest(final Class<E> encoderType, final Class<D> decoderType) { super(); encoder = BinaryEncoderFactory.newInstance(requireNonNull(encoderType, "null encoderType")); decoder = BinaryDecoderFactory.newInstance(requireNonNull(decoderType, "null decoderType")); } @BeforeClass protected void skipIfEitherEncoderOrDecoderIsNull() { if (encoder == null) { final String message = "encoder is null. skipping..."; logger.warning(message); throw new SkipException(message); } if (decoder == null) { final String message = "decoder is null. skipping...."; logger.warning(message); throw new SkipException(message); } } protected final void encodeDecode(final ByteBuffer expectedBuffer) { expectedBuffer.mark(); final ByteBuffer encodedBuffer = encoder.encode(expectedBuffer); final ByteBuffer actualBuffer = decoder.decode(encodedBuffer); expectedBuffer.reset(); assertEquals(actualBuffer, expectedBuffer); } /** * Tests encoding/decoding for given byte array. * * @param expectedBytes the decoded byte array. * * @see HexEncoder#encode(byte[]) * @see HexDecoder#decode(byte[]) */ protected final void encodeDecode(final byte[] expectedBytes) { encodeDecode(ByteBuffer.wrap(expectedBytes)); } /** * Tests encoding/decoding for given string and charset. This method first * encodes given string and then decodes the result and finally asserts the * final output equals to given origin string. * * @param expectedString the original string * @param expectedCharset the charset to decode or encode the original * string to or from byte array. * @param encodedCharset * * @see HexEncoder#encode(java.lang.String, java.nio.charset.Charset) * @see HexDecoder#decode(java.lang.String, java.nio.charset.Charset) */ protected final void encodeDecode(final String expectedString, final Charset expectedCharset, final Charset encodedCharset) { final String encodedString = encoder.encode(expectedString, expectedCharset, encodedCharset); final String actualString = decoder.decode(encodedString, encodedCharset, expectedCharset); assertEquals(actualString, expectedString); } protected final void encodeDecode(final ReadableByteChannel expectedChannel) throws IOException { if (expectedChannel == null) { throw new NullPointerException("null expectedChannel"); } final Path encodedPath = Files.createTempFile("test", null); getRuntime().addShutdownHook(new Thread(() -> { try { Files.delete(encodedPath); } catch (final IOException ioe) { ioe.printStackTrace(System.err); } })); final WritableByteChannel encodedChannel = FileChannel.open(encodedPath, StandardOpenOption.WRITE); final ByteBuffer decodedBuffer = ByteBuffer.allocate(128); final ByteBuffer encodedBuffer = ByteBuffer.allocate(decodedBuffer.capacity() << 1); while (expectedChannel.read(decodedBuffer) != -1) { decodedBuffer.flip(); // limit -> position; position -> zero encoder.encode(decodedBuffer, encodedBuffer); encodedBuffer.flip(); encodedChannel.write(encodedBuffer); encodedBuffer.compact(); // position -> n + 1; limit -> capacity decodedBuffer.compact(); } decodedBuffer.flip(); while (decodedBuffer.hasRemaining()) { encoder.encode(decodedBuffer, encodedBuffer); encodedBuffer.flip(); encodedChannel.write(encodedBuffer); encodedBuffer.compact(); } encodedBuffer.flip(); while (encodedBuffer.hasRemaining()) { encodedChannel.write(encodedBuffer); } } protected final void encodeDecode(final Path expectedPath) throws IOException { if (expectedPath == null) { throw new NullPointerException("null expectedPath"); } try (final FileChannel expectedChannel = FileChannel.open(expectedPath, StandardOpenOption.READ)) { encodeDecode(expectedChannel); } } @Test(enabled = true, invocationCount = 1) public void encodeDecodeEmptyStrings(final Charset encodedCharset) { encodeDecode("", UTF_8, encodedCharset); } @Test(enabled = true, invocationCount = 128) public void encodeDecodeRandomStrings(final Charset encodedCharset) { final String expectedString = RandomStringUtils.random(current().nextInt(128)); encodeDecode(expectedString, UTF_8, encodedCharset); } @Test(enabled = true, invocationCount = 1) public void encodeDecodeEmptyBytes() { encodeDecode(new byte[0]); } @Test(enabled = true, invocationCount = 128) public void encodeDecodeRandomBytes() { final byte[] expected = new byte[current().nextInt(128)]; current().nextBytes(expected); encodeDecode(expected); } @Test(enabled = true, invocationCount = 128) public void encodeDecodeRandomFiles() throws IOException { final Path expectedPath = Files.createTempFile("test", null); getRuntime().addShutdownHook(new Thread(() -> { try { Files.delete(expectedPath); } catch (final IOException ioe) { ioe.printStackTrace(System.err); } })); final byte[] array = new byte[1024]; final ByteBuffer buffer = ByteBuffer.wrap(array); try (final FileChannel decodedChannel = FileChannel.open(expectedPath, StandardOpenOption.WRITE)) { final int count = current().nextInt(128); for (int i = 0; i < count; i++) { current().nextBytes(array); decodedChannel.write(buffer); } decodedChannel.force(false); } encodeDecode(expectedPath); } /** * hex encoder. */ protected final E encoder; /** * hex decoder. */ protected final D decoder; }