diff options
Diffstat (limited to 'Scala/example/src')
-rw-r--r-- | Scala/example/src/main/scala/common/package.scala | 40 | ||||
-rw-r--r-- | Scala/example/src/main/scala/example/Lists.scala | 42 | ||||
-rw-r--r-- | Scala/example/src/test/scala/example/ListsSuite.scala | 124 |
3 files changed, 206 insertions, 0 deletions
diff --git a/Scala/example/src/main/scala/common/package.scala b/Scala/example/src/main/scala/common/package.scala new file mode 100644 index 0000000..f1c74c3 --- /dev/null +++ b/Scala/example/src/main/scala/common/package.scala @@ -0,0 +1,40 @@ +import java.io.File + +package object common { + + /** An alias for the `Nothing` type. + * Denotes that the type should be filled in. + */ + type ??? = Nothing + + /** An alias for the `Any` type. + * Denotes that the type should be filled in. + */ + type *** = Any + + + /** + * Get a child of a file. For example, + * + * subFile(homeDir, "b", "c") + * + * corresponds to ~/b/c + */ + def subFile(file: File, children: String*) = { + children.foldLeft(file)((file, child) => new File(file, child)) + } + + /** + * Get a resource from the `src/main/resources` directory. Eclipse does not copy + * resources to the output directory, then the class loader cannot find them. + */ + def resourceAsStreamFromSrc(resourcePath: List[String]): Option[java.io.InputStream] = { + val classesDir = new File(getClass.getResource(".").toURI) + val projectDir = classesDir.getParentFile.getParentFile.getParentFile.getParentFile + val resourceFile = subFile(projectDir, ("src" :: "main" :: "resources" :: resourcePath): _*) + if (resourceFile.exists) + Some(new java.io.FileInputStream(resourceFile)) + else + None + } +} diff --git a/Scala/example/src/main/scala/example/Lists.scala b/Scala/example/src/main/scala/example/Lists.scala new file mode 100644 index 0000000..4e5feee --- /dev/null +++ b/Scala/example/src/main/scala/example/Lists.scala @@ -0,0 +1,42 @@ +package example + +import common._ + +object Lists { + /** + * This method computes the sum of all elements in the list xs. There are + * multiple techniques that can be used for implementing this method, and + * you will learn during the class. + * + * For this example assignment you can use the following methods in class + * `List`: + * + * - `xs.isEmpty: Boolean` returns `true` if the list `xs` is empty + * - `xs.head: Int` returns the head element of the list `xs`. If the list + * is empty an exception is thrown + * - `xs.tail: List[Int]` returns the tail of the list `xs`, i.e. the the + * list `xs` without its `head` element + * + * ''Hint:'' instead of writing a `for` or `while` loop, think of a recursive + * solution. + * + * @param xs A list of natural numbers + * @return The sum of all elements in `xs` + */ + def sum(xs: List[Int]): Int = ??? + + /** + * This method returns the largest element in a list of integers. If the + * list `xs` is empty it throws a `java.util.NoSuchElementException`. + * + * You can use the same methods of the class `List` as mentioned above. + * + * ''Hint:'' Again, think of a recursive solution instead of using looping + * constructs. You might need to define an auxiliary method. + * + * @param xs A list of natural numbers + * @return The largest element in `xs` + * @throws java.util.NoSuchElementException if `xs` is an empty list + */ + def max(xs: List[Int]): Int = ??? +} diff --git a/Scala/example/src/test/scala/example/ListsSuite.scala b/Scala/example/src/test/scala/example/ListsSuite.scala new file mode 100644 index 0000000..4a52667 --- /dev/null +++ b/Scala/example/src/test/scala/example/ListsSuite.scala @@ -0,0 +1,124 @@ +package example + +import org.scalatest.FunSuite + +import org.junit.runner.RunWith +import org.scalatest.junit.JUnitRunner + +/** + * This class implements a ScalaTest test suite for the methods in object + * `Lists` that need to be implemented as part of this assignment. A test + * suite is simply a collection of individual tests for some specific + * component of a program. + * + * A test suite is created by defining a class which extends the type + * `org.scalatest.FunSuite`. When running ScalaTest, it will automatically + * find this class and execute all of its tests. + * + * Adding the `@RunWith` annotation enables the test suite to be executed + * inside eclipse using the built-in JUnit test runner. + * + * You have two options for running this test suite: + * + * - Start the sbt console and run the "test" command + * - Right-click this file in eclipse and chose "Run As" - "JUnit Test" + */ +@RunWith(classOf[JUnitRunner]) +class ListsSuite extends FunSuite { + + /** + * Tests are written using the `test` operator which takes two arguments: + * + * - A description of the test. This description has to be unique, no two + * tests can have the same description. + * - The test body, a piece of Scala code that implements the test + * + * The most common way to implement a test body is using the method `assert` + * which tests that its argument evaluates to `true`. So one of the simplest + * successful tests is the following: + */ + test("one plus one is two")(assert(1 + 1 == 2)) + + + /** + * In Scala, it is allowed to pass an argument to a method using the block + * syntax, i.e. `{ argument }` instead of parentheses `(argument)`. + * + * This allows tests to be written in a more readable manner: + */ + test("one plus one is three?") { + assert(1 + 1 == 3) // This assertion fails! Go ahead and fix it. + } + + + /** + * One problem with the previous (failing) test is that ScalaTest will + * only tell you that a test failed, but it will not tell you what was + * the reason for the failure. The output looks like this: + * + * {{{ + * [info] - one plus one is three? *** FAILED *** + * }}} + * + * This situation can be improved by using a special equality operator + * `===` instead of `==` (this is only possible in ScalaTest). So if you + * run the next test, ScalaTest will show the following output: + * + * {{{ + * [info] - details why one plus one is not three *** FAILED *** + * [info] 2 did not equal 3 (ListsSuite.scala:67) + * }}} + * + * We recommend to always use the `===` equality operator when writing tests. + */ + test("details why one plus one is not three") { + assert(1 + 1 === 3) // Fix me, please! + } + + + /** + * In order to test the exceptional behavior of a methods, ScalaTest offers + * the `intercept` operation. + * + * In the following example, we test the fact that the method `intNotZero` + * throws an `IllegalArgumentException` if its argument is `0`. + */ + test("intNotZero throws an exception if its argument is 0") { + intercept[IllegalArgumentException] { + intNotZero(0) + } + } + + def intNotZero(x: Int): Int = { + if (x == 0) throw new IllegalArgumentException("zero is not allowed") + else x + } + + + /** + * Now we finally write some tests for the list functions that have to be + * implemented for this assignment. We fist import all members of the + * `List` object. + */ + import Lists._ + + + /** + * We only provide two very basic tests for you. Write more tests to make + * sure your `sum` and `max` methods work as expected. + * + * In particular, write tests for corner cases: negative numbers, zeros, + * empty lists, lists with repeated elements, etc. + * + * It is allowed to have multiple `assert` statements inside one test, + * however it is recommended to write an individual `test` statement for + * every tested aspect of a method. + */ + test("sum of a few numbers") { + assert(sum(List(1,2,0)) === 3) + } + + test("max of a few numbers") { + assert(max(List(3, 7, 2)) === 7) + } +} |