Skip to content

Commit 73bca38

Browse files
committed
rust: test transformation module
1 parent 55a836f commit 73bca38

File tree

2 files changed

+279
-12
lines changed

2 files changed

+279
-12
lines changed

rust/bear/src/semantic/mod.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,35 @@ pub enum CompilerPass {
3737
},
3838
}
3939

40+
#[cfg(test)]
41+
impl Clone for CompilerCall {
42+
fn clone(&self) -> Self {
43+
Self {
44+
compiler: self.compiler.clone(),
45+
working_dir: self.working_dir.clone(),
46+
passes: self.passes.clone(),
47+
}
48+
}
49+
}
50+
51+
#[cfg(test)]
52+
impl Clone for CompilerPass {
53+
fn clone(&self) -> Self {
54+
match self {
55+
CompilerPass::Preprocess => CompilerPass::Preprocess,
56+
CompilerPass::Compile {
57+
source,
58+
output,
59+
flags,
60+
} => CompilerPass::Compile {
61+
source: source.clone(),
62+
output: output.clone(),
63+
flags: flags.clone(),
64+
},
65+
}
66+
}
67+
}
68+
4069
/// Responsible to recognize the semantic of an executed command.
4170
///
4271
/// The implementation can be responsible for a single compiler,

rust/bear/src/semantic/transformation.rs

Lines changed: 250 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -149,9 +149,53 @@ mod formatter {
149149

150150
/// Compute the relative path from the root directory.
151151
fn relative_to(root: &Path, path: &Path) -> Result<PathBuf, Error> {
152-
// The implementation is naive; it assumes that the path is a child of the root.
153-
let relative_path = path.strip_prefix(root)?;
154-
Ok(relative_path.to_path_buf())
152+
// This is a naive implementation that assumes the root is
153+
// on the same filesystem/volume as the path.
154+
let mut root_components = root.components();
155+
let mut path_components = path.components();
156+
157+
let mut remaining_root_components = Vec::new();
158+
let mut remaining_path_components = Vec::new();
159+
160+
// Find the common prefix
161+
loop {
162+
let root_comp = root_components.next();
163+
let path_comp = path_components.next();
164+
match (root_comp, path_comp) {
165+
(Some(root), Some(path)) if root != path => {
166+
remaining_root_components.push(root);
167+
remaining_root_components.extend(root_components);
168+
remaining_path_components.push(path);
169+
remaining_path_components.extend(path_components);
170+
break;
171+
}
172+
(Some(root), None) => {
173+
remaining_root_components.push(root);
174+
remaining_root_components.extend(root_components);
175+
break;
176+
}
177+
(None, Some(path)) => {
178+
remaining_path_components.push(path);
179+
remaining_path_components.extend(path_components);
180+
break;
181+
}
182+
(None, None) => break,
183+
_ => continue,
184+
}
185+
}
186+
187+
// Count remaining components in the root to determine how many `..` are needed
188+
let mut result = PathBuf::new();
189+
for _ in remaining_root_components {
190+
result.push("..");
191+
}
192+
193+
// Add the remaining components of the path
194+
for comp in remaining_path_components {
195+
result.push(comp);
196+
}
197+
198+
Ok(result)
155199
}
156200

157201
/// Convenient function to resolve the path based on the configuration.
@@ -212,6 +256,208 @@ mod formatter {
212256
}
213257
}
214258
}
259+
260+
#[cfg(test)]
261+
mod formatter_tests {
262+
use super::*;
263+
use crate::config::{PathFormat, PathResolver};
264+
use crate::semantic::{CompilerCall, CompilerPass};
265+
use crate::vec_of_strings;
266+
use std::fs;
267+
use std::path::PathBuf;
268+
use tempfile::tempdir;
269+
270+
#[test]
271+
fn test_absolute_to() {
272+
// The test creates a temporary directory and a file in it.
273+
// Then it verifies that the absolute path of the file is correct.
274+
//
275+
// E.g., `/tmp/tmpdir/file.txt` is the absolute path of the file,
276+
// if `/tmp/tmpdir` is the root directory and `file.txt` is the file.
277+
let root_dir = tempdir().unwrap();
278+
let root_dir_path = root_dir.path().canonicalize().unwrap();
279+
280+
let file_path = root_dir_path.join("file.txt");
281+
fs::write(&file_path, "content").unwrap();
282+
283+
let file_relative_path = PathBuf::from("file.txt");
284+
285+
let result = absolute_to(&root_dir_path, &file_relative_path).unwrap();
286+
assert_eq!(result, file_path);
287+
288+
let result = absolute_to(&root_dir_path, &file_path).unwrap();
289+
assert_eq!(result, file_path);
290+
291+
let result = absolute_to(&root_dir_path, &root_dir_path).unwrap();
292+
assert_eq!(result, root_dir_path);
293+
}
294+
295+
#[test]
296+
fn test_relative_to() {
297+
// The test creates two temporary directories and a file in the first one.
298+
// Then it verifies that the relative path from the second directory to the file
299+
// in the first directory is correct.
300+
//
301+
// E.g., `../tmpdir/file.txt` is the relative path to the file,
302+
// if `/tmp/tmpdir2` is the root directory and `/tmp/tmpdir/file.txt` is the file.
303+
let a_dir = tempdir().unwrap();
304+
let a_dir_path = a_dir.path().canonicalize().unwrap();
305+
let a_dir_name = a_dir_path.file_name().unwrap();
306+
307+
let file_path = a_dir_path.join("file.txt");
308+
fs::write(&file_path, "content").unwrap();
309+
310+
let b_dir = tempdir().unwrap();
311+
let b_dir_path = b_dir.path().canonicalize().unwrap();
312+
313+
let result = relative_to(&b_dir_path, &file_path).unwrap();
314+
assert_eq!(
315+
result,
316+
PathBuf::from("..").join(a_dir_name).join("file.txt")
317+
);
318+
319+
let result = relative_to(&a_dir_path, &file_path).unwrap();
320+
assert_eq!(result, PathBuf::from("file.txt"));
321+
}
322+
323+
#[test]
324+
fn test_path_resolver() {
325+
let root_dir = tempdir().unwrap();
326+
let root_dir_path = root_dir.path().canonicalize().unwrap();
327+
328+
let file_path = root_dir_path.join("file.txt");
329+
fs::write(&file_path, "content").unwrap();
330+
331+
let resolver = PathResolver::Canonical;
332+
let result = resolver.resolve(&root_dir_path, &file_path).unwrap();
333+
assert_eq!(result, file_path);
334+
335+
let resolver = PathResolver::Relative;
336+
let result = resolver.resolve(&root_dir_path, &file_path).unwrap();
337+
assert_eq!(result, PathBuf::from("file.txt"));
338+
}
339+
340+
#[test]
341+
fn test_path_formatter_skip_format() {
342+
let formatter = PathFormatter::SkipFormat;
343+
344+
let input = CompilerCall {
345+
compiler: PathBuf::from("gcc"),
346+
working_dir: PathBuf::from("/project"),
347+
passes: vec![CompilerPass::Compile {
348+
source: PathBuf::from("main.c"),
349+
output: PathBuf::from("main.o").into(),
350+
flags: vec!["-O2".into()],
351+
}],
352+
};
353+
354+
let result = formatter.apply(input.clone());
355+
assert!(result.is_ok());
356+
assert_eq!(result.unwrap(), input);
357+
}
358+
359+
#[test]
360+
fn test_path_formatter_do_format() {
361+
let source_dir = tempdir().unwrap();
362+
let source_dir_path = source_dir.path().canonicalize().unwrap();
363+
let source_dir_name = source_dir_path.file_name().unwrap();
364+
let source_file_path = source_dir_path.join("main.c");
365+
fs::write(&source_file_path, "int main() {}").unwrap();
366+
367+
let build_dir = tempdir().unwrap();
368+
let build_dir_path = build_dir.path().canonicalize().unwrap();
369+
let build_dir_name = build_dir_path.file_name().unwrap();
370+
let output_file_path = build_dir_path.join("main.o");
371+
fs::write(&output_file_path, "object").unwrap();
372+
373+
let execution_dir = tempdir().unwrap();
374+
let execution_dir_path = execution_dir.path().canonicalize().unwrap();
375+
376+
// The entry contains compiler call with absolute paths.
377+
let input = CompilerCall {
378+
compiler: PathBuf::from("gcc"),
379+
working_dir: build_dir_path.to_path_buf(),
380+
passes: vec![CompilerPass::Compile {
381+
source: source_file_path.clone(),
382+
output: output_file_path.clone().into(),
383+
flags: vec_of_strings!["-O2"],
384+
}],
385+
};
386+
387+
{
388+
let sut = PathFormatter::DoFormat(
389+
PathFormat {
390+
directory: PathResolver::Canonical,
391+
file: PathResolver::Canonical,
392+
output: PathResolver::Canonical,
393+
},
394+
execution_dir_path.to_path_buf(),
395+
);
396+
397+
let expected = CompilerCall {
398+
compiler: input.compiler.clone(),
399+
working_dir: build_dir_path.clone(),
400+
passes: vec![CompilerPass::Compile {
401+
source: source_file_path.clone(),
402+
output: output_file_path.clone().into(),
403+
flags: vec_of_strings!["-O2"],
404+
}],
405+
};
406+
407+
let result = sut.apply(input.clone());
408+
assert!(result.is_ok());
409+
assert_eq!(result.unwrap(), expected);
410+
}
411+
{
412+
let sut = PathFormatter::DoFormat(
413+
PathFormat {
414+
directory: PathResolver::Canonical,
415+
file: PathResolver::Relative,
416+
output: PathResolver::Relative,
417+
},
418+
execution_dir_path.to_path_buf(),
419+
);
420+
421+
let expected = CompilerCall {
422+
compiler: input.compiler.clone(),
423+
working_dir: build_dir_path.clone(),
424+
passes: vec![CompilerPass::Compile {
425+
source: PathBuf::from("..").join(source_dir_name).join("main.c"),
426+
output: PathBuf::from("main.o").into(),
427+
flags: vec_of_strings!["-O2"],
428+
}],
429+
};
430+
431+
let result = sut.apply(input.clone());
432+
assert!(result.is_ok());
433+
assert_eq!(result.unwrap(), expected);
434+
}
435+
{
436+
let sut = PathFormatter::DoFormat(
437+
PathFormat {
438+
directory: PathResolver::Relative,
439+
file: PathResolver::Relative,
440+
output: PathResolver::Relative,
441+
},
442+
execution_dir_path.to_path_buf(),
443+
);
444+
445+
let expected = CompilerCall {
446+
compiler: input.compiler.clone(),
447+
working_dir: PathBuf::from("..").join(build_dir_name),
448+
passes: vec![CompilerPass::Compile {
449+
source: PathBuf::from("..").join(source_dir_name).join("main.c"),
450+
output: PathBuf::from("main.o").into(),
451+
flags: vec_of_strings!["-O2"],
452+
}],
453+
};
454+
455+
let result = sut.apply(input.clone());
456+
assert!(result.is_ok());
457+
assert_eq!(result.unwrap(), expected);
458+
}
459+
}
460+
}
215461
}
216462

217463
mod filter {
@@ -428,15 +674,7 @@ mod filter {
428674
}],
429675
working_dir: std::path::PathBuf::from("/project"),
430676
};
431-
let expected = CompilerCall {
432-
compiler: std::path::PathBuf::from("gcc"),
433-
passes: vec![CompilerPass::Compile {
434-
source: PathBuf::from("main.c"),
435-
output: PathBuf::from("main.o").into(),
436-
flags: vec!["-O2".into()],
437-
}],
438-
working_dir: std::path::PathBuf::from("/project"),
439-
};
677+
let expected = input.clone();
440678

441679
let compilers: Vec<Compiler> = vec![];
442680
let sut = SemanticFilter::try_from(compilers.as_slice());

0 commit comments

Comments
 (0)