This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-io.git
The following commit(s) were added to refs/heads/master by this push: new 1ee660d [IO-615] Add classes TeeWriter, FilterCollectionWriter, ProxyCollectionWriter, IOExceptionList, IOIndexedException. 1ee660d is described below commit 1ee660d9f1f2af298b7ebe49ddf8191592267aca Author: Gary Gregory <gardgreg...@gmail.com> AuthorDate: Fri Aug 9 10:36:39 2019 -0400 [IO-615] Add classes TeeWriter, FilterCollectionWriter, ProxyCollectionWriter, IOExceptionList, IOIndexedException. Closes #88. --- src/changes/changes.xml | 3 + .../org/apache/commons/io/IOExceptionList.java | 89 ++++ .../org/apache/commons/io/IOIndexedException.java | 60 +++ .../commons/io/output/FilterCollectionWriter.java | 302 ++++++++++++++ .../commons/io/output/ProxyCollectionWriter.java | 280 +++++++++++++ .../org/apache/commons/io/output/TeeWriter.java | 51 +++ .../apache/commons/io/IOExceptionListTestCase.java | 58 +++ .../commons/io/IOIndexedExceptionTestCase.java | 48 +++ .../io/output/ProxyCollectionWriterTest.java | 449 +++++++++++++++++++++ .../apache/commons/io/output/TeeWriterTest.java | 449 +++++++++++++++++++++ 10 files changed, 1789 insertions(+) diff --git a/src/changes/changes.xml b/src/changes/changes.xml index 9d627c1..888aeb5 100644 --- a/src/changes/changes.xml +++ b/src/changes/changes.xml @@ -119,6 +119,9 @@ The <action> type attribute can be add,update,fix,remove. <action issue="IO-614" dev="ggregory" type="add" due-to="Rob Spoor"> Add classes TaggedWriter, ClosedWriter and BrokenWriter. #86. </action> + <action issue="IO-615" dev="ggregory" type="add" due-to="Gary Gregory, Rob Spoor"> + Add classes TeeWriter, FilterCollectionWriter, ProxyCollectionWriter, IOExceptionList, IOIndexedException. + </action> </release> <release version="2.6" date="2017-10-15" description="Java 7 required, Java 9 supported."> diff --git a/src/main/java/org/apache/commons/io/IOExceptionList.java b/src/main/java/org/apache/commons/io/IOExceptionList.java new file mode 100644 index 0000000..f013f68 --- /dev/null +++ b/src/main/java/org/apache/commons/io/IOExceptionList.java @@ -0,0 +1,89 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.io; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +/** + * A IOException based on a list of Throwable causes. + * <p> + * The first exception in the list is used as this exception's cause and is accessible with the usual + * {@link #getCause()} while the complete list is accessible with {@link #getCauseList()}. + * </p> + * + * @since 2.7 + */ +public class IOExceptionList extends IOException { + + private static final long serialVersionUID = 1L; + private final List<? extends Throwable> causeList; + + /** + * Creates a new exception caused by a list of exceptions. + * + * @param causeList a list of cause exceptions. + */ + public IOExceptionList(List<? extends Throwable> causeList) { + super(String.format("%,d exceptions: %s", causeList == null ? 0 : causeList.size(), causeList), + causeList == null ? null : causeList.get(0)); + this.causeList = causeList == null ? Collections.emptyList() : causeList; + } + + /** + * Gets the cause list. + * + * @return The list of causes. + */ + public <T extends Throwable> List<T> getCauseList() { + return (List<T>) causeList; + } + + /** + * Gets the cause list. + * + * @param index index in the cause list. + * @return The list of causes. + */ + public <T extends Throwable> T getCause(final int index) { + return (T) causeList.get(index); + } + + /** + * Gets the cause list. + * + * @param index index in the cause list. + * @return The list of causes. + */ + public <T extends Throwable> T getCause(final int index, Class<T> clazz) { + return (T) causeList.get(index); + } + + /** + * Works around Throwable and Generics, may fail at runtime depending on the argument value. + * + * @param <T> the target type + * @param clazz the target type + * @return The list of causes. + */ + public <T extends Throwable> List<T> getCauseList(Class<T> clazz) { + return (List<T>) causeList; + } + +} diff --git a/src/main/java/org/apache/commons/io/IOIndexedException.java b/src/main/java/org/apache/commons/io/IOIndexedException.java new file mode 100644 index 0000000..635c9ad --- /dev/null +++ b/src/main/java/org/apache/commons/io/IOIndexedException.java @@ -0,0 +1,60 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.io; + +import java.io.IOException; + +/** + * A IOException associated with a source index. + * + * @since 2.7 + */ +public class IOIndexedException extends IOException { + + private static final long serialVersionUID = 1L; + private final int index; + + /** + * Creates a new exception. + * + * @param index index of this exception. + * @param cause cause exceptions. + */ + public IOIndexedException(final int index, final Throwable cause) { + super(toMessage(index, cause), cause); + this.index = index; + } + + protected static String toMessage(final int index, final Throwable cause) { + // Letting index be any int + final String unspecified = "Null"; + final String name = cause == null ? unspecified : cause.getClass().getSimpleName(); + final String msg = cause == null ? unspecified : cause.getMessage(); + return String.format("%s #%,d: %s", name, index, msg); + } + + /** + * The index of this exception. + * + * @return index of this exception. + */ + public int getIndex() { + return index; + } + +} diff --git a/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java b/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java new file mode 100644 index 0000000..ed9114c --- /dev/null +++ b/src/main/java/org/apache/commons/io/output/FilterCollectionWriter.java @@ -0,0 +1,302 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.io.output; + +import java.io.FilterWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +import org.apache.commons.io.IOExceptionList; +import org.apache.commons.io.IOIndexedException; + +/** + * Abstract class for writing filtered character streams to a {@link Collection} of writers. This is in contrast to + * {@link FilterWriter} which is backed by a single {@link Writer}. + * <p> + * This abstract class provides default methods that pass all requests to the contained writers. Subclasses should + * likely override some of these methods. + * </p> + * <p> + * The class {@link Writer} defines method signatures with {@code throws} {@link IOException}, which in this class are + * actually {@link IOExceptionList} containing a list of {@link IOIndexedException}. + * </p> + * + * @since 2.7 + */ +public class FilterCollectionWriter extends Writer { + + /** + * Empty and immutable collection of writers. + */ + protected final Collection<Writer> EMPTY_WRITERS = Collections.emptyList(); + + /** + * The underlying writers. + */ + protected final Collection<Writer> writers; + + /** + * Creates a new filtered collection writer. + * + * @param writers Writers to provide the underlying targets. + */ + protected FilterCollectionWriter(final Collection<Writer> writers) { + this.writers = writers == null ? EMPTY_WRITERS : writers; + } + + /** + * Creates a new filtered collection writer. + * + * @param writers Writers to provide the underlying targets. + */ + protected FilterCollectionWriter(final Writer... writers) { + this.writers = writers == null ? EMPTY_WRITERS : Arrays.asList(writers); + } + + @Override + public Writer append(final char c) throws IOException { + final List<Exception> causeList = new ArrayList<>(); + int i = 0; + for (final Writer w : writers) { + if (w != null) { + try { + w.append(c); + } catch (final IOException e) { + causeList.add(new IOIndexedException(i, e)); + } + } + i++; + } + if (!causeList.isEmpty()) { + throw new IOExceptionList(causeList); + } + return this; + } + + @Override + public Writer append(final CharSequence csq) throws IOException { + final List<Exception> causeList = new ArrayList<>(); + int i = 0; + for (final Writer w : writers) { + if (w != null) { + try { + w.append(csq); + } catch (final IOException e) { + causeList.add(new IOIndexedException(i, e)); + } + } + i++; + } + if (!causeList.isEmpty()) { + throw new IOExceptionList(causeList); + } + return this; + } + + @Override + public Writer append(final CharSequence csq, final int start, final int end) throws IOException { + + final List<Exception> causeList = new ArrayList<>(); + int i = 0; + for (final Writer w : writers) { + if (w != null) { + try { + w.append(csq, start, end); + } catch (final IOException e) { + causeList.add(new IOIndexedException(i, e)); + } + } + i++; + } + if (!causeList.isEmpty()) { + throw new IOExceptionList(causeList); + } + return this; + } + + @Override + public void close() throws IOException { + final List<Exception> causeList = new ArrayList<>(); + int i = 0; + for (final Writer w : writers) { + if (w != null) { + try { + w.close(); + } catch (final IOException e) { + causeList.add(new IOIndexedException(i, e)); + } + } + i++; + } + if (!causeList.isEmpty()) { + throw new IOExceptionList(causeList); + } + + } + + /** + * Flushes the stream. + * + * @exception IOException If an I/O error occurs + */ + @Override + public void flush() throws IOException { + final List<Exception> causeList = new ArrayList<>(); + int i = 0; + for (final Writer w : writers) { + if (w != null) { + try { + w.flush(); + } catch (final IOException e) { + causeList.add(new IOIndexedException(i, e)); + } + } + i++; + } + if (!causeList.isEmpty()) { + throw new IOExceptionList(causeList); + } + + } + + /** + * Writes a portion of an array of characters. + * + * @param cbuf Buffer of characters to be written + * @param off Offset from which to start reading characters + * @param len Number of characters to be written + * + * @exception IOException If an I/O error occurs + */ + @Override + public void write(final char cbuf[], final int off, final int len) throws IOException { + final List<Exception> causeList = new ArrayList<>(); + int i = 0; + for (final Writer w : writers) { + if (w != null) { + try { + w.write(cbuf, off, len); + } catch (final IOException e) { + causeList.add(new IOIndexedException(i, e)); + } + } + i++; + } + if (!causeList.isEmpty()) { + throw new IOExceptionList(causeList); + } + } + + @Override + public void write(final char[] cbuf) throws IOException { + final List<Exception> causeList = new ArrayList<>(); + int i = 0; + for (final Writer w : writers) { + if (w != null) { + try { + w.write(cbuf); + } catch (final IOException e) { + causeList.add(new IOIndexedException(i, e)); + } + } + i++; + } + if (!causeList.isEmpty()) { + throw new IOExceptionList(causeList); + } + } + + /** + * Writes a single character. + * + * @exception IOException If an I/O error occurs + */ + @Override + public void write(final int c) throws IOException { + final List<Exception> causeList = new ArrayList<>(); + int i = 0; + for (final Writer w : writers) { + if (w != null) { + try { + w.write(c); + } catch (final IOException e) { + causeList.add(new IOIndexedException(i, e)); + } + } + i++; + } + if (!causeList.isEmpty()) { + throw new IOExceptionList(causeList); + } + } + + @Override + public void write(final String str) throws IOException { + final List<Exception> causeList = new ArrayList<>(); + int i = 0; + for (final Writer w : writers) { + if (w != null) { + try { + w.write(str); + } catch (final IOException e) { + causeList.add(new IOIndexedException(i, e)); + } + } + i++; + } + if (!causeList.isEmpty()) { + throw new IOExceptionList(causeList); + } + + } + + /** + * Writes a portion of a string. + * + * @param str String to be written + * @param off Offset from which to start reading characters + * @param len Number of characters to be written + * + * @exception IOException If an I/O error occurs + */ + @Override + public void write(final String str, final int off, final int len) throws IOException { + final List<Exception> causeList = new ArrayList<>(); + int i = 0; + for (final Writer w : writers) { + if (w != null) { + try { + w.write(str, off, len); + } catch (final IOException e) { + causeList.add(new IOIndexedException(i, e)); + } + } + i++; + } + if (!causeList.isEmpty()) { + throw new IOExceptionList(causeList); + } + + } + +} diff --git a/src/main/java/org/apache/commons/io/output/ProxyCollectionWriter.java b/src/main/java/org/apache/commons/io/output/ProxyCollectionWriter.java new file mode 100644 index 0000000..b5fdcfb --- /dev/null +++ b/src/main/java/org/apache/commons/io/output/ProxyCollectionWriter.java @@ -0,0 +1,280 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.io.output; + +import java.io.FilterWriter; +import java.io.IOException; +import java.io.Writer; +import java.util.Collection; + +import org.apache.commons.io.IOUtils; + +/** + * A Proxy stream collection which acts as expected, that is it passes the method calls on to the proxied streams and + * doesn't change which methods are being called. It is an alternative base class to {@link FilterWriter} and + * {@link FilterCollectionWriter} to increase reusability, because FilterWriter changes the methods being called, such + * as {@code write(char[])} to {@code write(char[], int, int)} and {@code write(String)} to + * {@code write(String, int, int)}. This is in contrast to {@link ProxyWriter} which is backed by a single + * {@link Writer}. + * + * @since 2.7 + */ +public class ProxyCollectionWriter extends FilterCollectionWriter { + + /** + * Creates a new proxy collection writer. + * + * @param writers Writers object to provide the underlying targets. + */ + public ProxyCollectionWriter(final Collection<Writer> writers) { + super(writers); + } + + /** + * Creates a new proxy collection writer. + * + * @param writers Writers to provide the underlying targets. + */ + public ProxyCollectionWriter(final Writer... writers) { + super(writers); + } + + /** + * Invoked by the write methods after the proxied call has returned successfully. The number of chars written (1 for + * the {@link #write(int)} method, buffer length for {@link #write(char[])}, etc.) is given as an argument. + * <p> + * Subclasses can override this method to add common post-processing functionality without having to override all + * the write methods. The default implementation does nothing. + * </p> + * + * @param n number of chars written + * @throws IOException if the post-processing fails + */ + protected void afterWrite(final int n) throws IOException { + // noop + } + + /** + * Invokes the delegates' <code>append(char)</code> methods. + * + * @param c The character to write + * @return this writer + * @throws IOException if an I/O error occurs + * @since 2.0 + */ + @Override + public Writer append(final char c) throws IOException { + try { + beforeWrite(1); + super.append(c); + afterWrite(1); + } catch (final IOException e) { + handleIOException(e); + } + return this; + } + + /** + * Invokes the delegates' <code>append(CharSequence)</code> methods. + * + * @param csq The character sequence to write + * @return this writer + * @throws IOException if an I/O error occurs + */ + @Override + public Writer append(final CharSequence csq) throws IOException { + try { + final int len = IOUtils.length(csq); + beforeWrite(len); + super.append(csq); + afterWrite(len); + } catch (final IOException e) { + handleIOException(e); + } + return this; + } + + /** + * Invokes the delegates' <code>append(CharSequence, int, int)</code> methods. + * + * @param csq The character sequence to write + * @param start The index of the first character to write + * @param end The index of the first character to write (exclusive) + * @return this writer + * @throws IOException if an I/O error occurs + */ + @Override + public Writer append(final CharSequence csq, final int start, final int end) throws IOException { + try { + beforeWrite(end - start); + super.append(csq, start, end); + afterWrite(end - start); + } catch (final IOException e) { + handleIOException(e); + } + return this; + } + + /** + * Invoked by the write methods before the call is proxied. The number of chars to be written (1 for the + * {@link #write(int)} method, buffer length for {@link #write(char[])}, etc.) is given as an argument. + * <p> + * Subclasses can override this method to add common pre-processing functionality without having to override all the + * write methods. The default implementation does nothing. + * </p> + * + * @param n number of chars to be written + * @throws IOException if the pre-processing fails + */ + protected void beforeWrite(final int n) throws IOException { + // noop + } + + /** + * Invokes the delegate's <code>close()</code> method. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void close() throws IOException { + try { + super.close(); + } catch (final IOException e) { + handleIOException(e); + } + } + + /** + * Invokes the delegate's <code>flush()</code> method. + * + * @throws IOException if an I/O error occurs + */ + @Override + public void flush() throws IOException { + try { + super.flush(); + } catch (final IOException e) { + handleIOException(e); + } + } + + /** + * Handle any IOExceptions thrown. + * <p> + * This method provides a point to implement custom exception handling. The default behaviour is to re-throw the + * exception. + * </p> + * + * @param e The IOException thrown + * @throws IOException if an I/O error occurs + */ + protected void handleIOException(final IOException e) throws IOException { + throw e; + } + + /** + * Invokes the delegate's <code>write(char[])</code> method. + * + * @param cbuf the characters to write + * @throws IOException if an I/O error occurs + */ + @Override + public void write(final char[] cbuf) throws IOException { + try { + final int len = IOUtils.length(cbuf); + beforeWrite(len); + super.write(cbuf); + afterWrite(len); + } catch (final IOException e) { + handleIOException(e); + } + } + + /** + * Invokes the delegate's <code>write(char[], int, int)</code> method. + * + * @param cbuf the characters to write + * @param off The start offset + * @param len The number of characters to write + * @throws IOException if an I/O error occurs + */ + @Override + public void write(final char[] cbuf, final int off, final int len) throws IOException { + try { + beforeWrite(len); + super.write(cbuf, off, len); + afterWrite(len); + } catch (final IOException e) { + handleIOException(e); + } + } + + /** + * Invokes the delegate's <code>write(int)</code> method. + * + * @param c the character to write + * @throws IOException if an I/O error occurs + */ + @Override + public void write(final int c) throws IOException { + try { + beforeWrite(1); + super.write(c); + afterWrite(1); + } catch (final IOException e) { + handleIOException(e); + } + } + + /** + * Invokes the delegate's <code>write(String)</code> method. + * + * @param str the string to write + * @throws IOException if an I/O error occurs + */ + @Override + public void write(final String str) throws IOException { + try { + final int len = IOUtils.length(str); + beforeWrite(len); + super.write(str); + afterWrite(len); + } catch (final IOException e) { + handleIOException(e); + } + } + + /** + * Invokes the delegate's <code>write(String)</code> method. + * + * @param str the string to write + * @param off The start offset + * @param len The number of characters to write + * @throws IOException if an I/O error occurs + */ + @Override + public void write(final String str, final int off, final int len) throws IOException { + try { + beforeWrite(len); + super.write(str, off, len); + afterWrite(len); + } catch (final IOException e) { + handleIOException(e); + } + } + +} diff --git a/src/main/java/org/apache/commons/io/output/TeeWriter.java b/src/main/java/org/apache/commons/io/output/TeeWriter.java new file mode 100644 index 0000000..3fc0168 --- /dev/null +++ b/src/main/java/org/apache/commons/io/output/TeeWriter.java @@ -0,0 +1,51 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.io.output; + +import java.io.Writer; +import java.util.Collection; + +/** + * Classic splitter of {@link Writer}. Named after the Unix 'tee' command. It allows a stream to be branched off so + * there are now two streams. + * <p> + * This currently a only convenience class with the proper name "TeeWriter". + * </p> + * + * @since 2.7 + */ +public class TeeWriter extends ProxyCollectionWriter { + + /** + * Creates a new filtered collection writer. + * + * @param writers Writers to provide the underlying targets. + */ + public TeeWriter(final Collection<Writer> writers) { + super(writers); + } + + /** + * Creates a new filtered collection writer. + * + * @param writers Writers to provide the underlying targets. + */ + public TeeWriter(final Writer... writers) { + super(writers); + } +} diff --git a/src/test/java/org/apache/commons/io/IOExceptionListTestCase.java b/src/test/java/org/apache/commons/io/IOExceptionListTestCase.java new file mode 100644 index 0000000..7289129 --- /dev/null +++ b/src/test/java/org/apache/commons/io/IOExceptionListTestCase.java @@ -0,0 +1,58 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.io; + +import java.io.EOFException; +import java.util.Collections; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; + +public class IOExceptionListTestCase { + + @Test + public void testCause() { + final EOFException cause = new EOFException(); + final List<EOFException> list = Collections.singletonList(cause); + final IOExceptionList sqlExceptionList = new IOExceptionList(list); + Assert.assertEquals(cause, sqlExceptionList.getCause()); + Assert.assertEquals(cause, sqlExceptionList.getCause(0)); + Assert.assertEquals(list, sqlExceptionList.getCauseList()); + Assert.assertEquals(list, sqlExceptionList.getCauseList(EOFException.class)); + Assert.assertEquals(cause, sqlExceptionList.getCause(0, EOFException.class)); + // No CCE: + final List<EOFException> causeList = sqlExceptionList.getCauseList(); + Assert.assertEquals(list, causeList); + } + + @Test + public void testNullCause() { + final IOExceptionList sqlExceptionList = new IOExceptionList(null); + Assert.assertNull(sqlExceptionList.getCause()); + Assert.assertTrue(sqlExceptionList.getCauseList().isEmpty()); + } + + @Test + public void testPrintStackTrace() { + final EOFException cause = new EOFException(); + final List<EOFException> list = Collections.singletonList(cause); + final IOExceptionList sqlExceptionList = new IOExceptionList(list); + sqlExceptionList.printStackTrace(); + } +} diff --git a/src/test/java/org/apache/commons/io/IOIndexedExceptionTestCase.java b/src/test/java/org/apache/commons/io/IOIndexedExceptionTestCase.java new file mode 100644 index 0000000..bed9157 --- /dev/null +++ b/src/test/java/org/apache/commons/io/IOIndexedExceptionTestCase.java @@ -0,0 +1,48 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.io; + +import java.io.EOFException; + +import org.junit.Assert; +import org.junit.Test; + +/** + * Tests {@link IOIndexedException}. + * + * @since 2.7 + */ +public class IOIndexedExceptionTestCase { + + @Test + public void testEdge() { + final IOIndexedException exception = new IOIndexedException(-1, null); + Assert.assertEquals(-1, exception.getIndex()); + Assert.assertEquals(null, exception.getCause()); + Assert.assertNotNull(exception.getMessage()); + } + + @Test + public void testPlain() { + final EOFException e = new EOFException("end"); + final IOIndexedException exception = new IOIndexedException(0, e); + Assert.assertEquals(0, exception.getIndex()); + Assert.assertEquals(e, exception.getCause()); + Assert.assertNotNull(exception.getMessage()); + } +} diff --git a/src/test/java/org/apache/commons/io/output/ProxyCollectionWriterTest.java b/src/test/java/org/apache/commons/io/output/ProxyCollectionWriterTest.java new file mode 100644 index 0000000..94fc4c4 --- /dev/null +++ b/src/test/java/org/apache/commons/io/output/ProxyCollectionWriterTest.java @@ -0,0 +1,449 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.io.output; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Arrays; +import java.util.Collection; + +import org.apache.commons.io.IOExceptionList; +import org.apache.commons.io.IOIndexedException; +import org.junit.Assert; +import org.junit.Test; + +/** + * JUnit Test Case for {@link ProxyCollectionWriter}. + */ +public class ProxyCollectionWriterTest { + + @Test + public void testArrayIOExceptionOnAppendChar1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + final char data = 'A'; + try { + tw.append(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnAppendChar2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + final char data = 'A'; + try { + tw.append(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnAppendCharSequence1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + final CharSequence data = "A"; + try { + tw.append(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnAppendCharSequence2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + final CharSequence data = "A"; + try { + tw.append(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnAppendCharSequenceIntInt1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + final CharSequence data = "A"; + try { + tw.append(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnAppendCharSequenceIntInt2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + final CharSequence data = "A"; + try { + tw.append(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnClose1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + try { + tw.close(); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).close(); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnClose2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + try { + tw.close(); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).close(); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnFlush1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + try { + tw.flush(); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).flush(); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnFlush2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + try { + tw.flush(); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).flush(); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteCharArray1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + final char[] data = new char[] { 'a' }; + try { + tw.write(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteCharArray2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + final char[] data = new char[] { 'a' }; + try { + tw.write(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteCharArrayIntInt1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + final char[] data = new char[] { 'a' }; + try { + tw.write(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteCharArrayIntInt2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + final char[] data = new char[] { 'a' }; + try { + tw.write(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteInt1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + final int data = 32; + try { + tw.write(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteInt2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + try { + tw.write(32); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(32); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + + } + } + + @Test + public void testArrayIOExceptionOnWriteString1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + final String data = "A"; + try { + tw.write(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteString2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + final String data = "A"; + try { + tw.write(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + + } + } + + @Test + public void testArrayIOExceptionOnWriteStringIntInt1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + final String data = "A"; + try { + tw.write(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteStringIntInt2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + final String data = "A"; + try { + tw.write(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + + } + } + + @Test + public void testCollectionCloseBranchIOException() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(Arrays.asList(goodW, badW, null)); + try { + tw.close(); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).close(); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + + } + } + + @Test + public void testConstructorsNull() throws IOException { + try (final ProxyCollectionWriter teeWriter = new ProxyCollectionWriter((Writer[]) null)) { + // Call any method, should not throw + teeWriter.append('a'); + teeWriter.flush(); + } + try (final ProxyCollectionWriter teeWriter = new ProxyCollectionWriter((Collection<Writer>) null)) { + // Call any method, should not throw + teeWriter.append('a'); + teeWriter.flush(); + } + } + + @Test + public void testTee() throws IOException { + final StringBuilderWriter sbw1 = new StringBuilderWriter(); + final StringBuilderWriter sbw2 = new StringBuilderWriter(); + final StringBuilderWriter expected = new StringBuilderWriter(); + + try (final ProxyCollectionWriter tw = new ProxyCollectionWriter(sbw1, sbw2, null)) { + for (int i = 0; i < 20; i++) { + tw.write(i); + expected.write(i); + } + assertEquals("ProxyCollectionWriter.write(int)", expected.toString(), sbw1.toString()); + assertEquals("ProxyCollectionWriter.write(int)", expected.toString(), sbw2.toString()); + + final char[] array = new char[10]; + for (int i = 20; i < 30; i++) { + array[i - 20] = (char) i; + } + tw.write(array); + expected.write(array); + assertEquals("ProxyCollectionWriter.write(char[])", expected.toString(), sbw1.toString()); + assertEquals("ProxyCollectionWriter.write(char[])", expected.toString(), sbw2.toString()); + + for (int i = 25; i < 35; i++) { + array[i - 25] = (char) i; + } + tw.write(array, 5, 5); + expected.write(array, 5, 5); + assertEquals("TeeOutputStream.write(byte[], int, int)", expected.toString(), sbw1.toString()); + assertEquals("TeeOutputStream.write(byte[], int, int)", expected.toString(), sbw2.toString()); + + for (int i = 0; i < 20; i++) { + tw.append((char) i); + expected.append((char) i); + } + assertEquals("ProxyCollectionWriter.append(char)", expected.toString(), sbw1.toString()); + assertEquals("ProxyCollectionWriter.append(char)", expected.toString(), sbw2.toString()); + + for (int i = 20; i < 30; i++) { + array[i - 20] = (char) i; + } + tw.append(new String(array)); + expected.append(new String(array)); + assertEquals("ProxyCollectionWriter.append(CharSequence)", expected.toString(), sbw1.toString()); + assertEquals("ProxyCollectionWriter.write(CharSequence)", expected.toString(), sbw2.toString()); + + for (int i = 25; i < 35; i++) { + array[i - 25] = (char) i; + } + tw.append(new String(array), 5, 5); + expected.append(new String(array), 5, 5); + assertEquals("ProxyCollectionWriter.append(CharSequence, int, int)", expected.toString(), sbw1.toString()); + assertEquals("ProxyCollectionWriter.append(CharSequence, int, int)", expected.toString(), sbw2.toString()); + + expected.flush(); + expected.close(); + + tw.flush(); + } + } + +} diff --git a/src/test/java/org/apache/commons/io/output/TeeWriterTest.java b/src/test/java/org/apache/commons/io/output/TeeWriterTest.java new file mode 100644 index 0000000..90e028a --- /dev/null +++ b/src/test/java/org/apache/commons/io/output/TeeWriterTest.java @@ -0,0 +1,449 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.commons.io.output; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import java.io.IOException; +import java.io.StringWriter; +import java.io.Writer; +import java.util.Arrays; +import java.util.Collection; + +import org.apache.commons.io.IOExceptionList; +import org.apache.commons.io.IOIndexedException; +import org.junit.Assert; +import org.junit.Test; + +/** + * JUnit Test Case for {@link TeeWriter}. + */ +public class TeeWriterTest { + + @Test + public void testArrayIOExceptionOnAppendChar1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(badW, goodW, null); + final char data = 'A'; + try { + tw.append(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnAppendChar2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final ProxyCollectionWriter tw = new ProxyCollectionWriter(goodW, badW, null); + final char data = 'A'; + try { + tw.append(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnAppendCharSequence1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(badW, goodW, null); + final String data = "A"; + try { + tw.append(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnAppendCharSequence2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(goodW, badW, null); + final String data = "A"; + try { + tw.append(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnAppendCharSequenceIntInt1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(badW, goodW, null); + final String data = "A"; + try { + tw.append(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnAppendCharSequenceIntInt2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(goodW, badW, null); + final String data = "A"; + try { + tw.append(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).append(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnClose1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(badW, goodW, null); + try { + tw.close(); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).close(); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnClose2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(goodW, badW, null); + try { + tw.close(); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).close(); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnFlush1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(badW, goodW, null); + try { + tw.flush(); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).flush(); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnFlush2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(goodW, badW, null); + try { + tw.flush(); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).flush(); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteCharArray1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(badW, goodW, null); + final char[] data = new char[] { 'a' }; + try { + tw.write(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteCharArray2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(goodW, badW, null); + final char[] data = new char[] { 'a' }; + try { + tw.write(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteCharArrayIntInt1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(badW, goodW, null); + final char[] data = new char[] { 'a' }; + try { + tw.write(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteCharArrayIntInt2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(goodW, badW, null); + final char[] data = new char[] { 'a' }; + try { + tw.write(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteInt1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(badW, goodW, null); + final int data = 32; + try { + tw.write(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteInt2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(goodW, badW, null); + try { + tw.write(32); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(32); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + + } + } + + @Test + public void testArrayIOExceptionOnWriteString1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(badW, goodW, null); + final String data = "A"; + try { + tw.write(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteString2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(goodW, badW, null); + final String data = "A"; + try { + tw.write(data); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + + } + } + + @Test + public void testArrayIOExceptionOnWriteStringIntInt1() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(badW, goodW, null); + final String data = "A"; + try { + tw.write(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(0, e.getCause(0, IOIndexedException.class).getIndex()); + } + } + + @Test + public void testArrayIOExceptionOnWriteStringIntInt2() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(goodW, badW, null); + final String data = "A"; + try { + tw.write(data, 0, 0); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).write(data, 0, 0); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + + } + } + + @Test + public void testCollectionCloseBranchIOException() throws IOException { + final Writer badW = new BrokenWriter(); + final StringWriter goodW = mock(StringWriter.class); + final TeeWriter tw = new TeeWriter(Arrays.asList(goodW, badW, null)); + try { + tw.close(); + fail("Expected " + IOException.class.getName()); + } catch (final IOExceptionList e) { + verify(goodW).close(); + Assert.assertEquals(1, e.getCauseList().size()); + Assert.assertEquals(1, e.getCause(0, IOIndexedException.class).getIndex()); + + } + } + + @Test + public void testConstructorsNull() throws IOException { + try (final TeeWriter teeWriter = new TeeWriter((Writer[]) null)) { + // Call any method, should not throw + teeWriter.append('a'); + teeWriter.flush(); + } + try (final TeeWriter teeWriter = new TeeWriter((Collection<Writer>) null)) { + // Call any method, should not throw + teeWriter.append('a'); + teeWriter.flush(); + } + } + + @Test + public void testTee() throws IOException { + final StringBuilderWriter sbw1 = new StringBuilderWriter(); + final StringBuilderWriter sbw2 = new StringBuilderWriter(); + final StringBuilderWriter expected = new StringBuilderWriter(); + + try (final TeeWriter tw = new TeeWriter(sbw1, sbw2, null)) { + for (int i = 0; i < 20; i++) { + tw.write(i); + expected.write(i); + } + assertEquals("TeeWriter.write(int)", expected.toString(), sbw1.toString()); + assertEquals("TeeWriter.write(int)", expected.toString(), sbw2.toString()); + + final char[] array = new char[10]; + for (int i = 20; i < 30; i++) { + array[i - 20] = (char) i; + } + tw.write(array); + expected.write(array); + assertEquals("TeeWriter.write(char[])", expected.toString(), sbw1.toString()); + assertEquals("TeeWriter.write(char[])", expected.toString(), sbw2.toString()); + + for (int i = 25; i < 35; i++) { + array[i - 25] = (char) i; + } + tw.write(array, 5, 5); + expected.write(array, 5, 5); + assertEquals("TeeOutputStream.write(byte[], int, int)", expected.toString(), sbw1.toString()); + assertEquals("TeeOutputStream.write(byte[], int, int)", expected.toString(), sbw2.toString()); + + for (int i = 0; i < 20; i++) { + tw.append((char) i); + expected.append((char) i); + } + assertEquals("TeeWriter.append(char)", expected.toString(), sbw1.toString()); + assertEquals("TeeWriter.append(char)", expected.toString(), sbw2.toString()); + + for (int i = 20; i < 30; i++) { + array[i - 20] = (char) i; + } + tw.append(new String(array)); + expected.append(new String(array)); + assertEquals("TeeWriter.append(CharSequence)", expected.toString(), sbw1.toString()); + assertEquals("TeeWriter.write(CharSequence)", expected.toString(), sbw2.toString()); + + for (int i = 25; i < 35; i++) { + array[i - 25] = (char) i; + } + tw.append(new String(array), 5, 5); + expected.append(new String(array), 5, 5); + assertEquals("TeeWriter.append(CharSequence, int, int)", expected.toString(), sbw1.toString()); + assertEquals("TeeWriter.append(CharSequence, int, int)", expected.toString(), sbw2.toString()); + + expected.flush(); + expected.close(); + + tw.flush(); + } + } + +}