Skip to content

Commit

Permalink
Merge pull request #316 from jmcgarr/fix/issue-314
Browse files Browse the repository at this point in the history
Improve testability of Main.java while fixing issue #314
  • Loading branch information
jonbullock committed Jan 4, 2017
2 parents c3aaaf2 + f4e198b commit 92fb557
Show file tree
Hide file tree
Showing 5 changed files with 223 additions and 56 deletions.
38 changes: 38 additions & 0 deletions src/main/java/org/jbake/launcher/BakeWatcher.java
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.jbake.launcher;

import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.VFS;
import org.apache.commons.vfs2.impl.DefaultFileMonitor;
import org.jbake.app.ConfigUtil;

/**
* Delegate responsible for watching the file system for changes.
*
* @author jmcgarr@gmail.com
*/
public class BakeWatcher {

/**
* Starts watching the file system for changes to trigger a bake.
*
* @param res Commandline options
* @param config Configuration settings
*/
public void start(final LaunchOptions res, CompositeConfiguration config) {
try {
FileSystemManager fsMan = VFS.getManager();
FileObject listenPath = fsMan.resolveFile(res.getSource(), config.getString( ConfigUtil.Keys.CONTENT_FOLDER));

System.out.println("Watching for changes in [" + res.getSource() + "]");
DefaultFileMonitor monitor = new DefaultFileMonitor(new CustomFSChangeListener(res, config));
monitor.setRecursive(true);
monitor.addFile(listenPath);
monitor.start();
} catch (FileSystemException e) {
e.printStackTrace();
}
}
}
35 changes: 35 additions & 0 deletions src/main/java/org/jbake/launcher/Baker.java
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.jbake.launcher;

import org.apache.commons.configuration.CompositeConfiguration;
import org.jbake.app.JBakeException;
import org.jbake.app.Oven;

import java.text.MessageFormat;
import java.util.List;

/**
* Delegate class responsible for launching a Bake.
*
* @author jmcgarr@gmail.com
*/
public class Baker {

public void bake(final LaunchOptions options, final CompositeConfiguration config) {
final Oven oven = new Oven(options.getSource(), options.getDestination(), config, options.isClearCache());
oven.setupPaths();
oven.bake();

final List<Throwable> errors = oven.getErrors();
if (!errors.isEmpty()) {
final StringBuilder msg = new StringBuilder();
// TODO: decide, if we want the all errors here
msg.append( MessageFormat.format("JBake failed with {0} errors:\n", errors.size()));
int errNr = 1;
for (final Throwable error : errors) {
msg.append(MessageFormat.format("{0}. {1}\n", errNr, error.getMessage()));
++errNr;
}
throw new JBakeException(msg.toString(), errors.get(0));
}
}
}
2 changes: 1 addition & 1 deletion src/main/java/org/jbake/launcher/JettyServer.java
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class JettyServer {
* @param path * @param path
* @param port * @param port
*/ */
public static void run(String path, String port) { public void run(String path, String port) {
Server server = new Server(); Server server = new Server();
SelectChannelConnector connector = new SelectChannelConnector(); SelectChannelConnector connector = new SelectChannelConnector();
connector.setPort(Integer.parseInt(port)); connector.setPort(Integer.parseInt(port));
Expand Down
99 changes: 44 additions & 55 deletions src/main/java/org/jbake/launcher/Main.java
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -2,21 +2,13 @@


import java.io.File; import java.io.File;
import java.io.StringWriter; import java.io.StringWriter;
import java.text.MessageFormat;
import java.util.List;


import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.CompositeConfiguration;
import org.apache.commons.configuration.ConfigurationException; import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.vfs2.FileObject;
import org.apache.commons.vfs2.FileSystemException;
import org.apache.commons.vfs2.FileSystemManager;
import org.apache.commons.vfs2.VFS;
import org.apache.commons.vfs2.impl.DefaultFileMonitor;
import org.jbake.app.ConfigUtil; import org.jbake.app.ConfigUtil;
import org.jbake.app.ConfigUtil.Keys; import org.jbake.app.ConfigUtil.Keys;
import org.jbake.app.FileUtil; import org.jbake.app.FileUtil;
import org.jbake.app.JBakeException; import org.jbake.app.JBakeException;
import org.jbake.app.Oven;
import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.CmdLineParser;
import org.slf4j.bridge.SLF4JBridgeHandler; import org.slf4j.bridge.SLF4JBridgeHandler;
Expand All @@ -31,10 +23,10 @@ public class Main {


private final String USAGE_PREFIX = "Usage: jbake"; private final String USAGE_PREFIX = "Usage: jbake";
private final String ALT_USAGE_PREFIX = " or jbake"; private final String ALT_USAGE_PREFIX = " or jbake";

/** /**
* Runs the app with the given arguments. * Runs the app with the given arguments.
* *
* @param args * @param args
*/ */
public static void main(final String[] args) { public static void main(final String[] args) {
Expand All @@ -50,37 +42,46 @@ public static void main(final String[] args) {
} }
} }


private void bake(final LaunchOptions options, final CompositeConfiguration config) { private Baker baker;
final Oven oven = new Oven(options.getSource(), options.getDestination(), config, options.isClearCache()); private JettyServer jettyServer;
oven.setupPaths(); private BakeWatcher watcher;
oven.bake();

/**
final List<Throwable> errors = oven.getErrors(); * Default constructor.
if (!errors.isEmpty()) { */
final StringBuilder msg = new StringBuilder(); public Main() {
// TODO: decide, if we want the all errors here this(new Baker(), new JettyServer(), new BakeWatcher());
msg.append(MessageFormat.format("JBake failed with {0} errors:\n", errors.size()));
int errNr = 1;
for (final Throwable error : errors) {
msg.append(MessageFormat.format("{0}. {1}\n", errNr, error.getMessage()));
++errNr;
}
throw new JBakeException(msg.toString(), errors.get(0));
}
} }


private void run(String[] args) { /**
* Optional constructor to externalize dependencies.
*
* @param baker
* @param jetty
* @param watcher
*/
protected Main(Baker baker, JettyServer jetty, BakeWatcher watcher) {
this.baker = baker;
this.jettyServer = jetty;
this.watcher = watcher;
}

protected void run(String[] args) {
SLF4JBridgeHandler.removeHandlersForRootLogger(); SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install(); SLF4JBridgeHandler.install();
LaunchOptions res = parseArguments(args); LaunchOptions res = parseArguments( args );

final CompositeConfiguration config; final CompositeConfiguration config;
try { try {
config = ConfigUtil.load(res.getSource()); config = ConfigUtil.load( res.getSource() );
} catch (final ConfigurationException e) { } catch( final ConfigurationException e ) {
throw new JBakeException("Configuration error: " + e.getMessage(), e); throw new JBakeException( "Configuration error: " + e.getMessage(), e );
} }


run(res, config);
}

protected void run(LaunchOptions res, CompositeConfiguration config) {
System.out.println("JBake " + config.getString(Keys.VERSION) + " (" + config.getString(Keys.BUILD_TIMESTAMP) + ") [http://jbake.org]"); System.out.println("JBake " + config.getString(Keys.VERSION) + " (" + config.getString(Keys.BUILD_TIMESTAMP) + ") [http://jbake.org]");
System.out.println(); System.out.println();


Expand All @@ -91,20 +92,23 @@ private void run(String[] args) {
} }


if (res.isBake()) { if (res.isBake()) {
bake(res, config); baker.bake(res, config);
} }


if (res.isInit()) { if (res.isInit()) {
initStructure(config, res.getTemplate(), res.getSourceValue()); initStructure(config, res.getTemplate(), res.getSourceValue());
} }


if (res.isRunServer()) { if (res.isRunServer()) {
startWatch(res, config); watcher.start(res, config);
if (res.getSource().getPath().equals(".")) { if (res.getSource().getPath().equals(".")) {
// use the default destination folder // use the default destination folder
runServer(config.getString(Keys.DESTINATION_FOLDER), config.getString(Keys.SERVER_PORT)); runServer( config.getString( Keys.DESTINATION_FOLDER ), config.getString( Keys.SERVER_PORT ) );
} else if (res.getDestination() != null) {
// use the destination provided via the commandline
runServer( res.getDestination().getPath(), config.getString( Keys.SERVER_PORT ));
} else { } else {
runServer(res.getSource().getPath(), config.getString(Keys.SERVER_PORT)); runServer(config.getString( Keys.DESTINATION_FOLDER ), config.getString(Keys.SERVER_PORT));
} }
} }


Expand Down Expand Up @@ -136,7 +140,7 @@ private void printUsage(Object options) {
} }


private void runServer(String path, String port) { private void runServer(String path, String port) {
JettyServer.run(path, port); jettyServer.run(path, port);
} }


private void initStructure(CompositeConfiguration config, String type, String source) { private void initStructure(CompositeConfiguration config, String type, String source) {
Expand All @@ -156,20 +160,5 @@ private void initStructure(CompositeConfiguration config, String type, String so
throw new JBakeException(msg, e); throw new JBakeException(msg, e);
} }
} }


private void startWatch(final LaunchOptions res, CompositeConfiguration config) {
try {
FileSystemManager fsMan = VFS.getManager();
FileObject listenPath = fsMan.resolveFile(res.getSource(), config.getString(Keys.CONTENT_FOLDER));

DefaultFileMonitor monitor = new DefaultFileMonitor(new CustomFSChangeListener(res, config));
monitor.setRecursive(true);
monitor.addFile(listenPath);
monitor.start();
} catch (FileSystemException e) {
e.printStackTrace();
}


}
} }
105 changes: 105 additions & 0 deletions src/test/java/org/jbake/launcher/MainTest.java
Original file line number Original file line Diff line number Diff line change
@@ -0,0 +1,105 @@
package org.jbake.launcher;

import org.apache.commons.configuration.CompositeConfiguration;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.kohsuke.args4j.CmdLineException;
import org.kohsuke.args4j.CmdLineParser;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import static org.mockito.Mockito.verify;

@RunWith( MockitoJUnitRunner.class )
public class MainTest {

private Main main;

@Mock private Baker mockBaker;
@Mock private JettyServer mockJetty;
@Mock private BakeWatcher mockWatcher;

@Before
public void setup() {
this.main = new Main(mockBaker, mockJetty, mockWatcher);
}

@Test
public void launchJetty() {
String[] args = {"-s"};
main.run(args);

verify(mockJetty).run("output","8820");
}

@Test
public void launchJettyWithCustomSourceDir() {
String[] args = {"src/jbake", "-s"};
main.run(args);

verify(mockJetty).run("output","8820");
}

// Documentation states these two commands will define the custom output, but the LaunchOptions file isn't setup for that.
// I have written this test to define the existing functionality of the code and not that defined in docs.
@Test
public void launchJettyWithCustomDestinationDir() {
String[] args = {"-s", "build/jbake"};
main.run(args);

verify(mockJetty).run("output","8820");
}

@Test
public void launchJettyWithCustomSrcAndDestDir() {
String[] args = {"jbake", "build/jbake", "-s"};
main.run(args);

verify(mockJetty).run("build/jbake","8820");
}

@Test
public void launchJettyWithCustomDestViaConfig() throws CmdLineException {
String[] args = {"-s"};
Map properties = new HashMap(){{
put("destination.folder", "build/jbake");
}};
main.run(stubOptions(args), stubConfig(properties));

verify(mockJetty).run("build/jbake","8820");
}

@Test
public void launchJettyWithCmdlineOverridingProperties() throws CmdLineException {
String[] args = {"src/jbake", "build/jbake", "-s"};
Map properties = new HashMap(){{
put("destination.folder", "target/jbake");
}};
main.run(stubOptions(args), stubConfig(properties));

verify(mockJetty).run("build/jbake","8820");
}

private LaunchOptions stubOptions(String[] args) throws CmdLineException {
LaunchOptions res = new LaunchOptions();
CmdLineParser parser = new CmdLineParser(res);
parser.parseArgument(args);
return res;
}

private CompositeConfiguration stubConfig(Map<String, String> properties) {
CompositeConfiguration config = new CompositeConfiguration();
config.addProperty("server.port", "8820");
Iterator it = properties.entrySet().iterator();
while(it.hasNext()) {
Map.Entry<String,String> pair = (Map.Entry<String,String>)it.next();
config.addProperty( pair.getKey(), pair.getValue() );
}
return config;
}
}

0 comments on commit 92fb557

Please sign in to comment.