repochecker: Test number step are in phase with response precision

Closes: https://gitlab.cri.epita.fr/ing/majeures/srs/fic/server/-/issues/36
This commit is contained in:
nemunaire 2024-01-13 16:40:25 +01:00
parent 04e938ff73
commit 954cf84f0f
2 changed files with 60 additions and 8 deletions

View File

@ -3,6 +3,7 @@ package sync
import ( import (
"fmt" "fmt"
"path" "path"
"strconv"
"github.com/BurntSushi/toml" "github.com/BurntSushi/toml"
"go.uber.org/multierr" "go.uber.org/multierr"
@ -78,6 +79,41 @@ func (f ExerciceFlag) RawString() []string {
} }
} }
func (f ExerciceFlag) RawNumber() ([]float64, error) {
switch f.Raw.(type) {
case float64:
return []float64{f.Raw.(float64)}, nil
case []float64:
return f.Raw.([]float64), nil
case int64:
return []float64{float64(f.Raw.(int64))}, nil
case []int64:
var res []float64
for _, raw := range f.Raw.([]int64) {
res = append(res, float64(raw))
}
return res, nil
case string:
if v, err := strconv.ParseFloat(f.Raw.(string), 64); err == nil {
return []float64{v}, nil
} else {
return nil, err
}
case []string:
var res []float64
for _, raw := range f.Raw.([]string) {
if v, err := strconv.ParseFloat(raw, 64); err == nil {
res = append(res, v)
} else {
return nil, err
}
}
return res, nil
default:
return nil, fmt.Errorf("invalid raw type: %T", f.Raw)
}
}
// ExerciceFlagChoice holds informations about a choice (for MCQ and UCQ). // ExerciceFlagChoice holds informations about a choice (for MCQ and UCQ).
type ExerciceFlagChoice struct { type ExerciceFlagChoice struct {
ExerciceFlag ExerciceFlag

View File

@ -2,6 +2,7 @@ package sync
import ( import (
"fmt" "fmt"
"math"
"math/rand" "math/rand"
"net/http" "net/http"
"path" "path"
@ -286,17 +287,19 @@ type importFlag struct {
FlagsDeps []int64 FlagsDeps []int64
} }
func iface2Number(input interface{}, output *string) error { func iface2Number(input interface{}, output *string) (norm float64, err error) {
if input != nil { if input != nil {
if v, ok := input.(int64); ok { if v, ok := input.(int64); ok {
*output = fmt.Sprintf("%d", v) *output = fmt.Sprintf("%d", v)
norm = float64(v)
} else if v, ok := input.(float64); ok { } else if v, ok := input.(float64); ok {
*output = strconv.FormatFloat(v, 'f', -1, 64) *output = strconv.FormatFloat(v, 'f', -1, 64)
norm = v
} else { } else {
return fmt.Errorf("has an invalid type: expected int or float, got %T", input) err = fmt.Errorf("has an invalid type: expected int or float, got %T", input)
} }
} }
return nil return
} }
// buildExerciceFlag read challenge.txt and extract all flags. // buildExerciceFlag read challenge.txt and extract all flags.
@ -310,20 +313,33 @@ func buildExerciceFlag(i Importer, exercice *fic.Exercice, flag ExerciceFlag, nl
flag.Type = "key" flag.Type = "key"
case "number": case "number":
var smin, smax, sstep string var smin, smax, sstep string
err := iface2Number(flag.NumberMin, &smin) fstep, err := iface2Number(flag.NumberStep, &sstep)
if err != nil {
errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("step %w", err)))
}
_, err = iface2Number(flag.NumberMin, &smin)
if err != nil { if err != nil {
errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("min %w", err))) errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("min %w", err)))
} }
err = iface2Number(flag.NumberMax, &smax) _, err = iface2Number(flag.NumberMax, &smax)
if err != nil { if err != nil {
errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("max %w", err))) errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("max %w", err)))
} }
err = iface2Number(flag.NumberStep, &sstep) // Ensure step permit validating the flag
if err != nil { if rns, err := flag.RawNumber(); err != nil {
errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("step %w", err))) errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("raw %w", err)))
} else {
for _, rn := range rns {
v := math.Abs(rn) / fstep
if float64(int(v)) != v {
errs = multierr.Append(errs, NewFlagError(exercice, &flag, nline+1, fmt.Errorf("choosen step=%f doesn't include response=%f", fstep, rn)))
}
}
} }
flag.Type = fmt.Sprintf("number,%s,%s,%s", smin, smax, sstep) flag.Type = fmt.Sprintf("number,%s,%s,%s", smin, smax, sstep)
case "text": case "text":
flag.Type = "text" flag.Type = "text"