Skip to content

Instantly share code, notes, and snippets.

@renaud
Last active November 23, 2024 22:08
Show Gist options
  • Save renaud/a2b90fe8cc3f1d0451c4b65a00a93260 to your computer and use it in GitHub Desktop.
Save renaud/a2b90fe8cc3f1d0451c4b65a00a93260 to your computer and use it in GitHub Desktop.
scala_via_python template for Coderunner / Jobe

How to create scala test questions in Coderunner / Jobe

Following the approach from https://github.com/trampgeek/moodle-qtype_coderunner?tab=readme-ov-file#supporting-or-implementing-new-languages

More docs: https://hub.docker.com/r/trampgeek/jobeinabox/ https://github.com/trampgeek/jobeinabox

1. Install scala on Jobe Docker container

docker exec -it <container_id_or_name> bash

Search for the jobeinabox container id

As per https://github.com/trampgeek/jobeinabox/blob/master/Dockerfile , the container is based on ubuntu:24.04, so installing Scala goes like this:

# install scala scalac
# check latest 2.x version: https://www.scala-lang.org/download/
sudo apt update
sudo apt install curl
export SCALA_VERSION=2.12.20
curl -fsSL https://downloads.lightbend.com/scala/${SCALA_VERSION}/scala-${SCALA_VERSION}.tgz | tar -xz -C /usr/local 
ln -s /usr/local/scala-${SCALA_VERSION}/bin/scala /usr/bin/scala 
ln -s /usr/local/scala-${SCALA_VERSION}/bin/scalac /usr/bin/scalac 

# smoke test
cd   
echo 'object HelloWorld extends App{println("yes :-)")}' > HelloWorld.scala
scalac HelloWorld.scala
scala HelloWorld
rm HelloWorld*

Coderunner template question

Following the approach from https://github.com/trampgeek/moodle-qtype_coderunner?tab=readme-ov-file#supporting-or-implementing-new-languages

  1. Create a new CodeRunner question.
  2. Choose the question type python3
  3. Click Customise
  4. Replace the contents of the Template text area with the template code below.
  5. Uncheck the Is combinator checkbox
  6. Enter TEMPLATE_QUESTION_scala_using_python as the question name
  7. Enter whatever text you wish to use to describe the question type in the Question text area. This text will be displayed to any authors using this new question type if they open the Question type details section of the question authoring form.
  8. Open Advanced Customisation
  9. Set Is prototype? to Yes (user defined)
  10. Set Question type to scala_via_python.
  11. Set Time limit to 15.
  12. Set Memory limit to 0.
  13. Set Run_spec parameters to { "memorylimit": 0, "cputime": 15 }.
  14. Set Ace language to scala, so that the students' code will be edited as Scala (syntax highlighting) even though the prototype is in Python.
  15. Save the question.

You should now find the new question type scala_via_python appearing in the Question type dropdown of the author edit form for a new CodeRunner question. This new question type should allow you to create questions in scala.

The full question prototype for the scala_via_python question type is included in this gist

Write a new question

  1. write a new CodeRunner question, select scala_via_python question type
  2. give it a name: Exercice 1: Array.reverse
  3. give it a question text: Reverse an array without using the reverse method
  4. as answer, give
def reverse(in: Array[Int]): Array[Int] = {
    val result: Array[Int] = new Array[Int](in.length)
    for (i <- in.indices) {
      result(i) = in(in.length - 1 - i)
    }
    result
}
  1. as pre-filled answer, give
// Implement the reverse function marked with (???) below.
// You may NOT use the built-in reverse methode from Array
def reverse(in: Array[Int]): Array[Int] = ???
  1. as 1st testcase, set Test case 1 to:
// Test with 1,2,3 
assert(reverse(Array(1, 2, 3)).sameElements(Array(3, 2, 1)), "Test reverse(Array(1, 2, 3)) failed")
  1. Leave the other fields from the testcase empty.
  2. Write more tests and save question

Issues with JVM memory

See https://coderunner.org.nz/mod/forum/discuss.php?d=722#p2961 for futher information

Quoting Richard Lobb :

The JVM is a notorious memory gobbler, and it's difficult to run it under Linux ulimit 
memory restrictions, even without the complication of running it on top of Python. 
Try simply setting the Jobe memory limit to 0, which turns off the ulimit on memory, 
and removing all the memory limits on the scalac command. You should be able to trust 
the JVM to manage memory itself, at least sufficiently to prevent the Jobe from thrashing. 
You can add back JVM memory limits later if you have load problems on Jobe.

TODOs

it's quite slow, so maybe group the tests?

{{ STUDENT_ANSWER }}
__student_answer__ = """{{ STUDENT_ANSWER | e('py') }}"""
SEPARATOR = "#<ab@17943918#@>#"
{% for TEST in TESTCASES %}
{{ TEST.testcode }}
{% if not loop.last %}
print(SEPARATOR)
{% endif %}
{% endfor %}
"""
The template for a question type that compiles and runs a student-submitted
Scala program.
Does not use the stdin text specified in the test case
"""
import subprocess, sys
# wrap the student code in a scala App, together with the per-test testcode
# write the student code and test code to a file MyProg.scala
student_answer = """{{ STUDENT_ANSWER | e('java') }}"""
test_code = """{{ Test.testcode | e('java') }}"""
# escape the student_answer and test_code within the Scala program
program = '''object MyProg extends App {\n\n//student code\n'''
program += student_answer
program += '\n\n//test code\n'
program += test_code
program += '\n}'
# write the program string to MyProg.scala
with open("MyProg.scala", "w") as f:
f.write(program)
# compile
return_code = subprocess.call("scalac MyProg.scala".split())
if return_code != 0:
print("** Compilation failed.. Testing aborted **", file=sys.stderr)
# if compile succeeded, run the code.
if return_code == 0:
try:
output = subprocess.check_output(["scala","-J-Xmx64m","MyProg"], universal_newlines=True)
print(output)
except subprocess.CalledProcessError as e:
if e.returncode > 0:
# Ignore non-zero positive return codes
if e.output:
print(e.output)
else:
# But negative return codes are signals - abort
if e.output:
print(e.output, file=sys.stderr)
if e.returncode < 0:
print("Task failed with signal", -e.returncode, file=sys.stderr)
print("** Further testing aborted **", file=sys.stderr)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment