Skip to content

Commit abdcaca

Browse files
committed
feat: added light conformance for staff to help troubleshoot
1 parent 5ac9689 commit abdcaca

3 files changed

Lines changed: 153 additions & 0 deletions

File tree

src/main/java/chalkbox/commands/Grade.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ private Stage getStage(String name, Config config) {
8383
case "ai" -> config.toAI();
8484
case "codestyle" -> config.toCodestyle();
8585
case "conformance" -> config.toConformance();
86+
case "conformance-lite" -> config.toConformanceLight();
8687
case "functionality" -> config.toFunctionality();
8788
case "mutation" -> config.toMutation();
8889
case "tlc" -> config.toTLC();

src/main/java/chalkbox/config/Config.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import chalkbox.stages.functionality.Functionality;
77
import chalkbox.stages.codestyle.CodeStyle;
88
import chalkbox.stages.conformance.Conformance;
9+
import chalkbox.stages.conformance.ConformanceLite;
910
import chalkbox.stages.mutation.Mutation;
1011
import chalkbox.stages.tlc.TLC;
1112
import org.github.gestalt.config.Gestalt;
@@ -64,6 +65,10 @@ public Conformance toConformance() {
6465
return new Conformance(new ArrayList<>());
6566
}
6667

68+
public ConformanceLite toConformanceLight() {
69+
return new ConformanceLite(new ArrayList<>());
70+
}
71+
6772
public Functionality toFunctionality() throws ConfigException {
6873
try {
6974
return new Functionality(
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package chalkbox.stages.conformance;
2+
3+
import chalkbox.api.files.FileLoader;
4+
import chalkbox.source.Solution;
5+
import chalkbox.source.Source;
6+
import chalkbox.source.Submission;
7+
import chalkbox.stages.*;
8+
import chalkbox.stages.conformance.comparator.ClassComparator;
9+
import com.google.common.flogger.FluentLogger;
10+
11+
import java.io.IOException;
12+
import java.nio.file.FileSystems;
13+
import java.nio.file.Path;
14+
import java.util.ArrayList;
15+
import java.util.Collections;
16+
import java.util.List;
17+
import java.util.Map;
18+
19+
/**
20+
* Checks whether a submission conforms at minimum to the specified public API.
21+
*
22+
* Detects missing files in a submission, compared to the expected
23+
* file structure. Uses class comparators to identify methods and members in the
24+
* submission that differ to those in the correct solution.
25+
*/
26+
public class ConformanceLite implements Stage {
27+
private static final String name = "Conformance (Lite)";
28+
29+
private final List<String> ignoreWildcards;
30+
private static final FluentLogger logger = FluentLogger.forEnclosingClass();
31+
32+
/**
33+
* Sets up the conformance checker ready to check a submission.
34+
*/
35+
public ConformanceLite(List<String> ignoreWildcards){
36+
this.ignoreWildcards = ignoreWildcards;
37+
}
38+
39+
@Override
40+
public String getName() {
41+
return name;
42+
}
43+
44+
@Override
45+
public Type getType() {
46+
return Type.SUBMISSION_AND_SOLUTION;
47+
}
48+
49+
@Override
50+
public StageResult run(Submission submission) throws StageException {
51+
return null;
52+
}
53+
54+
@Override
55+
public StageResult run(Submission submission, List<Solution> solutions) throws StageException {
56+
return null;
57+
}
58+
59+
/**
60+
* Runs the conformance stage against the provided submission.
61+
*
62+
* @param submission submission to check for conformance
63+
* @return given submission with extra test result for conformance results
64+
*/
65+
public StageResult run(Submission submission, Solution solution) throws StageException {
66+
var result = new Result(name).setVisibility(Visibility.HIDDEN).setOutputFormat("html");
67+
var missing = new ArrayList<String>();
68+
var extra = new ArrayList<String>();
69+
70+
try {
71+
var compilation = submission.compileSrc();
72+
if (!compilation.success()) {
73+
result.appendOutput("Submission did not compile, not checking conformance");
74+
result.appendOutput(compilation.output());
75+
return StageResult.fromOverview(result);
76+
}
77+
} catch (IOException e) {
78+
result.appendOutput("Submission did not compile, not checking conformance");
79+
result.appendOutput(e.toString());
80+
return StageResult.fromOverview(result);
81+
}
82+
83+
try {
84+
var compilation = solution.compileSrc();
85+
if (!compilation.success()) {
86+
logger.atSevere().log(compilation.output());
87+
throw new StageException("Unable to compile solution");
88+
}
89+
} catch (IOException e) {
90+
throw new StageException(e.toString());
91+
}
92+
93+
var actual = removeMatchingFiles(FileLoader.loadFiles(submission.getSrcFolder()), ignoreWildcards);
94+
var expectedFiles = removeMatchingFiles(FileLoader.loadFiles(solution.getSrcFolder()), ignoreWildcards);
95+
96+
for (var expected : expectedFiles) {
97+
if (!actual.contains(expected)) {
98+
missing.add(expected);
99+
}
100+
}
101+
102+
// Enforce deterministic order of list of missing/extra files
103+
Collections.sort(missing);
104+
Collections.sort(extra);
105+
106+
if (missing.isEmpty()) {
107+
result.appendOutput("<p>✅ No missing files<p/>");
108+
} else {
109+
result.appendOutput("""
110+
<div style="
111+
background-color: #ffe5e5;
112+
border: 1px solid #cc0000;
113+
padding: 15px;
114+
margin-bottom: 20px;
115+
border-radius: 5px;
116+
font-family: Arial, sans-serif;
117+
font-size: 16px;
118+
">
119+
<strong style="font-weight: bold; color: #cc0000;">Warning:</strong> Having missing files will most likely cause an issue in latter stages.
120+
""");
121+
result.appendOutput("<p>Missing files</p>");
122+
result.appendOutput("<ul>");
123+
for (var missingFile : missing) {
124+
result.appendOutput(String.format("<li>%s</li>", missingFile));
125+
}
126+
result.appendOutput("</ul>");
127+
result.appendOutput("</div>");
128+
}
129+
return StageResult.fromOverview(result);
130+
}
131+
132+
private List<String> removeMatchingFiles(List<String> files, List<String> globs) {
133+
var fs = FileSystems.getDefault();
134+
var filtered = new ArrayList<String>(files);
135+
var toBeRemoved = new ArrayList<String>();
136+
for (var glob : globs) {
137+
var matcher = fs.getPathMatcher(glob);
138+
for (var file : files) {
139+
if (!matcher.matches(Path.of(file))) {
140+
toBeRemoved.add(file);
141+
}
142+
}
143+
}
144+
filtered.removeAll(toBeRemoved);
145+
return filtered;
146+
}
147+
}

0 commit comments

Comments
 (0)