Skip to content

Commit 34010ae

Browse files
ErikSchierboomJohn Reese
authored and
John Reese
committed
Kindergarten-garden generator (#446)
* Fix null-reference exception when key is missing * Don't discard JToken hierarchy * Add kindergarten-garden generator
1 parent 971a22d commit 34010ae

10 files changed

+183
-88
lines changed

exercises/kindergarten-garden/Example.cs

+9-10
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ public enum Plant
1010
Grass
1111
}
1212

13-
public class Garden
13+
public class KindergartenGarden
1414
{
1515
private const int PlantsPerChildPerRow = 2;
1616
private const char RowSeparator = '\n';
@@ -32,22 +32,21 @@ public class Garden
3232

3333
private readonly IDictionary<string, IEnumerable<Plant>> plantsByChild;
3434

35-
public Garden(IEnumerable<string> children, string windowSills)
35+
public KindergartenGarden(string diagram) : this(diagram, DefaultChildren)
3636
{
37-
var plantsPerChild = PlantsPerChild(windowSills);
38-
plantsByChild = children.OrderBy(c => c)
39-
.Zip(plantsPerChild, Tuple.Create)
40-
.ToDictionary(kv => kv.Item1, kv => kv.Item2);
4137
}
4238

43-
public IEnumerable<Plant> GetPlants(string child)
39+
public KindergartenGarden(string diagram, IEnumerable<string> students)
4440
{
45-
return plantsByChild.ContainsKey(child) ? plantsByChild[child] : Enumerable.Empty<Plant>();
41+
var plantsPerChild = PlantsPerChild(diagram);
42+
plantsByChild = students.OrderBy(c => c)
43+
.Zip(plantsPerChild, Tuple.Create)
44+
.ToDictionary(kv => kv.Item1, kv => kv.Item2);
4645
}
4746

48-
public static Garden DefaultGarden(string windowSills)
47+
public IEnumerable<Plant> Plants(string child)
4948
{
50-
return new Garden(DefaultChildren, windowSills);
49+
return plantsByChild.ContainsKey(child) ? plantsByChild[child] : Enumerable.Empty<Plant>();
5150
}
5251

5352
private static Plant PlantFromCode(char code)

exercises/kindergarten-garden/KinderGartenGardenTest.cs

-60
This file was deleted.

exercises/kindergarten-garden/KindergartenGarden.cs

+5-6
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,17 @@ public enum Plant
99
Grass
1010
}
1111

12-
public class Garden
13-
{
14-
public Garden(IEnumerable<string> children, string windowSills)
12+
public class KindergartenGarden
13+
{
14+
public KindergartenGarden(string diagram)
1515
{
1616
}
1717

18-
public IEnumerable<Plant> GetPlants(string child)
18+
public KindergartenGarden(string diagram, IEnumerable<string> students)
1919
{
20-
throw new NotImplementedException("You need to implement this function.");
2120
}
2221

23-
public static Garden DefaultGarden(string windowSills)
22+
public IEnumerable<Plant> Plants(string student)
2423
{
2524
throw new NotImplementedException("You need to implement this function.");
2625
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// This file was auto-generated based on version 1.0.0 of the canonical data.
2+
3+
using Xunit;
4+
5+
public class KindergartenGardenTest
6+
{
7+
[Fact]
8+
public void Partial_garden_garden_with_single_student()
9+
{
10+
var sut = new KindergartenGarden("RC\nGG");
11+
Assert.Equal(new[] { Plant.Radishes, Plant.Clover, Plant.Grass, Plant.Grass }, sut.Plants("Alice"));
12+
}
13+
14+
[Fact(Skip = "Remove to run test")]
15+
public void Partial_garden_different_garden_with_single_student()
16+
{
17+
var sut = new KindergartenGarden("VC\nRC");
18+
Assert.Equal(new[] { Plant.Violets, Plant.Clover, Plant.Radishes, Plant.Clover }, sut.Plants("Alice"));
19+
}
20+
21+
[Fact(Skip = "Remove to run test")]
22+
public void Partial_garden_garden_with_two_students()
23+
{
24+
var sut = new KindergartenGarden("VVCG\nVVRC");
25+
Assert.Equal(new[] { Plant.Clover, Plant.Grass, Plant.Radishes, Plant.Clover }, sut.Plants("Bob"));
26+
}
27+
28+
[Fact(Skip = "Remove to run test")]
29+
public void Partial_garden_multiple_students_for_the_same_garden_with_three_students_second_students_garden()
30+
{
31+
var sut = new KindergartenGarden("VVCCGG\nVVCCGG");
32+
Assert.Equal(new[] { Plant.Clover, Plant.Clover, Plant.Clover, Plant.Clover }, sut.Plants("Bob"));
33+
}
34+
35+
[Fact(Skip = "Remove to run test")]
36+
public void Partial_garden_multiple_students_for_the_same_garden_with_three_students_third_students_garden()
37+
{
38+
var sut = new KindergartenGarden("VVCCGG\nVVCCGG");
39+
Assert.Equal(new[] { Plant.Grass, Plant.Grass, Plant.Grass, Plant.Grass }, sut.Plants("Charlie"));
40+
}
41+
42+
[Fact(Skip = "Remove to run test")]
43+
public void Full_garden_first_students_garden()
44+
{
45+
var sut = new KindergartenGarden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV");
46+
Assert.Equal(new[] { Plant.Violets, Plant.Radishes, Plant.Violets, Plant.Radishes }, sut.Plants("Alice"));
47+
}
48+
49+
[Fact(Skip = "Remove to run test")]
50+
public void Full_garden_second_students_garden()
51+
{
52+
var sut = new KindergartenGarden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV");
53+
Assert.Equal(new[] { Plant.Clover, Plant.Grass, Plant.Clover, Plant.Clover }, sut.Plants("Bob"));
54+
}
55+
56+
[Fact(Skip = "Remove to run test")]
57+
public void Full_garden_second_to_last_students_garden()
58+
{
59+
var sut = new KindergartenGarden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV");
60+
Assert.Equal(new[] { Plant.Grass, Plant.Clover, Plant.Clover, Plant.Grass }, sut.Plants("Kincaid"));
61+
}
62+
63+
[Fact(Skip = "Remove to run test")]
64+
public void Full_garden_last_students_garden()
65+
{
66+
var sut = new KindergartenGarden("VRCGVVRVCGGCCGVRGCVCGCGV\nVRCCCGCRRGVCGCRVVCVGCGCV");
67+
Assert.Equal(new[] { Plant.Grass, Plant.Violets, Plant.Clover, Plant.Violets }, sut.Plants("Larry"));
68+
}
69+
70+
[Fact(Skip = "Remove to run test")]
71+
public void Non_alphabetical_student_list_first_students_garden()
72+
{
73+
var sut = new KindergartenGarden("VCRRGVRG\nRVGCCGCV", new[] { "Samantha", "Patricia", "Xander", "Roger" });
74+
Assert.Equal(new[] { Plant.Violets, Plant.Clover, Plant.Radishes, Plant.Violets }, sut.Plants("Patricia"));
75+
}
76+
77+
[Fact(Skip = "Remove to run test")]
78+
public void Non_alphabetical_student_list_second_students_garden()
79+
{
80+
var sut = new KindergartenGarden("VCRRGVRG\nRVGCCGCV", new[] { "Samantha", "Patricia", "Xander", "Roger" });
81+
Assert.Equal(new[] { Plant.Radishes, Plant.Radishes, Plant.Grass, Plant.Clover }, sut.Plants("Roger"));
82+
}
83+
84+
[Fact(Skip = "Remove to run test")]
85+
public void Non_alphabetical_student_list_third_students_garden()
86+
{
87+
var sut = new KindergartenGarden("VCRRGVRG\nRVGCCGCV", new[] { "Samantha", "Patricia", "Xander", "Roger" });
88+
Assert.Equal(new[] { Plant.Grass, Plant.Violets, Plant.Clover, Plant.Grass }, sut.Plants("Samantha"));
89+
}
90+
91+
[Fact(Skip = "Remove to run test")]
92+
public void Non_alphabetical_student_list_fourth_last_students_garden()
93+
{
94+
var sut = new KindergartenGarden("VCRRGVRG\nRVGCCGCV", new[] { "Samantha", "Patricia", "Xander", "Roger" });
95+
Assert.Equal(new[] { Plant.Radishes, Plant.Grass, Plant.Clover, Plant.Violets }, sut.Plants("Xander"));
96+
}
97+
}

generators/Exercise.cs

+6-1
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,15 @@ private HashSet<string> GetUsingNamespaces()
6767
private TestMethod CreateTestMethod(CanonicalDataCase canonicalDataCase, int index) => new TestMethod
6868
{
6969
Skip = index > 0,
70-
Name = canonicalDataCase.Description.ToTestMethodName(),
70+
Name = ToTestMethodName(canonicalDataCase),
7171
Body = RenderTestMethodBody(canonicalDataCase)
7272
};
7373

74+
private static string ToTestMethodName(CanonicalDataCase canonicalDataCase)
75+
=> canonicalDataCase.UseFullDescriptionPath
76+
? string.Join(" - ", canonicalDataCase.DescriptionPath).ToTestMethodName()
77+
: canonicalDataCase.Description.ToTestMethodName();
78+
7479
private string RenderTestMethodBody(CanonicalDataCase canonicalDataCase)
7580
{
7681
var testMethodBody = CreateTestMethodBody(canonicalDataCase);
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using Generators.Input;
4+
using Generators.Output;
5+
using Humanizer;
6+
7+
namespace Generators.Exercises
8+
{
9+
public class KindergartenGarden : Exercise
10+
{
11+
protected override void UpdateCanonicalData(CanonicalData canonicalData)
12+
{
13+
foreach (var canonicalDataCase in canonicalData.Cases)
14+
{
15+
canonicalDataCase.TestedMethodType = TestedMethodType.Instance;
16+
canonicalDataCase.UseFullDescriptionPath = true;
17+
18+
if (canonicalDataCase.Properties.ContainsKey("students"))
19+
canonicalDataCase.SetConstructorInputParameters("diagram", "students");
20+
else
21+
canonicalDataCase.SetConstructorInputParameters("diagram");
22+
23+
var plants = (IEnumerable<string>)canonicalDataCase.Properties["expected"];
24+
canonicalDataCase.Properties["expected"] = plants
25+
.Select(x => new UnescapedValue($"Plant.{x.Humanize()}"))
26+
.ToArray();
27+
}
28+
}
29+
}
30+
}

generators/Input/CanonicalDataCase.cs

+8-2
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@ public class CanonicalDataCase
1313
private readonly HashSet<string> _constructorInputParameters = new HashSet<string>();
1414

1515
[Required]
16-
public string Description { get; set; }
16+
public string Property { get; set; }
1717

1818
[Required]
19-
public string Property { get; set; }
19+
public string Description { get; set; }
20+
21+
[JsonIgnore]
22+
public string[] DescriptionPath { get; set; }
2023

2124
[JsonIgnore]
2225
public IReadOnlyDictionary<string, dynamic> Input
@@ -48,6 +51,9 @@ public dynamic Expected
4851
[JsonIgnore]
4952
public bool UseVariableForTested { get; set; }
5053

54+
[JsonIgnore]
55+
public bool UseFullDescriptionPath { get; set; }
56+
5157
[JsonIgnore]
5258
public TestedMethodType TestedMethodType { get; set; }
5359

generators/Input/CanonicalDataCaseJsonConverter.cs

+21-4
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,36 @@ public class CanonicalDataCaseJsonConverter : JsonConverter
1414

1515
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
1616
{
17-
var jToken = JToken.ReadFrom(reader);
17+
var jTokenReader = (JTokenReader) reader;
1818

1919
var canonicalDataCase = new CanonicalDataCase();
20-
serializer.Populate(new JTokenReader(jToken), canonicalDataCase);
20+
serializer.Populate(jTokenReader, canonicalDataCase);
2121

22-
canonicalDataCase.Properties = CanonicalDataCaseJson.ToDictionary(jToken);
22+
canonicalDataCase.Properties = CanonicalDataCaseJson.ToDictionary(jTokenReader.CurrentToken);
2323
canonicalDataCase.SetInputParameters(GetInputProperties(canonicalDataCase.Properties));
24-
24+
canonicalDataCase.DescriptionPath = GetDescriptionPath(jTokenReader.CurrentToken);
25+
2526
return canonicalDataCase;
2627
}
2728

2829
private static string[] GetInputProperties(IDictionary<string, dynamic> properties) => properties.Keys.Except(NonInputProperties).ToArray();
2930

31+
private static string[] GetDescriptionPath(JToken canonicalDataCaseToken)
32+
{
33+
var descriptionPath = new Stack<string>();
34+
var currentToken = canonicalDataCaseToken;
35+
36+
while (currentToken != null)
37+
{
38+
if (currentToken.Type == JTokenType.Object)
39+
descriptionPath.Push(currentToken.SelectToken("description").ToObject<string>());
40+
41+
currentToken = currentToken.Parent;
42+
}
43+
44+
return descriptionPath.Where(x => !string.IsNullOrEmpty(x)).ToArray();
45+
}
46+
3047
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => throw new NotImplementedException();
3148
}
3249
}

generators/Input/CanonicalDataCasesJson.cs

+5-5
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ public static class CanonicalDataCasesJson
1111

1212
public static CanonicalDataCase[] ToArray(JToken casesToken)
1313
{
14-
var caseTokens = new JArray(casesToken.SelectTokens(TokensPath));
15-
var canonicalDataCases = new JArray(caseTokens).ToObject<CanonicalDataCase[]>();
14+
var caseTokens = casesToken.SelectTokens(TokensPath);
15+
var canonicalDataCases = caseTokens.Select(x => x.ToObject<CanonicalDataCase>()).ToArray();
1616

1717
ConvertEmptyJArrayToArray(canonicalDataCases);
1818

@@ -49,10 +49,10 @@ private static Type GetArrayType(IGrouping<string, KeyValuePair<string, dynamic>
4949
return null;
5050
}
5151

52-
private static IEnumerable<CanonicalDataCase> GetEmptyJArrays(IGrouping<string, CanonicalDataCase> groupedCanonicalDataCases, IGrouping<string, KeyValuePair<string, dynamic>> groupedProperties)
53-
=> groupedCanonicalDataCases.Where(canonicalDataCase => IsEmptyJArray(canonicalDataCase, groupedProperties));
52+
private static IEnumerable<CanonicalDataCase> GetEmptyJArrays(IGrouping<string, CanonicalDataCase> groupedCanonicalDataCases, IGrouping<string, KeyValuePair<string, dynamic>> groupedProperties)
53+
=> groupedCanonicalDataCases.Where(canonicalDataCase => canonicalDataCase.Properties.ContainsKey(groupedProperties.Key) && IsEmptyJArray(canonicalDataCase, groupedProperties));
5454

55-
private static bool IsEmptyJArray(CanonicalDataCase x, IGrouping<string, KeyValuePair<string, dynamic>> groupedProperties)
55+
private static bool IsEmptyJArray(CanonicalDataCase x, IGrouping<string, KeyValuePair<string, dynamic>> groupedProperties)
5656
=> x.Properties[groupedProperties.Key] is JArray jArray && jArray.Count == 0;
5757
}
5858
}

generators/Output/ValueFormatter.cs

+2
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ public static object Format(object val)
2828
return ints.Any() ? $"new[] {{ {string.Join(", ", ints)} }}" : "new int[0]";
2929
case IEnumerable<string> strings:
3030
return strings.Any() ? $"new[] {{ {string.Join(", ", strings.Select(Format) )} }}" : "new string[0]";
31+
case IEnumerable<UnescapedValue> unescapedValues when unescapedValues.Any():
32+
return $"new[] {{ {string.Join(", ", unescapedValues.Select(Format) )} }}";
3133
case double dbl:
3234
return dbl.ToString(CultureInfo.InvariantCulture);
3335
case float flt:

0 commit comments

Comments
 (0)