package net.thucydides.core.reports.json;
import ch.lambdaj.function.convert.Converter;
import com.google.common.base.Optional;
import com.google.common.collect.Sets;
import com.google.gson.*;
import net.thucydides.core.model.*;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Map;
import java.util.Set;
import static ch.lambdaj.Lambda.convert;
import static org.apache.commons.lang3.StringUtils.isNotEmpty;
public class TestOutcomeSerializer implements JsonSerializer<TestOutcome>,
JsonDeserializer<TestOutcome> {
private static final String TITLE_FIELD = "title";
private static final String NAME_FIELD = "name";
private static final String DESCRIPTION_FIELD = "description";
private static final String STEPS_FIELD = "steps";
private static final String SUCCESSFUL_FIELD = "successful";
private static final String FAILURES_FIELD = "failures";
private static final String ERRORS_FIELD = "errors";
private static final String SKIPPED_FIELD = "skipped";
private static final String IGNORED_FIELD = "ignored";
private static final String PENDING_FIELD = "pending";
private static final String RESULT_FIELD = "result";
private static final String TEST_STEPS = "test-steps";
private static final String USER_STORY = "user-story";
private static final String ISSUES = "issues";
private static final String VERSIONS = "versions";
private static final String TAGS = "tags";
private static final String QUALIFIER_FIELD = "qualifier";
private static final String DURATION = "duration";
private static final String TIMESTAMP = "timestamp";
private static final String BATCH_START_TIME = "batchStartTime";
private static final String SESSION_ID = "session-id";
private static final String EXAMPLES = "examples";
private static final String MANUAL = "manual";
public static final String NEW_LINE_CHAR = "\n";
public static final String ESCAPE_CHAR_FOR_NEW_LINE = "
";
private static final String TEST_CASE_FIELD = "test-case";
private static final String PROJECT_FIELD = "project";
@Override
public JsonElement serialize(TestOutcome testOutcome, Type typeOfSrc,
JsonSerializationContext context) {
JsonObject obj = new JsonObject();
obj.addProperty(TITLE_FIELD, escape(titleFrom(testOutcome)));
obj.addProperty(NAME_FIELD, nameFrom(testOutcome));
if (testOutcome.getDescription() != null) {
obj.addProperty(DESCRIPTION_FIELD, escape(descriptionFrom(testOutcome)));
}
obj.add(TEST_CASE_FIELD, context.serialize(testOutcome.getTestCase()));
if (testOutcome.getProject() != null) {
obj.add(PROJECT_FIELD, context.serialize(testOutcome.getProject()));
}
obj.addProperty(RESULT_FIELD, testOutcome.getResult().name());
if (testOutcome.getQualifier() != null && testOutcome.getQualifier().isPresent()) {
obj.addProperty(QUALIFIER_FIELD, escape(testOutcome.getQualifier().get()));
}
obj.addProperty(STEPS_FIELD, Integer.toString(testOutcome.countTestSteps()));
obj.addProperty(SUCCESSFUL_FIELD, Integer.toString(testOutcome.getSuccessCount()));
obj.addProperty(FAILURES_FIELD, Integer.toString(testOutcome.getFailureCount()));
if (testOutcome.getErrorCount() > 0) {
obj.addProperty(ERRORS_FIELD, Integer.toString(testOutcome.getErrorCount()));
}
obj.addProperty(SKIPPED_FIELD, Integer.toString(testOutcome.getSkippedCount()));
obj.addProperty(IGNORED_FIELD, Integer.toString(testOutcome.getIgnoredCount()));
obj.addProperty(PENDING_FIELD, Integer.toString(testOutcome.getPendingCount()));
obj.addProperty(DURATION, Long.toString(testOutcome.getDuration()));
obj.addProperty(TIMESTAMP, formattedTimestamp(testOutcome.getStartTime()));
if (testOutcome.has().testRunTimestamp()) {
obj.addProperty(BATCH_START_TIME, formattedTimestamp(testOutcome.getTestRunTimestamp()));
}
if (testOutcome.isManual()) {
obj.addProperty(MANUAL, "true");
}
if (isNotEmpty(testOutcome.getSessionId())) {
obj.addProperty(SESSION_ID, testOutcome.getSessionId());
}
obj.add(USER_STORY, context.serialize(testOutcome.getUserStory()));
obj.add(ISSUES, context.serialize(testOutcome.getIssues()));
obj.add(VERSIONS, context.serialize(testOutcome.getVersions()));
obj.add(TAGS, context.serialize(testOutcome.getTags()));
obj.add(TEST_STEPS, context.serialize(testOutcome.getTestSteps()));
obj.add(EXAMPLES, context.serialize(testOutcome.getDataTable()));
return obj;
}
@Override
public TestOutcome deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
JsonObject outcomeJsonObject = json.getAsJsonObject();
Class<?> testCase = context.deserialize(outcomeJsonObject.getAsJsonObject(TEST_CASE_FIELD), Class.class);
String testOutcomeName = outcomeJsonObject.get(NAME_FIELD).getAsString();
TestOutcome testOutcome = new TestOutcome(testOutcomeName,testCase);
testOutcome.setTitle(unescape(outcomeJsonObject.get(TITLE_FIELD).getAsString()));
if (outcomeJsonObject.get(PROJECT_FIELD) != null) {
String project = (outcomeJsonObject.get(PROJECT_FIELD).getAsString());
testOutcome = testOutcome.forProject(project);
}
if (outcomeJsonObject.get(DESCRIPTION_FIELD) != null) {
testOutcome.setDescription(unescape(outcomeJsonObject.get(DESCRIPTION_FIELD).getAsString()));
}
TestResult savedTestResult = TestResult.valueOf(outcomeJsonObject.get(RESULT_FIELD).getAsString());
Long duration = readDuration(outcomeJsonObject);
testOutcome.setDuration(duration);
Optional<DateTime> timestamp = readTimestamp(outcomeJsonObject);
if (timestamp.isPresent()) {
testOutcome.setStartTime(timestamp.get());
}
Optional<DateTime> batchStartTime = readBatchStartTime(outcomeJsonObject);
if (batchStartTime.isPresent()) {
testOutcome.setTestRunTimestamp(batchStartTime.get());
}
boolean isManualTest = readManualTest(outcomeJsonObject);
if (isManualTest) {
testOutcome = testOutcome.asManualTest();
}
String sessionId = readSessionId(outcomeJsonObject);
testOutcome.setSessionId(sessionId);
Story story = context.deserialize(outcomeJsonObject.getAsJsonObject(USER_STORY), Story.class);
testOutcome.setUserStory(story);
testOutcome = addQualifierIfPresent(outcomeJsonObject, testOutcome);
addIssuesIfPresent(context, outcomeJsonObject, testOutcome);
addVersionsIfPresent(context, outcomeJsonObject, testOutcome);
addTagsIfPresent(context, outcomeJsonObject, testOutcome);
JsonArray testStepsJsonArray = outcomeJsonObject.getAsJsonArray(TEST_STEPS);
for(JsonElement currentJsonElement : testStepsJsonArray ){
TestStep currentStep = context.deserialize(currentJsonElement, TestStep.class);
testOutcome.recordStep(currentStep);
}
DataTable dataTable = context.deserialize(outcomeJsonObject.getAsJsonObject(EXAMPLES), DataTable.class);
testOutcome.useExamplesFrom(dataTable);
if(testOutcome.getStepCount().equals(0)) {
testOutcome.setAnnotatedResult(savedTestResult);
}
return testOutcome;
}
private TestOutcome addQualifierIfPresent(JsonObject outcomeJsonObject, TestOutcome testOutcome) {
JsonElement qualifierField = outcomeJsonObject.get(QUALIFIER_FIELD);
if (qualifierField != null) {
testOutcome = testOutcome.withQualifier(unescape(qualifierField.getAsString()));
}
return testOutcome;
}
private void addTagsIfPresent(JsonDeserializationContext context, JsonObject outcomeJsonObject, TestOutcome testOutcome) {
Set<TestTag> tags = Sets.newHashSet(convert(context.deserialize(outcomeJsonObject.getAsJsonArray(TAGS), Set.class), toTags()));
if (tags != null) {
testOutcome.setTags(tags);
}
}
private Converter<Map, TestTag> toTags() {
return new Converter<Map, TestTag>() {
@Override
public TestTag convert(Map from) {
return TestTag.withName((String)from.get("name")).andType((String)from.get("type"));
}
};
}
private void addIssuesIfPresent(JsonDeserializationContext context, JsonObject outcomeJsonObject, TestOutcome testOutcome) {
Set<String> issues = context.deserialize(outcomeJsonObject.getAsJsonArray(ISSUES), Set.class);
if (issues != null) {
ArrayList<String> issuesAsString = new ArrayList<String>();
issuesAsString.addAll(issues);
testOutcome.addIssues(issuesAsString);
}
}
private void addVersionsIfPresent(JsonDeserializationContext context, JsonObject outcomeJsonObject, TestOutcome testOutcome) {
Set<String> versions = context.deserialize(outcomeJsonObject.getAsJsonArray(VERSIONS), Set.class);
if (versions != null) {
ArrayList<String> versionsAsString = new ArrayList<String>();
versionsAsString.addAll(versions);
testOutcome.addVersions(versionsAsString);
}
}
private String escape(String attribute) {
return StringUtils.replace(attribute, NEW_LINE_CHAR, ESCAPE_CHAR_FOR_NEW_LINE);
}
private String unescape(String attribute) {
return StringUtils.replace(attribute, ESCAPE_CHAR_FOR_NEW_LINE, NEW_LINE_CHAR);
}
private String titleFrom(final TestOutcome testOutcome) {
return testOutcome.getTitle();
}
private String nameFrom(final TestOutcome testOutcome) {
return testOutcome.getMethodName();
}
private String descriptionFrom(final TestOutcome testOutcome) {
return testOutcome.getDescription();
}
private String formattedTimestamp(DateTime startTime) {
return startTime.toString();
}
private long readDuration(JsonObject jsonObject) {
JsonElement durationElement = jsonObject.get(DURATION);
if (durationElement != null && StringUtils.isNumeric(durationElement.getAsString())) {
return Long.parseLong(jsonObject.get(DURATION).getAsString());
} else {
return 0;
}
}
private Optional<DateTime> readTimestamp(JsonObject jsonObject) {
DateTime timestamp = null;
JsonElement timestampElement = jsonObject.get(TIMESTAMP);
if (timestampElement != null) {
String timestampValue = jsonObject.get(TIMESTAMP).getAsString();
timestamp = DateTime.parse(timestampValue);
}
return Optional.fromNullable(timestamp);
}
private Optional<DateTime> readBatchStartTime(JsonObject jsonObject) {
DateTime timestamp = null;
JsonElement timestampElement = jsonObject.get(BATCH_START_TIME);
if (timestampElement != null) {
String timestampValue = jsonObject.get(BATCH_START_TIME).getAsString();
timestamp = DateTime.parse(timestampValue);
}
return Optional.fromNullable(timestamp);
}
private boolean readManualTest(JsonObject jsonObject) {
boolean isManualTest = false;
JsonElement manualTestAttribute = jsonObject.get(MANUAL);
if (manualTestAttribute != null) {
isManualTest = Boolean.valueOf(manualTestAttribute.getAsString());
}
return isManualTest;
}
private String readSessionId(JsonObject jsonObject) {
JsonElement jsonElement = jsonObject.get(SESSION_ID);
if(jsonElement != null) {
return jsonElement.getAsString();
} else {
return null;
}
}
}