Skip to content

Commit

Permalink
0.3.2 Release
Browse files Browse the repository at this point in the history
Bugfixes
- Fixed a bug in which warnings were being printed even when they should not be.

Improvements
- Changed output type from unstructured text to JSON.
  • Loading branch information
joeflack4 committed Nov 11, 2018
1 parent e4d6744 commit aca5e28
Show file tree
Hide file tree
Showing 5 changed files with 580 additions and 75 deletions.
8 changes: 6 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,12 @@ dependencies {
// Be sure to update dependencies in pom.xml to match
compile group: 'org.opendatakit', name: 'opendatakit-javarosa', version: '2.11.2'
compile group: 'org.slf4j', name: 'slf4j-nop', version: '1.7.21'
extraLibs group: 'org.slf4j', name: 'slf4j-nop', version: '1.7.21'
compile group: 'com.google.code.gson', name: 'gson', version: '2.8.5'

extraLibs group: 'org.opendatakit', name: 'opendatakit-javarosa', version: '2.11.2'
extraLibs group: 'org.slf4j', name: 'slf4j-nop', version: '1.7.21'
extraLibs group: 'com.google.code.gson', name: 'gson', version: '2.8.5'

configurations.compile.extendsFrom(configurations.extraLibs)
}

Expand All @@ -49,7 +53,7 @@ jar {
// Be sure to update version in pom.xml to match
// snapshot release = x.x.x-SNAPSHOT
// production release = x.x.x
version = '0.3.1'
version = '0.3.2'
archiveName = baseName + '-' + version + '.jar'

manifest {
Expand Down
8 changes: 7 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<!-- Be sure to update version in build.gradle to match -->
<!-- snapshot release = x.x.x-SNAPSHOT -->
<!-- production release = x.x.x -->
<version>0.3.1</version>
<version>0.3.2</version>
<packaging>jar</packaging>
<name>XFormTest</name>
<description>A test suite for Xforms.</description>
Expand Down Expand Up @@ -51,6 +51,12 @@
<version>2.11.2</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.8.5</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
Expand Down
165 changes: 119 additions & 46 deletions src/org/pma2020/xform_test/XFormTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@
// @Whenever: Validate things like this: "yes, relevant: 0"
// @Whenever: Remove '-modified' xml.

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import org.javarosa.core.model.DataType;
import org.javarosa.core.model.FormDef;
import org.javarosa.core.model.FormIndex;
Expand Down Expand Up @@ -62,6 +64,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

// #to-do this might be useful for unit testing my own framework
//import org.javarosa.xform_test.FormParseInit;
Expand All @@ -73,14 +76,14 @@

// #to-do: Add documentation: https://www.youtube.com/watch?v=heh4OeB9A-c&feature=youtu.be&t=33m57s
// #to-do: Make JavaDoc
/** XForm-test is a tool for creating and running tests to ensure the quality and stability of XForms.
/** XFormTest is a tool for creating and running tests to ensure the quality and stability of XForms.
* Documentation: http://xform-test.pma2020.org/ */
@SuppressWarnings("unchecked")
public class XFormTest {
private static final List<String> validXformTestBindNames = unmodifiableList(asList(
"xform-test",
"xtest",
"test"
"xform-test",
"xtest",
"test"
));
private static final List<String> validAssertionFields = unmodifiableList(asList(
"constraint",
Expand All @@ -97,6 +100,14 @@ public class XFormTest {
"instanceID",
"instanceName"
));
private static final Map<String, Map<String, String>> unsupportedFeatures;
static {
Map<String, Map<String, String>> mapping = new HashMap<>();
Map<String, String> calculateFindReplaces = new HashMap<>();
calculateFindReplaces.put("pulldata", "1");
mapping.put("calculate", calculateFindReplaces);
unsupportedFeatures = mapping;
}
private static final Map<Integer, String> formEntryControllerEntryStatusMapping;
static {
Map<Integer, String> mapping = new HashMap<>();
Expand All @@ -120,7 +131,7 @@ public class XFormTest {
/** References: (1) XLSForm Types - http://xlsform.org/en/#question-types
* ...(2) XForm-to-JavaRosa mappings - org.javarosa.xform.parse.TypeMappings.typeMappings */
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private static final HashMap<Integer, String> javarosaValToDefMapping = new HashMap<>();
private static final Map<Integer, String> javarosaValToDefMapping = new HashMap<>();
static {
for (DataType datatype : DataType.class.getEnumConstants()) {
javarosaValToDefMapping.put(datatype.value, datatype.name());
Expand Down Expand Up @@ -171,6 +182,8 @@ public class XFormTest {
"end")));
javarosaToXlsformTypeMapping = Collections.unmodifiableMap(mapping);
}
@SuppressWarnings("FieldCanBeLocal")
private static boolean reportOnTestType = false;

// #to-do utilize this
// public static final int CONTROL_UNTYPED = ControlType.UNTYPED.value;
Expand Down Expand Up @@ -199,12 +212,10 @@ public class XFormTest {
private String thisRepeatInstanceAssertionStr = "";
private int currentRepeatTotIterationsAsserted = 0;
private int currentRepeatNum = 0;
@SuppressWarnings("MismatchedQueryAndUpdateOfCollection")
private ArrayList<String> warnings = new ArrayList<>(); // remove comment when warnings is used
private Map warnings = new HashMap();
private ArrayList<HashMap<String, String>> testCases = new ArrayList<>();
private HashMap<String, XPathConditional> calculateList;


/** Runs XFormTest spec tests on valid XForm XML files.
*
* @param args Plain text paths to valid XForm XML files. */
Expand All @@ -214,20 +225,16 @@ public static void main(String[] args) {
try {
for (String file : args)
validateFile(file);

System.out.println("\nRunning XForm Test");
System.out.println("http://xform-test.pma2020.org");

for (String file : args) {
runTestsOnFile(file);
}
for (String file : args) {
XFormTest testCase = new XFormTest();
testCase.runTestsOnFile(file);
}
} catch (AssertionSyntaxException | AssertionTypeException| MissingAssertionError |
RelevantAssertionError | XformTestIllegalArgumentException err) {
System.err.println(err.getClass().getSimpleName() + ": " + err.getMessage());
}
}


/** Runs all applicable tests on a single file.
*
* Side effects: Prints test results.
Expand All @@ -241,9 +248,8 @@ public static void main(String[] args) {
* questions, no value can be entered, so such an assertion is inherently invalid.
* @throws MissingAssertionError if no assertions on non-read only , required question prompts.
* @throws RelevantAssertionError if relevant did not evaluate as expected. */
private static void runTestsOnFile(String filePath) throws MissingAssertionError, RelevantAssertionError,
private void runTestsOnFile(String filePath) throws MissingAssertionError, RelevantAssertionError,
AssertionTypeException, AssertionSyntaxException {
System.out.println("1. Loading form:\n" + filePath);
ArrayList<String> testFieldNames = testFieldNames(filePath);
List<String> testTypesToRun = unmodifiableList(Collections.singletonList("linearAssertionTest"));
for (String testType : testTypesToRun) {
Expand All @@ -253,7 +259,7 @@ private static void runTestsOnFile(String filePath) throws MissingAssertionError
FormEntryController formEntryController = testCase.setUpAndGetController(filePath);
ArrayList<TreeElement> instanceNodes = instanceNodes(formEntryController);
ArrayList<TreeElement> formElements = instanceNodesToFormElements(instanceNodes);
HashMap<String, String> results = testCase.linearAssertionTest(formElements, testFieldName);
Map results = testCase.linearAssertionTest(formElements, testFieldName);
printResults(testType, results);
}
}
Expand Down Expand Up @@ -326,18 +332,35 @@ public void write(int b) {} // NO-OP
return fpi;
}

/** Removes pulldata(), an ODK Collect client-specific function not yet supported by XFormTest.
* @param filePath The plain text path to a valid XForm XML file.
* @return plain text path to a new XForm XML file that no longer contains 'pulldata()'.
/** Creates new file with <code>unsupportedFeatures</code> removed.
*
* Side effects: Creates a new file.
*
* #to-do: Make a map of all invalid {attr: feature}s to be removed. */
private static String removePullData(String filePath) {
* @param filePath The plain text path to a valid XForm XML file.
*
* @return map with two things: (1) plain text path to a new XForm XML file with <code>unsupportedFeatures</code>
* removed, and (2) a list of all <code>correctionsMade</code> based on <code>unsupportedFeatures</code>. */
private static HashMap handleUnsupportedFeatures(String filePath) {
HashMap results = new HashMap();
HashMap<String, List> correctionsMade = new HashMap();
results.put("correctionsMade", correctionsMade);
XmlModifier xml = new XmlModifier(filePath);
xml.modifyNodeAttributesByFindReplace("calculate", "pulldata", "1");

unsupportedFeatures.forEach((attr, features) ->
features.forEach((find, replace) -> {
boolean correctionMade = xml.modifyNodeAttributesByFindReplace(attr, find, replace);
if (correctionMade) {
if (correctionsMade.get(attr) == null || correctionsMade.get(attr).isEmpty())
correctionsMade.put(attr, new ArrayList());
correctionsMade.get(attr).add(find);
}
})
);

xml.writeToFile();
return xml.getnewFilePath();
results.put("modifiedFilePath", xml.getnewFilePath());

return results;
}

/** Initializes a FormDef object, but silences all terminal output during the process.
Expand All @@ -358,12 +381,17 @@ public void write(int b) {} // NO-OP
* @return formEntryController JavaRosa's controller class for doing form entry.
*
* Side effects:
* - formDef mutation: Initializes.
* - calculateList mutation: Initializes.
- formEntryController mutation: Initializes */
* - <code>formDef</code> mutation: Initializes.
* - <code>calculateList</code> mutation: Initializes.
* - <code>formEntryController</code> mutation: Initializes
* - warnings mutation: Adds <code>correctionsMade</code> */
private FormEntryController setUpAndGetController(String xmlFilePathStr) {
setUpMockClient();
String newXmlFilePath = removePullData(xmlFilePathStr);
Map results = handleUnsupportedFeatures(xmlFilePathStr);
String newXmlFilePath = (String) results.get("modifiedFilePath");
Map correctionsMade = (Map) results.get("correctionsMade");
if (!correctionsMade.isEmpty())
warnings.put("correctionsMade", results.get("correctionsMade"));
Path xmlFilePath = Paths.get(newXmlFilePath);
FormParseInit fpi = squelchedFormParseInit(xmlFilePath);
formDef = fpi.getFormDef();
Expand All @@ -380,15 +408,60 @@ private FormEntryController setUpAndGetController(String xmlFilePathStr) {
* @param results The test results.
*
* Side effects: Prints to the terminal. */
private static void printResults(String testType, HashMap<String, String> results) {
System.out.println("\nXForm Test '" + testType + "' result: 100% (n=" + results.get("numTestcases")
+ ") of tests passed!");
System.out.println("Test case summary: ");
System.out.println(results.get("testCases"));
if (!results.get("warnings").equals("")) {
System.out.println("\nWarnings: ");
System.out.println(results.get("warnings"));
private void printResults(String testType, Map results) {
Map resultsData = new HashMap();

if (reportOnTestType)
resultsData.put("testType", testType);

resultsData.put("success", String.valueOf(true));
resultsData.put("successMsg", "SUCCESS!\n\n" +
"100% (n=" + results.get("numTestcases") +") of tests passed!\n\n" +
"Test case summary:\n\n" +
results.get("testCases"));

resultsData.put("failures", String.valueOf(false));
resultsData.put("failuresMsg", "");

resultsData.put("errors", String.valueOf(false));
resultsData.put("errorsMsg", "");

StringBuilder warningMsg = new StringBuilder();
Map warnings = (Map) results.get("warnings");
if (!warnings.isEmpty()) {
resultsData.put("warnings", String.valueOf(!results.get("warnings").equals("{}")));
warningMsg.append("WARNINGS\n\n" +
"XformTest currently does not support some features of the XForm spec, as well as other " +
"features that are specific to clients such as ODK Collect, SurveyCTO, etc. " +
"The following is a list of attributes and unsupported features of those attributes " +
"that have been found and replaced with some other text in the form " +
"'unsupportedFeatureFound: replacedWithText'.\n\n");
Map unsupportedFoundReplaced = new HashMap();
Map correctionsMade = (Map) warnings.get("correctionsMade");
correctionsMade.forEach((attr, featuresReplaced) -> {
List<String> findReplaces = new ArrayList();
unsupportedFoundReplaced.put(attr, findReplaces);
for (String feature : (List<String>) featuresReplaced) {
//noinspection SuspiciousMethodCalls
findReplaces.add(feature + ": " + unsupportedFeatures.get(attr).get(feature));
}
});
for (String attr : (Set<String>) unsupportedFoundReplaced.keySet()) {
warningMsg.append(attr);
warningMsg.append(":\n");
for (String findReplaceText : (List<String>) unsupportedFoundReplaced.get(attr)) {
warningMsg.append(" ");
warningMsg.append(findReplaceText);
warningMsg.append("\n");
}
}
}
resultsData.put("warningsMsg", warningMsg);

// TODO: This doesn't look great in the CLI.
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String prettyJsonString = gson.toJson(resultsData);
System.out.println(prettyJsonString);
}

/** Takes a nested tree structure of TreeElement's recurses through it, eventually returning a flat list.
Expand Down Expand Up @@ -1004,7 +1077,7 @@ private void handleRelevantAssertion(TreeElement arbitraryNode, String relevant)

/** Tests assertions on a given node.
*
* @param node The TreeElement instance node which contains xform-test bind information.
* @param node The TreeElement instance node which contain XFormTest bind information.
* @param testFieldName Name of test field <bind/> element (XLSForm column).
*
* @throws AssertionSyntaxException if invalid syntax via usage of comma within values rather than exclusively as an
Expand Down Expand Up @@ -1079,7 +1152,7 @@ private boolean processNodes(ArrayList<TreeElement> arbitraryNodeset, String tes
/** Runs a XFormTest 'linear assertion test'.
*
* # Background
* XForm-test introduces a specific kind of test called the ‘linear scenario test’. A linear scenario test defined
* XFormTest introduces a specific kind of test called the ‘linear scenario test’. A linear scenario test defined
* as a set of unit tests or test assertions that are specifically designed to be executed in a linear sequence, one
* after the other. The reason it can be important to test a set of questions answers or assertions linearly is due
* to interdependencies. In forms of any non-trivial complexity, there exist many questions which are dependent on
Expand Down Expand Up @@ -1124,18 +1197,18 @@ private boolean processNodes(ArrayList<TreeElement> arbitraryNodeset, String tes
* TreeElement .setValue() && .constraint || .getConstraint()?
* #to-do: relevant assertions
* boolean relevantEval = state.isIndexRelevant(); */
private HashMap<String, String> linearAssertionTest(ArrayList<TreeElement> formElements, String testFieldName)
private Map linearAssertionTest(ArrayList<TreeElement> formElements, String testFieldName)
throws AssertionSyntaxException, AssertionTypeException, MissingAssertionError, RelevantAssertionError {
HashMap<String, String> results = new HashMap<>();
System.out.println("2. Running test: linear assertion test");
Map results = new HashMap<>();

step();
boolean success = processNodes(formElements, testFieldName);

String exitCode = success ? "0" : "1";
results.put("exitCode", exitCode);
results.put("exitCode", success ? "0" : "1");
results.put("testCases", String.valueOf(testCases));
results.put("numTestcases", String.valueOf(testCases.size()));
results.put("warnings", warnings.isEmpty() ? "" : String.valueOf(warnings));
results.put("warnings", warnings);

return results;
}
}
Loading

0 comments on commit aca5e28

Please sign in to comment.