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

Add base64-decode #708

Merged
merged 6 commits into from
Mar 16, 2023
Merged

Add base64-decode #708

merged 6 commits into from
Mar 16, 2023

Conversation

lognaturel
Copy link
Member

@lognaturel lognaturel commented Feb 10, 2023

Discussion at https://forum.getodk.org/t/proposal-add-centroid-and-base64binary-to-string-functions/40289/5

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

Added tests.

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

Name was selected with community input. Since we're not following an existing spec, I decided not to add an encoding parameter. In practice, most things are UTF-8 these days. We can add an encoding parameter later if needed.

We had talked about throwing an exception in case of invalid input but I decided to use a blank/null value instead. The problem with throwing an exception is that the exception can manifest as a Validate error and/or be thrown at undesirable moment if the input is not complete yet. With a blank value, it may be harder to track down unexpected values but I think it's an overall better experience.

I wanted to use standard Java Base64 but that's not in Android until API 26. We already have a precedent for pulling code in from https://github.com/brsanthu/migbase64/blob/master/src/main/java/com/migcomponents/migbase64/Base64.java so I continued that.

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?

Isolated addition so all risk is to the new code which could be wrong.

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

See test.

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

getodk/xforms-spec#301

@lognaturel lognaturel marked this pull request as draft February 10, 2023 22:04
@lognaturel lognaturel marked this pull request as ready for review February 10, 2023 23:10
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Enclosed.class)
Copy link
Member Author

Choose a reason for hiding this comment

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

I wanted to have both valid and invalid input cases in the same file. This is the cleanest way I found to do that.

Copy link
Member

Choose a reason for hiding this comment

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

What about removing Parameterized and just creating a different test for each of the 5 cases? I know it's more code, but I usually find parameterized tests (and especially JUnit's implementation of them) very hard to read and reason about. They're also very hard to rework if the one of the cases is modified, so there's a caveat (different setup, more assertions etc).

Copy link
Member Author

Choose a reason for hiding this comment

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

I agree with you in some cases but I think it really depends on the shape of the tests. With this style of test where there's a standard form with different values subbed in, I find parameterized tests easier to work with. We have a lot of examples of Parameterized tests in JR.

That said, I'm ok moving to lowercase-p parameterized tests. I've done that in the latest commit. I think the important thing is that the form is only defined once. Hopefully this is a reasonable compromise for you.

Copy link
Member

Choose a reason for hiding this comment

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

I much prefer the new approach. I find using common builders/helpers a much nicer approach than an entire parametrized test, as you're still able to have a unique procedure for each case (without branching).

Copy link
Member Author

Choose a reason for hiding this comment

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

Makes sense! We can move towards that as we introduce new tests.

@lognaturel lognaturel requested a review from seadowg March 7, 2023 15:49
import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public class XPathFuncExprBase64DecodeTest {
Copy link
Member

Choose a reason for hiding this comment

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

Could this just be Base64DecodeTest? I think the fact that it's nested in a way that makes it's clear it's testing an XPath expression.

import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;

@RunWith(Enclosed.class)
Copy link
Member

Choose a reason for hiding this comment

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

What about removing Parameterized and just creating a different test for each of the 5 cases? I know it's more code, but I usually find parameterized tests (and especially JUnit's implementation of them) very hard to read and reason about. They're also very hard to rework if the one of the cases is modified, so there's a caveat (different setup, more assertions etc).

// Copied from: https://github.com/brsanthu/migbase64/blob/master/src/main/java/com/migcomponents/migbase64/Base64.java
// Irrelevant after Java8
@Override
String encode(byte[] sArr) {
Copy link
Member

Choose a reason for hiding this comment

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

Could encode just throw an Unsupported operation for now seeing as it's not being used and it's untested?

Copy link
Member Author

Choose a reason for hiding this comment

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

This is only a whitespace change to make indentation consistent with rest of project. encode was introduced and tested in #247.

Copy link
Member

Choose a reason for hiding this comment

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

Ah, sorry I got confused. I thought the whitespace change was just for the HEX stuff, but I see now that you only added decode to this file.

import org.junit.runners.Parameterized;

@RunWith(Parameterized.class)
public class EncodingDecodeTest {
Copy link
Member

Choose a reason for hiding this comment

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

It looks like this adds testing for hex, but as far as I can see, the Base 64 cases are double coverage of the testing provides by XPathFuncExprBase64DecodeTest. Would it make sense just to add a test for hex decoding instead? That could be a follow-up PR I reckon.

Copy link
Member Author

Choose a reason for hiding this comment

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

I have a pretty strong preference to test at both levels because it's a pattern that exists throughout JR. There have been cases of functions that work when tested in isolation like this but not in forms. It's helpful to have the test coverage to differentiate the two, especially when there are regressions.

In this specific case, I further like the symmetry with EncodingEncodeTest.

@@ -486,6 +487,9 @@ public Object eval(DataInstance model, EvaluationContext evalContext) {
return XPathNodeset.shuffle((XPathNodeset) argVals[0], toNumeric(argVals[1]).longValue());

throw new XPathUnhandledException("function \'randomize\' requires 1 or 2 arguments. " + args.length + " provided.");
} else if (name.equals("base64-decode")) {
assertArgsCount(name, args, 1);
Copy link
Member

Choose a reason for hiding this comment

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

It doesn't look like there's a test for this line.

@seadowg seadowg merged commit 96c37ee into getodk:master Mar 16, 2023
@lognaturel lognaturel deleted the base64 branch March 17, 2023 00:03
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.

None yet

2 participants