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())