/**
* Copyright (C) 2010 Hal Hildebrand. All rights reserved.
*
* This file is part of the Prime Mover Event Driven Simulation Framework.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.hellblazer.primeMover.soot;
import static com.hellblazer.primeMover.soot.util.Utils.getEntityInterfaces;
import static java.util.Arrays.asList;
import java.io.ByteArrayOutputStream;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import junit.framework.TestCase;
import soot.G;
import soot.Printer;
import soot.Scene;
import soot.SootClass;
import soot.SootMethod;
import soot.options.Options;
import testClasses.Entity2Impl;
import com.hellblazer.primeMover.runtime.EntityReference;
import com.hellblazer.primeMover.runtime.Framework;
/**
* Test the generation of the proxies which implement the actual mechanics of
* the event simulation framework.
*
* @author <a href="mailto:hal.hildebrand@gmail.com">Hal Hildebrand</a>
*
*/
public class TestEntityGenerator extends TestCase {
private static class MyMockController extends MockController {
int event1Ordinal;
int event2Ordinal;
boolean event1Posted = false;
boolean event2Posted = false;
@Override
public Object postContinuingEvent(EntityReference entity, int event,
Object... arguments) throws Throwable {
assertNotNull(arguments);
assertEquals(1, arguments.length);
event2Ordinal = event;
event2Posted = true;
return null;
}
@Override
public void postEvent(EntityReference entity, int event,
Object... arguments) {
assertNull(arguments);
event1Ordinal = event;
event1Posted = true;
}
}
EntityGenerator generator;
ArrayList<SootClass> generated;
public void testCreateClass() {
assertNotNull(generator.getEntity());
assertNotNull(generator.getEntity().getFieldByName(EntityGenerator.INITIALIZED_FIELD));
assertNotNull(generator.getEntity().getFieldByName(EntityGenerator.CONTROLLER_FIELD));
}
public void testGenerateClassInitMethod() {
generator.constructInvokeMap();
SootMethod init = generator.generateClassInitMethod();
assertNotNull(init);
}
public void testGenerateConstructors() {
SootMethod initialize = generator.generateInitializeMethod();
initialize.setDeclaringClass(generator.getBase());
assertNotNull(initialize);
List<SootMethod> constructors = generator.generateConstructors(initialize);
assertNotNull(constructors);
assertTrue(constructors.size() > 0);
}
public void testGenerateEntity() throws Throwable {
generator.generateEntity();
assertNotNull(generator.getEntity());
assertSame(generator.getEntity(),
Scene.v().getSootClass(generator.getEntity().toString()));
PrintStream out;
out = System.out;
out = new PrintStream(new ByteArrayOutputStream());
PrintWriter pw = new PrintWriter(out);
Printer.v().printTo(generator.getEntity(), pw);
pw.flush();
Scene.v().loadNecessaryClasses();
ClassLoader entityLoader = new LocalLoader(
asList(generator.getEntity()));
Class<?> entityClass = entityLoader.loadClass(generator.getEntity().toString());
assertNotNull(entityClass);
MyMockController controller = new MyMockController();
Framework.setController(controller);
EntityThroughSuperclass entity = (EntityThroughSuperclass) entityClass.newInstance();
EntityReference ref = (EntityReference) entity;
entity.event1();
assertTrue(controller.event1Posted);
entity.event2(null);
assertTrue(controller.event2Posted);
ref.__invoke(controller.event1Ordinal, new Object[0]);
assertTrue(entity.invoke1);
assertNull(entity.field2);
assertFalse(entity.invoke2);
ref.__invoke(controller.event2Ordinal,
new Object[] { new Entity2Impl() });
assertTrue(entity.invoke2);
try {
ref.__invoke(2, new Object[0]);
fail("expected NoSuchMethodError");
} catch (NoSuchMethodError e) {
// expected
}
}
public void testGenerateEvents() {
SootMethod event1 = generator.getBase().getSuperclass().getMethod("void event1()");
SootMethod event2 = generator.getBase().getSuperclass().getMethod("void event2(testClasses.Entity2)");
generator.constructInvokeMap();
SootMethod initialize = generator.generateInitializeMethod();
assertNotNull(initialize);
initialize.setDeclaringClass(generator.getBase());
SootMethod generatedEvent1 = generator.generateEvent(event1, initialize);
assertNotNull(generatedEvent1);
SootMethod generatedEvent2 = generator.generateEvent(event2, initialize);
assertNotNull(generatedEvent2);
}
public void testGenerateInitializeMethod() {
SootMethod initialize = generator.generateInitializeMethod();
assertNotNull(initialize);
}
public void testGenerateInvokeMethod() {
generator.constructInvokeMap();
SootMethod invoke = generator.generateInvokeMethod();
assertNotNull(invoke);
}
public void testGetEntityInterfaces() {
Collection<SootClass> interfaces = getEntityInterfaces(generator.getBase());
assertNotNull(interfaces);
assertEquals(3, interfaces.size());
assertTrue(interfaces.contains(Scene.v().loadClass(CompositeInterface.class.getCanonicalName(),
SootClass.SIGNATURES)));
assertTrue(interfaces.contains(Scene.v().loadClass(Interface1.class.getCanonicalName(),
SootClass.SIGNATURES)));
assertTrue(interfaces.contains(Scene.v().loadClass(Interface2.class.getCanonicalName(),
SootClass.SIGNATURES)));
}
@Override
protected void setUp() throws Exception {
super.setUp();
G.reset();
SimulationTransform.setStandardClassPath();
Options.v().set_validate(true);
Scene.v().loadBasicClasses();
generated = new ArrayList<SootClass>();
generator = new EntityGenerator(
generated,
Scene.v().loadClassAndSupport(EntityThroughSuperclass.class.getCanonicalName()),
true);
}
}