add more junit coverage to new Serializer class

Removed calls to superclass methods so that appropriate
exceptions are thrown.
Better comments for new tests.
Removed @Ignore from tests.
Removed tabs from pom.

Change-Id: I97fa63951eed4c2ac5ce0a267d8da5134a2a1c71
Issue-ID: POLICY-1106
Signed-off-by: Jim Hahn <jrh3@att.com>
diff --git a/utils-test/pom.xml b/utils-test/pom.xml
index 933104b..c87da91 100644
--- a/utils-test/pom.xml
+++ b/utils-test/pom.xml
@@ -7,9 +7,9 @@
   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.
@@ -19,70 +19,80 @@
   -->
 
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
-  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-	<modelVersion>4.0.0</modelVersion>
-	
-  	<parent>
-		<groupId>org.onap.policy.common</groupId>
-		<artifactId>common-modules</artifactId>
-		<version>1.3.0-SNAPSHOT</version>
-	</parent>
-	
-	<artifactId>utils-test</artifactId>
-	<description>Common Utilities to facilitate testing via JUnit</description>
-	<packaging>jar</packaging>
-  
-	<dependencies>
-		<dependency>
-			<groupId>junit</groupId>
-			<artifactId>junit</artifactId>
-			<scope>provided</scope>
-		</dependency>
-		<dependency>
-			<groupId>ch.qos.logback</groupId>
-			<artifactId>logback-classic</artifactId>
-		</dependency>
-		<dependency>
-			<groupId>org.onap.policy.common</groupId>
-			<artifactId>utils</artifactId>
-			<version>${project.version}</version>
-		</dependency>
-	</dependencies>
+    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
 
-	<build>
-		<pluginManagement>
-			<plugins>
-				<!--This plugin's configuration is used to store Eclipse m2e settings 
-					only. It has no influence on the Maven build itself. -->
-				<plugin>
-					<groupId>org.eclipse.m2e</groupId>
-					<artifactId>lifecycle-mapping</artifactId>
-					<version>1.0.0</version>
-					<configuration>
-						<lifecycleMappingMetadata>
-							<pluginExecutions>
-								<pluginExecution>
-									<pluginExecutionFilter>
-										<groupId>org.jacoco</groupId>
-										<artifactId>
-											jacoco-maven-plugin
-										</artifactId>
-										<versionRange>
-											[0.7.1.201405082137,)
-										</versionRange>
-										<goals>
-											<goal>prepare-agent</goal>
-										</goals>
-									</pluginExecutionFilter>
-									<action>
-										<ignore></ignore>
-									</action>
-								</pluginExecution>
-							</pluginExecutions>
-						</lifecycleMappingMetadata>
-					</configuration>
-				</plugin>
-			</plugins>
-		</pluginManagement>
-	</build>
+    <parent>
+        <groupId>org.onap.policy.common</groupId>
+        <artifactId>common-modules</artifactId>
+        <version>1.3.0-SNAPSHOT</version>
+    </parent>
+
+    <artifactId>utils-test</artifactId>
+    <description>Common Utilities to facilitate testing via JUnit</description>
+    <packaging>jar</packaging>
+
+    <properties>
+        <powermock.version>1.6.6</powermock.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>ch.qos.logback</groupId>
+            <artifactId>logback-classic</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.onap.policy.common</groupId>
+            <artifactId>utils</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+        <dependency>
+            <groupId>org.powermock</groupId>
+            <artifactId>powermock-api-mockito</artifactId>
+            <version>${powermock.version}</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
+
+    <build>
+        <pluginManagement>
+            <plugins>
+                <!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven
+                    build itself. -->
+                <plugin>
+                    <groupId>org.eclipse.m2e</groupId>
+                    <artifactId>lifecycle-mapping</artifactId>
+                    <version>1.0.0</version>
+                    <configuration>
+                        <lifecycleMappingMetadata>
+                            <pluginExecutions>
+                                <pluginExecution>
+                                    <pluginExecutionFilter>
+                                        <groupId>org.jacoco</groupId>
+                                        <artifactId>
+                                            jacoco-maven-plugin
+                                        </artifactId>
+                                        <versionRange>
+                                            [0.7.1.201405082137,)
+                                        </versionRange>
+                                        <goals>
+                                            <goal>prepare-agent</goal>
+                                        </goals>
+                                    </pluginExecutionFilter>
+                                    <action>
+                                        <ignore></ignore>
+                                    </action>
+                                </pluginExecution>
+                            </pluginExecutions>
+                        </lifecycleMappingMetadata>
+                    </configuration>
+                </plugin>
+            </plugins>
+        </pluginManagement>
+    </build>
 </project>
diff --git a/utils-test/src/main/java/org/onap/policy/common/utils/io/Serializer.java b/utils-test/src/main/java/org/onap/policy/common/utils/io/Serializer.java
index 740aad4..227b810 100644
--- a/utils-test/src/main/java/org/onap/policy/common/utils/io/Serializer.java
+++ b/utils-test/src/main/java/org/onap/policy/common/utils/io/Serializer.java
@@ -32,6 +32,11 @@
 public class Serializer {
 
     /**
+     * Factory to access various objects. May be overridden for junit tests.
+     */
+    private static Factory factory = new Factory();
+
+    /**
      * The constructor.
      */
     private Serializer() {
@@ -40,15 +45,20 @@
 
     /**
      * Serializes an object into a byte array.
-     * 
+     *
      * @param object the object to be serialized
      * @return the byte array containing the serialized object
      * @throws IOException if an error occurs
      */
     public static <T> byte[] serialize(T object) throws IOException {
-        try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
-            try (ObjectOutputStream oos = new ObjectOutputStream(out)) {
-                oos.writeObject(object);
+        try (ByteArrayOutputStream out = factory.makeByteArrayOutputStream()) {
+            try (ObjectOutputStream oos = factory.makeObjectOutputStream(out)) {
+                /*
+                 * writeObject() is final and mockito can't mock final methods. In
+                 * addition, powermock seemed to be having difficulty with the junit test
+                 * class as well, so we'll just do it with a factory method.
+                 */
+                factory.writeObject(object, oos);
             }
 
             return out.toByteArray();
@@ -57,7 +67,7 @@
 
     /**
      * De-serializes an object from a byte array.
-     * 
+     *
      * @param clazz class of object that is expected to be de-serialized
      * @param data the byte array containing the serialized object
      * @return the object that was de-serialized from the byte array
@@ -65,9 +75,14 @@
      */
     public static <T> T deserialize(Class<T> clazz, byte[] data) throws IOException {
 
-        try (ByteArrayInputStream in = new ByteArrayInputStream(data);
-                        ObjectInputStream ois = new ObjectInputStream(in)) {
-            return clazz.cast(ois.readObject());
+        try (ByteArrayInputStream in = factory.makeByteArrayInputStream(data);
+                        ObjectInputStream ois = factory.makeObjectInputStream(in)) {
+            /*
+             * readObject() is final and mockito can't mock final methods. In addition,
+             * powermock seemed to be having difficulty with the junit test class as well,
+             * so we'll just do it with a factory method.
+             */
+            return clazz.cast(factory.readObject(ois));
 
         } catch (ClassNotFoundException e) {
             throw new IOException(e);
@@ -77,7 +92,7 @@
     /**
      * Runs an object through a complete round trip, serializing and then de-serializing
      * it.
-     * 
+     *
      * @param object object to be serialized
      * @return the object that was de-serialized
      * @throws IOException if an error occurs
@@ -86,4 +101,35 @@
     public static <T> T roundTrip(T object) throws IOException {
         return (T) deserialize(object.getClass(), serialize(object));
     }
+
+    /**
+     * Factory to access various objects.
+     */
+    public static class Factory {
+
+        public ByteArrayOutputStream makeByteArrayOutputStream() {
+            return new ByteArrayOutputStream();
+        }
+
+        public ByteArrayInputStream makeByteArrayInputStream(byte[] data) {
+            return new ByteArrayInputStream(data);
+        }
+
+        public ObjectOutputStream makeObjectOutputStream(ByteArrayOutputStream out) throws IOException {
+            return new ObjectOutputStream(out);
+        }
+
+        public ObjectInputStream makeObjectInputStream(ByteArrayInputStream in) throws IOException {
+            return new ObjectInputStream(in);
+        }
+
+        public void writeObject(Object object, ObjectOutputStream oos) throws IOException {
+            oos.writeObject(object);
+        }
+
+        public Object readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+            return ois.readObject();
+        }
+
+    }
 }
diff --git a/utils-test/src/test/java/org/onap/policy/common/utils/io/SerializerTest.java b/utils-test/src/test/java/org/onap/policy/common/utils/io/SerializerTest.java
index 4d4385f..bdf0dba 100644
--- a/utils-test/src/test/java/org/onap/policy/common/utils/io/SerializerTest.java
+++ b/utils-test/src/test/java/org/onap/policy/common/utils/io/SerializerTest.java
@@ -7,9 +7,9 @@
  * 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.
@@ -21,14 +21,52 @@
 package org.onap.policy.common.utils.io;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
 import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
 import java.io.Serializable;
 import java.util.Arrays;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
 import org.junit.Test;
+import org.onap.policy.common.utils.io.Serializer.Factory;
+import org.powermock.reflect.Whitebox;
 
 public class SerializerTest {
 
+    /**
+     * Saved and restored when tests complete. Also restored at the start of each test.
+     */
+    private static Factory saveFactory;
+
+    @BeforeClass
+    public static void setUpBeforeClass() {
+        saveFactory = Whitebox.getInternalState(Serializer.class, "factory");
+    }
+
+    @AfterClass
+    public static void tearDownAfterClass() {
+        Whitebox.setInternalState(Serializer.class, "factory", saveFactory);
+    }
+
+    @Before
+    public void setUp() {
+        setFactory(saveFactory);
+    }
+
+    @Test
+    public void testFactory() {
+        assertNotNull(saveFactory);
+    }
+
     @Test
     public void testSerialize() throws Exception {
         MyObject obj1 = new MyObject(3);
@@ -48,6 +86,83 @@
     }
 
     @Test
+    public void testSerialize_ArrayCloseEx() throws Exception {
+        IOException ex = new IOException("testSerialize_ArrayCloseEx");
+
+        /*
+         * This stream will throw an exception when close() is invoked. However, close()
+         * is called twice, once by the ObjectOutputStream and once by the code we want to
+         * test. As a result, we'll have the first close() succeed and the second one
+         * fail.
+         */
+        ByteArrayOutputStream out = new ByteArrayOutputStream() {
+            private int nclose = 0;
+
+            @Override
+            public void close() throws IOException {
+                if (++nclose > 1) {
+                    throw ex;
+                }
+            }
+        };
+
+        /*
+         * Use a factory that returns our special stream.
+         */
+        setFactory(new Factory() {
+            @Override
+            public ByteArrayOutputStream makeByteArrayOutputStream() {
+                return out;
+            }
+        });
+
+        assertEquals(ex, expectException(() -> Serializer.serialize(new MyObject(100))));
+    }
+
+    @Test
+    public void testSerialize_ObjectWriteEx() throws Exception {
+        IOException ex = new IOException("testSerialize_ObjectWriteEx");
+
+        /*
+         * Use a factory that throws an exception when writeObject() is invoked.
+         */
+        setFactory(new Factory() {
+            @Override
+            public void writeObject(Object object, ObjectOutputStream oos) throws IOException {
+                throw ex;
+            }
+        });
+
+        assertEquals(ex, expectException(() -> Serializer.serialize(new MyObject(110))));
+    }
+
+    @Test
+    public void testSerialize_ObjectCloseEx() throws Exception {
+        IOException ex = new IOException("testSerialize_ObjectCloseEx");
+        ObjectOutputStream oos = mock(ObjectOutputStream.class);
+        doThrow(ex).when(oos).close();
+
+        /*
+         * Use a factory that returns the mocked object which throws an exception when
+         * close() is invoked. However, we also have to override writeObject() so that it
+         * succeeds even through we're using a mock.
+         */
+        setFactory(new Factory() {
+            @Override
+            public ObjectOutputStream makeObjectOutputStream(ByteArrayOutputStream out) throws IOException {
+                return oos;
+            }
+
+            @Override
+            public void writeObject(Object object, ObjectOutputStream oos) throws IOException {
+                return;
+            }
+        });
+
+        assertEquals(ex, expectException(() -> Serializer.serialize(new MyObject(120))));
+    }
+
+    @Test
     public void testDeserialize() throws Exception {
         MyObject obj1 = new MyObject(3);
         byte[] data = Serializer.serialize(obj1);
@@ -57,6 +172,136 @@
     }
 
     @Test
+    public void testDeserialize_ArrayCloseEx() throws Exception {
+        IOException ex = new IOException("testSerialize_ObjectWriteEx");
+
+        /*
+         * Use a factory that returns a stream that will throw an exception when close()
+         * is invoked. However, close() is called twice, once by the ObjectInputStream and
+         * once by the code we want to test. As a result, we'll have the first close()
+         * succeed and the second one fail.
+         */
+        setFactory(new Factory() {
+            @Override
+            public ByteArrayInputStream makeByteArrayInputStream(byte[] data) {
+                return new ByteArrayInputStream(data) {
+                    private int nclose = 0;
+
+                    @Override
+                    public void close() throws IOException {
+                        if (++nclose > 1) {
+                            throw ex;
+                        }
+                    }
+                };
+            }
+        });
+
+        byte[] data = Serializer.serialize(new MyObject(300));
+        assertEquals(ex, expectException(() -> Serializer.deserialize(MyObject.class, data)));
+    }
+
+    @Test
+    public void testDeserialize_ObjectReadClassEx() throws Exception {
+        ClassNotFoundException ex = new ClassNotFoundException("testDeserialize_ObjectReadClassEx");
+
+        /*
+         * Use a factory that throws a ClassNotFoundException when readObject() is
+         * invoked.
+         */
+        setFactory(new Factory() {
+            @Override
+            public Object readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+                throw ex;
+            }
+        });
+
+        byte[] data = Serializer.serialize(new MyObject(305));
+
+        Exception exwrap = expectException(() -> Serializer.deserialize(MyObject.class, data));
+        assertTrue(exwrap instanceof IOException);
+        assertNotNull(exwrap.getCause());
+        assertEquals(ex, exwrap.getCause());
+    }
+
+    @Test
+    public void testDeserialize_ObjectReadEx() throws Exception {
+        IOException ex = new IOException("testDeserialize_ObjectReadEx");
+
+        /*
+         * Use a factory that throws an IOException when readObject() is invoked.
+         */
+        setFactory(new Factory() {
+            @Override
+            public Object readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
+                throw ex;
+            }
+        });
+
+        byte[] data = Serializer.serialize(new MyObject(310));
+        assertEquals(ex, expectException(() -> Serializer.deserialize(MyObject.class, data)));
+    }
+
+    @Test
+    public void testDeserialize_ObjectCloseEx() throws Exception {
+        IOException ex = new IOException("testDeserialize_ObjectCloseEx");
+
+        /*
+         * Use a factory that returns an ObjectInputStream that throws an exception when
+         * close() is invoked.
+         */
+        setFactory(new Factory() {
+            @Override
+            public ObjectInputStream makeObjectInputStream(ByteArrayInputStream in) throws IOException {
+                return new ObjectInputStream(in) {
+                    @Override
+                    public void close() throws IOException {
+                        throw ex;
+                    }
+                };
+            }
+        });
+
+        byte[] data = Serializer.serialize(new MyObject(320));
+        assertEquals(ex, expectException(() -> Serializer.deserialize(MyObject.class, data)));
+    }
+
+    @Test
+    public void testDeserialize_BothCloseEx() throws Exception {
+        IOException ex = new IOException("testDeserialize_BothCloseEx");
+        IOException ex2 = new IOException("testDeserialize_BothCloseEx_2");
+
+        /*
+         * Use a factory that returns input streams, both of which throw exceptions when
+         * close() is invoked.
+         */
+        setFactory(new Factory() {
+            @Override
+            public ByteArrayInputStream makeByteArrayInputStream(byte[] data) {
+                return new ByteArrayInputStream(data) {
+                    @Override
+                    public void close() throws IOException {
+                        throw ex;
+                    }
+                };
+            }
+
+            @Override
+            public ObjectInputStream makeObjectInputStream(ByteArrayInputStream in) throws IOException {
+                return new ObjectInputStream(in) {
+                    @Override
+                    public void close() throws IOException {
+                        throw ex2;
+                    }
+                };
+            }
+        });
+
+        byte[] data = Serializer.serialize(new MyObject(330));
+        assertEquals(ex2, expectException(() -> Serializer.deserialize(MyObject.class, data)));
+    }
+
+    @Test
     public void testRoundTrip() throws Exception {
         MyObject obj1 = new MyObject(3);
 
@@ -70,6 +315,37 @@
     }
 
     /**
+     * Sets a new factory in the Serializer.
+     *
+     * @param factory new factory to be set
+     */
+    private void setFactory(Factory factory) {
+        Whitebox.setInternalState(Serializer.class, "factory", factory);
+    }
+
+    /**
+     * Applies a function, which is expected to throw an exception.
+     *
+     * @param func the function to apply
+     * @return the exception thrown by the function, or {@code null} if it did not throw
+     *         an exception
+     */
+    private Exception expectException(RunnerWithEx func) {
+        try {
+            func.apply();
+            return null;
+
+        } catch (Exception ex) {
+            return ex;
+        }
+    }
+
+    @FunctionalInterface
+    private static interface RunnerWithEx {
+        public void apply() throws Exception;
+    }
+
+    /**
      * Simple, serializable object.
      */
     public static class MyObject implements Serializable {