package org.trimou.spec;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.trimou.engine.MustacheEngine;
import org.trimou.engine.MustacheEngineBuilder;
import org.trimou.engine.config.EngineConfigurationKey;
import org.trimou.engine.locator.MapTemplateLocator;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
public final class SpecUtils {
private static final Logger LOGGER = LoggerFactory
.getLogger(SpecUtils.class);
/**
* Execute all the spec tests in the given JSON file.
*
* @param filename
* @param specVersion
* @throws IOException
*/
static void executeTests(String filename, String specVersion)
throws IOException {
executeTests(filename, specVersion, null);
}
static void executeTests(String filename, String specVersion,
String singleTest) throws IOException {
List<Definition> definitions = parseDefinitions(getSpecFile(filename,
specVersion));
if (!definitions.isEmpty()) {
int idx = 0;
int failures = 0;
for (Definition definition : definitions) {
if (singleTest != null
&& !singleTest.equals(definition.getName())) {
continue;
}
// Mock partials
MapTemplateLocator mockTemplateLocator = new MapTemplateLocator(
definition.getPartials());
MustacheEngine factory = MustacheEngineBuilder
.newBuilder()
.addTemplateLocator(mockTemplateLocator)
.setProperty(
EngineConfigurationKey.HANDLEBARS_SUPPORT_ENABLED,
false).build();
idx++;
try {
assertEquals(
definition.getExpected(),
factory.compileMustache(definition.getName(),
definition.getTemplate()).render(
definition.getData()));
} catch (Exception e) {
failures++;
LOGGER.error("{} {}: {} - {}", idx, definition.getName(),
e.getClass(), e.getMessage());
} catch (Error e) {
failures++;
LOGGER.error("{} {}: {} - {}", idx, definition.getName(),
e.getClass(), e.getMessage());
}
}
// Use warn log level so that this message is visible during
// ordinary build (warn is the default log level)
LOGGER.warn("Spec tests finished [filename: {}, tests: {}, failures: {}]", filename, definitions.size(),
failures);
if (failures > 0) {
fail(String.format("Spec tests failures: %s", failures));
}
}
}
static Reader getSpecFile(String filename, String specVersion) {
return new InputStreamReader(
SpecUtils.class.getResourceAsStream("/spec/" + specVersion
+ "/" + filename));
}
static List<Definition> parseDefinitions(Reader reader) throws IOException {
List<Definition> definitions = new ArrayList<>();
JsonParser parser = new JsonParser();
JsonElement spec = parser.parse(reader);
reader.close();
JsonArray tests = spec.getAsJsonObject().get("tests").getAsJsonArray();
for (JsonElement test : tests) {
JsonObject testObject = test.getAsJsonObject();
Definition definition = new Definition();
definition.setName(testObject.get("name").getAsString());
definition.setDesc(testObject.get("desc").getAsString());
definition.setTemplate(testObject.get("template").getAsString());
definition.setExpected(testObject.get("expected").getAsString());
Map<String, Object> data = new HashMap<>();
JsonObject dataObject = testObject.get("data").getAsJsonObject();
for (Entry<String, JsonElement> property : dataObject.entrySet()) {
if (!property.getKey().equals("lambda")) {
data.put(property.getKey(),
getJsonElementValue(property.getValue()));
}
}
if (Lambdas.testMap.containsKey(definition.getName())) {
data.put("lambda", Lambdas.testMap.get(definition.getName()));
}
definition.setData(data);
if (testObject.has("partials")) {
JsonObject partialsObject = testObject.get("partials")
.getAsJsonObject();
Map<String, String> partials = new HashMap<>();
for (Entry<String, JsonElement> entry : partialsObject
.entrySet()) {
partials.put(entry.getKey(), entry.getValue().getAsString());
}
definition.setPartials(partials);
}
definitions.add(definition);
}
return definitions;
}
private static Object getJsonElementValue(JsonElement element) {
if (element.isJsonPrimitive()) {
return getJsonPrimitiveElementValue(element);
} else if (element.isJsonArray()) {
return getJsonArrayElementValue(element);
} else if (element.isJsonObject()) {
Map<String, Object> objectData = new HashMap<>();
for (Entry<String, JsonElement> objectProperty : element
.getAsJsonObject().entrySet()) {
objectData.put(objectProperty.getKey(),
getJsonElementValue(objectProperty.getValue()));
}
return objectData;
} else if (element.isJsonNull()) {
return null;
}
throw new IllegalStateException("Unsupported JSON element");
}
private static Object getJsonPrimitiveElementValue(JsonElement element) {
JsonPrimitive primitive = element.getAsJsonPrimitive();
if (primitive.isBoolean()) {
return primitive.getAsBoolean();
} else if (primitive.isString()) {
return primitive.getAsString();
} else if (primitive.isNumber()) {
return primitive.getAsNumber();
} else {
throw new IllegalStateException("Unsupported primitive type");
}
}
private static Object getJsonArrayElementValue(JsonElement element) {
JsonArray array = element.getAsJsonArray();
List<Object> values = new ArrayList<>(array.size());
for (JsonElement jsonElement : array) {
values.add(getJsonElementValue(jsonElement));
}
return values;
}
}