Improve diagnostics when command line is not quite correct.
findbugs.home.is.not.set=The environment variable FINDBUGS_HOME is not set. Please set it to the path where FindBugs is installed.
findbugs.home.is.set.to=The environment variable FINDBUGS_HOME is set to {0}. Please ensure that it points to the path where FindBugs is installed.
fixed.bugs=Fixed Bugs
+invoke.with.help.for.help=Use the command-line option "--help" for help.
+must.specify.fbp.file=No FindBugs Project (.fbp) file was specified. Unable to continue.
new.bugs=New Bugs
new.version=New Version
+no.earlier.run=(none)
num.bugs={0}
num.bugs.old={0} (common to both versions)
old.bugs=Old Bugs
opt.addOption("d", "dbname", true, "DB name");
opt.addOption(null, "drop-tables", false, "Remove database schema (drop all data)");
opt.addOption("f", "fbp", true, "FindBugsProject file");
+ opt.addOption(null, "help", false, "Display help message and exit");
opt.addOption("h", "host", true, "DB hostname");
opt.addOption("j", "project", true, "proJect name");
opt.addOption("n", "number", true, "Build number (version)");
try {
CommandLine line = new GnuParser().parse(opt, args);
+
if (line.hasOption("c")) {
m_configFile = new File(line.getOptionValue("c"));
}
if (line.hasOption("h")) {
m_config.setDbHost(line.getOptionValue("h"));
}
+ if (line.hasOption("help")) {
+ usage(pw, opt);
+ return false;
+ }
if (line.hasOption("j")) {
m_projName = line.getOptionValue("j");
}
return false;
}
+ // Check for required parameters
+ if (m_removeSchema) {
+ // No other parameters required
+ return true;
+ }
+ if (null == m_fbp) {
+ pw.println(trans(CfbBundle.MUST_SPECIFY_FBP_FILE));
+ pw.println(trans(CfbBundle.INVOKE_WITH_HELP_FOR_HELP));
+ return false;
+ }
+ if (null == m_projName) {
+ m_projName = m_fbp.getName();
+ if (m_projName.endsWith(".fbp")) {
+ m_projName = m_projName.substring(0, m_projName.length() - 4);
+ }
+ }
+
return true;
}
+ // Note that this leverages commons-cli's HelpFormatter to
+ // generate the usage message. It will always be in English.
+ // If we want to localize that, we'd need to recode this,
+ // and also translate the parameter descriptions in
+ // createOptions().
void usage(PrintWriter pw, Options opt) {
HelpFormatter help = new HelpFormatter();
help.printHelp(pw, 80, getClass().getName(), "", opt, 0, 0, "", true);
return getBundle(m_locale).get(key, params);
}
- String getenv(String varName) {
- // This is a separate function so that we can override it at unit test time
- return System.getenv(varName);
- }
-
String getProperty(String propName) {
// This is a separate function so that we can override it at unit test time
return System.getProperty(propName);
}
void initArgs() {
- String findBugsDir = getenv("FINDBUGS_HOME");
+ String findBugsDir = Env.get("FINDBUGS_HOME");
if (null != findBugsDir) {
m_fbDir = new File(findBugsDir);
}
public static final String FINDBUGS_HOME_IS_NOT_SET = "findbugs.home.is.not.set";
public static final String FINDBUGS_HOME_IS_SET_TO = "findbugs.home.is.set.to";
public static final String FIXED_BUGS = "fixed.bugs";
+ public static final String INVOKE_WITH_HELP_FOR_HELP = "invoke.with.help.for.help";
+ public static final String MUST_SPECIFY_FBP_FILE = "must.specify.fbp.file";
public static final String NEW_BUGS = "new.bugs";
public static final String NEW_VERSION = "new.version";
+ public static final String NO_EARLIER_RUN = "no.earlier.run";
public static final String NUM_BUGS = "num.bugs";
public static final String NUM_BUGS_OLD = "num.bugs.old";
public static final String OLD_BUGS = "old.bugs";
import net.jaekl.cfb.CfbBundle;
import net.jaekl.cfb.Config;
+import net.jaekl.cfb.store.Run;
import net.jaekl.qd.util.MailException;
import net.jaekl.qd.util.MimePart;
import net.jaekl.qd.util.SendMail;
sendEmail(pw, report);
}
}
+
+ // --- end of public interface ---
- void sendEmail(PrintWriter pw, HtmlReport report) {
- SendMail sendMail = new SendMail();
+ String constructSubject(HtmlReport report) {
+ String earlier;
+ Run earlierRun = report.getDelta().getEarlier();
+ if (null == earlierRun) {
+ earlier = m_bundle.get(CfbBundle.NO_EARLIER_RUN);
+ }
+ else {
+ earlier = report.getDelta().getEarlier().constructVersionText(m_bundle);
+ }
+ String later = report.getDelta().getLater().constructVersionText(m_bundle);
+
+ String subject = m_bundle.get(CfbBundle.CFB_MAIL_SUBJECT, earlier, later);
+ return subject;
+ }
+
+ SendMail createSendMail() {
+ return new SendMail();
+ }
+
+ void sendEmail(PrintWriter pw, HtmlReport report) {
+ SendMail sendMail = createSendMail();
sendMail.setSmtpHost(m_config.getMailSmtpHost());
ArrayList<String> recipients = m_config.getNotify();
PrintWriter mailWriter = null;
try {
- String earlier = report.getDelta().getEarlier().constructVersionText(m_bundle);
- String later = report.getDelta().getLater().constructVersionText(m_bundle);
-
+ sendMail.setSubject(constructSubject(report));
sendMail.setFrom(m_config.getMailFrom());
- sendMail.setSubject(m_bundle.get(CfbBundle.CFB_MAIL_SUBJECT, earlier, later));
for (String recipient : recipients) {
sendMail.addTo(recipient);
}
assert (null != analysis.getProjectName());
- assert (null != analysis.getBuildNumber());
// ----------------------------------
// Add a run record for this analysis
package net.jaekl.cfb;
import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
m_cfb = new CFBMock(Locale.getDefault(), m_driver);
}
+ @Test
+ public void testParseArgs_noParams() throws IOException
+ {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ String[] args = {};
+
+ boolean result = m_cfb.parseArgs(pw, args);
+ pw.close();
+ sw.close();
+
+ assertFalse(result);
+ assertEquals("[must.specify.fbp.file]\n[invoke.with.help.for.help]\n", sw.toString());
+ }
+
+ @Test
+ public void testParseArgs_dropTables() throws IOException
+ {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ String[] args = { "--drop-tables" };
+
+ boolean result = m_cfb.parseArgs(pw, args);
+ pw.close();
+ sw.close();
+
+ assertTrue(result);
+ assertEquals("", sw.toString());
+ }
+
+ @Test
+ public void testParseArgs_invalidParam() throws IOException
+ {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ String[] args = {"--xyzzyaoeuidhtnsl"};
+
+ boolean result = m_cfb.parseArgs(pw, args);
+ pw.close();
+ sw.close();
+ String actual = sw.toString();
+
+ assertFalse(result);
+ assertTrue(actual.contains("usage"));
+ assertTrue(actual.contains("--help"));
+ }
+
@Test
public void testStoreAndReport_noPrior() throws IOException, SQLException, SAXException, TypeMismatchException
{
--- /dev/null
+package net.jaekl.cfb.analyze;
+
+import net.jaekl.cfb.CfbBundle;
+import net.jaekl.cfb.Config;
+import net.jaekl.qd.util.SendMail;
+import net.jaekl.qd.util.SendMailMock;
+import net.jaekl.qd.util.SmtpConversationMock;
+
+public class NotifierMock extends Notifier {
+ private final String TYPICAL_INIT = "220 BBN-UNIX.ARPA Simple Mail Transfer Service Ready";
+ private final String[][] TYPICAL = {
+ {
+ "HELO ",
+ "250 BBN-UNIX.ARPA"
+ },
+ {
+ "MAIL FROM:",
+ "250 OK"
+ },
+ {
+ "RCPT TO:",
+ "250 OK"
+ },
+ {
+ "DATA",
+ "354 Start mail input; end with <CRLF>.<CRLF>"
+ },
+ {
+ ".\r\n",
+ "250 OK"
+ },
+ {
+ "QUIT",
+ "221 BBN-UNIX.ARPA Service closing transmission channel"
+ }
+ };
+
+ private SendMailMock mock_sendMail;
+ private SmtpConversationMock mock_conversat;
+
+ public NotifierMock(CfbBundle bundle, Config config) {
+ super(bundle, config);
+
+ mock_sendMail = null;
+ }
+
+ @Override
+ SendMail createSendMail() {
+ mock_conversat = new SmtpConversationMock(TYPICAL_INIT, TYPICAL);
+ mock_sendMail = new SendMailMock(mock_conversat);
+ return mock_sendMail;
+ }
+
+ public SendMailMock mock_getSendMail() {
+ return mock_sendMail;
+ }
+}
--- /dev/null
+package net.jaekl.cfb.analyze;
+
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+
+import net.jaekl.cfb.CfbBundleMock;
+import net.jaekl.cfb.Config;
+
+import org.junit.Before;
+import org.junit.Test;
+
+public class NotifierTest {
+ private CfbBundleMock m_bundle;
+ private Config m_config;
+ private MessageMapMock m_msgMap;
+ private NotifierMock m_notifier;
+
+ @Before
+ public void setUp() {
+ m_bundle = new CfbBundleMock();
+ m_config = new Config();
+ m_msgMap = new MessageMapMock();
+ m_notifier = new NotifierMock(m_bundle, m_config);
+ }
+
+ @Test
+ public void testConstructSubject_noPrior() throws IOException {
+ String[][] data = {
+ {
+ "ProjectNameGoesHere",
+ "0.7.42-SNAPSHOT",
+ "[cfb.mail.subject][[no.earlier.run]][[version.num][",
+ "][0.7.42-SNAPSHOT]]"
+ },
+ {
+ "ProjectNameGoesHere",
+ null,
+ "[cfb.mail.subject][[no.earlier.run]][[analyzed.at][",
+ "]]"
+ },
+ {
+ null,
+ "0.7.42-SNAPSHOT",
+ "[cfb.mail.subject][[no.earlier.run]][[version.num][",
+ "][0.7.42-SNAPSHOT]]"
+ },
+ {
+ null,
+ null,
+ "[cfb.mail.subject][[no.earlier.run]][[analyzed.at][",
+ "]]"
+ }
+ };
+
+ for (String[] datum : data) {
+ String projectName = datum[0];
+ String buildNumber = datum[1];
+ String expectedPart1 = datum[2];
+ String expectedPart2 = datum[3];
+
+ Analysis first = null;
+ Analysis second = new Analysis(projectName, buildNumber);
+
+ Delta delta = new Delta(first, second);
+ HtmlReport htmlReport = new HtmlReport(m_bundle, m_msgMap.getColl(), delta);
+
+ String actual = m_notifier.constructSubject(htmlReport);
+ boolean pass = (actual.startsWith(expectedPart1)) && (actual.endsWith(expectedPart2));
+ if (!pass) {
+ System.out.println("ProjectName:\n" + projectName + "\nVersion:\n" + buildNumber
+ + "\nExpected:\n" + expectedPart1 + "{date}" + expectedPart2 + "\nActual:\n" + actual);
+ }
+ assertTrue(pass);
+ }
+ }
+
+}