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 }