diff --git a/go/src/pythia/backend/job.go b/go/src/pythia/backend/job.go
index 69c4479..d3a37f2 100644
--- a/go/src/pythia/backend/job.go
+++ b/go/src/pythia/backend/job.go
@@ -16,6 +16,7 @@
package backend
import (
+ "bytes"
"encoding/json"
"errors"
"flag"
@@ -185,6 +186,10 @@ func (job *Job) gatherOutput(stdout io.Reader) {
if read > job.Task.Limits.Output {
read = job.Task.Limits.Output
}
+ // Trim NULL bytes that could occur at the end of the buffer
+ if bytes.IndexByte(buffer, 0) != -1 {
+ read = bytes.IndexByte(buffer, 0)
+ }
job.output = strings.Replace(string(buffer[:read]), "\r\n", "\n", -1)
}
diff --git a/go/src/pythia/backend/job_test.go b/go/src/pythia/backend/job_test.go
index 441782d..7e7c3b0 100644
--- a/go/src/pythia/backend/job_test.go
+++ b/go/src/pythia/backend/job_test.go
@@ -23,62 +23,21 @@ import (
"time"
)
-// NewTestJob creates a job, configured with the paths exported from make.
-func newTestJob(task pythia.Task, input string) *Job {
- job := NewJob()
- job.Task = task
- job.Input = input
- job.UmlPath = pytest.UmlPath
- job.EnvDir = pytest.VmDir
- job.TasksDir = pytest.TasksDir
- return job
-}
-
-// RunTask executes task with input.
-// It checks that the execution time and output length are within the specified
-// limits.
-func runTask(t *testing.T, task pythia.Task, input string) (status pythia.Status, output string) {
- job := newTestJob(task, input)
- wd := testutils.Watchdog(t, task.Limits.Time+1)
- status, output = job.Execute()
- wd.Stop()
- if len(output) > task.Limits.Output {
- t.Errorf("Job output is too large: max %d, got %d.", task.Limits.Output,
- len(output))
- }
- return
-}
-
-// RunTaskCheck behaves like RunTask, but additionally checks for expected
-// status and output.
-func runTaskCheck(t *testing.T, task pythia.Task, input string,
- status pythia.Status, output string) {
- st, out := runTask(t, task, input)
- testutils.Expect(t, "status", status, st)
- testutils.Expect(t, "output", output, out)
-}
-
-// Shortcut for runTask(t, pytest.ReadTask(t, basename), ...)
-func run(t *testing.T, basename string, input string, status pythia.Status,
- output string) {
- runTaskCheck(t, pytest.ReadTask(t, basename), input, status, output)
-}
-
// Basic hello world task.
func TestJobHelloWorld(t *testing.T) {
- run(t, "hello-world", "", pythia.Success, "Hello world!\n")
+ Run(t, "hello-world", "", pythia.Success, "Hello world!\n")
}
// Check that the goroutines are cleaned correctly.
func TestJobCleanup(t *testing.T) {
testutils.CheckGoroutines(t, func() {
- run(t, "hello-world", "", pythia.Success, "Hello world!\n")
+ Run(t, "hello-world", "", pythia.Success, "Hello world!\n")
})
}
// Hello world task with input.
func TestJobHelloInput(t *testing.T) {
- run(t, "hello-input", "me\npythia\n",
+ Run(t, "hello-input", "me\npythia\n",
pythia.Success, "Hello me!\nHello pythia!\n")
}
@@ -87,7 +46,7 @@ func TestJobTimeout(t *testing.T) {
if testing.Short() {
t.Skip("skipping timeout test in short mode")
}
- run(t, "timeout", "", pythia.Timeout, "Start\n")
+ Run(t, "timeout", "", pythia.Timeout, "Start\n")
}
// This task should overflow the output buffer.
@@ -113,20 +72,20 @@ func TestJobOverflow(t *testing.T) {
// This task should overflow and be killed before the end.
func TestJobOverflowKill(t *testing.T) {
wd := testutils.Watchdog(t, 2)
- run(t, "overflow-kill", "", pythia.Overflow, "abcde")
+ Run(t, "overflow-kill", "", pythia.Overflow, "abcde")
wd.Stop()
}
// This task is a fork bomb. It should succeed, but not take the whole time.
func TestJobForkbomb(t *testing.T) {
wd := testutils.Watchdog(t, 2)
- run(t, "forkbomb", "", pythia.Success, "Start\nDone\n")
+ Run(t, "forkbomb", "", pythia.Success, "Start\nDone\n")
wd.Stop()
}
// Flooding the disk should not have any adverse effect.
func TestJobFlooddisk(t *testing.T) {
- run(t, "flooddisk", "", pythia.Success, "Start\nDone\n")
+ Run(t, "flooddisk", "", pythia.Success, "Start\nDone\n")
}
// Aborting a job shall be immediate.
diff --git a/go/src/pythia/backend/job_test_helper.go b/go/src/pythia/backend/job_test_helper.go
new file mode 100644
index 0000000..44d84d1
--- /dev/null
+++ b/go/src/pythia/backend/job_test_helper.go
@@ -0,0 +1,65 @@
+// Copyright 2013 The Pythia Authors.
+// This file is part of Pythia.
+//
+// Pythia is free software: you can redistribute it and/or modify
+// it under the terms of the GNU Affero General Public License as published by
+// the Free Software Foundation, version 3 of the License.
+//
+// Pythia is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU Affero General Public License for more details.
+//
+// You should have received a copy of the GNU Affero General Public License
+// along with Pythia. If not, see .
+
+package backend
+
+import (
+ "pythia"
+ "testing"
+ "testutils"
+ "testutils/pytest"
+)
+
+// NewTestJob creates a job, configured with the paths exported from make.
+func newTestJob(task pythia.Task, input string) *Job {
+ job := NewJob()
+ job.Task = task
+ job.Input = input
+ job.UmlPath = pytest.UmlPath
+ job.EnvDir = pytest.VmDir
+ job.TasksDir = pytest.TasksDir
+ return job
+}
+
+// RunTask executes task with input.
+// It checks that the execution time and output length are within the specified
+// limits.
+func runTask(t *testing.T, task pythia.Task, input string) (status pythia.Status, output string) {
+ job := newTestJob(task, input)
+ wd := testutils.Watchdog(t, task.Limits.Time+1)
+ status, output = job.Execute()
+ wd.Stop()
+ if len(output) > task.Limits.Output {
+ t.Errorf("Job output is too large: max %d, got %d.", task.Limits.Output,
+ len(output))
+ }
+ return
+}
+
+// RunTaskCheck behaves like RunTask, but additionally checks for expected
+// status and output.
+func runTaskCheck(t *testing.T, task pythia.Task, input string,
+ status pythia.Status, output string) {
+ st, out := runTask(t, task, input)
+ testutils.Expect(t, "status", status, st)
+ testutils.Expect(t, "output", output, out)
+}
+
+// Shortcut for runTask(t, pytest.ReadTask(t, basename), ...)
+// Warning: This function should not used except for testing purpose
+func Run(t *testing.T, basename string, input string, status pythia.Status,
+ output string) {
+ runTaskCheck(t, pytest.ReadTask(t, basename), input, status, output)
+}
diff --git a/tasks/test-bash.task b/tasks/test-bash.task
new file mode 100644
index 0000000..7bf597d
--- /dev/null
+++ b/tasks/test-bash.task
@@ -0,0 +1,10 @@
+{
+ "environment": "busybox",
+ "taskfs": "test-bash.sfs",
+ "limits": {
+ "time": 60,
+ "memory": 32,
+ "disk": 50,
+ "output": 1024
+ }
+}
diff --git a/tasks/test-bash/control b/tasks/test-bash/control
new file mode 100755
index 0000000..b89c7d1
--- /dev/null
+++ b/tasks/test-bash/control
@@ -0,0 +1 @@
+/task/test-bash.sh
diff --git a/tasks/test-bash/test-bash.sh b/tasks/test-bash/test-bash.sh
new file mode 100755
index 0000000..53b1eae
--- /dev/null
+++ b/tasks/test-bash/test-bash.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+cat
diff --git a/tasks/test-c.task b/tasks/test-c.task
new file mode 100644
index 0000000..1c6e93d
--- /dev/null
+++ b/tasks/test-c.task
@@ -0,0 +1,10 @@
+{
+ "environment": "c",
+ "taskfs": "test-c.sfs",
+ "limits": {
+ "time": 60,
+ "memory": 32,
+ "disk": 50,
+ "output": 1024
+ }
+}
diff --git a/tasks/test-c/control b/tasks/test-c/control
new file mode 100644
index 0000000..8d702f2
--- /dev/null
+++ b/tasks/test-c/control
@@ -0,0 +1 @@
+/task/run.sh
diff --git a/tasks/test-c/run.sh b/tasks/test-c/run.sh
new file mode 100755
index 0000000..21b1466
--- /dev/null
+++ b/tasks/test-c/run.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+mkdir /tmp/work
+cp /task/test-c.c /tmp/work/test-c.c
+cd /tmp/work
+make --silent test-c
+./test-c
diff --git a/tasks/test-c/test-c.c b/tasks/test-c/test-c.c
new file mode 100644
index 0000000..0245f7d
--- /dev/null
+++ b/tasks/test-c/test-c.c
@@ -0,0 +1,11 @@
+#include
+
+#define MAX_INPUT 100
+
+int main() {
+ char line[MAX_INPUT];
+ while (fgets(line, MAX_INPUT, stdin)) {
+ printf("%s", line);
+ }
+ return 0;
+}
diff --git a/tasks/test-java.task b/tasks/test-java.task
new file mode 100644
index 0000000..3f09702
--- /dev/null
+++ b/tasks/test-java.task
@@ -0,0 +1,10 @@
+{
+ "environment": "java",
+ "taskfs": "test-java.sfs",
+ "limits": {
+ "time": 60,
+ "memory": 32,
+ "disk": 50,
+ "output": 1024
+ }
+}
diff --git a/tasks/test-java/TestJava.java b/tasks/test-java/TestJava.java
new file mode 100644
index 0000000..e70a52c
--- /dev/null
+++ b/tasks/test-java/TestJava.java
@@ -0,0 +1,10 @@
+import java.io.IOException;
+
+public class TestJava {
+ public static void main (String[] arg) throws java.io.IOException {
+ int i;
+ do {
+ System.out.write(i = System.in.read());
+ } while (i != -1);
+ }
+}
diff --git a/tasks/test-java/control b/tasks/test-java/control
new file mode 100755
index 0000000..8d702f2
--- /dev/null
+++ b/tasks/test-java/control
@@ -0,0 +1 @@
+/task/run.sh
diff --git a/tasks/test-java/run.sh b/tasks/test-java/run.sh
new file mode 100755
index 0000000..26dc17d
--- /dev/null
+++ b/tasks/test-java/run.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+PATH=/usr/lib/jvm/java-7-openjdk-i386/bin:$PATH
+mkdir /tmp/work
+cp /task/TestJava.java /tmp/work/TestJava.java
+cd /tmp/work
+javac TestJava.java
+java TestJava
diff --git a/tasks/test-mono.task b/tasks/test-mono.task
new file mode 100644
index 0000000..f7502df
--- /dev/null
+++ b/tasks/test-mono.task
@@ -0,0 +1,10 @@
+{
+ "environment": "mono",
+ "taskfs": "test-mono.sfs",
+ "limits": {
+ "time": 60,
+ "memory": 32,
+ "disk": 50,
+ "output": 1024
+ }
+}
diff --git a/tasks/test-mono/TestMono.cs b/tasks/test-mono/TestMono.cs
new file mode 100644
index 0000000..fcdbb1d
--- /dev/null
+++ b/tasks/test-mono/TestMono.cs
@@ -0,0 +1,15 @@
+using System;
+
+namespace Main
+{
+ public class TestMono {
+ public static void Main (string[] args)
+ {
+ while (true) {
+ int input = Console.In.Read();
+ if (input == -1) break;
+ Console.Out.Write((char)input);
+ }
+ }
+ }
+}
diff --git a/tasks/test-mono/control b/tasks/test-mono/control
new file mode 100755
index 0000000..8d702f2
--- /dev/null
+++ b/tasks/test-mono/control
@@ -0,0 +1 @@
+/task/run.sh
diff --git a/tasks/test-mono/run.sh b/tasks/test-mono/run.sh
new file mode 100755
index 0000000..964c0be
--- /dev/null
+++ b/tasks/test-mono/run.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+mkdir /tmp/work
+cp /task/TestMono.cs /tmp/work/TestMono.cs
+cd /tmp/work
+mcs TestMono.cs
+mono TestMono.exe
diff --git a/tasks/test-python.task b/tasks/test-python.task
new file mode 100644
index 0000000..f572513
--- /dev/null
+++ b/tasks/test-python.task
@@ -0,0 +1,10 @@
+{
+ "environment": "python",
+ "taskfs": "test-python.sfs",
+ "limits": {
+ "time": 60,
+ "memory": 32,
+ "disk": 50,
+ "output": 1024
+ }
+}
diff --git a/tasks/test-python/control b/tasks/test-python/control
new file mode 100755
index 0000000..a9c2973
--- /dev/null
+++ b/tasks/test-python/control
@@ -0,0 +1 @@
+/task/test-python.py
diff --git a/tasks/test-python/test-python.py b/tasks/test-python/test-python.py
new file mode 100755
index 0000000..c7f52f9
--- /dev/null
+++ b/tasks/test-python/test-python.py
@@ -0,0 +1,5 @@
+#!/usr/bin/python3
+
+from sys import stdout, stdin
+
+stdout.write(stdin.read())