package net.jaekl.cfb;
import java.io.File;
-import java.io.FileInputStream;
import java.io.IOException;
+import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
+import net.jaekl.qd.util.FileIO;
+
import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.GnuParser;
import org.apache.commons.cli.Options;
public void readFile(File configProperties) throws IOException
{
Properties props = new Properties();
- FileInputStream fis = null;
+ InputStream is = null;
try {
- fis = new FileInputStream(configProperties);
- props.load(fis);
+ is = FileIO.getInst().openInput(configProperties);
+ props.load(is);
}
finally {
- if (null != fis) {
- fis.close();
+ if (null != is) {
+ is.close();
}
}
--- /dev/null
+package net.jaekl.qd.util;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+// Dispatch File Input/Output operations from a singleton
+// This makes it possible to intercept file reads and writes
+// during unit testing, and replace them with mock operations.
+
+public class FileIO {
+ // Note: need volatile to implement double-checked locking in getInst()
+ static volatile FileIO m_inst = null;
+
+ FileIO() { ; }
+
+ public static FileIO getInst() {
+ FileIO result = m_inst;
+ if (null == result) {
+ synchronized(FileIO.class) {
+ if (null == m_inst) {
+ m_inst = new FileIO();
+ }
+ result = m_inst;
+ }
+ }
+ return result;
+ }
+
+ public InputStream openInput(File file) throws FileNotFoundException
+ {
+ return new FileInputStream(file);
+ }
+
+ public OutputStream openOutput(File file) throws FileNotFoundException
+ {
+ return new FileOutputStream(file);
+ }
+}
package net.jaekl.cfb;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import java.io.IOException;
+import java.util.List;
+
+import net.jaekl.qd.util.FileIOMock;
+import net.jaekl.qd.util.FileMock;
+
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
import org.junit.Test;
public class ConfigTest {
+ private static final String CHRIS = "chris@localhost";
+ private static final String HUDSON = "hudson@jenkins.org";
+ private static final String MAIL_FROM = "findbugs@jaekl.net";
+ private static final String MAIL_TO = CHRIS + "," + HUDSON;
+ private static final String LOCALHOST = "localhost";
private static final String SAMPLE1 =
"; Path (relative or absolute) to the FINDBUGS_HOME, i.e., where FindBugs is installed\n"
+ "FindBugsHome=../findbugs-3.0.1/\n"
+ "; List (comma-separated) of email addresses to which notifications should be sent\n"
- + "notify=chris@localhost\n"
+ + "notify=" + MAIL_TO + "\n"
+ "\n"
+ "; Mail server setup\n"
- + "mail.smtp.host=localhost\n"
- + "mail.from=findbugs@localhost\n";
+ + "mail.smtp.host=" + LOCALHOST + "\n"
+ + "mail.from=" + MAIL_FROM + "\n";
+ @BeforeClass
+ public static void beforeClass() {
+ FileIOMock.mock_setInstance();
+ }
+
+ @AfterClass
+ public static void afterClass() {
+ FileIOMock.mock_clearInstance();
+ }
+
@Test
- public void testReadFile() {
- // TODO: wrap file access so that we can test this
+ public void testReadFile() throws IOException {
+ Config config = new Config();
+
+ FileMock fm = new FileMock("config.properties");
+ fm.mock_setContent(SAMPLE1);
+
+ config.readFile(fm);
+
+ assertEquals(MAIL_FROM, config.getMailFrom());
+ assertEquals(LOCALHOST, config.getMailSmtpHost());
+
+ List<String> notify = config.getNotify();
+ assertTrue(notify.contains(CHRIS));
+ assertTrue(notify.contains(HUDSON));
+ }
+
+ @Test
+ public void testReadEmptyFile() throws IOException {
+ Config config = new Config();
+ FileMock fm = new FileMock("empty.properties");
+ fm.mock_setContent("");
+
+ config.readFile(fm);
+
+ assertEquals("findbugs@localhost", config.getMailFrom());
+ assertEquals("localhost", config.getMailSmtpHost());
+ assertEquals(0, config.getNotify().size());
}
-
}
--- /dev/null
+package net.jaekl.qd.util;
+
+import java.io.File;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class FileIOMock extends FileIO {
+ private FileIOMock() { ; }
+
+ public static void mock_setInstance() {
+ synchronized(FileIO.class) {
+ m_inst = new FileIOMock();
+ }
+ }
+
+ public static void mock_clearInstance() {
+ synchronized(FileIO.class) {
+ m_inst = null;
+ }
+ }
+
+ @Override
+ public InputStream openInput(File file) {
+ if (file instanceof FileMock) {
+ FileMock fm = (FileMock)file;
+ return fm.mock_openInput();
+ }
+ throw new IllegalArgumentException("Expected FileMock, not File");
+ }
+
+ @Override
+ public OutputStream openOutput(File file) {
+ if (file instanceof FileMock) {
+ FileMock fm = (FileMock)file;
+ return fm.mock_openOutput();
+ }
+ throw new IllegalArgumentException("Expected FileMock, not File");
+ }
+}
--- /dev/null
+package net.jaekl.qd.util;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.nio.charset.Charset;
+
+public class FileMock extends File {
+ private static final long serialVersionUID = 1L;
+ public static final String UTF_8 = "UTF-8";
+
+ private ByteArrayInputStream m_bais;
+ private ByteArrayOutputStream m_baos;
+ private byte[] m_content;
+
+ public FileMock(String fileName) {
+ super(fileName);
+
+ m_bais = null;
+ m_baos = null;
+ m_content = new byte[0];
+ }
+
+ public ByteArrayInputStream mock_openInput() {
+ m_bais = new ByteArrayInputStream(mock_getContent());
+ return m_bais;
+ }
+
+ public ByteArrayOutputStream mock_openOutput() {
+ m_baos = new ByteArrayOutputStream();
+ return m_baos;
+ }
+
+ public void mock_setContent(byte[] content) {
+ m_content = content.clone();
+ }
+
+ public void mock_setContent(String content, Charset charset) {
+ m_content = content.getBytes(charset);
+ }
+
+ public void mock_setContent(String content) {
+ mock_setContent(content, Charset.forName(UTF_8));
+ }
+
+ public byte[] mock_getContent() {
+ if (null != m_baos) {
+ m_content = m_baos.toByteArray();
+ }
+ return m_content.clone();
+ }
+}