refactor(parser/diff): split to smaller functions
This commit is contained in:
parent
c3ba14d321
commit
997e7dde20
|
@ -7,13 +7,7 @@ import "github.com/joint-online-judge/JOJ3/internal/stage"
|
||||||
|
|
||||||
var name = "diff"
|
var name = "diff"
|
||||||
|
|
||||||
type Conf struct {
|
type Output struct {
|
||||||
PassComment string `default:"🥳Passed!"`
|
|
||||||
FailComment string `default:"🧐Failed..."`
|
|
||||||
FailOnNotAccepted bool `default:"true"`
|
|
||||||
ForceQuitOnFailed bool `default:"false"`
|
|
||||||
Cases []struct {
|
|
||||||
Outputs []struct {
|
|
||||||
Score int
|
Score int
|
||||||
Filename string
|
Filename string
|
||||||
AnswerPath string
|
AnswerPath string
|
||||||
|
@ -23,8 +17,18 @@ type Conf struct {
|
||||||
MaxDiffLength int `default:"2048"` // just for reference
|
MaxDiffLength int `default:"2048"` // just for reference
|
||||||
MaxDiffLines int `default:"50"` // just for reference
|
MaxDiffLines int `default:"50"` // just for reference
|
||||||
HideCommonPrefix bool
|
HideCommonPrefix bool
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
type Case struct {
|
||||||
|
Outputs []Output
|
||||||
|
}
|
||||||
|
|
||||||
|
type Conf struct {
|
||||||
|
PassComment string `default:"🥳Passed!"`
|
||||||
|
FailComment string `default:"🧐Failed..."`
|
||||||
|
FailOnNotAccepted bool `default:"true"`
|
||||||
|
ForceQuitOnFailed bool `default:"false"`
|
||||||
|
Cases []Case
|
||||||
}
|
}
|
||||||
|
|
||||||
type Diff struct{}
|
type Diff struct{}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import (
|
||||||
"github.com/joint-online-judge/JOJ3/internal/stage"
|
"github.com/joint-online-judge/JOJ3/internal/stage"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
|
func (d *Diff) Run(results []stage.ExecutorResult, confAny any) (
|
||||||
[]stage.ParserResult, bool, error,
|
[]stage.ParserResult, bool, error,
|
||||||
) {
|
) {
|
||||||
conf, err := stage.DecodeConf[Conf](confAny)
|
conf, err := stage.DecodeConf[Conf](confAny)
|
||||||
|
@ -24,10 +24,26 @@ func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
|
||||||
forceQuit := false
|
forceQuit := false
|
||||||
for i, caseConf := range conf.Cases {
|
for i, caseConf := range conf.Cases {
|
||||||
result := results[i]
|
result := results[i]
|
||||||
|
parserResult, fq, err := d.processCase(caseConf, result, conf, i)
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, err
|
||||||
|
}
|
||||||
|
res = append(res, parserResult)
|
||||||
|
if fq {
|
||||||
|
forceQuit = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, forceQuit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processCase handles a single test case.
|
||||||
|
func (d *Diff) processCase(caseConf Case, result stage.ExecutorResult, conf *Conf, index int) (stage.ParserResult, bool, error) {
|
||||||
score := 0
|
score := 0
|
||||||
comment := ""
|
comment := ""
|
||||||
if conf.FailOnNotAccepted &&
|
forceQuit := false
|
||||||
result.Status != stage.StatusAccepted {
|
|
||||||
|
if conf.FailOnNotAccepted && result.Status != stage.StatusAccepted {
|
||||||
if conf.ForceQuitOnFailed {
|
if conf.ForceQuitOnFailed {
|
||||||
forceQuit = true
|
forceQuit = true
|
||||||
}
|
}
|
||||||
|
@ -35,45 +51,80 @@ func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
|
||||||
comment += "Executor status not `Accepted`\n"
|
comment += "Executor status not `Accepted`\n"
|
||||||
} else {
|
} else {
|
||||||
for _, output := range caseConf.Outputs {
|
for _, output := range caseConf.Outputs {
|
||||||
|
outputScore, outputComment, outputForceQuit, err := d.processOutput(output, result, conf, index)
|
||||||
|
if err != nil {
|
||||||
|
return stage.ParserResult{}, true, err
|
||||||
|
}
|
||||||
|
score += outputScore
|
||||||
|
comment += outputComment
|
||||||
|
if outputForceQuit {
|
||||||
|
forceQuit = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return stage.ParserResult{
|
||||||
|
Score: score,
|
||||||
|
Comment: comment,
|
||||||
|
}, forceQuit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// processOutput handles a single output comparison.
|
||||||
|
func (d *Diff) processOutput(output Output, result stage.ExecutorResult, conf *Conf, index int) (int, string, bool, error) {
|
||||||
answer, err := os.ReadFile(output.AnswerPath)
|
answer, err := os.ReadFile(output.AnswerPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, true, err
|
return 0, "", true, err
|
||||||
}
|
}
|
||||||
answerStr := string(answer)
|
answerStr := string(answer)
|
||||||
resultStr := result.Files[output.Filename]
|
resultStr := result.Files[output.Filename]
|
||||||
|
|
||||||
isSame := stringsEqual(
|
isSame := stringsEqual(
|
||||||
answerStr,
|
answerStr,
|
||||||
resultStr,
|
resultStr,
|
||||||
output.CompareSpace,
|
output.CompareSpace,
|
||||||
)
|
)
|
||||||
|
|
||||||
slog.Debug(
|
slog.Debug(
|
||||||
"compare",
|
"compare",
|
||||||
"filename", output.Filename,
|
"filename", output.Filename,
|
||||||
"answerPath", output.AnswerPath,
|
"answerPath", output.AnswerPath,
|
||||||
"actualLength", len(resultStr),
|
"actualLength", len(resultStr),
|
||||||
"answerLength", len(answerStr),
|
"answerLength", len(answerStr),
|
||||||
"index", i,
|
"index", index,
|
||||||
"isSame", isSame,
|
"isSame", isSame,
|
||||||
)
|
)
|
||||||
// If no difference, assign score
|
|
||||||
if isSame {
|
if isSame {
|
||||||
score += output.Score
|
return output.Score, conf.PassComment + "\n", false, nil
|
||||||
comment += conf.PassComment + "\n"
|
}
|
||||||
} else {
|
|
||||||
|
// They are different.
|
||||||
|
forceQuit := false
|
||||||
if output.ForceQuitOnDiff || conf.ForceQuitOnFailed {
|
if output.ForceQuitOnDiff || conf.ForceQuitOnFailed {
|
||||||
forceQuit = true
|
forceQuit = true
|
||||||
}
|
}
|
||||||
comment += conf.FailComment + "\n"
|
comment := conf.FailComment + "\n"
|
||||||
comment += fmt.Sprintf("Difference found in `%s`\n",
|
comment += fmt.Sprintf("Difference found in `%s`\n", output.Filename)
|
||||||
output.Filename)
|
|
||||||
if !output.AlwaysHide {
|
if !output.AlwaysHide {
|
||||||
|
diffComment := d.generateDiffComment(answerStr, resultStr, output)
|
||||||
|
comment += diffComment
|
||||||
|
} else {
|
||||||
|
comment += "(content hidden)\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0, comment, forceQuit, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// generateDiffComment generates a diff comment for the given strings.
|
||||||
|
func (d *Diff) generateDiffComment(answerStr, resultStr string, output Output) string {
|
||||||
if output.MaxDiffLength == 0 { // real default value
|
if output.MaxDiffLength == 0 { // real default value
|
||||||
output.MaxDiffLength = 2048
|
output.MaxDiffLength = 2048
|
||||||
}
|
}
|
||||||
if output.MaxDiffLines == 0 { // real default value
|
if output.MaxDiffLines == 0 { // real default value
|
||||||
output.MaxDiffLines = 50
|
output.MaxDiffLines = 50
|
||||||
}
|
}
|
||||||
// Convert answer to string and split by lines
|
|
||||||
truncated := false
|
truncated := false
|
||||||
if len(answerStr) > output.MaxDiffLength {
|
if len(answerStr) > output.MaxDiffLength {
|
||||||
answerStr = answerStr[:output.MaxDiffLength]
|
answerStr = answerStr[:output.MaxDiffLength]
|
||||||
|
@ -83,9 +134,11 @@ func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
|
||||||
resultStr = resultStr[:output.MaxDiffLength]
|
resultStr = resultStr[:output.MaxDiffLength]
|
||||||
truncated = true
|
truncated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
answerLines := strings.Split(answerStr, "\n")
|
answerLines := strings.Split(answerStr, "\n")
|
||||||
resultLines := strings.Split(resultStr, "\n")
|
resultLines := strings.Split(resultStr, "\n")
|
||||||
commonPrefixLineCount := 0
|
commonPrefixLineCount := 0
|
||||||
|
|
||||||
if output.HideCommonPrefix {
|
if output.HideCommonPrefix {
|
||||||
n := 0
|
n := 0
|
||||||
for ; n < len(answerLines) &&
|
for ; n < len(answerLines) &&
|
||||||
|
@ -102,6 +155,7 @@ func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
|
||||||
commonPrefixLineCount = n
|
commonPrefixLineCount = n
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(answerLines) > output.MaxDiffLines {
|
if len(answerLines) > output.MaxDiffLines {
|
||||||
answerLines = answerLines[:output.MaxDiffLines]
|
answerLines = answerLines[:output.MaxDiffLines]
|
||||||
truncated = true
|
truncated = true
|
||||||
|
@ -110,6 +164,7 @@ func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
|
||||||
resultLines = resultLines[:output.MaxDiffLines]
|
resultLines = resultLines[:output.MaxDiffLines]
|
||||||
truncated = true
|
truncated = true
|
||||||
}
|
}
|
||||||
|
|
||||||
diffs := patienceDiff(
|
diffs := patienceDiff(
|
||||||
answerLines,
|
answerLines,
|
||||||
resultLines,
|
resultLines,
|
||||||
|
@ -118,6 +173,7 @@ func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
|
||||||
})
|
})
|
||||||
diffOutput := diffText(diffs)
|
diffOutput := diffText(diffs)
|
||||||
diffOutput = strings.TrimSuffix(diffOutput, "\n ")
|
diffOutput = strings.TrimSuffix(diffOutput, "\n ")
|
||||||
|
|
||||||
if truncated {
|
if truncated {
|
||||||
diffOutput += "\n\n(truncated)"
|
diffOutput += "\n\n(truncated)"
|
||||||
}
|
}
|
||||||
|
@ -127,21 +183,9 @@ func (*Diff) Run(results []stage.ExecutorResult, confAny any) (
|
||||||
commonPrefixLineCount,
|
commonPrefixLineCount,
|
||||||
) + diffOutput
|
) + diffOutput
|
||||||
}
|
}
|
||||||
comment += fmt.Sprintf(
|
|
||||||
|
return fmt.Sprintf(
|
||||||
"```diff\n%s\n```\n",
|
"```diff\n%s\n```\n",
|
||||||
diffOutput,
|
diffOutput,
|
||||||
)
|
)
|
||||||
} else {
|
|
||||||
comment += "(content hidden)\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res = append(res, stage.ParserResult{
|
|
||||||
Score: score,
|
|
||||||
Comment: comment,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, forceQuit, nil
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user