/*
* Copyright 2011 Google Inc.
*
* 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. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.google.jstestdriver.embedded;
import com.google.common.base.Preconditions;
import com.google.common.collect.Lists;
import com.google.inject.Binder;
import com.google.inject.Injector;
import com.google.inject.Key;
import com.google.inject.Module;
import com.google.inject.TypeLiteral;
import com.google.inject.multibindings.Multibinder;
import com.google.jstestdriver.ActionRunner;
import com.google.jstestdriver.Args4jFlagsParser;
import com.google.jstestdriver.FlagsParser;
import com.google.jstestdriver.JsTestDriver;
import com.google.jstestdriver.PluginLoader;
import com.google.jstestdriver.ResponseStreamFactory;
import com.google.jstestdriver.config.Configuration;
import com.google.jstestdriver.config.ConfigurationException;
import com.google.jstestdriver.config.ConfigurationSource;
import com.google.jstestdriver.config.UserConfigurationSource;
import com.google.jstestdriver.config.YamlParser;
import com.google.jstestdriver.hooks.ActionListProcessor;
import com.google.jstestdriver.hooks.FileLoadPostProcessor;
import com.google.jstestdriver.hooks.JsTestDriverValidator;
import com.google.jstestdriver.hooks.PluginInitializer;
import com.google.jstestdriver.hooks.ServerListener;
import com.google.jstestdriver.hooks.TestListener;
import com.google.jstestdriver.model.BasePaths;
import com.google.jstestdriver.runner.RunnerMode;
import java.io.File;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
/**
* @author corbinrsmith@gmail.com (Cory Smith)
*
*/
public class JsTestDriverBuilder {
private static final List<JsTestDriverValidator> DEFAULT_VALIDATORS =
Lists.<JsTestDriverValidator>newArrayList(new JsTestDriverValidator() {
@Override
public void validate(Injector injector) throws AssertionError {
Preconditions.checkArgument(injector.getInstance(Key.get(new TypeLiteral<Set<ResponseStreamFactory>>() {}))
.size() > 0);
Preconditions.checkArgument(
injector.getInstance(Key.get(new TypeLiteral<Set<ActionListProcessor>>() {})).size() > 0);
Preconditions.checkArgument(
injector.getInstance(Key.get(new TypeLiteral<Set<FileLoadPostProcessor>>() {}))
.size() > 0);
injector.getInstance(ActionRunner.class);
}
});
private BasePaths basePaths = new BasePaths();
private List<Module> pluginModules = Lists.newArrayList();
private String[] flags = new String[]{};
private Configuration configuration;
private final PluginLoader pluginLoader = new PluginLoader();
private int port = -1;
private final List<ServerListener> serverListeners = Lists.newArrayList();
private final List<TestListener> testListeners = Lists.newArrayList();
private RunnerMode runnerMode = RunnerMode.QUIET;
private String serverAddress;
private final List<JsTestDriverValidator> validators = Lists.newArrayList();
private boolean raiseOnFailure = false;
private boolean preload = false;
private final List<Class<? extends PluginInitializer>> pluginInitializers = Lists.newArrayList();
private final List<PluginInitializer> pluginInitializersInstances = Lists.newArrayList();
private FlagsParser flagsParser = new Args4jFlagsParser();
/**
* @param configPath
* @return The builder.
*/
public JsTestDriverBuilder setDefaultConfiguration(String configPath) {
setConfigurationSource(new UserConfigurationSource(new File(configPath)));
return this;
}
/**
* @param configuration
* @return The builder.
*/
public JsTestDriverBuilder setDefaultConfiguration(Configuration configuration) {
this.configuration = configuration;
return this;
}
/**
* Forces JsTestDriver to validate the runtime configuration. Used to ensure
* plugins are properly configured, and find early indications of problems.
* @param validate
* @return The builder.
*/
public JsTestDriverBuilder shouldValidate(boolean validate, JsTestDriverValidator... extraValidators) {
if (validate) {
validators.addAll(DEFAULT_VALIDATORS);
for (JsTestDriverValidator jsTestDriverValidator : extraValidators) {
validators.add(jsTestDriverValidator);
}
} else {
validators.clear();
}
return this;
}
/**
* @param port
* @return The builder.
*/
public JsTestDriverBuilder setPort(int port) {
this.port = port;
return this;
}
/**
* @deprecated Use the builder to set the options usually dictated by flags.
*/
@Deprecated
public JsTestDriverBuilder setFlagsParser(FlagsParser flagsParser) {
this.flagsParser = flagsParser;
return this;
}
/**
* @param testServerListener
* @return The builder.
*/
public JsTestDriverBuilder addServerListener(ServerListener testServerListener) {
serverListeners.add(testServerListener);
return this;
}
public JsTestDriverBuilder raiseExceptionOnTestFailure(boolean raiseOnFailure) {
this.raiseOnFailure = raiseOnFailure;
return this;
}
/**
* Builds a configured JsTestDriver instance, and possibly validates the
* configuration.
*/
public JsTestDriver build() throws AssertionError {
if (configuration == null) {
throw new ConfigurationException("A default configuration is required.");
}
// TODO(corysmith): add check to resolve the serverAddress and port issues.
List<Module> plugins = Lists.newArrayList(pluginModules);
plugins.add(new ListenerBindingModule(serverListeners, testListeners));
List<Module> initializers =
Lists.<Module>newArrayList(new PluginInitializerModule(pluginInitializers,
pluginInitializersInstances));
initializers.addAll(pluginModules);
// merge basepaths
basePaths.addAll(configuration.getBasePaths());
configuration.getBasePaths().addAll(basePaths);
JsTestDriverImpl jsTestDriver = new JsTestDriverImpl(configuration,
pluginLoader,
runnerMode,
flags,
port,
plugins,
initializers,
basePaths,
serverAddress,
raiseOnFailure,
preload,
flagsParser);
if (!validators.isEmpty()) {
jsTestDriver.validate(
validators.toArray(new JsTestDriverValidator[validators.size()]));
}
return jsTestDriver;
}
/**
* @param initializer The {@link PluginInitializer} class, used during initialization
* to instal a plugin into the jstd system.
* @return The build instance.
*/
public JsTestDriverBuilder withPluginInitializer(
Class<? extends PluginInitializer> initializer) {
pluginInitializers.add(initializer);
return this;
}
/**
* @param plugin An instance of the initializer.
* @return The builder instance.
*/
public JsTestDriverBuilder withPluginInitializer(
PluginInitializer initializer) {
pluginInitializersInstances.add(initializer);
return this;
}
/**
* Sets the runner mode for JsTestDriver. The Runner mode is a combination of
* logging and reporting level indications.
*/
public JsTestDriverBuilder setRunnerMode(RunnerMode runnerMode) {
this.runnerMode = runnerMode;
return this;
}
/**
* @param testTestResultsListener
* @return
*/
public JsTestDriverBuilder addTestListener(TestListener testResultListener) {
testListeners.add(testResultListener);
return this;
}
/**
* @param string
* @return
*/
public JsTestDriverBuilder setServer(String serverAddress) {
this.serverAddress = serverAddress;
return this;
}
/**
* @param file
* @return
*/
public JsTestDriverBuilder addBaseDir(File file) {
this.basePaths.add(file);
return this;
}
/**
* @param configurationSource
*/
public JsTestDriverBuilder setConfigurationSource(ConfigurationSource source) {
setDefaultConfiguration(source.parse(basePaths, new YamlParser()));
return this;
}
/**
* Modules for the plugins.
* @param pluginModules
* @return
* @deprecated In favor of passing in {@link PluginInitializer}s
*/
public JsTestDriverBuilder addPluginModules(List<Module> pluginModules) {
this.pluginModules = pluginModules;
return this;
}
/**
* Don't pass in the commandline flags. This method is heavily used by the
* command line JsTD. Currently work to remove it.
* @param flags
* @return
* @deprecated
*/
public JsTestDriverBuilder setFlags(String[] flags) {
this.flags = flags;
return this;
}
private static final class PluginInitializerModule implements Module {
private final List<Class<? extends PluginInitializer>> initializers;
private final List<PluginInitializer> instances;
public PluginInitializerModule(List<Class<? extends PluginInitializer>> initializers,
List<PluginInitializer> instances) {
this.initializers = initializers;
this.instances = instances;
}
@Override
public void configure(Binder binder) {
Multibinder<PluginInitializer> setBinder =
Multibinder.newSetBinder(binder, PluginInitializer.class);
for (Class<? extends PluginInitializer> initializer : initializers) {
setBinder.addBinding().to(initializer);
}
for (PluginInitializer initializer : instances) {
setBinder.addBinding().toInstance(initializer);
}
}
}
private static class ListenerBindingModule implements Module {
private final List<? extends ServerListener> serverListeners;
private final List<? extends TestListener>testListeners;
ListenerBindingModule(List<? extends ServerListener> serverListeners,
List<? extends TestListener> testListeners) {
this.serverListeners = serverListeners;
this.testListeners = testListeners;
}
@Override
public void configure(Binder binder) {
Multibinder<ServerListener> serverSetBinder =
Multibinder.newSetBinder(binder, ServerListener.class);
for (ServerListener listener : serverListeners) {
serverSetBinder.addBinding().toInstance(listener);
}
Multibinder<TestListener> testSetBinder =
Multibinder.newSetBinder(binder, TestListener.class);
for (TestListener listener : testListeners) {
testSetBinder.addBinding().toInstance(listener);
}
}
}
/**
* Configures JsTestDriver to load the default configuration files into the browser on capture.
*/
public JsTestDriverBuilder preloadFiles() {
preload = true;
return this;
}
/**
* Adds the basePaths for the JsTestDriver instance to use for path resolution.
*/
public JsTestDriverBuilder addBasePaths(BasePaths basePaths) {
this.basePaths.addAll(basePaths);
return this;
}
/**
* Adds the basePaths for the JsTestDriver instance to use for path resolution.
*/
public JsTestDriverBuilder addBasePaths(File...basePaths) {
this.basePaths.addAll(Arrays.asList(basePaths));
return this;
}
}