/**
* This file Copyright (c) 2009-2012 Magnolia International
* Ltd. (http://www.magnolia-cms.com). All rights reserved.
*
*
* This file is dual-licensed under both the Magnolia
* Network Agreement and the GNU General Public License.
* You may elect to use one or the other of these licenses.
*
* This file is distributed in the hope that it will be
* useful, but AS-IS and WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A
* PARTICULAR PURPOSE, TITLE, or NONINFRINGEMENT.
* Redistribution, except as permitted by whichever of the GPL
* or MNA you select, is prohibited.
*
* 1. For the GPL license (GPL), you can redistribute and/or
* modify this file under the terms of the GNU General
* Public License, Version 3, as published by the Free Software
* Foundation. You should have received a copy of the GNU
* General Public License, Version 3 along with this program;
* if not, write to the Free Software Foundation, Inc., 51
* Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 2. For the Magnolia Network Agreement (MNA), this file
* and the accompanying materials are made available under the
* terms of the MNA which accompanies this distribution, and
* is available at http://www.magnolia-cms.com/mna.html
*
* Any modifications to this file must keep this entire header
* intact.
*
*/
package info.magnolia.module;
import static org.easymock.EasyMock.createStrictMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import info.magnolia.cms.core.Content;
import info.magnolia.cms.core.HierarchyManager;
import info.magnolia.cms.core.ItemType;
import info.magnolia.cms.util.ContentUtil;
import info.magnolia.cms.util.NodeDataUtil;
import info.magnolia.context.MgnlContext;
import info.magnolia.jcr.node2bean.Node2BeanProcessor;
import info.magnolia.jcr.node2bean.impl.Node2BeanProcessorImpl;
import info.magnolia.jcr.node2bean.impl.Node2BeanTransformerImpl;
import info.magnolia.jcr.node2bean.impl.TypeMappingImpl;
import info.magnolia.logging.AuditLoggingManager;
import info.magnolia.module.InstallContext.Message;
import info.magnolia.module.delta.Delta;
import info.magnolia.module.model.ModuleDefinition;
import info.magnolia.module.model.RepositoryDefinition;
import info.magnolia.module.model.Version;
import info.magnolia.module.model.reader.BetwixtModuleDefinitionReader;
import info.magnolia.module.model.reader.DependencyChecker;
import info.magnolia.module.model.reader.ModuleDefinitionReader;
import info.magnolia.module.model.reader.ModuleDependencyException;
import info.magnolia.repository.RepositoryConstants;
import info.magnolia.test.ComponentsTestUtil;
import info.magnolia.test.RepositoryTestCase;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import javax.jcr.RepositoryException;
import org.junit.Before;
/**
* A base class for testing implementations of ModuleVersionHandler.
*
* @author gjoseph
* @version $Revision: $ ($Author: $)
*/
public abstract class ModuleVersionHandlerTestCase extends RepositoryTestCase {
@Override
@Before
public void setUp() throws Exception {
// ComponentsTestUtil.setInstance(ModuleRegistry.class, new ModuleRegistryImpl());
// assertNotNull(Components.getComponent(ModuleRegistry.class));
super.setUp();
// this should disable observation for audit logging (mgnl-beans.properties registers a path for this component)
ComponentsTestUtil.setInstance(AuditLoggingManager.class, new AuditLoggingManager());
}
@Override
protected List<ModuleDefinition> getModuleDefinitionsForTests() throws ModuleManagementException {
final BetwixtModuleDefinitionReader reader = new BetwixtModuleDefinitionReader();
final List<ModuleDefinition> modules = new ArrayList<ModuleDefinition>();
final List<String> moduleDescriptorPaths = getModuleDescriptorPathsForTests();
for (String moduleDescriptorPath : moduleDescriptorPaths) {
final ModuleDefinition module = reader.readFromResource(moduleDescriptorPath);
modules.add(module);
}
return modules;
}
protected List<String> getModuleDescriptorPathsForTests() {
return Collections.singletonList(getModuleDescriptorPath());
}
/**
* A helper method to quickly set up a few properties to simulate a given environment.
* Could be advantageously replaced by a dsl-like api, see MAGNOLIA-2828.
* @param itemType an instance of {@link ItemType}. If <code>null</code>, defaults to {@link ItemType#CONTENT}
*/
protected void setupProperty(final String workspace, String path, String propertyName, String value, ItemType itemType) throws RepositoryException {
final HierarchyManager hm = MgnlContext.getHierarchyManager(workspace);
final Content content = ContentUtil.createPath(hm, path, itemType != null ? itemType : ItemType.CONTENT);
if (propertyName != null) {
NodeDataUtil.getOrCreateAndSet(content, propertyName, value);
}
hm.save();
}
/**
* Helper to set a property in the config workspace.
* @see #setupProperty(String, String, String, String, ItemType)
*/
protected void setupConfigProperty(String path, String propertyName, String value) throws RepositoryException {
setupProperty(RepositoryConstants.CONFIG, path, propertyName, value, null);
}
/**
* Helper to set a property in the config workspace.
* @see #setupProperty(String, String, String, String, ItemType)
*/
protected void setupNode(String workspace, String path) throws RepositoryException {
setupProperty(workspace, path, null, null, null);
}
/**
* Helper to set a property in the config workspace.
* @see #setupProperty(String, String, String, String, ItemType)
*/
protected void setupNode(String workspace, String path, ItemType type) throws RepositoryException {
setupProperty(workspace, path, null, null, type);
}
/**
* Helper to create an empty node.
* @see #setupProperty(String, String, String, String, ItemType)
*/
protected void setupConfigNode(String path) throws RepositoryException {
setupNode(RepositoryConstants.CONFIG, path);
}
/**
* Helper to create an empty node.
* @see #setupProperty(String, String, String, String, ItemType)
*/
protected void setupConfigNode(String path, ItemType type) throws RepositoryException {
setupNode(RepositoryConstants.CONFIG, path, type);
}
/**
* Asserts that a property at the given path as the expected value.
*/
protected void assertConfig(String expectedValue, String path) throws RepositoryException {
assertEquals(expectedValue, MgnlContext.getHierarchyManager("config").getNodeData(path).getString());
}
protected void assertNoMessages(InstallContext ctx) {
Map<String, List<Message>> messages = ctx.getMessages();
if (!messages.isEmpty()) {
Iterator<String> keyIterator = messages.keySet().iterator();
StringBuilder builder = new StringBuilder("Expected no Messages but got:\n");
String currentKey;
List<Message> currentMessages;
while (keyIterator.hasNext()) {
currentKey = keyIterator.next();
builder.append("Key: ").append(currentKey);
currentMessages = messages.get(currentKey);
Iterator<Message> messageIterator = currentMessages.iterator();
Message currentMessage;
while (messageIterator.hasNext()) {
currentMessage = messageIterator.next();
builder.append("\n");
builder.append(" Message: ").append(currentMessage.getMessage());
builder.append(" Details: ").append(currentMessage.getDetails());
}
builder.append("\n");
}
fail(builder.toString());
}
}
/**
* Asserts that the install context contains one single message, with the expected contents and priority.
*/
protected void assertSingleMessage(InstallContext installContext, String expectedMessage, InstallContext.MessagePriority expectedPriority) {
final Map<String, List<InstallContext.Message>> messages = installContext.getMessages();
assertEquals(1, messages.size());
final List<InstallContext.Message> messagesForModule = messages.values().iterator().next();
assertEquals(1, messagesForModule.size());
final InstallContext.Message msg = messagesForModule.get(0);
assertEquals(expectedMessage, msg.getMessage());
assertEquals(expectedPriority, msg.getPriority());
}
/**
* This essentially fakes calls to ModuleManager and ensures only the ModuleVersionHandler under test
* is in use.
* Returns the InstallContext so one can further assert the state of the install/update (status, messages, ...)
*
* It's likely that this will need improvements when we want to test ModuleVersionHandler which
* use IsModuleInstalledOrRegistered tasks, for example.
*/
protected InstallContext executeUpdatesAsIfTheCurrentlyInstalledVersionWas(final Version currentlyInstalledVersion) throws ModuleManagementException {
final BetwixtModuleDefinitionReader reader = new BetwixtModuleDefinitionReader();
final ModuleDefinition moduleDefinition = reader.readFromResource(getModuleDescriptorPath());
final String[] extraWorkspaces = getExtraWorkspaces();
final String nodeTypeFile = getExtraNodeTypes();
if (extraWorkspaces.length > 0 || nodeTypeFile != null) {
final RepositoryDefinition repo = new RepositoryDefinition();
repo.setName("magnolia");
for (String wsName : extraWorkspaces) {
repo.addWorkspace(wsName);
}
repo.setNodeTypeFile(nodeTypeFile);
moduleDefinition.addRepository(repo);
}
final ModuleDefinitionReader readerMock = createStrictMock(ModuleDefinitionReader.class);
expect(readerMock.readAll()).andReturn(Collections.singletonMap(moduleDefinition.getName(), moduleDefinition));
replay(readerMock);
final Node2BeanProcessor n2b = new Node2BeanProcessorImpl(new TypeMappingImpl(), new Node2BeanTransformerImpl());
final ModuleVersionHandler versionHandlerUnderTest = newModuleVersionHandlerForTests();
final ModuleRegistryImpl moduleRegistry = new ModuleRegistryImpl();
final DependencyChecker depCheck = new NullDependencyChecker();
final InstallContextImpl ctx = new InstallContextImpl(moduleRegistry) {
@Override
public void error(String message, Throwable th) {
th.printStackTrace();
// let's fail the test if we encounter a logged error, because ModuleManagerImpl.applyDeltas() swallows TaskExecutionException and RuntimeException and logs errors instead
fail(message);
}
};
final ModuleManagerImpl mm = new ModuleManagerImpl(ctx, readerMock, moduleRegistry, depCheck, n2b) {
@Override
protected ModuleVersionHandler newVersionHandler(ModuleDefinition module) {
assertEquals("this test doesn't behave as expected", moduleDefinition, module);
// wrap and delegate so that we control getCurrentlyInstalled()
return new ModuleVersionHandler() {
@Override
public Version getCurrentlyInstalled(InstallContext ctx) {
return currentlyInstalledVersion;
}
@Override
public List<Delta> getDeltas(InstallContext installContext, Version from) {
return versionHandlerUnderTest.getDeltas(installContext, from);
}
@Override
public Delta getStartupDelta(InstallContext installContext) {
return versionHandlerUnderTest.getStartupDelta(installContext);
}
};
}
};
final List<ModuleDefinition> definitions = mm.loadDefinitions();
assertEquals(1, definitions.size());
assertEquals(moduleDefinition, definitions.get(0));
mm.checkForInstallOrUpdates();
assertTrue(mm.getStatus().needsUpdateOrInstall());
mm.performInstallOrUpdate();
assertEquals(InstallStatus.installDone, mm.getInstallContext().getStatus());
verify(readerMock);
return ctx;
}
/**
* Extend this method if you need more workspaces than the default ones.
* This can be useful if your MVH adds content to workspaces registered by
* a module it depends upon.
* Be aware that this is used in such a way that the ModuleDefinition of the
* module under test will be modified to register those repositories itself.
*/
protected String[] getExtraWorkspaces() {
return new String[]{};
}
/**
* Extend this method if you need more node types than the default ones.
* This can be useful if your MVH needs node types registered by a module
* it depends upon.
* Be aware that this is used in such a way that the ModuleDefinition of the
* module under test will be modified to register those repositories itself.
* @return the path to the node type definition resource, as found in a module descriptor
*/
protected String getExtraNodeTypes() {
return null;
}
protected abstract String getModuleDescriptorPath();
protected abstract ModuleVersionHandler newModuleVersionHandlerForTests();
private static class NullDependencyChecker implements DependencyChecker {
@Override
public void checkDependencies(Map<String, ModuleDefinition> moduleDefinitions) throws ModuleDependencyException {
// do nothing
}
@Override
public List<ModuleDefinition> sortByDependencyLevel(Map<String, ModuleDefinition> moduleDefinitions) {
return new ArrayList<ModuleDefinition>(moduleDefinitions.values());
}
}
}