Skip to content
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

Fixed deserialization of forms containing selects with default answers #814

Merged
merged 2 commits into from
Feb 3, 2025

Conversation

grzesiek2010
Copy link
Member

@grzesiek2010 grzesiek2010 commented Jan 29, 2025

Closes #getodk/collect#6571

What has been done to verify that this works as intended?

I've added an automated test and performed manual tests based on the steps described in the issue.

Why is this the best possible solution? Were any other approaches considered?

According to my findings, the issue stems from setting the instance name for tree elements in the form instance after deserialization. When reading the form from XML, everything works correctly.

According to the provided reference:
image

For elements in the primary instance, the instance name should be null. However, during deserialization, when attempting to read the reference (ref) of a select-one question located in the primary instance, the reference appears as:

instance(Internal choices)/data/select-from-secondary-instance

In this case, the instance name is mistakenly set to the ID of a child in the primary instance. According to the comment in the reference, the instance name should be null, and the reference should instead be (exactly as it appears when we read form from XML):

/data/select-from-secondary-instance

This difference in TreeReferences triggers an exception because the expected question cannot be found. I believe it worked correctly for a long time only because the equals method used for comparing references was faulty. Once that method was fixed in [this pull request](#578), the issue began to occur.

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?

It shouldn't affect users at all. It should only fix the problem with the deserialization of forms containing selects with default answers.

Do we need any specific form for testing your changes? If so, please attach one.

The form is attached to the issue.

Does this change require updates to documentation? If so, please file an issue here and include the link below.

No.

@grzesiek2010 grzesiek2010 force-pushed the instanceName branch 2 times, most recently from c5c4f61 to abcce0c Compare January 29, 2025 17:26
@grzesiek2010 grzesiek2010 marked this pull request as ready for review January 29, 2025 18:02
))
);

scenario.serializeAndDeserializeForm();
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be ideal to have a test that fails when checking the instance name or reference of the select_one question. However, the issue occurs only after deserialization, and before any validation can be performed, the deserialization process throws an exception.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is an integration test and that feels ok. I wish the test didn't end with _worksCorrectly but I also don't have a great alternate suggestion so I'm inclined to go with it!

@lognaturel
Copy link
Member

Wow, this is a blast from the past.

I couldn't immediately figure out how your description as related to default values so I debugged the test myself. I now see that when there's a default value, there's a block in attachControlsToInstanceData for going from the serialized selected option to the full internal representation of that option. This requires the reference to the question which is broken after deserialization for the reasons you describe.

I don't really understand why and how the name for the FormInstance is being set (e.g. to "Internal choices" in this case). This troubles me but I'm inclined not to dig deeper unless you think it's important, @grzesiek2010.

@grzesiek2010
Copy link
Member Author

I don't really understand why and how the name for the FormInstance is being set (e.g. to "Internal choices" in this case). This troubles me but I'm inclined not to dig deeper unless you think it's important, @grzesiek2010.

I don't fully understand why it was implemented that way, so I can't be completely confident that the fix is safe. Based on my tests and debugging, I'm pretty sure it is, but I wanted to check if you might know something about it.

There is a difference between TreeElement#instanceName:
image

and DataInstance#name:
image
(instanceName should be null for primary instances but name should take the form title in such cases)

so setting TreeElement#instanceName using DataInstance#name in #setRoot seemed to me like an obvious bug but maybe there was some rationale behind it.

@lognaturel lognaturel merged commit b8bd09b into getodk:master Feb 3, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants