-
Notifications
You must be signed in to change notification settings - Fork 109
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Consider instances in TreeReference comparison and set null instanceName for main instance on deserialization #578
Conversation
Codecov Report
@@ Coverage Diff @@
## master #578 +/- ##
============================================
+ Coverage 54.55% 54.65% +0.10%
- Complexity 3221 3228 +7
============================================
Files 244 243 -1
Lines 13345 13347 +2
Branches 2565 2567 +2
============================================
+ Hits 7280 7295 +15
+ Misses 5256 5244 -12
+ Partials 809 808 -1
Continue to review full report at Codecov.
|
/** The integer Id of the model */ | ||
private int recordid = -1; | ||
|
||
/** The name for this data model */ | ||
/** | ||
* A unique name for this instance. The value of the id attribute in the case of a secondary instance or the title |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's strange to describe how this field is used in this comment. However, I couldn't find a better place for it and it seemed valuable to me for making sense of debug contents. I can remove it if others disagree, though.
@@ -100,6 +100,10 @@ | |||
private String namespace; | |||
private String namespacePrefix; | |||
|
|||
/** | |||
* The name of the instance that this node is in. The value of the id attribute in the case of a secondary |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Again, maybe this information shouldn't be here.
src/test/java/org/javarosa/core/model/instance/TreeReferenceSerializationTest.java
Outdated
Show resolved
Hide resolved
Couple of comments and a merge conflict to fix here! |
This test was the only one that failed when I changed the Scenario class to always work off of a deserialized form definition. It turned out to have a misunderstanding about when choice lists are evaluated. The test now better matches client behavior and is consistent pre and post serialization.
Closes #449
This is pretty high-risk and would be good to do an interactive review on.
What has been done to verify that this works as intended?
Thought really hard about what was going on and tried to write as specific of tests as possible.
Why is this the best possible solution? Were any other approaches considered?
The change to
TreeReference
is the same as in #450 with the addition of an implementation forhashCode
. It also avoids using a customObjects
class now that Collect has aminSdkVersion
of 21. I feel good about this change and don't really see alternatives.This unmasks some inconsistencies in how references in form definitions are resolved by JavaRosa and in how the primary instance was represented before and after serialization. The main symptom when we tried to release this previously is that constraints were broken - #475. The reason for this is that before serialization, the
instanceName
of the primary instance root node was null and after deserialization, it was populated. In some code paths, this led to references to primary instance nodes with an explicit instance (e.g.instance("My cool form")/data/a
instead of/data/a
). This didn't matter before because instances were ignored when doing reference comparison. Constraints were broken because in the evaluation process there ended up being a comparison betweeninstance("My cool form")/data/a
and/data/a
that used to pass but now fails. This comparison is made inXPathPathExpr.getRefValue
to identify whether the latest value entered by the user should be used.Even though it's been working forever, it's not right that the primary instance's
instanceName
would be different before and after serialization. I decided to explicitly set it on deserialization. This is the counterpart toFormInstanceParser.buildInstanceStructure
which creates the primary instance initially and setsinstanceName
tonull
. I think that in an ideal world there wouldn't be this kind of special case for the primary instance name and some other mechanism would be used to identify that an instance is primary. However, that would require a lot of high-risk refactoring throughout the codebase.How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?
The goal is for this to only unlock new behavior. However' it's high risk because as we saw with #450, it does touch anything having to do with
TreeReference
comparison or hashing. The places whereTreeReference
s are compared are:EvaluationContext.rescope
FormDef.getElementsFromReferences
FormDef.findQuestionByRef
FormInstanceParser.verifyRepeatMemberBindings
-- true case never hitTreeReference.intersect
-- true case never hitTriggerableDag.deleteRepeatGroup
XPathPathExpr.getRefValue
XPathPathExpr.pivot
-- not tested at allXPathReference.equals
-- false case never hitOne nice thing about
Scenario
that I realized writing up this list is that because it gives us a single entry point into tests, we can do things like immediately serialize all form defs onScenario.init
. I did this and ran the tests and one failed inChoiceNameTest
. I initially thought it could be a problem with reference comparison but I'm pretty sure it's just that the test wasn't actually mimicking client behavior.The
jr:choice-name
function handler (seeFormDef.initEvalContext
) makes a call to populate dynamic choices, but only if they haven't already been populated. In the case of an unserialized form definition, the choices were populated on form initialization as an empty list. And then when thejr:choice-name
call was evaluated, the choices weren't repopulated. After deserialization, though, the initialization step didn't happen. That meant that when thejr:choice-name
call was evaluated, the choices were populated.But Collect will explicitly call
populateDynamicChoices
on displaying the choice list for cities. So I updated the test to reflect this and now the behavior is the same with and without serialization.Do we need any specific form for testing your changes? If so, please attach one.
See form at getodk/collect#3316.
Does this change require updates to documentation? If so, please file an issue here and include the link below.
No.