From d476f9e80db693682844e9bcf404d8add90ebbee Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Fri, 3 Nov 2023 09:52:55 -0400 Subject: [PATCH 01/23] adding elvis op --- .../demo/src/main/kotlin/Calculator.kt | 20 ++ src/cli.rs | 1 - src/mutation_tool/mutation.rs | 10 + src/mutation_tool/operators.rs | 254 +++++++++++++++--- src/mutation_tool/tool.rs | 97 ++++++- src/test_config/mod.rs | 8 + 6 files changed, 344 insertions(+), 46 deletions(-) diff --git a/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt b/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt index ddbcbfa..a5c76ba 100644 --- a/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt +++ b/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt @@ -16,4 +16,24 @@ class Calculator { var result:Int? = test!!.length return result } + + fun elvisOperator() { + var b: String? = null + val c = 1 + val x = b?.length ?: 1 + x = b?.length ?: "Hello There" + // x = b?.length ?: c + x = b?.length ?: 1.0 + x = b?.length ?: 1.0f + // x = b?.length ?: 1L + // x = b?.length ?: 1.toByte() + // x = b?.length ?: 1.toShort() + // x = b?.length ?: 1.toChar() + + // if (x > 1) { + // println("Len is greater than 1!") + // } + + println(x) + } } diff --git a/src/cli.rs b/src/cli.rs index 69a3645..3eab054 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -97,7 +97,6 @@ pub fn run_cli(config: KodeKrakenConfig) { .set_general_config(config) .set_mutation_comment(true) .build(); - println!("{:#?}", tool.kodekraken_config.general.timeout); let res = match tool.kodekraken_config.general.timeout { Some(timeout) => { println!("Timeout set to {} seconds", timeout); diff --git a/src/mutation_tool/mutation.rs b/src/mutation_tool/mutation.rs index 24ae937..ab31426 100644 --- a/src/mutation_tool/mutation.rs +++ b/src/mutation_tool/mutation.rs @@ -35,26 +35,36 @@ impl Default for MutationResult { } #[derive(Debug, Clone, Table, serde::Serialize)] +/// Represents a mutation applied to a code file. pub struct Mutation { + /// The unique identifier for the mutation. #[table(title = "Id")] #[serde(skip)] pub id: Uuid, + /// The starting byte of the old operator. #[table(skip)] #[serde(skip)] pub start_byte: usize, + /// The ending byte of the old operator. #[table(skip)] #[serde(skip)] pub end_byte: usize, + /// The name of the file that was mutated. #[table(title = "File Name")] pub file_name: String, + /// The line number where the mutation was applied. #[table(title = "Line Number")] pub line_number: usize, + /// The new operator that was applied. #[table(title = "New Operator")] pub new_op: String, + /// The old operator that was replaced. #[table(title = "Old Operator")] pub old_op: String, + /// The type of mutation that was applied. #[table(title = "Mutation Type")] pub mutation_type: MutationOperators, + /// The result of the mutation. #[table(title = "Result")] pub result: MutationResult, } diff --git a/src/mutation_tool/operators.rs b/src/mutation_tool/operators.rs index 9efb20f..83afebe 100644 --- a/src/mutation_tool/operators.rs +++ b/src/mutation_tool/operators.rs @@ -3,7 +3,7 @@ use crate::{ kotlin_types::KotlinTypes, mutation_tool::Mutation, }; -use std::{collections::HashSet, fmt::Display}; +use std::{collections::HashSet, fmt::Display, fs}; // Struct that stores all the mutations operators by default #[derive(Clone)] @@ -21,6 +21,8 @@ pub enum MutationOperators { AssignmentOperator, UnaryOperator, NotNullAssertionOperator, + RemoveElvisOperator, + ElvisOperator, } impl Display for MutationOperators { @@ -36,6 +38,8 @@ impl Display for MutationOperators { MutationOperators::AssignmentOperator => "AssignmentOperator", MutationOperators::UnaryOperator => "UnaryOperator", MutationOperators::NotNullAssertionOperator => "NotNullAssertionOperator", + MutationOperators::RemoveElvisOperator => "RemoveElvisOperator", + MutationOperators::ElvisOperator => "ElvisOperator", } ) } @@ -102,6 +106,11 @@ impl MutationOperators { ] .into_iter() .collect(), + MutationOperators::RemoveElvisOperator | MutationOperators::ElvisOperator => { + vec![KotlinTypes::NonNamedType("?:".to_string())] + .into_iter() + .collect() + } } } @@ -127,6 +136,9 @@ impl MutationOperators { KotlinTypes::PrefixExpression, ], MutationOperators::NotNullAssertionOperator => vec![KotlinTypes::PostfixExpression], + MutationOperators::RemoveElvisOperator | MutationOperators::ElvisOperator => { + vec![KotlinTypes::ElvisExpression] + } } } @@ -139,7 +151,28 @@ impl MutationOperators { mutations } - /// Recursive function that finds all the mutations that can be made to the file + /// Mutates the given `root` node and its children using the provided `cursor`, `parent`, `mutations_made`, `file_name`, `operators`, and `parent_necessary_types`. + /// + /// # Arguments + /// + /// * `root` - A `tree_sitter::Node` representing the root node to mutate. + /// * `cursor` - A mutable reference to a `tree_sitter::TreeCursor` used to traverse the syntax tree. + /// * `parent` - An optional `tree_sitter::Node` representing the parent node of `root`. + /// * `mutations_made` - A mutable reference to a `Vec` that will be populated with any mutations made during the function's execution. + /// * `file_name` - A `String` representing the name of the file being mutated. + /// + /// # Examples + /// + /// ``` + /// let mut cursor = tree_sitter::TreeCursor::new(); + /// let mut mutations_made = Vec::new(); + /// let root_node = tree_sitter::Node::new(); + /// let parent_node = tree_sitter::Node::new(); + /// let file_name = String::from("example.kt"); + /// let operator = Operator::new(); + /// let parent_necessary_types = vec![KotlinTypes::IfStatement]; + /// operator.mutate(root_node, &mut cursor, Some(parent_node), &mut mutations_made, &file_name); + /// ``` fn mutate( &self, root: tree_sitter::Node, @@ -152,33 +185,44 @@ impl MutationOperators { let root_type = KotlinTypes::new(node.kind()).expect("Failed to convert to KotlinType"); let parent_type = parent .map(|p| KotlinTypes::new(p.kind()).expect("Failed to convert to KotlinType")); + if root_type == KotlinTypes::NonNamedType("?:".into()) { + // println!("Found elvis operator"); + // println!("{:#?}", node); + // println!("{:#?}", node.parent()); + } mutations_made.append( &mut self - .mutate_operator( - &node, - &root_type, - &parent_type, - self.get_operators(), - self.get_parent_necessary_types(), - file_name, - ) + .mutate_operator(&node, &root_type, &parent_type, file_name) .expect("Failed to mutate an operator"), ); self.mutate(node, cursor, Some(node), mutations_made, file_name); }); } - /// Checks to see if the mutation operator can be applied to the node + /// Mutates the given root node based on the specified mutation operators and returns a vector of mutations made. + /// + /// # Arguments + /// + /// * `root_node` - A reference to the root node of the AST. + /// * `root` - A reference to the root type of the AST. + /// * `parent` - An optional reference to the parent type of the AST. + /// * `mutation_operators` - A HashSet of mutation operators to apply. + /// * `parent_types` - A vector of parent types to check against. + /// * `file_name` - The name of the file being mutated. + /// + /// # Returns + /// + /// A Result containing a vector of mutations made. fn mutate_operator( &self, root_node: &tree_sitter::Node, root: &KotlinTypes, parent: &Option, - mutation_operators: HashSet, - parent_types: Vec, file_name: &str, ) -> Result> { let mut mutations_made = Vec::new(); + let mutation_operators = self.get_operators(); + let parent_types = self.get_parent_necessary_types(); // Check to see if root type is in the mutation_operators // Check to see if the parent exists // Checks to see if the parent is the necessary kotlin type @@ -189,36 +233,127 @@ impl MutationOperators { return Ok(mutations_made); } - if *self == MutationOperators::UnaryRemovalOperator { - // If the operator is a unary removal operator, we just remove the operator - let mutation = Mutation::new( - root_node.start_byte(), - root_node.end_byte(), - KotlinTypes::RemoveOperator.to_string(), - root.as_str(), - root_node.start_position().row + 1, - self.clone(), - file_name.to_string(), - ); - mutations_made.push(mutation); - return Ok(mutations_made); - } - - // Create a mutant for all mutation operators - mutation_operators.iter().for_each(|operator| { - if operator != root { + match *self { + MutationOperators::UnaryRemovalOperator => { + // If the operator is a unary removal operator, we just remove the operator let mutation = Mutation::new( root_node.start_byte(), root_node.end_byte(), - operator.to_string(), + KotlinTypes::RemoveOperator.to_string(), root.as_str(), root_node.start_position().row + 1, self.clone(), file_name.to_string(), ); - mutations_made.push(mutation) + mutations_made.push(mutation); } - }); + MutationOperators::RemoveElvisOperator => { + // If the operator is a Remove elvis operator, we remove the operator and everything after it + // Get the end byte of the end of the line + let end_byte = root_node.parent().unwrap().end_byte(); // TODO: remove unwrap + + let mutation = Mutation::new( + root_node.start_byte(), + end_byte, + KotlinTypes::RemoveOperator.to_string(), + root.as_str(), + root_node.start_position().row + 1, + self.clone(), + file_name.to_string(), + ); + mutations_made.push(mutation); + } + MutationOperators::ElvisOperator => { + println!("Found elvis operator for mutation"); + let parent_node = root_node.parent().unwrap(); + println!("Parent node: {:#?}", parent_node); + let file = fs::read(file_name).expect("Failed to read file"); + let file = file.as_slice(); + parent_node + .children(&mut parent_node.walk()) + .for_each(|node| { + let child_type = + KotlinTypes::new(node.kind()).expect("Failed to convert to KotlinType"); + + println!("Child type: {:#?}", child_type); + + // Change the literal to a different literal + let mut val = node.utf8_text(file).unwrap(); + match child_type { + KotlinTypes::IntegerLiteral => { + let val = val.parse::().unwrap(); + // Change the value and create a mutation + let rnd_val = rand::random::() % 100; + let mutated_val = val & rnd_val; + println!("new value is: {}", mutated_val); + let mutation = Mutation::new( + node.start_byte(), + node.end_byte(), + mutated_val.to_string(), + val.to_string(), + node.start_position().row + 1, + self.clone(), + file_name.to_string(), + ); + mutations_made.push(mutation); + } + KotlinTypes::PrefixExpression => { + println!("Found prefix expression"); + } + KotlinTypes::LineStringLiteral => { + println!("Value is: {}", val); + println!("Found line string literal"); + } + KotlinTypes::BooleanLiteral => { + let val = val.parse::().unwrap(); + println!("Value is: {}", val); + println!("Found boolean literal"); + } + KotlinTypes::LongLiteral => { + // Need to strip off the l at the end + if val.ends_with("l") { + val = val.strip_suffix("l").unwrap(); + } + + let val = val.parse::().unwrap(); + println!("Value is: {}", val); + println!("Found long literal"); + } + KotlinTypes::RealLiteral => { + // Need to strip off the f at the end + if val.ends_with("f") { + val = val.strip_suffix("f").unwrap(); + } + let val = val.parse::().unwrap(); + println!("Value is: {}", val); + println!("Found real literal"); + } + KotlinTypes::CharacterLiteral => { + println!("Found character literal"); + } + _ => {} + } + }); + } + _ => { + // Create a mutant for all mutation operators + mutation_operators.iter().for_each(|operator| { + if operator != root { + let mutation = Mutation::new( + root_node.start_byte(), + root_node.end_byte(), + operator.to_string(), + root.as_str(), + root_node.start_position().row + 1, + self.clone(), + file_name.to_string(), + ); + mutations_made.push(mutation) + } + }); + } + } + Ok(mutations_made) } } @@ -227,13 +362,15 @@ impl AllMutationOperators { pub fn new() -> Self { Self { mutation_operators: vec![ - MutationOperators::ArthimeticOperator, - MutationOperators::UnaryRemovalOperator, - MutationOperators::LogicalOperator, - MutationOperators::RelationalOperator, - MutationOperators::AssignmentOperator, - MutationOperators::UnaryOperator, - MutationOperators::NotNullAssertionOperator, + // MutationOperators::ArthimeticOperator, + // MutationOperators::UnaryRemovalOperator, + // MutationOperators::LogicalOperator, + // MutationOperators::RelationalOperator, + // MutationOperators::AssignmentOperator, + // MutationOperators::UnaryOperator, + // MutationOperators::NotNullAssertionOperator, + MutationOperators::RemoveElvisOperator, + MutationOperators::ElvisOperator, ], } } @@ -391,6 +528,26 @@ mod tests { } } + #[test] + fn test_elvis_remove_operator() { + let tree = get_ast(KOTLIN_ELVIS_TEST_CODE); + let root = tree.root_node(); + let mut mutations_made = Vec::new(); + MutationOperators::RemoveElvisOperator.mutate( + root, + &mut root.walk(), + None, + &mut mutations_made, + &"".into(), + ); + assert_eq!(mutations_made.len(), 1); + // Assert that the old operator is not the same as the new operator + for mutation in mutations_made { + assert_ne!(mutation.old_op, mutation.new_op); + assert_eq!(mutation.new_op, "".to_string()); + } + } + #[test] fn test_arthimetic_operator_does_not_create_mutations() { let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); @@ -450,4 +607,19 @@ mod tests { ); assert_eq!(mutations_made.len(), 0); } + + #[test] + fn test_remove_elvis_operator_does_not_create_mutations() { + let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); + let root = tree.root_node(); + let mut mutations_made = Vec::new(); + MutationOperators::RemoveElvisOperator.mutate( + root, + &mut root.walk(), + None, + &mut mutations_made, + &"".into(), + ); + assert_eq!(mutations_made.len(), 0); + } } diff --git a/src/mutation_tool/tool.rs b/src/mutation_tool/tool.rs index fa8307d..28fca5f 100644 --- a/src/mutation_tool/tool.rs +++ b/src/mutation_tool/tool.rs @@ -50,6 +50,19 @@ impl Default for MutationTool { } impl MutationTool { + /// Creates a new instance of the mutation tool with the given configuration. + /// + /// # Arguments + /// + /// * `mutate_config` - The mutation command configuration. + /// * `kodekraken_config` - The KodeKraken configuration. + /// * `output_directory` - The output directory for the mutated files. + /// * `mutation_operators` - The mutation operators to use. + /// * `enable_mutation_comment` - Whether to enable mutation comments in the mutated files. + /// + /// # Returns + /// + /// A `Result` containing the new instance of the mutation tool, or an error if one occurred. pub fn new( mutate_config: MutationCommandConfig, kodekraken_config: KodeKrakenConfig, @@ -110,6 +123,16 @@ impl MutationTool { }) } + /// Creates a mutated file name by appending the mutation ID to the original file name. + /// + /// # Arguments + /// + /// * `file_name` - A string slice that holds the name of the original file. + /// * `mutation` - A reference to a `Mutation` struct that holds the mutation ID. + /// + /// # Returns + /// + /// A `Result` that contains a string with the mutated file name if successful, or a `KodeKrakenError` if an error occurs. fn create_mutated_file_name(&self, file_name: &str, mutation: &Mutation) -> Result { Ok(format!( "{}_{}", @@ -126,6 +149,8 @@ impl MutationTool { )) } + /// Mutates the project by gathering files, gathering mutations per file, generating mutations per file, + /// building and testing, reporting results, saving results in csv, and generating an HTML report. pub fn mutate(&mut self) -> Result<()> { tracing::info!("Mutation tool started..."); // Phase 1: Get files from project @@ -168,6 +193,27 @@ impl MutationTool { Ok(()) } + /// Saves the given mutations to a CSV file located in the `OUT_DIRECTORY`. + /// + /// # Arguments + /// + /// * `mutations` - A reference to a vector of `Mutation` structs to be saved. + /// + /// # Errors + /// + /// Returns a `KodeKrakenError` if there was an error creating the CSV writer, serializing a mutation, + /// flushing the CSV writer, or writing to the output file. + /// + /// # Examples + /// + /// ``` + /// use mutation_tool::tool::Tool; + /// + /// let tool = Tool::new(); + /// let mutations = vec![Mutation::new("file.rs", 10, "foo".to_string(), "bar".to_string())]; + /// let result = tool.save_results(&mutations); + /// assert!(result.is_ok()); + /// ``` fn save_results(&self, mutations: &Vec) -> Result<()> { let mut writer = csv::WriterBuilder::new() .from_path(Path::new(OUT_DIRECTORY).join("output.csv")) @@ -185,6 +231,9 @@ impl MutationTool { Ok(()) } + /// This function retrieves all files from the project directory specified in the `mutate_config` field of the `Tool` struct. + /// It returns a `Result` containing a `Vec` of `String`s representing the file paths. + /// If no files are found in the directory, it returns an `Err` containing a `KodeKrakenError`. fn get_files_from_project(&self) -> Result> { let start = Instant::now(); let mut existing_files: Vec = vec![]; @@ -268,13 +317,32 @@ impl MutationTool { Ok(()) } + /// Builds and tests mutated files in parallel using a thread pool. + /// + /// # Arguments + /// + /// * `file_mutations` - A hashmap containing the mutations for each file. + /// + /// # Returns + /// + /// A `Result` containing a vector of `Mutation` structs. + /// + /// # Examples + /// + /// ``` + /// let mut tool = Tool::new(config); + /// let file_mutations = tool.generate_mutations()?; + /// let mutations = tool.build_and_test(&file_mutations)?; + /// ``` fn build_and_test( &mut self, file_mutations: &HashMap, ) -> Result> { + // Get total number of mutations let num_mutations = file_mutations .iter() .fold(0, |acc, (_, fm)| acc + fm.mutations.len()); + // Set up progress bar let progress_bar = Arc::new(ProgressBar::new(num_mutations as u64)); progress_bar.set_style( ProgressStyle::default_bar() @@ -283,6 +351,7 @@ impl MutationTool { .progress_chars("=> "), ); + // Make Copies of all files self.copy_files(file_mutations)?; // Merge all mutants into one vector @@ -293,7 +362,8 @@ impl MutationTool { } } // Partition the mutations into chunks - let chunk_size = ((all_mutations.len() as f32) / MAX_BUILD_THREADS) as usize; + let chunk_size = ((all_mutations.len() as f32) / MAX_BUILD_THREADS).ceil() as usize; + let mut chunks: Vec> = all_mutations .chunks(chunk_size) .map(|c| c.to_vec()) @@ -411,6 +481,9 @@ impl MutationTool { Ok(()) } + /// Generates mutations for each file in the given `file_mutations` HashMap. + /// Each mutation is applied to the corresponding file, and the resulting mutated file is written to + /// the output directory specified in the `MutationTool` configuration. fn generate_mutations_per_file( &self, file_mutations: &HashMap, @@ -418,9 +491,11 @@ impl MutationTool { tracing::info!("Generating mutations per file"); let start = Instant::now(); self.thread_pool.scope(|s| { + // Iterate over each file and generate mutations file_mutations.iter().for_each(|(file_name, fm)| { let file_str = fs::read_to_string(file_name).expect("Failed to read file"); s.spawn(move |_| { + // Iterate over each mutation and apply it to the file fm.mutations.iter().for_each(|m| { let new_op_bytes = m.new_op.as_bytes(); let mut file = file_str.as_bytes().to_vec(); @@ -460,6 +535,23 @@ impl MutationTool { Ok(()) } + /// Gathers mutations for each file in the given list of existing files. + /// + /// # Arguments + /// + /// * `existing_files` - A mutable slice of strings representing the paths to the existing files. + /// + /// # Returns + /// + /// A `Result` containing a `HashMap` of `String` keys and `FileMutations` values, or an error if the operation fails. + /// + /// # Examples + /// + /// ``` + /// let mut tool = Tool::new(); + /// let existing_files = vec!["/path/to/file1.rs", "/path/to/file2.rs"]; + /// let mutations = tool.gather_mutations_per_file(&mut existing_files).unwrap(); + /// ``` fn gather_mutations_per_file( &mut self, existing_files: &mut [String], @@ -515,9 +607,6 @@ impl MutationTool { .map_err(|_| KodeKrakenError::Error("Failed to unwrap file_mutations".to_string()))?) } - /* - Take in path to directory and get all files that end with .kt - */ fn get_files_from_directory( &self, path: String, diff --git a/src/test_config/mod.rs b/src/test_config/mod.rs index 327392e..8066b82 100644 --- a/src/test_config/mod.rs +++ b/src/test_config/mod.rs @@ -62,3 +62,11 @@ h *= 2 h /= 4 h %= 2 "#; + +pub const KOTLIN_ELVIS_TEST_CODE: &str = r#" +fun main() { + val a = 10 + val b = 3 + val c = a ?: b +} +"#; From 394953e3e85f1a2519c96c87a2f579924173e0f0 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Sat, 18 Nov 2023 00:38:57 -0500 Subject: [PATCH 02/23] working on adding new operator --- .../demo/src/main/kotlin/Calculator.kt | 34 +-- src/mutation_tool/operators.rs | 218 +++++++++++------- 2 files changed, 156 insertions(+), 96 deletions(-) diff --git a/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt b/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt index a5c76ba..afe5236 100644 --- a/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt +++ b/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt @@ -17,23 +17,23 @@ class Calculator { return result } - fun elvisOperator() { - var b: String? = null - val c = 1 - val x = b?.length ?: 1 - x = b?.length ?: "Hello There" - // x = b?.length ?: c - x = b?.length ?: 1.0 - x = b?.length ?: 1.0f - // x = b?.length ?: 1L - // x = b?.length ?: 1.toByte() - // x = b?.length ?: 1.toShort() - // x = b?.length ?: 1.toChar() + // fun elvisOperator() { + // var b: String? = null + // val c = 1 + // val x = b?.length ?: -11 + // x = b?.length ?: "Hello There" + // // x = b?.length ?: c + // x = b?.length ?: 1.0 + // x = b?.length ?: 1.0f + // x = b?.length ?: 1L + // // x = b?.length ?: 'a' + // // x = b?.length ?: 1.toShort() + // // x = b?.length ?: 1.toChar() - // if (x > 1) { - // println("Len is greater than 1!") - // } + // // if (x > 1) { + // // println("Len is greater than 1!") + // // } - println(x) - } + // println(x) + // } } diff --git a/src/mutation_tool/operators.rs b/src/mutation_tool/operators.rs index 83afebe..0ec8765 100644 --- a/src/mutation_tool/operators.rs +++ b/src/mutation_tool/operators.rs @@ -1,3 +1,5 @@ +use rand::Rng; + use crate::{ error::{KodeKrakenError, Result}, kotlin_types::KotlinTypes, @@ -264,76 +266,7 @@ impl MutationOperators { mutations_made.push(mutation); } MutationOperators::ElvisOperator => { - println!("Found elvis operator for mutation"); - let parent_node = root_node.parent().unwrap(); - println!("Parent node: {:#?}", parent_node); - let file = fs::read(file_name).expect("Failed to read file"); - let file = file.as_slice(); - parent_node - .children(&mut parent_node.walk()) - .for_each(|node| { - let child_type = - KotlinTypes::new(node.kind()).expect("Failed to convert to KotlinType"); - - println!("Child type: {:#?}", child_type); - - // Change the literal to a different literal - let mut val = node.utf8_text(file).unwrap(); - match child_type { - KotlinTypes::IntegerLiteral => { - let val = val.parse::().unwrap(); - // Change the value and create a mutation - let rnd_val = rand::random::() % 100; - let mutated_val = val & rnd_val; - println!("new value is: {}", mutated_val); - let mutation = Mutation::new( - node.start_byte(), - node.end_byte(), - mutated_val.to_string(), - val.to_string(), - node.start_position().row + 1, - self.clone(), - file_name.to_string(), - ); - mutations_made.push(mutation); - } - KotlinTypes::PrefixExpression => { - println!("Found prefix expression"); - } - KotlinTypes::LineStringLiteral => { - println!("Value is: {}", val); - println!("Found line string literal"); - } - KotlinTypes::BooleanLiteral => { - let val = val.parse::().unwrap(); - println!("Value is: {}", val); - println!("Found boolean literal"); - } - KotlinTypes::LongLiteral => { - // Need to strip off the l at the end - if val.ends_with("l") { - val = val.strip_suffix("l").unwrap(); - } - - let val = val.parse::().unwrap(); - println!("Value is: {}", val); - println!("Found long literal"); - } - KotlinTypes::RealLiteral => { - // Need to strip off the f at the end - if val.ends_with("f") { - val = val.strip_suffix("f").unwrap(); - } - let val = val.parse::().unwrap(); - println!("Value is: {}", val); - println!("Found real literal"); - } - KotlinTypes::CharacterLiteral => { - println!("Found character literal"); - } - _ => {} - } - }); + self.mutate_literal(&root_node.parent().unwrap(), &mut mutations_made, file_name) } _ => { // Create a mutant for all mutation operators @@ -356,21 +289,148 @@ impl MutationOperators { Ok(mutations_made) } + + fn mutate_literal( + &self, + root_node: &tree_sitter::Node, + mutations_made: &mut Vec, + file_name: &str, + ) { + let file = fs::read(file_name).expect("Failed to read file"); + let file = file.as_slice(); + let children = root_node + .children(&mut root_node.walk()) + .collect::>(); + println!("Children: {:#?}", children); + println!("Num of children: {}", children.len()); + let node = children.iter().last().unwrap(); + + let child_type = KotlinTypes::new(node.kind()).expect("Failed to convert to KotlinType"); + + println!("Child type: {:#?}", child_type); + + // Change the literal to a different literal + let mut val = node.utf8_text(file).unwrap(); + match child_type { + KotlinTypes::IntegerLiteral => { + let val = val.parse::().unwrap(); + // Change the value and create a mutation + let rnd_val = rand::random::() % 100; + let mutated_val = val & rnd_val; + println!("new value is: {}", mutated_val); + let mutation = Mutation::new( + node.start_byte(), + node.end_byte(), + mutated_val.to_string(), + val.to_string(), + node.start_position().row + 1, + self.clone(), + file_name.to_string(), + ); + mutations_made.push(mutation); + } + KotlinTypes::PrefixExpression => { + // In this case, we need to see the type of the prefix expression, so we need to + // Recurse down to the literal + self.mutate_literal(node, mutations_made, file_name) + } + KotlinTypes::LineStringLiteral => { + // Replace the string with a different string + let val = r#""Hello I am a Mutant!""#.to_string(); + + let mutation = Mutation::new( + node.start_byte(), + node.end_byte(), + val, + node.utf8_text(file).unwrap().to_string(), + node.start_position().row + 1, + self.clone(), + file_name.to_string(), + ); + mutations_made.push(mutation); + } + KotlinTypes::BooleanLiteral => { + let val = val.parse::().unwrap(); + + // Change the value and create a mutation + let mutated_val = !val; + + let mutation = Mutation::new( + node.start_byte(), + node.end_byte(), + mutated_val.to_string(), + val.to_string(), + node.start_position().row + 1, + self.clone(), + file_name.to_string(), + ); + mutations_made.push(mutation); + } + KotlinTypes::LongLiteral => { + // Need to strip off the l at the end + if val.ends_with("L") { + val = val.strip_suffix("L").unwrap(); + } + + let val = val.parse::().unwrap(); + println!("Value is: {}", val); + println!("Found long literal"); + } + KotlinTypes::RealLiteral => { + // Need to strip off the f at the end + if val.ends_with("f") { + val = val.strip_suffix("f").unwrap(); + } + let val = val.parse::().unwrap(); + println!("Value is: {}", val); + println!("Found real literal"); + } + KotlinTypes::CharacterLiteral => { + // Remove the single quotes and get the value + let val = val + .strip_prefix("'") + .unwrap() + .strip_suffix("'") + .unwrap() + .chars() + .next() + .unwrap(); + + // Get a random character between 'a' and 'z' + let mut rnd_val = rand::thread_rng().gen_range(b'a'..b'z') as char; + while rnd_val == val { + rnd_val = rand::thread_rng().gen_range(b'a'..b'z') as char; + } + let mut_val = format!("'{}'", rnd_val); + let mutation = Mutation::new( + node.start_byte(), + node.end_byte(), + mut_val, + val.to_string(), + node.start_position().row + 1, + self.clone(), + file_name.to_string(), + ); + mutations_made.push(mutation); + } + _ => {} + } + } } impl AllMutationOperators { pub fn new() -> Self { Self { mutation_operators: vec![ - // MutationOperators::ArthimeticOperator, - // MutationOperators::UnaryRemovalOperator, - // MutationOperators::LogicalOperator, - // MutationOperators::RelationalOperator, - // MutationOperators::AssignmentOperator, - // MutationOperators::UnaryOperator, - // MutationOperators::NotNullAssertionOperator, - MutationOperators::RemoveElvisOperator, - MutationOperators::ElvisOperator, + MutationOperators::ArthimeticOperator, + MutationOperators::UnaryRemovalOperator, + MutationOperators::LogicalOperator, + MutationOperators::RelationalOperator, + MutationOperators::AssignmentOperator, + MutationOperators::UnaryOperator, + MutationOperators::NotNullAssertionOperator, + // MutationOperators::RemoveElvisOperator, + // MutationOperators::ElvisOperator, ], } } From ffcbac288c2b56e949e6efc8fb18b8771753a696 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Sat, 18 Nov 2023 00:46:18 -0500 Subject: [PATCH 03/23] rename mutation operators --- .../demo/src/main/kotlin/Calculator.kt | 2 +- kotlin-test-projects/mutations/BuildFails.kt | 2 +- kotlin-test-projects/mutations/Killed.kt | 2 +- kotlin-test-projects/mutations/Survived.kt | 2 +- src/gradle.rs | 4 +- src/mutation_tool/operators.rs | 94 ++++++++++--------- src/mutation_tool/tool.rs | 20 ++-- 7 files changed, 64 insertions(+), 62 deletions(-) diff --git a/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt b/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt index afe5236..75a6d43 100644 --- a/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt +++ b/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt @@ -17,7 +17,7 @@ class Calculator { return result } - // fun elvisOperator() { + // fun ElvisLiteralChangeOperator() { // var b: String? = null // val c = 1 // val x = b?.length ?: -11 diff --git a/kotlin-test-projects/mutations/BuildFails.kt b/kotlin-test-projects/mutations/BuildFails.kt index b469653..5d4bf18 100644 --- a/kotlin-test-projects/mutations/BuildFails.kt +++ b/kotlin-test-projects/mutations/BuildFails.kt @@ -20,7 +20,7 @@ class Calculator { /** AUTO GENERATED COMMENT - Mutation Operator: UnaryOperator + Mutation Operator: UnaryReplacementOperator Line number: 29 Id: eeea3a90-506d-49c7-b0ab-219fecc09ceb, Old Operator: ++, diff --git a/kotlin-test-projects/mutations/Killed.kt b/kotlin-test-projects/mutations/Killed.kt index 3310703..ca36d9f 100644 --- a/kotlin-test-projects/mutations/Killed.kt +++ b/kotlin-test-projects/mutations/Killed.kt @@ -23,7 +23,7 @@ class Calculator { /** AUTO GENERATED COMMENT - Mutation Operator: AssignmentOperator + Mutation Operator: AssignmentReplacementOperator Line number: 32 Id: e4f91eaa-6011-44cd-ae0b-0668eda62769, Old Operator: *=, diff --git a/kotlin-test-projects/mutations/Survived.kt b/kotlin-test-projects/mutations/Survived.kt index 657d75a..405ee8c 100644 --- a/kotlin-test-projects/mutations/Survived.kt +++ b/kotlin-test-projects/mutations/Survived.kt @@ -11,7 +11,7 @@ class Calculator { /** AUTO GENERATED COMMENT - Mutation Operator: AssignmentOperator + Mutation Operator: AssignmentReplacementOperator Line number: 20 Id: 7d99bafd-300e-4fb0-9527-524b808763f6, Old Operator: =, diff --git a/src/gradle.rs b/src/gradle.rs index a6242dd..10c42df 100644 --- a/src/gradle.rs +++ b/src/gradle.rs @@ -148,7 +148,7 @@ mod test { "new_op".into(), "old_op".into(), 0, - crate::mutation_tool::MutationOperators::ArthimeticOperator, + crate::mutation_tool::MutationOperators::ArithmeticReplacementOperator, "file_name".into(), ), ) @@ -170,7 +170,7 @@ mod test { "new_op".into(), "old_op".into(), 0, - crate::mutation_tool::MutationOperators::ArthimeticOperator, + crate::mutation_tool::MutationOperators::ArithmeticReplacementOperator, "file_name".into(), ); run( diff --git a/src/mutation_tool/operators.rs b/src/mutation_tool/operators.rs index 0ec8765..792e832 100644 --- a/src/mutation_tool/operators.rs +++ b/src/mutation_tool/operators.rs @@ -16,15 +16,15 @@ pub struct AllMutationOperators { // The different types of mutation operators that can be performed on a file #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] pub enum MutationOperators { - ArthimeticOperator, + ArithmeticReplacementOperator, UnaryRemovalOperator, - LogicalOperator, - RelationalOperator, - AssignmentOperator, - UnaryOperator, + LogicalReplacementOperator, + RelationalReplacementOperator, + AssignmentReplacementOperator, + UnaryReplacementOperator, NotNullAssertionOperator, - RemoveElvisOperator, - ElvisOperator, + ElvisRemoveOperator, + ElvisLiteralChangeOperator, } impl Display for MutationOperators { @@ -33,15 +33,15 @@ impl Display for MutationOperators { f, "{}", match self { - MutationOperators::ArthimeticOperator => "ArthimeticOperator", + MutationOperators::ArithmeticReplacementOperator => "ArithmeticReplacementOperator", MutationOperators::UnaryRemovalOperator => "UnaryRemovalOperator", - MutationOperators::LogicalOperator => "LogicalOperator", - MutationOperators::RelationalOperator => "RelationalOperator", - MutationOperators::AssignmentOperator => "AssignmentOperator", - MutationOperators::UnaryOperator => "UnaryOperator", + MutationOperators::LogicalReplacementOperator => "LogicalReplacementOperator", + MutationOperators::RelationalReplacementOperator => "RelationalReplacementOperator", + MutationOperators::AssignmentReplacementOperator => "AssignmentReplacementOperator", + MutationOperators::UnaryReplacementOperator => "UnaryReplacementOperator", MutationOperators::NotNullAssertionOperator => "NotNullAssertionOperator", - MutationOperators::RemoveElvisOperator => "RemoveElvisOperator", - MutationOperators::ElvisOperator => "ElvisOperator", + MutationOperators::ElvisRemoveOperator => "ElvisRemoveOperator", + MutationOperators::ElvisLiteralChangeOperator => "ElvisLiteralChangeOperator", } ) } @@ -51,7 +51,7 @@ impl MutationOperators { /// Get the operators that correspond to the mutation operator fn get_operators(&self) -> HashSet { match self { - MutationOperators::ArthimeticOperator => vec![ + MutationOperators::ArithmeticReplacementOperator => vec![ KotlinTypes::NonNamedType("+".to_string()), KotlinTypes::NonNamedType("-".to_string()), KotlinTypes::NonNamedType("*".to_string()), @@ -67,13 +67,13 @@ impl MutationOperators { ] .into_iter() .collect(), - MutationOperators::LogicalOperator => vec![ + MutationOperators::LogicalReplacementOperator => vec![ KotlinTypes::NonNamedType("&&".to_string()), KotlinTypes::NonNamedType("||".to_string()), ] .into_iter() .collect(), - MutationOperators::RelationalOperator => vec![ + MutationOperators::RelationalReplacementOperator => vec![ KotlinTypes::NonNamedType("==".to_string()), KotlinTypes::NonNamedType("!=".to_string()), KotlinTypes::NonNamedType("<".to_string()), @@ -83,7 +83,7 @@ impl MutationOperators { ] .into_iter() .collect(), - MutationOperators::AssignmentOperator => vec![ + MutationOperators::AssignmentReplacementOperator => vec![ KotlinTypes::NonNamedType("=".to_string()), KotlinTypes::NonNamedType("+=".to_string()), KotlinTypes::NonNamedType("-=".to_string()), @@ -93,7 +93,7 @@ impl MutationOperators { ] .into_iter() .collect(), - MutationOperators::UnaryOperator => vec![ + MutationOperators::UnaryReplacementOperator => vec![ KotlinTypes::NonNamedType("!".to_string()), KotlinTypes::NonNamedType("++".to_string()), KotlinTypes::NonNamedType("--".to_string()), @@ -108,7 +108,8 @@ impl MutationOperators { ] .into_iter() .collect(), - MutationOperators::RemoveElvisOperator | MutationOperators::ElvisOperator => { + MutationOperators::ElvisRemoveOperator + | MutationOperators::ElvisLiteralChangeOperator => { vec![KotlinTypes::NonNamedType("?:".to_string())] .into_iter() .collect() @@ -119,26 +120,27 @@ impl MutationOperators { /// Get the parent types that are necessary for the mutation operator fn get_parent_necessary_types(&self) -> Vec { match self { - MutationOperators::ArthimeticOperator => vec![ + MutationOperators::ArithmeticReplacementOperator => vec![ KotlinTypes::AdditiveExpression, KotlinTypes::MultiplicativeExpression, ], MutationOperators::UnaryRemovalOperator => vec![KotlinTypes::PrefixExpression], - MutationOperators::LogicalOperator => vec![ + MutationOperators::LogicalReplacementOperator => vec![ KotlinTypes::ConjunctionExpression, KotlinTypes::DisjunctionExpression, ], - MutationOperators::RelationalOperator => vec![ + MutationOperators::RelationalReplacementOperator => vec![ KotlinTypes::EqualityExpression, KotlinTypes::ComparisonExpression, ], - MutationOperators::AssignmentOperator => vec![KotlinTypes::Assignment], - MutationOperators::UnaryOperator => vec![ + MutationOperators::AssignmentReplacementOperator => vec![KotlinTypes::Assignment], + MutationOperators::UnaryReplacementOperator => vec![ KotlinTypes::PostfixExpression, KotlinTypes::PrefixExpression, ], MutationOperators::NotNullAssertionOperator => vec![KotlinTypes::PostfixExpression], - MutationOperators::RemoveElvisOperator | MutationOperators::ElvisOperator => { + MutationOperators::ElvisRemoveOperator + | MutationOperators::ElvisLiteralChangeOperator => { vec![KotlinTypes::ElvisExpression] } } @@ -249,7 +251,7 @@ impl MutationOperators { ); mutations_made.push(mutation); } - MutationOperators::RemoveElvisOperator => { + MutationOperators::ElvisRemoveOperator => { // If the operator is a Remove elvis operator, we remove the operator and everything after it // Get the end byte of the end of the line let end_byte = root_node.parent().unwrap().end_byte(); // TODO: remove unwrap @@ -265,7 +267,7 @@ impl MutationOperators { ); mutations_made.push(mutation); } - MutationOperators::ElvisOperator => { + MutationOperators::ElvisLiteralChangeOperator => { self.mutate_literal(&root_node.parent().unwrap(), &mut mutations_made, file_name) } _ => { @@ -422,15 +424,15 @@ impl AllMutationOperators { pub fn new() -> Self { Self { mutation_operators: vec![ - MutationOperators::ArthimeticOperator, + MutationOperators::ArithmeticReplacementOperator, MutationOperators::UnaryRemovalOperator, - MutationOperators::LogicalOperator, - MutationOperators::RelationalOperator, - MutationOperators::AssignmentOperator, - MutationOperators::UnaryOperator, + MutationOperators::LogicalReplacementOperator, + MutationOperators::RelationalReplacementOperator, + MutationOperators::AssignmentReplacementOperator, + MutationOperators::UnaryReplacementOperator, MutationOperators::NotNullAssertionOperator, - // MutationOperators::RemoveElvisOperator, - // MutationOperators::ElvisOperator, + // MutationOperators::ElvisRemoveOperator, + // MutationOperators::ElvisLiteralChangeOperator, ], } } @@ -473,7 +475,7 @@ mod tests { let tree = get_ast(KOTLIN_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::ArthimeticOperator.mutate( + MutationOperators::ArithmeticReplacementOperator.mutate( root, &mut root.walk(), None, @@ -492,7 +494,7 @@ mod tests { let tree = get_ast(KOTLIN_RELATIONAL_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::RelationalOperator.mutate( + MutationOperators::RelationalReplacementOperator.mutate( root, &mut root.walk(), None, @@ -512,7 +514,7 @@ mod tests { let tree = get_ast(KOTLIN_LOGICAL_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::LogicalOperator.mutate( + MutationOperators::LogicalReplacementOperator.mutate( root, &mut root.walk(), None, @@ -532,7 +534,7 @@ mod tests { let tree = get_ast(KOTLIN_ASSIGNMENT_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::AssignmentOperator.mutate( + MutationOperators::AssignmentReplacementOperator.mutate( root, &mut root.walk(), None, @@ -552,7 +554,7 @@ mod tests { let tree = get_ast(KOTLIN_UNARY_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::UnaryOperator.mutate( + MutationOperators::UnaryReplacementOperator.mutate( root, &mut root.walk(), None, @@ -593,7 +595,7 @@ mod tests { let tree = get_ast(KOTLIN_ELVIS_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::RemoveElvisOperator.mutate( + MutationOperators::ElvisRemoveOperator.mutate( root, &mut root.walk(), None, @@ -613,7 +615,7 @@ mod tests { let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::ArthimeticOperator.mutate( + MutationOperators::ArithmeticReplacementOperator.mutate( root, &mut root.walk(), None, @@ -628,7 +630,7 @@ mod tests { let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::RelationalOperator.mutate( + MutationOperators::RelationalReplacementOperator.mutate( root, &mut root.walk(), None, @@ -643,7 +645,7 @@ mod tests { let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::LogicalOperator.mutate( + MutationOperators::LogicalReplacementOperator.mutate( root, &mut root.walk(), None, @@ -658,7 +660,7 @@ mod tests { let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::AssignmentOperator.mutate( + MutationOperators::AssignmentReplacementOperator.mutate( root, &mut root.walk(), None, @@ -673,7 +675,7 @@ mod tests { let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); let root = tree.root_node(); let mut mutations_made = Vec::new(); - MutationOperators::RemoveElvisOperator.mutate( + MutationOperators::ElvisRemoveOperator.mutate( root, &mut root.walk(), None, diff --git a/src/mutation_tool/tool.rs b/src/mutation_tool/tool.rs index 28fca5f..79a4f9d 100644 --- a/src/mutation_tool/tool.rs +++ b/src/mutation_tool/tool.rs @@ -786,7 +786,7 @@ mod tests { let mut mutator = create_mutator_with_specific_operators( mutation_test_id, output_directory.clone(), - vec![MutationOperators::ArthimeticOperator], + vec![MutationOperators::ArithmeticReplacementOperator], ); assert_all_mutation_files_were_created(&mut mutator, mutation_test_id, output_directory); } @@ -797,7 +797,7 @@ mod tests { let mut mutator = create_mutator_with_specific_operators( mutation_test_id, output_directory.clone(), - vec![MutationOperators::ArthimeticOperator], + vec![MutationOperators::ArithmeticReplacementOperator], ); assert_all_mutations_are_correct(&mut mutator, mutation_test_id, output_directory); } @@ -809,7 +809,7 @@ mod tests { let mut mutator = create_mutator_with_specific_operators( mutation_test_id, output_directory.clone(), - vec![MutationOperators::AssignmentOperator], + vec![MutationOperators::AssignmentReplacementOperator], ); assert_all_mutation_files_were_created(&mut mutator, mutation_test_id, output_directory); } @@ -821,7 +821,7 @@ mod tests { let mut mutator = create_mutator_with_specific_operators( mutation_test_id, output_directory.clone(), - vec![MutationOperators::AssignmentOperator], + vec![MutationOperators::AssignmentReplacementOperator], ); assert_all_mutations_are_correct(&mut mutator, mutation_test_id, output_directory); } @@ -832,7 +832,7 @@ mod tests { let mut mutator = create_mutator_with_specific_operators( mutation_test_id, output_directory.clone(), - vec![MutationOperators::LogicalOperator], + vec![MutationOperators::LogicalReplacementOperator], ); assert_all_mutation_files_were_created(&mut mutator, mutation_test_id, output_directory); } @@ -843,7 +843,7 @@ mod tests { let mut mutator = create_mutator_with_specific_operators( mutation_test_id, output_directory.clone(), - vec![MutationOperators::LogicalOperator], + vec![MutationOperators::LogicalReplacementOperator], ); assert_all_mutations_are_correct(&mut mutator, mutation_test_id, output_directory); } @@ -855,7 +855,7 @@ mod tests { let mut mutator = create_mutator_with_specific_operators( mutation_test_id, output_directory.clone(), - vec![MutationOperators::RelationalOperator], + vec![MutationOperators::RelationalReplacementOperator], ); assert_all_mutation_files_were_created(&mut mutator, mutation_test_id, output_directory); } @@ -867,7 +867,7 @@ mod tests { let mut mutator = create_mutator_with_specific_operators( mutation_test_id, output_directory.clone(), - vec![MutationOperators::RelationalOperator], + vec![MutationOperators::RelationalReplacementOperator], ); assert_all_mutations_are_correct(&mut mutator, mutation_test_id, output_directory); } @@ -878,7 +878,7 @@ mod tests { let mut mutator = create_mutator_with_specific_operators( mutation_test_id, output_directory.clone(), - vec![MutationOperators::UnaryOperator], + vec![MutationOperators::UnaryReplacementOperator], ); assert_all_mutation_files_were_created(&mut mutator, mutation_test_id, output_directory); } @@ -889,7 +889,7 @@ mod tests { let mut mutator = create_mutator_with_specific_operators( mutation_test_id, output_directory.clone(), - vec![MutationOperators::UnaryOperator], + vec![MutationOperators::UnaryReplacementOperator], ); assert_all_mutations_are_correct(&mut mutator, mutation_test_id, output_directory); } From 2196655ce3e97ea0522c07a468df2e35ae5b7707 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Sat, 18 Nov 2023 01:24:36 -0500 Subject: [PATCH 04/23] testing new gradle features --- .../demo/src/main/kotlin/Calculator.kt | 34 +++++++++---------- src/gradle.rs | 30 ++++++++++++++++ src/mutation_tool/operators.rs | 25 +++++--------- src/mutation_tool/tool.rs | 12 +++++++ 4 files changed, 68 insertions(+), 33 deletions(-) diff --git a/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt b/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt index 75a6d43..7505828 100644 --- a/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt +++ b/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt @@ -17,23 +17,23 @@ class Calculator { return result } - // fun ElvisLiteralChangeOperator() { - // var b: String? = null - // val c = 1 - // val x = b?.length ?: -11 - // x = b?.length ?: "Hello There" - // // x = b?.length ?: c - // x = b?.length ?: 1.0 - // x = b?.length ?: 1.0f - // x = b?.length ?: 1L - // // x = b?.length ?: 'a' - // // x = b?.length ?: 1.toShort() - // // x = b?.length ?: 1.toChar() + fun ElvisLiteralChangeOperator() { + var b: String? = null + val c = 1 + val x = b?.length ?: -11 + x = b?.length ?: "Hello There" + // x = b?.length ?: c + x = b?.length ?: 1.0 + x = b?.length ?: 1.0f + x = b?.length ?: 1L + // x = b?.length ?: 'a' + // x = b?.length ?: 1.toShort() + // x = b?.length ?: 1.toChar() - // // if (x > 1) { - // // println("Len is greater than 1!") - // // } + // if (x > 1) { + // println("Len is greater than 1!") + // } - // println(x) - // } + println(x) + } } diff --git a/src/gradle.rs b/src/gradle.rs index 10c42df..60dbaea 100644 --- a/src/gradle.rs +++ b/src/gradle.rs @@ -28,6 +28,36 @@ impl<'a> ToString for GradleCommand<'a> { } } +/// Checks to see if gradle is installed on the system +pub fn is_gradle_installed() -> bool { + let mut cmd = if cfg!(unix) { + Command::new("gradle") + } else if cfg!(windows) { + Command::new("cmd") + } else { + panic!("Unsupported OS"); + }; + let mut args = vec![]; + if cfg!(windows) { + args.append(&mut ["/C".into(), "gradlew.bat".into()].to_vec()) + } + args.append(&mut ["--version".to_string()].to_vec()); + + let res = cmd + .args(args) + .stderr(Stdio::null()) + .stdout(Stdio::null()) + .spawn() + .map_err(|e| KodeKrakenError::Error(format!("Failed to run gradle command: {}", e))); + match res { + Ok(mut child) => { + let res = child.wait().unwrap(); + res.success() + } + Err(_) => false, + } +} + /// Run the gradle commands, assemble and test /// This will check to see if there is a gradlew file in the root of the directory pub fn run( diff --git a/src/mutation_tool/operators.rs b/src/mutation_tool/operators.rs index 792e832..f90d539 100644 --- a/src/mutation_tool/operators.rs +++ b/src/mutation_tool/operators.rs @@ -189,11 +189,6 @@ impl MutationOperators { let root_type = KotlinTypes::new(node.kind()).expect("Failed to convert to KotlinType"); let parent_type = parent .map(|p| KotlinTypes::new(p.kind()).expect("Failed to convert to KotlinType")); - if root_type == KotlinTypes::NonNamedType("?:".into()) { - // println!("Found elvis operator"); - // println!("{:#?}", node); - // println!("{:#?}", node.parent()); - } mutations_made.append( &mut self .mutate_operator(&node, &root_type, &parent_type, file_name) @@ -309,8 +304,6 @@ impl MutationOperators { let child_type = KotlinTypes::new(node.kind()).expect("Failed to convert to KotlinType"); - println!("Child type: {:#?}", child_type); - // Change the literal to a different literal let mut val = node.utf8_text(file).unwrap(); match child_type { @@ -424,15 +417,15 @@ impl AllMutationOperators { pub fn new() -> Self { Self { mutation_operators: vec![ - MutationOperators::ArithmeticReplacementOperator, - MutationOperators::UnaryRemovalOperator, - MutationOperators::LogicalReplacementOperator, - MutationOperators::RelationalReplacementOperator, - MutationOperators::AssignmentReplacementOperator, - MutationOperators::UnaryReplacementOperator, - MutationOperators::NotNullAssertionOperator, - // MutationOperators::ElvisRemoveOperator, - // MutationOperators::ElvisLiteralChangeOperator, + // MutationOperators::ArithmeticReplacementOperator, + // MutationOperators::UnaryRemovalOperator, + // MutationOperators::LogicalReplacementOperator, + // MutationOperators::RelationalReplacementOperator, + // MutationOperators::AssignmentReplacementOperator, + // MutationOperators::UnaryReplacementOperator, + // MutationOperators::NotNullAssertionOperator, + MutationOperators::ElvisRemoveOperator, + MutationOperators::ElvisLiteralChangeOperator, ], } } diff --git a/src/mutation_tool/tool.rs b/src/mutation_tool/tool.rs index 79a4f9d..2b8f418 100644 --- a/src/mutation_tool/tool.rs +++ b/src/mutation_tool/tool.rs @@ -338,6 +338,12 @@ impl MutationTool { &mut self, file_mutations: &HashMap, ) -> Result> { + // Check to see if gradle is installed + if !gradle::is_gradle_installed() { + return Err(KodeKrakenError::Error( + "Gradle is not installed. Please install Gradle and try again.".into(), + )); + } // Get total number of mutations let num_mutations = file_mutations .iter() @@ -601,6 +607,12 @@ impl MutationTool { .map_err(|_| KodeKrakenError::Error("Failed to unwrap mutation_count".to_string()))?; tracing::info!("Mutations made to all files"); tracing::info!("Total mutations made: {}", mutation_count); + + if mutation_count == 0 { + return Err(KodeKrakenError::Error( + "No mutations were found in the project".into(), + )); + } Ok(Arc::try_unwrap(file_mutations) .map_err(|_| KodeKrakenError::Error("Failed to unwrap file_mutations".to_string()))? .into_inner() From e7e75958470342be0655b2ec376353d662b5acbd Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Sun, 19 Nov 2023 14:38:36 -0500 Subject: [PATCH 05/23] rework tests --- Cargo.lock | 131 ++++++++++++++- Cargo.toml | 5 +- kodekraken.config.json | 16 +- src/cli.rs | 35 +++- src/config.rs | 154 ++++++++++++++++-- src/error.rs | 74 ++++++++- src/gradle.rs | 32 +++- src/html_gen.rs | 143 ++++++++++++++++ src/lib.rs | 2 - src/main.rs | 34 +--- src/mutation_tool/builder.rs | 112 +++++++++++++ src/mutation_tool/mod.rs | 100 ++++++++++++ src/mutation_tool/mutation.rs | 101 ++++++++++++ src/mutation_tool/operators.rs | 130 +++++++++++---- src/mutation_tool/tool.rs | 77 +++++---- src/test_config/mod.rs | 72 -------- tests/kode_kraken/main.rs | 42 +++++ .../demo/build.gradle.kts | 0 .../demo/gradle.properties | 0 .../demo/gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../kotlin-test-projects}/demo/gradlew | 0 .../kotlin-test-projects}/demo/gradlew.bat | 0 .../demo/kodekraken.config.json | 0 .../demo/settings.gradle.kts | 0 .../demo/src/main/kotlin/Calculator.kt | 11 +- .../demo/src/main/kotlin/NumberOperations.kt | 0 .../demo/src/test/kotlin/CalculatorTest.kt | 0 .../src/test/kotlin/NumberOperationTest.kt | 0 .../kotlin-project/build.gradle.kts | 0 .../kotlin-project/gradle.properties | 0 .../gradle/wrapper/gradle-wrapper.jar | Bin .../gradle/wrapper/gradle-wrapper.properties | 0 .../kotlin-project/gradlew | 0 .../kotlin-project/gradlew.bat | 0 .../kotlin-project/settings.gradle.kts | 0 .../src/main/kotlin/Calculator.kt | 0 .../src/main/kotlin/NumberOperations.kt | 0 .../src/test/kotlin/CalculatorTest.kt | 0 .../src/test/kotlin/NumberOperationTest.kt | 0 .../mutations/BuildFails.kt | 0 .../kotlin-test-projects}/mutations/Killed.kt | 0 .../mutations/Survived.kt | 0 .../no-gradle-project/Calculator.kt | 0 .../no-gradle-project/Main.kt | 0 45 files changed, 1065 insertions(+), 206 deletions(-) delete mode 100644 src/test_config/mod.rs create mode 100644 tests/kode_kraken/main.rs rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/build.gradle.kts (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/gradle.properties (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/gradle/wrapper/gradle-wrapper.jar (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/gradle/wrapper/gradle-wrapper.properties (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/gradlew (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/gradlew.bat (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/kodekraken.config.json (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/settings.gradle.kts (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/src/main/kotlin/Calculator.kt (82%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/src/main/kotlin/NumberOperations.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/src/test/kotlin/CalculatorTest.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/demo/src/test/kotlin/NumberOperationTest.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/build.gradle.kts (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/gradle.properties (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/gradle/wrapper/gradle-wrapper.jar (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/gradle/wrapper/gradle-wrapper.properties (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/gradlew (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/gradlew.bat (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/settings.gradle.kts (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/src/main/kotlin/Calculator.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/src/main/kotlin/NumberOperations.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/src/test/kotlin/CalculatorTest.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/kotlin-project/src/test/kotlin/NumberOperationTest.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/mutations/BuildFails.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/mutations/Killed.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/mutations/Survived.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/no-gradle-project/Calculator.kt (100%) rename {kotlin-test-projects => tests/kotlin-test-projects}/no-gradle-project/Main.kt (100%) diff --git a/Cargo.lock b/Cargo.lock index 26042d3..91b3f2c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -11,6 +11,27 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstyle" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" + +[[package]] +name = "assert_cmd" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88903cb14723e4d4003335bb7f8a14f27691649105346a0f0957466c096adfe6" +dependencies = [ + "anstyle", + "bstr", + "doc-comment", + "predicates", + "predicates-core", + "predicates-tree", + "wait-timeout", +] + [[package]] name = "autocfg" version = "1.1.0" @@ -23,6 +44,17 @@ version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +[[package]] +name = "bstr" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "542f33a8835a0884b006a0c3df3dadd99c0c3f296ed26c2fdc8028e01ad6230c" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + [[package]] name = "cc" version = "1.0.79" @@ -172,6 +204,18 @@ dependencies = [ "memchr", ] +[[package]] +name = "difflib" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" + +[[package]] +name = "doc-comment" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" + [[package]] name = "either" version = "1.8.1" @@ -205,6 +249,15 @@ dependencies = [ "libc", ] +[[package]] +name = "float-cmp" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" +dependencies = [ + "num-traits", +] + [[package]] name = "getrandom" version = "0.2.8" @@ -277,6 +330,15 @@ dependencies = [ "windows-sys 0.45.0", ] +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.5" @@ -287,11 +349,14 @@ checksum = "fad582f4b9e86b6caa621cabeb0963332d92eea04729ab12892c2533951e6440" name = "kode-kraken" version = "0.1.0" dependencies = [ + "assert_cmd", "clap", "cli-table", "csv", "horrorshow", "indicatif", + "num-traits", + "predicates", "rand", "rayon", "regex", @@ -314,9 +379,9 @@ checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" [[package]] name = "libc" -version = "0.2.139" +version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "201de327520df007757c1f0adce6e827fe8562fbc28bfd9c15571c66ca1f5f79" +checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" [[package]] name = "linux-raw-sys" @@ -335,9 +400,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.5.0" +version = "2.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" +checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" [[package]] name = "memoffset" @@ -348,6 +413,12 @@ dependencies = [ "autocfg", ] +[[package]] +name = "normalize-line-endings" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -358,6 +429,15 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", +] + [[package]] name = "num_cpus" version = "1.15.0" @@ -410,6 +490,37 @@ version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +[[package]] +name = "predicates" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfc28575c2e3f19cb3c73b93af36460ae898d426eba6fc15b9bd2a5220758a0" +dependencies = [ + "anstyle", + "difflib", + "float-cmp", + "itertools", + "normalize-line-endings", + "predicates-core", + "regex", +] + +[[package]] +name = "predicates-core" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" + +[[package]] +name = "predicates-tree" +version = "1.0.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" +dependencies = [ + "predicates-core", + "termtree", +] + [[package]] name = "proc-macro-error" version = "1.0.4" @@ -515,6 +626,12 @@ dependencies = [ "regex-syntax", ] +[[package]] +name = "regex-automata" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" + [[package]] name = "regex-syntax" version = "0.7.1" @@ -619,6 +736,12 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "termtree" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" + [[package]] name = "thread_local" version = "1.1.7" diff --git a/Cargo.toml b/Cargo.toml index 47448a2..7827823 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ path = "src/lib.rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -clap = { version = "4.1.6", features = ["derive"] } +clap = { version = "4.1.6", features = ["derive"] } cli-table = "0.4.7" csv = "1.2.1" indicatif = "0.17.3" @@ -30,3 +30,6 @@ wait-timeout = "0.2.0" regex = "1.8.1" tracing-appender = "0.2.2" horrorshow = "0.8.4" +num-traits = "0.2.17" +assert_cmd = "2.0.12" +predicates = "3.0.4" diff --git a/kodekraken.config.json b/kodekraken.config.json index a89da3b..86c0bfd 100644 --- a/kodekraken.config.json +++ b/kodekraken.config.json @@ -1,9 +1,9 @@ { - "ignore": { - "ignore_files": [ - "^.*Test\\.[^.]*$" + "ignore": { + "ignore_files": [ + "^.*Test\\.[^.]*$" ], - "ignore_directories": [ + "ignore_directories": [ "dist", "build", "bin", @@ -12,13 +12,13 @@ "gradle" ] }, - "threading": { + "threading": { "max_threads": 30 }, - "output": { + "output": { "display_end_table": false }, - "logging": { - "log_level": "DEBUG" + "logging": { + "log_level": "INFO" } } diff --git a/src/cli.rs b/src/cli.rs index 3eab054..9918c7d 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,6 +1,7 @@ use std::{path::Path, time::Duration}; use clap::{Args, CommandFactory, Parser, Subcommand}; +use tracing_appender::non_blocking::WorkerGuard; use crate::{ config::KodeKrakenConfig, @@ -37,7 +38,7 @@ pub struct Cli { pub command: Commands, } -#[derive(Args, Debug, Clone)] +#[derive(Args, Debug, Clone, PartialEq, Eq)] pub struct MutationCommandConfig { /// The path to the files to be mutated /// Error will be thrown if the path is not a directory @@ -86,12 +87,18 @@ where } } -pub fn run_cli(config: KodeKrakenConfig) { +pub fn run_cli() { + let _guard: WorkerGuard; + tracing::info!("Starting Kode Kraken"); + let args = Cli::parse(); let mutate_tool_builder = MutationToolBuilder::new(); match args.command { Commands::Mutate(mutate_config) => { + let config = KodeKrakenConfig::load_config(mutate_config.path.clone()); + _guard = setup_logging(&config.logging.log_level); + let mut tool = mutate_tool_builder .set_mutate_config(mutate_config) .set_general_config(config) @@ -155,6 +162,30 @@ pub fn run_cli(config: KodeKrakenConfig) { } } +fn setup_logging(log_level: &str) -> WorkerGuard { + let log_level = match log_level.to_lowercase().as_str() { + "trace" => tracing::Level::TRACE, + "debug" => tracing::Level::DEBUG, + "info" => tracing::Level::INFO, + "warn" => tracing::Level::WARN, + "error" => tracing::Level::ERROR, + _ => tracing::Level::INFO, + }; + // Create dist log folder if it doesn't exist + let log_dir = Path::new(OUT_DIRECTORY).join("logs"); + std::fs::create_dir_all(&log_dir).expect("Could not create log directory"); + let file_appender = tracing_appender::rolling::never(log_dir, "kode-kraken.log"); + let (non_blocking, guard) = tracing_appender::non_blocking(file_appender); + tracing_subscriber::fmt() + .with_max_level(log_level) + .with_ansi(false) + .with_target(false) + .with_writer(non_blocking) + .with_thread_ids(true) + .init(); + guard +} + #[cfg(test)] mod tests { use super::*; diff --git a/src/config.rs b/src/config.rs index f0e2b5d..7068502 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,8 +1,10 @@ -use std::{fs, io::BufReader}; +use std::{fs, io::BufReader, path::Path}; use serde::Deserialize; -#[derive(Debug, Deserialize, Default)] +use crate::mutation_tool::MutationOperators; + +#[derive(Debug, Deserialize, Default, PartialEq, Eq, Clone)] pub struct KodeKrakenConfig { pub general: GeneralConfig, pub ignore: IgnoreConfig, @@ -11,37 +13,41 @@ pub struct KodeKrakenConfig { pub logging: LoggingConfig, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, PartialEq, Eq, Clone)] pub struct GeneralConfig { /// The time in seconds to wait for the mutation tool to finish /// before killing the process pub timeout: Option, + pub operators: Vec, } impl Default for GeneralConfig { /// Set Default timeout of 5 minutes fn default() -> Self { - Self { timeout: None } + Self { + timeout: None, + operators: vec![], + } } } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, PartialEq, Eq, Clone)] pub struct LoggingConfig { pub log_level: String, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, PartialEq, Eq, Clone)] pub struct IgnoreConfig { pub ignore_files: Vec, pub ignore_directories: Vec, } -#[derive(Debug, Deserialize)] +#[derive(Debug, Deserialize, PartialEq, Eq, Clone)] pub struct ThreadingConfig { pub max_threads: usize, } -#[derive(Debug, Deserialize, Default)] +#[derive(Debug, Deserialize, Default, PartialEq, Eq, Clone)] pub struct OutputConfig { pub display_end_table: bool, } @@ -51,8 +57,8 @@ impl KodeKrakenConfig { Self::default() } - pub fn load_config() -> Self { - match fs::File::open("kodekraken.config.json") { + pub fn load_config>(path: P) -> Self { + match fs::File::open(path.as_ref().join("kodekraken.config.json")) { Ok(file) => { let buffer_reader = BufReader::new(file); match serde_json::from_reader(buffer_reader) { @@ -68,7 +74,7 @@ impl KodeKrakenConfig { } } Err(_) => { - println!("[WARNING] ⚠️ Could not find kodekraken.config.json file, using default config."); + println!("[WARNING] ⚠️ Could not find kodekraken.config.json file in root directory, using default config."); Self::default() } } @@ -106,3 +112,129 @@ impl Default for LoggingConfig { } } } +#[cfg(test)] +mod tests { + use super::*; + use std::{env::temp_dir, fs::File, io::Write}; + + #[test] + fn test_default_general_config() { + let default_general = GeneralConfig::default(); + assert_eq!(default_general.timeout, None); + assert_eq!(default_general.operators, vec![]); + } + + #[test] + fn test_default_ignore_config() { + let default_ignore = IgnoreConfig::default(); + assert_eq!(default_ignore.ignore_files.len(), 1); + assert_eq!(default_ignore.ignore_directories.len(), 6); + } + + #[test] + fn test_default_threading_config() { + let default_threading = ThreadingConfig::default(); + assert_eq!(default_threading.max_threads, 30); + } + + #[test] + fn test_default_logging_config() { + let default_logging = LoggingConfig::default(); + assert_eq!(default_logging.log_level, "info"); + } + + #[test] + fn test_default_output_config() { + let default_output = OutputConfig::default(); + assert_eq!(default_output.display_end_table, false); + } + + #[test] + fn test_new_kodekraken_config() { + let config = KodeKrakenConfig::new(); + assert_eq!(config.general.timeout, None); + assert_eq!(config.general.operators, vec![]); + assert_eq!(config.ignore.ignore_files.len(), 1); + assert_eq!(config.ignore.ignore_directories.len(), 6); + assert_eq!(config.threading.max_threads, 30); + assert_eq!(config.output.display_end_table, false); + assert_eq!(config.logging.log_level, "info"); + } + + #[test] + fn test_load_config_from_valid_file() { + let temp_dir = temp_dir(); + let file_path = temp_dir.join("kodekraken.config.json"); + let mut file = File::create(file_path).expect("Failed to create temporary file"); + + // Create a valid JSON content + let json_content = r#" + { + "general": { + "timeout": 10, + "operators": ["UnaryRemovalOperator", "AssignmentReplacementOperator"] + }, + "ignore": { + "ignore_files": ["file1", "file2"], + "ignore_directories": ["dir1", "dir2"] + }, + "threading": { + "max_threads": 42 + }, + "output": { + "display_end_table": true + }, + "logging": { + "log_level": "debug" + } + } + "#; + + writeln!(file, "{}", json_content).expect("Failed to write to temporary file"); + let config = KodeKrakenConfig::load_config(temp_dir); + assert_eq!(config.general.timeout, Some(10)); + assert_eq!( + config.general.operators, + vec![ + MutationOperators::UnaryRemovalOperator, + MutationOperators::AssignmentReplacementOperator + ] + ); + assert_eq!(config.ignore.ignore_files, vec!["file1", "file2"]); + assert_eq!(config.ignore.ignore_directories, vec!["dir1", "dir2"]); + assert_eq!(config.threading.max_threads, 42); + assert_eq!(config.output.display_end_table, true); + assert_eq!(config.logging.log_level, "debug"); + } + + #[test] + fn test_load_config_from_invalid_file() { + // Create an invalid JSON content + let temp_dir = temp_dir(); + let file_path = temp_dir.join("kodekraken.config.json"); + let mut file = File::create(file_path).expect("Failed to create temporary file"); + + let invalid_json_content = r#" + { + "general": { + "timeout": "invalid_timeout_value" + } + } + "#; + + writeln!(file, "{}", invalid_json_content).expect("Failed to write to temporary file"); + let config = KodeKrakenConfig::load_config(temp_dir); + // Since the JSON is invalid, it should fall back to default values + assert_eq!(config.general.timeout, None); + assert_eq!(config.general.operators, vec![]); + } + + #[test] + fn test_load_config_from_missing_file() { + // Test loading config from a missing file + let config = KodeKrakenConfig::load_config("/tmp"); + // Since the file is missing, it should fall back to default values + assert_eq!(config.general.timeout, None); + assert_eq!(config.general.operators, vec![]); + } +} diff --git a/src/error.rs b/src/error.rs index ce4bd86..f576efb 100644 --- a/src/error.rs +++ b/src/error.rs @@ -2,7 +2,7 @@ use std::io; pub type Result = std::result::Result; -#[derive(Debug)] +#[derive(Debug, PartialEq, Eq, Clone)] pub enum KodeKrakenError { FileReadingError(String), MutationGenerationError, @@ -42,3 +42,75 @@ impl From for KodeKrakenError { KodeKrakenError::Error(error.to_string()) } } + +#[cfg(test)] +mod test { + use super::*; + use std::io; + #[test] + fn test_display_file_reading_error() { + let error = KodeKrakenError::FileReadingError("Failed to read file".to_string()); + let expected_output = "Error while reading file: Failed to read file"; + assert_eq!(error.to_string(), expected_output); + } + + #[test] + fn test_display_mutation_generation_error() { + let error = KodeKrakenError::MutationGenerationError; + let expected_output = "Error while generating mutation"; + assert_eq!(error.to_string(), expected_output); + } + + #[test] + fn test_display_mutation_gathering_error() { + let error = KodeKrakenError::MutationGatheringError; + let expected_output = "Error while gathering mutations"; + assert_eq!(error.to_string(), expected_output); + } + + #[test] + fn test_display_mutation_build_test_error() { + let error = KodeKrakenError::MutationBuildTestError; + let expected_output = "Error while building and testing"; + assert_eq!(error.to_string(), expected_output); + } + + #[test] + fn test_display_conversion_error() { + let error = KodeKrakenError::ConversionError; + let expected_output = "Error while converting"; + assert_eq!(error.to_string(), expected_output); + } + + #[test] + fn test_display_generic_error() { + let error = KodeKrakenError::Error("Something went wrong".to_string()); + let expected_output = "Error: Something went wrong"; + assert_eq!(error.to_string(), expected_output); + } + + #[test] + fn test_from_io_error() { + let io_error = io::Error::new(io::ErrorKind::NotFound, "File not found"); + let converted_error: KodeKrakenError = io_error.into(); + let expected_error = KodeKrakenError::Error("File not found".to_string()); + assert_eq!(converted_error, expected_error); + } + + #[test] + fn test_result_conversion_ok() { + let result: Result = Ok(42); + assert_eq!(result.is_ok(), true); + assert_eq!(result.unwrap(), 42); + } + + #[test] + fn test_result_conversion_err() { + let result: Result = Err(KodeKrakenError::Error("Something went wrong".to_string())); + assert_eq!(result.is_err(), true); + assert_eq!( + result.unwrap_err(), + KodeKrakenError::Error("Something went wrong".to_string()) + ); + } +} diff --git a/src/gradle.rs b/src/gradle.rs index 60dbaea..cb1b4d4 100644 --- a/src/gradle.rs +++ b/src/gradle.rs @@ -28,6 +28,20 @@ impl<'a> ToString for GradleCommand<'a> { } } +pub fn build_project_success(path: &PathBuf) -> Result { + let res = build_gradle_command(path, GradleCommand::Assemble)? + .wait() + .map_err(|e| KodeKrakenError::Error(format!("Failed to run gradle command: {}", e)))?; + Ok(res.success()) +} + +pub fn project_tests_pass(path: &PathBuf) -> Result { + let res = build_gradle_command(path, GradleCommand::Test("*"))? + .wait() + .map_err(|e| KodeKrakenError::Error(format!("Failed to run gradle command: {}", e)))?; + Ok(res.success()) +} + /// Checks to see if gradle is installed on the system pub fn is_gradle_installed() -> bool { let mut cmd = if cfg!(unix) { @@ -164,12 +178,12 @@ fn build_gradle_command(config_path: &PathBuf, command: GradleCommand) -> Result #[cfg(test)] mod test { use super::*; - + const KOTLIN_CODE_DIR: &str = "./tests/kotlin-test-projects"; #[test] #[should_panic(expected = "gradlew does not exist at the root of this project")] fn test() { run( - &PathBuf::from("./kotlin-test-projects/no-gradle-project"), + &PathBuf::from(KOTLIN_CODE_DIR).join("no-gradle-project"), &PathBuf::new(), &PathBuf::new(), &mut Mutation::new( @@ -187,11 +201,13 @@ mod test { #[test] fn run_mutations_should_all_pass() { - let dir = PathBuf::from("./kotlin-test-projects/mutations") + let dir = PathBuf::from(KOTLIN_CODE_DIR) + .join("mutations") .read_dir() .unwrap(); - let file_backup = - include_str!("../kotlin-test-projects/kotlin-project/src/main/kotlin/Calculator.kt"); + let file_backup = include_str!( + "../tests/kotlin-test-projects/kotlin-project/src/main/kotlin/Calculator.kt" + ); for entry in dir { let entry = entry.unwrap().path(); let mut mutation = Mutation::new( @@ -204,10 +220,10 @@ mod test { "file_name".into(), ); run( - &PathBuf::from("./kotlin-test-projects/kotlin-project"), + &PathBuf::from(KOTLIN_CODE_DIR).join("kotlin-project"), &entry, &PathBuf::from( - "./kotlin-test-projects/kotlin-project/src/main/kotlin/Calculator.kt", + "./tests/kotlin-test-projects/kotlin-project/src/main/kotlin/Calculator.kt", ), &mut mutation, ) @@ -215,7 +231,7 @@ mod test { // Reset File fs::write( &PathBuf::from( - "./kotlin-test-projects/kotlin-project/src/main/kotlin/Calculator.kt", + "./tests/kotlin-test-projects/kotlin-project/src/main/kotlin/Calculator.kt", ), file_backup, ) diff --git a/src/html_gen.rs b/src/html_gen.rs index 8b03a6e..bca0821 100644 --- a/src/html_gen.rs +++ b/src/html_gen.rs @@ -99,3 +99,146 @@ pub fn build_html_page(data: &Vec) { let mut file = File::create(file_path).unwrap(); file.write_all(report.as_bytes()).unwrap(); } + +#[cfg(test)] +mod test { + use std::collections::HashMap; + use std::fs::File; + use std::io::Read; + use std::path::Path; + + use crate::mutation_tool::{Mutation, MutationOperators, MutationResult}; + + use super::build_html_page; + + #[test] + fn test_build_html_page() { + // Create some sample mutations + let mutation1 = Mutation::new( + 0, + 10, + "new_op1".to_string(), + "old_op1".to_string(), + 1, + MutationOperators::ArithmeticReplacementOperator, + "file1".to_string(), + ); + let mutation2 = Mutation::new( + 0, + 15, + "new_op2".to_string(), + "old_op2".to_string(), + 2, + MutationOperators::AssignmentReplacementOperator, + "file1".to_string(), + ); + let mutation3 = Mutation::new( + 0, + 8, + "new_op3".to_string(), + "old_op3".to_string(), + 1, + MutationOperators::ElvisLiteralChangeOperator, + "file2".to_string(), + ); + let mutation4 = Mutation::new( + 0, + 12, + "new_op4".to_string(), + "old_op4".to_string(), + 3, + MutationOperators::LogicalReplacementOperator, + "file2".to_string(), + ); + + let mutations = vec![ + mutation1.clone(), + mutation2.clone(), + mutation3.clone(), + mutation4.clone(), + ]; + + // Create test data + let mut file_mutations = HashMap::new(); + for mutation in &mutations { + let file_name = mutation.file_name.clone(); + let file_mutations = file_mutations.entry(file_name).or_insert(Vec::new()); + file_mutations.push(mutation.clone()); + } + + // Call the function + build_html_page(&mutations); + + // Read the generated HTML file + let file_path = Path::new("kode-kraken-dist").join("report.html"); + let mut file_content = String::new(); + File::open(file_path) + .expect("Failed to open the generated HTML file") + .read_to_string(&mut file_content) + .expect("Failed to read the generated HTML file"); + + // Verify that HTML content contains information about each file and mutation + assert_contains( + &file_content, + "Kode Kraken Results", + ); + assert_contains(&file_content, "File Name"); + assert_contains(&file_content, "# of Mutations"); + assert_contains(&file_content, "# Survived"); + assert_contains(&file_content, "# Killed"); + assert_contains(&file_content, "Score"); + + for (file_name, fm) in file_mutations.iter() { + assert_contains( + &file_content, + &format!("{}", file_name), + ); + assert_contains( + &file_content, + &format!("{}", fm.len()), + ); + assert_contains( + &file_content, + &format!( + "{}", + fm.iter() + .filter(|m| m.result == MutationResult::Survived) + .count() + ), + ); + assert_contains( + &file_content, + &format!( + "{}", + fm.iter() + .filter(|m| m.result == MutationResult::Killed) + .count() + ), + ); + let score = (fm + .iter() + .filter(|m| m.result == MutationResult::Killed) + .count() as f32 + / (fm.len() + - fm.iter() + .filter(|m| { + m.result != MutationResult::Killed + && m.result != MutationResult::Survived + }) + .count()) as f32) + * 100.0; + assert_contains( + &file_content, + &format!("{:.2}%", score), + ); + } + } + + fn assert_contains(haystack: &str, needle: &str) { + assert!( + haystack.contains(needle), + "{}", + format!("Expected content:\n{}\nTo contain:\n{}", haystack, needle) + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 53e867a..a6ce7d4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -5,5 +5,3 @@ pub mod gradle; pub mod html_gen; pub mod kotlin_types; pub mod mutation_tool; -#[cfg(test)] -mod test_config; diff --git a/src/main.rs b/src/main.rs index 34528d6..ff6241b 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,35 +1,5 @@ -use kode_kraken::{cli::run_cli, config::KodeKrakenConfig, mutation_tool::OUT_DIRECTORY}; -use std::path::Path; -use tracing_appender::non_blocking::WorkerGuard; +use kode_kraken::cli::run_cli; fn main() { - let config = KodeKrakenConfig::load_config(); - - let _guard = setup_logging(&config.logging.log_level); - tracing::info!("Starting Kode Kraken"); - run_cli(config); -} - -fn setup_logging(log_level: &str) -> WorkerGuard { - let log_level = match log_level.to_lowercase().as_str() { - "trace" => tracing::Level::TRACE, - "debug" => tracing::Level::DEBUG, - "info" => tracing::Level::INFO, - "warn" => tracing::Level::WARN, - "error" => tracing::Level::ERROR, - _ => tracing::Level::INFO, - }; - // Create dist log folder if it doesn't exist - let log_dir = Path::new(OUT_DIRECTORY).join("logs"); - std::fs::create_dir_all(&log_dir).expect("Could not create log directory"); - let file_appender = tracing_appender::rolling::never(log_dir, "kode-kraken.log"); - let (non_blocking, guard) = tracing_appender::non_blocking(file_appender); - tracing_subscriber::fmt() - .with_max_level(log_level) - .with_ansi(false) - .with_target(false) - .with_writer(non_blocking) - .with_thread_ids(true) - .init(); - guard + run_cli(); } diff --git a/src/mutation_tool/builder.rs b/src/mutation_tool/builder.rs index cd67053..ffe3dec 100644 --- a/src/mutation_tool/builder.rs +++ b/src/mutation_tool/builder.rs @@ -66,3 +66,115 @@ impl MutationToolBuilder { .unwrap() } } +#[cfg(test)] +mod tests { + use crate::config::GeneralConfig; + + use super::*; + use std::sync::Arc; + + #[test] + fn test_default_builder() { + let builder = MutationToolBuilder::new(); + let mutation_tool = builder.build(); + + // Add assertions based on your specific default values + assert_eq!(mutation_tool.enable_mutation_comment, false); + assert_eq!(mutation_tool.kodekraken_config, KodeKrakenConfig::new()); + assert_eq!( + mutation_tool.mutate_config, + MutationCommandConfig::default() + ); + assert_eq!( + Arc::into_inner(mutation_tool.mutation_operators).unwrap(), + AllMutationOperators::new().get_mutation_operators() + ); + } + + #[test] + fn test_set_general_config() { + let general_config = KodeKrakenConfig { + general: GeneralConfig { + timeout: Some(10), + operators: vec![ + MutationOperators::AssignmentReplacementOperator, + MutationOperators::UnaryRemovalOperator, + ], + }, + ..Default::default() + }; + + let builder = MutationToolBuilder::new().set_general_config(general_config.clone()); + let mutation_tool = builder.build(); + + assert_eq!(mutation_tool.kodekraken_config.general.timeout, Some(10)); + assert_eq!( + mutation_tool.kodekraken_config.general.operators, + vec![ + MutationOperators::AssignmentReplacementOperator, + MutationOperators::UnaryRemovalOperator + ] + ); + } + + #[test] + fn test_set_mutate_config() { + let mutate_config = MutationCommandConfig { + path: "./tests/kotlin-test-projects/demo".into(), + }; + + let builder = MutationToolBuilder::new().set_mutate_config(mutate_config.clone()); + let mutation_tool = builder.build(); + + // Add assertions based on your specific mutate_config fields + assert_eq!(mutation_tool.mutate_config, mutate_config); + assert_eq!( + mutation_tool.mutate_config.path, + "./tests/kotlin-test-projects/demo".to_string() + ); + } + + #[test] + fn test_set_mutation_operators() { + let mutation_operators = vec![ + MutationOperators::AssignmentReplacementOperator, + MutationOperators::ArithmeticReplacementOperator, + ]; + + let builder = MutationToolBuilder::new().set_mutation_operators(mutation_operators.clone()); + let mutation_tool = builder.build(); + + assert_eq!( + Arc::into_inner(mutation_tool.mutation_operators).unwrap(), + mutation_operators + ); + } + + #[test] + fn test_set_mutation_comment() { + let builder = MutationToolBuilder::new().set_mutation_comment(true); + let mutation_tool = builder.build(); + + assert_eq!(mutation_tool.enable_mutation_comment, true); + } + + #[test] + fn test_build_with_defaults() { + let builder = MutationToolBuilder::new(); + let mutation_tool = builder.build(); + + // Add assertions based on your specific default values + assert_eq!(mutation_tool.enable_mutation_comment, false); + assert_eq!(mutation_tool.kodekraken_config, KodeKrakenConfig::default()); + assert_eq!( + mutation_tool.mutate_config, + MutationCommandConfig::default() + ); + assert_eq!( + Arc::into_inner(mutation_tool.mutation_operators).unwrap(), + AllMutationOperators::new().get_mutation_operators() + ); + } + + // Add more tests based on your specific requirements and configurations +} diff --git a/src/mutation_tool/mod.rs b/src/mutation_tool/mod.rs index 63fc5b2..89ab941 100644 --- a/src/mutation_tool/mod.rs +++ b/src/mutation_tool/mod.rs @@ -7,3 +7,103 @@ pub use builder::*; pub use mutation::*; pub use operators::*; pub use tool::*; + +#[cfg(test)] +pub mod test_util { + pub const KOTLIN_TEST_CODE: &str = r#" +fun main() { + // Arithmetic expressions + val a = 10 + val b = 3 + val c = a + b + val d = a - b + val e = a * b + val f = a / b + val g = a % b +} +"#; + + pub const KOTLIN_RELATIONAL_TEST_CODE: &str = r#" +fun main() { + // Relational expressions + val a = 10 + val b = 3 + val c = a > b + val d = a < b + val e = a >= b + val f = a <= b + val g = a == b + val h = a != b +} +"#; + + pub const KOTLIN_LOGICAL_TEST_CODE: &str = r#" +fun main() { + // Logical expressions + val a = true + val b = false + val c = a && b + val d = a || b +} +"#; + + pub const KOTLIN_UNARY_TEST_CODE: &str = r#" +var h = 5 +h++ +h-- +++h +--h +"#; + + pub const KOTLIN_UNARY_REMOVAL_TEST_CODE: &str = r#" +var h = 5 +h++ +h-- +++h +--h +val a = !h +val b = -h +val c = +h +"#; + + pub const KOTLIN_ASSIGNMENT_TEST_CODE: &str = r#" +var h = 5 +h += 3 +h -= 1 +h *= 2 +h /= 4 +h %= 2 +"#; + + pub const KOTLIN_ELVIS_TEST_CODE: &str = r#" +fun main() { + val a = 10 + val b = 3 + val c = a ?: b +} +"#; + + pub const KOTLIN_ELVIS_LITERAL_TEST_CODE: &str = r#" +fun main() { + val a: String? = null + val b = a ?: "b" + val c: Int? = null + val d = c ?: 1 + val e = c ?: -10 + val f: Boolean? = null + val g = e ?: true + val h: Double? = null + val i = h ?: 2.0 + val j = h ?: -3.0 + val k: Float? = null + val l = k ?: 4.0f + val m = k ?: -5.0f + val n: Long? = null + val o = n ?: 6L + val p = n ?: -7L + val q: Char? = null + val r = q ?: 'a' + val s = q ?: 'b' +} +"#; +} diff --git a/src/mutation_tool/mutation.rs b/src/mutation_tool/mutation.rs index ab31426..336121e 100644 --- a/src/mutation_tool/mutation.rs +++ b/src/mutation_tool/mutation.rs @@ -119,3 +119,104 @@ impl Display for Mutation { pub struct FileMutations { pub mutations: Vec, } +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_default_mutation_result() { + let default_result = MutationResult::default(); + assert_eq!(default_result, MutationResult::InProgress); + } + + #[test] + fn test_display_mutation_result() { + let result = MutationResult::Timeout; + let formatted_result = format!("{}", result); + assert_eq!(formatted_result, "Timeout"); + } + + #[test] + fn test_create_mutation() { + let mutation = Mutation::new( + 10, // start_byte + 20, // end_byte + "new_op".to_string(), + "old_op".to_string(), + 42, // line_number + MutationOperators::ArithmeticReplacementOperator, + "example.rs".to_string(), + ); + + assert_eq!(mutation.start_byte, 10); + assert_eq!(mutation.end_byte, 20); + assert_eq!(mutation.new_op, "new_op"); + assert_eq!(mutation.old_op, "old_op"); + assert_eq!(mutation.line_number, 42); + assert_eq!( + mutation.mutation_type, + MutationOperators::ArithmeticReplacementOperator + ); + assert_eq!(mutation.file_name, "example.rs"); + assert_ne!(mutation.id, Uuid::nil()); // Ensure that UUID is not nil + assert_eq!(mutation.result, MutationResult::InProgress); + } + + #[test] + fn test_display_mutation() { + let mutation = Mutation::new( + 10, // start_byte + 20, // end_byte + "new_op".to_string(), + "old_op".to_string(), + 42, // line_number + MutationOperators::ArithmeticReplacementOperator, + "example.rs".to_string(), + ); + + let expected_output = format!( + " + /** + AUTO GENERATED COMMENT + Mutation Operator: ArithmeticReplacementOperator + Line number: {} + Id: {}, + Old Operator: old_op, + New Operator: new_op + */", + (42 + 9), + mutation.id + ); + + assert_eq!(format!("{}", mutation), expected_output); + } + + #[test] + fn test_create_file_mutations() { + let mutations = vec![ + Mutation::new( + 10, // start_byte + 20, // end_byte + "new_op1".to_string(), + "old_op1".to_string(), + 42, // line_number + MutationOperators::ArithmeticReplacementOperator, + "example.rs".to_string(), + ), + Mutation::new( + 30, // start_byte + 40, // end_byte + "new_op2".to_string(), + "old_op2".to_string(), + 56, // line_number + MutationOperators::UnaryRemovalOperator, + "example.rs".to_string(), + ), + ]; + + let file_mutations = FileMutations { mutations }; + + assert_eq!(file_mutations.mutations.len(), 2); + // Add more assertions based on your specific requirements + } +} diff --git a/src/mutation_tool/operators.rs b/src/mutation_tool/operators.rs index f90d539..84e88f5 100644 --- a/src/mutation_tool/operators.rs +++ b/src/mutation_tool/operators.rs @@ -1,4 +1,4 @@ -use rand::Rng; +use rand::{distributions::uniform::SampleUniform, Rng}; use crate::{ error::{KodeKrakenError, Result}, @@ -14,7 +14,7 @@ pub struct AllMutationOperators { } // The different types of mutation operators that can be performed on a file -#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize)] +#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)] pub enum MutationOperators { ArithmeticReplacementOperator, UnaryRemovalOperator, @@ -165,18 +165,6 @@ impl MutationOperators { /// * `mutations_made` - A mutable reference to a `Vec` that will be populated with any mutations made during the function's execution. /// * `file_name` - A `String` representing the name of the file being mutated. /// - /// # Examples - /// - /// ``` - /// let mut cursor = tree_sitter::TreeCursor::new(); - /// let mut mutations_made = Vec::new(); - /// let root_node = tree_sitter::Node::new(); - /// let parent_node = tree_sitter::Node::new(); - /// let file_name = String::from("example.kt"); - /// let operator = Operator::new(); - /// let parent_necessary_types = vec![KotlinTypes::IfStatement]; - /// operator.mutate(root_node, &mut cursor, Some(parent_node), &mut mutations_made, &file_name); - /// ``` fn mutate( &self, root: tree_sitter::Node, @@ -310,9 +298,7 @@ impl MutationOperators { KotlinTypes::IntegerLiteral => { let val = val.parse::().unwrap(); // Change the value and create a mutation - let rnd_val = rand::random::() % 100; - let mutated_val = val & rnd_val; - println!("new value is: {}", mutated_val); + let mutated_val = generate_random_literal(val, i32::MIN, i32::MAX); let mutation = Mutation::new( node.start_byte(), node.end_byte(), @@ -368,8 +354,19 @@ impl MutationOperators { } let val = val.parse::().unwrap(); - println!("Value is: {}", val); - println!("Found long literal"); + // Change the value and create a mutation + let mutated_val = generate_random_literal(val, i64::MIN, i64::MAX); + + let mutation = Mutation::new( + node.start_byte(), + node.end_byte(), + mutated_val.to_string(), + val.to_string(), + node.start_position().row + 1, + self.clone(), + file_name.to_string(), + ); + mutations_made.push(mutation); } KotlinTypes::RealLiteral => { // Need to strip off the f at the end @@ -377,8 +374,19 @@ impl MutationOperators { val = val.strip_suffix("f").unwrap(); } let val = val.parse::().unwrap(); - println!("Value is: {}", val); - println!("Found real literal"); + // Change the value and create a mutation + let mutated_val = generate_random_literal(val, 0.0, 1.0); + let mutation = Mutation::new( + node.start_byte(), + node.end_byte(), + mutated_val.to_string(), + val.to_string(), + node.start_position().row + 1, + self.clone(), + file_name.to_string(), + ); + + mutations_made.push(mutation); } KotlinTypes::CharacterLiteral => { // Remove the single quotes and get the value @@ -413,17 +421,34 @@ impl MutationOperators { } } +fn generate_random_literal(original_literal: T, min: T, max: T) -> T +where + T: std::cmp::PartialOrd + std::cmp::PartialEq + Copy + SampleUniform, +{ + let mut rng = rand::thread_rng(); + + // Generate a random integer different from the original literal + let mut random_literal = rng.gen_range(min..max); + + // Ensure the random literal is different from the original + while random_literal == original_literal { + random_literal = rng.gen_range(min..max); + } + + random_literal +} + impl AllMutationOperators { pub fn new() -> Self { Self { mutation_operators: vec![ - // MutationOperators::ArithmeticReplacementOperator, - // MutationOperators::UnaryRemovalOperator, - // MutationOperators::LogicalReplacementOperator, - // MutationOperators::RelationalReplacementOperator, - // MutationOperators::AssignmentReplacementOperator, - // MutationOperators::UnaryReplacementOperator, - // MutationOperators::NotNullAssertionOperator, + MutationOperators::ArithmeticReplacementOperator, + MutationOperators::UnaryRemovalOperator, + MutationOperators::LogicalReplacementOperator, + MutationOperators::RelationalReplacementOperator, + MutationOperators::AssignmentReplacementOperator, + MutationOperators::UnaryReplacementOperator, + MutationOperators::NotNullAssertionOperator, MutationOperators::ElvisRemoveOperator, MutationOperators::ElvisLiteralChangeOperator, ], @@ -451,8 +476,11 @@ impl Iterator for AllMutationOperators { #[cfg(test)] mod tests { + use std::{env::temp_dir, io::Write}; + + use crate::mutation_tool::test_util::*; + use super::*; - use crate::test_config::*; use tree_sitter::Parser; fn get_ast(text: &str) -> tree_sitter::Tree { @@ -603,6 +631,34 @@ mod tests { } } + #[test] + fn test_elvis_literal_operator() { + // Create a temp file + let temp_dir = temp_dir(); + let temp_file = temp_dir.join("temp_file.kt"); + let mut file = fs::File::create(&temp_file).expect("Failed to create temp file"); + file.write_all(KOTLIN_ELVIS_LITERAL_TEST_CODE.as_bytes()) + .expect("Failed to write to temp file"); + + let tree = get_ast(KOTLIN_ELVIS_LITERAL_TEST_CODE); + + let root = tree.root_node(); + let mut mutations_made = Vec::new(); + MutationOperators::ElvisLiteralChangeOperator.mutate( + root, + &mut root.walk(), + None, + &mut mutations_made, + &temp_file.to_str().unwrap().to_string(), + ); + println!("{:#?}", mutations_made); + assert_eq!(mutations_made.len(), 12); + // Assert that the old operator is not the same as the new operator + for mutation in mutations_made { + assert_ne!(mutation.old_op, mutation.new_op); + } + } + #[test] fn test_arthimetic_operator_does_not_create_mutations() { let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); @@ -663,6 +719,22 @@ mod tests { assert_eq!(mutations_made.len(), 0); } + #[test] + fn test_unary_removal_operator_does_not_create_mutations() { + let tree = get_ast(KOTLIN_ASSIGNMENT_TEST_CODE); + let mutations_made = + MutationOperators::UnaryRemovalOperator.find_mutation(&tree, &"file_name".into()); + assert_eq!(mutations_made.len(), 0); + } + + #[test] + fn test_unary_replacement_operator_does_not_create_mutations() { + let tree = get_ast(KOTLIN_ASSIGNMENT_TEST_CODE); + let mutations_made = + MutationOperators::UnaryReplacementOperator.find_mutation(&tree, &"file_name".into()); + assert_eq!(mutations_made.len(), 0); + } + #[test] fn test_remove_elvis_operator_does_not_create_mutations() { let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); diff --git a/src/mutation_tool/tool.rs b/src/mutation_tool/tool.rs index 2b8f418..c661143 100644 --- a/src/mutation_tool/tool.rs +++ b/src/mutation_tool/tool.rs @@ -27,11 +27,11 @@ const MAX_BUILD_THREADS: f32 = 5f32; pub struct MutationTool { parser: Arc>, - mutate_config: MutationCommandConfig, - mutation_operators: Arc>, - mutation_dir: PathBuf, - backup_dir: PathBuf, - enable_mutation_comment: bool, + pub mutate_config: MutationCommandConfig, + pub mutation_operators: Arc>, + pub mutation_dir: PathBuf, + pub backup_dir: PathBuf, + pub enable_mutation_comment: bool, thread_pool: rayon::ThreadPool, pub kodekraken_config: KodeKrakenConfig, } @@ -204,16 +204,6 @@ impl MutationTool { /// Returns a `KodeKrakenError` if there was an error creating the CSV writer, serializing a mutation, /// flushing the CSV writer, or writing to the output file. /// - /// # Examples - /// - /// ``` - /// use mutation_tool::tool::Tool; - /// - /// let tool = Tool::new(); - /// let mutations = vec![Mutation::new("file.rs", 10, "foo".to_string(), "bar".to_string())]; - /// let result = tool.save_results(&mutations); - /// assert!(result.is_ok()); - /// ``` fn save_results(&self, mutations: &Vec) -> Result<()> { let mut writer = csv::WriterBuilder::new() .from_path(Path::new(OUT_DIRECTORY).join("output.csv")) @@ -327,13 +317,6 @@ impl MutationTool { /// /// A `Result` containing a vector of `Mutation` structs. /// - /// # Examples - /// - /// ``` - /// let mut tool = Tool::new(config); - /// let file_mutations = tool.generate_mutations()?; - /// let mutations = tool.build_and_test(&file_mutations)?; - /// ``` fn build_and_test( &mut self, file_mutations: &HashMap, @@ -344,6 +327,21 @@ impl MutationTool { "Gradle is not installed. Please install Gradle and try again.".into(), )); } + + let path = PathBuf::from(&self.mutate_config.path); + // Make sure the project builds and tests pass before mutating + if !gradle::build_project_success(&path)? { + return Err(KodeKrakenError::Error( + "Project does not build successfully. Please fix the errors and try again.".into(), + )); + } + + if !gradle::project_tests_pass(&path)? { + return Err(KodeKrakenError::Error( + "Project tests do not pass. Please fix the errors and try again.".into(), + )); + } + // Get total number of mutations let num_mutations = file_mutations .iter() @@ -551,13 +549,6 @@ impl MutationTool { /// /// A `Result` containing a `HashMap` of `String` keys and `FileMutations` values, or an error if the operation fails. /// - /// # Examples - /// - /// ``` - /// let mut tool = Tool::new(); - /// let existing_files = vec!["/path/to/file1.rs", "/path/to/file2.rs"]; - /// let mutations = tool.gather_mutations_per_file(&mut existing_files).unwrap(); - /// ``` fn gather_mutations_per_file( &mut self, existing_files: &mut [String], @@ -698,8 +689,9 @@ mod tests { use uuid::Uuid; + use crate::mutation_tool::test_util::*; + use super::*; - use crate::test_config::*; fn create_temp_directory(file_contents: &str) -> (Uuid, String) { let mutation_test_id = Uuid::new_v4(); @@ -792,6 +784,16 @@ mod tests { remove_directory(mutation_test_id); } + // #[test] + // fn test_tool_exists_if_no_mutable_files_found() { + // todo!() + // } + + // #[test] + // fn test_tool_exists_if_no_mutations_are_made() { + // todo!() + // } + #[test] fn test_mutate_arithmetic_mutated_files_exist() { let (mutation_test_id, output_directory) = create_temp_directory(KOTLIN_TEST_CODE); @@ -929,4 +931,19 @@ mod tests { ); assert_all_mutations_are_correct(&mut mutator, mutation_test_id, output_directory); } + + // #[test] + // fn test_mutate_no_null_assertion_mutations_are_correct() { + // todo!() + // } + + // #[test] + // fn test_mutate_elvis_remove_mutations_are_correct() { + // todo!() + // } + + // #[test] + // fn test_mutate_elvis_literal_change_mutations_are_correct() { + // todo!() + // } } diff --git a/src/test_config/mod.rs b/src/test_config/mod.rs deleted file mode 100644 index 8066b82..0000000 --- a/src/test_config/mod.rs +++ /dev/null @@ -1,72 +0,0 @@ -pub const KOTLIN_TEST_CODE: &str = r#" -fun main() { - // Arithmetic expressions - val a = 10 - val b = 3 - val c = a + b - val d = a - b - val e = a * b - val f = a / b - val g = a % b -} -"#; - -pub const KOTLIN_RELATIONAL_TEST_CODE: &str = r#" -fun main() { - // Relational expressions - val a = 10 - val b = 3 - val c = a > b - val d = a < b - val e = a >= b - val f = a <= b - val g = a == b - val h = a != b -} -"#; - -pub const KOTLIN_LOGICAL_TEST_CODE: &str = r#" -fun main() { - // Logical expressions - val a = true - val b = false - val c = a && b - val d = a || b -} -"#; - -pub const KOTLIN_UNARY_TEST_CODE: &str = r#" -var h = 5 -h++ -h-- -++h ---h -"#; - -pub const KOTLIN_UNARY_REMOVAL_TEST_CODE: &str = r#" -var h = 5 -h++ -h-- -++h ---h -val a = !h -val b = -h -val c = +h -"#; - -pub const KOTLIN_ASSIGNMENT_TEST_CODE: &str = r#" -var h = 5 -h += 3 -h -= 1 -h *= 2 -h /= 4 -h %= 2 -"#; - -pub const KOTLIN_ELVIS_TEST_CODE: &str = r#" -fun main() { - val a = 10 - val b = 3 - val c = a ?: b -} -"#; diff --git a/tests/kode_kraken/main.rs b/tests/kode_kraken/main.rs new file mode 100644 index 0000000..ad55e44 --- /dev/null +++ b/tests/kode_kraken/main.rs @@ -0,0 +1,42 @@ +use assert_cmd::prelude::*; +use std::{fs, path::Path, process::Command}; + +#[test] +fn test_tool_runs_correctly() { + let mut cmd = Command::cargo_bin("kode-kraken").unwrap(); + + // Create command + cmd.arg("mutate").arg("tests/kotlin-test-projects/demo"); + + // Assert that the command runs successfully + cmd.assert().success(); + + let dir_path = Path::new("kode-kraken-dist"); + // Assert that kode-kraken-dist was created + assert!(dir_path.exists()); + // Assert that the backups directory was created and that the backup file exists + let backup_path = dir_path.join("backups"); + assert!(backup_path.exists()); + // Get the files in the backups directory + let backup_files = fs::read_dir(backup_path).unwrap(); + // Get the files in the mutations directory + let real_files = fs::read_dir("tests/kotlin-test-projects/demo/src/main/kotlin").unwrap(); + // Assert that the number of files in the backups directory is the same as the number of files in the mutations directory + assert_eq!(backup_files.count(), real_files.count()); + + // Assert that the logs directory was created and that the log file exists + let log_path = dir_path.join("logs"); + assert!(log_path.exists()); + assert!(log_path.join("kode-kraken.log").exists()); + // Assert that the mutations directory was created and that the mutations file exists + let mutations_path = dir_path.join("mutations"); + assert!(mutations_path.exists()); + let mutation_files = fs::read_dir(mutations_path).unwrap(); + assert!(mutation_files.count() > 0); + // Assert that the output.csv file was created + let output_path = dir_path.join("output.csv"); + assert!(output_path.exists()); + // Assert that the report.html file was created + let report_path = dir_path.join("report.html"); + assert!(report_path.exists()); +} diff --git a/kotlin-test-projects/demo/build.gradle.kts b/tests/kotlin-test-projects/demo/build.gradle.kts similarity index 100% rename from kotlin-test-projects/demo/build.gradle.kts rename to tests/kotlin-test-projects/demo/build.gradle.kts diff --git a/kotlin-test-projects/demo/gradle.properties b/tests/kotlin-test-projects/demo/gradle.properties similarity index 100% rename from kotlin-test-projects/demo/gradle.properties rename to tests/kotlin-test-projects/demo/gradle.properties diff --git a/kotlin-test-projects/demo/gradle/wrapper/gradle-wrapper.jar b/tests/kotlin-test-projects/demo/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from kotlin-test-projects/demo/gradle/wrapper/gradle-wrapper.jar rename to tests/kotlin-test-projects/demo/gradle/wrapper/gradle-wrapper.jar diff --git a/kotlin-test-projects/demo/gradle/wrapper/gradle-wrapper.properties b/tests/kotlin-test-projects/demo/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from kotlin-test-projects/demo/gradle/wrapper/gradle-wrapper.properties rename to tests/kotlin-test-projects/demo/gradle/wrapper/gradle-wrapper.properties diff --git a/kotlin-test-projects/demo/gradlew b/tests/kotlin-test-projects/demo/gradlew similarity index 100% rename from kotlin-test-projects/demo/gradlew rename to tests/kotlin-test-projects/demo/gradlew diff --git a/kotlin-test-projects/demo/gradlew.bat b/tests/kotlin-test-projects/demo/gradlew.bat similarity index 100% rename from kotlin-test-projects/demo/gradlew.bat rename to tests/kotlin-test-projects/demo/gradlew.bat diff --git a/kotlin-test-projects/demo/kodekraken.config.json b/tests/kotlin-test-projects/demo/kodekraken.config.json similarity index 100% rename from kotlin-test-projects/demo/kodekraken.config.json rename to tests/kotlin-test-projects/demo/kodekraken.config.json diff --git a/kotlin-test-projects/demo/settings.gradle.kts b/tests/kotlin-test-projects/demo/settings.gradle.kts similarity index 100% rename from kotlin-test-projects/demo/settings.gradle.kts rename to tests/kotlin-test-projects/demo/settings.gradle.kts diff --git a/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt b/tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt similarity index 82% rename from kotlin-test-projects/demo/src/main/kotlin/Calculator.kt rename to tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt index 7505828..ed9afec 100644 --- a/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt +++ b/tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt @@ -19,13 +19,12 @@ class Calculator { fun ElvisLiteralChangeOperator() { var b: String? = null - val c = 1 - val x = b?.length ?: -11 - x = b?.length ?: "Hello There" + var x = b?.length ?: -11 + var y = b?.length ?: "Hello There" // x = b?.length ?: c - x = b?.length ?: 1.0 - x = b?.length ?: 1.0f - x = b?.length ?: 1L + var z = b?.length ?: 1.0 + var a = b?.length ?: 1.0f + var c = b?.length ?: 1L // x = b?.length ?: 'a' // x = b?.length ?: 1.toShort() // x = b?.length ?: 1.toChar() diff --git a/kotlin-test-projects/demo/src/main/kotlin/NumberOperations.kt b/tests/kotlin-test-projects/demo/src/main/kotlin/NumberOperations.kt similarity index 100% rename from kotlin-test-projects/demo/src/main/kotlin/NumberOperations.kt rename to tests/kotlin-test-projects/demo/src/main/kotlin/NumberOperations.kt diff --git a/kotlin-test-projects/demo/src/test/kotlin/CalculatorTest.kt b/tests/kotlin-test-projects/demo/src/test/kotlin/CalculatorTest.kt similarity index 100% rename from kotlin-test-projects/demo/src/test/kotlin/CalculatorTest.kt rename to tests/kotlin-test-projects/demo/src/test/kotlin/CalculatorTest.kt diff --git a/kotlin-test-projects/demo/src/test/kotlin/NumberOperationTest.kt b/tests/kotlin-test-projects/demo/src/test/kotlin/NumberOperationTest.kt similarity index 100% rename from kotlin-test-projects/demo/src/test/kotlin/NumberOperationTest.kt rename to tests/kotlin-test-projects/demo/src/test/kotlin/NumberOperationTest.kt diff --git a/kotlin-test-projects/kotlin-project/build.gradle.kts b/tests/kotlin-test-projects/kotlin-project/build.gradle.kts similarity index 100% rename from kotlin-test-projects/kotlin-project/build.gradle.kts rename to tests/kotlin-test-projects/kotlin-project/build.gradle.kts diff --git a/kotlin-test-projects/kotlin-project/gradle.properties b/tests/kotlin-test-projects/kotlin-project/gradle.properties similarity index 100% rename from kotlin-test-projects/kotlin-project/gradle.properties rename to tests/kotlin-test-projects/kotlin-project/gradle.properties diff --git a/kotlin-test-projects/kotlin-project/gradle/wrapper/gradle-wrapper.jar b/tests/kotlin-test-projects/kotlin-project/gradle/wrapper/gradle-wrapper.jar similarity index 100% rename from kotlin-test-projects/kotlin-project/gradle/wrapper/gradle-wrapper.jar rename to tests/kotlin-test-projects/kotlin-project/gradle/wrapper/gradle-wrapper.jar diff --git a/kotlin-test-projects/kotlin-project/gradle/wrapper/gradle-wrapper.properties b/tests/kotlin-test-projects/kotlin-project/gradle/wrapper/gradle-wrapper.properties similarity index 100% rename from kotlin-test-projects/kotlin-project/gradle/wrapper/gradle-wrapper.properties rename to tests/kotlin-test-projects/kotlin-project/gradle/wrapper/gradle-wrapper.properties diff --git a/kotlin-test-projects/kotlin-project/gradlew b/tests/kotlin-test-projects/kotlin-project/gradlew similarity index 100% rename from kotlin-test-projects/kotlin-project/gradlew rename to tests/kotlin-test-projects/kotlin-project/gradlew diff --git a/kotlin-test-projects/kotlin-project/gradlew.bat b/tests/kotlin-test-projects/kotlin-project/gradlew.bat similarity index 100% rename from kotlin-test-projects/kotlin-project/gradlew.bat rename to tests/kotlin-test-projects/kotlin-project/gradlew.bat diff --git a/kotlin-test-projects/kotlin-project/settings.gradle.kts b/tests/kotlin-test-projects/kotlin-project/settings.gradle.kts similarity index 100% rename from kotlin-test-projects/kotlin-project/settings.gradle.kts rename to tests/kotlin-test-projects/kotlin-project/settings.gradle.kts diff --git a/kotlin-test-projects/kotlin-project/src/main/kotlin/Calculator.kt b/tests/kotlin-test-projects/kotlin-project/src/main/kotlin/Calculator.kt similarity index 100% rename from kotlin-test-projects/kotlin-project/src/main/kotlin/Calculator.kt rename to tests/kotlin-test-projects/kotlin-project/src/main/kotlin/Calculator.kt diff --git a/kotlin-test-projects/kotlin-project/src/main/kotlin/NumberOperations.kt b/tests/kotlin-test-projects/kotlin-project/src/main/kotlin/NumberOperations.kt similarity index 100% rename from kotlin-test-projects/kotlin-project/src/main/kotlin/NumberOperations.kt rename to tests/kotlin-test-projects/kotlin-project/src/main/kotlin/NumberOperations.kt diff --git a/kotlin-test-projects/kotlin-project/src/test/kotlin/CalculatorTest.kt b/tests/kotlin-test-projects/kotlin-project/src/test/kotlin/CalculatorTest.kt similarity index 100% rename from kotlin-test-projects/kotlin-project/src/test/kotlin/CalculatorTest.kt rename to tests/kotlin-test-projects/kotlin-project/src/test/kotlin/CalculatorTest.kt diff --git a/kotlin-test-projects/kotlin-project/src/test/kotlin/NumberOperationTest.kt b/tests/kotlin-test-projects/kotlin-project/src/test/kotlin/NumberOperationTest.kt similarity index 100% rename from kotlin-test-projects/kotlin-project/src/test/kotlin/NumberOperationTest.kt rename to tests/kotlin-test-projects/kotlin-project/src/test/kotlin/NumberOperationTest.kt diff --git a/kotlin-test-projects/mutations/BuildFails.kt b/tests/kotlin-test-projects/mutations/BuildFails.kt similarity index 100% rename from kotlin-test-projects/mutations/BuildFails.kt rename to tests/kotlin-test-projects/mutations/BuildFails.kt diff --git a/kotlin-test-projects/mutations/Killed.kt b/tests/kotlin-test-projects/mutations/Killed.kt similarity index 100% rename from kotlin-test-projects/mutations/Killed.kt rename to tests/kotlin-test-projects/mutations/Killed.kt diff --git a/kotlin-test-projects/mutations/Survived.kt b/tests/kotlin-test-projects/mutations/Survived.kt similarity index 100% rename from kotlin-test-projects/mutations/Survived.kt rename to tests/kotlin-test-projects/mutations/Survived.kt diff --git a/kotlin-test-projects/no-gradle-project/Calculator.kt b/tests/kotlin-test-projects/no-gradle-project/Calculator.kt similarity index 100% rename from kotlin-test-projects/no-gradle-project/Calculator.kt rename to tests/kotlin-test-projects/no-gradle-project/Calculator.kt diff --git a/kotlin-test-projects/no-gradle-project/Main.kt b/tests/kotlin-test-projects/no-gradle-project/Main.kt similarity index 100% rename from kotlin-test-projects/no-gradle-project/Main.kt rename to tests/kotlin-test-projects/no-gradle-project/Main.kt From 4652175768f5e0fc78a4962d53f8f1a4ca59efb0 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Wed, 6 Dec 2023 17:00:40 -0500 Subject: [PATCH 06/23] working on small refactors --- src/mutation_tool/tool.rs | 207 +++++++++++++++++++++++++------------- 1 file changed, 139 insertions(+), 68 deletions(-) diff --git a/src/mutation_tool/tool.rs b/src/mutation_tool/tool.rs index c661143..662bef2 100644 --- a/src/mutation_tool/tool.rs +++ b/src/mutation_tool/tool.rs @@ -321,57 +321,23 @@ impl MutationTool { &mut self, file_mutations: &HashMap, ) -> Result> { - // Check to see if gradle is installed - if !gradle::is_gradle_installed() { - return Err(KodeKrakenError::Error( - "Gradle is not installed. Please install Gradle and try again.".into(), - )); - } - - let path = PathBuf::from(&self.mutate_config.path); - // Make sure the project builds and tests pass before mutating - if !gradle::build_project_success(&path)? { - return Err(KodeKrakenError::Error( - "Project does not build successfully. Please fix the errors and try again.".into(), - )); - } - - if !gradle::project_tests_pass(&path)? { - return Err(KodeKrakenError::Error( - "Project tests do not pass. Please fix the errors and try again.".into(), - )); - } + self.gradle_checks()?; // Get total number of mutations let num_mutations = file_mutations - .iter() - .fold(0, |acc, (_, fm)| acc + fm.mutations.len()); - // Set up progress bar - let progress_bar = Arc::new(ProgressBar::new(num_mutations as u64)); - progress_bar.set_style( - ProgressStyle::default_bar() - .template("[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg} - Running tests...") - .map_err(|e| KodeKrakenError::Error(e.to_string()))? - .progress_chars("=> "), - ); + .values() + .map(|fm| fm.mutations.len()) + .sum::(); - // Make Copies of all files + // Create Progress Bar + let progress_bar = fun_name(num_mutations)?; + + // Make copies of all files self.copy_files(file_mutations)?; - // Merge all mutants into one vector - let mut all_mutations: Vec = vec![]; - for (_, fm) in file_mutations.iter() { - for mutation in fm.mutations.iter() { - all_mutations.push(mutation.clone()); - } - } - // Partition the mutations into chunks - let chunk_size = ((all_mutations.len() as f32) / MAX_BUILD_THREADS).ceil() as usize; + // Create mutation chunks + let chunks = create_mutation_chucks(file_mutations); - let mut chunks: Vec> = all_mutations - .chunks(chunk_size) - .map(|c| c.to_vec()) - .collect(); // Set up threading let path = Arc::new(self.mutate_config.path.clone()); let mutation_dir = Arc::new(self.mutation_dir.clone()); @@ -380,43 +346,42 @@ impl MutationTool { .num_threads(chunks.len()) .build() .map_err(|e| KodeKrakenError::Error(e.to_string()))?; + thread_pool.scope(|s| { - for chunck in chunks.iter_mut() { + for chunk in chunks.iter() { // Create unique temp directory let uuid = uuid::Uuid::new_v4(); - let mut td = Path::new(OUT_DIRECTORY).join(format!("temp/{}", uuid)); - fs::create_dir_all(&td).expect("Failed to create temp directory"); + let temp_dir = Path::new(OUT_DIRECTORY).join(format!("temp/{}", uuid)); + fs::create_dir_all(&temp_dir).expect("Failed to create temp directory"); + // Create directory structure inside temp directory that matches the original project let dir = PathBuf::from(&self.mutate_config.path); - let mut config_prefix = PathBuf::new(); - for c in dir.components() { - if let Component::Normal(dir) = c { - td = td.join(dir); - config_prefix = config_prefix.join(dir); - } - } - fs::create_dir_all(&td).expect("Failed to create temp directory"); - self.create_temp_directory(dir, &td) + let config_prefix = dir.components().collect::(); + let temp_dir = temp_dir.join(&config_prefix); + fs::create_dir_all(&temp_dir).expect("Failed to create temp directory"); + self.create_temp_directory(dir, &temp_dir) .expect("Failed to create temp directory"); + // Run gradle build and tests in parallel let path = path.clone(); let mutation_dir = mutation_dir.clone(); let backup_dir = backup_dir.clone(); let progress_bar = progress_bar.clone(); + s.spawn(move |_| { - chunck.iter_mut().for_each(|mutation| { - let original_file_name = mutation.file_name.clone(); - let file_name = Path::new(&original_file_name) - .strip_prefix(path.as_ref()) + for mutation in chunk.iter() { + let original_file_name = &mutation.file_name; + let file_name = Path::new(original_file_name) + .strip_prefix(&path) .expect("Failed to strip prefix"); - let original_file_path = - PathBuf::from(format!("{}/{}", td.display(), file_name.display())); + let original_file_path = temp_dir.join(file_name); progress_bar.inc(1); + let mutated_file_path = mutation_dir.join(format!( "{}_{}", mutation.id, - Path::new(&file_name) + Path::new(original_file_name) .file_name() .expect("Failed to get the filename") .to_str() @@ -424,37 +389,74 @@ impl MutationTool { )); if let Err(err) = gradle::run( - &PathBuf::from(&td), + &temp_dir, &mutated_file_path, &original_file_path, - mutation, + &mut mutation, ) { tracing::error!("An error occurred building and testing: {}", err); mutation.result = MutationResult::BuildFailed; } + let backup_path = backup_dir.join( - Path::new(&file_name) + Path::new(original_file_name) .file_name() .expect("Failed to convert file name to string") .to_str() .expect("Failed to convert file name to string"), ); + // Restore original file - fs::copy(backup_path, &original_file_path) + fs::copy(&backup_path, &original_file_path) .expect("Failed to restore original file"); - }); + } }); } }); + progress_bar.finish(); + // Delete temp directory if let Err(err) = fs::remove_dir_all(Path::new(OUT_DIRECTORY).join("temp")) { println!("[ERROR] Failed to delete kode-kraken-dist/temp directory. Please view logs for more information."); tracing::error!("Failed to delete kode-kraken-dist/temp directory: {}", err); } + Ok(chunks.into_iter().flatten().collect()) } + fn gradle_checks(&mut self) -> Result<(), KodeKrakenError> { + if !gradle::is_gradle_installed() { + return Err(KodeKrakenError::Error( + "Gradle is not installed. Please install Gradle and try again.".into(), + )); + } + let path = PathBuf::from(&self.mutate_config.path); + if !gradle::build_project_success(&path)? { + return Err(KodeKrakenError::Error( + "Project does not build successfully. Please fix the errors and try again.".into(), + )); + } + Ok(if !gradle::project_tests_pass(&path)? { + return Err(KodeKrakenError::Error( + "Project tests do not pass. Please fix the errors and try again.".into(), + )); + }) + } + + /// Recursively creates a temporary directory and copies all files from the given directory into it, + /// excluding the "kode-kraken-dist" folder. If a file is named "gradlew" or "gradlew.bat", it is copied + /// to the temporary directory with the same permissions. All other files are written to the temporary + /// directory with their original contents. + /// + /// # Arguments + /// + /// * `dir` - The directory to copy files from. + /// * `temp_dir` - The temporary directory to create and copy files into. + /// + /// # Returns + /// + /// Returns `Ok(())` if the operation is successful, or an error if any file operations fail. fn create_temp_directory(&self, dir: PathBuf, temp_dir: &Path) -> Result<()> { for entry in dir.read_dir()? { let path = entry?.path(); @@ -553,25 +555,38 @@ impl MutationTool { &mut self, existing_files: &mut [String], ) -> Result> { + // Record the start time for measuring performance let start = Instant::now(); + + // Create shared mutable state for collecting mutations and mutation count let file_mutations: Arc>> = Arc::new(Mutex::new(HashMap::new())); let mutation_count = Arc::new(Mutex::new(0)); + + // Use thread pool to parallelize mutation gathering for each file self.thread_pool.scope(|s| { for file in existing_files { + // Clone shared state for each thread let mutation_count = mutation_count.clone(); let file_mutations = file_mutations.clone(); let parser = self.parser.clone(); let mutation_operators = self.mutation_operators.clone(); + + // Spawn a thread for each file s.spawn(move |_| { + // Parse the file content using the parser let ast = parser .lock() .expect("Failed to lock parser") .parse(fs::read_to_string(&file).expect("File Not Found!"), None) .expect("Parsing file failed"); + + // Iterate through mutation operators to find mutations for mut_op in mutation_operators.iter() { // Get a list of mutations that can be made let mutations = mut_op.find_mutation(&ast, file); + + // Update mutation count and file mutations *mutation_count .lock() .expect("Failed to lock mutation_count var") += mutations.len(); @@ -587,11 +602,15 @@ impl MutationTool { }); } }); + + // Record the end time and log the time taken to gather mutations let end = Instant::now(); tracing::info!( "Time to gather mutations: {}", end.duration_since(start).as_secs() ); + + // Unwrap and log the total mutation count let mutation_count = Arc::try_unwrap(mutation_count) .map_err(|_| KodeKrakenError::Error("Failed to unwrap mutation_count".to_string()))? .into_inner() @@ -599,6 +618,7 @@ impl MutationTool { tracing::info!("Mutations made to all files"); tracing::info!("Total mutations made: {}", mutation_count); + // Return an error if no mutations were found, otherwise return the collected file mutations if mutation_count == 0 { return Err(KodeKrakenError::Error( "No mutations were found in the project".into(), @@ -610,17 +630,28 @@ impl MutationTool { .map_err(|_| KodeKrakenError::Error("Failed to unwrap file_mutations".to_string()))?) } + /// Gets all files from the given directory and adds them to the given vector. + /// This function is recursive, so it will also get files from subdirectories. + /// It will ignore files and directories that match the ignore patterns in the `KodeKrakenConfig`. + /// It will also ignore the `kode-kraken-dist` directory. + /// If the given path is not a directory, it will return an error. fn get_files_from_directory( &self, path: String, existing_files: &mut Vec, ) -> Result<()> { + // Open the directory at the given path let directory = Path::new(path.as_str()) .read_dir() .map_err(|_| KodeKrakenError::MutationGatheringError)?; + + // Iterate over entries in the directory for entry in directory { + // Handle potential errors when reading directory entries let entry = entry.map_err(|_| KodeKrakenError::MutationGatheringError)?; let path = entry.path(); + + // Skip processing if the entry corresponds to a specific directory if path .file_name() .ok_or(KodeKrakenError::MutationGatheringError)? @@ -630,6 +661,8 @@ impl MutationTool { { continue; } + + // Recursively process subdirectories if path.is_dir() { self.get_files_from_directory( path.to_str() @@ -639,9 +672,13 @@ impl MutationTool { )?; continue; } + + // Skip files with extensions other than "kt" if path.extension() != Some("kt".as_ref()) { continue; } + + // Skip files in ignored directories if path.components().any(|p| { self.kodekraken_config .ignore @@ -652,7 +689,10 @@ impl MutationTool { }) { continue; } + let file_name = entry.file_name(); + + // Skip files matching the specified regex patterns if self .kodekraken_config .ignore @@ -670,6 +710,8 @@ impl MutationTool { { continue; } + + // Add the path to the list of existing files existing_files.push( path.to_str() .ok_or(KodeKrakenError::Error( @@ -683,6 +725,35 @@ impl MutationTool { } } +fn create_mutation_chucks(file_mutations: &HashMap) -> Vec> { + // Merge all mutants into one vector + let all_mutations: Vec = file_mutations + .values() + .flat_map(|fm| fm.mutations.clone()) + .collect(); + + // Partition the mutations into chunks + let chunk_size = ((all_mutations.len() as f32) / MAX_BUILD_THREADS).ceil() as usize; + let chunks: Vec> = all_mutations + .chunks(chunk_size) + .map(|c| c.to_vec()) + .collect(); + chunks +} + +fn create_progress_bar(num_mutations: usize) -> Result, KodeKrakenError> { + let progress_bar = Arc::new(ProgressBar::new(num_mutations as u64)); + progress_bar.set_style( + ProgressStyle::default_bar() + .template( + "[{elapsed_precise}] {bar:40.cyan/blue} {pos:>7}/{len:7} {msg} - Running tests...", + ) + .map_err(|e| KodeKrakenError::Error(e.to_string()))? + .progress_chars("=> "), + ); + Ok(progress_bar) +} + #[cfg(test)] mod tests { use std::path::PathBuf; From d44e72e339dcce71320177d65307d516779860fd Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Wed, 6 Dec 2023 17:01:48 -0500 Subject: [PATCH 07/23] change about text --- assets/about.txt | 82 +++++++++++++++++++++++++----------------------- 1 file changed, 42 insertions(+), 40 deletions(-) diff --git a/assets/about.txt b/assets/about.txt index f94705d..b2a9b4f 100644 --- a/assets/about.txt +++ b/assets/about.txt @@ -1,42 +1,44 @@ -########################################################################################################### +##################################################################################################################################### + + /$$ /$$ /$$ /$$ /$$ /$$ /$$ +| $$$ /$$$ | $$ | $$ | $$ /$$/ | $$ +| $$$$ /$$$$ /$$ /$$ /$$$$$$ /$$$$$$ /$$$$$$$ /$$$$$$ | $$ /$$/ /$$$$$$ /$$$$$$ | $$ /$$ /$$$$$$ /$$$$$$$ +| $$ $$/$$ $$| $$ | $$|_ $$_/ |____ $$| $$__ $$|_ $$_/ | $$$$$/ /$$__ $$|____ $$| $$ /$$/ /$$__ $$| $$__ $$ +| $$ $$$| $$| $$ | $$ | $$ /$$$$$$$| $$ \ $$ | $$ | $$ $$ | $$ \__/ /$$$$$$$| $$$$$$/ | $$$$$$$$| $$ \ $$ +| $$\ $ | $$| $$ | $$ | $$ /$$ /$$__ $$| $$ | $$ | $$ /$$ | $$\ $$ | $$ /$$__ $$| $$_ $$ | $$_____/| $$ | $$ +| $$ \/ | $$| $$$$$$/ | $$$$/| $$$$$$$| $$ | $$ | $$$$/ | $$ \ $$| $$ | $$$$$$$| $$ \ $$| $$$$$$$| $$ | $$ +|__/ |__/ \______/ \___/ \_______/|__/ |__/ \___/ |__/ \__/|__/ \_______/|__/ \__/ \_______/|__/ |__/ - /$$ /$$ /$$ /$$ /$$ /$$ -| $$ /$$/ | $$ | $$ /$$/ | $$ -| $$ /$$/ /$$$$$$ /$$$$$$$ /$$$$$$ | $$ /$$/ /$$$$$$ /$$$$$$ | $$ /$$ /$$$$$$ /$$$$$$$ -| $$$$$/ /$$__ $$ /$$__ $$ /$$__ $$ | $$$$$/ /$$__ $$|____ $$| $$ /$$/ /$$__ $$| $$__ $$ -| $$ $$ | $$ \ $$| $$ | $$| $$$$$$$$ | $$ $$ | $$ \__/ /$$$$$$$| $$$$$$/ | $$$$$$$$| $$ \ $$ -| $$\ $$ | $$ | $$| $$ | $$| $$_____/ | $$\ $$ | $$ /$$__ $$| $$_ $$ | $$_____/| $$ | $$ -| $$ \ $$| $$$$$$/| $$$$$$$| $$$$$$$ | $$ \ $$| $$ | $$$$$$$| $$ \ $$| $$$$$$$| $$ | $$ -|__/ \__/ \______/ \_______/ \_______/ |__/ \__/|__/ \_______/|__/ \__/ \_______/|__/ |__/ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣶⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⡿⠿⢿⣿⣷⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⣷⣠⣴⣶⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠻⢿⡄⠀⠀⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⣿⣿⣟⠉⢹⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠛⠿⠿⠿⠋⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠉⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⢀⣀⣀⠀⠀⠀⠀⠀⣰⣿⣿⡟⠁⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠈⢿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⣴⣾⣿⣿⣿⣿⣶⡀⢀⣾⣿⣿⠋⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠹⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⢸⣿⡁⠀⠀⢀⣿⣿⢇⣾⣿⣿⠃⠀⠀⠀⠀⠀⠀⣿⡈⠙⢿⣿⣿⣿⠿⠋⢩⡇⠀⠀⠀⠀⠀⠀⠙⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀ - ⠈⠛⠛⣠⣴⣿⡿⠋⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⣿⣿⣶⣾⣿⣿⣿⣷⣶⣿⡇⠀⠀⠀⠀⠀⠀⠀⣻⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⣠⣾⣿⡿⠋⠀⠀⢻⣿⣿⣷⡀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⢠⣿⣿⣏⣠⣤⣶⣤⠀⠀⠀⠀ - ⢰⣿⣿⣟⠀⠀⠀⠀⠘⢿⣿⣿⣿⣷⣶⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣤⣤⣴⣿⣿⣿⣿⠋⠀⠀⠀⠀⠀⠀⠀ - ⢸⣿⣿⣿⣦⣄⣀⠀⠀⠀⠉⠙⠛⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠛⠉⢻⣿⣄⠀⠀⠀⠀⠀⠀⠀ - ⠀⠙⠿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠈⢿⣿⣶⣄⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠈⠉⠉⠙⠛⠛⠛⠛⠛⣿⣿⣿⣿⠟⢋⣿⣿⣿⡿⠋⠙⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠙⢿⣿⣧⡀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⠟⠁⠀⣿⣿⣿⠟⠀⠀⢀⣿⣿⣿⡿⢿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠈⢿⣿⣷⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠏⠀⠀⢸⣿⣿⣿⠀⠀⠀⢸⣿⣿⣿⠀⠈⢻⣿⣿⣿⢿⣿⣿⣦⡀⠀⠀⠀⣸⣿⣿⠀⣀⡄ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⡟⠀⠀⠀⠸⣿⣿⣿⠀⠀⠀⢻⣿⣿⣿⠀⠀⠀⢻⣿⣿⡆⠹⢿⣿⣿⣶⣶⣾⣿⣿⣿⣿⠋⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⡿⠁⠀⠀⠀⠀⢿⣿⣿⡆⠀⠀⠸⣿⣿⣿⡄⠀⠀⠀⢿⣿⣿⠀⠀⠙⠛⠿⠿⠿⠛⠋⢸⣿⠀⠀ - ⠀⠀⠀⠀⠀⠀⣠⣴⣿⣿⡿⠛⠁⠀⠀⠀⠀⠀⠘⣿⣿⣿⠀⠀⠀⣿⣿⣿⡇⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⠀ - ⠀⠀⠀⢠⣶⣿⣿⠿⠋⠁⠒⠛⢻⣷⠀⠀⠀⠀⠀⢹⣿⣿⡇⠀⣠⣿⣿⣿⢃⣴⣿⠟⠛⢿⣿⣿⡄⠀⠀⠀⠀⠀⠀⢠⣿⣿⠀⠀ - ⠀⠀⢰⣿⣿⠟⠁⠀⠀⠀⠀⢀⣾⡟⠀⠀⠀⠀⠀⠘⣿⣿⣧⣾⣿⣿⠟⠁⣾⣿⡇⠀⠀⠘⢿⣿⣿⣦⡀⠀⠀⣀⣴⣿⣿⠃⠀⠀ - ⠀⠀⣿⣿⡇⠀⠀⢀⡄⠀⢠⣿⣿⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠟⠁⠀⠀⢿⣿⣇⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⡿⠟⠁⠀⠀⠀ - ⠀⠀⠹⣿⣷⣄⣀⣼⡇⠀⢸⣿⣿⡀⠀⠀⠀⠀⣠⣿⣿⣿⡿⠋⠀⠀⠀⠀⢸⣿⣿⡀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠈⠛⠛⠛⠋⠀⠀⠀⢻⣿⣿⣶⣶⣶⣿⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠛⠛⠛⠛⠉⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣷⣄⣀⠀⢀⣀⣴⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ - ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ -########################################################################################################### \ No newline at end of file + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣀⣀⣀⣀⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣶⣿⣿⣿⣿⣿⣿⣿⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⠀⠀⠀⢠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⡿⠿⢿⣿⣷⣠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡆⣷⣠⣴⣶⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠻⢿⡄⠀⠀⠹⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣧⣿⣿⣟⠉⢹⣿⣷⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠛⠿⠿⠿⠋⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⡿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠉⣿⣿⣧⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⢀⣀⣀⠀⠀⠀⠀⠀⣰⣿⣿⡟⠁⠘⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠈⢿⣿⣷⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⣴⣾⣿⣿⣿⣿⣶⡀⢀⣾⣿⣿⠋⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⠃⠀⠀⠀⠀⠹⣿⣿⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⢸⣿⡁⠀⠀⢀⣿⣿⢇⣾⣿⣿⠃⠀⠀⠀⠀⠀⠀⣿⡈⠙⢿⣿⣿⣿⠿⠋⢩⡇⠀⠀⠀⠀⠀⠀⠙⣿⣿⣇⠀⠀⠀⠀⠀⠀⠀⠀ + ⠈⠛⠛⣠⣴⣿⡿⠋⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⣿⣿⣶⣾⣿⣿⣿⣷⣶⣿⡇⠀⠀⠀⠀⠀⠀⠀⣻⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⣠⣾⣿⡿⠋⠀⠀⢻⣿⣿⣷⡀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠀⢠⣿⣿⣏⣠⣤⣶⣤⠀⠀⠀⠀ + ⢰⣿⣿⣟⠀⠀⠀⠀⠘⢿⣿⣿⣿⣷⣶⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣶⣤⣤⣴⣿⣿⣿⣿⠋⠀⠀⠀⠀⠀⠀⠀ + ⢸⣿⣿⣿⣦⣄⣀⠀⠀⠀⠉⠙⠛⠛⢿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⡿⠿⠛⠉⢻⣿⣄⠀⠀⠀⠀⠀⠀⠀ + ⠀⠙⠿⣿⣿⣿⣿⣿⣿⣿⣶⣶⣶⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣦⡀⠀⠀⠀⠈⢿⣿⣶⣄⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠈⠉⠉⠙⠛⠛⠛⠛⠛⣿⣿⣿⣿⠟⢋⣿⣿⣿⡿⠋⠙⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠙⢿⣿⣧⡀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢀⣾⣿⣿⠟⠁⠀⣿⣿⣿⠟⠀⠀⢀⣿⣿⣿⡿⢿⣿⣿⣿⣿⣿⣿⣆⠀⠀⠀⠀⠈⢿⣿⣷⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣾⣿⣿⠏⠀⠀⢸⣿⣿⣿⠀⠀⠀⢸⣿⣿⣿⠀⠈⢻⣿⣿⣿⢿⣿⣿⣦⡀⠀⠀⠀⣸⣿⣿⠀⣀⡄ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣰⣿⣿⡟⠀⠀⠀⠸⣿⣿⣿⠀⠀⠀⢻⣿⣿⣿⠀⠀⠀⢻⣿⣿⡆⠹⢿⣿⣿⣶⣶⣾⣿⣿⣿⣿⠋⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⢀⣴⣿⣿⡿⠁⠀⠀⠀⠀⢿⣿⣿⡆⠀⠀⠸⣿⣿⣿⡄⠀⠀⠀⢿⣿⣿⠀⠀⠙⠛⠿⠿⠿⠛⠋⢸⣿⠀⠀ + ⠀⠀⠀⠀⠀⠀⣠⣴⣿⣿⡿⠛⠁⠀⠀⠀⠀⠀⠘⣿⣿⣿⠀⠀⠀⣿⣿⣿⡇⠀⠀⠀⢸⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⢸⣿⠀⠀ + ⠀⠀⠀⢠⣶⣿⣿⠿⠋⠁⠒⠛⢻⣷⠀⠀⠀⠀⠀⢹⣿⣿⡇⠀⣠⣿⣿⣿⢃⣴⣿⠟⠛⢿⣿⣿⡄⠀⠀⠀⠀⠀⠀⢠⣿⣿⠀⠀ + ⠀⠀⢰⣿⣿⠟⠁⠀⠀⠀⠀⢀⣾⡟⠀⠀⠀⠀⠀⠘⣿⣿⣧⣾⣿⣿⠟⠁⣾⣿⡇⠀⠀⠘⢿⣿⣿⣦⡀⠀⠀⣀⣴⣿⣿⠃⠀⠀ + ⠀⠀⣿⣿⡇⠀⠀⢀⡄⠀⢠⣿⣿⠀⠀⠀⠀⠀⠀⢰⣿⣿⣿⣿⠟⠁⠀⠀⢿⣿⣇⠀⠀⠀⠈⠻⣿⣿⣿⣿⣿⣿⡿⠟⠁⠀⠀⠀ + ⠀⠀⠹⣿⣷⣄⣀⣼⡇⠀⢸⣿⣿⡀⠀⠀⠀⠀⣠⣿⣿⣿⡿⠋⠀⠀⠀⠀⢸⣿⣿⡀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠁⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠈⠛⠛⠛⠋⠀⠀⠀⢻⣿⣿⣶⣶⣶⣿⣿⣿⣿⣿⠁⠀⠀⠀⠀⠀⠀⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠛⠛⠛⠛⠛⠉⣿⣿⣿⡀⠀⠀⠀⠀⠀⠀⣿⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⣿⣿⣷⣄⣀⠀⢀⣀⣴⣿⣿⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢿⣿⣿⣿⣿⣿⣿⣿⡿⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠉⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ + +##################################################################################################################################### \ No newline at end of file From 8af61d9c9dbf867de42e92578ec0943b59edc382 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Wed, 6 Dec 2023 17:10:32 -0500 Subject: [PATCH 08/23] fix build errors --- src/mutation_tool/tool.rs | 73 ++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 39 deletions(-) diff --git a/src/mutation_tool/tool.rs b/src/mutation_tool/tool.rs index 662bef2..86b792c 100644 --- a/src/mutation_tool/tool.rs +++ b/src/mutation_tool/tool.rs @@ -325,18 +325,16 @@ impl MutationTool { // Get total number of mutations let num_mutations = file_mutations - .values() - .map(|fm| fm.mutations.len()) - .sum::(); + .iter() + .fold(0, |acc, (_, fm)| acc + fm.mutations.len()); + // Set up progress bar + let progress_bar = create_progress_bar(num_mutations)?; - // Create Progress Bar - let progress_bar = fun_name(num_mutations)?; - - // Make copies of all files + // Make Copies of all files self.copy_files(file_mutations)?; - // Create mutation chunks - let chunks = create_mutation_chucks(file_mutations); + // Merge all mutants into one vector + let mut chunks = create_mutation_chucks(file_mutations); // Set up threading let path = Arc::new(self.mutate_config.path.clone()); @@ -346,42 +344,43 @@ impl MutationTool { .num_threads(chunks.len()) .build() .map_err(|e| KodeKrakenError::Error(e.to_string()))?; - thread_pool.scope(|s| { - for chunk in chunks.iter() { + for chunck in chunks.iter_mut() { // Create unique temp directory let uuid = uuid::Uuid::new_v4(); - let temp_dir = Path::new(OUT_DIRECTORY).join(format!("temp/{}", uuid)); - fs::create_dir_all(&temp_dir).expect("Failed to create temp directory"); - + let mut td = Path::new(OUT_DIRECTORY).join(format!("temp/{}", uuid)); + fs::create_dir_all(&td).expect("Failed to create temp directory"); // Create directory structure inside temp directory that matches the original project let dir = PathBuf::from(&self.mutate_config.path); - let config_prefix = dir.components().collect::(); - let temp_dir = temp_dir.join(&config_prefix); - fs::create_dir_all(&temp_dir).expect("Failed to create temp directory"); - self.create_temp_directory(dir, &temp_dir) + let mut config_prefix = PathBuf::new(); + for c in dir.components() { + if let Component::Normal(dir) = c { + td = td.join(dir); + config_prefix = config_prefix.join(dir); + } + } + fs::create_dir_all(&td).expect("Failed to create temp directory"); + self.create_temp_directory(dir, &td) .expect("Failed to create temp directory"); - // Run gradle build and tests in parallel let path = path.clone(); let mutation_dir = mutation_dir.clone(); let backup_dir = backup_dir.clone(); let progress_bar = progress_bar.clone(); - s.spawn(move |_| { - for mutation in chunk.iter() { - let original_file_name = &mutation.file_name; - let file_name = Path::new(original_file_name) - .strip_prefix(&path) + chunck.iter_mut().for_each(|mutation| { + let original_file_name = mutation.file_name.clone(); + let file_name = Path::new(&original_file_name) + .strip_prefix(path.as_ref()) .expect("Failed to strip prefix"); - let original_file_path = temp_dir.join(file_name); + let original_file_path = + PathBuf::from(format!("{}/{}", td.display(), file_name.display())); progress_bar.inc(1); - let mutated_file_path = mutation_dir.join(format!( "{}_{}", mutation.id, - Path::new(original_file_name) + Path::new(&file_name) .file_name() .expect("Failed to get the filename") .to_str() @@ -389,43 +388,38 @@ impl MutationTool { )); if let Err(err) = gradle::run( - &temp_dir, + &PathBuf::from(&td), &mutated_file_path, &original_file_path, - &mut mutation, + mutation, ) { tracing::error!("An error occurred building and testing: {}", err); mutation.result = MutationResult::BuildFailed; } - let backup_path = backup_dir.join( - Path::new(original_file_name) + Path::new(&file_name) .file_name() .expect("Failed to convert file name to string") .to_str() .expect("Failed to convert file name to string"), ); - // Restore original file - fs::copy(&backup_path, &original_file_path) + fs::copy(backup_path, &original_file_path) .expect("Failed to restore original file"); - } + }); }); } }); - progress_bar.finish(); - // Delete temp directory if let Err(err) = fs::remove_dir_all(Path::new(OUT_DIRECTORY).join("temp")) { println!("[ERROR] Failed to delete kode-kraken-dist/temp directory. Please view logs for more information."); tracing::error!("Failed to delete kode-kraken-dist/temp directory: {}", err); } - Ok(chunks.into_iter().flatten().collect()) } - fn gradle_checks(&mut self) -> Result<(), KodeKrakenError> { + fn gradle_checks(&mut self) -> Result<()> { if !gradle::is_gradle_installed() { return Err(KodeKrakenError::Error( "Gradle is not installed. Please install Gradle and try again.".into(), @@ -437,6 +431,7 @@ impl MutationTool { "Project does not build successfully. Please fix the errors and try again.".into(), )); } + Ok(if !gradle::project_tests_pass(&path)? { return Err(KodeKrakenError::Error( "Project tests do not pass. Please fix the errors and try again.".into(), @@ -741,7 +736,7 @@ fn create_mutation_chucks(file_mutations: &HashMap) -> Ve chunks } -fn create_progress_bar(num_mutations: usize) -> Result, KodeKrakenError> { +fn create_progress_bar(num_mutations: usize) -> Result> { let progress_bar = Arc::new(ProgressBar::new(num_mutations as u64)); progress_bar.set_style( ProgressStyle::default_bar() From feb11e54e286e7a912d900a96312afc65cb77dfb Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Wed, 6 Dec 2023 17:22:50 -0500 Subject: [PATCH 09/23] Fix tests --- src/html_gen.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/html_gen.rs b/src/html_gen.rs index bca0821..8857648 100644 --- a/src/html_gen.rs +++ b/src/html_gen.rs @@ -158,6 +158,9 @@ mod test { mutation4.clone(), ]; + // Create kode-kraken-dist directory + std::fs::create_dir_all("kode-kraken-dist").unwrap(); + // Create test data let mut file_mutations = HashMap::new(); for mutation in &mutations { From a114153ab39854fcce7427160c59789bd0860d72 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Thu, 7 Dec 2023 20:48:04 -0500 Subject: [PATCH 10/23] working on implementing literal op --- src/mutation_tool/mod.rs | 17 +++++++++++ src/mutation_tool/operators.rs | 56 +++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 1 deletion(-) diff --git a/src/mutation_tool/mod.rs b/src/mutation_tool/mod.rs index 89ab941..49961a8 100644 --- a/src/mutation_tool/mod.rs +++ b/src/mutation_tool/mod.rs @@ -105,5 +105,22 @@ fun main() { val r = q ?: 'a' val s = q ?: 'b' } +"#; + + pub const KOTLIN_LITERAL_TEST_CODE: &str = r#" +fun main() { + val a = 10 + val b = -10 + val c = 10.0 + val d = -10.0 + val e = 10L + val f = -10L + val g = 10.0f + val h = -10.0f + val i = true + val j = false + val k = 'a' + val l = "a" +} "#; } diff --git a/src/mutation_tool/operators.rs b/src/mutation_tool/operators.rs index 84e88f5..74e05e3 100644 --- a/src/mutation_tool/operators.rs +++ b/src/mutation_tool/operators.rs @@ -25,6 +25,7 @@ pub enum MutationOperators { NotNullAssertionOperator, ElvisRemoveOperator, ElvisLiteralChangeOperator, + LiteralChangeOpeator, } impl Display for MutationOperators { @@ -42,6 +43,7 @@ impl Display for MutationOperators { MutationOperators::NotNullAssertionOperator => "NotNullAssertionOperator", MutationOperators::ElvisRemoveOperator => "ElvisRemoveOperator", MutationOperators::ElvisLiteralChangeOperator => "ElvisLiteralChangeOperator", + MutationOperators::LiteralChangeOpeator => "LiteralChangeOpeator", } ) } @@ -114,6 +116,16 @@ impl MutationOperators { .into_iter() .collect() } + MutationOperators::LiteralChangeOpeator => vec![ + KotlinTypes::IntegerLiteral, + KotlinTypes::LineStringLiteral, + KotlinTypes::BooleanLiteral, + KotlinTypes::LongLiteral, + KotlinTypes::RealLiteral, + KotlinTypes::CharacterLiteral, + ] + .into_iter() + .collect(), } } @@ -143,6 +155,16 @@ impl MutationOperators { | MutationOperators::ElvisLiteralChangeOperator => { vec![KotlinTypes::ElvisExpression] } + MutationOperators::LiteralChangeOpeator => vec![ + KotlinTypes::IntegerLiteral, + KotlinTypes::LineStringLiteral, + KotlinTypes::BooleanLiteral, + KotlinTypes::LongLiteral, + KotlinTypes::RealLiteral, + KotlinTypes::CharacterLiteral, + KotlinTypes::PrefixExpression, + KotlinTypes::PostfixExpression, + ], } } @@ -253,6 +275,10 @@ impl MutationOperators { MutationOperators::ElvisLiteralChangeOperator => { self.mutate_literal(&root_node.parent().unwrap(), &mut mutations_made, file_name) } + MutationOperators::LiteralChangeOpeator => { + println!("Literal change operator on root: {:#?}", root_node); + self.mutate_literal(root_node, &mut mutations_made, file_name) + } _ => { // Create a mutant for all mutation operators mutation_operators.iter().for_each(|operator| { @@ -288,7 +314,10 @@ impl MutationOperators { .collect::>(); println!("Children: {:#?}", children); println!("Num of children: {}", children.len()); - let node = children.iter().last().unwrap(); + let node = match children.iter().last() { + Some(node) => node, + None => root_node, + }; let child_type = KotlinTypes::new(node.kind()).expect("Failed to convert to KotlinType"); @@ -659,6 +688,31 @@ mod tests { } } + #[test] + fn test_literal_change_operator() { + // Create a temp file + let temp_dir = temp_dir(); + let temp_file = temp_dir.join("literal_change_temp_file.kt"); + let mut file = fs::File::create(&temp_file).expect("Failed to create temp file"); + file.write_all(KOTLIN_LITERAL_TEST_CODE.as_bytes()) + .expect("Failed to write to temp file"); + let tree = get_ast(KOTLIN_LITERAL_TEST_CODE); + let root = tree.root_node(); + let mut mutations_made = Vec::new(); + MutationOperators::LiteralChangeOpeator.mutate( + root, + &mut root.walk(), + None, + &mut mutations_made, + &temp_file.to_str().unwrap().to_string(), + ); + println!("{:#?}", mutations_made); + assert_eq!(mutations_made.len(), 12); + // Assert that the old operator is not the same as the new operator + for mutation in mutations_made { + assert_ne!(mutation.old_op, mutation.new_op); + } + } #[test] fn test_arthimetic_operator_does_not_create_mutations() { let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); From bc344e4eba09eec164de7ff6dc306f9c6855abc7 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 17:02:37 -0500 Subject: [PATCH 11/23] fix failing tests --- kodekraken.config.json | 48 +++++++++++++------------ src/config.rs | 66 ++++++++++++++++++---------------- src/mutation_tool/builder.rs | 12 ++++--- src/mutation_tool/mod.rs | 25 ++++++++----- src/mutation_tool/operators.rs | 26 +++++++++----- 5 files changed, 105 insertions(+), 72 deletions(-) diff --git a/kodekraken.config.json b/kodekraken.config.json index 86c0bfd..2d32524 100644 --- a/kodekraken.config.json +++ b/kodekraken.config.json @@ -1,24 +1,28 @@ { - "ignore": { - "ignore_files": [ - "^.*Test\\.[^.]*$" - ], - "ignore_directories": [ - "dist", - "build", - "bin", - ".gradle", - ".idea", - "gradle" - ] - }, - "threading": { - "max_threads": 30 - }, - "output": { - "display_end_table": false - }, - "logging": { - "log_level": "INFO" - } + "general": { + "timeout": 10, + "operators": [] + }, + "ignore": { + "ignore_files": [ + "^.*Test\\.[^.]*$" + ], + "ignore_directories": [ + "dist", + "build", + "bin", + ".gradle", + ".idea", + "gradle" + ] + }, + "threading": { + "max_threads": 30 + }, + "output": { + "display_end_table": false + }, + "logging": { + "log_level": "INFO" + } } diff --git a/src/config.rs b/src/config.rs index 7068502..184f154 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,10 +1,10 @@ use std::{fs, io::BufReader, path::Path}; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use crate::mutation_tool::MutationOperators; -#[derive(Debug, Deserialize, Default, PartialEq, Eq, Clone)] +#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq, Clone)] pub struct KodeKrakenConfig { pub general: GeneralConfig, pub ignore: IgnoreConfig, @@ -13,7 +13,7 @@ pub struct KodeKrakenConfig { pub logging: LoggingConfig, } -#[derive(Debug, Deserialize, PartialEq, Eq, Clone)] +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct GeneralConfig { /// The time in seconds to wait for the mutation tool to finish /// before killing the process @@ -31,23 +31,23 @@ impl Default for GeneralConfig { } } -#[derive(Debug, Deserialize, PartialEq, Eq, Clone)] +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct LoggingConfig { pub log_level: String, } -#[derive(Debug, Deserialize, PartialEq, Eq, Clone)] +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct IgnoreConfig { pub ignore_files: Vec, pub ignore_directories: Vec, } -#[derive(Debug, Deserialize, PartialEq, Eq, Clone)] +#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone)] pub struct ThreadingConfig { pub max_threads: usize, } -#[derive(Debug, Deserialize, Default, PartialEq, Eq, Clone)] +#[derive(Debug, Serialize, Deserialize, Default, PartialEq, Eq, Clone)] pub struct OutputConfig { pub display_end_table: bool, } @@ -69,6 +69,7 @@ impl KodeKrakenConfig { "Could not parse config file, using default config. Error: {}", e ); + println!("{:?}", e); Self::default() } } @@ -168,29 +169,31 @@ mod tests { let mut file = File::create(file_path).expect("Failed to create temporary file"); // Create a valid JSON content - let json_content = r#" - { - "general": { - "timeout": 10, - "operators": ["UnaryRemovalOperator", "AssignmentReplacementOperator"] - }, - "ignore": { - "ignore_files": ["file1", "file2"], - "ignore_directories": ["dir1", "dir2"] - }, - "threading": { - "max_threads": 42 - }, - "output": { - "display_end_table": true - }, - "logging": { - "log_level": "debug" - } - } - "#; + let config = KodeKrakenConfig { + general: GeneralConfig { + timeout: Some(10), + operators: vec![ + MutationOperators::UnaryRemovalOperator, + MutationOperators::AssignmentReplacementOperator, + ], + }, + ignore: IgnoreConfig { + ignore_files: vec!["file1".into(), "file2".into()], + ignore_directories: vec!["dir1".into(), "dir2".into()], + }, + threading: ThreadingConfig { max_threads: 42 }, + output: OutputConfig { + display_end_table: true, + }, + logging: LoggingConfig { + log_level: "debug".into(), + }, + }; + + let config_json = serde_json::to_string_pretty(&config).unwrap(); + file.write_all(config_json.as_bytes()) + .expect("Failed to write to temporary file"); - writeln!(file, "{}", json_content).expect("Failed to write to temporary file"); let config = KodeKrakenConfig::load_config(temp_dir); assert_eq!(config.general.timeout, Some(10)); assert_eq!( @@ -210,7 +213,10 @@ mod tests { #[test] fn test_load_config_from_invalid_file() { // Create an invalid JSON content - let temp_dir = temp_dir(); + let temp_dir = temp_dir().join("invalid_config"); + // Create the temporary directory + fs::create_dir_all(&temp_dir).expect("Failed to create temporary directory"); + // Create the temporary file let file_path = temp_dir.join("kodekraken.config.json"); let mut file = File::create(file_path).expect("Failed to create temporary file"); diff --git a/src/mutation_tool/builder.rs b/src/mutation_tool/builder.rs index ffe3dec..21e22bd 100644 --- a/src/mutation_tool/builder.rs +++ b/src/mutation_tool/builder.rs @@ -69,6 +69,7 @@ impl MutationToolBuilder { #[cfg(test)] mod tests { use crate::config::GeneralConfig; + use std::env::temp_dir; use super::*; use std::sync::Arc; @@ -119,8 +120,13 @@ mod tests { #[test] fn test_set_mutate_config() { + // Create a temp directory + let temp_dir = temp_dir().join("set_mutate_config"); + // Create the temp directory + std::fs::create_dir_all(&temp_dir).unwrap(); + let mutate_config = MutationCommandConfig { - path: "./tests/kotlin-test-projects/demo".into(), + path: temp_dir.to_str().unwrap().to_string(), }; let builder = MutationToolBuilder::new().set_mutate_config(mutate_config.clone()); @@ -130,7 +136,7 @@ mod tests { assert_eq!(mutation_tool.mutate_config, mutate_config); assert_eq!( mutation_tool.mutate_config.path, - "./tests/kotlin-test-projects/demo".to_string() + temp_dir.to_str().unwrap().to_string() ); } @@ -175,6 +181,4 @@ mod tests { AllMutationOperators::new().get_mutation_operators() ); } - - // Add more tests based on your specific requirements and configurations } diff --git a/src/mutation_tool/mod.rs b/src/mutation_tool/mod.rs index 49961a8..2f0e80d 100644 --- a/src/mutation_tool/mod.rs +++ b/src/mutation_tool/mod.rs @@ -8,6 +8,15 @@ pub use mutation::*; pub use operators::*; pub use tool::*; +pub fn debug_print_ast(ast: &tree_sitter::Node, spaces: usize) { + // Print the node + println!("{}{:?}", " ".repeat(spaces), ast.kind()); + // Go through the children + for child in ast.children(&mut ast.walk()) { + debug_print_ast(&child, spaces + 2); + } +} + #[cfg(test)] pub mod test_util { pub const KOTLIN_TEST_CODE: &str = r#" @@ -109,14 +118,14 @@ fun main() { pub const KOTLIN_LITERAL_TEST_CODE: &str = r#" fun main() { - val a = 10 - val b = -10 - val c = 10.0 - val d = -10.0 - val e = 10L - val f = -10L - val g = 10.0f - val h = -10.0f + val a = 1 + val b = -2 + val c = 4.0 + val d = -6.0 + val e = 12L + val f = -13L + val g = 14.0f + val h = -16.0f val i = true val j = false val k = 'a' diff --git a/src/mutation_tool/operators.rs b/src/mutation_tool/operators.rs index 74e05e3..7f76261 100644 --- a/src/mutation_tool/operators.rs +++ b/src/mutation_tool/operators.rs @@ -164,6 +164,14 @@ impl MutationOperators { KotlinTypes::CharacterLiteral, KotlinTypes::PrefixExpression, KotlinTypes::PostfixExpression, + KotlinTypes::AdditiveExpression, + KotlinTypes::MultiplicativeExpression, + KotlinTypes::ConjunctionExpression, + KotlinTypes::DisjunctionExpression, + KotlinTypes::EqualityExpression, + KotlinTypes::ComparisonExpression, + KotlinTypes::PropertyDeclaration, + KotlinTypes::VariableDeclaration, ], } } @@ -176,7 +184,6 @@ impl MutationOperators { self.mutate(root, &mut cursor, None, &mut mutations, file_name); mutations } - /// Mutates the given `root` node and its children using the provided `cursor`, `parent`, `mutations_made`, `file_name`, `operators`, and `parent_necessary_types`. /// /// # Arguments @@ -239,6 +246,9 @@ impl MutationOperators { || parent.is_none() || !parent_types.contains(parent.as_ref().ok_or(KodeKrakenError::ConversionError)?) { + if root == &KotlinTypes::BooleanLiteral { + println!("Boolean Literal, root: {:?}, parent: {:?}", root, parent); + } return Ok(mutations_made); } @@ -272,13 +282,10 @@ impl MutationOperators { ); mutations_made.push(mutation); } - MutationOperators::ElvisLiteralChangeOperator => { + MutationOperators::ElvisLiteralChangeOperator + | MutationOperators::LiteralChangeOpeator => { self.mutate_literal(&root_node.parent().unwrap(), &mut mutations_made, file_name) } - MutationOperators::LiteralChangeOpeator => { - println!("Literal change operator on root: {:#?}", root_node); - self.mutate_literal(root_node, &mut mutations_made, file_name) - } _ => { // Create a mutant for all mutation operators mutation_operators.iter().for_each(|operator| { @@ -320,7 +327,7 @@ impl MutationOperators { }; let child_type = KotlinTypes::new(node.kind()).expect("Failed to convert to KotlinType"); - + println!("Child type: {:?}", child_type); // Change the literal to a different literal let mut val = node.utf8_text(file).unwrap(); match child_type { @@ -361,7 +368,7 @@ impl MutationOperators { } KotlinTypes::BooleanLiteral => { let val = val.parse::().unwrap(); - + println!("Boolean literal: {}", val); // Change the value and create a mutation let mutated_val = !val; @@ -510,6 +517,7 @@ mod tests { use crate::mutation_tool::test_util::*; use super::*; + use crate::mutation_tool::debug_print_ast; use tree_sitter::Parser; fn get_ast(text: &str) -> tree_sitter::Tree { @@ -698,6 +706,7 @@ mod tests { .expect("Failed to write to temp file"); let tree = get_ast(KOTLIN_LITERAL_TEST_CODE); let root = tree.root_node(); + debug_print_ast(&root, 0); let mut mutations_made = Vec::new(); MutationOperators::LiteralChangeOpeator.mutate( root, @@ -713,6 +722,7 @@ mod tests { assert_ne!(mutation.old_op, mutation.new_op); } } + #[test] fn test_arthimetic_operator_does_not_create_mutations() { let tree = get_ast(KOTLIN_UNARY_REMOVAL_TEST_CODE); From 1e08fd4d3114209fbcfd64d1f57ed2eef7ba81db Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 17:14:30 -0500 Subject: [PATCH 12/23] Remove all mutation operators struct in place of config --- src/config.rs | 14 +++++++- src/mutation_tool/builder.rs | 61 ++++++++++++++++------------------ src/mutation_tool/operators.rs | 42 ----------------------- src/mutation_tool/tool.rs | 13 +++----- 4 files changed, 46 insertions(+), 84 deletions(-) diff --git a/src/config.rs b/src/config.rs index 184f154..0bf883e 100644 --- a/src/config.rs +++ b/src/config.rs @@ -24,9 +24,21 @@ pub struct GeneralConfig { impl Default for GeneralConfig { /// Set Default timeout of 5 minutes fn default() -> Self { + use MutationOperators::*; Self { timeout: None, - operators: vec![], + operators: vec![ + ArithmeticReplacementOperator, + UnaryRemovalOperator, + LogicalReplacementOperator, + RelationalReplacementOperator, + AssignmentReplacementOperator, + UnaryReplacementOperator, + NotNullAssertionOperator, + ElvisRemoveOperator, + ElvisLiteralChangeOperator, + LiteralChangeOpeator, + ], } } } diff --git a/src/mutation_tool/builder.rs b/src/mutation_tool/builder.rs index 21e22bd..c93e675 100644 --- a/src/mutation_tool/builder.rs +++ b/src/mutation_tool/builder.rs @@ -1,11 +1,10 @@ use crate::{cli::MutationCommandConfig, config::KodeKrakenConfig}; -use super::{AllMutationOperators, MutationOperators, MutationTool, OUT_DIRECTORY}; +use super::{MutationOperators, MutationTool, OUT_DIRECTORY}; pub struct MutationToolBuilder { mutate_config: Option, kodekraken_config: Option, - mutation_operators: Option>, enable_mutation_comment: bool, } @@ -21,7 +20,6 @@ impl MutationToolBuilder { Self { mutate_config: None, kodekraken_config: None, - mutation_operators: None, enable_mutation_comment: false, } } @@ -38,12 +36,6 @@ impl MutationToolBuilder { self } - /// Set the mutation operators to be used - pub fn set_mutation_operators(mut self, mutation_operators: Vec) -> Self { - self.mutation_operators = Some(mutation_operators); - self - } - /// Sets whether to enable the mutation comment pub fn set_mutation_comment(mut self, enable_mutation_comment: bool) -> Self { self.enable_mutation_comment = enable_mutation_comment; @@ -53,14 +45,12 @@ impl MutationToolBuilder { pub fn build(self) -> MutationTool { let mutate_config = self.mutate_config.unwrap_or_default(); let kodekraken_config = self.kodekraken_config.unwrap_or_default(); - let mutation_operators = self - .mutation_operators - .unwrap_or(AllMutationOperators::new().get_mutation_operators()); + MutationTool::new( mutate_config, - kodekraken_config, + kodekraken_config.clone(), OUT_DIRECTORY.into(), - mutation_operators, + kodekraken_config.general.operators.clone(), self.enable_mutation_comment, ) .unwrap() @@ -73,6 +63,7 @@ mod tests { use super::*; use std::sync::Arc; + use MutationOperators::*; #[test] fn test_default_builder() { @@ -88,7 +79,18 @@ mod tests { ); assert_eq!( Arc::into_inner(mutation_tool.mutation_operators).unwrap(), - AllMutationOperators::new().get_mutation_operators() + vec![ + ArithmeticReplacementOperator, + UnaryRemovalOperator, + LogicalReplacementOperator, + RelationalReplacementOperator, + AssignmentReplacementOperator, + UnaryReplacementOperator, + NotNullAssertionOperator, + ElvisRemoveOperator, + ElvisLiteralChangeOperator, + LiteralChangeOpeator, + ] ); } @@ -140,22 +142,6 @@ mod tests { ); } - #[test] - fn test_set_mutation_operators() { - let mutation_operators = vec![ - MutationOperators::AssignmentReplacementOperator, - MutationOperators::ArithmeticReplacementOperator, - ]; - - let builder = MutationToolBuilder::new().set_mutation_operators(mutation_operators.clone()); - let mutation_tool = builder.build(); - - assert_eq!( - Arc::into_inner(mutation_tool.mutation_operators).unwrap(), - mutation_operators - ); - } - #[test] fn test_set_mutation_comment() { let builder = MutationToolBuilder::new().set_mutation_comment(true); @@ -178,7 +164,18 @@ mod tests { ); assert_eq!( Arc::into_inner(mutation_tool.mutation_operators).unwrap(), - AllMutationOperators::new().get_mutation_operators() + vec![ + ArithmeticReplacementOperator, + UnaryRemovalOperator, + LogicalReplacementOperator, + RelationalReplacementOperator, + AssignmentReplacementOperator, + UnaryReplacementOperator, + NotNullAssertionOperator, + ElvisRemoveOperator, + ElvisLiteralChangeOperator, + LiteralChangeOpeator, + ] ); } } diff --git a/src/mutation_tool/operators.rs b/src/mutation_tool/operators.rs index 7f76261..a41f910 100644 --- a/src/mutation_tool/operators.rs +++ b/src/mutation_tool/operators.rs @@ -7,12 +7,6 @@ use crate::{ }; use std::{collections::HashSet, fmt::Display, fs}; -// Struct that stores all the mutations operators by default -#[derive(Clone)] -pub struct AllMutationOperators { - mutation_operators: Vec, -} - // The different types of mutation operators that can be performed on a file #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, serde::Serialize, serde::Deserialize)] pub enum MutationOperators { @@ -474,42 +468,6 @@ where random_literal } -impl AllMutationOperators { - pub fn new() -> Self { - Self { - mutation_operators: vec![ - MutationOperators::ArithmeticReplacementOperator, - MutationOperators::UnaryRemovalOperator, - MutationOperators::LogicalReplacementOperator, - MutationOperators::RelationalReplacementOperator, - MutationOperators::AssignmentReplacementOperator, - MutationOperators::UnaryReplacementOperator, - MutationOperators::NotNullAssertionOperator, - MutationOperators::ElvisRemoveOperator, - MutationOperators::ElvisLiteralChangeOperator, - ], - } - } - - pub fn get_mutation_operators(&self) -> Vec { - self.mutation_operators.clone() - } -} - -impl Default for AllMutationOperators { - fn default() -> Self { - Self::new() - } -} - -impl Iterator for AllMutationOperators { - type Item = MutationOperators; - - fn next(&mut self) -> Option { - self.mutation_operators.pop() - } -} - #[cfg(test)] mod tests { use std::{env::temp_dir, io::Write}; diff --git a/src/mutation_tool/tool.rs b/src/mutation_tool/tool.rs index 86b792c..8c776d1 100644 --- a/src/mutation_tool/tool.rs +++ b/src/mutation_tool/tool.rs @@ -18,10 +18,12 @@ use crate::config::KodeKrakenConfig; use crate::error::{self, KodeKrakenError, Result}; use crate::mutation_tool::{ mutation::{FileMutations, Mutation, MutationResult}, - AllMutationOperators, MutationOperators, + MutationOperators, }; use crate::{gradle, html_gen}; +use super::MutationToolBuilder; + pub const OUT_DIRECTORY: &str = "./kode-kraken-dist"; const MAX_BUILD_THREADS: f32 = 5f32; @@ -38,14 +40,7 @@ pub struct MutationTool { impl Default for MutationTool { fn default() -> Self { - Self::new( - MutationCommandConfig::default(), - KodeKrakenConfig::default(), - OUT_DIRECTORY.into(), - AllMutationOperators::new().get_mutation_operators(), - false, - ) - .unwrap() + MutationToolBuilder::new().build() } } From e2e3fca8fa9f94d7277753aac455bf178181fec4 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 17:20:06 -0500 Subject: [PATCH 13/23] fix failing tests --- src/config.rs | 64 +++++++++++++++++++++++++++++++++--- src/mutation_tool/builder.rs | 4 +-- 2 files changed, 62 insertions(+), 6 deletions(-) diff --git a/src/config.rs b/src/config.rs index 0bf883e..0edfdb9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -134,7 +134,21 @@ mod tests { fn test_default_general_config() { let default_general = GeneralConfig::default(); assert_eq!(default_general.timeout, None); - assert_eq!(default_general.operators, vec![]); + assert_eq!( + default_general.operators, + vec![ + MutationOperators::ArithmeticReplacementOperator, + MutationOperators::UnaryRemovalOperator, + MutationOperators::LogicalReplacementOperator, + MutationOperators::RelationalReplacementOperator, + MutationOperators::AssignmentReplacementOperator, + MutationOperators::UnaryReplacementOperator, + MutationOperators::NotNullAssertionOperator, + MutationOperators::ElvisRemoveOperator, + MutationOperators::ElvisLiteralChangeOperator, + MutationOperators::LiteralChangeOpeator, + ] + ); } #[test] @@ -166,7 +180,21 @@ mod tests { fn test_new_kodekraken_config() { let config = KodeKrakenConfig::new(); assert_eq!(config.general.timeout, None); - assert_eq!(config.general.operators, vec![]); + assert_eq!( + config.general.operators, + vec![ + MutationOperators::ArithmeticReplacementOperator, + MutationOperators::UnaryRemovalOperator, + MutationOperators::LogicalReplacementOperator, + MutationOperators::RelationalReplacementOperator, + MutationOperators::AssignmentReplacementOperator, + MutationOperators::UnaryReplacementOperator, + MutationOperators::NotNullAssertionOperator, + MutationOperators::ElvisRemoveOperator, + MutationOperators::ElvisLiteralChangeOperator, + MutationOperators::LiteralChangeOpeator, + ] + ); assert_eq!(config.ignore.ignore_files.len(), 1); assert_eq!(config.ignore.ignore_directories.len(), 6); assert_eq!(config.threading.max_threads, 30); @@ -244,7 +272,21 @@ mod tests { let config = KodeKrakenConfig::load_config(temp_dir); // Since the JSON is invalid, it should fall back to default values assert_eq!(config.general.timeout, None); - assert_eq!(config.general.operators, vec![]); + assert_eq!( + config.general.operators, + vec![ + MutationOperators::ArithmeticReplacementOperator, + MutationOperators::UnaryRemovalOperator, + MutationOperators::LogicalReplacementOperator, + MutationOperators::RelationalReplacementOperator, + MutationOperators::AssignmentReplacementOperator, + MutationOperators::UnaryReplacementOperator, + MutationOperators::NotNullAssertionOperator, + MutationOperators::ElvisRemoveOperator, + MutationOperators::ElvisLiteralChangeOperator, + MutationOperators::LiteralChangeOpeator, + ] + ); } #[test] @@ -253,6 +295,20 @@ mod tests { let config = KodeKrakenConfig::load_config("/tmp"); // Since the file is missing, it should fall back to default values assert_eq!(config.general.timeout, None); - assert_eq!(config.general.operators, vec![]); + assert_eq!( + config.general.operators, + vec![ + MutationOperators::ArithmeticReplacementOperator, + MutationOperators::UnaryRemovalOperator, + MutationOperators::LogicalReplacementOperator, + MutationOperators::RelationalReplacementOperator, + MutationOperators::AssignmentReplacementOperator, + MutationOperators::UnaryReplacementOperator, + MutationOperators::NotNullAssertionOperator, + MutationOperators::ElvisRemoveOperator, + MutationOperators::ElvisLiteralChangeOperator, + MutationOperators::LiteralChangeOpeator, + ] + ); } } diff --git a/src/mutation_tool/builder.rs b/src/mutation_tool/builder.rs index c93e675..1b415af 100644 --- a/src/mutation_tool/builder.rs +++ b/src/mutation_tool/builder.rs @@ -1,6 +1,6 @@ use crate::{cli::MutationCommandConfig, config::KodeKrakenConfig}; -use super::{MutationOperators, MutationTool, OUT_DIRECTORY}; +use super::{MutationTool, OUT_DIRECTORY}; pub struct MutationToolBuilder { mutate_config: Option, @@ -58,7 +58,7 @@ impl MutationToolBuilder { } #[cfg(test)] mod tests { - use crate::config::GeneralConfig; + use crate::{config::GeneralConfig, mutation_tool::MutationOperators}; use std::env::temp_dir; use super::*; From 1c4bc7ef66a7bf92b45d24be1eea90961776f4ed Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 17:27:08 -0500 Subject: [PATCH 14/23] fix mutation operator config --- src/config.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/config.rs b/src/config.rs index 0edfdb9..992679c 100644 --- a/src/config.rs +++ b/src/config.rs @@ -18,6 +18,8 @@ pub struct GeneralConfig { /// The time in seconds to wait for the mutation tool to finish /// before killing the process pub timeout: Option, + + #[serde(default)] pub operators: Vec, } @@ -291,8 +293,12 @@ mod tests { #[test] fn test_load_config_from_missing_file() { + // Create a temp directory + let temp_dir = temp_dir().join("missing_config"); + // Create the temp directory + fs::create_dir_all(&temp_dir).expect("Failed to create temporary directory"); // Test loading config from a missing file - let config = KodeKrakenConfig::load_config("/tmp"); + let config = KodeKrakenConfig::load_config(temp_dir); // Since the file is missing, it should fall back to default values assert_eq!(config.general.timeout, None); assert_eq!( From 9d216e9995fef6e04f10f47638fe398d09bdd3cd Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 17:32:49 -0500 Subject: [PATCH 15/23] fix having no mutation operators --- src/cli.rs | 1 + src/config.rs | 9 ++++++++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/cli.rs b/src/cli.rs index 9918c7d..b66a38c 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -104,6 +104,7 @@ pub fn run_cli() { .set_general_config(config) .set_mutation_comment(true) .build(); + println!("{:#?}", tool.kodekraken_config.general.operators); let res = match tool.kodekraken_config.general.timeout { Some(timeout) => { println!("Timeout set to {} seconds", timeout); diff --git a/src/config.rs b/src/config.rs index 992679c..9e4c519 100644 --- a/src/config.rs +++ b/src/config.rs @@ -72,7 +72,7 @@ impl KodeKrakenConfig { } pub fn load_config>(path: P) -> Self { - match fs::File::open(path.as_ref().join("kodekraken.config.json")) { + let mut config = match fs::File::open(path.as_ref().join("kodekraken.config.json")) { Ok(file) => { let buffer_reader = BufReader::new(file); match serde_json::from_reader(buffer_reader) { @@ -92,7 +92,14 @@ impl KodeKrakenConfig { println!("[WARNING] ⚠️ Could not find kodekraken.config.json file in root directory, using default config."); Self::default() } + }; + + if config.general.operators.is_empty() { + println!("[WARNING] ⚠️ No mutation operators specified in config file, using default operators."); + config.general.operators = GeneralConfig::default().operators; } + + config } } From 7e3e928429f0d946cb6d4adace380b27fcfddd5b Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 17:39:42 -0500 Subject: [PATCH 16/23] write config file explicity --- tests/kode_kraken/main.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/tests/kode_kraken/main.rs b/tests/kode_kraken/main.rs index ad55e44..c34868c 100644 --- a/tests/kode_kraken/main.rs +++ b/tests/kode_kraken/main.rs @@ -1,8 +1,18 @@ use assert_cmd::prelude::*; +use kode_kraken::config::KodeKrakenConfig; use std::{fs, path::Path, process::Command}; #[test] fn test_tool_runs_correctly() { + // Set the config + let config = KodeKrakenConfig::default(); + // Create or replace the config file + fs::write( + "tests/kotlin-test-projects/demo/kodekraken.config.json", + serde_json::to_string(&config).unwrap(), + ) + .unwrap(); + let mut cmd = Command::cargo_bin("kode-kraken").unwrap(); // Create command From c409fea2117280f161c548984d52afeca2fa8ebe Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 17:49:23 -0500 Subject: [PATCH 17/23] pls --- src/mutation_tool/builder.rs | 43 +++++++++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 5 deletions(-) diff --git a/src/mutation_tool/builder.rs b/src/mutation_tool/builder.rs index 1b415af..7bc9161 100644 --- a/src/mutation_tool/builder.rs +++ b/src/mutation_tool/builder.rs @@ -67,7 +67,14 @@ mod tests { #[test] fn test_default_builder() { - let builder = MutationToolBuilder::new(); + // Create temp directory + let temp_dir = temp_dir().join("default_builder"); + // Create the temp directory + std::fs::create_dir_all(&temp_dir).unwrap(); + + let builder = MutationToolBuilder::new().set_mutate_config(MutationCommandConfig { + path: temp_dir.to_str().unwrap().to_string(), + }); let mutation_tool = builder.build(); // Add assertions based on your specific default values @@ -96,6 +103,11 @@ mod tests { #[test] fn test_set_general_config() { + // Create temp directory + let temp_dir = temp_dir().join("set_general_config"); + // Create the temp directory + std::fs::create_dir_all(&temp_dir).unwrap(); + let general_config = KodeKrakenConfig { general: GeneralConfig { timeout: Some(10), @@ -107,7 +119,11 @@ mod tests { ..Default::default() }; - let builder = MutationToolBuilder::new().set_general_config(general_config.clone()); + let builder = MutationToolBuilder::new() + .set_mutate_config(MutationCommandConfig { + path: temp_dir.to_str().unwrap().to_string(), + }) + .set_general_config(general_config.clone()); let mutation_tool = builder.build(); assert_eq!(mutation_tool.kodekraken_config.general.timeout, Some(10)); @@ -144,7 +160,15 @@ mod tests { #[test] fn test_set_mutation_comment() { - let builder = MutationToolBuilder::new().set_mutation_comment(true); + // Create a temp directory + let temp_dir = temp_dir().join("set_mutation_comment"); + // Create the temp directory + std::fs::create_dir_all(&temp_dir).unwrap(); + let builder = MutationToolBuilder::new() + .set_mutate_config(MutationCommandConfig { + path: temp_dir.to_str().unwrap().to_string(), + }) + .set_mutation_comment(true); let mutation_tool = builder.build(); assert_eq!(mutation_tool.enable_mutation_comment, true); @@ -152,7 +176,14 @@ mod tests { #[test] fn test_build_with_defaults() { - let builder = MutationToolBuilder::new(); + // Create a temp directory + let temp_dir = temp_dir().join("build_with_defaults"); + // Create the temp directory + std::fs::create_dir_all(&temp_dir).unwrap(); + + let builder = MutationToolBuilder::new().set_mutate_config(MutationCommandConfig { + path: temp_dir.to_str().unwrap().to_string(), + }); let mutation_tool = builder.build(); // Add assertions based on your specific default values @@ -160,7 +191,9 @@ mod tests { assert_eq!(mutation_tool.kodekraken_config, KodeKrakenConfig::default()); assert_eq!( mutation_tool.mutate_config, - MutationCommandConfig::default() + MutationCommandConfig { + path: temp_dir.to_str().unwrap().to_string(), + } ); assert_eq!( Arc::into_inner(mutation_tool.mutation_operators).unwrap(), From e19d09120f7ebede0788daf336c742512674381e Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 17:53:20 -0500 Subject: [PATCH 18/23] fix failing tests --- src/mutation_tool/builder.rs | 4 +++- tests/kotlin-test-projects/demo/kodekraken.config.json | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/mutation_tool/builder.rs b/src/mutation_tool/builder.rs index 7bc9161..789e754 100644 --- a/src/mutation_tool/builder.rs +++ b/src/mutation_tool/builder.rs @@ -82,7 +82,9 @@ mod tests { assert_eq!(mutation_tool.kodekraken_config, KodeKrakenConfig::new()); assert_eq!( mutation_tool.mutate_config, - MutationCommandConfig::default() + MutationCommandConfig { + path: temp_dir.to_str().unwrap().to_string(), + } ); assert_eq!( Arc::into_inner(mutation_tool.mutation_operators).unwrap(), diff --git a/tests/kotlin-test-projects/demo/kodekraken.config.json b/tests/kotlin-test-projects/demo/kodekraken.config.json index 111aa79..ca501c2 100644 --- a/tests/kotlin-test-projects/demo/kodekraken.config.json +++ b/tests/kotlin-test-projects/demo/kodekraken.config.json @@ -22,6 +22,9 @@ "log_level": "DEBUG" }, "general": { - "timeout": 60 + "timeout": 60, + "operators": [ + "LiteralChangeOpeator" + ] } -} +} \ No newline at end of file From 3e035ea6e0652e078aa72325b3015c406f3a1902 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 18:00:30 -0500 Subject: [PATCH 19/23] fix --- src/mutation_tool/builder.rs | 11 +++++-- .../demo/kodekraken.config.json | 31 +------------------ 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/src/mutation_tool/builder.rs b/src/mutation_tool/builder.rs index 789e754..7cf6fb7 100644 --- a/src/mutation_tool/builder.rs +++ b/src/mutation_tool/builder.rs @@ -1,3 +1,5 @@ +use std::path::{Path, PathBuf}; + use crate::{cli::MutationCommandConfig, config::KodeKrakenConfig}; use super::{MutationTool, OUT_DIRECTORY}; @@ -46,10 +48,15 @@ impl MutationToolBuilder { let mutate_config = self.mutate_config.unwrap_or_default(); let kodekraken_config = self.kodekraken_config.unwrap_or_default(); + let output_directory = PathBuf::from(&mutate_config.path) + .join(OUT_DIRECTORY) + .to_str() + .unwrap() + .to_string(); MutationTool::new( - mutate_config, + mutate_config.clone(), kodekraken_config.clone(), - OUT_DIRECTORY.into(), + output_directory, kodekraken_config.general.operators.clone(), self.enable_mutation_comment, ) diff --git a/tests/kotlin-test-projects/demo/kodekraken.config.json b/tests/kotlin-test-projects/demo/kodekraken.config.json index ca501c2..ac7c0d9 100644 --- a/tests/kotlin-test-projects/demo/kodekraken.config.json +++ b/tests/kotlin-test-projects/demo/kodekraken.config.json @@ -1,30 +1 @@ -{ - "ignore": { - "ignore_files": [ - "^.*Test\\.[^.]*$" - ], - "ignore_directories": [ - "dist", - "build", - "bin", - ".gradle", - ".idea", - "gradle" - ] - }, - "threading": { - "max_threads": 30 - }, - "output": { - "display_end_table": false - }, - "logging": { - "log_level": "DEBUG" - }, - "general": { - "timeout": 60, - "operators": [ - "LiteralChangeOpeator" - ] - } -} \ No newline at end of file +{"general":{"timeout":null,"operators":["ArithmeticReplacementOperator","UnaryRemovalOperator","LogicalReplacementOperator","RelationalReplacementOperator","AssignmentReplacementOperator","UnaryReplacementOperator","NotNullAssertionOperator","ElvisRemoveOperator","ElvisLiteralChangeOperator","LiteralChangeOpeator"]},"ignore":{"ignore_files":["^.*Test\\.[^.]*$"],"ignore_directories":["dist","build","bin",".gradle",".idea","gradle"]},"threading":{"max_threads":30},"output":{"display_end_table":false},"logging":{"log_level":"info"}} \ No newline at end of file From d9374cba1642ea239d5deb9387740a0eed8e5905 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 18:08:27 -0500 Subject: [PATCH 20/23] working on tests --- tests/kode_kraken/main.rs | 3 +- .../demo/kodekraken.config.json | 32 ++++++++++++++++++- .../demo/src/main/kotlin/Calculator.kt | 14 ++++---- 3 files changed, 40 insertions(+), 9 deletions(-) diff --git a/tests/kode_kraken/main.rs b/tests/kode_kraken/main.rs index c34868c..379dd46 100644 --- a/tests/kode_kraken/main.rs +++ b/tests/kode_kraken/main.rs @@ -21,7 +21,8 @@ fn test_tool_runs_correctly() { // Assert that the command runs successfully cmd.assert().success(); - let dir_path = Path::new("kode-kraken-dist"); + let dir_path = + Path::new("tests/kotlin-test-projects/demo/kodekraken.config.json/kode-kraken-dist"); // Assert that kode-kraken-dist was created assert!(dir_path.exists()); // Assert that the backups directory was created and that the backup file exists diff --git a/tests/kotlin-test-projects/demo/kodekraken.config.json b/tests/kotlin-test-projects/demo/kodekraken.config.json index ac7c0d9..a972899 100644 --- a/tests/kotlin-test-projects/demo/kodekraken.config.json +++ b/tests/kotlin-test-projects/demo/kodekraken.config.json @@ -1 +1,31 @@ -{"general":{"timeout":null,"operators":["ArithmeticReplacementOperator","UnaryRemovalOperator","LogicalReplacementOperator","RelationalReplacementOperator","AssignmentReplacementOperator","UnaryReplacementOperator","NotNullAssertionOperator","ElvisRemoveOperator","ElvisLiteralChangeOperator","LiteralChangeOpeator"]},"ignore":{"ignore_files":["^.*Test\\.[^.]*$"],"ignore_directories":["dist","build","bin",".gradle",".idea","gradle"]},"threading":{"max_threads":30},"output":{"display_end_table":false},"logging":{"log_level":"info"}} \ No newline at end of file +{ + "ignore": { + "ignore_files": [ + "^.*Test\\.[^.]*$" + ], + "ignore_directories": [ + "dist", + "build", + "bin", + ".gradle", + ".idea", + "gradle" + ] + }, + "threading": { + "max_threads": 30 + }, + "output": { + "display_end_table": false + }, + "logging": { + "log_level": "DEBUG" + }, + "general": { + "timeout": 60, + "operators": [ + "LiteralChangeOpeator", + "ElvisLiteralChangeOperator" + ] + } +} \ No newline at end of file diff --git a/tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt b/tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt index ed9afec..be57d92 100644 --- a/tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt +++ b/tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt @@ -21,17 +21,17 @@ class Calculator { var b: String? = null var x = b?.length ?: -11 var y = b?.length ?: "Hello There" - // x = b?.length ?: c + var ab = b?.length ?: c var z = b?.length ?: 1.0 var a = b?.length ?: 1.0f var c = b?.length ?: 1L - // x = b?.length ?: 'a' - // x = b?.length ?: 1.toShort() - // x = b?.length ?: 1.toChar() + var abc = b?.length ?: 'a' + var abcd = b?.length ?: 1.toShort() + var xy = b?.length ?: 1.toChar() - // if (x > 1) { - // println("Len is greater than 1!") - // } + if (x > 1) { + println("Len is greater than 1!") + } println(x) } From ab2e569d1c750c772c2bb166d4976826472ecf39 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 18:56:09 -0500 Subject: [PATCH 21/23] fix failing tests --- report.html | 10 ++++++ src/cli.rs | 16 ++++++---- src/html_gen.rs | 8 ++--- src/mutation_tool/builder.rs | 10 +++--- src/mutation_tool/tool.rs | 15 +++++---- tests/kode_kraken/main.rs | 13 +++++--- .../demo/kodekraken.config.json | 32 +------------------ .../demo/src/main/kotlin/Calculator.kt | 6 +--- 8 files changed, 47 insertions(+), 63 deletions(-) create mode 100644 report.html diff --git a/report.html b/report.html new file mode 100644 index 0000000..43ba2bb --- /dev/null +++ b/report.html @@ -0,0 +1,10 @@ +Kode Kraken Results
Kode Kraken Results
File Name# of Mutations# Survived# KilledScore
file2200NaN%
file1200NaN%
\ No newline at end of file diff --git a/src/cli.rs b/src/cli.rs index b66a38c..1046100 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -6,7 +6,7 @@ use tracing_appender::non_blocking::WorkerGuard; use crate::{ config::KodeKrakenConfig, error::{self, KodeKrakenError}, - mutation_tool::{MutationToolBuilder, OUT_DIRECTORY}, + mutation_tool::MutationToolBuilder, }; #[derive(Subcommand, Debug, Clone)] @@ -21,7 +21,7 @@ pub enum Commands { /// Clean the kode-kraken-dist directory /// This will delete all files in the directory /// This is useful if you want to remove all the files - Clean, + Clean(MutationCommandConfig), } const ABOUT: &str = include_str!("../assets/about.txt"); @@ -97,7 +97,7 @@ pub fn run_cli() { match args.command { Commands::Mutate(mutate_config) => { let config = KodeKrakenConfig::load_config(mutate_config.path.clone()); - _guard = setup_logging(&config.logging.log_level); + _guard = setup_logging(&config.logging.log_level, mutate_config.path.clone()); let mut tool = mutate_tool_builder .set_mutate_config(mutate_config) @@ -152,9 +152,9 @@ pub fn run_cli() { println!("3. Edit the config file to your liking"); } } - Commands::Clean => { + Commands::Clean(config) => { // Check to see if the output directory exists - let output_dir = Path::new(OUT_DIRECTORY); + let output_dir = Path::new(config.path.as_str()).join("kode-kraken-dist"); if output_dir.exists() { // Delete the output directory std::fs::remove_dir_all(output_dir).expect("Could not delete output directory"); @@ -163,7 +163,7 @@ pub fn run_cli() { } } -fn setup_logging(log_level: &str) -> WorkerGuard { +fn setup_logging(log_level: &str, dir: String) -> WorkerGuard { let log_level = match log_level.to_lowercase().as_str() { "trace" => tracing::Level::TRACE, "debug" => tracing::Level::DEBUG, @@ -173,7 +173,9 @@ fn setup_logging(log_level: &str) -> WorkerGuard { _ => tracing::Level::INFO, }; // Create dist log folder if it doesn't exist - let log_dir = Path::new(OUT_DIRECTORY).join("logs"); + let log_dir = Path::new(dir.as_str()) + .join("kode-kraken-dist") + .join("logs"); std::fs::create_dir_all(&log_dir).expect("Could not create log directory"); let file_appender = tracing_appender::rolling::never(log_dir, "kode-kraken.log"); let (non_blocking, guard) = tracing_appender::non_blocking(file_appender); diff --git a/src/html_gen.rs b/src/html_gen.rs index 8857648..9ce3651 100644 --- a/src/html_gen.rs +++ b/src/html_gen.rs @@ -4,7 +4,7 @@ use horrorshow::{helper::doctype, html}; use crate::mutation_tool::{Mutation, MutationResult}; -pub fn build_html_page(data: &Vec) { +pub fn build_html_page(data: &Vec, path: &Path) { // Group the mutations by file name let mut file_mutations = HashMap::new(); for mutation in data { @@ -95,8 +95,8 @@ pub fn build_html_page(data: &Vec) { } ); // write file to kode-kraken-dist/report.html - let file_path = Path::new("kode-kraken-dist").join("report.html"); - let mut file = File::create(file_path).unwrap(); + let file_path = path.join("report.html"); + let mut file = File::create(file_path).expect("Could not create report.html file"); file.write_all(report.as_bytes()).unwrap(); } @@ -170,7 +170,7 @@ mod test { } // Call the function - build_html_page(&mutations); + build_html_page(&mutations, Path::new(".")); // Read the generated HTML file let file_path = Path::new("kode-kraken-dist").join("report.html"); diff --git a/src/mutation_tool/builder.rs b/src/mutation_tool/builder.rs index 7cf6fb7..3c7416c 100644 --- a/src/mutation_tool/builder.rs +++ b/src/mutation_tool/builder.rs @@ -1,8 +1,8 @@ -use std::path::{Path, PathBuf}; +use std::path::Path; use crate::{cli::MutationCommandConfig, config::KodeKrakenConfig}; -use super::{MutationTool, OUT_DIRECTORY}; +use super::MutationTool; pub struct MutationToolBuilder { mutate_config: Option, @@ -48,10 +48,10 @@ impl MutationToolBuilder { let mutate_config = self.mutate_config.unwrap_or_default(); let kodekraken_config = self.kodekraken_config.unwrap_or_default(); - let output_directory = PathBuf::from(&mutate_config.path) - .join(OUT_DIRECTORY) + let output_directory = Path::new(&mutate_config.path) + .join("kode-kraken-dist") .to_str() - .unwrap() + .unwrap_or_default() .to_string(); MutationTool::new( mutate_config.clone(), diff --git a/src/mutation_tool/tool.rs b/src/mutation_tool/tool.rs index 8c776d1..e7b2b74 100644 --- a/src/mutation_tool/tool.rs +++ b/src/mutation_tool/tool.rs @@ -24,13 +24,13 @@ use crate::{gradle, html_gen}; use super::MutationToolBuilder; -pub const OUT_DIRECTORY: &str = "./kode-kraken-dist"; const MAX_BUILD_THREADS: f32 = 5f32; pub struct MutationTool { parser: Arc>, pub mutate_config: MutationCommandConfig, pub mutation_operators: Arc>, + pub output_directory: String, pub mutation_dir: PathBuf, pub backup_dir: PathBuf, pub enable_mutation_comment: bool, @@ -110,6 +110,7 @@ impl MutationTool { mutate_config, parser: Arc::new(Mutex::new(parser)), mutation_operators: Arc::new(mutation_operators), + output_directory, mutation_dir, backup_dir, enable_mutation_comment, @@ -170,14 +171,14 @@ impl MutationTool { self.save_results(&mutations)?; // Phase 7: Generate HTML Report println!("[7/7] 📊 Generating HTML report..."); - html_gen::build_html_page(&mutations); + html_gen::build_html_page(&mutations, &Path::new(self.output_directory.as_str())); Ok(()) } /// Store All Mutations into a json filed name mutations.json within kode-kraken-dist/mutations directory fn store_mutations(&self, file_mutations: &HashMap) -> Result<()> { std::fs::write( - Path::new(OUT_DIRECTORY) + Path::new(self.output_directory.as_str()) .join("mutations") .join("mutations.json"), serde_json::to_string_pretty(file_mutations) @@ -201,7 +202,7 @@ impl MutationTool { /// fn save_results(&self, mutations: &Vec) -> Result<()> { let mut writer = csv::WriterBuilder::new() - .from_path(Path::new(OUT_DIRECTORY).join("output.csv")) + .from_path(Path::new(self.output_directory.as_str()).join("output.csv")) .map_err(|e| { KodeKrakenError::Error(format!("Error while creating csv writer: {}", e)) })?; @@ -343,7 +344,8 @@ impl MutationTool { for chunck in chunks.iter_mut() { // Create unique temp directory let uuid = uuid::Uuid::new_v4(); - let mut td = Path::new(OUT_DIRECTORY).join(format!("temp/{}", uuid)); + let mut td = + Path::new(self.output_directory.as_str()).join(format!("temp/{}", uuid)); fs::create_dir_all(&td).expect("Failed to create temp directory"); // Create directory structure inside temp directory that matches the original project let dir = PathBuf::from(&self.mutate_config.path); @@ -407,7 +409,8 @@ impl MutationTool { }); progress_bar.finish(); // Delete temp directory - if let Err(err) = fs::remove_dir_all(Path::new(OUT_DIRECTORY).join("temp")) { + if let Err(err) = fs::remove_dir_all(Path::new(self.output_directory.as_str()).join("temp")) + { println!("[ERROR] Failed to delete kode-kraken-dist/temp directory. Please view logs for more information."); tracing::error!("Failed to delete kode-kraken-dist/temp directory: {}", err); } diff --git a/tests/kode_kraken/main.rs b/tests/kode_kraken/main.rs index 379dd46..9359206 100644 --- a/tests/kode_kraken/main.rs +++ b/tests/kode_kraken/main.rs @@ -1,11 +1,15 @@ use assert_cmd::prelude::*; -use kode_kraken::config::KodeKrakenConfig; +use kode_kraken::{config::KodeKrakenConfig, mutation_tool::MutationOperators}; use std::{fs, path::Path, process::Command}; #[test] fn test_tool_runs_correctly() { // Set the config - let config = KodeKrakenConfig::default(); + let mut config = KodeKrakenConfig::default(); + config.general.operators = vec![ + MutationOperators::ArithmeticReplacementOperator, + MutationOperators::AssignmentReplacementOperator, + ]; // Create or replace the config file fs::write( "tests/kotlin-test-projects/demo/kodekraken.config.json", @@ -13,7 +17,7 @@ fn test_tool_runs_correctly() { ) .unwrap(); - let mut cmd = Command::cargo_bin("kode-kraken").unwrap(); + let mut cmd = Command::cargo_bin("kode-kraken").expect("Could not find kode-kraken binary"); // Create command cmd.arg("mutate").arg("tests/kotlin-test-projects/demo"); @@ -21,8 +25,7 @@ fn test_tool_runs_correctly() { // Assert that the command runs successfully cmd.assert().success(); - let dir_path = - Path::new("tests/kotlin-test-projects/demo/kodekraken.config.json/kode-kraken-dist"); + let dir_path = Path::new("tests/kotlin-test-projects/demo/kode-kraken-dist"); // Assert that kode-kraken-dist was created assert!(dir_path.exists()); // Assert that the backups directory was created and that the backup file exists diff --git a/tests/kotlin-test-projects/demo/kodekraken.config.json b/tests/kotlin-test-projects/demo/kodekraken.config.json index a972899..6cafeef 100644 --- a/tests/kotlin-test-projects/demo/kodekraken.config.json +++ b/tests/kotlin-test-projects/demo/kodekraken.config.json @@ -1,31 +1 @@ -{ - "ignore": { - "ignore_files": [ - "^.*Test\\.[^.]*$" - ], - "ignore_directories": [ - "dist", - "build", - "bin", - ".gradle", - ".idea", - "gradle" - ] - }, - "threading": { - "max_threads": 30 - }, - "output": { - "display_end_table": false - }, - "logging": { - "log_level": "DEBUG" - }, - "general": { - "timeout": 60, - "operators": [ - "LiteralChangeOpeator", - "ElvisLiteralChangeOperator" - ] - } -} \ No newline at end of file +{"general":{"timeout":null,"operators":["ArithmeticReplacementOperator","AssignmentReplacementOperator"]},"ignore":{"ignore_files":["^.*Test\\.[^.]*$"],"ignore_directories":["dist","build","bin",".gradle",".idea","gradle"]},"threading":{"max_threads":30},"output":{"display_end_table":false},"logging":{"log_level":"info"}} \ No newline at end of file diff --git a/tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt b/tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt index be57d92..cec6f6e 100644 --- a/tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt +++ b/tests/kotlin-test-projects/demo/src/main/kotlin/Calculator.kt @@ -21,14 +21,10 @@ class Calculator { var b: String? = null var x = b?.length ?: -11 var y = b?.length ?: "Hello There" - var ab = b?.length ?: c var z = b?.length ?: 1.0 var a = b?.length ?: 1.0f var c = b?.length ?: 1L - var abc = b?.length ?: 'a' - var abcd = b?.length ?: 1.toShort() - var xy = b?.length ?: 1.toChar() - + if (x > 1) { println("Len is greater than 1!") } From e588a8ef55c94defa596daa680304cb25a901bee Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 19:16:46 -0500 Subject: [PATCH 22/23] finally fix tests --- src/html_gen.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/html_gen.rs b/src/html_gen.rs index 9ce3651..f5b9c7e 100644 --- a/src/html_gen.rs +++ b/src/html_gen.rs @@ -170,7 +170,7 @@ mod test { } // Call the function - build_html_page(&mutations, Path::new(".")); + build_html_page(&mutations, Path::new("./kode-kraken-dist")); // Read the generated HTML file let file_path = Path::new("kode-kraken-dist").join("report.html"); From 387226c6a739d96b477bdd4cf11827f0f330cdd7 Mon Sep 17 00:00:00 2001 From: Josue Morales Date: Tue, 2 Jan 2024 19:24:29 -0500 Subject: [PATCH 23/23] finish implementing literal change mutant --- src/cli.rs | 2 -- src/config.rs | 1 - src/mutation_tool/operators.rs | 9 --------- 3 files changed, 12 deletions(-) diff --git a/src/cli.rs b/src/cli.rs index 1046100..9807cd9 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -104,10 +104,8 @@ pub fn run_cli() { .set_general_config(config) .set_mutation_comment(true) .build(); - println!("{:#?}", tool.kodekraken_config.general.operators); let res = match tool.kodekraken_config.general.timeout { Some(timeout) => { - println!("Timeout set to {} seconds", timeout); run_with_timeout(move || tool.mutate(), Duration::from_secs(timeout)) } None => tool.mutate(), diff --git a/src/config.rs b/src/config.rs index 9e4c519..06658b9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -83,7 +83,6 @@ impl KodeKrakenConfig { "Could not parse config file, using default config. Error: {}", e ); - println!("{:?}", e); Self::default() } } diff --git a/src/mutation_tool/operators.rs b/src/mutation_tool/operators.rs index a41f910..9b4778b 100644 --- a/src/mutation_tool/operators.rs +++ b/src/mutation_tool/operators.rs @@ -240,9 +240,6 @@ impl MutationOperators { || parent.is_none() || !parent_types.contains(parent.as_ref().ok_or(KodeKrakenError::ConversionError)?) { - if root == &KotlinTypes::BooleanLiteral { - println!("Boolean Literal, root: {:?}, parent: {:?}", root, parent); - } return Ok(mutations_made); } @@ -313,15 +310,12 @@ impl MutationOperators { let children = root_node .children(&mut root_node.walk()) .collect::>(); - println!("Children: {:#?}", children); - println!("Num of children: {}", children.len()); let node = match children.iter().last() { Some(node) => node, None => root_node, }; let child_type = KotlinTypes::new(node.kind()).expect("Failed to convert to KotlinType"); - println!("Child type: {:?}", child_type); // Change the literal to a different literal let mut val = node.utf8_text(file).unwrap(); match child_type { @@ -362,7 +356,6 @@ impl MutationOperators { } KotlinTypes::BooleanLiteral => { let val = val.parse::().unwrap(); - println!("Boolean literal: {}", val); // Change the value and create a mutation let mutated_val = !val; @@ -646,7 +639,6 @@ mod tests { &mut mutations_made, &temp_file.to_str().unwrap().to_string(), ); - println!("{:#?}", mutations_made); assert_eq!(mutations_made.len(), 12); // Assert that the old operator is not the same as the new operator for mutation in mutations_made { @@ -673,7 +665,6 @@ mod tests { &mut mutations_made, &temp_file.to_str().unwrap().to_string(), ); - println!("{:#?}", mutations_made); assert_eq!(mutations_made.len(), 12); // Assert that the old operator is not the same as the new operator for mutation in mutations_made {