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

Harden test-cases and align with other implementations #84

Merged
merged 2 commits into from
Feb 19, 2017

Conversation

ZauberNerd
Copy link
Contributor

@ZauberNerd ZauberNerd commented Feb 12, 2017

This commit refactors and adds more tests to the parse() implementation
to align with similar libraries's behaviour, e.g.:

When running the new test suite against the old lib/parse.js implementation
a few tests were failing:

31 passing (177ms)
10 failing

1) parse() real should throw an Error when parsing an empty real:
    AssertionError: Missing expected exception..

2) parse() string should parse a self closing string:
    AssertionError: null === ''

3) parse() string should parse an empty string:
    AssertionError: null === ''

4) parse() string should parse a string with comments:
    AssertionError: 'a comment  string' === 'a string'

5) parse() array should parse empty elements inside an array:
    AssertionError: [ false ] deepEqual [ '', false ]

6) parse() dict should throw if key is missing:
    AssertionError: Missing expected exception..

7) parse() dict should throw if two keys follow each other:
    AssertionError: Missing expected exception..

8) parse() dict should throw if value is missing:
    AssertionError: Missing expected exception..

9) parse() dict should parse an empty key:
    AssertionError: {} deepEqual { '': '1' }

10) parse() dict should parse an empty value:
    AssertionError: { a: null } deepEqual { a: '' }

When executing the new implementation of lib/parse.js agains the old
tests the following tests were failing:

25 passing (143ms)
3 failing

1) plist parse() should parse an empty <key/> in a dictionary:
    AssertionError: false == true

2) plist parse() should parse an empty <key></key> in a dictionary:
    AssertionError: false == true

3) plist parse() should parse an empty <key></key> and <string></string> in dictionary with more data:
    AssertionError: { '': '', UIRequiredDeviceCapabilities: [ 'armv7' ] } deepEqual { UIRequiredDeviceCapabilities: [ 'armv7' ] }

As the output above shows, most of the issue are with the handling of
empty strings and empty dictionary keys.
Added with the new implementation are custom Errors for invalid
input, better handling of comments inside tags and a more aligned
handling of empty keys/strings.

I know this is a huge change, so I'd understand if you're hesitant to merge this,
but let me know your thoughts.
Initially I only wanted to fix the handling of empty strings/keys but then I got a bit carried away.

Related issues: #79 #71 #66 #81 #80 #67

This commit refactors and adds more tests to the `parse()` implementation
to align with similar libraries's behaviour, e.g.:
- https://github.com/Microsoft/node-fast-plist
- https://docs.python.org/3/library/plistlib.html

With the new tests a few of the tests were failing:
```
31 passing (177ms)
10 failing

1) parse() real should throw an Error when parsing an empty real:
    AssertionError: Missing expected exception..

2) parse() string should parse a self closing string:
    AssertionError: null === ''

3) parse() string should parse an empty string:
    AssertionError: null === ''

4) parse() string should parse a string with comments:
    AssertionError: 'a comment  string' === 'a string'

5) parse() array should parse empty elements inside an array:
    AssertionError: [ false ] deepEqual [ '', false ]

6) parse() dict should throw if key is missing:
    AssertionError: Missing expected exception..

7) parse() dict should throw if two keys follow each other:
    AssertionError: Missing expected exception..

8) parse() dict should throw if value is missing:
    AssertionError: Missing expected exception..

9) parse() dict should parse an empty key:
    AssertionError: {} deepEqual { '': '1' }

10) parse() dict should parse an empty value:
    AssertionError: { a: null } deepEqual { a: '' }
```

This commit also refactors the `lib/parse.js` to pass these tests.
When executing the new implementation of `lib/parse.js` agains the old
tests the following tests were failing:
```
25 passing (143ms)
3 failing

1) plist parse() should parse an empty <key/> in a dictionary:
    AssertionError: false == true

2) plist parse() should parse an empty <key></key> in a dictionary:
    AssertionError: false == true

3) plist parse() should parse an empty <key></key> and <string></string> in dictionary with more data:
    AssertionError: { '': '', UIRequiredDeviceCapabilities: [ 'armv7' ] } deepEqual { UIRequiredDeviceCapabilities: [ 'armv7' ] }
```

As the output above shows, most of the issue are with the handling of
empty strings and empty dictionary keys.
Added with the new implementation are custom Errors for unexpected
input, better handling of comments inside tags and a more aligned
handling of empty keys/strings.
@ZauberNerd ZauberNerd changed the title Harden test-cases and implementation to align with other implementations Harden test-cases and align with other implementations Feb 12, 2017
@ZauberNerd
Copy link
Contributor Author

The browser tests are failing because of: https://docs.travis-ci.com/user/pull-requests#Pull-Requests-and-Security-Restrictions

@ZauberNerd
Copy link
Contributor Author

@TooTallNate @mreinstein ping, any update on this?

Copy link
Collaborator

@mreinstein mreinstein left a comment

Choose a reason for hiding this comment

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

@ZauberNerd overall, good changes. I'm sorry it took so long to get around to this. most of my comments are fairly trivial.

Another issue is I'd like to ensure the tests pass.

lib/parse.js Outdated
// ignore comment nodes (text)
if (!shouldIgnoreNode(node.childNodes[i])) {
new_arr.push( parsePlistXML(node.childNodes[i]));
if (!isEmptyNode(node)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I'd prefer to return early rather than wrap the whole thing in an if block. results in less nesting. e.g.,

if(isEmptyNode(node)) {
  return [];
}

It also eliminates the "not" modifier (!) which makes the if condition ever-so-slightly easier to read.

lib/parse.js Outdated
if (!shouldIgnoreNode(node.childNodes[i])) {
if (key === null) {
counter = 0;
if (!isEmptyNode(node)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

same here in terms of early return.

lib/parse.js Outdated
if (!shouldIgnoreNode(node.childNodes[i])) {
res = parsePlistXML(node.childNodes[i]);
if (null != res) new_arr.push(res);
if (!isEmptyNode(node)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

another early return opportunity

lib/parse.js Outdated
res = '';
if(node.childNodes[0]) {
if(!isEmptyNode(node)) {
res = node.childNodes[0].nodeValue;
Copy link
Collaborator

Choose a reason for hiding this comment

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

another early return opportunity. I think you can just return node.childNodes[0].nodeValue rather than filling a variable and returning that?

lib/parse.js Outdated
if(!isEmptyNode(node)) {
for (i=0; i < node.childNodes.length; i++) {
var type = node.childNodes[i].nodeType;
if (type === 3 || type === 4) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

rather than using these magic numbers 3 and 4 can you define them as node type constants?

lib/parse.js Outdated
if (node.childNodes[d].nodeType === 3) {
res += node.childNodes[d].nodeValue;
for (i=0; i < node.childNodes.length; i++) {
if (node.childNodes[i].nodeType === 3) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I know you didn't write this code; it pre-dates your additions. But while you're in here, can you replace the magic number usage here too?

lib/parse.js Outdated
for (d=0; d < node.childNodes.length; d++) {
if (node.childNodes[d].nodeType === 3) {
res += node.childNodes[d].nodeValue.replace(/\s+/g, '');
if (!isEmptyNode(node)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

another early return opportunity I think

Copy link
Collaborator

@mreinstein mreinstein left a comment

Choose a reason for hiding this comment

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

changes look good. I'll merge after this change

lib/parse.js Outdated
@@ -10,6 +10,12 @@ var DOMParser = require('xmldom').DOMParser;

exports.parse = parse;

var NODE_TYPES = {
Copy link
Collaborator

Choose a reason for hiding this comment

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

let's do these instead please:

TEXT_NODE = 3
CDATA_NODE = 4
COMMENT_NODE = 8

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ok, will do.
Btw: https://docs.travis-ci.com/user/sauce-connect/ and https://docs.travis-ci.com/user/jwt might help with getting the sauce labs tests to run for PRs from forks.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't think I have access to sauce, nor am I familiar with the service in great detail. @TooTallNate care to take this on?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Otherwise we could just disable them for PRs - I think they'll still be run on master merge, see this section: https://docs.travis-ci.com/user/pull-requests#Pull-Requests-and-Security-Restrictions

Copy link
Collaborator

Choose a reason for hiding this comment

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

yeah but the problem with that is it's more work to verify PRs are good before they're merged. I would rather not merge anything to master and have the tests fail after the fact.

@mreinstein mreinstein merged commit 4853b74 into TooTallNate:master Feb 19, 2017
@ZauberNerd ZauberNerd deleted the harden-parser branch February 19, 2017 21:00
@mreinstein
Copy link
Collaborator

@ZauberNerd can you open a separate issue for the sauce labs change please?

@ZauberNerd
Copy link
Contributor Author

@mreinstein do you have an idea when you are going to release a new version to npm with these changes included?

ZauberNerd added a commit to ZauberNerd/react-native-background-fetch that referenced this pull request May 10, 2017
The `plist` module had a bug when parsing an empty key which has since
been fixed and released in a new version.
This commit updates the `plist` dependency to the latest version in
order to remove a fix we had to introduce in this project to work around
that issue.

Ref: TooTallNate/plist.js#84
dpa99c added a commit to dpa99c/cordova-custom-config that referenced this pull request Jan 16, 2019
@mreinstein
Copy link
Collaborator

@ZauberNerd sorry for the crazy long reply delay. I'm hoping to lump together all of these changes that are considered "API breaking" in the next version (v4)

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