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

twelve-days: Implement canonical-data.json #954

Merged
merged 1 commit into from
Nov 2, 2017

Conversation

alpakhomov
Copy link

@alpakhomov alpakhomov commented Oct 14, 2017


Resolves #590 // m-a-age

@alpakhomov
Copy link
Author

I'm not sure about my implementation of canonical-data.json. The problem is that a lot of language tracks test every single line of the song - like 1, 2, 3 and so on till 12. I don't understand the reason behind it - it looks redundant to me. So, I think it's important to test the first and the last lines (checking the offset if some array-like data structure is used). Then, we check that all other parts of the song are there when we will run tests for intervals (for example, from first to third line). So, I don't really know what should be a priority - sticking to test cases that already are in many language tracks, or creating a suite of essential tests.
And maybe I'm wrong about this "test redundancy" in general.

@Insti
Copy link
Contributor

Insti commented Oct 14, 2017

With only 12 items it doesn't seem unreasonable to test every verse individually.

@alpakhomov
Copy link
Author

Added test cases for every verse and changed their descriptions accordingly

"version": "1.0.0",
"cases": [
{
"description": "returns verse number 1",
Copy link
Contributor

@Insti Insti Oct 15, 2017

Choose a reason for hiding this comment

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

I'd like to see more descriptive descriptions. This does not tell me anything that cannot be automatically generated from the input.

How about:
"first day a partridge in a pair tree"
"second day two turtle doves"
etc.

Or something similar.

{
"description": "finds multiple verses, which are a fragment of the song",
"property": "verses",
"input": [1, 3],
Copy link
Contributor

Choose a reason for hiding this comment

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

There is no test for verses that doesn't start at 1
what should happen in that case?

@alpakhomov
Copy link
Author

Thanks @Insti, I've changed test descriptions and added test for interval from the middle of the song (4-6). Also changed interval test description accordingly.

I have also rebased commits into one.

Copy link
Member

@petertseng petertseng left a comment

Choose a reason for hiding this comment

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

it seems to me that for the verse tests, instead of having "input": [1], it can just be "input": 1, - it will never be the case that the input should contain more than one verse number, so might as well not use an array.

input also seems unnecessary for sing.

Copy link
Member

@petertseng petertseng left a comment

Choose a reason for hiding this comment

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

Nice.

I am wondering whether the trailing newlines on all expected outputs are necessary. Naively I might assume they aren't, but maybe there is something I don't know.

After this, I think I have said all I need to say. I haven't explicitly checked that all the expected values are correct, so I leave that to others (in other words, please don't wait for my approval before merging)

@alpakhomov
Copy link
Author

Thank you @petertseng
About this newlines - I just copied them from current implementations, it looks like most people included them in their tests. But I don't know if there is any logic behind it.

@ErikSchierboom
Copy link
Member

I am wondering whether the trailing newlines on all expected outputs are necessary. Naively I might assume they aren't, but maybe there is something I don't know.

I agree. It makes sense for the verses to be separated by newlines, but for an individual verse it doesn't IMHO. Maybe this is the point where we should remove those extra newlines?

@alpakhomov
Copy link
Author

Okay, I a bit confused now, so, just to be clear - we are talking about last newline symbol at the end of each verse/block of verses, right?

"description": "first day a partridge in a pear tree",
"property": "verse",
"input": 1,
"expected": "On the first day of Christmas my true love gave to me, a Partridge in a Pear Tree.\n"
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe the suggestion is that this line should not have the \n at the end.

Copy link
Member

Choose a reason for hiding this comment

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

Correct

Copy link
Author

Choose a reason for hiding this comment

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

Removed trailing newline for single verse tests

"description": "finds multiple verses, which are a fragment from the beggining of the song",
"property": "verses",
"input": [1, 3],
"expected": "On the first day of Christmas my true love gave to me, a Partridge in a Pear Tree.\n\nOn the second day of Christmas my true love gave to me, two Turtle Doves, and a Partridge in a Pear Tree.\n\nOn the third day of Christmas my true love gave to me, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree.\n"
Copy link
Contributor

@Insti Insti Oct 17, 2017

Choose a reason for hiding this comment

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

But whenever there are multiple verses they should be separated by \n\n
But no trailing newline. Maybe the trailing single newline should be there.

Copy link
Member

Choose a reason for hiding this comment

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

But it only makes sense in the context of multiple verses, so why add it to a single verse?

Copy link
Contributor

@Insti Insti Oct 17, 2017

Choose a reason for hiding this comment

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

Maybe the trailing single newline should be there.

In this case I am wondering about how many newlines should be at the end of the completed verses string. 0, 1 or 2? Currently there is 1.

I still agree there should be 0 at the end of each individual verse.
And that the joined verses should be separated by newlines.

Copy link
Member

@ErikSchierboom ErikSchierboom Oct 17, 2017

Choose a reason for hiding this comment

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

I would expect the following to hold:

  1. A single verse would have no newline at the end
  2. Verses (more than one) are separated by an empty line
  3. Verses don't have trailing newlines (so that means 0)

@alpakhomov alpakhomov force-pushed the twelve-days branch 2 times, most recently from e5ada1f to e0a528e Compare October 17, 2017 07:49
@alpakhomov
Copy link
Author

A single verse would have no newline at the end
Verses (more than one) are separated by an empty line
Verses don't have trailing newlines (so that means 0)

Fixed

Copy link
Contributor

@Insti Insti left a comment

Choose a reason for hiding this comment

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

Looks great 👍

@Insti
Copy link
Contributor

Insti commented Oct 17, 2017

Do you have an automated checker for this exercise @petertseng ?

Copy link
Member

@petertseng petertseng left a comment

Choose a reason for hiding this comment

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

Do you have an automated checker for this exercise @petertseng

Nope.

{
"description": "finds multiple verses, which are a fragment from the beggining of the song",
"property": "verses",
"input": [1, 3],
Copy link
Member

Choose a reason for hiding this comment

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

hmm, sorry to swoop in with another thing again, but I just remembered... what about assigning meaning to the two elements of this array? See how beer song does it at https://github.com/exercism/problem-specifications/blob/master/exercises/beer-song/canonical-data.json#L54-L55 . Any desire to do the same here?

Copy link
Author

Choose a reason for hiding this comment

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

Added two elements instead of one array; named them beginning and end respectively. Not sure if it will be a good idea to split all cases into two groups, like "beer-song" does (for single and multiple verse test cases).

Copy link
Contributor

@Insti Insti Oct 17, 2017

Choose a reason for hiding this comment

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

Should beginning and end be subkeys of input?

"input": {"beginning": 1, "end": 3}

Copy link
Author

Choose a reason for hiding this comment

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

Should it? Wouldn't it be confused with some object or map data structure?

Copy link
Member

@petertseng petertseng Oct 17, 2017

Choose a reason for hiding this comment

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

Should beginning and end be subkeys of input?

That would be an interesting change to make for the JSON files all around. Then, instead of having to vary the input keys per exercise, test generators would know that all inputs can be found in "input". That could be a useful change for the schema! I think the track maintainer who wishes that to happen for all JSON files should make the issue requesting it.

"end": 3,
"expected": "On the first day of Christmas my true love gave to me, a Partridge in a Pear Tree.\n\nOn the second day of Christmas my true love gave to me, two Turtle Doves, and a Partridge in a Pear Tree.\n\nOn the third day of Christmas my true love gave to me, three French Hens, two Turtle Doves, and a Partridge in a Pear Tree."
},

Copy link
Member

Choose a reason for hiding this comment

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

Line 84 should be removed.

{
"description": "twelfth day twelve drummers drumming",
"property": "verse",
"input": 12,
Copy link
Member

Choose a reason for hiding this comment

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

Instead of "input" how about "number" since it is the verse number being asked for.

@rpottsoh
Copy link
Member

If you would like to make this more beer-song like, you could group the Verse cases under a description of "Verse" and all the other cases under a description of "Lyrics".

@alpakhomov
Copy link
Author

Thanks @rpottsoh
I've splitted cases into two groups (verse and lyrics), renamed input to number, and removed unnecessary empty line.

@petertseng
Copy link
Member

Do you have an automated checker for this exercise

OK, guess that wasn't so much to write.

require 'json'
require_relative '../../verify'

DAYS = {
  twelfth: 'twelve Drummers Drumming',
  eleventh: 'eleven Pipers Piping',
  tenth: 'ten Lords-a-Leaping',
  ninth: 'nine Ladies Dancing',
  eighth: 'eight Maids-a-Milking',
  seventh: 'seven Swans-a-Swimming',
  sixth: 'six Geese-a-Laying',
  fifth: 'five Gold Rings',
  fourth: 'four Calling Birds',
  third: 'three French Hens',
  second: 'two Turtle Doves',
  first: 'a Partridge in a Pear Tree',
}.each_value(&:freeze).freeze

def list(things)
  things.size == 1 ? things[0] : (things[0...-1] << "and #{things[-1]}").join(', ')
end

def verse(n)
  ordinal = DAYS.keys[-n]
  gifts = DAYS.values.last(n)
  "On the #{ordinal} day of Christmas my true love gave to me, #{list(gifts)}."
end

def verses(a, b)
  (a..b).map { |n| verse(n) }.join("\n\n")
end

json = JSON.parse(File.read(File.join(__dir__, 'canonical-data.json')))

raise "There should be exactly two cases, not #{json['cases'].size}" if json['cases'].size != 2

verify(json['cases'][0]['cases'], property: 'verse') { |c|
  verse(c['number'])
}

subcases = by_property(json['cases'][1]['cases'], %w(verses sing))

verify(subcases['verses'], property: 'verses') { |c|
  verses(c['beginning'], c['end'])
}

verify(subcases['sing'], property: 'sing') { |c|
  verses(1, DAYS.size)
}

As written, it's fine.

Note that it may be a bit strange that both verses and sing are in the same group. As you can see though it can be dealt with.

@Insti
Copy link
Contributor

Insti commented Oct 18, 2017

Should verses and sing have their own sections?

@Insti
Copy link
Contributor

Insti commented Oct 18, 2017

Looking at the description and the policy that we should not be testing intermediate values it would seem that there should only be one test for the complete song.

(Sorry I've only just noticed this @alpakhomov )

The solution may be to update the description to add the requirement that verse ranges are also required, otherwise the tests will be passable by a hardcoded string, which seems like it would not be a "good" exercise.

The interface could then be, sing(x,y) where x <= y. and we don't need 3 different methods.

Thoughts?

@alpakhomov
Copy link
Author

I changed properties and descriptions of tests in the lyrics block - now there is only one test for the whole song, plus we can still have only two groups of test cases (verse and lyrics). I think it looks better now - but I can revert chages back if needed.
About changes in exercise description - should it be done? If yes, do I need a separate pull request?

@petertseng
Copy link
Member

If we take this PR as is, this exercise is on par with:

Whatever treatment is given to this should also be given to all the others.

@Insti
Copy link
Contributor

Insti commented Oct 30, 2017

Hi @alpakhomov, are you willing to make some more changes to this to match what we're planning on doing to food-chain in #973

property: was verse and sing, both become recite

number,beginning, end: become start verse and end verse, each test case should have both. Where there was only one number they should start verse and end verse should be the same.

@alpakhomov
Copy link
Author

Changed property and parameter names, everything else is the same

@ErikSchierboom
Copy link
Member

Great work @alpakhomov ! What I was wondering, is if we should encoding the expected value as an array of strings instead of a single string, similar to what happens in the food-chain canonical data? My personal preference would be to use an array. @Insti, what are your thoughts?

@Insti
Copy link
Contributor

Insti commented Oct 30, 2017

encoding the expected value as an array of strings

I have no strong opinion, an array of strings seems fine, so long as there is a comment about it for the test implementer: https://github.com/exercism/problem-specifications/blob/master/exercises/food-chain/canonical-data.json#L4-L7

Copy link
Contributor

@Insti Insti left a comment

Choose a reason for hiding this comment

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

Convert the multi-line expectations into an array of strings.

{
"description": "first day a partridge in a pear tree",
"property": "recite",
"start verse": 1,
Copy link
Member

Choose a reason for hiding this comment

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

We have now come up with an updated format, which requires the properties to be lowerCamelCase (see #983). Could you update the property to startVerse? And do the same for the other "start verse" properties?

"description": "first day a partridge in a pear tree",
"property": "recite",
"start verse": 1,
"end verse": 1,
Copy link
Member

Choose a reason for hiding this comment

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

We have now come up with an updated format, which requires the properties to be lowerCamelCase (see #983). Could you update the property to endVerse? And do the same for the other "end verse" properties?

"property": "recite",
"start verse": 2,
"end verse": 2,
"expected": "On the second day of Christmas my true love gave to me, two Turtle Doves, and a Partridge in a Pear Tree."
Copy link
Member

Choose a reason for hiding this comment

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

Could you encode the expected value as an array of strings? See #983 and #973. And do the same for the other "expected" properties?

@ErikSchierboom
Copy link
Member

@alpakhomov Sorry about the churn in the changing requirements! It has taken us a little while to come up with uniform specifications, but the final result is in #983 and it should now be stable.

@alpakhomov
Copy link
Author

Renamed parameters with camelCase, changed all expected values to arrays (even single line ones), added comment about expected values same as here https://github.com/exercism/problem-specifications/blob/master/exercises/food-chain/canonical-data.json#L4-L7

@ErikSchierboom
Copy link
Member

I think this is ready to merge. @Insti, do you agree?

@Insti
Copy link
Contributor

Insti commented Nov 2, 2017

Yes it looks good to me.

@ErikSchierboom ErikSchierboom merged commit 7c59aaa into exercism:master Nov 2, 2017
@ErikSchierboom
Copy link
Member

Merged! Thanks a lot @alpakhomov!

@Insti
Copy link
Contributor

Insti commented Nov 2, 2017

Thanks for all your work on this @alpakhomov and your patience with the constantly changing requirements ❤️

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.

5 participants