/*******************************************************************************
* Copyright (c) 2009 itemis AG (http://www.itemis.eu) and others.
* 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
*
*******************************************************************************/
package org.eclipse.xtend.profiler;
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 java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringReader;
import java.math.BigInteger;
import java.net.URL;
import junit.framework.TestCase;
import org.easymock.IExpectationSetters;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.internal.xpand2.ast.Template;
import org.eclipse.internal.xpand2.model.XpandDefinition;
import org.eclipse.internal.xpand2.parser.XpandParseFacade;
import org.eclipse.xpand2.XpandExecutionContext;
import org.eclipse.xpand2.XpandExecutionContextImpl;
import org.eclipse.xpand2.output.FileHandle;
import org.eclipse.xpand2.output.Outlet;
import org.eclipse.xpand2.output.Output;
import org.eclipse.xpand2.output.OutputImpl;
import org.eclipse.xpand2.output.VetoStrategy;
import org.eclipse.xtend.XtendFacade;
import org.eclipse.xtend.expression.ExecutionContextImpl;
import org.eclipse.xtend.profiler.profilermodel.Item;
import org.eclipse.xtend.profiler.profilermodel.ModelFactory;
import org.eclipse.xtend.profiler.profilermodel.ProfilingResult;
import org.eclipse.xtend.typesystem.MetaModel;
import org.eclipse.xtend.typesystem.Type;
import org.eclipse.xtend.typesystem.emf.EmfMetaModel;
import org.eclipse.xtend.typesystem.emf.EmfRegistryMetaModel;
/**
* @author Heiko Behrens - Initial contribution and API
*/
public class ProfilerTest extends TestCase implements VetoStrategy {
// needed for server-side build since encoding of project has been set to ISO-8859-1
// by convention whereas server ignores this and runs on UTF-8
private static final String FORCED_ENCODING = "ISO-8859-1";
Profiler profiler = new Profiler();
Profiler.TimeProvider timeProvider = createStrictMock(Profiler.TimeProvider.class);
private String lastOutput;
private void expectTimingCalls(int calls) {
IExpectationSetters<Long> expect = expect(timeProvider.getNanoSeconds());
for(int i=1; i<=calls; i++)
expect.andReturn(s2n(i));
replay(timeProvider);
profiler.setTimeProvider(timeProvider);
}
private long s2n(int i) {
return ((long) i) * 1000000000;
}
public void testSimple() throws Exception {
expectTimingCalls(2);
profiler.setTimeProvider(timeProvider);
profiler.beginRoutine("a");
profiler.endRoutine();
verify(timeProvider);
}
private String convertURLToString(URL url) throws Exception {
StringBuilder sb = new StringBuilder();
InputStream is = url.openStream();
try {
BufferedReader reader = new BufferedReader(
new InputStreamReader(is, FORCED_ENCODING));
String line = null;
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
} finally {
is.close();
}
return sb.toString();
}
private String loadRessource(String relativeFilename, String className) throws Exception {
File caller = new File(className.replace('.', '/') + ".java");
String directory = caller.getParent();
File file = new File(directory, relativeFilename);
URL resource = this.getClass().getClassLoader().getResource(file.toString());
if(resource==null)
throw new IllegalStateException("No such file " + file);
return convertURLToString(resource);
}
private String loadRessource(String relativeFilename) throws Exception{
StackTraceElement element = new RuntimeException().getStackTrace()[1];
return loadRessource(relativeFilename, element.getClassName());
}
private String evaluateDefine(ProfilingResult profilingResult, String resName, String defName) throws Exception {
Template template = XpandParseFacade.file(getReaderForTemplate(resName), "Foo");
//OutputStringImpl out = new OutputStringImpl();
OutputImpl out = new OutputImpl();
Outlet outlet = new Outlet(".");
outlet.addVetoStrategy(this);
out.addOutlet(outlet);
XpandExecutionContext ctx = createCtx(out);
XpandDefinition def = template.getDefinitionsByName(defName)[0];
assertNotNull(def);
def.evaluate(ctx,profilingResult);
return this.lastOutput;
}
private Reader getReaderForTemplate(String resName) throws Exception {
String res = loadRessource(resName, org.eclipse.xtend.profiler.templates.Extensions.class.getName());
return new StringReader(res);
}
private XpandExecutionContext createCtx(Output out) {
final XpandExecutionContextImpl result = new XpandExecutionContextImpl(
out, null);
Item item = ModelFactory.eINSTANCE.createItem();
EPackage modelPackage = item.eClass().getEPackage();//ModelFactory.eINSTANCE.getModelPackage();
assertNotNull(modelPackage);
MetaModel mm = new EmfMetaModel(modelPackage);
result.registerMetaModel(mm);
result.getResourceManager().setFileEncoding(FORCED_ENCODING);
return result;
}
public void testGProfCycleExample() throws Exception {
// example from http://www.cs.utah.edu/dept/old/texinfo/as/gprof.html#SEC10
expectTimingCalls(16);
profiler.beginRoutine("main"); // main
profiler.beginRoutine("start"); // main->start
profiler.beginRoutine("a"); // main->start->a
profiler.beginRoutine("b"); // main->start->a->b
profiler.beginRoutine("a"); // main->start->[a->b->a]
profiler.beginRoutine("c"); // main->start->[a->b->a]->c
profiler.endRoutine(); // main->start->[a->b->a]
profiler.endRoutine(); // main->start->a->b
profiler.beginRoutine("c"); // main->start->a->b->c
profiler.endRoutine(); // main->start->a->b
profiler.endRoutine(); // main->start->a
profiler.beginRoutine("c"); // main->start->a->c
profiler.endRoutine(); // main->start->a
profiler.endRoutine(); // main->start
profiler.endRoutine(); // main
profiler.endRoutine();
verify(timeProvider);
Item itemMain = profiler.getProfilingResult().getItems().get(0);
assertEquals("main", itemMain.getName());
assertEquals(15, n2s(itemMain.getItemTime()));
assertEquals(2, n2s(itemMain.getSelfTime()));
assertEquals(15, n2s(profiler.getProfilingResult().getTime()));
assertEquals(5, profiler.getProfilingResult().getItems().size());
assertEquals(0, profiler.getProfilingResult().getCycles().size());
CycleDetector detector = new CycleDetector(profiler.getProfilingResult());
detector.detectCycles();
assertEquals(15, n2s(profiler.getProfilingResult().getTime()));
assertEquals(1, profiler.getProfilingResult().getCycles().size());
assertEqualToRessource("GProfCycleExample.txt", "GProf.xpt", "Main");
Item itemA = itemMain;
assertEquals("main", itemA.getName());
assertEquals(1, itemA.getSubroutines().size());
assertEquals(0, itemA.getInvocations().size());
assertEquals(2, n2s(itemA.getSelfTime()));
assertEquals(15, n2s(itemA.getTime()));
}
private void assertEqualToRessource(String relativeFilename, String resName, String defName)
throws Exception {
String expected = loadRessource(relativeFilename);
String actual = evaluateDefine(profiler.getProfilingResult(), resName, defName);
assertEquals(expected, actual);
}
public void testOawClassic() throws Exception {
expectTimingCalls(8);
profiler.beginRoutine("XPD Root::Root FOR Model");
profiler.beginRoutine("XPD Root::Root FOR Package");
profiler.beginRoutine("XPD Root::Root FOR Package");
profiler.beginRoutine("XPD Root::Root FOR Entity");
profiler.endRoutine();
profiler.endRoutine();
profiler.endRoutine();
profiler.endRoutine();
verify(timeProvider);
CycleDetector detector = new CycleDetector(profiler.getProfilingResult());
detector.detectCycles();
assertEqualToRessource("OawClassic.txt", "GProf.xpt", "Main");
String actual = evaluateDefine(profiler.getProfilingResult(), "Html.xpt", "Main");
assertNotNull("HTML smoke test failed", actual);
}
public void testSelfRecursion() throws Exception {
expectTimingCalls(4);
profiler.beginRoutine("a");
profiler.beginRoutine("a");
profiler.endRoutine();
profiler.endRoutine();
verify(timeProvider);
CycleDetector detector = new CycleDetector(profiler.getProfilingResult());
detector.detectCycles();
assertEqualToRessource("SelfRecursion.txt", "GProf.xpt", "Main");
}
public void testSimpleResultTime() throws Exception {
expectTimingCalls(2);
profiler.beginRoutine("a");
profiler.endRoutine();
verify(timeProvider);
CycleDetector detector = new CycleDetector(profiler.getProfilingResult());
detector.detectCycles();
assertEquals(1, n2s(profiler.getProfilingResult().getTime()));
}
public void testRecursionResultTime() throws Exception {
expectTimingCalls(4);
profiler.beginRoutine("a");
profiler.beginRoutine("a");
profiler.endRoutine();
profiler.endRoutine();
verify(timeProvider);
CycleDetector detector = new CycleDetector(profiler.getProfilingResult());
detector.detectCycles();
assertEquals(3, n2s(profiler.getProfilingResult().getTime()));
}
private int n2s(long time) {
return (int)(time / 1000000000);
}
public boolean hasVeto(FileHandle handle) {
this.lastOutput = handle.getBuffer().toString();
return true;
}
public void testProfilerInUnitTest() throws Exception {
// manage execution context manually and wire profiler component
ExecutionContextImpl ctx = new ExecutionContextImpl();
ProfilerComponent profComponent = new ProfilerComponent();
profComponent.prepareProfiler();
ctx.setVetoableCallBack(profComponent);
// use facade to execute template/extension
XtendFacade xtdf = XtendFacade.create(ctx, "org::eclipse::xtend::profiler::SimpleXtendFile");
Object result = xtdf.call("plusOne", 5);
assertEquals(BigInteger.valueOf(6), result);
// finalizing profiling output
profComponent.finalizeProfiler();
// work with profiling results, e.g. create report
String report = evaluateDefine(profComponent.getProfilingResult(), "GProf.xpt", "Main");
assertTrue(report.contains("XTD org::eclipse::xtend::profiler::SimpleXtendFile::plusOne(Integer) [1]"));
}
public void testProfilerPackageRegistered() throws Exception {
// invoke static initializer
assertNotNull(new ProfilerComponent());
EmfRegistryMetaModel mm = new EmfRegistryMetaModel();
mm.setTypeSystem(null);
Type type = mm.getTypeForName("profilermodel::ProfilingResult");
assertNotNull(type);
}
}