Skip to content

Commit

Permalink
Merge pull request #614 from lognaturel/constraints
Browse files Browse the repository at this point in the history
Set instance name to null when deserializing main instance
  • Loading branch information
seadowg authored Oct 28, 2020
2 parents 5e60ac5 + 834c628 commit 3746adc
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 9 deletions.
5 changes: 5 additions & 0 deletions src/main/java/org/javarosa/xform/parse/XFormParser.java
Original file line number Diff line number Diff line change
Expand Up @@ -2178,6 +2178,11 @@ private static void loadXmlInstance(FormDef f, Document xmlInst) {
tr.add(templateRoot.getName(), TreeReference.INDEX_UNBOUND);
templateRoot.populate(savedRoot, f);

// FormInstanceParser.parseInstance is responsible for initial creation of instances. It explicitly sets the
// main instance name to null so we force this again on deserialization because some code paths rely on the main
// instance not having a name.
f.getMainInstance().setName(null);

// populated model to current form
f.getMainInstance().setRoot(templateRoot);

Expand Down
51 changes: 42 additions & 9 deletions src/test/java/org/javarosa/core/model/test/FormDefTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,8 @@

package org.javarosa.core.model.test;

import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.javarosa.core.model.FormDef;
import org.javarosa.core.test.Scenario;
import org.javarosa.form.api.FormEntryCaption;
import org.junit.Test;

import java.io.IOException;

import static org.hamcrest.Matchers.is;
import static org.javarosa.core.test.AnswerDataMatchers.stringAnswer;
import static org.javarosa.core.test.Scenario.AnswerResult.CONSTRAINT_VIOLATED;
import static org.javarosa.core.test.Scenario.AnswerResult.OK;
import static org.javarosa.core.test.Scenario.getRef;
Expand All @@ -44,6 +36,15 @@
import static org.javarosa.core.util.XFormsElement.title;
import static org.javarosa.test.utils.ResourcePathHelper.r;
import static org.junit.Assert.assertThat;

import java.io.IOException;
import org.hamcrest.MatcherAssert;
import org.hamcrest.Matchers;
import org.javarosa.core.model.FormDef;
import org.javarosa.core.test.Scenario;
import org.javarosa.core.util.XFormsElement;
import org.javarosa.form.api.FormEntryCaption;
import org.junit.Test;
/**
* See testAnswerConstraint() for an example of how to write the
* constraint unit type tests.
Expand All @@ -61,6 +62,38 @@ public void enforces_constraints_defined_in_a_field() {
assertThat(scenario.answer("13"), Matchers.is(OK));
}

@Test
public void enforcesConstraints_whenInstanceIsDeserialized() throws IOException {
XFormsElement formDef = html(
head(
title("Some form"),
model(
mainInstance(t("data id=\"some-form\"",
t("a")
)),
bind("/data/a").type("string").constraint("regex(.,'[0-9]{10}')")
)
),
body(input("/data/a"))
);

Scenario scenario = Scenario.init("Some form", formDef);

scenario.next();
Scenario.AnswerResult result = scenario.answer("00000");
assertThat(result, Matchers.is(CONSTRAINT_VIOLATED));

scenario.answer("0000000000");
scenario.next();
assertThat(scenario.getCurrentIndex().isEndOfFormIndex(), is(true));

Scenario restored = scenario.serializeAndDeserializeInstance(formDef);
restored.next();
assertThat(restored.answerOf("/data/a"), is(stringAnswer("0000000000")));
result = restored.answer("00000");
assertThat(result, Matchers.is(CONSTRAINT_VIOLATED));
}

//region Repeat relevance
@Test
public void repeatRelevanceChanges_whenDependentValuesOfRelevanceExpressionChange() throws IOException {
Expand Down
25 changes: 25 additions & 0 deletions src/test/java/org/javarosa/core/test/Scenario.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@
import static org.javarosa.xpath.expr.XPathPathExpr.INIT_CONTEXT_RELATIVE;
import static org.javarosa.xpath.expr.XPathStep.AXIS_ATTRIBUTE;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.nio.file.Path;
import java.sql.Date;
import java.time.LocalDate;
Expand Down Expand Up @@ -79,7 +81,9 @@
import org.javarosa.form.api.FormEntryController;
import org.javarosa.form.api.FormEntryModel;
import org.javarosa.form.api.FormEntryPrompt;
import org.javarosa.model.xform.XFormSerializingVisitor;
import org.javarosa.model.xform.XFormsModule;
import org.javarosa.xform.parse.XFormParser;
import org.javarosa.xpath.XPathParseTool;
import org.javarosa.xpath.expr.XPathExpression;
import org.javarosa.xpath.expr.XPathNumNegExpr;
Expand Down Expand Up @@ -125,6 +129,7 @@
public class Scenario {
private static final Logger log = LoggerFactory.getLogger(Scenario.class);
public static final FormIndex BEGINNING_OF_FORM = FormIndex.createBeginningOfFormIndex();
private Path formFile;
private final FormDef formDef;
private FormEntryController controller;
private EvaluationContext evaluationContext;
Expand Down Expand Up @@ -271,6 +276,26 @@ public Scenario serializeAndDeserializeForm() throws IOException, Deserializatio
return Scenario.from(deserializedFormDef);
}

// The fact that we need to pass in the same raw form definition that the current scenario is built around suggests
// that XFormParser.loadXmlInstance(FormDef f, Reader xmlReader) should probably be public. This is also the method that Collect
// copies because the FormDef may be built from cache meaning there won't be a Reader/Document available and because it makes
// some extra calls for search(). We pass in an XFormsElement for now until we decide on an interface that Collect can use.
public Scenario serializeAndDeserializeInstance(XFormsElement form) throws IOException {
FormInstance originalInstance = getFormDef().getMainInstance();
XFormSerializingVisitor serializer = new XFormSerializingVisitor();
byte[] formInstanceBytes = serializer.serializeInstance(originalInstance);

InputStreamReader instanceReader = new InputStreamReader(new ByteArrayInputStream(formInstanceBytes));
InputStreamReader formReader = new InputStreamReader(new ByteArrayInputStream(form.asXml().getBytes()));

XFormParser parser = new XFormParser(formReader, instanceReader);
FormDef restoredFormDef = parser.parse();

Scenario restored = Scenario.from(restoredFormDef);

return restored;
}

/**
* Returns the single expanded reference of the provided reference.
* <p>
Expand Down

0 comments on commit 3746adc

Please sign in to comment.