package jnpf.elnFormula; import jnpf.util.JsonUtil; import org.junit.Test; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.math.RoundingMode; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.fail; public class FormulaSystemExamplesTest { private final FormulaParser parser = new FormulaParser(); private final FormulaFunctionRegistry functions = new FormulaFunctionRegistry(); private final FormulaTypeChecker typeChecker = new FormulaTypeChecker(functions); private final FormulaEvaluator evaluator = new FormulaEvaluator(functions); @Test public void evaluatesSystemExamplesAndStableFailures() throws Exception { List examples = loadExamples(); assertEquals(25, examples.size()); for (Example example : examples) { try { FormulaAst.Node ast = parser.parse(example.expression); Map slots = slotTypes(example.slots); typeChecker.check(ast, slots); FormulaValue raw = evaluator.evaluate(ast, inputValues(example.inputs, slots)); FormulaValue rounded = example.roundScale == null ? raw : evaluator.round(raw, example.roundScale, RoundingMode.valueOf(example.roundMode)); if (example.errorCode != null) { fail(example.name + " expected error " + example.errorCode); } assertEquals(example.name, example.expectedRaw, raw.toPlainString()); assertEquals(example.name, example.expectedRounded == null ? example.expectedRaw : example.expectedRounded, rounded.toPlainString()); } catch (FormulaParser.ParseException ex) { assertError(example, ex.getCode()); } catch (FormulaException ex) { assertError(example, ex.getCode()); } } } @Test public void exposesStableFunctionCatalog() { assertEquals(asList("ABS", "POW", "SQRT", "MIN", "MAX", "IF", "COALESCE", "IS_BLANK", "SUM", "MEAN", "STDDEV_S", "RSD", "ROUND", "ROUND_UP", "ROUND_DOWN", "CONVERT", "LOOKUP_EXACT", "LOOKUP_LINEAR", "LN", "LOG10", "LOG", "EXP", "NTH_ROOT", "COUNT", "MEDIAN", "STDDEV_P", "VAR_S", "VAR_P", "GEOMEAN", "WEIGHTED_MEAN", "SLOPE", "INTERCEPT", "CORREL", "LINEAR_PREDICT", "ROUND_HALF_EVEN", "ROUND_SIG", "ROUND_SIG_HALF_EVEN"), functions.codes()); for (FormulaFunction function : functions.catalog()) { assertNotNull(function.getChineseName()); assertNotNull(function.getCategory()); assertNotNull(function.getSignature()); assertNotNull(function.getDescription()); assertNotNull(function.getExample()); } } private static void assertError(Example example, String actualCode) { if (example.errorCode == null) { fail(example.name + " unexpected error " + actualCode); } assertEquals(example.name, example.errorCode, actualCode); } private static Map slotTypes(Map values) { Map result = new LinkedHashMap<>(); for (Map.Entry entry : values.entrySet()) { result.put(entry.getKey(), FormulaType.valueOf(entry.getValue())); } return result; } private static Map inputValues(Map values, Map slots) { Map result = new LinkedHashMap<>(); for (Map.Entry entry : values.entrySet()) { FormulaType type = slots.get(entry.getKey()); Object value = entry.getValue(); if (value == null) { result.put(entry.getKey(), FormulaValue.nullValue()); } else if (type == FormulaType.DECIMAL) { result.put(entry.getKey(), FormulaValue.decimal(value.toString())); } else if (type == FormulaType.DECIMAL_LIST) { List items = new ArrayList<>(); for (Object item : (List) value) { items.add(FormulaValue.decimal(item.toString())); } result.put(entry.getKey(), FormulaValue.decimalList(items)); } else if (type == FormulaType.BOOLEAN) { result.put(entry.getKey(), FormulaValue.bool(Boolean.parseBoolean(value.toString()))); } else { result.put(entry.getKey(), FormulaValue.text(value.toString())); } } return result; } private static List loadExamples() throws IOException { InputStream input = FormulaSystemExamplesTest.class.getResourceAsStream( "/formula/system-examples.json"); assertNotNull(input); try { ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int read; while ((read = input.read(buffer)) >= 0) { output.write(buffer, 0, read); } return JsonUtil.getJsonToList(new String(output.toByteArray(), StandardCharsets.UTF_8), Example.class); } finally { input.close(); } } private static List asList(String... values) { List result = new ArrayList<>(); Collections.addAll(result, values); return result; } public static class Example { public String name; public String expression; public Map slots = new LinkedHashMap<>(); public Map inputs = new LinkedHashMap<>(); public Integer roundScale; public String roundMode; public String expectedRaw; public String expectedRounded; public String errorCode; } }