Java tutorial
/* * Copyright (c) 2015 mgm technology partners GmbH * * 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.mgmtp.jfunk.core.module; import static com.google.common.base.Preconditions.checkNotNull; import static com.google.common.base.Preconditions.checkState; import static com.google.common.collect.Lists.newArrayListWithExpectedSize; import static com.google.common.collect.Lists.transform; import java.io.File; import java.util.List; import javax.inject.Inject; import javax.inject.Provider; import org.apache.commons.lang3.builder.ToStringBuilder; import org.apache.commons.lang3.builder.ToStringStyle; import com.google.common.base.Function; import com.mgmtp.jfunk.common.JFunkConstants; import com.mgmtp.jfunk.common.util.Configuration; import com.mgmtp.jfunk.core.config.ArchiveDir; import com.mgmtp.jfunk.core.config.ModuleArchiveDir; import com.mgmtp.jfunk.core.module.ContainerModule.Builder.ModuleWithCallbacks; import com.mgmtp.jfunk.core.scripting.ScriptContext; /** * Allows grouping of independent {@link TestModule}s into one container. The main purpose of this * is that, in addition to the test archives of the individual {@link TestModule}s, an overall test * archive is written containing the individual archives and a log file of the complete execution of * all contained {@link TestModule}s. A {@link ContainerModule} is created using * {@link ContainerModule.Builder}: * * <pre> * * @Test * public void testContainerModule() { * jFunkRunner.set("archive.dir", "testruns/container"); * * TestModule testModule = ContainerModule.forName("container") * .addTestModule(new FooModule(), new Runnable() { * @Override * public void run() { * jFunkRunner.prepareNextDataSet("foo"); * } * }, null) * .addTestModule(new BarModule(), new Runnable() { * @Override * public void run() { * jFunkRunner.prepareNextDataSet("bar"); * } * }, null) * .build(); * * jFunkRunner.run(testModule); * } * </pre> * * @author rnaegele * @since 3.1 */ public class ContainerModule implements TestModule { private final String name; private final List<ModuleWithCallbacks> modulesWithCallbacks; private boolean error; @Inject private ScriptContext scriptContext; @Inject @ModuleArchiveDir // need provider because it is not yet set at injection time private Provider<File> moduleArchiveDirProvider; @Inject @ArchiveDir // need provider because it is not yet set at injection time private Provider<File> archiveDirProvider; @Inject private Configuration config; private String moduleArchiveDir; private ContainerModule(final String name, final List<ModuleWithCallbacks> modulesWithCallbacks) { this.name = checkNotNull(name, "'name' must not be null"); this.modulesWithCallbacks = checkNotNull(modulesWithCallbacks, "'modulesWithCallbacks' must not be null"); checkState(modulesWithCallbacks.size() > 0, "At least one test module must be specified"); } /** * @return the module name */ @Override public String getName() { return name; } /** * Executes the added test modules and their callbacks in the order of their addition using * {@link ScriptContext#run(TestModule)}. */ @Override public void execute() { // save archive dir of the outer module so it can be reset later String previousArchiveDir = archiveDirProvider.get().getPath(); try { // make sure archive dirs are within that of the container module moduleArchiveDir = moduleArchiveDirProvider.get().getPath(); config.put(JFunkConstants.ARCHIVE_DIR, moduleArchiveDir); for (ModuleWithCallbacks moduleWithCallbacks : modulesWithCallbacks) { TestModule testModule = moduleWithCallbacks.testModule; Runnable beforeModuleCallback = moduleWithCallbacks.beforeModuleCallback; if (beforeModuleCallback != null) { beforeModuleCallback.run(); } scriptContext.run(testModule); Runnable afterModuleCallback = moduleWithCallbacks.afterModuleCallback; if (afterModuleCallback != null) { afterModuleCallback.run(); } } } finally { // reset for outer module, so property is correctly archived config.put(JFunkConstants.ARCHIVE_DIR, previousArchiveDir); // it does not make sense to archive datasets in the container module, // these would be those of the test module executed last config.put(JFunkConstants.ARCHIVE_DATASETS, "false"); } } @Override public String getDataSetKey() { throw new UnsupportedOperationException("Not implemented."); } @Override public void setDataSetKey(final String dataSetKey) { throw new UnsupportedOperationException("Not implemented."); } @Override public void setError(final boolean error) { this.error = error; } @Override public boolean isError() { return error; } @Override public String toString() { ToStringBuilder tsb = new ToStringBuilder(this, ToStringStyle.SHORT_PREFIX_STYLE); tsb.append("name", name); if (moduleArchiveDir != null) { tsb.append("moduleArchiveDir", moduleArchiveDir); } tsb.append("error", error); tsb.append("modules", transform(modulesWithCallbacks, new Function<ModuleWithCallbacks, TestModule>() { @Override public TestModule apply(final ModuleWithCallbacks input) { return input.testModule; } })); return tsb.toString(); } /** * Creates a {@link Builder} for creating a {@link ContainerModule} with the specified name. * * @param name * the module name * @return the {@link Builder} */ public static Builder forName(final String name) { return new Builder(name); } /** * Builder for {@link ContainerModule}. * * @author rnaegele */ public static class Builder { private final String name; private final List<ModuleWithCallbacks> modulesWithCallbacks = newArrayListWithExpectedSize(2); /** * @param name * the module name */ public Builder(final String name) { this.name = name; } /** * Adds a test module to the {@link ContainerModule}. * * @param testModule * the test module to be added to the {@link ContainerModule} * @return this {@link Builder} instance */ public Builder addTestModule(final TestModule testModule) { modulesWithCallbacks.add(new ModuleWithCallbacks(testModule, null, null)); return this; } /** * Adds a test module and the specified callback to the {@link ContainerModule}. The * callback is executed before the specified module in the scope of the * {@link ContainerModule}. * * @param beforeModuleCallback * a {@link Runnable} to be executed just before the specified module is * executed, may be {@code null} * @param testModule * the test module to be added to the {@link ContainerModule} * @return this {@link Builder} instance */ public Builder addTestModule(final Runnable beforeModuleCallback, final TestModule testModule) { modulesWithCallbacks.add(new ModuleWithCallbacks(testModule, beforeModuleCallback, null)); return this; } /** * Adds a test module and the specified callbacks to the {@link ContainerModule}. Callbacks * are executed before and/or after the specified module in the scope of the * {@link ContainerModule}. * * @param beforeModuleCallback * a {@link Runnable} to be executed just before the specified module is * executed, may be {@code null} * @param testModule * the test module to be added to the {@link ContainerModule} * @param afterModuleCallback * a {@link Runnable} to be executed right after the specified module is * executed, may be {@code null} * @return this {@link Builder} instance */ public Builder addTestModule(final Runnable beforeModuleCallback, final TestModule testModule, final Runnable afterModuleCallback) { modulesWithCallbacks .add(new ModuleWithCallbacks(testModule, beforeModuleCallback, afterModuleCallback)); return this; } /** * Adds a test module and the specified callback to the {@link ContainerModule}. The * callback is executed after the specified module in the scope of the * {@link ContainerModule}. * * @param testModule * the test module to be added to the {@link ContainerModule} * @param afterModuleCallback * a {@link Runnable} to be executed right after the specified module is * executed, may be {@code null} * @return this {@link Builder} instance */ public Builder addTestModule(final TestModule testModule, final Runnable afterModuleCallback) { modulesWithCallbacks.add(new ModuleWithCallbacks(testModule, null, afterModuleCallback)); return this; } /** * Create the {@link ContainerModule} from the {@link Builder}. * * @return the {@link ContainerModule} */ public ContainerModule build() { return new ContainerModule(name, modulesWithCallbacks); } static class ModuleWithCallbacks { public ModuleWithCallbacks(final TestModule testModule, final Runnable beforeModuleCallback, final Runnable afterModuleCallback) { this.testModule = testModule; this.beforeModuleCallback = beforeModuleCallback; this.afterModuleCallback = afterModuleCallback; } TestModule testModule; Runnable beforeModuleCallback; Runnable afterModuleCallback; } } }