-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #28 from WaDelma/rust
Added initial support for Rust.
- Loading branch information
Showing
26 changed files
with
591 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
135 changes: 135 additions & 0 deletions
135
tmc-langs-rust/src/main/java/fi/helsinki/cs/tmc/langs/rust/CargoPlugin.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
package fi.helsinki.cs.tmc.langs.rust; | ||
|
||
import fi.helsinki.cs.tmc.langs.AbstractLanguagePlugin; | ||
import fi.helsinki.cs.tmc.langs.abstraction.ValidationResult; | ||
import fi.helsinki.cs.tmc.langs.domain.Configuration; | ||
import fi.helsinki.cs.tmc.langs.domain.ExerciseBuilder; | ||
import fi.helsinki.cs.tmc.langs.domain.ExerciseDesc; | ||
import fi.helsinki.cs.tmc.langs.domain.RunResult; | ||
import fi.helsinki.cs.tmc.langs.domain.RunResult.Status; | ||
import fi.helsinki.cs.tmc.langs.domain.SpecialLogs; | ||
import fi.helsinki.cs.tmc.langs.domain.TestResult; | ||
import fi.helsinki.cs.tmc.langs.io.StudentFilePolicy; | ||
import fi.helsinki.cs.tmc.langs.io.sandbox.StudentFileAwareSubmissionProcessor; | ||
import fi.helsinki.cs.tmc.langs.io.zip.StudentFileAwareUnzipper; | ||
import fi.helsinki.cs.tmc.langs.io.zip.StudentFileAwareZipper; | ||
import fi.helsinki.cs.tmc.langs.rust.util.Constants; | ||
import fi.helsinki.cs.tmc.langs.utils.ProcessResult; | ||
import fi.helsinki.cs.tmc.langs.utils.ProcessRunner; | ||
|
||
import com.google.common.base.Optional; | ||
import com.google.common.collect.ImmutableList; | ||
import com.google.common.collect.ImmutableMap; | ||
|
||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.Arrays; | ||
|
||
public class CargoPlugin extends AbstractLanguagePlugin { | ||
|
||
private static final Logger log = LoggerFactory.getLogger(CargoPlugin.class); | ||
private static final RunResult EMPTY_FAILURE = new RunResult( | ||
Status.COMPILE_FAILED, | ||
ImmutableList.<TestResult>of(), | ||
new ImmutableMap.Builder<String, byte[]>().build()); | ||
|
||
public CargoPlugin() { | ||
super( | ||
new ExerciseBuilder(), | ||
new StudentFileAwareSubmissionProcessor(), | ||
new StudentFileAwareZipper(), | ||
new StudentFileAwareUnzipper()); | ||
} | ||
|
||
@Override | ||
public boolean isExerciseTypeCorrect(Path path) { | ||
return Files.isRegularFile(path.resolve(Constants.CARGO_TOML)); | ||
} | ||
|
||
@Override | ||
protected StudentFilePolicy getStudentFilePolicy(Path projectPath) { | ||
return new CargoStudentFilePolicy(projectPath); | ||
} | ||
|
||
@Override | ||
public ValidationResult checkCodeStyle(Path path) { | ||
throw new UnsupportedOperationException("Not supported yet."); | ||
} | ||
|
||
@Override | ||
public String getLanguageName() { | ||
return "cargo"; | ||
} | ||
|
||
public String getPluginName() { | ||
return "cargo"; | ||
} | ||
|
||
@Override | ||
public Optional<ExerciseDesc> scanExercise(Path path, String exerciseName) { | ||
throw new UnsupportedOperationException("Not supported yet."); | ||
} | ||
|
||
@Override | ||
public RunResult runTests(Path path) { | ||
Optional<RunResult> result = build(path); | ||
if (result.isPresent()) { | ||
log.info("Failed to compile project."); | ||
return result.get(); | ||
} | ||
return runBuiltTests(path); | ||
} | ||
|
||
private Optional<RunResult> build(Path path) { | ||
String[] command = {"cargo", "test", "--no-run"}; | ||
log.info("Building project with command {0}", Arrays.deepToString(command)); | ||
Optional<ProcessResult> result = run(command, path); | ||
if (result.isPresent()) { | ||
if (result.get().statusCode == 0) { | ||
return Optional.absent(); | ||
} | ||
return Optional.of(filledFailure(result.get())); | ||
} | ||
return Optional.of(EMPTY_FAILURE); | ||
} | ||
|
||
private RunResult runBuiltTests(Path dir) { | ||
String[] command = {"cargo", "test"}; | ||
log.info("Running tests with command {0}", Arrays.deepToString(command)); | ||
Optional<ProcessResult> result = run(command, dir); | ||
if (result.isPresent()) { | ||
return parseResult(result.get(), dir); | ||
} | ||
return EMPTY_FAILURE; | ||
} | ||
|
||
private Optional<ProcessResult> run(String[] command, Path dir) { | ||
ProcessRunner runner = new ProcessRunner(command, dir); | ||
try { | ||
return Optional.of(runner.call()); | ||
} catch (Exception e) { | ||
log.error("Running command {0} failed {1}", Arrays.deepToString(command), e); | ||
return Optional.absent(); | ||
} | ||
} | ||
|
||
private RunResult filledFailure(ProcessResult processResult) { | ||
byte[] errorOutput = processResult.errorOutput.getBytes(StandardCharsets.UTF_8); | ||
ImmutableMap<String, byte[]> logs = new ImmutableMap.Builder() | ||
.put(SpecialLogs.COMPILER_OUTPUT, errorOutput) | ||
.<String, byte[]>build(); | ||
return new RunResult( | ||
Status.COMPILE_FAILED, | ||
ImmutableList.<TestResult>of(), | ||
logs); | ||
} | ||
|
||
private RunResult parseResult(ProcessResult processResult, Path path) { | ||
return new CargoResultParser().parse(processResult); | ||
} | ||
|
||
} |
115 changes: 115 additions & 0 deletions
115
tmc-langs-rust/src/main/java/fi/helsinki/cs/tmc/langs/rust/CargoResultParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package fi.helsinki.cs.tmc.langs.rust; | ||
|
||
import fi.helsinki.cs.tmc.langs.domain.RunResult; | ||
import fi.helsinki.cs.tmc.langs.domain.RunResult.Status; | ||
import fi.helsinki.cs.tmc.langs.domain.SpecialLogs; | ||
import fi.helsinki.cs.tmc.langs.domain.TestResult; | ||
import fi.helsinki.cs.tmc.langs.utils.ProcessResult; | ||
|
||
import com.google.common.base.Optional; | ||
import com.google.common.collect.ImmutableList; | ||
import com.google.common.collect.ImmutableMap; | ||
|
||
import java.nio.charset.StandardCharsets; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import java.util.List; | ||
import java.util.Map; | ||
import java.util.regex.Matcher; | ||
import java.util.regex.Pattern; | ||
|
||
/** | ||
* Parses Cargos test output. | ||
*/ | ||
public class CargoResultParser { | ||
|
||
private static final RunResult PARSING_FAILED = new RunResult( | ||
Status.GENERIC_ERROR, | ||
ImmutableList.<TestResult>of(), | ||
new ImmutableMap.Builder<String, byte[]>() | ||
.put("generic_error_message", "Test results couldn't be parsed." | ||
.getBytes(StandardCharsets.UTF_8)) | ||
.build()); | ||
|
||
//test result: FAILED. 25 passed; 1 failed; 0 ignored; 0 measured | ||
private static final Pattern RESULT | ||
= Pattern.compile("test result: .*\\. (?<passes>\\d*) passed; (?<fails>\\d*) failed; \\d* ignored; \\d* measured"); | ||
//test test::dim4::test_4d_1_80_perioditic ... ok | ||
private static final Pattern TEST = Pattern.compile("test (?<name>.*) \\.\\.\\. .*"); | ||
//thread 'test::dim2::test_2d_1_80_normal' panicked at 'assertion failed: false', src\test\dim2.rs:12 | ||
private static final Pattern FAILURES | ||
= Pattern.compile(".*thread '(?<name>.*)' panicked at '(?<description>.*)', .*"); | ||
|
||
/** | ||
* Parses given process results outputs to a run result. | ||
*/ | ||
public RunResult parse(ProcessResult processResult) { | ||
String output = processResult.output; | ||
String[] lines = output.split("\\r?\\n"); | ||
|
||
Matcher matcher = RESULT.matcher(output); | ||
while (matcher.find()) { | ||
int fails = Integer.parseInt(matcher.group("fails")); | ||
int passes = Integer.parseInt(matcher.group("passes")); | ||
if (fails + passes > 0) { | ||
Map<String, String> failures = findFailures(processResult.output, fails); | ||
Status status = fails == 0 ? Status.PASSED : Status.TESTS_FAILED; | ||
List<String> tests = parseResults(lines); | ||
return new RunResult( | ||
status, | ||
buildTestResults(tests, failures), | ||
new ImmutableMap.Builder() | ||
.put(SpecialLogs.STDOUT, | ||
processResult.output.getBytes(StandardCharsets.UTF_8)) | ||
.put(SpecialLogs.STDERR, | ||
processResult.errorOutput.getBytes(StandardCharsets.UTF_8)) | ||
.build()); | ||
} | ||
} | ||
return PARSING_FAILED; | ||
} | ||
|
||
private List<String> parseResults(String[] lines) { | ||
List<String> result = new ArrayList<>(); | ||
for (String line : lines) { | ||
Matcher matcher = TEST.matcher(line); | ||
if (matcher.matches()) { | ||
result.add(matcher.group("name")); | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
private Map<String, String> findFailures(String errorOutput, int fails) { | ||
Map<String, String> result = new HashMap<>(); | ||
String[] lines = errorOutput.split("\\r?\\n"); | ||
for (String line : lines) { | ||
Matcher matcher = FAILURES.matcher(line); | ||
if (matcher.matches()) { | ||
result.put(matcher.group("name"), matcher.group("description")); | ||
} | ||
} | ||
return result; | ||
} | ||
|
||
private ImmutableList<TestResult> buildTestResults(List<String> results, | ||
Map<String, String> failures) { | ||
ImmutableList.Builder<TestResult> testResults = ImmutableList.builder(); | ||
for (String test : results) { | ||
String description = failures.get(test); | ||
boolean pass = false; | ||
if (description == null) { | ||
description = ""; | ||
pass = true; | ||
} | ||
TestResult testResult = new TestResult( | ||
test, | ||
pass, | ||
ImmutableList.<String>of(), | ||
description, | ||
ImmutableList.<String>of()); | ||
testResults.add(testResult); | ||
} | ||
return testResults.build(); | ||
} | ||
} |
27 changes: 27 additions & 0 deletions
27
tmc-langs-rust/src/main/java/fi/helsinki/cs/tmc/langs/rust/CargoStudentFilePolicy.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
package fi.helsinki.cs.tmc.langs.rust; | ||
|
||
import fi.helsinki.cs.tmc.langs.io.ConfigurableStudentFilePolicy; | ||
import fi.helsinki.cs.tmc.langs.rust.util.Constants; | ||
|
||
import java.nio.file.Path; | ||
|
||
public class CargoStudentFilePolicy extends ConfigurableStudentFilePolicy { | ||
|
||
public CargoStudentFilePolicy(Path configFileParent) { | ||
super(configFileParent); | ||
} | ||
|
||
/** | ||
* Returns {@code true} for all files in the <tt>projectRoot/src</tt> directory and other | ||
* files required for building the project. | ||
* | ||
* <p>Will NOT return {@code true} for any test files. If test file modification are part | ||
* of the exercise, those test files are whitelisted as <tt>ExtraStudentFiles</tt> and the | ||
* decision to move them is made by {@link ConfigurableStudentFilePolicy}. | ||
*/ | ||
@Override | ||
public boolean isStudentSourceFile(Path path) { | ||
return !path.endsWith(Constants.CARGO_TOML) && path.startsWith(Constants.SOURCE); | ||
} | ||
|
||
} |
10 changes: 10 additions & 0 deletions
10
tmc-langs-rust/src/main/java/fi/helsinki/cs/tmc/langs/rust/util/Constants.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
|
||
package fi.helsinki.cs.tmc.langs.rust.util; | ||
|
||
import java.nio.file.Path; | ||
import java.nio.file.Paths; | ||
|
||
public class Constants { | ||
public static final Path CARGO_TOML = Paths.get("Cargo.toml"); | ||
public static final Path SOURCE = Paths.get("src"); | ||
} |
Oops, something went wrong.