/*
* Copyright (C) 2015 Red Hat, Inc. and/or its affiliates.
*
* 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 org.jboss.errai.codegen.test;
import static org.junit.Assert.assertTrue;
import java.util.Arrays;
import org.jboss.errai.codegen.Context;
import org.jboss.errai.codegen.SnapshotMaker;
import org.jboss.errai.codegen.Statement;
import org.jboss.errai.codegen.builder.BlockBuilder;
import org.jboss.errai.codegen.builder.ClassStructureBuilder;
import org.jboss.errai.codegen.builder.impl.ClassBuilder;
import org.jboss.errai.codegen.exception.CyclicalObjectGraphException;
import org.jboss.errai.codegen.meta.MetaMethod;
import org.jboss.errai.codegen.test.AbstractCodegenTest;
import org.jboss.errai.codegen.test.model.Person;
import org.jboss.errai.codegen.test.model.PersonImpl;
import org.jboss.errai.codegen.test.model.SnapshotInterfaceWithCollections;
import org.jboss.errai.codegen.test.model.SnapshotInterfaceWithCollectionsImpl;
import org.jboss.errai.codegen.util.Stmt;
import org.junit.Assert;
import org.junit.Test;
// Note: extends from AbstractCodeGenTest to inherit overridden assertEquals(String, String) methods which are whitespace
// insensitive.
public class SnapshotMakerTest extends AbstractCodegenTest {
@Test
public void testGenerateSnapshotOfMethod() throws Exception {
Person mother = new PersonImpl("mom", 30, null);
Person child = new PersonImpl("kid", 5, mother);
Statement snapshotStmt = SnapshotMaker.makeSnapshotAsSubclass(child, Person.class, Person.class, null, Person.class);
final String generated
= snapshotStmt.generate(Context.create());
final String expectedValue =
"new org.jboss.errai.codegen.test.model.Person() {\n" +
" public int getAge() {\n" +
" return 5;\n" +
" }\n" +
" public org.jboss.errai.codegen.test.model.Person getMother() {\n" +
" return new org.jboss.errai.codegen.test.model.Person() {\n" +
" public int getAge() {\n" +
" return 30;\n" +
" }\n" +
" public org.jboss.errai.codegen.test.model.Person getMother() {\n" +
" return null;\n" +
" }\n" +
" public String getName() {\n" +
" return \"mom\";\n" +
" }\n" +
" };\n" +
" }\n" +
" public String getName() {\n" +
" return \"kid\";\n" +
" }\n" +
"}";
assertEquals(expectedValue, generated);
}
@Test
public void testNoStackOverflowOnObjectCycle() {
PersonImpl cycle1 = new PersonImpl("cycle1", 30, null);
Person cycle2 = new PersonImpl("cycle2", 5, cycle1);
cycle1.setMother(cycle2);
try {
Statement snapshotStmt = SnapshotMaker.makeSnapshotAsSubclass(cycle2, Person.class, Person.class, null, Person.class);
snapshotStmt.generate(Context.create());
Assert.fail("Instance cycle was not detected");
}
catch (CyclicalObjectGraphException e) {
assertTrue(e.getObjectsInvolvedInCycle().contains(cycle1));
assertTrue(e.getObjectsInvolvedInCycle().contains(cycle2));
}
}
/**
* This test confirms the SnapshotMaker is called automatically by the code generator's literal reification logic
* for interfaces and classes explicitly market "literalizable" in the context.
*
* @throws Exception
*/
@Test
public void testCollectionsLiteralizableInSnapshots() throws Exception {
Person jonathan = new PersonImpl("Jonathan F.", 20, new PersonImpl("Jonathans's Parent", 50, null));
Person christian = new PersonImpl("Christian S.", 20, new PersonImpl("Christians's Parent", 50, null));
Person mike = new PersonImpl("Mike B.", 0, new PersonImpl("Mike's Parent", 50, null));
SnapshotInterfaceWithCollections personCollection
= new SnapshotInterfaceWithCollectionsImpl(Arrays.asList(jonathan, christian, mike));
// create a manual context
final Context ctx = Context.create();
// tell the code generator that classes that implement these interfaces are literalizable
ctx.addLiteralizableClass(SnapshotInterfaceWithCollections.class);
ctx.addLiteralizableClass(Person.class);
// initialize a new builder with this context and try and build 'snapshot' as a code snapshot
final String generated = Stmt.create(ctx).load(personCollection).toJavaString();
final String expectedValue =
"new org.jboss.errai.codegen.test.model.SnapshotInterfaceWithCollections() {\n" +
" public java.util.List getPersons() {\n" +
" return new java.util.ArrayList() {\n" +
" {\n" +
" add(new org.jboss.errai.codegen.test.model.Person() {\n" +
" public int getAge() {\n" +
" return 20;\n" +
" }\n" +
" public org.jboss.errai.codegen.test.model.Person getMother() {\n" +
" return new org.jboss.errai.codegen.test.model.Person() {\n" +
" public int getAge() {\n" +
" return 50;\n" +
" }\n" +
" public org.jboss.errai.codegen.test.model.Person getMother() {\n" +
" return null;\n" +
" }\n" +
" public String getName() {\n" +
" return \"Jonathans's Parent\";\n" +
" }\n" +
" };\n" +
" }\n" +
" public String getName() {\n" +
" return \"Jonathan F.\";\n" +
" }\n" +
" });\n" +
" add(new org.jboss.errai.codegen.test.model.Person() {\n" +
" public int getAge() {\n" +
" return 20;\n" +
" }\n" +
" public org.jboss.errai.codegen.test.model.Person getMother() {\n" +
" return new org.jboss.errai.codegen.test.model.Person() {\n" +
" public int getAge() {\n" +
" return 50;\n" +
" }\n" +
" public org.jboss.errai.codegen.test.model.Person getMother() {\n" +
" return null;\n" +
" }\n" +
" public String getName() {\n" +
" return \"Christians's Parent\";\n" +
" }\n" +
" };\n" +
" }\n" +
" public String getName() {\n" +
" return \"Christian S.\";\n" +
" }\n" +
" });\n" +
" add(new org.jboss.errai.codegen.test.model.Person() {\n" +
" public int getAge() {\n" +
" return 0;\n" +
" }\n" +
" public org.jboss.errai.codegen.test.model.Person getMother() {\n" +
" return new org.jboss.errai.codegen.test.model.Person() {\n" +
" public int getAge() {\n" +
" return 50;\n" +
" }\n" +
" public org.jboss.errai.codegen.test.model.Person getMother() {\n" +
" return null;\n" +
" }\n" +
" public String getName() {\n" +
" return \"Mike's Parent\";\n" +
" }\n" +
" };\n" +
" }\n" +
" public String getName() {\n" +
" return \"Mike B.\";\n" +
" }\n" +
" });\n" +
" }\n" +
" };\n" +
" }\n" +
"}\n";
assertEquals(expectedValue, generated);
}
@Test
public void testMethodBodyCallback() {
Person mom = new PersonImpl("mom", 30, null);
Person kid1 = new PersonImpl("Kid 1", 3, mom);
Person kid2 = new PersonImpl("Kid 2", 4, mom);
Person kid3 = new PersonImpl("Kid 3", 5, mom);
ClassStructureBuilder<?> peopleClassBuilder = ClassBuilder.define("com.foo.People").publicScope().body();
BlockBuilder<?> method = peopleClassBuilder.publicMethod(void.class, "makePeople").body();
Statement momVar = Stmt
.declareVariable(Person.class)
.asFinal()
.named("mom")
.initializeWith(SnapshotMaker.makeSnapshotAsSubclass(mom, Person.class, Person.class, null, Person.class));
method.append(momVar);
SnapshotMaker.MethodBodyCallback mbcb = new SnapshotMaker.MethodBodyCallback() {
@Override
public Statement generateMethodBody(MetaMethod method, Object o, ClassStructureBuilder<?> containingClass) {
if (method.getName().equals("getMother")) {
return Stmt.loadVariable("mom").returnValue();
}
return null;
}
};
method.append(Stmt.declareVariable("kid1", SnapshotMaker.makeSnapshotAsSubclass(kid1, Person.class, Person.class, mbcb, Person.class)));
method.append(Stmt.declareVariable("kid2", SnapshotMaker.makeSnapshotAsSubclass(kid2, Person.class, Person.class, mbcb, Person.class)));
method.append(Stmt.declareVariable("kid3", SnapshotMaker.makeSnapshotAsSubclass(kid3, Person.class, Person.class, mbcb, Person.class)));
method.finish();
final String generated = peopleClassBuilder.toJavaString();
String expected = "package com.foo;\n" +
"\n" +
"import org.jboss.errai.codegen.test.model.Person;\n" +
"\n" +
"public class People {\n" +
" public void makePeople() {\n" +
" final Person mom = new Person() {\n" +
" public int getAge() {\n" +
" return 30;\n" +
" }\n" +
" public Person getMother() {\n" +
" return null;\n" +
" }\n" +
" public String getName() {\n" +
" return \"mom\";\n" +
" }\n" +
" };\n" +
" Person kid1 = new Person() {\n" +
" public int getAge() {\n" +
" return 3;\n" +
" }\n" +
" public Person getMother() {\n" +
" return mom;\n" +
" }\n" +
" public String getName() {\n" +
" return \"Kid 1\";\n" +
" }\n" +
" };\n" +
" Person kid2 = new Person() {\n" +
" public int getAge() {\n" +
" return 4;\n" +
" }\n" +
" public Person getMother() {\n" +
" return mom;\n" +
" }\n" +
" public String getName() {\n" +
" return \"Kid 2\";\n" +
" }\n" +
" };\n" +
" Person kid3 = new Person() {\n" +
" public int getAge() {\n" +
" return 5;\n" +
" }\n" +
" public Person getMother() {\n" +
" return mom;\n" +
" }\n" +
" public String getName() {\n" +
" return \"Kid 3\";\n" +
" }\n" +
" };\n" +
" }\n" +
"}\n";
assertEquals(expected, generated);
}
}