trackler 2.2.1.52 → 2.2.1.53

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/problem-specifications/exercises/leap/canonical-data.json +2 -2
  4. data/tracks/cfml/tasks/FormatConfig.cfc +6 -4
  5. data/tracks/cfml/tasks/GenerateReadme.cfc +197 -0
  6. data/tracks/cfml/tasks/ScaffoldExercise.cfc +6 -0
  7. data/tracks/csharp/config.json +1 -1
  8. data/tracks/delphi/exercises/leap/uLeapTests.pas +1 -1
  9. data/tracks/fsharp/exercises/clock/Clock.fs +1 -1
  10. data/tracks/fsharp/exercises/clock/ClockTest.fs +239 -61
  11. data/tracks/fsharp/exercises/clock/Example.fs +3 -3
  12. data/tracks/fsharp/exercises/dominoes/DominoesTest.fs +29 -26
  13. data/tracks/fsharp/exercises/largest-series-product/LargestSeriesProductTest.fs +30 -15
  14. data/tracks/fsharp/exercises/leap/LeapTest.fs +2 -2
  15. data/tracks/fsharp/exercises/rectangles/Example.fs +1 -1
  16. data/tracks/fsharp/exercises/rectangles/Rectangles.fs +1 -1
  17. data/tracks/fsharp/exercises/rectangles/RectanglesTest.fs +66 -43
  18. data/tracks/fsharp/generators/Generators.fs +85 -1
  19. data/tracks/java/scripts/canonical_data_check.sh +8 -0
  20. data/tracks/kotlin/.gitignore +1 -0
  21. data/tracks/kotlin/exercises/acronym/.meta/version +1 -0
  22. data/tracks/kotlin/exercises/acronym/src/test/kotlin/AcronymTest.kt +9 -10
  23. data/tracks/kotlin/exercises/all-your-base/.meta/version +1 -1
  24. data/tracks/kotlin/exercises/all-your-base/src/test/kotlin/BaseConverterTest.kt +20 -20
  25. data/tracks/kotlin/exercises/allergies/.meta/version +1 -0
  26. data/tracks/kotlin/exercises/allergies/src/test/kotlin/AllergiesTest.kt +19 -72
  27. data/tracks/kotlin/exercises/anagram/.meta/src/reference/kotlin/Anagram.kt +7 -4
  28. data/tracks/kotlin/exercises/anagram/.meta/version +1 -0
  29. data/tracks/kotlin/exercises/anagram/src/test/kotlin/AnagramTest.kt +76 -29
  30. data/tracks/kotlin/exercises/atbash-cipher/.meta/version +1 -0
  31. data/tracks/kotlin/exercises/atbash-cipher/src/test/kotlin/AtbashTest.kt +6 -3
  32. data/tracks/kotlin/exercises/beer-song/.meta/version +1 -0
  33. data/tracks/kotlin/exercises/beer-song/src/test/kotlin/BeerSongTest.kt +21 -46
  34. data/tracks/kotlin/exercises/binary-search/.meta/version +1 -0
  35. data/tracks/kotlin/exercises/binary-search/src/test/kotlin/BinarySearchTest.kt +30 -19
  36. data/tracks/kotlin/exercises/bob/.meta/src/reference/kotlin/Bob.kt +8 -7
  37. data/tracks/kotlin/exercises/bob/.meta/version +1 -0
  38. data/tracks/kotlin/exercises/bob/src/test/kotlin/BobTest.kt +57 -15
  39. data/tracks/kotlin/exercises/bracket-push/.meta/version +1 -0
  40. data/tracks/kotlin/exercises/bracket-push/src/test/kotlin/BracketPushTest.kt +3 -1
  41. data/tracks/kotlin/exercises/collatz-conjecture/.meta/version +1 -1
  42. data/tracks/kotlin/exercises/difference-of-squares/.meta/version +1 -0
  43. data/tracks/kotlin/exercises/difference-of-squares/src/test/kotlin/SquaresTest.kt +10 -23
  44. data/tracks/kotlin/exercises/etl/.meta/src/reference/kotlin/ETL.kt +7 -1
  45. data/tracks/kotlin/exercises/etl/.meta/version +1 -0
  46. data/tracks/kotlin/exercises/etl/src/test/kotlin/ETLTest.kt +10 -10
  47. data/tracks/kotlin/exercises/flatten-array/.meta/version +1 -0
  48. data/tracks/kotlin/exercises/flatten-array/src/test/kotlin/FlattenerTest.kt +8 -16
  49. data/tracks/kotlin/exercises/gigasecond/.meta/version +1 -0
  50. data/tracks/kotlin/exercises/gigasecond/src/test/kotlin/GigasecondTest.kt +1 -1
  51. data/tracks/kotlin/exercises/hamming/.meta/src/reference/kotlin/Hamming.kt +1 -1
  52. data/tracks/kotlin/exercises/hamming/.meta/version +1 -1
  53. data/tracks/kotlin/exercises/hamming/src/test/kotlin/HammingTest.kt +4 -4
  54. data/tracks/kotlin/exercises/hello-world/.meta/version +1 -0
  55. data/tracks/kotlin/exercises/hello-world/src/test/kotlin/HelloWorldTest.kt +1 -1
  56. data/tracks/kotlin/exercises/isogram/.meta/version +1 -0
  57. data/tracks/kotlin/exercises/isogram/src/test/kotlin/IsogramTest.kt +5 -8
  58. data/tracks/kotlin/exercises/largest-series-product/.meta/src/reference/kotlin/Series.kt +2 -3
  59. data/tracks/kotlin/exercises/largest-series-product/.meta/version +1 -0
  60. data/tracks/kotlin/exercises/largest-series-product/src/test/kotlin/SeriesTest.kt +97 -0
  61. data/tracks/kotlin/exercises/leap/.meta/version +1 -0
  62. data/tracks/kotlin/exercises/leap/src/test/kotlin/LeapTest.kt +8 -26
  63. data/tracks/kotlin/exercises/list-ops/.meta/version +1 -1
  64. data/tracks/kotlin/exercises/luhn/.meta/version +1 -0
  65. data/tracks/kotlin/exercises/luhn/src/test/kotlin/LuhnTest.kt +0 -3
  66. data/tracks/kotlin/exercises/nth-prime/.meta/version +1 -0
  67. data/tracks/kotlin/exercises/nth-prime/src/test/kotlin/PrimeTest.kt +0 -2
  68. data/tracks/kotlin/exercises/nucleotide-count/.meta/src/reference/kotlin/{DNA.kt → Dna.kt} +3 -8
  69. data/tracks/kotlin/exercises/nucleotide-count/.meta/version +1 -0
  70. data/tracks/kotlin/exercises/nucleotide-count/src/test/kotlin/DnaTest.kt +48 -0
  71. data/tracks/kotlin/exercises/pangram/.meta/src/reference/kotlin/{Pangrams.kt → Pangram.kt} +1 -1
  72. data/tracks/kotlin/exercises/pangram/.meta/version +1 -1
  73. data/tracks/kotlin/exercises/pangram/src/test/kotlin/PangramTest.kt +15 -9
  74. data/tracks/kotlin/exercises/pascals-triangle/.meta/version +1 -0
  75. data/tracks/kotlin/exercises/pascals-triangle/src/test/kotlin/PascalsTriangleTest.kt +32 -14
  76. data/tracks/kotlin/exercises/perfect-numbers/.meta/src/reference/kotlin/NaturalNumber.kt +1 -1
  77. data/tracks/kotlin/exercises/perfect-numbers/.meta/version +1 -0
  78. data/tracks/kotlin/exercises/perfect-numbers/src/test/kotlin/{PerfectNumbersTest.kt → NaturalNumberTest.kt} +25 -7
  79. data/tracks/kotlin/exercises/phone-number/.meta/src/reference/kotlin/PhoneNumber.kt +15 -7
  80. data/tracks/kotlin/exercises/phone-number/.meta/version +1 -0
  81. data/tracks/kotlin/exercises/phone-number/src/test/kotlin/PhoneNumberTest.kt +46 -29
  82. data/tracks/kotlin/exercises/pig-latin/.meta/src/reference/kotlin/PigLatin.kt +2 -2
  83. data/tracks/kotlin/exercises/pig-latin/.meta/version +1 -0
  84. data/tracks/kotlin/exercises/pig-latin/src/test/kotlin/PigLatinTest.kt +21 -10
  85. data/tracks/kotlin/exercises/raindrops/.meta/version +1 -0
  86. data/tracks/kotlin/exercises/raindrops/src/test/kotlin/RaindropsTest.kt +19 -26
  87. data/tracks/kotlin/exercises/react/.meta/version +1 -0
  88. data/tracks/kotlin/exercises/react/src/test/kotlin/ReactTest.kt +11 -4
  89. data/tracks/kotlin/exercises/robot-simulator/.meta/version +1 -1
  90. data/tracks/kotlin/exercises/roman-numerals/.meta/version +1 -0
  91. data/tracks/kotlin/exercises/roman-numerals/src/test/kotlin/RomanNumeralTest.kt +1 -2
  92. data/tracks/kotlin/exercises/rotational-cipher/.meta/version +1 -1
  93. data/tracks/kotlin/exercises/rotational-cipher/src/test/kotlin/RotationalCipherTest.kt +7 -7
  94. data/tracks/kotlin/exercises/scrabble-score/.meta/src/reference/kotlin/{Scrabble.kt → ScrabbleScore.kt} +1 -1
  95. data/tracks/kotlin/exercises/scrabble-score/.meta/version +1 -0
  96. data/tracks/kotlin/exercises/scrabble-score/src/test/kotlin/ScrabbleScoreTest.kt +9 -7
  97. data/tracks/kotlin/exercises/sieve/.meta/version +1 -0
  98. data/tracks/kotlin/exercises/sieve/src/test/kotlin/SieveTest.kt +8 -0
  99. data/tracks/kotlin/exercises/space-age/.meta/version +1 -0
  100. data/tracks/kotlin/exercises/space-age/src/test/kotlin/SpaceAgeTest.kt +1 -16
  101. data/tracks/kotlin/exercises/sum-of-multiples/.meta/version +1 -0
  102. data/tracks/kotlin/exercises/sum-of-multiples/src/test/kotlin/SumOfMultiplesTest.kt +7 -7
  103. data/tracks/kotlin/exercises/triangle/.meta/version +1 -0
  104. data/tracks/kotlin/exercises/two-fer/.meta/version +1 -0
  105. data/tracks/kotlin/exercises/two-fer/src/test/kotlin/TwoferTest.kt +0 -1
  106. data/tracks/kotlin/exercises/word-count/.meta/src/reference/kotlin/WordCount.kt +6 -2
  107. data/tracks/kotlin/exercises/word-count/.meta/version +1 -0
  108. data/tracks/kotlin/exercises/word-count/src/test/kotlin/WordCountTest.kt +31 -9
  109. data/tracks/kotlin/scripts/canonical_data_check.sh +8 -0
  110. data/tracks/perl6/exercises/leap/leap.t +2 -2
  111. data/tracks/php/config.json +16 -0
  112. data/tracks/php/exercises/crypto-square/crypto-square_test.php +50 -0
  113. data/tracks/php/exercises/crypto-square/example.php +32 -0
  114. data/tracks/php/exercises/meetup/README.md +21 -0
  115. data/tracks/php/exercises/meetup/example.php +19 -0
  116. data/tracks/php/exercises/meetup/meetup_test.php +577 -0
  117. data/tracks/python/README.md +7 -5
  118. data/tracks/python/config.json +36 -27
  119. data/tracks/python/docs/TESTS.md +36 -12
  120. data/tracks/python/exercises/complex-numbers/complex_numbers_test.py +1 -1
  121. data/tracks/python/exercises/diffie-hellman/.meta/hints.md +16 -0
  122. data/tracks/python/exercises/diffie-hellman/README.md +15 -2
  123. data/tracks/python/exercises/error-handling/README.md +23 -0
  124. data/tracks/python/exercises/error-handling/error_handling.py +14 -0
  125. data/tracks/python/exercises/error-handling/error_handling_test.py +66 -0
  126. data/tracks/python/exercises/error-handling/example.py +21 -0
  127. data/tracks/python/exercises/forth/forth_test.py +1 -1
  128. data/tracks/python/exercises/isogram/isogram_test.py +5 -3
  129. data/tracks/python/exercises/leap/leap_test.py +2 -2
  130. data/tracks/python/exercises/rotational-cipher/rotational_cipher_test.py +4 -4
  131. data/tracks/python/exercises/simple-cipher/.meta/hints.md +16 -0
  132. data/tracks/python/exercises/simple-cipher/README.md +17 -0
  133. data/tracks/python/exercises/sum-of-multiples/sum_of_multiples_test.py +4 -1
  134. data/tracks/python/exercises/twelve-days/example.py +3 -3
  135. data/tracks/python/exercises/twelve-days/twelve_days.py +2 -2
  136. metadata +53 -9
  137. data/tracks/kotlin/exercises/largest-series-product/src/test/kotlin/SeriesInvalidInputTest.kt +0 -30
  138. data/tracks/kotlin/exercises/largest-series-product/src/test/kotlin/SeriesValidInputTest.kt +0 -34
  139. data/tracks/kotlin/exercises/nucleotide-count/src/test/kotlin/NucleotideTest.kt +0 -87
@@ -1,11 +1,9 @@
1
1
  import org.junit.Test
2
2
  import org.junit.Ignore
3
3
  import kotlin.test.assertEquals
4
- import kotlin.test.fail
5
4
 
6
5
  class PrimeTest {
7
6
 
8
-
9
7
  @Test
10
8
  fun firstPrime() {
11
9
  assertEquals(2, Prime.nth(1))
@@ -1,9 +1,10 @@
1
- class DNA(strand: String) {
1
+ class Dna(strand: String) {
2
+
2
3
  init {
3
4
  require(strand.matches(Regex("^[$nucleotides]*$")), { "DNA sequence contains invalid nucleotides sequence." })
4
5
  }
5
6
 
6
- companion object DNA {
7
+ companion object Dna {
7
8
  val nucleotides = "ATCG"
8
9
  val emptyNucleotideCounts = nucleotides.map { it to 0 }.toMap()
9
10
  }
@@ -12,10 +13,4 @@ class DNA(strand: String) {
12
13
  emptyNucleotideCounts + strand.groupBy { it }.mapValues { it.value.size }
13
14
  }
14
15
 
15
- fun count(nucleotide: Char): Int {
16
- require(nucleotide in nucleotides, { "$nucleotide is not a nucleotide" })
17
-
18
- return nucleotideCounts[nucleotide] ?: 0
19
- }
20
16
  }
21
-
@@ -0,0 +1,48 @@
1
+ import org.junit.Test
2
+ import org.junit.Ignore
3
+ import kotlin.test.assertEquals
4
+
5
+ class DnaTest {
6
+
7
+ @Test
8
+ fun emptyDnaStringHasNoNucleotides() {
9
+ val dna = Dna("")
10
+ val expected = mapOf('A' to 0, 'C' to 0, 'G' to 0, 'T' to 0)
11
+
12
+ assertEquals(expected, dna.nucleotideCounts)
13
+ }
14
+
15
+ @Ignore
16
+ @Test
17
+ fun canCountOneNucleotideInSingleCharacterInput() {
18
+ val dna = Dna("G")
19
+ val expected = mapOf('A' to 0, 'C' to 0, 'G' to 1, 'T' to 0)
20
+
21
+ assertEquals(expected, dna.nucleotideCounts)
22
+ }
23
+
24
+ @Ignore
25
+ @Test
26
+ fun canCountRepeatedNucleotide() {
27
+ val dna = Dna("GGGGGGG")
28
+ val expected = mapOf('A' to 0, 'C' to 0, 'G' to 7, 'T' to 0)
29
+
30
+ assertEquals(expected, dna.nucleotideCounts)
31
+ }
32
+
33
+ @Ignore
34
+ @Test
35
+ fun canCountMultipleNucleotides() {
36
+ val dna = Dna("AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC")
37
+ val expected = mapOf('A' to 20, 'C' to 12, 'G' to 17, 'T' to 21)
38
+
39
+ assertEquals(expected, dna.nucleotideCounts)
40
+ }
41
+
42
+ @Ignore
43
+ @Test(expected = IllegalArgumentException::class)
44
+ fun validatesNucleotides() {
45
+ Dna("AGXXACT")
46
+ }
47
+
48
+ }
@@ -1,4 +1,4 @@
1
- object Pangrams {
1
+ object Pangram {
2
2
  val alphaLength = 26
3
3
  fun isPangram(input: String) = input.toLowerCase().replace(Regex("[^a-z]"), "").toSet().size == alphaLength
4
4
  }
@@ -1 +1 @@
1
- 1.1.0
1
+ 1.3.0
@@ -7,55 +7,61 @@ class PangramTest {
7
7
 
8
8
  @Test
9
9
  fun emptySentence() {
10
- assertFalse(Pangrams.isPangram(""))
10
+ assertFalse(Pangram.isPangram(""))
11
+ }
12
+
13
+ @Ignore
14
+ @Test
15
+ fun perfectLowercasePangram() {
16
+ assertTrue(Pangram.isPangram("abcdefghijklmnopqrstuvwxyz"))
11
17
  }
12
18
 
13
19
  @Ignore
14
20
  @Test
15
21
  fun lowercasePangram() {
16
- assertTrue(Pangrams.isPangram("the quick brown fox jumps over the lazy dog"))
22
+ assertTrue(Pangram.isPangram("the quick brown fox jumps over the lazy dog"))
17
23
  }
18
24
 
19
25
  @Ignore
20
26
  @Test
21
27
  fun missingCharacterX() {
22
- assertFalse(Pangrams.isPangram("a quick movement of the enemy will jeopardize five gunboats"))
28
+ assertFalse(Pangram.isPangram("a quick movement of the enemy will jeopardize five gunboats"))
23
29
  }
24
30
 
25
31
  @Ignore
26
32
  @Test
27
33
  fun anotherMissingCharacterX() {
28
- assertFalse(Pangrams.isPangram("the quick brown fish jumps over the lazy dog"))
34
+ assertFalse(Pangram.isPangram("five boxing wizards jump quickly at it"))
29
35
  }
30
36
 
31
37
  @Ignore
32
38
  @Test
33
39
  fun withUnderscores() {
34
- assertTrue(Pangrams.isPangram("the_quick_brown_fox_jumps_over_the_lazy_dog"))
40
+ assertTrue(Pangram.isPangram("the_quick_brown_fox_jumps_over_the_lazy_dog"))
35
41
  }
36
42
 
37
43
  @Ignore
38
44
  @Test
39
45
  fun withNumbers() {
40
- assertTrue(Pangrams.isPangram("the 1 quick brown fox jumps over the 2 lazy dogs"))
46
+ assertTrue(Pangram.isPangram("the 1 quick brown fox jumps over the 2 lazy dogs"))
41
47
  }
42
48
 
43
49
  @Ignore
44
50
  @Test
45
51
  fun missingLettersReplacedByNumbers() {
46
- assertFalse(Pangrams.isPangram("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog"))
52
+ assertFalse(Pangram.isPangram("7h3 qu1ck brown fox jumps ov3r 7h3 lazy dog"))
47
53
  }
48
54
 
49
55
  @Ignore
50
56
  @Test
51
57
  fun mixedCaseAndPunctuation() {
52
- assertTrue(Pangrams.isPangram("\"Five quacking Zephyrs jolt my wax bed.\""))
58
+ assertTrue(Pangram.isPangram("\"Five quacking Zephyrs jolt my wax bed.\""))
53
59
  }
54
60
 
55
61
  @Ignore
56
62
  @Test
57
63
  fun mixedCaseDuplicatedCharacters() {
58
- assertFalse(Pangrams.isPangram("the quick brown fox jumps over with lazy FX"))
64
+ assertFalse(Pangram.isPangram("the quick brown fox jumps over with lazy FX"))
59
65
  }
60
66
 
61
67
  }
@@ -4,40 +4,57 @@ import kotlin.test.assertEquals
4
4
 
5
5
  class PascalsTriangleTest {
6
6
 
7
+ @Test
8
+ fun zeroRows() {
9
+ val expectedOutput = emptyList<List<Int>>()
10
+
11
+ assertEquals(expectedOutput, PascalsTriangle.computeTriangle(0))
12
+ }
13
+
14
+ @Ignore
15
+ @Test
16
+ fun oneRow() {
17
+ val expectedOutput = listOf(
18
+ listOf(1)
19
+ )
7
20
 
21
+ assertEquals(expectedOutput, PascalsTriangle.computeTriangle(1))
22
+ }
23
+
24
+ @Ignore
8
25
  @Test
9
- fun triangleWithFourRows() {
26
+ fun twoRows() {
10
27
  val expectedOutput = listOf(
11
28
  listOf(1),
12
- listOf(1, 1),
13
- listOf(1, 2, 1),
14
- listOf(1, 3, 3, 1)
29
+ listOf(1, 1)
15
30
  )
16
31
 
17
- assertEquals(expectedOutput, PascalsTriangle.computeTriangle(4))
32
+ assertEquals(expectedOutput, PascalsTriangle.computeTriangle(2))
18
33
  }
19
34
 
20
35
  @Ignore
21
36
  @Test
22
- fun triangleWithSixRows() {
37
+ fun threeRows() {
23
38
  val expectedOutput = listOf(
24
39
  listOf(1),
25
40
  listOf(1, 1),
26
- listOf(1, 2, 1),
27
- listOf(1, 3, 3, 1),
28
- listOf(1, 4, 6, 4, 1),
29
- listOf(1, 5, 10, 10, 5, 1)
41
+ listOf(1, 2, 1)
30
42
  )
31
43
 
32
- assertEquals(expectedOutput, PascalsTriangle.computeTriangle(6))
44
+ assertEquals(expectedOutput, PascalsTriangle.computeTriangle(3))
33
45
  }
34
46
 
35
47
  @Ignore
36
48
  @Test
37
- fun expectEmptyTriangle() {
38
- val expectedOutput = emptyList<List<Int>>()
49
+ fun fourRows() {
50
+ val expectedOutput = listOf(
51
+ listOf(1),
52
+ listOf(1, 1),
53
+ listOf(1, 2, 1),
54
+ listOf(1, 3, 3, 1)
55
+ )
39
56
 
40
- assertEquals(expectedOutput, PascalsTriangle.computeTriangle(0))
57
+ assertEquals(expectedOutput, PascalsTriangle.computeTriangle(4))
41
58
  }
42
59
 
43
60
  @Ignore
@@ -45,4 +62,5 @@ class PascalsTriangleTest {
45
62
  fun validatesNotNegativeRows() {
46
63
  PascalsTriangle.computeTriangle(-1)
47
64
  }
65
+
48
66
  }
@@ -4,7 +4,7 @@ enum class Classification {
4
4
  }
5
5
 
6
6
  fun classify(naturalNumber: Int): Classification {
7
- require(naturalNumber >= 0, { "$naturalNumber is not a natural number" })
7
+ require(naturalNumber > 0, { "$naturalNumber is not a natural number" })
8
8
 
9
9
  val aliquotSum = naturalNumber.aliquotSum()
10
10
  return when {
@@ -31,7 +31,7 @@ class NaturalNumberTest {
31
31
  @Ignore
32
32
  @Test
33
33
  fun mediumAbundantNumberIsClassifiedCorrectly() {
34
- assertEquals(Classification.ABUNDANT, classify(24))
34
+ assertEquals(Classification.ABUNDANT, classify(30))
35
35
  }
36
36
 
37
37
  @Ignore
@@ -42,14 +42,20 @@ class NaturalNumberTest {
42
42
 
43
43
  @Ignore
44
44
  @Test
45
- fun smallDeficientNumberIsClassifiedCorrectly() {
46
- assertEquals(Classification.DEFICIENT, classify(8))
45
+ fun smallestPrimeDeficientNumberIsClassifiedCorrectly() {
46
+ assertEquals(Classification.DEFICIENT, classify(2))
47
+ }
48
+
49
+ @Ignore
50
+ @Test
51
+ fun smallestNonPrimeDeficientNumberIsClassifiedCorrectly() {
52
+ assertEquals(Classification.DEFICIENT, classify(4))
47
53
  }
48
54
 
49
55
  @Ignore
50
56
  @Test
51
57
  fun mediumNumberIsClassifiedCorrectly() {
52
- assertEquals(Classification.DEFICIENT, classify(31))
58
+ assertEquals(Classification.DEFICIENT, classify(32))
53
59
  }
54
60
 
55
61
  @Ignore
@@ -58,10 +64,22 @@ class NaturalNumberTest {
58
64
  assertEquals(Classification.DEFICIENT, classify(33550337))
59
65
  }
60
66
 
67
+ @Ignore
68
+ @Test
69
+ fun edgeCaseWithNoFactorsOtherThanItselfIsClassifiedCorrectly() {
70
+ assertEquals(Classification.DEFICIENT, classify(1))
71
+ }
72
+
73
+ @Ignore
74
+ @Test(expected = RuntimeException::class)
75
+ fun zeroIsNotANaturalNumber() {
76
+ classify(0)
77
+ }
78
+
61
79
  @Ignore
62
80
  @Test(expected = RuntimeException::class)
63
- fun mustProvideNaturalNumber() {
64
- assertEquals(Classification.DEFICIENT, classify(-1))
81
+ fun negativeNumberIsNotANaturalNumber() {
82
+ classify(-1)
65
83
  }
66
84
 
67
- }
85
+ }
@@ -1,13 +1,21 @@
1
1
  data class PhoneNumber(private val rawNumber: String) {
2
+
2
3
  companion object {
3
- private val validationRegex = Regex("^1?([2-9]\\d{2}[2-9]\\d{6})$")
4
+ private val invalidChars = Regex("[^\\d() -.]")
5
+ private val digitsValidationRegex = Regex("^1?([2-9]\\d{2}[2-9]\\d{6})$")
4
6
  }
5
- private val cleanedNumber = rawNumber.replace(Regex("[^\\d]"), "")
6
7
 
7
- val number = validationRegex.matchEntire(cleanedNumber)?.groupValues?.last() ?: throw IllegalArgumentException()
8
- val areaCode = number.substring(0, 3)
9
- val exchangeCode = number.substring(3, 6)
10
- val subscriberNumber = number.substring(6, 10)
8
+ val number: String?
9
+
10
+ init {
11
+ val containsInvalidChar = invalidChars.containsMatchIn(rawNumber)
12
+
13
+ if (containsInvalidChar) {
14
+ number = null
15
+ } else {
16
+ val digits = rawNumber.replace(Regex("[^\\d]"), "")
17
+ number = digitsValidationRegex.matchEntire(digits)?.groupValues?.last()
18
+ }
19
+ }
11
20
 
12
- override fun toString() = "($areaCode) $exchangeCode-$subscriberNumber"
13
21
  }
@@ -1,11 +1,10 @@
1
- import org.junit.Test
2
1
  import org.junit.Ignore
2
+ import org.junit.Test
3
3
  import kotlin.test.assertEquals
4
- import kotlin.test.fail
4
+ import kotlin.test.assertNull
5
5
 
6
6
  class PhoneNumberTest {
7
7
 
8
-
9
8
  @Test
10
9
  fun cleansNumber() {
11
10
  val expectedNumber = "2234567890"
@@ -25,62 +24,80 @@ class PhoneNumberTest {
25
24
 
26
25
  @Ignore
27
26
  @Test
28
- fun validWhen11DigitsAndFirstIs1() {
27
+ fun cleansNumberWithMultipleSpaces() {
29
28
  val expectedNumber = "2234567890"
30
- val actualNumber = PhoneNumber("12234567890").number
29
+ val actualNumber = PhoneNumber("223 456 7890 ").number
31
30
 
32
31
  assertEquals(expectedNumber, actualNumber)
33
32
  }
34
33
 
35
34
  @Ignore
36
- @Test(expected = IllegalArgumentException::class)
37
- fun invalidWhen10DigitsAndFirstIs1() {
38
- val actualNumber = PhoneNumber("1234567890")
35
+ @Test
36
+ fun invalidWhen9Digits() {
37
+ assertNull(PhoneNumber("123456789").number)
38
+ }
39
+
40
+ @Ignore
41
+ @Test
42
+ fun invalidWhen11DigitsAndFirstIsNot1() {
43
+ assertNull(PhoneNumber("22234567890").number)
44
+ }
45
+
46
+ @Ignore
47
+ @Test
48
+ fun validWhen11DigitsAndFirstIs1() {
49
+ val expectedNumber = "2234567890"
50
+ val actualNumber = PhoneNumber("12234567890").number
39
51
 
40
- fail("IllegalArgumentException should have been thrown")
52
+ assertEquals(expectedNumber, actualNumber)
41
53
  }
42
54
 
43
55
  @Ignore
44
- @Test(expected = IllegalArgumentException::class)
45
- fun invalidWhenOnly11Digits() {
46
- val actualNumber = PhoneNumber("21234567890")
56
+ @Test
57
+ fun validWhen11DigitsAndFirstIs1WithPunctuation() {
58
+ val expectedNumber = "2234567890"
59
+ val actualNumber = PhoneNumber("+1 (223) 456-7890").number
47
60
 
48
- fail("IllegalArgumentException should have been thrown")
61
+ assertEquals(expectedNumber, actualNumber)
49
62
  }
50
63
 
51
64
  @Ignore
52
- @Test(expected = IllegalArgumentException::class)
53
- fun invalidWhen9Digits() {
54
- val actualNumber = PhoneNumber("223456789")
65
+ @Test
66
+ fun invalidWhenMoreThan11Digits() {
67
+ val expectedNumber = null
68
+ val actualNumber = PhoneNumber("321234567890").number
55
69
 
56
- fail("IllegalArgumentException should have been thrown")
70
+ assertEquals(expectedNumber, actualNumber)
57
71
  }
58
72
 
59
73
  @Ignore
60
74
  @Test
61
- fun areaCode() {
62
- val expectedAreaCode = "223"
63
- val actualAreaCode = PhoneNumber("2234567890").areaCode
75
+ fun invalidWithLetters() {
76
+ val expectedNumber = null
77
+ val actualNumber = PhoneNumber("123-abc-7890").number
64
78
 
65
- assertEquals(expectedAreaCode, actualAreaCode)
79
+ assertEquals(expectedNumber, actualNumber)
66
80
  }
67
81
 
68
82
  @Ignore
69
83
  @Test
70
- fun toStringPrint() {
71
- val expectedtoStringNumber = "(223) 456-7890"
72
- val actualtoStringNumber = PhoneNumber("2234567890").toString()
84
+ fun invalidWithInvalidPunctuation() {
85
+ val expectedNumber = null
86
+ val actualNumber = PhoneNumber("123-@:!-7890").number
73
87
 
74
- assertEquals(expectedtoStringNumber, actualtoStringNumber)
88
+ assertEquals(expectedNumber, actualNumber)
75
89
  }
76
90
 
77
91
  @Ignore
78
92
  @Test
79
- fun toStringPrintWithFullUSPhoneNumber() {
80
- val expectedtoStringNumber = "(223) 456-7890"
81
- val actualtoStringNumber = PhoneNumber("12234567890").toString()
93
+ fun invalidWhenAreaCodeStartsWith0or1() {
94
+ assertNull(PhoneNumber("(123) 456-7890").number)
95
+ }
82
96
 
83
- assertEquals(expectedtoStringNumber, actualtoStringNumber)
97
+ @Ignore
98
+ @Test
99
+ fun invalidWhenExchangeCodeStartsWith0or1() {
100
+ assertNull(PhoneNumber("(223) 056-7890").number)
84
101
  }
85
102
 
86
103
  }