//
// Copyright © 2014, David Tesler (https://github.com/protobufel)
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above copyright
// notice, this list of conditions and the following disclaimer in the
// documentation and/or other materials provided with the distribution.
// * Neither the name of the <organization> nor the
// names of its contributors may be used to endorse or promote products
// derived from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
// DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
package com.github.protobufel.el;
import static com.github.protobufel.test.util.ProtoUtils.galaxyKeyword;
import static com.github.protobufel.test.util.ProtoUtils.galaxyStar;
import static com.github.protobufel.test.util.ProtoUtils.planetCountry;
import static com.github.protobufel.test.util.ProtoUtils.starKeyword;
import static com.github.protobufel.test.util.ProtoUtils.starName;
import static com.github.protobufel.test.util.ProtoUtils.starPlanet;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.containsString;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import javax.el.ELException;
import javax.el.ELProcessor;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.fictional.test.GalaxyProto.Galaxy;
import com.fictional.test.GalaxyProto.Galaxy.Star;
import com.github.protobufel.DynamicMessage;
import com.github.protobufel.ProtoInterfaces.IBuilder2;
import com.github.protobufel.test.util.ProtoUtils;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.protobuf.Descriptors.FieldDescriptor;
import com.google.protobuf.Message;
@RunWith(JUnit4.class)
public class ProtoElTest {
private static final Logger log = LoggerFactory.getLogger(ProtoElTest.class);
private ELProcessor elp;
private ELProcessor stdElp;
private ELProcessor protoElp;
private Galaxy originalMsg;
private IBuilder2 builder;
@Rule
public ExpectedException expectedException = ExpectedException.none();
@Before
public void setup() {
originalMsg = ProtoUtils.newGalaxy();
elp = new ELProcessorEx();
stdElp = new ELProcessor();
protoElp = new ProtoELProcessorEx();
builder = DynamicMessage.newBuilder(originalMsg);
}
@Test
public void testEl1() {
stdElp.defineBean("employee", new Employee("Charlie Brown"));
final String name = (String) stdElp.eval("employee.name");
log.debug("name = '{}'", name);
@SuppressWarnings("unchecked")
final List<String> keywords =
(List<String>) stdElp.eval("employee.keywords = ['one', 'two', 'three']");
log.debug("keywords = '{}'", keywords);
@SuppressWarnings("unchecked")
final List<String> keywords2 =
(List<String>) stdElp.eval("employee.keywords = ['one', 'two', 'three'];"
+ "employee.keywords.stream().filter(e->e!='two').toList()");
log.debug("keywords = '{}'", keywords2);
@SuppressWarnings("unchecked")
final List<String> keywords3 =
(List<String>) stdElp.eval("employee.keywords = ['one', 'two', 'three'];"
+ "i=0; employee.keywords.stream().filter(e->(i=i+1; i!=3)).toList()");
log.debug("keywords = '{}'", keywords3);
@SuppressWarnings("unchecked")
final List<String> keywords4 =
(List<String>) stdElp.eval("list = employee.keywords = ['one', 'two', 'three'];"
+ "list[1] = list[1].toUpperCase(); list");
log.debug("keywords = '{}'", keywords4);
@SuppressWarnings("unchecked")
final Map<String, Long> attributes =
(Map<String, Long>) stdElp
.eval("map = employee.attributes = {'one':1, 'two':2, 'three':3};"
+ "map.two = map.two * 111; map");
log.debug("attributes = '{}'", attributes);
@SuppressWarnings("unchecked")
final Map<String, Long> attributes2 =
(Map<String, Long>) stdElp
.eval("map = employee.attributes = {'one':1, 'two':2, 'three':3};"
+ "map['two'] = map['two'] * 111; map");
log.debug("attributes = '{}'", attributes2);
}
@Test
public void testElOverloadedMethod1() {
elp.defineBean("employee", new Employee("Charlie Brown"));
final String overloaded1 = (String) elp.eval("employee.overloaded1('param1', 99)");
log.debug("name = '{}'", overloaded1);
assertThat(overloaded1, is("overloaded1_string"));
}
/**
* This test shows deficiency of Standard BeanElResolver.
* <p>
* It doesn't handle overloaded methods, even in the presence of BeanInfo. One of the overloaded
* methods will be randomly chosen, and so will randomly fail! The BeanELResolverEx should never
* fail in the presence of BeanInfo!
*/
@Test
public void testStandardElOverloadedMethod1() {
try {
stdElp.defineBean("employee", new Employee("Charlie Brown"));
@SuppressWarnings("unused")
final String overloaded1 = (String) stdElp.eval("employee.overloaded1('param1', 99)");
} catch (final ELException e) {
log.debug("stdElp randomly fails on an overloaded method, this is expected!", e);
}
}
@Test
public void testGetGeneratedProto1() {
final Galaxy msg = ProtoUtils.newGalaxy();
elp.defineBean("msg", msg);
final Star expectedStar1 = msg.getStar(0);
final Star actualStar1 = (Star) elp.eval("msg.getStar(0)");
log.debug("actualStar1 = '{}'", actualStar1);
assertThat(actualStar1, is(expectedStar1));
}
@Test
public void testGetGeneratedProtoDynamicStyle1() throws ELException {
final Galaxy msg = ProtoUtils.newGalaxy();
elp.defineBean("msg", msg);
final Star expectedStar1 = msg.getStar(0);
expectedException.expect(ELException.class);
expectedException.expectMessage(containsString(FieldDescriptor.class.getName()));
final Star actualStar1 = (Star) elp.eval("msg.getField('star')[0]");
log.debug("actualStar1 = '{}'", actualStar1);
assertThat(actualStar1, is(expectedStar1));
}
@Test
public void testGetDMProto1() {
protoElp.defineBean("builder", builder);
final Message expectedStar1 = originalMsg.getStar(0);
final Message expectedDMStar1 = (Message) builder.clone().getRepeatedField(galaxyStar, 0);
assertThat("generated star = DM star", expectedStar1, is(expectedDMStar1));
final Message actualStar1 = (Message) protoElp.eval("builder.star[0].clone().build()");
log.debug("actualStar1 = '{}'", actualStar1);
assertThat("EL star = generated star", actualStar1, is(expectedStar1));
}
@Test
public void testSetDMProto1() {
protoElp.defineBean("builder", builder);
final Message expectedMessage =
builder.clone().getFieldBuilder(galaxyStar, 0).setField(starName, "Star1*").toParent()
.build();
final Message actualMessage =
(Message) protoElp.eval("builder.star[0].name = 'Star1*'; builder.build()");
log.debug("actualGalaxy = '{}'", actualMessage);
assertThat(actualMessage, is(expectedMessage));
}
@Test
public void testSetDMProto2() {
protoElp.defineBean("builder", builder);
final IBuilder2 expectedBuilder = builder.clone();
for (final IBuilder2 child : ensureList(expectedBuilder.getBuilderList(galaxyStar))) {
child.setField(starName, child.getField(starName) + "*");
}
final Message expectedMessage = expectedBuilder.build();
final Message actualMessage =
(Message) protoElp
.eval("builder.star.getBuilders().stream().forEach(b->(b.name = b.name += '*')); builder.build()");
log.debug("actualGalaxy = '{}'", actualMessage);
assertThat(actualMessage, is(expectedMessage));
}
@Test
public void testSetDMProto3() {
protoElp.defineBean("builder", builder);
protoElp.defineBean("log", log);
final IBuilder2 expectedBuilder = builder.clone();
for (final IBuilder2 star : ensureList(expectedBuilder.getBuilderList(galaxyStar))) {
for (final IBuilder2 planet : ensureList(star.getBuilderList(starPlanet))) {
if (planet.getRepeatedFieldCount(planetCountry) > 0) {
planet.removeRepeatedField(planetCountry, 0);
}
}
}
final Message expectedMessage = expectedBuilder.build();
final Message actualMessage =
(Message) protoElp.eval("builder.star.getBuilders().stream()"
+ ".flatMap(b->b.planet.getBuilders().stream()).filter(b->(b.country.size() > 0))"
+ ".forEach(b->b.country.remove(0))" + ".toList(); builder.build()");
log.debug("actualGalaxy = '{}'", actualMessage);
assertThat(actualMessage, is(expectedMessage));
}
@Test
public void testGetDMBuilderAttributeList1() {
protoElp.defineBean("builder", builder);
protoElp.defineBean("log", log);
final IBuilder2 expectedBuilder = builder.clone();
@SuppressWarnings("unchecked")
List<String> expectedList =
(List<String>) expectedBuilder.getFieldBuilder(galaxyStar, 0).getField(starKeyword);
expectedList = ImmutableList.<String>builder().addAll(expectedList).add("***1").build();
@SuppressWarnings("unchecked")
final List<String> actualList =
(List<String>) protoElp.getValue("list=builder.star[0].keyword.add('***1').getList()",
List.class);
log.debug("builder.star[0].keyword = '{}'", actualList);
assertThat(actualList, is(expectedList));
}
@Test
public void testGetDMBuilderAttributeList2() {
protoElp.defineBean("builder", builder);
protoElp.defineBean("log", log);
final IBuilder2 expectedBuilder = builder.clone();
@SuppressWarnings("unchecked")
List<String> expectedList = (List<String>) expectedBuilder.getField(galaxyKeyword);
expectedList = ImmutableList.<String>builder().addAll(expectedList).add("***1").build();
@SuppressWarnings("unchecked")
final List<String> actualList =
(List<String>) protoElp.getValue("builder.keyword.add('***1').getList()", List.class);
log.debug("builder.keyword = '{}'", actualList);
assertThat(actualList, is(expectedList));
}
@Test
public void testGetDMBuilderBuilderList1() {
protoElp.defineBean("builder", builder);
protoElp.defineBean("log", log);
final IBuilder2 expectedBuilder = builder.clone();
protoElp.defineBean("starFD", galaxyStar);
// List<IBuilder2> starList = expectedBuilder.getBuilderList(starFD);
// starList.add(expectedBuilder.newBuilderForField(starFD).setAttribute("name", "New Star2"));
@SuppressWarnings("unchecked")
final List<IBuilder2> starList =
(List<IBuilder2>) expectedBuilder.addFieldBuilder(galaxyStar)
.setField(starName, "New Star2").toParent().getBuilderList(galaxyStar);
final List<String> expectedList = Lists.newArrayList();
for (final IBuilder2 star : starList) {
expectedList.add((String) star.getField(starName));
}
@SuppressWarnings("unchecked")
final List<String> actualList =
(List<String>) protoElp.getValue(
"fbuilder=builder.star; newStar=builder.newBuilderForField(starFD); newStar.name='New Star2'; "
+ "fbuilder.add(newStar).getBuilders().stream().map(e->e.name).toList()",
List.class);
log.debug("builder.star.name = '{}'", actualList);
assertThat(actualList, is(expectedList));
}
@Test
public void testGetDMBuilderBuilderList1_newStyle() {
protoElp.defineBean("builder", builder);
protoElp.defineBean("log", log);
final IBuilder2 expectedBuilder = builder.clone();
protoElp.defineBean("starFD", galaxyStar);
// List<IBuilder2> starList = expectedBuilder.getBuilderList(starFD);
// starList.add(expectedBuilder.newBuilderForField(starFD).setAttribute("name", "New Star2"));
@SuppressWarnings("unchecked")
final List<IBuilder2> starList =
(List<IBuilder2>) expectedBuilder.addFieldBuilder(galaxyStar)
.setField(starName, "New Star2").toParent().getBuilderList(galaxyStar);
final List<String> expectedList = Lists.newArrayList();
for (final IBuilder2 star : starList) {
expectedList.add((String) star.getField(starName));
}
@SuppressWarnings("unchecked")
final List<String> actualList =
(List<String>) protoElp.getValue(
"starBuilder=builder.star; starBuilder.add().name='New Star2';"
+ "starBuilder.getList().stream().map(e->e.name).toList()", List.class);
log.debug("builder.star.name = '{}'", actualList);
assertThat(actualList, is(expectedList));
}
private <T> List<T> ensureList(final List<T> list) {
return list == null ? Collections.<T>emptyList() : list;
}
private <T> List<T> ensureUnmodifiableList(final List<T> list) {
return list == null ? Collections.<T>emptyList() : Collections.unmodifiableList(list);
}
@Test
public void testSetGeneratedProto1() {
final Galaxy.Builder builder = originalMsg.toBuilder();
elp.defineBean("builder", builder);
final Galaxy.Builder expectedBuilder = builder.clone();
expectedBuilder.getStarBuilder(0).setName("Star1*");
final Galaxy expectedGalaxy = expectedBuilder.build();
final Galaxy actualGalaxy =
(Galaxy) elp.eval("builder.getStarBuilder(0).setName('Star1*'); builder.build()");
log.debug("actualGalaxy = '{}'", actualGalaxy);
assertThat(actualGalaxy, is(expectedGalaxy));
}
}