Task grading
This commit is contained in:
parent
19f44ee666
commit
6a83a4f1fc
12 changed files with 350 additions and 38 deletions
|
@ -1,6 +1,4 @@
|
||||||
grade:
|
grade:
|
||||||
image: eu.gcr.io/shad-ts/grader/go
|
image: eu.gcr.io/shad-ts/grader/go
|
||||||
only:
|
|
||||||
- /^submits/.*$/
|
|
||||||
script:
|
script:
|
||||||
- echo "Not implemented"
|
- testtool grade
|
||||||
|
|
1
go.mod
1
go.mod
|
@ -6,4 +6,5 @@ require (
|
||||||
github.com/spf13/cobra v0.0.5
|
github.com/spf13/cobra v0.0.5
|
||||||
github.com/stretchr/testify v1.4.0
|
github.com/stretchr/testify v1.4.0
|
||||||
golang.org/x/tools v0.0.0-20200125223703-d33eef8e6825
|
golang.org/x/tools v0.0.0-20200125223703-d33eef8e6825
|
||||||
|
gopkg.in/yaml.v2 v2.2.8
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -53,3 +53,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
|
108
tools/testtool/commands/deadlines.go
Normal file
108
tools/testtool/commands/deadlines.go
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
const timeFormat = "02-01-2006 15:04"
|
||||||
|
|
||||||
|
type (
|
||||||
|
Task struct {
|
||||||
|
Name string `yaml:"task"`
|
||||||
|
Score int `yaml:"score"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Group struct {
|
||||||
|
Name string `yaml:"group"`
|
||||||
|
Start string `yaml:"start"`
|
||||||
|
Deadline string `yaml:"deadline"`
|
||||||
|
Tasks []Task `yaml:"tasks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
Deadlines []Group
|
||||||
|
)
|
||||||
|
|
||||||
|
func (g Group) IsOpen() bool {
|
||||||
|
t, _ := time.Parse(timeFormat, g.Start)
|
||||||
|
return time.Until(t) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d Deadlines) FindTask(name string) (*Group, *Task) {
|
||||||
|
for _, g := range d {
|
||||||
|
for _, t := range g.Tasks {
|
||||||
|
if t.Name == name {
|
||||||
|
return &g, &t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadDeadlines(filename string) (Deadlines, error) {
|
||||||
|
b, err := ioutil.ReadFile(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var d Deadlines
|
||||||
|
if err := yaml.Unmarshal(b, &d); err != nil {
|
||||||
|
return nil, fmt.Errorf("error reading deadlines: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, g := range d {
|
||||||
|
if _, err := time.Parse(timeFormat, g.Start); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid time format in task %q: %w", g.Name, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := time.Parse(timeFormat, g.Deadline); err != nil {
|
||||||
|
return nil, fmt.Errorf("invalid time format in task %q: %w", g.Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return d, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findChangedTasks(d Deadlines, files []string) []string {
|
||||||
|
tasks := map[string]struct{}{}
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
for {
|
||||||
|
dir, _ := filepath.Split(f)
|
||||||
|
if dir == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
f = dir
|
||||||
|
}
|
||||||
|
|
||||||
|
if f == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
group, task := d.FindTask(f)
|
||||||
|
if task == nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if !group.IsOpen() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks[f] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
var l []string
|
||||||
|
for t := range tasks {
|
||||||
|
l = append(l, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(l)
|
||||||
|
return l
|
||||||
|
}
|
17
tools/testtool/commands/deadlines_test.go
Normal file
17
tools/testtool/commands/deadlines_test.go
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDeadlines(t *testing.T) {
|
||||||
|
d, err := loadDeadlines("../../../.deadlines.yml")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, d)
|
||||||
|
|
||||||
|
_, sum := d.FindTask("sum")
|
||||||
|
require.NotNil(t, sum)
|
||||||
|
require.Equal(t, "sum", sum.Name)
|
||||||
|
}
|
22
tools/testtool/commands/git.go
Normal file
22
tools/testtool/commands/git.go
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func listChangedFiles(gitPath string) ([]string, error) {
|
||||||
|
var gitOutput bytes.Buffer
|
||||||
|
|
||||||
|
cmd := exec.Command("git", "diff-tree", "--no-commit-id", "--name-only", "-r", "HEAD")
|
||||||
|
cmd.Dir = gitPath
|
||||||
|
cmd.Stdout = &gitOutput
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.Split(gitOutput.String(), "\n"), nil
|
||||||
|
}
|
13
tools/testtool/commands/git_test.go
Normal file
13
tools/testtool/commands/git_test.go
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGit(t *testing.T) {
|
||||||
|
files, err := listChangedFiles(".")
|
||||||
|
require.NoError(t, err)
|
||||||
|
require.NotEmpty(t, files)
|
||||||
|
}
|
82
tools/testtool/commands/grade.go
Normal file
82
tools/testtool/commands/grade.go
Normal file
|
@ -0,0 +1,82 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
privateRepoRoot = "/opt/shad"
|
||||||
|
deadlinesYML = ".deadlines.yml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func grade() error {
|
||||||
|
userID := os.Getenv("GITLAB_USER_ID")
|
||||||
|
testerToken := os.Getenv("TESTER_TOKEN")
|
||||||
|
submitRoot := os.Getenv("CI_PROJECT_DIR")
|
||||||
|
|
||||||
|
changedFiles, err := listChangedFiles(submitRoot)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
deadlines, err := loadDeadlines(filepath.Join(privateRepoRoot, deadlinesYML))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
changedTasks := findChangedTasks(deadlines, changedFiles)
|
||||||
|
log.Printf("detected change in tasks %v", changedTasks)
|
||||||
|
|
||||||
|
var failed bool
|
||||||
|
for _, task := range changedTasks {
|
||||||
|
log.Printf("testing task %s", task)
|
||||||
|
|
||||||
|
var testFailed bool
|
||||||
|
|
||||||
|
err := testSubmission(submitRoot, privateRepoRoot, task)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("task %s failed: %s", task, err)
|
||||||
|
failed = true
|
||||||
|
|
||||||
|
var testFailedErr *TestFailedError
|
||||||
|
testFailed = errors.As(err, &testFailedErr)
|
||||||
|
|
||||||
|
if !testFailed {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("task %s passed", task)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := reportTestResults(testerToken, task, userID, testFailed); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if failed {
|
||||||
|
return fmt.Errorf("some tasks failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var gradeCmd = &cobra.Command{
|
||||||
|
Use: "grade",
|
||||||
|
Short: "test all tasks in the last commit",
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
if err := grade(); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "%v\n", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(gradeCmd)
|
||||||
|
}
|
44
tools/testtool/commands/report.go
Normal file
44
tools/testtool/commands/report.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
var testingToken = ""
|
||||||
|
|
||||||
|
const reportEndpoint = "https://go.manytask.org/api/report"
|
||||||
|
|
||||||
|
func reportTestResults(token string, task string, userID string, failed bool) error {
|
||||||
|
form := url.Values{}
|
||||||
|
form.Set("token", token)
|
||||||
|
form.Set("task", task)
|
||||||
|
form.Set("user_id", userID)
|
||||||
|
|
||||||
|
if failed {
|
||||||
|
form.Set("failed", "1")
|
||||||
|
}
|
||||||
|
|
||||||
|
var rsp *http.Response
|
||||||
|
var err error
|
||||||
|
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
rsp, err = http.PostForm(reportEndpoint, form)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("retrying report: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if rsp.StatusCode != 200 {
|
||||||
|
err = fmt.Errorf("server returned status %d", rsp.StatusCode)
|
||||||
|
log.Printf("retrying report: %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
15
tools/testtool/commands/report_test.go
Normal file
15
tools/testtool/commands/report_test.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestReport(t *testing.T) {
|
||||||
|
if testingToken == "" {
|
||||||
|
t.Skip("token is missing")
|
||||||
|
}
|
||||||
|
|
||||||
|
require.NoError(t, reportTestResults(testingToken, "sum", "1", false))
|
||||||
|
}
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
@ -27,10 +28,8 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var testSubmissionCmd = &cobra.Command{
|
var testSubmissionCmd = &cobra.Command{
|
||||||
Use: "test",
|
Use: "check-task",
|
||||||
Aliases: []string{"check", "test-submission", "check-submission"},
|
Short: "test single task",
|
||||||
Short: "test submission",
|
|
||||||
Long: `run solution on private and private tests`,
|
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
problem, err := cmd.Flags().GetString(problemFlag)
|
problem, err := cmd.Flags().GetString(problemFlag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -47,7 +46,9 @@ var testSubmissionCmd = &cobra.Command{
|
||||||
log.Fatalf("%s does not have %s directory", privateRepo, problem)
|
log.Fatalf("%s does not have %s directory", privateRepo, problem)
|
||||||
}
|
}
|
||||||
|
|
||||||
testSubmission(studentRepo, privateRepo, problem)
|
if err := testSubmission(studentRepo, privateRepo, problem); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -85,7 +86,7 @@ func problemDirExists(repo, problem string) bool {
|
||||||
return info.IsDir()
|
return info.IsDir()
|
||||||
}
|
}
|
||||||
|
|
||||||
func testSubmission(studentRepo, privateRepo, problem string) {
|
func testSubmission(studentRepo, privateRepo, problem string) error {
|
||||||
// Create temp directory to store all files required to test the solution.
|
// Create temp directory to store all files required to test the solution.
|
||||||
tmpRepo, err := ioutil.TempDir("/tmp", problem+"-")
|
tmpRepo, err := ioutil.TempDir("/tmp", problem+"-")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -125,7 +126,7 @@ func testSubmission(studentRepo, privateRepo, problem string) {
|
||||||
|
|
||||||
// Run tests.
|
// Run tests.
|
||||||
log.Printf("running tests")
|
log.Printf("running tests")
|
||||||
runTests(tmpRepo, problem)
|
return runTests(tmpRepo, problem)
|
||||||
}
|
}
|
||||||
|
|
||||||
// copyDir recursively copies src directory to dst.
|
// copyDir recursively copies src directory to dst.
|
||||||
|
@ -172,8 +173,20 @@ func randomName() string {
|
||||||
return hex.EncodeToString(raw[:])
|
return hex.EncodeToString(raw[:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type TestFailedError struct {
|
||||||
|
E error
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TestFailedError) Error() string {
|
||||||
|
return fmt.Sprintf("test failed: %v", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TestFailedError) Unwrap() error {
|
||||||
|
return e.E
|
||||||
|
}
|
||||||
|
|
||||||
// runTests runs all tests in directory with race detector.
|
// runTests runs all tests in directory with race detector.
|
||||||
func runTests(testDir, problem string) {
|
func runTests(testDir, problem string) error {
|
||||||
binCache, err := ioutil.TempDir("/tmp", "bincache")
|
binCache, err := ioutil.TempDir("/tmp", "bincache")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
|
@ -182,7 +195,7 @@ func runTests(testDir, problem string) {
|
||||||
log.Fatal(err)
|
log.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
runGo := func(arg ...string) {
|
runGo := func(arg ...string) error {
|
||||||
log.Printf("> go %s", strings.Join(arg, " "))
|
log.Printf("> go %s", strings.Join(arg, " "))
|
||||||
|
|
||||||
cmd := exec.Command("go", arg...)
|
cmd := exec.Command("go", arg...)
|
||||||
|
@ -190,9 +203,7 @@ func runTests(testDir, problem string) {
|
||||||
cmd.Dir = testDir
|
cmd.Dir = testDir
|
||||||
cmd.Stdout = os.Stdout
|
cmd.Stdout = os.Stdout
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
if err := cmd.Run(); err != nil {
|
return cmd.Run()
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
binaries := map[string]string{}
|
binaries := map[string]string{}
|
||||||
|
@ -202,7 +213,10 @@ func runTests(testDir, problem string) {
|
||||||
for binaryPkg := range binPkgs {
|
for binaryPkg := range binPkgs {
|
||||||
binPath := filepath.Join(binCache, randomName())
|
binPath := filepath.Join(binCache, randomName())
|
||||||
binaries[binaryPkg] = binPath
|
binaries[binaryPkg] = binPath
|
||||||
runGo("build", "-mod", "readonly", "-tags", "private", "-o", binPath, binaryPkg)
|
|
||||||
|
if err := runGo("build", "-mod", "readonly", "-tags", "private", "-o", binPath, binaryPkg); err != nil {
|
||||||
|
return fmt.Errorf("error building binary in %s: %w", binaryPkg, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
binariesJSON, _ := json.Marshal(binaries)
|
binariesJSON, _ := json.Marshal(binaries)
|
||||||
|
@ -210,7 +224,9 @@ func runTests(testDir, problem string) {
|
||||||
for testPkg := range testPkgs {
|
for testPkg := range testPkgs {
|
||||||
binPath := filepath.Join(binCache, randomName())
|
binPath := filepath.Join(binCache, randomName())
|
||||||
testBinaries[testPkg] = binPath
|
testBinaries[testPkg] = binPath
|
||||||
runGo("test", "-mod", "readonly", "-tags", "private", "-c", "-o", binPath, testPkg)
|
if err := runGo("test", "-mod", "readonly", "-tags", "private", "-c", "-o", binPath, testPkg); err != nil {
|
||||||
|
return fmt.Errorf("error building test in %s: %w", testPkg, err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for testPkg, testBinary := range testBinaries {
|
for testPkg, testBinary := range testBinaries {
|
||||||
|
@ -229,9 +245,11 @@ func runTests(testDir, problem string) {
|
||||||
cmd.Stderr = os.Stderr
|
cmd.Stderr = os.Stderr
|
||||||
|
|
||||||
if err := cmd.Run(); err != nil {
|
if err := cmd.Run(); err != nil {
|
||||||
log.Fatal(err)
|
return &TestFailedError{E: err}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// relPaths converts paths to relative (to the baseDir) ones.
|
// relPaths converts paths to relative (to the baseDir) ones.
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
package commands
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path"
|
"path"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -39,7 +38,8 @@ func Test_testSubmission_correct(t *testing.T) {
|
||||||
t.Run(problem, func(t *testing.T) {
|
t.Run(problem, func(t *testing.T) {
|
||||||
studentRepo := path.Join(dir, "student")
|
studentRepo := path.Join(dir, "student")
|
||||||
privateRepo := path.Join(dir, "private")
|
privateRepo := path.Join(dir, "private")
|
||||||
testSubmission(studentRepo, privateRepo, problem)
|
|
||||||
|
require.NoError(t, testSubmission(studentRepo, privateRepo, problem))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,24 +54,16 @@ func Test_testSubmission_incorrect(t *testing.T) {
|
||||||
|
|
||||||
problem := path.Base(dir)
|
problem := path.Base(dir)
|
||||||
t.Run(problem, func(t *testing.T) {
|
t.Run(problem, func(t *testing.T) {
|
||||||
if os.Getenv("BE_CRASHER") == "1" {
|
studentRepo := path.Join(dir, "student")
|
||||||
studentRepo := path.Join(dir, "student")
|
privateRepo := path.Join(dir, "private")
|
||||||
privateRepo := path.Join(dir, "private")
|
|
||||||
testSubmission(studentRepo, privateRepo, problem)
|
err := testSubmission(studentRepo, privateRepo, problem)
|
||||||
return
|
require.Error(t, err)
|
||||||
|
|
||||||
|
if problem == "brokentest" {
|
||||||
|
var testFailedErr *TestFailedError
|
||||||
|
require.True(t, errors.As(err, &testFailedErr))
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd := exec.Command(os.Args[0], "-test.run=Test_testSubmission_incorrect/"+problem)
|
|
||||||
cmd.Env = append(os.Environ(), "BE_CRASHER=1")
|
|
||||||
cmd.Stdout = nil
|
|
||||||
cmd.Stderr = os.Stderr
|
|
||||||
|
|
||||||
err := cmd.Run()
|
|
||||||
if e, ok := err.(*exec.ExitError); ok && !e.Success() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
t.Fatalf("process ran with err %v, want exit status != 0", err)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue