diff --git a/rust/semantic/src/tools/combinators.rs b/rust/semantic/src/tools/combinators.rs index 0bf41353..4f790ec3 100644 --- a/rust/semantic/src/tools/combinators.rs +++ b/rust/semantic/src/tools/combinators.rs @@ -20,6 +20,9 @@ use crate::{Meaning, RecognitionResult, Tool}; use intercept::ipc::Execution; +/// Represents a set of tools, where any of them can recognize the semantic. +/// The evaluation is done in the order of the tools. The first one which +/// recognizes the semantic will be returned as result. pub struct Any { tools: Vec>, } @@ -31,7 +34,6 @@ impl Any { } impl Tool for Any { - /// Any of the tool recognize the semantic, will be returned as result. fn recognize(&self, x: &Execution) -> RecognitionResult { for tool in &self.tools { match tool.recognize(x) { diff --git a/rust/semantic/src/tools/generic.rs b/rust/semantic/src/tools/generic.rs index 910eab5e..3299997a 100644 --- a/rust/semantic/src/tools/generic.rs +++ b/rust/semantic/src/tools/generic.rs @@ -16,30 +16,33 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ - -use std::path::{Path, PathBuf}; +use std::collections::HashSet; +use std::path::PathBuf; use std::vec; use super::super::{CompilerPass, Meaning, RecognitionResult, Tool}; use super::matchers::source::looks_like_a_source_file; use intercept::ipc::Execution; +/// A tool to recognize a compiler by executable name. pub struct Generic { - pub executable: PathBuf, + executables: HashSet, } impl Generic { - pub fn new(compiler: &Path) -> Box { - Box::new(Self { - executable: compiler.to_path_buf(), - }) + pub fn from(compilers: &[PathBuf]) -> Box { + let executables = compilers.iter().map(|compiler| compiler.clone()).collect(); + Box::new(Self { executables }) } } impl Tool for Generic { - /// Any of the tool recognize the semantic, will be returned as result. + /// This tool is a naive implementation only considering: + /// - the executable name, + /// - one of the arguments is a source file, + /// - the rest of the arguments are flags. fn recognize(&self, x: &Execution) -> RecognitionResult { - if x.executable == self.executable { + if self.executables.contains(&x.executable) { let mut flags = vec![]; let mut sources = vec![]; @@ -80,7 +83,7 @@ mod test { use lazy_static::lazy_static; - use crate::vec_of_strings; + use crate::{vec_of_pathbuf, vec_of_strings}; use super::*; @@ -145,7 +148,7 @@ mod test { lazy_static! { static ref SUT: Generic = Generic { - executable: PathBuf::from("/usr/bin/something"), + executables: vec_of_pathbuf!["/usr/bin/something"].into_iter().collect() }; } } diff --git a/rust/semantic/src/tools/ignore.rs b/rust/semantic/src/tools/ignore.rs index b4fd0c29..0472b8de 100644 --- a/rust/semantic/src/tools/ignore.rs +++ b/rust/semantic/src/tools/ignore.rs @@ -23,6 +23,7 @@ use std::path::PathBuf; use super::super::{Meaning, RecognitionResult, Tool}; use intercept::ipc::Execution; +/// A tool to ignore a command execution by executable name. pub struct IgnoreByPath { executables: HashSet, } @@ -39,6 +40,7 @@ impl IgnoreByPath { } } +/// A tool to ignore a command execution by arguments. impl Tool for IgnoreByPath { fn recognize(&self, execution: &Execution) -> RecognitionResult { if self.executables.contains(&execution.executable) { @@ -193,7 +195,7 @@ mod test { use super::*; #[test] - fn test_unix_tools_are_recognized() { + fn test_executions_are_ignored_by_executable_name() { let input = Execution { executable: PathBuf::from("/usr/bin/ls"), arguments: vec_of_strings!["ls", "/home/user/build"], @@ -221,5 +223,20 @@ mod test { assert_eq!(RecognitionResult::NotRecognized, sut.recognize(&input)) } - // TODO: implement test cases for args + #[test] + fn test_executions_are_ignored_by_args() { + let input = Execution { + executable: PathBuf::from("/usr/bin/ls"), + arguments: vec_of_strings!["ls", "-l", "/home/user/build"], + working_dir: PathBuf::from("/home/user"), + environment: HashMap::new(), + }; + // let sut = IgnoreByArgs::new(&["-l".to_string()]); + let sut = IgnoreByArgs::new(&vec_of_strings!("-l")); + + assert_eq!( + RecognitionResult::Recognized(Ok(Meaning::Ignored)), + sut.recognize(&input) + ) + } } diff --git a/rust/semantic/src/tools/mod.rs b/rust/semantic/src/tools/mod.rs index f0750dde..0bd0539c 100644 --- a/rust/semantic/src/tools/mod.rs +++ b/rust/semantic/src/tools/mod.rs @@ -30,40 +30,42 @@ mod generic; mod ignore; mod matchers; +/// A builder for creating a tool which can recognize the semantic of a compiler, +/// or ignore known non-compilers. pub struct Builder { tools: Vec>, } -// TODO: write unit test for this!!! impl Builder { + /// Creates a new builder with default settings. pub fn new() -> Self { - // FIXME: replace this with gcc, when it's implemented - let gcc = PathBuf::from("/usr/bin/g++"); + // FIXME: replace generic with gcc, when it's implemented Builder { tools: vec![ // ignore executables which are not compilers, IgnoreByPath::new(), // recognize default compiler - Generic::new(gcc.as_path()), + Generic::from(&[PathBuf::from("/usr/bin/g++")]), ], } } + /// Factory method to create a new tool from the builder. pub fn build(self) -> impl Tool { Any::new(self.tools) } + /// Adds new tools to recognize as compilers by executable name. pub fn compilers_to_recognize(mut self, compilers: &[PathBuf]) -> Self { if !compilers.is_empty() { // Add the new compilers at the end of the tools. - for compiler in compilers { - let tool = Generic::new(compiler); - self.tools.push(tool); - } + let tool = Generic::from(compilers); + self.tools.push(tool); } self } + /// Adds new tools to recognize as non-compilers by executable names. pub fn compilers_to_exclude(mut self, compilers: &[PathBuf]) -> Self { if !compilers.is_empty() { // Add these new compilers at the front of the tools. @@ -73,6 +75,7 @@ impl Builder { self } + /// Adds new tools to recognize as non-compilers by arguments. pub fn compilers_to_exclude_by_arguments(mut self, args: &[String]) -> Self { if !args.is_empty() { // Add these new compilers at the front of the tools. @@ -82,3 +85,60 @@ impl Builder { self } } + +#[cfg(test)] +mod test { + use std::collections::HashMap; + use std::path::PathBuf; + + use super::super::{vec_of_pathbuf, vec_of_strings}; + use super::super::{Meaning, RecognitionResult}; + use super::*; + use intercept::ipc::Execution; + + #[test] + fn test_builder() { + let sut = Builder::new().build(); + + let input = any_execution(); + match sut.recognize(&input) { + RecognitionResult::Recognized(Ok(Meaning::Compiler { .. })) => assert!(true), + _ => assert!(false), + } + } + + #[test] + fn test_builder_with_compilers_to_exclude() { + let compilers = vec_of_pathbuf!["/usr/bin/g++"]; + let sut = Builder::new().compilers_to_exclude(&compilers).build(); + + let input = any_execution(); + match sut.recognize(&input) { + RecognitionResult::Recognized(Ok(Meaning::Ignored)) => assert!(true), + _ => assert!(false), + } + } + + #[test] + fn test_builder_with_compilers_to_exclude_by_arguments() { + let args = vec_of_strings!["-c"]; + let sut = Builder::new() + .compilers_to_exclude_by_arguments(&args) + .build(); + + let input = any_execution(); + match sut.recognize(&input) { + RecognitionResult::Recognized(Ok(Meaning::Ignored)) => assert!(true), + _ => assert!(false), + } + } + + fn any_execution() -> Execution { + Execution { + executable: PathBuf::from("/usr/bin/g++"), + arguments: vec_of_strings!["g++", "-c", "main.cpp"], + environment: HashMap::new(), + working_dir: PathBuf::from("/home/user"), + } + } +}