148 lines
3.7 KiB
Go
148 lines
3.7 KiB
Go
package diff
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/joint-online-judge/JOJ3/internal/stage"
|
|
)
|
|
|
|
func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
|
|
[]stage.ParserResult, bool, error,
|
|
) {
|
|
conf, err := stage.DecodeConf[Conf](confAny)
|
|
if err != nil {
|
|
return nil, true, err
|
|
}
|
|
if len(conf.Cases) != len(results) {
|
|
return nil, true, fmt.Errorf("cases number not match")
|
|
}
|
|
|
|
res := make([]stage.ParserResult, 0, len(conf.Cases))
|
|
forceQuit := false
|
|
for i, caseConf := range conf.Cases {
|
|
result := results[i]
|
|
score := 0
|
|
comment := ""
|
|
if conf.FailOnNotAccepted &&
|
|
result.Status != stage.StatusAccepted {
|
|
if conf.ForceQuitOnFailed {
|
|
forceQuit = true
|
|
}
|
|
comment += conf.FailComment
|
|
comment += "Executor status not `Accepted`\n"
|
|
} else {
|
|
for _, output := range caseConf.Outputs {
|
|
answer, err := os.ReadFile(output.AnswerPath)
|
|
if err != nil {
|
|
return nil, true, err
|
|
}
|
|
answerStr := string(answer)
|
|
resultStr := result.Files[output.FileName]
|
|
isSame := stringsEqual(
|
|
answerStr,
|
|
resultStr,
|
|
output.CompareSpace,
|
|
)
|
|
slog.Info(
|
|
"compare",
|
|
"filename", output.FileName,
|
|
"answerPath", output.AnswerPath,
|
|
"actualLength", len(resultStr),
|
|
"answerLength", len(answerStr),
|
|
"index", i,
|
|
"isSame", isSame,
|
|
)
|
|
// If no difference, assign score
|
|
if isSame {
|
|
score += output.Score
|
|
comment += conf.PassComment
|
|
} else {
|
|
if output.ForceQuitOnDiff || conf.ForceQuitOnFailed {
|
|
forceQuit = true
|
|
}
|
|
comment += conf.FailComment
|
|
comment += fmt.Sprintf("Difference found in `%s`\n",
|
|
output.FileName)
|
|
if !output.AlwaysHide {
|
|
if output.MaxDiffLength == 0 { // real default value
|
|
output.MaxDiffLength = 2048
|
|
}
|
|
if output.MaxDiffLines == 0 { // real default value
|
|
output.MaxDiffLines = 50
|
|
}
|
|
// Convert answer to string and split by lines
|
|
truncated := false
|
|
if len(answerStr) > output.MaxDiffLength {
|
|
answerStr = answerStr[:output.MaxDiffLength]
|
|
truncated = true
|
|
}
|
|
if len(resultStr) > output.MaxDiffLength {
|
|
resultStr = resultStr[:output.MaxDiffLength]
|
|
truncated = true
|
|
}
|
|
answerLines := strings.Split(answerStr, "\n")
|
|
resultLines := strings.Split(resultStr, "\n")
|
|
commonPreixLineCount := 0
|
|
if output.HideCommonPrefix {
|
|
n := 0
|
|
for ; n < len(answerLines) &&
|
|
n < len(resultLines) &&
|
|
stringsEqual(
|
|
answerLines[n],
|
|
resultLines[n],
|
|
output.CompareSpace,
|
|
); n += 1 {
|
|
}
|
|
if n > 0 {
|
|
answerLines = answerLines[n:]
|
|
resultLines = resultLines[n:]
|
|
commonPreixLineCount = n
|
|
}
|
|
}
|
|
if len(answerLines) > output.MaxDiffLines {
|
|
answerLines = answerLines[:output.MaxDiffLines]
|
|
truncated = true
|
|
}
|
|
if len(resultLines) > output.MaxDiffLines {
|
|
resultLines = resultLines[:output.MaxDiffLines]
|
|
truncated = true
|
|
}
|
|
diffs := patienceDiff(
|
|
answerLines,
|
|
resultLines,
|
|
func(a, b string) bool {
|
|
return stringsEqual(a, b, output.CompareSpace)
|
|
})
|
|
diffOutput := diffText(diffs)
|
|
diffOutput = strings.TrimSuffix(diffOutput, "\n ")
|
|
if truncated {
|
|
diffOutput += "\n\n(truncated)"
|
|
}
|
|
if commonPreixLineCount > 0 {
|
|
diffOutput = fmt.Sprintf(
|
|
"(%d line(s) of common prefix hidden)\n\n",
|
|
commonPreixLineCount,
|
|
) + diffOutput
|
|
}
|
|
comment += fmt.Sprintf(
|
|
"```diff\n%s\n```\n",
|
|
diffOutput,
|
|
)
|
|
} else {
|
|
comment += "(content hidden)\n"
|
|
}
|
|
}
|
|
}
|
|
}
|
|
res = append(res, stage.ParserResult{
|
|
Score: score,
|
|
Comment: comment,
|
|
})
|
|
}
|
|
|
|
return res, forceQuit, nil
|
|
}
|