diff --git a/README.md b/README.md index 81579a0..ec9608a 100644 --- a/README.md +++ b/README.md @@ -145,8 +145,7 @@ we click on [example.cpp](http://127.0.0.1:5000/projects/1/files/1), we see the - Next to "example.cpp", click on ["generate patches"](http://127.0.0.1:5000/projects/1/files/1/generate). - Leave "first line" and "last line" empty to mutate the whole file. -- Right now, none of the check boxes are actually implemented, so you can ignore all of them - all mutations will be - used by default. +- Select check boxes of mutations you want to use. If none is selected, all mutations will be used by default. - Click on "Generate patches". Back in the [project overview](http://127.0.0.1:5000/projects/1), you see that 14 patches have been generated. You can @@ -275,13 +274,21 @@ venv/bin/python3 cli/add_files.py --project "Example project" /tmp/cmake-example ``` ### `generate_patches.py` -This script will generate patches for all files in a project. +This script will generate patches for files in a project using selected mutators. Example usage: ```bash venv/bin/python3 cli/generate_patches.py --project "Example project" ``` +Example usage with files and mutators: +```bash +venv/bin/python3 cli/generate_patches.py \ + --project "Example project" \ + --files /tmp/cmake-example/src/example.cpp:13:-1 \ + --mutators logicalOperator,lineDeletion +``` + ### `queue_control.py` This script allows you to control the queue and view its state. @@ -290,6 +297,26 @@ Example usage: venv/bin/python3 cli/queue_control.py start ``` +## List of mutators + +| Mutator | Description | +|----------|-------------| +| lineDeletion | Deletes a whole line | +| logicalOperator | Replaces logical operators (&&, \|\|, and, or, !, not) | +| comparisonOperator | Replaces comparison operators (==, !=, <, >, <=, >=) | +| incDecOperator | Swaps increment and decrement operators (++, --) | +| assignmentOperator | Replaces assignment operators (=, +=, -=, *=, /=, %=) | +| booleanAssignmentOperator | Replaces Boolean assignment operators (=, &=, \|=, ^=, <<=, >>=) | +| arithmeticOperator | Replaces arithmetic operators (+, -, *, /, %) | +| booleanArithmeticOperator | Replaces Boolean arithmetic operators (&, \|, ^, <<, >>) | +| booleanLiteral | Swaps the Boolean literals true and false | +| stdInserter | Changes the position where elements are inserted (front_inserter, back_inserter) | +| stdRangePredicate | Changes the semantics of an STL range predicate (all_of, any_of, none_of) | +| stdMinMax | Swaps STL minimum by maximum calls (min, max) | +| decimalNumberLiteral | Replaces decimal number literals with different values | +| hexNumberLiteral | Replaces hex number literals with different values | +| iteratorRange | Changes an iterator range (begin, end) | + ## Help! Mutate++ is in a very early stage, and there is a lot to do. In particular, we are aware of severe limitations: diff --git a/app/utils/Mutation.py b/app/utils/Mutation.py index 621e97e..c10322f 100644 --- a/app/utils/Mutation.py +++ b/app/utils/Mutation.py @@ -339,7 +339,7 @@ def find_mutations(self, line): return self.pattern.mutate(line) -def get_mutators(): +def get_mutators(mutator_ids=None): mutators = [ LineDeletionMutator(), LogicalOperatorMutator(), @@ -358,4 +358,7 @@ def get_mutators(): IteratorRangeMutator() ] - return {mutator.mutator_id: mutator for mutator in mutators} + if mutator_ids: + mutators = [mutator for mutator in mutators if mutator.mutator_id in mutator_ids] + + return {mutator.mutator_id: mutator for mutator in mutators} \ No newline at end of file diff --git a/app/utils/SourceFile.py b/app/utils/SourceFile.py index 917ee4e..ff3d42b 100644 --- a/app/utils/SourceFile.py +++ b/app/utils/SourceFile.py @@ -25,8 +25,8 @@ def __init__(self, file: File, first_line, last_line): # read the relevant content self.content = '\n'.join(self.full_content[self.first_line - 1:self.last_line]) # type: str - def generate_patches(self): - mutators = get_mutators() + def generate_patches(self, mutator_ids): + mutators = get_mutators(mutator_ids) for line_number, line_raw in self.__get_lines(): for mutator_name, mutator in mutators.items(): diff --git a/app/views.py b/app/views.py index 713d5c6..8531f39 100644 --- a/app/views.py +++ b/app/views.py @@ -351,8 +351,9 @@ def route_v2_project_project_id_files_file_id_generate(project_id, file_id): except ValueError: last_line = -1 + mutators = [key for key in request.form if key not in ['first_line', 'last_line']] s = SourceFile(file, first_line, last_line) - s.generate_patches() + s.generate_patches(mutators) flash('Successfully created patches.', category='message') return redirect(url_for('route_v2_project_project_id', project_id=project.id)) diff --git a/cli/generate_patches.py b/cli/generate_patches.py index c42bb58..d60adbb 100755 --- a/cli/generate_patches.py +++ b/cli/generate_patches.py @@ -19,7 +19,14 @@ def main(): "--project", type=str, required=True, help="The name of the project. If not provided, all projects will be processed." ) - # TODO allow selection of first/last line + which patches to generate + argument_parser.add_argument( + "--files", type=str, + help='File list in format: "file1@start:end;file2@start:end". If not provided, all files in project will be used.' + ) + argument_parser.add_argument( + "--mutators", type=str, + help="Comma-separated list of mutators to use. If not provided, all mutators will be used." + ) arguments = argument_parser.parse_args() # Verify that the project exists @@ -35,10 +42,28 @@ def main(): print("No files found to process.") exit(2) + # Parse mutators if specified + mutator_ids = arguments.mutators.split(',') if arguments.mutators else None + + # Parse file filters if specified + file_filters = {} + if arguments.files: + for file_spec in arguments.files.split(';'): + filename, line_range = file_spec.split('@') + first_line, last_line = map(int, line_range.split(':')) + file_filters[filename] = (first_line, last_line) + + # Process files for file in files: + if file_filters and file.filename not in file_filters: + continue + print(f"Generating patches for '{file.filename}'...") - source_file = SourceFile(file, 1, -1) - source_file.generate_patches() + + # If file_filters is empty use all lines of every file + first_line, last_line = file_filters.get(file.filename, (1, -1)) + source_file = SourceFile(file, first_line, last_line) + source_file.generate_patches(mutator_ids) print("Done") exit(0)