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

Generators refactoring #601

Merged
merged 97 commits into from Jul 15, 2018
Merged

Generators refactoring #601

merged 97 commits into from Jul 15, 2018

Conversation

ErikSchierboom
Copy link
Member

This is to test the newly enabled WIP plugin (see exercism/discussions#217 (comment)). And it will also give insight into how my refactoring attempt is progressing.

@ErikSchierboom ErikSchierboom changed the title WIP Generators refactoring Generators refactoring Jul 8, 2018
@ErikSchierboom
Copy link
Member Author

Finally, after much work, I'm done with my huge generators refactoring. It has become even bigger that the F# equivalent, and it contains the following improvements:

  • Simplified extension points available to generators to three virtual methods.
  • Removed as many of the classes the user should know about.
  • Greatly simplified rendering code.
  • Make data rendering code generic (handle any dictionary/array/list instead of only a couple of hardcoded ones).
  • Many places in which the generator code has been simplified.
  • Fixed a bug where the test methods were not always in the correct order when using nested canonical data cases.
  • Made canonical data and canonical data case classes immutable.
  • Allow testing of properties and constructors.
  • Use ValueTuple<T> where possible.
  • Add MultilineString class to allow generators to be explicit on how they want the data to be output.
  • Add helper method to render enumerations.
  • Remove unused code.
  • Updated documentation with more examples of common use cases.
  • Restructured namespaces to make the code have a better structure.
  • And just overall many, many improvements to make the code more readable.

CC @robkeim @jpreese

@ErikSchierboom
Copy link
Member Author

@robkeim @jpreese If this PR is too big too review, no sweat. If you do want to review, it might be best to just check out this branch and examine the code locally.

@robkeim
Copy link
Contributor

robkeim commented Jul 9, 2018

@ErikSchierboom I'm feeling brave... I'll take a look :)

@ErikSchierboom
Copy link
Member Author

@robkeim Great! I'm really interested in what you think.

Copy link
Contributor

@robkeim robkeim left a comment

Choose a reason for hiding this comment

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

Awesome work @ErikSchierboom! Super cool stuff 👍

Added a few comments and reviewed through CollatatzConjecture so far.

Additional methods added using this override will be added to the bottom of the test suite.
In some case, you might want to override the parameters that are used as input parameters.

An example of this is the [two fer]() generator, which does not use any input parameters when the `"name"` input parameter is set to `null`:
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing url here

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed


If a test method tests an instance method, you can also specify which parameters to use as constructor parameters (the others will be input parameters, unless specified otherwise).

An example of this is the [matrix]() generator, which specifies that the `"string"` parameter should be passed as a constructor parameter:
Copy link
Contributor

Choose a reason for hiding this comment

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

Here as well

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed


Here the **Assert** is being overridden. The assert needs to call additional functions, but only if the property is `consistency`. Otherwise, render the assert as usual.
An example of this is the [RNA transcription]() generator, which defines that some test methods should throws an `ArgumentException`:
Copy link
Contributor

Choose a reason for hiding this comment

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

Missing url here

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed


This method allows you to customize the output of the test class. Only in rare cases would you want to override this method. The most common use case to override this method, is to add additional (helper) methods to the test suite.

An example of this is the [Tournament](https://github.com/exercism/csharp/blob/master/generators/Exercises/Tournament.cs) generator, which adds a helper method to the test suite:
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: tournament should be lowercase to be consistent with the other test names

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

@@ -89,7 +89,7 @@ public void Number_15_bit_integer()
public void Empty_list()
{
var inputBase = 2;
var digits = new int[0];
var digits = Array.Empty<int>();
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

@@ -60,7 +60,30 @@ public void Consecutive_spares_each_get_a_one_roll_bonus()
public void A_spare_in_the_last_frame_gets_a_one_roll_bonus_that_is_counted_once()
{
var sut = new BowlingGame();
var previousRolls = new [] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 3, 7 };
var previousRolls = new[]
Copy link
Contributor

Choose a reason for hiding this comment

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

Why are these all on separate lines but the previous tests aren't? IMO it's probably better to have it on one line

Copy link
Contributor

Choose a reason for hiding this comment

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

I guess there are a few other occurrences in the rest of this test file.

Copy link
Member Author

Choose a reason for hiding this comment

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

Well, this is me trying to be clever. Many of the input arrays have a very limited number of elements (e.g. 1 or 2), in which case rendering them over multiple lines would be overkill IMHO. The code that does this check can be found here. It essentially checks if it is not an array of arrays and if the rendered single line output would be less than or equal to 72 characters. If so, it is output as a single line.

This is of course completely up for debate. I'd be happy to hear your thoughts on this.

Copy link
Member Author

Choose a reason for hiding this comment

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

What I could do, is detect how many lines would end up being created. If there are more than a given number, say 8, then I could put multiple elements on a single line. Would that be an option?

Copy link
Member Author

Choose a reason for hiding this comment

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

@robkeim I've given it some more thought, and I think I was attempting to be a bit too clever. I've removed the automatic multi-line rendering of single-dimensional arrays. Exercises can choose to opt-in themselves, should they want to do multi-line array initialization.

new ValueTuple<int, int>(col+1, row),
new ValueTuple<int, int>(col, row+1)
(col, row - 1),
(col-1, row),
Copy link
Contributor

Choose a reason for hiding this comment

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

Super minor: missing spaces around the - here and the + below

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

var expected = new string[]
{
""
var minefield = new[]
Copy link
Contributor

Choose a reason for hiding this comment

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

Minor: the fact that each row is a separate line here vs in the expected it's on one line makes this definitely look like it wasn't handwritten.

Copy link
Member Author

Choose a reason for hiding this comment

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

Fixed

@@ -35,7 +35,177 @@ public void Limit_is_prime()
[Fact(Skip = "Remove to run test")]
public void Find_primes_up_to_1000()
{
var expected = new[] { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997 };
var expected = new[]
Copy link
Contributor

Choose a reason for hiding this comment

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

This could probably be on one line or a few lines instead of one line per number

Copy link
Member Author

Choose a reason for hiding this comment

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

See comment above.

@jpreese
Copy link
Contributor

jpreese commented Jul 9, 2018

haha, oh man.. where to start. Yeah, let me check this out locally and try to make sense of it all. Should hopefully have some time later in the week.

@ErikSchierboom
Copy link
Member Author

Although I do think the commits are really useful to see the gradual evolution of the refactoring, I think that squashing this will be the way to go when merging this PR.


As an example, if you wanted to change the default behavior so that when the `Input` value of a test is a negative number, an exception should be thrown, the code would look like this.
There are quite some generators that want to change the input or expected value.
Copy link
Contributor

Choose a reason for hiding this comment

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

This sentence doesn't seem quite right

As an example, if you wanted to change the default behavior so that when the `Input` value of a test is a negative number, an exception should be thrown, the code would look like this.
There are quite some generators that want to change the input or expected value.

An example of this is the [bracket-push](https://github.com/exercism/csharp/blob/master/generators/Exercises/Generators/BracketPush.cs) generator, which input value with the key `"value"` requires some additional escaping:
Copy link
Contributor

Choose a reason for hiding this comment

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

This sentence doesn't seem quite right


Here the **Assert** is being overridden. The assert needs to call additional functions, but only if the property is `consistency`. Otherwise, render the assert as usual.
An example of this is the [rna-transcription](https://github.com/exercism/csharp/blob/master/generators/Exercises/Generators/RnaTranscription.cs) generator, which defines that some test methods should throws an `ArgumentException`:
Copy link
Contributor

Choose a reason for hiding this comment

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

should throw*


Additional methods added using this override will be added to the bottom of the test suite.
In some case, you might want to override the parameters that are used as input parameters.
Copy link
Contributor

Choose a reason for hiding this comment

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

cases*

}
```

Note that as mentioned before, the namespace of any thrown exception types are automaticall added to the list of namespaces.
Copy link
Contributor

Choose a reason for hiding this comment

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

automatically*

@jpreese
Copy link
Contributor

jpreese commented Jul 14, 2018

That review destroyed by browser.

@ErikSchierboom
Copy link
Member Author

@jpreese Thanks for the review. I've updated the docs.

Copy link
Contributor

@robkeim robkeim left a comment

Choose a reason for hiding this comment

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

Awesome job @ErikSchierboom!!! Super high quality code as always 👍

@ErikSchierboom ErikSchierboom merged commit e3142cb into exercism:master Jul 15, 2018
@ErikSchierboom ErikSchierboom deleted the generators-refactoring branch July 15, 2018 16:38
@ErikSchierboom
Copy link
Member Author

Thanks a ton for reviewing @robkeim and @jpreese!

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

3 participants