trackler 2.2.1.36 → 2.2.1.37

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/lib/trackler/version.rb +1 -1
  3. data/tracks/bash/config.json +1 -0
  4. data/tracks/ecmascript/README.md +2 -2
  5. data/tracks/haskell/config/maintainers.json +2 -2
  6. data/tracks/java/config.json +15 -3
  7. data/tracks/java/exercises/difference-of-squares/src/test/java/DifferenceOfSquaresCalculatorTest.java +9 -9
  8. data/tracks/ruby/exercises/palindrome-products/README.md +12 -6
  9. data/tracks/ruby/exercises/space-age/.meta/generator/test_template.erb +1 -1
  10. data/tracks/ruby/lib/generator/template_values.rb +6 -0
  11. data/tracks/ruby/lib/generator/test_template.erb +1 -1
  12. data/tracks/ruby/test/generator/template_values_test.rb +3 -3
  13. data/tracks/rust/exercises/accumulate/.gitignore +7 -0
  14. data/tracks/rust/exercises/clock/.gitignore +7 -0
  15. data/tracks/rust/exercises/nth-prime/.gitignore +7 -0
  16. data/tracks/rust/exercises/pig-latin/.gitignore +7 -0
  17. data/tracks/rust/exercises/prime-factors/.gitignore +7 -0
  18. data/tracks/rust/exercises/proverb/.gitignore +7 -0
  19. data/tracks/rust/exercises/pythagorean-triplet/.gitignore +7 -0
  20. data/tracks/rust/exercises/run-length-encoding/.gitignore +7 -0
  21. data/tracks/rust/exercises/say/.gitignore +7 -0
  22. data/tracks/scala/config.json +1 -0
  23. data/tracks/scala/exercises/clock/src/test/scala/ClockTest.scala +73 -79
  24. data/tracks/scala/exercises/forth/src/test/scala/ForthTest.scala +128 -133
  25. data/tracks/scala/exercises/kindergarten-garden/example.scala +1 -1
  26. data/tracks/scala/exercises/kindergarten-garden/src/test/scala/GardenTest.scala +77 -52
  27. data/tracks/scala/exercises/nucleotide-count/src/main/scala/.keep +0 -0
  28. data/tracks/scala/exercises/nucleotide-count/src/test/scala/NucleotideCountTest.scala +21 -61
  29. data/tracks/scala/exercises/palindrome-products/example.scala +7 -4
  30. data/tracks/scala/exercises/palindrome-products/src/test/scala/PalindromeProductsTest.scala +54 -34
  31. data/tracks/scala/testgen/src/main/scala/ClockTestGenerator.scala +66 -0
  32. data/tracks/scala/testgen/src/main/scala/ForthTestGenerator.scala +66 -0
  33. data/tracks/scala/testgen/src/main/scala/KindergartenGardenTestGenerator.scala +48 -0
  34. data/tracks/scala/testgen/src/main/scala/NucleotideCountTestGenerator.scala +34 -0
  35. data/tracks/scala/testgen/src/main/scala/PalindromeProductsTestGenerator.scala +50 -0
  36. data/tracks/scala/testgen/src/main/scala/testgen/CanonicalDataParser.scala +7 -6
  37. data/tracks/sml/config.json +21 -0
  38. data/tracks/sml/exercises/phone-number/README.md +64 -0
  39. data/tracks/sml/exercises/phone-number/example.sml +18 -0
  40. data/tracks/sml/exercises/phone-number/phone-number.sml +2 -0
  41. data/tracks/sml/exercises/phone-number/test.sml +50 -0
  42. data/tracks/sml/exercises/phone-number/testlib.sml +159 -0
  43. data/tracks/sml/exercises/roman-numerals/README.md +79 -0
  44. data/tracks/sml/exercises/roman-numerals/example.sml +27 -0
  45. data/tracks/sml/exercises/roman-numerals/roman-numerals.sml +2 -0
  46. data/tracks/sml/exercises/roman-numerals/test.sml +66 -0
  47. data/tracks/sml/exercises/roman-numerals/testlib.sml +159 -0
  48. metadata +26 -3
  49. data/tracks/scala/exercises/nucleotide-count/src/main/scala/DNA.scala +0 -6
@@ -1,74 +1,34 @@
1
- import org.scalatest._
1
+ import org.scalatest.{Matchers, FunSuite}
2
2
 
3
- class NucleotideCountSpecs extends FlatSpec with Matchers {
4
- behavior of "count"
3
+ /** @version 1.0.0 */
4
+ class NucleotideCountTest extends FunSuite with Matchers {
5
5
 
6
- it should "have no adenine for an empty dna string" in {
7
- new DNA("").count('A') should be (Right(0))
8
- }
9
-
10
- it should "count cytosine for a repetitive sequence" in {
11
- pending
12
- new DNA("CCCCC").count('C') should be (Right(5))
13
- }
14
-
15
- it should "count only thymine for a mixed dna string" in {
16
- pending
17
- new DNA("GGGGGTAACCCGG").count('T') should be (Right(1))
18
- }
19
-
20
- it should "validate nucleotides" in {
21
- pending
22
- new DNA("GACT").count('X') should be (Left("invalid nucleotide 'X'"))
23
- }
24
-
25
- it should "validate dna" in {
26
- pending
27
- new DNA("GACYT").count('G') should be (Left("invalid nucleotide 'Y'"))
28
- }
29
-
30
- behavior of "nucleotideCounts"
31
6
 
32
- it should "have no nucleotides" in {
33
- pending
34
- val expected = Right(Map('A' -> 0, 'T' -> 0, 'C' -> 0, 'G' -> 0))
35
- new DNA("").nucleotideCounts should be (expected)
7
+ test("empty strand") {
8
+ new DNA("").nucleotideCounts should be (Right(Map('A' -> 0,
9
+ 'C' -> 0,
10
+ 'G' -> 0,
11
+ 'T' -> 0)))
36
12
  }
37
13
 
38
- it should "have only guanine" in {
14
+ test("strand with repeated nucleotide") {
39
15
  pending
40
- val expected = Right(Map('A' -> 0, 'T' -> 0, 'C' -> 0, 'G' -> 8))
41
- new DNA("GGGGGGGG").nucleotideCounts should be (expected)
16
+ new DNA("GGGGGGG").nucleotideCounts should be (Right(Map('A' -> 0,
17
+ 'C' -> 0,
18
+ 'G' -> 7,
19
+ 'T' -> 0)))
42
20
  }
43
21
 
44
- it should "count a nucleotide only once" in {
22
+ test("strand with multiple nucleotides") {
45
23
  pending
46
- val dna = new DNA("CGATTGGG")
47
- val counts = dna.nucleotideCounts.right.get
48
- counts('T')
49
- counts('T') should be (2)
24
+ new DNA("AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC").nucleotideCounts should be (Right(Map('A' -> 20,
25
+ 'C' -> 12,
26
+ 'G' -> 17,
27
+ 'T' -> 21)))
50
28
  }
51
29
 
52
- it should "not change counts after counting adenine" in {
30
+ test("strand with invalid nucleotides") {
53
31
  pending
54
- val dna = new DNA("GATTACA")
55
- val counts = dna.nucleotideCounts.right.get
56
- counts('A')
57
- val expected = Right(Map('A' -> 3, 'T' -> 2, 'C' -> 1, 'G' -> 1))
58
- dna.nucleotideCounts should be (expected)
32
+ new DNA("AGXXACT").nucleotideCounts.isLeft should be (true)
59
33
  }
60
-
61
- it should "count all nucleotides" in {
62
- pending
63
- val s = "AGCTTTTCATTCTGACTGCAACGGGCAATATGTCTCTGTGTGGATTAAAAAAAGAGTGTCTGATAGCAGC"
64
- val dna = new DNA(s)
65
- val expected = Right(Map('A' -> 20, 'T' -> 21, 'G' -> 17, 'C' -> 12))
66
- dna.nucleotideCounts should be (expected)
67
- }
68
-
69
- it should "validate dna" in {
70
- pending
71
- new DNA("GACYT").nucleotideCounts should be (Left("invalid nucleotide 'Y'"))
72
- }
73
- }
74
-
34
+ }
@@ -1,12 +1,15 @@
1
1
 
2
2
  case class PalindromeProducts(minFactor: Int, maxFactor: Int) {
3
3
 
4
- lazy val (smallest: (Int, Set[(Int, Int)]),
5
- largest: (Int, Set[(Int, Int)])) = {
4
+ lazy val (smallest: Option[(Int, Set[(Int, Int)])],
5
+ largest: Option[(Int, Set[(Int, Int)])]) = {
6
6
  val palindromes = for (a <- Range(minFactor, maxFactor + 1);
7
- b <- Range(a, maxFactor + 1) if isPalindrome(a * b)) yield (a * b, (a, b))
7
+ b <- Range(a, maxFactor + 1) if isPalindrome(a * b)) yield (a * b, (a, b))
8
8
  val mapped = palindromes.groupBy(_._1).map{case(k, v) => (k, v.map(_._2).toSet)}
9
- (mapped.minBy(_._1), mapped.maxBy(_._1))
9
+ if (mapped.isEmpty)
10
+ (None, None)
11
+ else
12
+ (Some(mapped.minBy(_._1)), Some(mapped.maxBy(_._1)))
10
13
  }
11
14
 
12
15
  private def isPalindrome(i: Int) = i.toString == i.toString.reverse
@@ -1,59 +1,79 @@
1
- import org.scalatest.{Matchers, FlatSpec}
1
+ import org.scalatest.{Matchers, FunSuite}
2
2
 
3
- class PalindromeProductsTest extends FlatSpec with Matchers {
3
+ /** @version 1.0.0 */
4
+ class PalindromeProductsTest extends FunSuite with Matchers {
4
5
 
5
- it should "find smallest palindrome from a single digit factors" in {
6
- val (value, factors) = PalindromeProducts(1, 9).smallest
7
- value should be (1)
8
- factors should be (Set((1, 1)))
6
+ // PalindromeProducts largest call is expecting a return type of
7
+ // Option[(Int, Set[(Int, Int)])] - None is expected for error cases.
8
+ // Some is expected for valid cases. The first element of the tuple
9
+ // is the largest palindrome product value. The second element of the
10
+ // tuple is the list of factors.
11
+ // PalindromeProducts smallest call is expecting a return type of
12
+ // Option[(Int, Set[(Int, Int)])] - None is expected for error cases.
13
+ // Some is expected for valid cases. The first element of the tuple
14
+ // is the smallest palindrome product value. The second element of the
15
+ // tuple is the list of factors.
16
+
17
+ test("finds the smallest palindrome from single digit factors") {
18
+ PalindromeProducts(1, 9).smallest should be(Some((1, Set((1, 1)))))
19
+ }
20
+
21
+ test("finds the largest palindrome from single digit factors") {
22
+ pending
23
+ PalindromeProducts(1, 9).largest should be(Some((9, Set((1, 9), (3, 3)))))
24
+ }
25
+
26
+ test("find the smallest palindrome from double digit factors") {
27
+ pending
28
+ PalindromeProducts(10, 99).smallest should be(Some((121, Set((11, 11)))))
29
+ }
30
+
31
+ test("find the largest palindrome from double digit factors") {
32
+ pending
33
+ PalindromeProducts(10, 99).largest should be(Some((9009, Set((91, 99)))))
34
+ }
35
+
36
+ test("find smallest palindrome from triple digit factors") {
37
+ pending
38
+ PalindromeProducts(100, 999).smallest should be(
39
+ Some((10201, Set((101, 101)))))
9
40
  }
10
41
 
11
- it should "find largest palindrome from a single digit factors" in {
42
+ test("find the largest palindrome from triple digit factors") {
12
43
  pending
13
- val (value, factors) = PalindromeProducts(0, 9).largest
14
- value should be (9)
15
- factors should be (Set((1, 9), (3, 3)))
44
+ PalindromeProducts(100, 999).largest should be(
45
+ Some((906609, Set((913, 993)))))
16
46
  }
17
47
 
18
- it should "find smallest palindrome from a double digit factors" in {
48
+ test("find smallest palindrome from four digit factors") {
19
49
  pending
20
- val (value, factors) = PalindromeProducts(10, 99).smallest
21
- value should be (121)
22
- factors should be (Set((11, 11)))
50
+ PalindromeProducts(1000, 9999).smallest should be(
51
+ Some((1002001, Set((1001, 1001)))))
23
52
  }
24
53
 
25
- it should "find largest palindrome from a double digit factors" in {
54
+ test("find the largest palindrome from four digit factors") {
26
55
  pending
27
- val (value, factors) = PalindromeProducts(10, 99).largest
28
- value should be (9009)
29
- factors should be (Set((91, 99)))
56
+ PalindromeProducts(1000, 9999).largest should be(
57
+ Some((99000099, Set((9901, 9999)))))
30
58
  }
31
59
 
32
- it should "find smallest palindrome from a triple digit factors" in {
60
+ test("empty result for smallest if no palindrome in the range") {
33
61
  pending
34
- val (value, factors) = PalindromeProducts(100, 999).smallest
35
- value should be (10201)
36
- factors should be (Set((101, 101)))
62
+ PalindromeProducts(1002, 1003).smallest should be(None)
37
63
  }
38
64
 
39
- it should "find largest palindrome from a triple digit factors" in {
65
+ test("empty result for largest if no palindrome in the range") {
40
66
  pending
41
- val (value, factors) = PalindromeProducts(100, 999).largest
42
- value should be (906609)
43
- factors should be (Set((913, 993)))
67
+ PalindromeProducts(15, 15).largest should be(None)
44
68
  }
45
69
 
46
- it should "find smallest palindrome from a four digit factors" in {
70
+ test("error result for smallest if min is more than max") {
47
71
  pending
48
- val (value, factors) = PalindromeProducts(1000, 9999).smallest
49
- value should be (1002001)
50
- factors should be (Set((1001, 1001)))
72
+ PalindromeProducts(10000, 1).smallest should be(None)
51
73
  }
52
74
 
53
- it should "find largest palindrome from a four digit factors" in {
75
+ test("error result for largest if min is more than max") {
54
76
  pending
55
- val (value, factors) = PalindromeProducts(1000, 9999).largest
56
- value should be (99000099)
57
- factors should be (Set((9901, 9999)))
77
+ PalindromeProducts(2, 1).largest should be(None)
58
78
  }
59
79
  }
@@ -0,0 +1,66 @@
1
+ import java.io.File
2
+
3
+ import testgen.TestSuiteBuilder.{toString, _}
4
+ import testgen._
5
+
6
+ object ClockTestGenerator {
7
+ private def toString(expected: CanonicalDataParser.Expected): String =
8
+ expected.fold(_ => "", TestSuiteBuilder.toString)
9
+
10
+ private def expectedToClockStr(expected: CanonicalDataParser.Expected): String =
11
+ expected.fold(_ => "", any => any match {
12
+ case str: String => str.split(':').map(_.toInt).mkString(", ")
13
+ case _ => throw new IllegalArgumentException
14
+ })
15
+
16
+ private def toOperation(addArg: String): String =
17
+ if (addArg.startsWith("-")) "-" else "+"
18
+
19
+ def mapArgToStringForEqual(arg: Any): String = {
20
+ arg match {
21
+ case vals: Map[String, String] => s"${vals("hour")}, ${vals("minute")}"
22
+ case _ => throw new IllegalArgumentException
23
+ }
24
+ }
25
+
26
+ def sutArgsForEqual(parseResult: CanonicalDataParser.ParseResult, argNames: String*): String =
27
+ argNames map (name => mapArgToStringForEqual(parseResult(name))) mkString
28
+
29
+ def fromLabeledTestAlt(propArgs: (String, Seq[String])*): ToTestCaseData =
30
+ withLabeledTest { sut => labeledTest =>
31
+ val sutFunction = labeledTest.property
32
+ val args = sutArgsAlt(labeledTest.result, propArgs:_*)
33
+
34
+ val sutCall =
35
+ if (sutFunction.toString == "create")
36
+ s"$sut($args)"
37
+ else if (sutFunction.toString == "add") {
38
+ val addArgs = sutArgs(labeledTest.result, "add")
39
+ s"$sut($args) ${toOperation(addArgs)} $sut(${addArgs.stripPrefix("-")})"
40
+ } else {
41
+ val argsForEqual1 = sutArgsForEqual(labeledTest.result, "clock1")
42
+ val argsForEqual2 = sutArgsForEqual(labeledTest.result, "clock2")
43
+ s"$sut($argsForEqual1) == $sut($argsForEqual2)"
44
+ }
45
+
46
+ val expected =
47
+ if (sutFunction.toString == "create" || sutFunction.toString == "add")
48
+ s"$sut(${expectedToClockStr(labeledTest.expected)})"
49
+ else
50
+ toString(labeledTest.expected)
51
+
52
+ TestCaseData(s"${labeledTest.description}", sutCall, expected)
53
+ }
54
+
55
+ def main(args: Array[String]): Unit = {
56
+ val file = new File("src/main/resources/clock.json")
57
+
58
+ val code = TestSuiteBuilder.build(file,
59
+ fromLabeledTestAlt("create" -> Seq("hour", "minute"),
60
+ "add" -> Seq("hour", "minute"),
61
+ "equal" -> Seq("clock1")))
62
+ println(s"-------------")
63
+ println(code)
64
+ println(s"-------------")
65
+ }
66
+ }
@@ -0,0 +1,66 @@
1
+ import java.io.File
2
+
3
+ import testgen.TestSuiteBuilder.{toString, _}
4
+ import testgen._
5
+
6
+ object ForthTestGenerator {
7
+ def main(args: Array[String]): Unit = {
8
+ val file = new File("src/main/resources/forth.json")
9
+
10
+ def toString(expected: CanonicalDataParser.Expected): String = {
11
+ expected match {
12
+ case Right(n: Int) => s""""${n.toString}""""
13
+ case Right(vals: List[Int]) => s""""${vals.map(_.toString).mkString(" ")}""""
14
+ case _ => throw new IllegalArgumentException
15
+ }
16
+ }
17
+
18
+ def isError(expected: CanonicalDataParser.Expected): Boolean = {
19
+ expected match {
20
+ case Left(_) => true
21
+ case Right(null) => true
22
+ case Right(n) => false
23
+ }
24
+ }
25
+
26
+ def argsToString(any: Any): String = {
27
+ any match {
28
+ case list: List[_] =>
29
+ val vals = list.map(s => argsToString(s)).mkString(" ")
30
+ s""""$vals""""
31
+ case str: String =>
32
+ s"$str"
33
+ case _ => any.toString
34
+ }
35
+ }
36
+
37
+ def sutArgs(parseResult: CanonicalDataParser.ParseResult, argNames: String*): String =
38
+ argNames map (name => argsToString(parseResult(name))) mkString ", "
39
+
40
+ def fromLabeledTest(argNames: String*): ToTestCaseData =
41
+ withLabeledTest { sut =>
42
+ labeledTest =>
43
+ val args = sutArgs(labeledTest.result, argNames: _*)
44
+ val property = labeledTest.property
45
+ val isErr = isError(labeledTest.expected)
46
+ val sutCall = if (isErr)
47
+ s"""forth.eval($args).isLeft"""
48
+ else
49
+ s"""forth.eval($args).fold(_ => "", _.toString)"""
50
+
51
+ val expected = if (isErr)
52
+ "true"
53
+ else
54
+ toString(labeledTest.expected)
55
+
56
+ TestCaseData(s"${labeledTest.parentDescriptions.mkString(" - " )} - ${labeledTest.description}", sutCall, expected)
57
+ }
58
+
59
+ val code =
60
+ TestSuiteBuilder.build(file, fromLabeledTest("input"), Seq(), Seq("private val forth = new Forth"))
61
+
62
+ println(s"-------------")
63
+ println(code)
64
+ println(s"-------------")
65
+ }
66
+ }
@@ -0,0 +1,48 @@
1
+ import java.io.File
2
+
3
+ import testgen.TestSuiteBuilder._
4
+ import testgen._
5
+
6
+ object KindergartenGardenTestGenerator {
7
+ def main(args: Array[String]): Unit = {
8
+ val file = new File("src/main/resources/kindergarten-garden.json")
9
+
10
+ def toString(expected: CanonicalDataParser.Expected): String = {
11
+ expected match {
12
+ case Right(xs: List[String]) => s"List(${xs.map(x => "Plant." + x.capitalize).mkString(", ")})"
13
+ case _ => throw new IllegalArgumentException
14
+ }
15
+ }
16
+
17
+ def fromLabeledTest(argNames: String*): ToTestCaseData =
18
+ withLabeledTest { sut =>
19
+ labeledTest =>
20
+ val diagram = labeledTest.result("diagram")
21
+ val student = labeledTest.result("student")
22
+ val students: List[String] = (labeledTest.result getOrElse("students", List())).asInstanceOf[List[String]]
23
+ val property = labeledTest.property
24
+ val sutCall =
25
+ if (students.isEmpty)
26
+ s"""Garden.defaultGarden(${escape(diagram.toString)}).$property("$student")"""
27
+ else
28
+ s"""Garden(List(${students.map(s => escape(s)).mkString(", ")}), ${escape(diagram.toString)}).$property(${escape(student.toString)})"""
29
+
30
+ val expected = toString(labeledTest.expected)
31
+ TestCaseData(labeledTest.parentDescriptions.mkString(", ") + " - " + labeledTest.description,
32
+ sutCall, expected)
33
+ }
34
+
35
+ // TODO: Move this escape up to TestSuiteBuilder
36
+ def escape(raw: String): String = {
37
+ import scala.reflect.runtime.universe._
38
+ Literal(Constant(raw)).toString
39
+ }
40
+
41
+ val code =
42
+ TestSuiteBuilder.build(file, fromLabeledTest("diagram", "student"))
43
+
44
+ println(s"-------------")
45
+ println(code)
46
+ println(s"-------------")
47
+ }
48
+ }
@@ -7,6 +7,40 @@ object NucleotideCountTestGenerator {
7
7
  def main(args: Array[String]): Unit = {
8
8
  val file = new File("src/main/resources/nucleotide-count.json")
9
9
 
10
+ def mapExpectedToString(arg: Map[String, Int]): String = {
11
+ s"Right(Map(${arg.toSeq
12
+ .sortBy(_._1)
13
+ .map{case (key, value) => "\'" + key.toString + "\' -> " + value.toString}
14
+ .mkString(",\n")}))"
15
+ }
16
+
17
+ def toString(expected: CanonicalDataParser.Expected): String = {
18
+ expected match {
19
+ case Left(s: String) => "true"
20
+ case Right(m: Map[String, Int]) => mapExpectedToString(m)
21
+ case _ => throw new IllegalArgumentException
22
+ }
23
+ }
24
+
25
+ def toSutCall(labeledTest: LabeledTest, argNames: String*): String = {
26
+ val args = sutArgs(labeledTest.result, argNames: _*)
27
+ val property = labeledTest.property
28
+ labeledTest.expected match {
29
+ case Left(_) => s"""new DNA($args).$property.isLeft"""
30
+ case Right(_) => s"""new DNA($args).$property"""
31
+ case _ => throw new IllegalArgumentException
32
+ }
33
+ }
34
+
35
+ def fromLabeledTest(argNames: String*): ToTestCaseData =
36
+ withLabeledTest { sut =>
37
+ labeledTest =>
38
+ val sutCall = toSutCall(labeledTest, argNames: _*)
39
+ val expected = toString(labeledTest.expected)
40
+ TestCaseData(labeledTest.description, sutCall, expected)
41
+ }
42
+
43
+
10
44
  val code = TestSuiteBuilder.build(file, fromLabeledTest("strand"))
11
45
  println(s"-------------")
12
46
  println(code)