/*
* Copyright 2016 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.drools.testcoverage.functional;
import org.assertj.core.api.Assertions;
import org.drools.decisiontable.ExternalSpreadsheetCompiler;
import org.drools.template.DataProviderCompiler;
import org.drools.template.ObjectDataCompiler;
import org.drools.template.objects.ArrayDataProvider;
import org.drools.testcoverage.common.util.KieBaseUtil;
import org.drools.testcoverage.common.util.TestConstants;
import org.junit.Test;
import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.definition.KiePackage;
import org.kie.api.io.Resource;
import org.kie.api.runtime.KieSession;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.io.StringReader;
import java.util.*;
/**
* Tests templates - providers, generating rules, performance.
*/
public class TemplatesTest {
private static final Logger LOGGER = LoggerFactory.getLogger(TemplatesTest.class);
private static final StringBuffer EXPECTED_RULES = new StringBuffer();
static {
final String head = "package " + TestConstants.PACKAGE_FUNCTIONAL + ";\n"
+ "import " + TemplatesTest.class.getCanonicalName() + ".Vegetable;\n"
+ "import " + TemplatesTest.class.getCanonicalName() + ".Taste;\n"
+ "global java.util.List list;\n\n";
final String rule2_when = "rule \"is appropriate 2\"\n"
+ "\twhen\n"
+ "\t\tVegetable( $name : name == \"carrot\", $field : weight >= 0 && <= 1000, $price : price <= 2, "
+ "$taste : taste == Taste.HORRIBLE )\n";
final String rule2_then = "\tthen\n\t\tlist.add( $name );\nend\n\n";
final String rule1_when = "rule \"is appropriate 1\"\n"
+ "\twhen\n"
+ "\t\tVegetable( $name : name == \"cucumber\", $field : length >= 20 && <= 40, $price : price <= 15, "
+ "$taste : taste == Taste.EXCELENT )\n";
final String rule1_then = "\tthen\n\t\tlist.add( $name );\nend\n\n";
final String rule0_when = "rule \"is appropriate 0\"\n"
+ "\twhen\n"
+ "\t\tVegetable( $name : name == \"tomato\", $field : weight >= 200 && <= 1000, $price : price <= 6, "
+ "$taste : taste == Taste.GOOD || == Taste.EXCELENT )\n";
final String rule0_then = "\tthen\n\t\tlist.add( $name );\nend\n\n";
EXPECTED_RULES.append(head);
EXPECTED_RULES.append(rule2_when).append(rule2_then);
EXPECTED_RULES.append(rule1_when).append(rule1_then);
EXPECTED_RULES.append(rule0_when).append(rule0_then);
}
@Test
public void loadingFromDLRObjsCorrectnessCheck() throws IOException {
final KieServices kieServices = KieServices.Factory.get();
final Collection<ParamSet> cfl = new ArrayList<ParamSet>();
cfl.add(new ParamSet("tomato", "weight", 200, 1000, 6, EnumSet.of(Taste.GOOD, Taste.EXCELENT)));
cfl.add(new ParamSet("cucumber", "length", 20, 40, 15, EnumSet.of(Taste.EXCELENT)));
cfl.add(new ParamSet("carrot", "weight", 0, 1000, 2, EnumSet.of(Taste.HORRIBLE)));
final ObjectDataCompiler converter = new ObjectDataCompiler();
final String drl = converter.compile(cfl, kieServices.getResources().newClassPathResource("template_1.drl", getClass())
.getInputStream());
// prints rules generated from template
LOGGER.debug(drl);
assertEqualsIgnoreWhitespace(EXPECTED_RULES.toString(), drl);
testCorrectnessCheck(drl);
}
@Test
public void loadingFromDLRMapsCorrectnessCheck() throws IOException {
final KieServices kieServices = KieServices.Factory.get();
final ObjectDataCompiler converter = new ObjectDataCompiler();
final String drl = converter.compile(
getMapsParam(),
kieServices.getResources().newClassPathResource("template_1.drl", getClass()).getInputStream());
// prints rules generated from template
LOGGER.debug(drl);
assertEqualsIgnoreWhitespace(EXPECTED_RULES.toString(), drl);
testCorrectnessCheck(drl);
}
@Test
public void loadingFromDLRArrayCorrectnessCheck() throws Exception {
final String[][] rows = new String[3][6];
rows[0] = new String[] { "tomato", "weight", "200", "1000", "6", "== Taste.GOOD || == Taste.EXCELENT" };
rows[1] = new String[] { "cucumber", "length", "20", "40", "15", "== Taste.EXCELENT" };
rows[2] = new String[] { "carrot", "weight", "0", "1000", "2", "== Taste.HORRIBLE" };
final ArrayDataProvider adp = new ArrayDataProvider(rows);
final DataProviderCompiler converter = new DataProviderCompiler();
final String drl = converter.compile(
adp,
KieServices.Factory.get().getResources().newClassPathResource("template_1.drl", getClass()).getInputStream());
// prints rules generated from template
LOGGER.debug(drl);
assertEqualsIgnoreWhitespace(EXPECTED_RULES.toString(), drl);
testCorrectnessCheck(drl);
}
@Test
public void loadingFromDLRSpreadsheetCorrectnessCheck() throws Exception {
final ExternalSpreadsheetCompiler converter = new ExternalSpreadsheetCompiler();
final KieServices kieServices = KieServices.Factory.get();
// the data we are interested in starts at row 1, column 1 (e.g. A1)
final String drl = converter.compile(
kieServices.getResources().newClassPathResource("template1_spreadsheet.xls", getClass()).getInputStream(),
kieServices.getResources().newClassPathResource("template_1.drl", getClass()).getInputStream(), 1, 1);
// prints rules generated from template
LOGGER.debug(drl);
assertEqualsIgnoreWhitespace(EXPECTED_RULES.toString(), drl);
testCorrectnessCheck(drl);
}
@Test
public void OneRuleManyRows() throws IOException {
final KieServices kieServices = KieServices.Factory.get();
final Collection<ParamSet> cfl = new ArrayList<ParamSet>();
cfl.add(new ParamSet("tomato", "weight", 200, 1000, 6, EnumSet.of(Taste.GOOD, Taste.EXCELENT)));
final ObjectDataCompiler converter = new ObjectDataCompiler();
final String drl = converter.compile(
cfl,
kieServices.getResources().newClassPathResource("template_1.drl", getClass()).getInputStream());
// prints rules generated from template
LOGGER.debug(drl);
testManyRows(drl, 0, 1);
}
@Test
public void TenRulesManyRows() throws IOException {
final KieServices kieServices = KieServices.Factory.get();
final ObjectDataCompiler converter = new ObjectDataCompiler();
final String drl = converter.compile(
generateParamSetCollection(1),
kieServices.getResources().newClassPathResource("template_1.drl", getClass()).getInputStream());
// prints rules generated from template
LOGGER.debug(drl);
testManyRows(drl, 500, 10);
}
@Test
public void OneTemplateManyRules() throws IOException {
final KieServices kieServices = KieServices.Factory.get();
final ObjectDataCompiler converter = new ObjectDataCompiler();
final String drl = converter.compile(
generateParamSetCollection(50),
kieServices.getResources().newClassPathResource("template_1.drl", getClass())
.getInputStream());
// prints rules generated from template
LOGGER.debug(drl);
testManyRules(drl, 500);
}
@Test
public void TenTemplatesManyRules() throws IOException {
final KieServices kieServices = KieServices.Factory.get();
final ObjectDataCompiler converter = new ObjectDataCompiler();
final String drl = converter.compile(
generateParamSetCollection(50),
kieServices.getResources().newClassPathResource("template_2.drl", getClass()).getInputStream());
// prints rules generated from template
LOGGER.debug(drl);
testManyRules(drl, 5000);
}
private void testCorrectnessCheck(final String drl) {
final Resource drlResource = KieServices.Factory.get().getResources().newReaderResource(new StringReader(drl));
drlResource.setTargetPath(TestConstants.DRL_TEST_TARGET_PATH);
final KieBase kbase = KieBaseUtil.getKieBaseFromResources(true, drlResource);
final KieSession session = kbase.newKieSession();
final List<String> list = new ArrayList<String>();
session.setGlobal("list", list);
session.insert(new Vegetable("tomato", 350, 8, 3, Taste.GOOD));
session.insert(new Vegetable("tomato", 150, 8, 3, Taste.BAD));
session.insert(new Vegetable("tomato", 350, 8, 7, Taste.GOOD));
session.insert(new Vegetable("tomato", 1000, 8, 6, Taste.EXCELENT));
session.insert(new Vegetable("cucumber", 1500, 19, 5, Taste.EXCELENT));
session.insert(new Vegetable("cucumber", 1500, 21, 5, Taste.EXCELENT));
session.insert(new Vegetable("carrot", 1000, 8, 6, Taste.EXCELENT));
session.insert(new Vegetable("carrot", 200, 8, 1, Taste.HORRIBLE));
session.insert(new Vegetable("onion", 500, 7, 4, Taste.EXCELENT));
session.fireAllRules();
// check of size of satisfying items
Assertions.assertThat(list.size()).isEqualTo(4);
final Collection<KiePackage> pkgs = kbase.getKiePackages();
Assertions.assertThat(pkgs.size()).isEqualTo(1);
final KiePackage pkg = pkgs.iterator().next();
// check of generated rules size from template
Assertions.assertThat(pkg.getRules().size()).isEqualTo(3);
}
private void testManyRows(final String drl, final int expectedResultListSize, final int expectedRulesCount) {
final Resource drlResource = KieServices.Factory.get().getResources().newReaderResource(new StringReader(drl));
drlResource.setTargetPath(TestConstants.DRL_TEST_TARGET_PATH);
final KieBase kbase = KieBaseUtil.getKieBaseFromResources(true, drlResource);
final KieSession session = kbase.newKieSession();
final List<String> list = new ArrayList<String>();
session.setGlobal("list", list);
for (int i = 0; i < 500; i++) {
session.insert(new Vegetable("tomato", 350, 8, 3, Taste.BAD));
}
session.fireAllRules();
// check of size of satisfying items
Assertions.assertThat(list.size()).isEqualTo(expectedResultListSize);
final Collection<KiePackage> pkgs = kbase.getKiePackages();
Assertions.assertThat(pkgs.size()).isEqualTo(1);
final KiePackage pkg = pkgs.iterator().next();
// check of generated rules size from template
Assertions.assertThat(pkg.getRules().size()).isEqualTo(expectedRulesCount);
}
private void testManyRules(final String drl, final int expectedRulesCount) {
final Resource drlResource = KieServices.Factory.get().getResources().newReaderResource(new StringReader(drl));
drlResource.setTargetPath(TestConstants.DRL_TEST_TARGET_PATH);
final KieBase kbase = KieBaseUtil.getKieBaseFromResources(true, drlResource);
Collection<KiePackage> pkgs = kbase.getKiePackages();
Assertions.assertThat(pkgs.size()).isEqualTo(1);
KiePackage pkg = pkgs.iterator().next();
// check of generated rules size from template
Assertions.assertThat(pkg.getRules().size()).isEqualTo(expectedRulesCount);
}
private Collection<Map<String, Object>> getMapsParam() {
final Collection<Map<String, Object>> maps = new ArrayList<Map<String, Object>>();
final Map<String, Object> mapTomato = new HashMap<String, Object>();
mapTomato.put("name", "tomato");
mapTomato.put("field", "weight");
mapTomato.put("fieldLower", 200);
mapTomato.put("fieldUpper", 1000);
mapTomato.put("price", 6);
mapTomato.put("tastes", "== Taste.GOOD || == Taste.EXCELENT");
maps.add(mapTomato);
final Map<String, Object> mapCucumber = new HashMap<String, Object>();
mapCucumber.put("name", "cucumber");
mapCucumber.put("field", "length");
mapCucumber.put("fieldLower", 20);
mapCucumber.put("fieldUpper", 40);
mapCucumber.put("price", 15);
mapCucumber.put("tastes", "== Taste.EXCELENT");
maps.add(mapCucumber);
final Map<String, Object> mapCarrot = new HashMap<String, Object>();
mapCarrot.put("name", "carrot");
mapCarrot.put("field", "weight");
mapCarrot.put("fieldLower", 0);
mapCarrot.put("fieldUpper", 1000);
mapCarrot.put("price", 2);
mapCarrot.put("tastes", "== Taste.HORRIBLE");
maps.add(mapCarrot);
return maps;
}
private Collection<ParamSet> generateParamSetCollection(final int loops) {
final Collection<ParamSet> result = new ArrayList<>();
for (int i = 0; i < loops; i++) {
result.add(new ParamSet("tomato", "weight", 200, (1000 + i), 6, EnumSet.of(Taste.GOOD, Taste.EXCELENT)));
result.add(new ParamSet("tomato", "weight", 100, (500 + i), 6, EnumSet.of(Taste.BAD)));
result.add(new ParamSet("tomato", "length", 5, (10 + i), 6, EnumSet.of(Taste.AVERAGE)));
result.add(new ParamSet("tomato", "weight", 300, (4000 + i), 6, EnumSet.of(Taste.GOOD, Taste.EXCELENT)));
result.add(new ParamSet("tomato", "weight", 200, (6000 + i), 6, EnumSet.of(Taste.EXCELENT)));
result.add(new ParamSet("tomato", "length", 2, (4 + i), 6, EnumSet.of(Taste.AVERAGE, Taste.GOOD)));
result.add(new ParamSet("tomato", "weight", 100, (1000 + i), 6, EnumSet.of(Taste.GOOD)));
result.add(new ParamSet("tomato", "weight", 500, (1000 + i), 6, EnumSet.of(Taste.EXCELENT)));
result.add(new ParamSet("tomato", "length", 4, (15 + i), 6, EnumSet.of(Taste.AVERAGE, Taste.EXCELENT)));
result.add(new ParamSet("tomato", "weight", 200, (1000 + i), 6, EnumSet.of(Taste.GOOD, Taste.EXCELENT)));
}
return result;
}
public static class ParamSet {
private String name;
private String field;
private int fieldLower;
private int fieldUpper;
private int price;
private EnumSet<Taste> tasteSet;
public ParamSet(String name, String field, int fieldLower, int fieldUpper, int price, EnumSet<Taste> tasteSet) {
this.name = name;
this.field = field;
this.fieldLower = fieldLower;
this.fieldUpper = fieldUpper;
this.tasteSet = tasteSet;
this.price = price;
}
public String getName() {
return name;
}
public String getField() {
return field;
}
public int getFieldLower() {
return fieldLower;
}
public int getFieldUpper() {
return fieldUpper;
}
public int getPrice() {
return price;
}
public String getTastes() {
StringBuilder sb = new StringBuilder();
String conn = "";
for (Taste t : tasteSet) {
sb.append(conn).append(" == Taste.").append(t);
conn = " ||";
}
return sb.toString();
}
}
public class Vegetable {
private String name;
private int weight;
private int length;
private int price;
private Taste taste;
public Vegetable(String name, int weight, int length, int price, Taste taste) {
this.name = name;
this.weight = weight;
this.length = length;
this.taste = taste;
this.price = price;
}
public String getName() {
return this.name;
}
public int getWeight() {
return this.weight;
}
public int getPrice() {
return this.price;
}
public int getLength() {
return this.length;
}
public Taste getTaste() {
return this.taste;
}
}
public enum Taste {
HORRIBLE, BAD, AVERAGE, GOOD, EXCELENT;
}
private static void assertEqualsIgnoreWhitespace(final String expected, final String actual) {
final String cleanExpected = expected.replaceAll("\\s+", "");
final String cleanActual = actual.replaceAll("\\s+", "");
// System.out.println(cleanExpected);
// System.out.println(cleanActual);
Assertions.assertThat(cleanExpected).isEqualTo(cleanActual);
}
}