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 | ||||||
|  | @ -24,7 +18,17 @@ type Conf struct { | ||||||
| 	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