Skip to content

Commit 25c877a

Browse files
committed
fix tests and remove unwraps
1 parent 2584e85 commit 25c877a

File tree

4 files changed

+92
-40
lines changed

4 files changed

+92
-40
lines changed

tmc-langs-util/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ pub enum UtilError {
3232
#[error("Error while writing file to zip")]
3333
ZipWrite(#[source] std::io::Error),
3434

35+
#[error("Cache path {0} was invalid. Not a valid UTF-8 string or did not contain a cache version after a dash")]
36+
InvalidCachePath(PathBuf),
3537
#[error("Path {0} contained a dash '-' which is currently not allowed")]
3638
InvalidDirectory(PathBuf),
3739

@@ -43,6 +45,8 @@ pub enum UtilError {
4345
Zip(#[from] zip::result::ZipError),
4446
#[error(transparent)]
4547
FileIo(#[from] FileIo),
48+
#[error(transparent)]
49+
Yaml(#[from] serde_yaml::Error),
4650

4751
#[cfg(unix)]
4852
#[error("Error changing permissions of {0}")]

tmc-langs-util/src/task_executor.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -207,14 +207,18 @@ pub fn clean(path: &Path) -> Result<(), UtilError> {
207207

208208
/// Recursively searches for valid exercise directories in the path.
209209
pub fn find_exercise_directories(exercise_path: &Path) -> Result<Vec<PathBuf>, UtilError> {
210+
log::info!(
211+
"finding exercise directories in {}",
212+
exercise_path.display()
213+
);
214+
210215
let mut paths = vec![];
211216
for entry in WalkDir::new(exercise_path).into_iter().filter_entry(|e| {
212217
!submission_processing::is_hidden_dir(e)
213218
&& e.file_name() != "private"
214219
&& !submission_processing::contains_tmcignore(e)
215220
}) {
216221
let entry = entry?;
217-
// TODO: Java implementation doesn't scan root directories
218222
if is_exercise_root_directory(entry.path()) {
219223
paths.push(entry.into_path())
220224
}

tmc-langs-util/src/task_executor/course_refresher.rs

Lines changed: 71 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,12 @@ impl CourseRefresher {
6868
// create new cache path
6969
let old_version = course_cache_path
7070
.to_str()
71-
.unwrap()
72-
.split('-')
73-
.last()
74-
.unwrap()
75-
.parse::<u32>()
76-
.unwrap();
71+
.and_then(|s| s.split('-').last())
72+
.and_then(|s| s.parse::<u32>().ok())
73+
.ok_or_else(|| UtilError::InvalidCachePath(course_cache_path.clone()))?;
7774
let new_cache_path = cache_root.join(format!("{}-{}", course_name, old_version + 1));
75+
log::info!("next cache path: {}", new_cache_path.display());
76+
7877
if new_cache_path.exists() {
7978
log::info!("clearing new cache path at {}", new_cache_path.display());
8079
file_util::remove_dir_all(&new_cache_path)?;
@@ -85,9 +84,8 @@ impl CourseRefresher {
8584

8685
// initialize new clone path and verify directory names
8786
let new_clone_path = new_cache_path.join("clone");
88-
log::info!("updating repository to {}", new_clone_path.display());
8987
let old_clone_path = course_cache_path.join("clone");
90-
update_or_clone_repository(
88+
initialize_new_cache_clone(
9189
&new_cache_path,
9290
&new_clone_path,
9391
&old_clone_path,
@@ -98,7 +96,6 @@ impl CourseRefresher {
9896
self.progress_reporter
9997
.finish_step("Updated repository".to_string(), None)?;
10098

101-
log::info!("updating course options");
10299
let course_options = get_course_options(&new_clone_path, &course_name)?;
103100
self.progress_reporter
104101
.finish_step("Updated course options".to_string(), None)?;
@@ -108,7 +105,7 @@ impl CourseRefresher {
108105

109106
let exercise_dirs = task_executor::find_exercise_directories(&new_clone_path)?
110107
.into_iter()
111-
.map(|ed| ed.strip_prefix(&new_clone_path).unwrap().to_path_buf())
108+
.map(|ed| ed.strip_prefix(&new_clone_path).unwrap().to_path_buf()) // safe
112109
.collect();
113110

114111
// make_solutions
@@ -133,23 +130,23 @@ impl CourseRefresher {
133130
self.progress_reporter
134131
.finish_step("Prepared stubs".to_string(), None)?;
135132

136-
// find exercises in new clone path
137-
log::info!("finding exercises");
138-
// (exercise name, exercise path)
139133
let exercises = get_exercises(exercise_dirs, &new_clone_path, &new_stub_path)?;
140134
self.progress_reporter
141135
.finish_step("Located exercises".to_string(), None)?;
142136

143137
// make_zips_of_solutions
144138
let new_solution_zip_path = new_cache_path.join("solution_zip");
145-
log::info!("compressing solutions");
146139
execute_zip(&exercises, &new_solution_path, &new_solution_zip_path)?;
147140
self.progress_reporter
148141
.finish_step("Compressed solutions".to_string(), None)?;
149142

150143
// make_zips_of_stubs
151144
let new_stub_zip_path = new_cache_path.join("stub_zip");
152-
log::info!("compressing stubs");
145+
log::info!(
146+
"compressing stubs from {} to {}",
147+
new_stub_path.display(),
148+
new_stub_zip_path.display()
149+
);
153150
execute_zip(&exercises, &new_stub_path, &new_stub_zip_path)?;
154151
self.progress_reporter
155152
.finish_step("Compressed stubs".to_string(), None)?;
@@ -193,15 +190,17 @@ pub fn refresh_course(
193190
/// If found, copies it to course_clone_path fetches origin from course_source_url, checks out origin/course_git_branch, cleans and checks out the repo.
194191
/// If not found or found but one of the git commands causes an error, deletes course_clone_path and clones course_git_branch from course_source_url there.
195192
/// NOP during testing.
196-
fn update_or_clone_repository(
193+
fn initialize_new_cache_clone(
197194
new_course_root: &Path,
198195
new_clone_path: &Path,
199196
old_clone_path: &Path,
200197
course_source_url: &str,
201198
course_git_branch: &str,
202199
) -> Result<(), UtilError> {
200+
log::info!("initializing repository at {}", new_clone_path.display());
201+
203202
if old_clone_path.join(".git").exists() {
204-
// Try a fast path: copy old clone and git fetch new stuff
203+
log::info!("trying to copy clone from previous cache");
205204

206205
// closure to collect any error that occurs during the process
207206
let copy_and_update_repository = || -> Result<(), UtilError> {
@@ -238,6 +237,8 @@ fn update_or_clone_repository(
238237
}
239238
};
240239

240+
log::info!("could not copy from previous cache, cloning");
241+
241242
// clone_repository
242243
TmcCommand::new("git".to_string())
243244
.with(|e| {
@@ -255,9 +256,11 @@ fn update_or_clone_repository(
255256
/// Makes sure no directory directly under path is an exercise directory containing a dash in the relative path from path to the dir.
256257
/// A dash is used as a special delimiter.
257258
fn check_directory_names(path: &Path) -> Result<(), UtilError> {
259+
log::info!("checking directory names for dashes");
260+
258261
// exercise directories in canonicalized form
259262
for exercise_dir in task_executor::find_exercise_directories(path)? {
260-
let relative = exercise_dir.strip_prefix(path).unwrap();
263+
let relative = exercise_dir.strip_prefix(path).unwrap(); // safe
261264
if relative.to_string_lossy().contains('-') {
262265
return Err(UtilError::InvalidDirectory(exercise_dir));
263266
}
@@ -269,10 +272,16 @@ fn check_directory_names(path: &Path) -> Result<(), UtilError> {
269272
/// If found, course-specific options are merged into it and it is returned.
270273
/// Else, an empty mapping is returned.
271274
fn get_course_options(course_clone_path: &Path, course_name: &str) -> Result<Mapping, UtilError> {
275+
log::info!(
276+
"collecting course options for {} in {}",
277+
course_name,
278+
course_clone_path.display()
279+
);
280+
272281
let options_file = course_clone_path.join("course_options.yml");
273282
if options_file.exists() {
274283
let file = file_util::open_file(options_file)?;
275-
let mut course_options: Mapping = serde_yaml::from_reader(file).unwrap();
284+
let mut course_options: Mapping = serde_yaml::from_reader(file)?;
276285
// try to remove the "courses" map
277286
if let Some(serde_yaml::Value::Mapping(mut courses)) =
278287
course_options.remove(&serde_yaml::Value::String("courses".to_string()))
@@ -300,6 +309,8 @@ fn get_exercises(
300309
course_clone_path: &Path,
301310
course_stub_path: &Path,
302311
) -> Result<Vec<RefreshExercise>, UtilError> {
312+
log::info!("finding exercise checksums and points");
313+
303314
let exercises = exercise_dirs
304315
.into_iter()
305316
.map(|exercise_dir| {
@@ -308,10 +319,7 @@ fn get_exercises(
308319
exercise_dir.display()
309320
);
310321
let name = exercise_dir.to_string_lossy().replace("/", "-");
311-
312-
// checksum
313322
let checksum = calculate_checksum(&course_stub_path.join(&exercise_dir))?;
314-
315323
let exercise_path = course_clone_path.join(&exercise_dir);
316324
let points = task_executor::get_available_points(&exercise_path)?;
317325

@@ -335,12 +343,10 @@ fn calculate_checksum(exercise_dir: &Path) -> Result<String, UtilError> {
335343
.sort_by(|a, b| a.file_name().cmp(b.file_name()))
336344
{
337345
let entry = entry?;
338-
let relative = entry.path().strip_prefix(exercise_dir).unwrap();
346+
let relative = entry.path().strip_prefix(exercise_dir).unwrap(); // safe
339347
let string = relative.as_os_str().to_string_lossy();
340-
log::debug!("updating {}", string);
341348
digest.consume(string.as_ref());
342349
if entry.path().is_file() {
343-
log::debug!("updating with file");
344350
let file = file_util::read_file(entry.path())?;
345351
digest.consume(file);
346352
}
@@ -356,6 +362,12 @@ fn execute_zip(
356362
root_path: &Path,
357363
zip_dir: &Path,
358364
) -> Result<(), UtilError> {
365+
log::info!(
366+
"compressing exercises from from {} to {}",
367+
root_path.display(),
368+
zip_dir.display()
369+
);
370+
359371
file_util::create_dir_all(zip_dir)?;
360372
for exercise in course_exercises {
361373
let exercise_root = root_path.join(&exercise.path);
@@ -373,16 +385,15 @@ fn execute_zip(
373385
let entry = entry?;
374386
if entry.path().is_file() {
375387
let relative_path = entry.path().strip_prefix(&root_path).unwrap(); // safe
376-
writer
377-
.start_file(
378-
relative_path.to_string_lossy(),
379-
zip::write::FileOptions::default(),
380-
)
381-
.unwrap();
388+
writer.start_file(
389+
relative_path.to_string_lossy(),
390+
zip::write::FileOptions::default(),
391+
)?;
382392
let bytes = file_util::read_file(entry.path())?;
383393
writer.write_all(&bytes).map_err(UtilError::ZipWrite)?;
384394
}
385395
}
396+
writer.finish()?;
386397
}
387398
Ok(())
388399
}
@@ -398,6 +409,8 @@ fn set_permissions(path: &Path) -> Result<(), UtilError> {
398409
use nix::sys::stat;
399410
use std::os::unix::io::AsRawFd;
400411

412+
log::info!("setting permissions in {}", path.display());
413+
401414
let chmod: ModeBits = 0o775; // octal, read and execute permissions for all users
402415
for entry in WalkDir::new(path) {
403416
let entry = entry?;
@@ -555,7 +568,15 @@ courses:
555568
"course/part1/ex1/test/test.py",
556569
"@points('1') @points('2')",
557570
);
558-
let exercise_dirs = find_exercise_directories(&temp.path().join("course")).unwrap();
571+
let exercise_dirs = find_exercise_directories(&temp.path().join("course"))
572+
.unwrap()
573+
.into_iter()
574+
.map(|ed| {
575+
ed.strip_prefix(&temp.path().join("course"))
576+
.unwrap()
577+
.to_path_buf()
578+
})
579+
.collect();
559580
let exercises = get_exercises(
560581
exercise_dirs,
561582
&temp.path().join("course"),
@@ -567,7 +588,7 @@ courses:
567588
assert_eq!(exercises[0].points.len(), 2);
568589
assert_eq!(exercises[0].points[0], "1");
569590
assert_eq!(exercises[0].points[1], "2");
570-
assert_eq!(exercises[0].checksum, "043fb4832da4e3fbf5babd13ed9fa732");
591+
assert_eq!(exercises[0].checksum, "129e7e898698465c4f24494219f06df9");
571592
}
572593

573594
#[test]
@@ -588,7 +609,15 @@ courses:
588609
file_to(&temp, "stub/part2/ex2/dir/subdir/file", "some file");
589610
file_to(&temp, "stub/part2/ex2/dir/subdir/.hidden", "hidden file");
590611

591-
let exercise_dirs = find_exercise_directories(&temp.path().join("clone")).unwrap();
612+
let exercise_dirs = find_exercise_directories(&temp.path().join("clone"))
613+
.unwrap()
614+
.into_iter()
615+
.map(|ed| {
616+
ed.strip_prefix(&temp.path().join("clone"))
617+
.unwrap()
618+
.to_path_buf()
619+
})
620+
.collect();
592621
let exercises = get_exercises(
593622
exercise_dirs,
594623
&temp.path().join("clone"),
@@ -611,23 +640,26 @@ courses:
611640
for i in fz.file_names() {
612641
log::debug!("{}", i);
613642
}
614-
assert!(fz.by_name("setup.py").is_ok());
615643
assert!(fz
616644
.by_name(
617-
&Path::new("dir")
645+
&Path::new("part2")
646+
.join("ex2")
647+
.join("dir")
618648
.join("subdir")
619649
.join(".hidden")
620650
.to_string_lossy()
621651
)
622-
.is_err());
652+
.is_err()); // hidden files filtered
623653
let mut file = fz
624654
.by_name(
625-
&Path::new("dir")
655+
&Path::new("part2")
656+
.join("ex2")
657+
.join("dir")
626658
.join("subdir")
627659
.join("file")
628660
.to_string_lossy(),
629661
)
630-
.unwrap();
662+
.unwrap(); // other files have their stub contents
631663
let mut buf = String::new();
632664
file.read_to_string(&mut buf).unwrap();
633665
assert_eq!(buf, "some file");

tmc-langs-util/src/task_executor/submission_processing.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,12 @@ fn process_files(
159159
///
160160
/// Binary files are copied without extra processing, while text files are parsed to remove solution tags and stubs.
161161
pub fn prepare_solution(exercise_path: &Path, dest_root: &Path) -> Result<(), TmcError> {
162+
log::debug!(
163+
"preparing solution from {} to {}",
164+
exercise_path.display(),
165+
dest_root.display()
166+
);
167+
162168
let line_filter = |meta: &MetaString| !matches!(meta, MetaString::Stub(_));
163169
let file_filter = |_metas: &[_]| true; // include all files in solution
164170
process_files(exercise_path, dest_root, line_filter, file_filter)?;
@@ -174,6 +180,12 @@ pub fn prepare_solution(exercise_path: &Path, dest_root: &Path) -> Result<(), Tm
174180
///
175181
/// Additionally, copies any shared files with the corresponding language plugins.
176182
pub fn prepare_stub(exercise_path: &Path, dest_root: &Path) -> Result<(), TmcError> {
183+
log::debug!(
184+
"preparing stub from {} to {}",
185+
exercise_path.display(),
186+
dest_root.display()
187+
);
188+
177189
let line_filter = |meta: &MetaString| !matches!(meta, MetaString::Solution(_));
178190
let file_filter = |metas: &[MetaString]| {
179191
!metas

0 commit comments

Comments
 (0)