def test_given_call_tree_depth_of_two__dump_returns_extected_output(): manager = CallTreeManager() excepted = 'f()\n g()\n' with generate_file('two-functions.cpp', 'void g() {}\nvoid f() {g();}') as file_name: actual = manager.dump(file_name, 'f()') assert actual == excepted
def main(): parser = OptionParser( 'usage: %prog [options] entry-point {file.cpp|compile_commands.json} [extra-clang-args*]' ) parser.add_option( '', '--include-system-headers', action="store_true", dest='include_system_headers', default=False, help='Include calls into system headers from the call-tree') # parser.add_option('', '--max-depth', dest='maxDepth', # help='Limit cursor expansion to depth N', # metavar='N', type=int, default=None) parser.disable_interspersed_args() (opts, args) = parser.parse_args() if len(args) < 2: parser.error('invalid number arguments') manager = CallTreeManager() extra_arguments = args[2:] if len(args) > 2 else None print( manager.dump(entry_point=args[0], file_name=args[1], include_system_headers=opts.include_system_headers, extra_arguments=extra_arguments))
def test_given_tu_with_include__select_tu_returns_only_callables_in_main_file( ): manager = CallTreeManager() with generate_file('include.h', 'void f() {}') as include_file_name: with generate_file( 'main_file.cpp', '#include "{}"\nvoid g();'.format( include_file_name)) as main_file_name: manager.open(main_file_name) callable_list = manager.select_tu(main_file_name) assert len(callable_list) == 1
def test_given_no_callable_included__export_returns_empty_diagram(): manager = CallTreeManager() with generate_file('file.cpp', 'void f();\nvoid g() { f(); }') as file_name: manager.open(file_name) manager.select_tu(file_name) manager.select_root('g()') expected = '@startuml\n\n\n@enduml' actual = manager.export() assert actual == expected
def test_given_werror_extra_argument_and_tu_with_unused_parameter__select_tu_fails( ): manager = CallTreeManager() manager.set_extra_arguments('-Werror -Wunused-parameter') with generate_file('file.cpp', 'void f(int i) {}') as file_name: with pytest.raises(SyntaxError): manager.open(file_name) manager.select_tu(file_name)
def test_given_tu_with_tree_three_functions_deep__tree_of_select_root_on_middle_function_has_depth_two( ): manager = CallTreeManager() content = 'void f();\nvoid g() { f(); }\nvoid h() { g(); }\n' with generate_file('file.cpp', content) as file_name: manager.open(file_name) manager.select_tu(file_name) root = manager.select_root('g()') children = manager.get_calls_of(root.name) assert len(children) == 1 grand_children = manager.get_calls_of(children[0].name) assert len(grand_children) == 0
def test_given_tu_with_two_functions__get_calls_of_returns_one_callable(): manager = CallTreeManager() with generate_file('file.cpp', 'void f();\nvoid g() { f(); }') as file_name: manager.open(file_name) manager.select_tu(file_name) unexpected_children = manager.get_calls_of('f()') expected_children = manager.get_calls_of('g()') assert len(unexpected_children) == 0 assert len(expected_children) == 1
def __init__(self, parent=None): super(EntryDialog, self).__init__(parent) self.setupUi(self) # TODO(KNR): prevent editing the entry file and entry point lists self.manager_ = CallTreeManager() self.browse_compilation_database_button_.clicked.connect( self.on_browse) self.compilation_database_path_.editingFinished.connect( self.on_edit_db_path) self.extra_arguments_.editingFinished.connect(self.on_edit_extra_args) self.entry_files_ = QStandardItemModel(self.entry_file_list_) self.entry_file_list_.setModel(self.entry_files_) self.entry_file_selection_ = self.entry_file_list_.selectionModel() self.entry_file_selection_.currentChanged.connect( self.on_select_entry_file) self.entry_points_ = QStandardItemModel(self.entry_point_list_)
class EntryDialog(QDialog, Ui_EntryDialog): def __init__(self, parent=None): super(EntryDialog, self).__init__(parent) self.setupUi(self) # TODO(KNR): prevent editing the entry file and entry point lists self.manager_ = CallTreeManager() self.browse_compilation_database_button_.clicked.connect( self.on_browse) self.compilation_database_path_.editingFinished.connect( self.on_edit_db_path) self.extra_arguments_.editingFinished.connect(self.on_edit_extra_args) self.entry_files_ = QStandardItemModel(self.entry_file_list_) self.entry_file_list_.setModel(self.entry_files_) self.entry_file_selection_ = self.entry_file_list_.selectionModel() self.entry_file_selection_.currentChanged.connect( self.on_select_entry_file) self.entry_points_ = QStandardItemModel(self.entry_point_list_) def on_edit_extra_args(self): args = self.extra_arguments_.text() self.manager_.state_ = CallTreeManagerState.INITIALIZED # TODO(KNR): yuck self.manager_.set_extra_arguments(args) if self.compilation_database_path_.text(): self.on_edit_db_path() def on_browse(self): path = QFileDialog.getOpenFileName(self, 'Open compilation database', '', '*.json') if path and len(path) > 0: path = path[0] if not path: return self.compilation_database_path_.setText( path) # TODO(KNR): prevent double-event self.set_db_path(path) def on_edit_db_path(self): path = self.compilation_database_path_.text() if not path: return self.set_db_path(path) def set_db_path(self, db_path): compilation_database_directory = os.path.dirname(db_path) os.chdir(compilation_database_directory) self.entry_points_.clear() self.entry_files_.clear() tu_list = self.manager_.open(db_path) common_path = os.path.commonprefix(list(tu_list)) for tu in tu_list: item = QStandardItem(tu.replace(common_path, '')) item.setData(tu) self.entry_files_.appendRow(item) def on_select_entry_file(self, current, previous): if not current: return entry_file_path = self.entry_files_.item(current.row(), 0).data() callable_list = self.manager_.select_tu(entry_file_path) self.entry_points_.clear() for callable in callable_list: item = CallableItem(callable) self.entry_points_.appendRow(item) self.entry_point_list_.setModel(self.entry_points_) def reject(self): QApplication.instance().quit() def accept(self): if not self.entry_point_list_.selectionModel(): return current = self.entry_point_list_.selectionModel().selectedIndexes() if current and len(current) > 0: entry_point = self.entry_points_.item(current[0].row(), 0) self.manager_.select_root(entry_point.callable.name) self.hide() self.window_ = DiagramConfiguration(self.manager_, entry_point) self.window_.show()
def test_given_tu_referencing_function_in_other_tu__load_definition_grows_call_tree( ): manager = CallTreeManager() with generate_file('g.cpp', 'extern void f();\nvoid g() { f(); }') as file_name: manager.open(file_name) manager.select_tu(file_name) root = manager.select_root('g()') with generate_file('f.cpp', 'void f() { f(); }\n') as file_name: manager.state_ = CallTreeManagerState.READY_TO_SELECT_TU manager.open( file_name) # TODO(KNR): a hack, use a compilation DB instead manager.load_definition('f()') children = manager.get_calls_of(root.name) grand_children = manager.get_calls_of(children[0].name) assert len(grand_children) == 1
def test_given_tu_with_one_function__select_tu_returns_list_with_one_item(): manager = CallTreeManager() with generate_file('file.cpp', 'void f();') as file_name: manager.open(file_name) callable_list = manager.select_tu(file_name) assert len(callable_list) == 1
def test_given_source_file__open_returns_tu_list_with_one_item(): manager = CallTreeManager() with generate_file('file.cpp', '') as file_name: tu_list = manager.open(file_name) assert len(tu_list) == 1
def test_given_methods_of_same_class__use_class_as_participant(): manager = CallTreeManager() with generate_file('file.cpp', 'class Foo {\nvoid bar();\nvoid baz() { bar(); }\n};' ) as file_name: manager.open(file_name) manager.select_tu(file_name) manager.select_root('Foo::baz()') manager.include('Foo::baz()') manager.include('Foo::bar()') expected = '@startuml\n\n -> "Foo": baz()\nactivate "Foo"\n"Foo" -> "Foo": bar()\nactivate "Foo"\ndeactivate "Foo"\ndeactivate "Foo"\n\n@enduml' actual = manager.export() assert actual == expected
def test_given_call_graph_of_depth_two__functions_exported_depth_first(): manager = CallTreeManager() with generate_file( 'file.cpp', 'void i();\nvoid h();\nvoid g() { h(); }\nvoid f() { g();\ni(); }' ) as file_name: manager.open(file_name) manager.select_tu(file_name) manager.select_root('f()') manager.include('f()') manager.include('g()') manager.include('h()') manager.include('i()') expected = '@startuml\n\n -> "file.cpp": f()\nactivate "file.cpp"\n"file.cpp" -> "file.cpp": g()\nactivate "file.cpp"\n"file.cpp" -> "file.cpp": h()\nactivate "file.cpp"\ndeactivate "file.cpp"\ndeactivate "file.cpp"\n"file.cpp" -> "file.cpp": i()\nactivate "file.cpp"\ndeactivate "file.cpp"\ndeactivate "file.cpp"\n\n@enduml' actual = manager.export() assert actual == expected
def test_given_included_function_excluded_again__export_returns_diagram_with_call( ): manager = CallTreeManager() with generate_file('file.cpp', 'void f();\nvoid g() { f(); }') as file_name: manager.open(file_name) manager.select_tu(file_name) manager.select_root('g()') manager.include('g()') manager.include('f()') manager.exclude('g()') expected = '@startuml\n\n -> "file.cpp": f()\nactivate "file.cpp"\ndeactivate "file.cpp"\n\n@enduml' actual = manager.export() assert actual == expected
# Copyright (C) 2020 R. Knuus from INCode.call_tree_manager import CallTreeManager from INCode.tui import TuiViewModel from prompt_toolkit.history import FileHistory import appdirs import click import click_repl import os import re manager = CallTreeManager() root_callable = None view_model = TuiViewModel(manager) def clean_up_usage_message_(message, command): '''Convert application help message into command help message.''' return re.sub( ' --help\\s+Show this message and exit.', '\n', message.replace('Usage: {}'.format(os.path.basename(__file__)), '').replace(' help', command)) @click.group(invoke_without_command=True) @click.pass_context def cli(ctx): '''Pleasantries CLI''' if ctx.invoked_subcommand is None: ctx.invoke(repl)