Skip to content

Commit ded4e2a

Browse files
committed
refactor csharp
1 parent 9feb6ce commit ded4e2a

File tree

5 files changed

+92
-43
lines changed

5 files changed

+92
-43
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
//! Contains the CSTestResult type that models the C# test runner result.
2+
3+
use serde::Deserialize;
4+
use tmc_langs_framework::domain::TestResult;
5+
6+
/// Test result from the C# test runner
7+
#[derive(Debug, Deserialize)]
8+
#[serde(rename_all = "PascalCase")]
9+
pub struct CSTestResult {
10+
pub name: String,
11+
pub passed: bool,
12+
pub message: String,
13+
pub points: Vec<String>,
14+
pub error_stack_trace: Vec<String>,
15+
}
16+
17+
impl From<CSTestResult> for TestResult {
18+
fn from(test_result: CSTestResult) -> Self {
19+
TestResult {
20+
name: test_result.name,
21+
successful: test_result.passed,
22+
message: test_result.message,
23+
exception: test_result.error_stack_trace,
24+
points: test_result.points,
25+
}
26+
}
27+
}
28+
29+
#[cfg(test)]
30+
mod test {
31+
use super::*;
32+
33+
#[test]
34+
fn deserializes() {
35+
let s = r#"
36+
{
37+
"Name": "n",
38+
"Passed": true,
39+
"Message": "m",
40+
"Points": ["p1", "p2"],
41+
"ErrorStackTrace": ["e1", "e2"]
42+
}
43+
"#;
44+
45+
let _cstr: CSTestResult = serde_json::from_str(s).unwrap();
46+
}
47+
}

plugins/csharp/src/error.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Error type for C#
1+
//! Error type for the crate.
22
33
use std::path::PathBuf;
44
use thiserror::Error;

plugins/csharp/src/lib.rs

Lines changed: 2 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,10 @@
1-
//! Language plugin for C#
1+
//! TMC language plugin for C#.
22
3+
mod cs_test_result;
34
mod error;
45
mod plugin;
56
mod policy;
67

78
pub use self::error::CSharpError;
89
pub use self::plugin::CSharpPlugin;
910
pub use self::policy::CSharpStudentFilePolicy;
10-
11-
use serde::Deserialize;
12-
use tmc_langs_framework::domain::TestResult;
13-
14-
#[derive(Debug, Deserialize)]
15-
#[serde(rename_all = "PascalCase")]
16-
struct CSTestResult {
17-
name: String,
18-
passed: bool,
19-
message: String,
20-
points: Vec<String>,
21-
error_stack_trace: Vec<String>,
22-
}
23-
24-
impl From<CSTestResult> for TestResult {
25-
fn from(test_result: CSTestResult) -> Self {
26-
TestResult {
27-
name: test_result.name,
28-
successful: test_result.passed,
29-
message: test_result.message,
30-
exception: test_result.error_stack_trace,
31-
points: test_result.points,
32-
}
33-
}
34-
}

plugins/csharp/src/plugin.rs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
1-
//! Implementation of LanguagePlugin for C#
1+
//! An implementation of LanguagePlugin for C#.
22
33
use crate::policy::CSharpStudentFilePolicy;
44

5-
use crate::{CSTestResult, CSharpError};
5+
use crate::{cs_test_result::CSTestResult, CSharpError};
66
use std::collections::HashMap;
77
use std::env;
88
use std::ffi::{OsStr, OsString};
@@ -33,7 +33,7 @@ impl CSharpPlugin {
3333
}
3434

3535
/// Extracts the bundled tmc-csharp-runner to the given path.
36-
fn extract_runner(target: &Path) -> Result<(), CSharpError> {
36+
fn extract_runner_to_dir(target: &Path) -> Result<(), CSharpError> {
3737
log::debug!("extracting C# runner to {}", target.display());
3838
const TMC_CSHARP_RUNNER: &[u8] = include_bytes!("../tmc-csharp-runner-1.0.1.zip");
3939

@@ -58,12 +58,14 @@ impl CSharpPlugin {
5858
}
5959

6060
/// Returns the directory of the TMC C# runner, writing it to the cache dir if it doesn't exist there yet.
61-
fn get_runner_dir() -> Result<PathBuf, CSharpError> {
61+
///
62+
/// NOTE: May cause issues if called concurrently.
63+
fn get_or_init_runner_dir() -> Result<PathBuf, CSharpError> {
6264
match dirs::cache_dir() {
6365
Some(cache_dir) => {
6466
let runner_dir = cache_dir.join("tmc").join("tmc-csharp-runner");
6567
if !runner_dir.exists() {
66-
Self::extract_runner(&runner_dir)?;
68+
Self::extract_runner_to_dir(&runner_dir)?;
6769
}
6870
Ok(runner_dir)
6971
}
@@ -77,7 +79,7 @@ impl CSharpPlugin {
7779
log::debug!("using bootstrap path TMC_CSHARP_BOOTSTRAP_PATH={}", var);
7880
Ok(PathBuf::from(var))
7981
} else {
80-
let runner_path = Self::get_runner_dir()?;
82+
let runner_path = Self::get_or_init_runner_dir()?;
8183
let bootstrap_path = runner_path.join("TestMyCode.CSharp.Bootstrap.dll");
8284
if bootstrap_path.exists() {
8385
log::debug!("found boostrap dll at {}", bootstrap_path.display());
@@ -139,9 +141,12 @@ impl LanguagePlugin for CSharpPlugin {
139141
let file_path = Path::new(file.name());
140142

141143
if file_path.extension() == Some(OsStr::new("csproj")) {
144+
// check parent of parent of csproj file for src
142145
if let Some(csproj_parent) = file_path.parent().and_then(Path::parent) {
143146
if csproj_parent.file_name() == Some(OsStr::new("src")) {
147+
// get parent of src
144148
if let Some(src_parent) = csproj_parent.parent() {
149+
// skip if any part of the path is __MACOSX
145150
if src_parent.components().any(|p| p.as_os_str() == "__MACOSX") {
146151
continue;
147152
}
@@ -276,8 +281,9 @@ impl LanguagePlugin for CSharpPlugin {
276281
}
277282
for entry in WalkDir::new(path).into_iter().filter_map(|e| e.ok()) {
278283
let file_name = entry.path().file_name();
279-
if file_name == Some(&OsString::from("bin"))
280-
|| file_name == Some(&OsString::from("obj"))
284+
if entry.path().is_dir()
285+
&& (file_name == Some(&OsString::from("bin"))
286+
|| file_name == Some(&OsString::from("obj")))
281287
{
282288
file_util::remove_dir_all(entry.path())?;
283289
}
@@ -331,7 +337,7 @@ mod test {
331337
fn init() {
332338
let _ = env_logger::builder().is_test(true).try_init();
333339
INIT_RUNNER.call_once(|| {
334-
let _ = CSharpPlugin::get_runner_dir().unwrap();
340+
let _ = CSharpPlugin::get_or_init_runner_dir().unwrap();
335341
});
336342
}
337343

plugins/csharp/src/policy.rs

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
//! Student file policy for C#
1+
//! Student file policy for the C# plugin.
22
33
use std::path::{Path, PathBuf};
44
use tmc_langs_framework::StudentFilePolicy;
@@ -14,11 +14,12 @@ impl CSharpStudentFilePolicy {
1414
}
1515
}
1616

17-
/// Goes up directories until a bin or obj directory is found
18-
fn is_child_of_binary_dir(&self, path: &Path) -> bool {
19-
for ancestor in path.ancestors() {
17+
/// Goes up directories until a bin or obj directory is found, either indicating that the path is in a binary directory.
18+
fn is_child_of_binary_dir(path: &Path) -> bool {
19+
// checks each parent directory for bin or obj
20+
for ancestor in path.ancestors().skip(1) {
2021
if let Some(file_name) = ancestor.file_name() {
21-
if ancestor.is_dir() && (file_name == "bin" || file_name == "obj") {
22+
if file_name == "bin" || file_name == "obj" {
2223
return true;
2324
}
2425
}
@@ -28,12 +29,31 @@ impl CSharpStudentFilePolicy {
2829
}
2930

3031
impl StudentFilePolicy for CSharpStudentFilePolicy {
31-
// false for files in bin or obj directories, true for other files in src
32+
// false for files in bin or obj directories, true for other files in src.
3233
fn is_student_source_file(&self, path: &Path) -> bool {
33-
path.starts_with("src") && !self.is_child_of_binary_dir(path)
34+
path.starts_with("src") && !Self::is_child_of_binary_dir(path)
3435
}
3536

3637
fn get_config_file_parent_path(&self) -> &Path {
3738
&self.config_file_parent_path
3839
}
3940
}
41+
42+
#[cfg(test)]
43+
mod test {
44+
use super::*;
45+
46+
#[test]
47+
fn file_in_binary_dir_is_not_student_file() {
48+
let policy = CSharpStudentFilePolicy::new(PathBuf::new());
49+
assert!(!policy.is_student_source_file(Path::new("src/bin/any/file")));
50+
assert!(!policy.is_student_source_file(Path::new("obj/any/src/file")));
51+
}
52+
53+
#[test]
54+
fn file_in_src_is_student_file() {
55+
let policy = CSharpStudentFilePolicy::new(PathBuf::new());
56+
assert!(policy.is_student_source_file(Path::new("src/file")));
57+
assert!(policy.is_student_source_file(Path::new("src/any/file")));
58+
}
59+
}

0 commit comments

Comments
 (0)