/*******************************************************************************
* Copyright (c) 2008-2011 Chair for Applied Software Engineering,
* Technische Universitaet Muenchen.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* TobiasVerhoeven
******************************************************************************/
package org.eclipse.emf.emfstore.performance.test.memory;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.emf.emfstore.client.ESLocalProject;
import org.eclipse.emf.emfstore.client.ESRemoteProject;
import org.eclipse.emf.emfstore.common.ESSystemOutProgressMonitor;
import org.eclipse.emf.emfstore.fuzzy.emf.junit.ESDefaultModelMutator;
import org.eclipse.emf.emfstore.internal.client.model.ProjectSpace;
import org.eclipse.emf.emfstore.internal.client.model.impl.api.ESLocalProjectImpl;
import org.eclipse.emf.emfstore.internal.client.model.impl.api.ESWorkspaceImpl;
import org.eclipse.emf.emfstore.internal.client.model.util.EMFStoreCommand;
import org.eclipse.emf.emfstore.internal.client.model.util.EMFStoreCommandWithResult;
import org.eclipse.emf.emfstore.internal.common.model.Project;
import org.eclipse.emf.emfstore.internal.server.exceptions.FatalESException;
import org.eclipse.emf.emfstore.internal.server.model.impl.api.versionspec.ESPrimaryVersionSpecImpl;
import org.eclipse.emf.emfstore.internal.server.model.versioning.VersionSpec;
import org.eclipse.emf.emfstore.modelmutator.ESModelMutatorConfiguration;
import org.eclipse.emf.emfstore.modelmutator.ESModelMutatorUtil;
import org.eclipse.emf.emfstore.server.exceptions.ESException;
import org.eclipse.emf.emfstore.server.model.versionspec.ESPrimaryVersionSpec;
import org.eclipse.emf.emfstore.server.model.versionspec.ESVersionSpec;
import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
/**
* Tests for creating big amounts of data in order to test memory management.
*
* @author Tobias Verhoeven
*/
@SuppressWarnings("restriction")
public class MemoryLoadTest {
private final String modelKey = "http://org/eclipse/example/bowling";
private final long seed = 47209572905723L;
private final boolean keepLocalData = false; // weather client data should
// be kept
private static final Logger LOGGER = Logger
.getLogger("org.eclipse.emf.emfstore.client.test");
private long currentProjectCount; // The current project count.
private ESModelMutatorConfiguration currentProjectConfiguration;
private static final ESSystemOutProgressMonitor MONITOR = new ESSystemOutProgressMonitor();
/** Class Rule for starting an EMFStore-Server. */
// @ClassRule
private static RunningEMFStoreRule runningEMFStoreRule = new RunningEMFStoreRule();
/** Rule for deleting all remote projects. */
@Rule
public NoRemoteProjectRule noRemoteProjectRule = new NoRemoteProjectRule(
runningEMFStoreRule);
/**
* Starts the EMFstore.
*
* @throws IOException
* Signals that an I/O exception has occurred.
* @throws FatalESException
* the fatal es exception
* @throws ESException
* the eS exception
*/
@BeforeClass
public static void before() throws IOException, FatalESException,
ESException {
runningEMFStoreRule.before();
}
/**
* Stops the EMFStore.
*/
@AfterClass
public static void after() {
runningEMFStoreRule.after();
}
/**
* Test for solely sharing projects.
*
* @throws ESException
* in case of an error
*/
@Test
public void shareProjectLoadTest() throws ESException {
shareProjectsLoadTest(100, 1000);
// shareProjectsLoadTest(1000, 10);
// shareProjectsLoadTest(10000, 1000);
}
private void shareProjectsLoadTest(int minProjectSize, int projectCount)
throws ESException {
for (int i = 0; i < projectCount; i++) {
final ESLocalProject project = generateRandomProject(minProjectSize);
long time = System.nanoTime();
project.shareProject(runningEMFStoreRule.defaultSession(), null);
time = System.nanoTime() - time;
log("Shared Project: " + project.getProjectName() + " ,Memory: "
+ usedMemoryInMib() + " MiB");
deleteLocallyIfNeeded(project);
log("Shared Project: " + project.getProjectName() + " ,Memory: "
+ usedMemoryInMib() + " MiB");
}
}
/**
* Test for sharing and checking out projects.
*
* @throws ESException
* in case of an error
*/
@Test
public void shareCheckoutProjectLoadTest() throws ESException {
shareCheckoutProjectsLoadTest(1000, 90);
// shareCheckoutProjectsLoadTest(10000, 1000);
}
private void shareCheckoutProjectsLoadTest(int minProjectSize,
int projectCount) throws ESException {
final long start = currentProjectCount;
final int[] sizes = new int[projectCount];
for (int i = 0; i < projectCount; i++) {
final ESLocalProject project = generateRandomProject(minProjectSize);
project.shareProject(runningEMFStoreRule.defaultSession(), null);
deleteLocallyIfNeeded(project);
sizes[i] = project.getAllModelElements().size();
log("Shared Project: " + project.getProjectName() + " ,Memory: "
+ usedMemoryInMib() + " MiB");
}
int i = 0;
for (final ESRemoteProject remoteProject : runningEMFStoreRule.server()
.getRemoteProjects(runningEMFStoreRule.defaultSession())) {
final ESLocalProject project = remoteProject.checkout(
"Generated project_" + (start + i), MONITOR);
assertEquals("Generated project_" + (start + i),
project.getProjectName());
// Assert.assertEquals(sizes[i],
// project.getAllModelElements().size());
deleteLocallyIfNeeded(project);
log("Checked out Project: " + project.getProjectName() + "Memory: "
+ usedMemoryInMib() + " MiB");
i++;
}
}
/**
* Test for committing changes.
*
* @throws ESException
* in case of an error
*/
@Test
public void commitLoadTest() throws ESException {
commitLoadTest(1000, 1, 200, 1000);
// commitLoadTest(1000, 1, 2500, 1000);
}
private void commitLoadTest(int minProjectSize, int projectCount,
int historySize, int minChangeSize) throws ESException {
for (int i = 0; i < projectCount; i++) {
final ESLocalProject project = generateRandomProject(minProjectSize);
project.shareProject(runningEMFStoreRule.defaultSession(), null);
for (int z = 0; z < historySize; z++) {
mutateProject(project, minChangeSize);
commitProject(project);
log("Committed Change: " + z + " of Project "
+ project.getProjectName() + " ,Memory: "
+ usedMemoryInMib() + " MiB");
}
deleteLocallyIfNeeded(project);
}
}
/**
* Test for committing changes and checking out resulting projectstates.
*
* @throws ESException
* in case of an error
*/
@Test
public void commitCheckoutLoadTest() throws ESException {
// commitCheckoutLoadTest(1000, 1, 1000, 100);
commitCheckoutLoadTest(1000, 1, 50, 1000, 2);
// commitCheckoutLoadTest(1000, 1, 1000, 1000,333);
}
private void commitCheckoutLoadTest(int minProjectSize, int projectCount,
int historySize, int minChangeSize, int checkoutStep)
throws ESException {
for (int i = 0; i < projectCount; i++) {
final ESLocalProject project = generateRandomProject(minProjectSize);
project.shareProject(runningEMFStoreRule.defaultSession(), null);
final List<ESPrimaryVersionSpec> versions = new ArrayList<ESPrimaryVersionSpec>();
for (int z = 0; z < historySize; z++) {
mutateProject(project, minChangeSize);
versions.add(commitProject(project));
log("Committed Change: " + z + " of Project "
+ project.getProjectName() + " ,Memory: "
+ usedMemoryInMib() + " MiB");
}
deleteLocallyIfNeeded(project);
for (int x = 0; x < versions.size(); x += checkoutStep) {
log("Checking out version: " + versions.get(x).getIdentifier());
final ESLocalProject projectCopy = project.getRemoteProject()
.checkout(project.getProjectName() + "_Copy" + x,
runningEMFStoreRule.defaultSession(),
versions.get(x), MONITOR);
log("Checked out version: " + versions.get(x).getIdentifier()
+ " ,Memory: " + usedMemoryInMib() + " MiB");
deleteLocallyIfNeeded(projectCopy);
}
}
}
/**
* Shares projects, commits changes and updates local versions.
*
* @throws ESException
* in case of an error
*/
@Test
public void updateLoadTest() throws ESException {
updateLoadTest(1000, 1, 10, 10);
}
private void updateLoadTest(int minProjectSize, int projectCount,
int historySize, int minChangeSize) throws ESException {
for (int i = 0; i < projectCount; i++) {
final ESLocalProject project = generateRandomProject(minProjectSize);
project.shareProject(runningEMFStoreRule.defaultSession(), null);
final ESLocalProject projectSecondCheckout = project
.getRemoteProject().checkout(
project.getProjectName() + "_SecondCheckout_" + i,
runningEMFStoreRule.defaultSession(),
project.getBaseVersion(), null);
final List<ESVersionSpec> versions = new ArrayList<ESVersionSpec>();
for (int z = 0; z < historySize; z++) {
mutateProject(project, minChangeSize);
versions.add(commitProject(project));
log("Memory: " + usedMemoryInMib() + " MiB");
}
deleteLocallyIfNeeded(project);
projectSecondCheckout.update(
versions.get((versions.size() - 1) / 2), null, null);
projectSecondCheckout.update(versions.get(1), null, null);
projectSecondCheckout.update(null);
deleteLocallyIfNeeded(projectSecondCheckout);
log("Memory: " + usedMemoryInMib() + " MiB");
}
}
/**
* Shares projects, commits changes and retrieves specific changePackages.
*
* @throws ESException
* in case of an error
*/
@Test
public void getChangePackagesLoadTest() throws ESException {
commitGetChangesLoadTest(1000, 1, 50, 100);
}
private void commitGetChangesLoadTest(int minProjectSize, int projectCount,
int historySize, int minChangeSize) throws ESException {
for (int i = 0; i < projectCount; i++) {
final ESLocalProject project = generateRandomProject(minProjectSize);
project.shareProject(runningEMFStoreRule.defaultSession(), null);
final List<VersionSpec> versions = new ArrayList<VersionSpec>();
for (int z = 0; z < historySize; z++) {
mutateProject(project, minChangeSize);
versions.add(((ESPrimaryVersionSpecImpl) commitProject(project))
.toInternalAPI());
}
((ESLocalProjectImpl) project).toInternalAPI().getChanges(
versions.get(0), versions.get(versions.size() - 1));
deleteLocallyIfNeeded(project);
log("Memory: " + usedMemoryInMib() + " MiB");
}
}
private long usedMemoryInMib() {
return Runtime.getRuntime().totalMemory()
- Runtime.getRuntime().freeMemory() >> 20;
}
private ESLocalProject generateRandomProject(int minProjectSize) {
final String projectName = "Generated project_" + currentProjectCount;
final Project project = org.eclipse.emf.emfstore.internal.common.model.ModelFactory.eINSTANCE
.createProject();
final ESModelMutatorConfiguration config = createModelMutatorConfigurationRandom(
modelKey, project, minProjectSize, seed);
new EMFStoreCommand() {
@Override
protected void doRun() {
ESDefaultModelMutator.generateModel(config);
}
}.run(false);
final ProjectSpace projectSpace = ((ESWorkspaceImpl) runningEMFStoreRule
.connectedWorkspace()).toInternalAPI().importProject(project,
projectName, "");
currentProjectCount++;
return projectSpace.toAPI();
}
private void mutateProject(final ESLocalProject project, int minChangeSize) {
if (currentProjectConfiguration == null
|| currentProjectConfiguration.getRootEObject() != ((ESLocalProjectImpl) project)
.toInternalAPI().getProject()) {
currentProjectConfiguration = createModelMutatorConfigurationRandom(
modelKey, ((ESLocalProjectImpl) project).toInternalAPI()
.getProject(), minChangeSize, seed);
}
currentProjectConfiguration.setMinObjectsCount(minChangeSize);
final ESModelMutatorConfiguration mmc = currentProjectConfiguration;
new EMFStoreCommand() {
@Override
protected void doRun() {
final long time = System.currentTimeMillis();
ESDefaultModelMutator.changeModel(mmc);
System.out.println("Changed model: "
+ (System.currentTimeMillis() - time) / 1000.0 + "sec");
}
}.run(false);
}
private ESModelMutatorConfiguration createModelMutatorConfigurationRandom(
String modelKey, EObject rootObject, int minObjectsCount, long seed) {
final ESModelMutatorConfiguration config = new ESModelMutatorConfiguration(
ESModelMutatorUtil.getEPackage(modelKey), rootObject, seed);
config.setIgnoreAndLog(false);
config.setMinObjectsCount(minObjectsCount);
final List<EStructuralFeature> eStructuralFeaturesToIgnore = new ArrayList<EStructuralFeature>();
config.setEditingDomain(((ESWorkspaceImpl) runningEMFStoreRule
.connectedWorkspace()).toInternalAPI().getEditingDomain());
config.seteStructuralFeaturesToIgnore(eStructuralFeaturesToIgnore);
return config;
}
private ESPrimaryVersionSpec commitProject(final ESLocalProject project) {
return new EMFStoreCommandWithResult<ESPrimaryVersionSpec>() {
@Override
protected ESPrimaryVersionSpec doRun() {
try {
return project.commit(null);
} catch (final ESException e) {
fail();
return null;
}
}
}.run(false);
}
private void deleteLocallyIfNeeded(final ESLocalProject project) {
if (!keepLocalData) {
try {
project.delete(null);
} catch (final ESException e) {
fail();
} catch (final IOException e) {
fail();
}
}
}
private static void log(String s) {
LOGGER.info(s);
}
}