/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.kernel.io; import com.liferay.portal.kernel.test.SyncThrowableThread; import java.io.IOException; import java.io.Reader; import java.io.Writer; import java.nio.CharBuffer; import java.util.Arrays; import java.util.Random; import java.util.concurrent.BlockingQueue; import java.util.concurrent.Callable; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.atomic.AtomicLong; import org.junit.Assert; import org.junit.Test; /** * @author Shuyang Zhou */ public class CharPipeTest { @Test public void testCloseForce() { CharPipe charPipe = new CharPipe(); Assert.assertFalse(charPipe.finished); charPipe.close(true); Assert.assertNull(charPipe.buffer); Assert.assertFalse(charPipe.finished); try { Reader reader = charPipe.getReader(); reader.read(); Assert.fail(); } catch (IOException ioe) { } try { Reader reader = charPipe.getReader(); reader.read(new char[1]); Assert.fail(); } catch (IOException ioe) { } try { Reader reader = charPipe.getReader(); reader.read(CharBuffer.allocate(1)); Assert.fail(); } catch (IOException ioe) { } try { Reader reader = charPipe.getReader(); reader.ready(); Assert.fail(); } catch (IOException ioe) { } try { Reader reader = charPipe.getReader(); reader.skip(1); Assert.fail(); } catch (IOException ioe) { } try { Writer writer = charPipe.getWriter(); writer.append('a'); Assert.fail(); } catch (IOException ioe) { } try { Writer writer = charPipe.getWriter(); writer.append("a"); Assert.fail(); } catch (IOException ioe) { } try { Writer writer = charPipe.getWriter(); writer.write("abc".toCharArray()); Assert.fail(); } catch (IOException ioe) { } try { Writer writer = charPipe.getWriter(); writer.write('a'); Assert.fail(); } catch (IOException ioe) { } try { Writer writer = charPipe.getWriter(); writer.write("a"); Assert.fail(); } catch (IOException ioe) { } } @Test public void testClosePeacefullyBlocking() throws Exception { final CharPipe charPipe = new CharPipe(); Reader reader = charPipe.getReader(); final AtomicLong timestampBeforeClose = new AtomicLong(); SyncThrowableThread<Void> syncThrowableThread = new SyncThrowableThread<>( new Callable<Void>() { @Override public Void call() throws Exception { timestampBeforeClose.set(System.currentTimeMillis()); charPipe.close(); return null; } }); syncThrowableThread.start(); int result = reader.read(); long timestampAfterRead = System.currentTimeMillis(); syncThrowableThread.sync(); Assert.assertEquals(-1, result); Assert.assertTrue(timestampAfterRead >= timestampBeforeClose.get()); } @Test public void testClosePeacefullyEmpty() throws IOException { CharPipe charPipe = new CharPipe(); Assert.assertFalse(charPipe.finished); charPipe.close(); Assert.assertNotNull(charPipe.buffer); Assert.assertTrue(charPipe.finished); Reader reader = charPipe.getReader(); Assert.assertEquals(-1, reader.read()); } @Test public void testClosePeacefullyNotEmpty() throws IOException { CharPipe charPipe = new CharPipe(); Writer writer = charPipe.getWriter(); writer.write("abcd"); Assert.assertFalse(charPipe.finished); charPipe.close(); Assert.assertNotNull(charPipe.buffer); Assert.assertTrue(charPipe.finished); char[] buffer = new char[5]; Reader reader = charPipe.getReader(); int result = reader.read(buffer); Assert.assertEquals(4, result); Assert.assertEquals('a', buffer[0]); Assert.assertEquals('b', buffer[1]); Assert.assertEquals('c', buffer[2]); Assert.assertEquals('d', buffer[3]); Assert.assertEquals(0, buffer[4]); Assert.assertEquals(-1, reader.read()); } @Test public void testConstructor() { CharPipe charPipe = new CharPipe(); Assert.assertNotNull(charPipe.buffer); Assert.assertEquals(8192, charPipe.buffer.length); Assert.assertEquals(0, charPipe.count); Assert.assertEquals(0, charPipe.readIndex); Assert.assertEquals(0, charPipe.writeIndex); Assert.assertNotNull(charPipe.bufferLock); Assert.assertNotNull(charPipe.notEmpty); Assert.assertNotNull(charPipe.notFull); charPipe = new CharPipe(1024); Assert.assertNotNull(charPipe.buffer); Assert.assertEquals(1024, charPipe.buffer.length); Assert.assertEquals(0, charPipe.count); Assert.assertEquals(0, charPipe.readIndex); Assert.assertEquals(0, charPipe.writeIndex); Assert.assertNotNull(charPipe.bufferLock); Assert.assertNotNull(charPipe.notEmpty); Assert.assertNotNull(charPipe.notFull); charPipe.close(); } @Test public void testGetReader() { CharPipe charPipe = new CharPipe(); Reader reader1 = charPipe.getReader(); Reader reader2 = charPipe.getReader(); Assert.assertSame(reader1, reader2); Assert.assertFalse(reader1.markSupported()); try { reader1.mark(1); Assert.fail(); } catch (IOException ioe) { } try { reader1.reset(); Assert.fail(); } catch (IOException ioe) { } charPipe.close(); } @Test public void testGetWriter() throws IOException { CharPipe charPipe = new CharPipe(); Writer writer1 = charPipe.getWriter(); Writer writer2 = charPipe.getWriter(); Assert.assertSame(writer1, writer2); writer1.flush(); charPipe.close(); } @Test public void testPipingChar() throws IOException { CharPipe charPipe = new CharPipe(4); Reader reader = charPipe.getReader(); Assert.assertFalse(reader.ready()); Writer writer = charPipe.getWriter(); writer.write('a'); Assert.assertTrue(reader.ready()); Assert.assertEquals('a', reader.read()); Assert.assertFalse(reader.ready()); writer.append('b'); Assert.assertTrue(reader.ready()); Assert.assertEquals('b', reader.read()); Assert.assertFalse(reader.ready()); charPipe.close(); } @Test public void testPipingCharArray() throws IOException { CharPipe charPipe = new CharPipe(4); Writer writer = charPipe.getWriter(); char[] data = "abcd".toCharArray(); writer.write(data); char[] buffer = new char[4]; Reader reader = charPipe.getReader(); int result = reader.read(buffer); Assert.assertEquals(4, result); Assert.assertTrue(Arrays.equals(data, buffer)); writer.append(new String(data)); result = reader.read(buffer); Assert.assertEquals(4, result); Assert.assertTrue(Arrays.equals(data, buffer)); charPipe.close(); } @Test public void testPipingCharArrayWithOffset() throws IOException { CharPipe charPipe = new CharPipe(4); Writer writer = charPipe.getWriter(); char[] data = "abcd".toCharArray(); writer.write(data, 0, 0); Reader reader = charPipe.getReader(); Assert.assertFalse(reader.ready()); writer.write(data, 1, 2); char[] buffer = new char[4]; int result = reader.read(buffer, 1, 0); Assert.assertEquals(0, result); result = reader.read(buffer, 1, 3); Assert.assertEquals(2, result); Assert.assertEquals('b', buffer[1]); Assert.assertEquals('c', buffer[2]); writer.append(new String(data), 1, 3); result = reader.read(buffer, 1, 0); Assert.assertEquals(0, result); result = reader.read(buffer, 1, 3); Assert.assertEquals(2, result); Assert.assertEquals('b', buffer[1]); Assert.assertEquals('c', buffer[2]); charPipe.close(); } @Test public void testPipingCharArrayWithOffsetTwoStep() throws IOException { CharPipe charPipe = new CharPipe(4); Writer writer = charPipe.getWriter(); char[] data = "abcd".toCharArray(); writer.write(data); Reader reader = charPipe.getReader(); char[] buffer = new char[4]; int result = reader.read(buffer, 0, 3); Assert.assertEquals(3, result); Assert.assertEquals('a', buffer[0]); Assert.assertEquals('b', buffer[1]); Assert.assertEquals('c', buffer[2]); writer.write(data, 0, 3); result = reader.read(buffer); Assert.assertEquals(4, result); Assert.assertEquals('d', buffer[0]); Assert.assertEquals('a', buffer[1]); Assert.assertEquals('b', buffer[2]); Assert.assertEquals('c', buffer[3]); writer.write(data); result = reader.read(buffer); Assert.assertEquals(4, result); Assert.assertEquals('a', buffer[0]); Assert.assertEquals('b', buffer[1]); Assert.assertEquals('c', buffer[2]); Assert.assertEquals('d', buffer[3]); charPipe.close(); } @Test public void testPipingCharBuffer() throws IOException { CharPipe charPipe = new CharPipe(4); Writer writer = charPipe.getWriter(); writer.write("abcd"); CharBuffer charBuffer = CharBuffer.allocate(0); Reader reader = charPipe.getReader(); int result = reader.read(charBuffer); Assert.assertEquals(0, result); charBuffer = CharBuffer.allocate(2); result = reader.read(charBuffer); Assert.assertEquals(2, result); charBuffer.flip(); Assert.assertEquals('a', charBuffer.get()); Assert.assertEquals('b', charBuffer.get()); charPipe.close(); } @Test public void testPipingString() throws IOException { CharPipe charPipe = new CharPipe(4); Reader reader = charPipe.getReader(); Writer writer = charPipe.getWriter(); writer.write("abcd"); char[] buffer = new char[4]; int result = reader.read(buffer); Assert.assertEquals(4, result); Assert.assertEquals("abcd", new String(buffer)); charPipe.close(); } @Test public void testPipingStringWithOffset() throws IOException { CharPipe charPipe = new CharPipe(4); Writer writer = charPipe.getWriter(); writer.write("abcd", 0, 0); Reader reader = charPipe.getReader(); Assert.assertFalse(reader.ready()); writer.write("abcd", 1, 3); char[] buffer = new char[4]; int result = reader.read(buffer, 1, 0); Assert.assertEquals(0, result); result = reader.read(buffer, 1, 3); Assert.assertEquals(3, result); Assert.assertEquals('b', buffer[1]); Assert.assertEquals('c', buffer[2]); Assert.assertEquals('d', buffer[3]); charPipe.close(); } @Test public void testPipingStringWithOffsetTwoStep() throws IOException { CharPipe charPipe = new CharPipe(4); Writer writer = charPipe.getWriter(); writer.write("abcd"); char[] buffer = new char[4]; Reader reader = charPipe.getReader(); int result = reader.read(buffer, 0, 3); Assert.assertEquals(3, result); Assert.assertEquals('a', buffer[0]); Assert.assertEquals('b', buffer[1]); Assert.assertEquals('c', buffer[2]); writer.write("abcd", 0, 3); result = reader.read(buffer); Assert.assertEquals(4, result); Assert.assertEquals('d', buffer[0]); Assert.assertEquals('a', buffer[1]); Assert.assertEquals('b', buffer[2]); Assert.assertEquals('c', buffer[3]); writer.write("abcd"); result = reader.read(buffer); Assert.assertEquals(4, result); Assert.assertEquals('a', buffer[0]); Assert.assertEquals('b', buffer[1]); Assert.assertEquals('c', buffer[2]); Assert.assertEquals('d', buffer[3]); charPipe.close(); } @Test public void testSkip() throws Exception { CharPipe charPipe = new CharPipe(4); Reader reader = charPipe.getReader(); try { reader.skip(-1); Assert.fail(); } catch (IllegalArgumentException iae) { } Writer writer = charPipe.getWriter(); SlowWriterJob slowWriterJob = new SlowWriterJob(writer, 4, false); Thread thread = new Thread(slowWriterJob); thread.start(); for (int i = 0; i < 10; i++) { long timestampBeforeWrite = slowWriterJob.getTimestampBeforeWrite(); long timestampAfterSkip1 = _timestampedSkip(reader, 2); long timestampAfterSkip2 = _timestampedSkip(reader, 2); Assert.assertTrue(timestampAfterSkip1 >= timestampBeforeWrite); Assert.assertTrue(timestampAfterSkip2 >= timestampAfterSkip1); } charPipe.close(); thread.join(); Assert.assertFalse(slowWriterJob.isFailed()); } @Test public void testSlowReader() throws Exception { CharPipe charPipe = new CharPipe(4); Reader reader = charPipe.getReader(); SlowReaderJob slowReaderJob = new SlowReaderJob( reader, 4, false, false); Thread thread = new Thread(slowReaderJob); Writer writer = charPipe.getWriter(); writer.write("abcd"); thread.start(); for (int i = 0; i < 5; i++) { if ((i % 2) == 0) { Assert.assertTrue( _timestampedWrite(writer, "abcdefgh") >= slowReaderJob.getTimestampBeforeRead()); } else { Assert.assertTrue( _timestampedWrite(writer, "abcdefgh".toCharArray()) >= slowReaderJob.getTimestampBeforeRead()); } } charPipe.close(); thread.join(); Assert.assertFalse(slowReaderJob.isFailed()); } @Test public void testSlowReaderOnCloseForce() throws Exception { CharPipe charPipe = new CharPipe(4); Reader reader = charPipe.getReader(); SlowReaderJob slowReaderJob = new SlowReaderJob(reader, 4, true, true); Thread thread = new Thread(slowReaderJob); Writer writer = charPipe.getWriter(); writer.write("abcd"); thread.start(); for (int i = 0; i < 2; i++) { Assert.assertTrue( _timestampedWrite(writer, "abcdefgh") >= slowReaderJob.getTimestampBeforeRead()); } charPipe.close(true); thread.join(); Assert.assertFalse(slowReaderJob.isFailed()); } @Test public void testSlowReaderOnClosePeacefully() throws Exception { CharPipe charPipe = new CharPipe(4); Reader reader = charPipe.getReader(); SlowReaderJob slowReaderJob = new SlowReaderJob(reader, 4, true, false); Thread thread = new Thread(slowReaderJob); Writer writer = charPipe.getWriter(); writer.write("abcd"); thread.start(); for (int i = 0; i < 2; i++) { Assert.assertTrue( _timestampedWrite(writer, "abcdefgh") >= slowReaderJob.getTimestampBeforeRead()); } charPipe.close(); thread.join(); Assert.assertFalse(slowReaderJob.isFailed()); } @Test public void testSlowWriter() throws Exception { CharPipe charPipe = new CharPipe(4); Writer writer = charPipe.getWriter(); SlowWriterJob slowWriterJob = new SlowWriterJob(writer, 4, false); Thread thread = new Thread(slowWriterJob); thread.start(); for (int i = 0; i < 10; i++) { Reader reader = charPipe.getReader(); char[] buffer = new char[8]; Assert.assertTrue( _timestampedRead(reader, buffer) >= slowWriterJob.getTimestampBeforeWrite()); } charPipe.close(); thread.join(); Assert.assertFalse(slowWriterJob.isFailed()); } @Test public void testSlowWriterOnClose() throws Exception { CharPipe charPipe = new CharPipe(4); Writer writer = charPipe.getWriter(); SlowWriterJob slowWriterJob = new SlowWriterJob(writer, 4, true); Thread thread = new Thread(slowWriterJob); thread.start(); for (int i = 0; i < 5; i++) { Reader reader = charPipe.getReader(); char[] buffer = new char[8]; Assert.assertTrue( _timestampedRead(reader, buffer) >= slowWriterJob.getTimestampBeforeWrite()); } charPipe.close(); thread.join(); Assert.assertFalse(slowWriterJob.isFailed()); } private void _randomWait(int time) throws InterruptedException { if (time < 0) { throw new IllegalArgumentException(); } int range = time / 2; int waitTime = new Random().nextInt(range) + range; Thread.sleep(waitTime); } private long _timestampedRead(Reader reader, char[] buffer) throws IOException { reader.read(buffer); return System.currentTimeMillis(); } private long _timestampedSkip(Reader reader, int skipSize) throws IOException { reader.skip(skipSize); return System.currentTimeMillis(); } private long _timestampedWrite(Writer writer, char[] data) throws IOException { writer.write(data); return System.currentTimeMillis(); } private long _timestampedWrite(Writer writer, String data) throws IOException { writer.write(data); return System.currentTimeMillis(); } private class SlowReaderJob implements Runnable { public SlowReaderJob( Reader reader, int bufferSize, boolean close, boolean force) { _reader = reader; _buffer = new char[bufferSize]; _close = close; _force = force; } public long getTimestampBeforeRead() throws InterruptedException { return _timestampsBeforeRead.take(); } public boolean isFailed() { return _failed; } @Override public void run() { try { for (int i = 0; i < 10; i++) { _randomWait(100); _timestampsBeforeRead.put(System.currentTimeMillis()); int result = _reader.read(_buffer); if (result == _buffer.length) { continue; } else if (_close && !_force && (result == -1)) { return; } else { _failed = true; break; } } if (_close && _force) { _failed = true; } } catch (Exception e) { if (!_close) { _failed = true; } } } private final char[] _buffer; private final boolean _close; private boolean _failed; private final boolean _force; private final Reader _reader; private final BlockingQueue<Long> _timestampsBeforeRead = new LinkedBlockingQueue<>(); } private class SlowWriterJob implements Runnable { public SlowWriterJob( Writer writer, int dataSize, boolean expectException) { _writer = writer; _dataSize = dataSize; _expectException = expectException; } public long getTimestampBeforeWrite() throws InterruptedException { return _timestampsBeforeWrite.take(); } public boolean isFailed() { return _failed; } @Override public void run() { try { for (int i = 0; i < 10; i++) { _randomWait(100); _timestampsBeforeWrite.put(System.currentTimeMillis()); _writer.write(new char[_dataSize]); } if (_expectException) { _failed = true; } } catch (Exception e) { if (!_expectException) { _failed = true; } } } private final int _dataSize; private final boolean _expectException; private boolean _failed; private final BlockingQueue<Long> _timestampsBeforeWrite = new LinkedBlockingQueue<>(); private final Writer _writer; } }