def run(self, stream=sys.stdout): file_view = FileInfoDatabase(self._state) print("File View", file=stream) for file_info in file_view: print(f" File : {file_info.filename}", file=stream) # Where files are generated in the working directory # by third party tools, we cannot guarantee the hashes if file_info.filename.match(f'{self._workspace}/*'): print(' Hash : --hidden-- (generated file)') else: print(f" Hash : {file_info.adler32}", file=stream) fortran_view = FortranWorkingState(self._state) header = False for info in fortran_view: if not header: print("Fortran View", file=stream) header = True print(f" Program unit : {info.unit.name}", file=stream) print(f" Found in : {info.unit.found_in}", file=stream) print(f" Prerequisites : {', '.join(info.depends_on)}", file=stream) c_view = CWorkingState(self._state) header = False for info in c_view: if not header: print("C View", file=stream) header = True print(f" Symbol : {info.symbol.name}", file=stream) print(f" Found in : {info.symbol.found_in}", file=stream) print(f" Prerequisites : {', '.join(info.depends_on)}", file=stream)
def run(self, source: Path): self._queue.run() visitor = SourceVisitor(self._extend_queue) descender = TreeDescent(source) descender.descend(visitor) self._queue.check_queue_done() self._queue.shutdown() file_db = FileInfoDatabase(self._state) for file_info in file_db: print(file_info.filename) # Where files are generated in the working directory # by third party tools, we cannot guarantee the hashes if file_info.filename.match(f'{self._workspace}/*'): print(' hash: --hidden-- (generated file)') else: print(f' hash: {file_info.adler32}') fortran_db = FortranWorkingState(self._state) for fortran_info in fortran_db: print(fortran_info.unit.name) print(' found in: ' + str(fortran_info.unit.found_in)) print(' depends on: ' + str(fortran_info.depends_on)) c_db = CWorkingState(self._state) for c_info in c_db: print(c_info.symbol.name) print(' found_in: ' + str(c_info.symbol.found_in)) print(' depends on: ' + str(c_info.depends_on))
def test_harvested_data(self, caplog, tmp_path): """ Checks that the analyser deals with rescanning a file. """ caplog.set_level(logging.DEBUG) first_file: Path = tmp_path / 'other.F90' first_file.write_text( dedent(''' program betty use barney_mod, only :: dino implicit none end program betty module barney_mod end module barney_mod ''')) second_file: Path = tmp_path / 'test.f90' second_file.write_text( dedent(''' module barney_mod end module barney_mod ''')) database: SqliteStateDatabase = SqliteStateDatabase(tmp_path) test_unit = FortranAnalyser(tmp_path) first_artifact = Artifact(first_file, FortranSource, Raw) second_artifact = Artifact(second_file, FortranSource, Raw) # Not going to test returned objects this time _ = test_unit.run([first_artifact]) _ = test_unit.run([second_artifact]) # Confirm the database has been updated fdb = FortranWorkingState(database) assert list(iter(fdb)) \ == [FortranInfo(FortranUnitID('barney_mod', first_file)), FortranInfo(FortranUnitID('barney_mod', second_file)), FortranInfo(FortranUnitID('betty', first_file), ['barney_mod'])] assert list(fdb.depends_on(FortranUnitID('betty', first_file))) \ == [FortranUnitID('barney_mod', tmp_path / 'other.F90'), FortranUnitID('barney_mod', tmp_path / 'test.f90')] # Repeat the scan of second_file, there should be no change. # _ = test_unit.run([second_artifact]) fdb = FortranWorkingState(database) assert list(iter(fdb)) \ == [FortranInfo(FortranUnitID('barney_mod', first_file)), FortranInfo(FortranUnitID('barney_mod', second_file)), FortranInfo(FortranUnitID('betty', first_file), ['barney_mod'])] assert list(fdb.depends_on(FortranUnitID('betty', first_file))) \ == [FortranUnitID('barney_mod', tmp_path / 'other.F90'), FortranUnitID('barney_mod', tmp_path / 'test.f90')]
def __init__( self, parent: TabManager, database: StateDatabase, ): super().__init__(parent) self._parent = parent fortran_db = FortranWorkingState(database) self.rowconfigure(0, weight=1) self.columnconfigure(1, weight=1) self._unit_name = UnitNameFrame(self, fortran_db) self._unit_name.grid(row=0, column=0, padx=5, pady=5, sticky=tk.N + tk.S) self._unit_filename = UnitFileFrame(self, fortran_db) self._unit_filename.grid(row=0, column=1, padx=5, pady=5, sticky=tk.N + tk.E + tk.S + tk.W) self._unit_details = UnitInfoFrame(self, fortran_db) self._unit_details.grid(row=0, column=2, padx=5, pady=5, sticky=tk.NE + tk.SE) message = "Single-click to select." \ + " Cross pointer indicates double-click to jump" instructions = tk.Label(self, text=message) instructions.grid(row=1, column=0, columnspan=3, sticky=tk.E + tk.W) self.select_unit(self._unit_name.get_selected_unit())
def test_analyser_scope(self, caplog, tmp_path): """ Tests that the analyser is able to track scope correctly. """ caplog.set_level(logging.DEBUG) test_file: Path = tmp_path / 'test.f90' test_file.write_text( dedent(''' program fred implicit none if (something) then named: do i=1, 10 end do named endif contains subroutine yabadabadoo() end end program module barney implicit none type betty_type integer :: property contains procedure inspect end type interface betty_type procedure betty_constructor end contains function inspect(this) class(betty_type), intent(in) :: this integer :: inspect inspect = this%property end function inspect end module ''')) database: SqliteStateDatabase = SqliteStateDatabase(tmp_path) test_unit = FortranAnalyser(tmp_path) test_artifact = Artifact(test_file, FortranSource, Raw) output_artifacts = test_unit.run([test_artifact]) # Confirm database is updated working_state = FortranWorkingState(database) assert list(working_state) \ == [FortranInfo(FortranUnitID('barney', tmp_path/'test.f90'), []), FortranInfo(FortranUnitID('fred', tmp_path/'test.f90'), [])] # Confirm returned Artifact is updated assert len(output_artifacts) == 1 assert output_artifacts[0].defines == ['fred', 'barney'] assert output_artifacts[0].depends_on == [] assert output_artifacts[0].location == test_file assert output_artifacts[0].filetype is FortranSource assert output_artifacts[0].state is Analysed
def test_analyser_program_units(self, caplog, tmp_path): """ Tests that program units and the "uses" they contain are correctly identified. """ caplog.set_level(logging.DEBUG) test_file: Path = tmp_path / 'test.f90' test_file.write_text( dedent(''' program foo use iso_fortran_env, only : output use, intrinsic :: ios_c_binding use beef_mod implicit none end program foo module bar use iso_fortran_env, only : output use, intrinsic :: ios_c_binding use cheese_mod, only : bits_n_bobs implicit none end module bar function baz(first, second) use iso_fortran_env, only : output use, intrinsic :: ios_c_binding use teapot_mod implicit none end function baz subroutine qux() use iso_fortran_env, only : output use, intrinsic :: ios_c_binding use wibble_mod use wubble_mod, only: stuff_n_nonsense implicit none end subroutine qux ''')) database: SqliteStateDatabase = SqliteStateDatabase(tmp_path) test_unit = FortranAnalyser(tmp_path) test_artifact = Artifact(test_file, FortranSource, Raw) output_artifacts = test_unit.run([test_artifact]) # Confirm database is updated working_state = FortranWorkingState(database) assert list(working_state) \ == [FortranInfo(FortranUnitID('bar', tmp_path/'test.f90'), ['cheese_mod']), FortranInfo(FortranUnitID('baz', tmp_path/'test.f90'), ['teapot_mod']), FortranInfo(FortranUnitID('foo', tmp_path/'test.f90'), ['beef_mod']), FortranInfo(FortranUnitID('qux', tmp_path/'test.f90'), ['wibble_mod', 'wubble_mod'])] # Confirm returned Artifact is updated assert len(output_artifacts) == 1 assert output_artifacts[0].defines == ['foo', 'bar', 'baz', 'qux'] assert output_artifacts[0].depends_on == [ 'beef_mod', 'cheese_mod', 'teapot_mod', 'wibble_mod', 'wubble_mod' ] assert output_artifacts[0].location == test_file assert output_artifacts[0].filetype is FortranSource assert output_artifacts[0].state is Analysed
def test_get_unit_info(self, tmp_path): database = SqliteStateDatabase(tmp_path) test_unit = FortranWorkingState(database) test_unit.add_fortran_program_unit( FortranUnitID('foo', tmp_path / 'foo.f90')) test_unit.add_fortran_program_unit( FortranUnitID('bar', tmp_path / 'bar.F90')) test_unit.add_fortran_program_unit( FortranUnitID('bar', tmp_path / 'brb.f90')) test_unit.add_fortran_program_unit( FortranUnitID('baz', tmp_path / 'baz.f90')) test_unit.add_fortran_dependency( FortranUnitID('bar', tmp_path / 'brb.f90'), 'foo') test_unit.add_fortran_dependency( FortranUnitID('baz', tmp_path / 'baz.f90'), 'foo') test_unit.add_fortran_dependency( FortranUnitID('baz', tmp_path / 'baz.f90'), 'bar') assert test_unit.get_program_unit('foo') \ == [FortranInfo(FortranUnitID('foo', tmp_path/'foo.f90'))] assert test_unit.get_program_unit('bar') \ == [FortranInfo(FortranUnitID('bar', tmp_path/'bar.F90')), FortranInfo(FortranUnitID('bar', tmp_path/'brb.f90'), ['foo'])] assert test_unit.get_program_unit('baz') \ == [FortranInfo(FortranUnitID('baz', tmp_path/'baz.f90'), ['bar', 'foo'])]
def test_get_program_unit(self, tmp_path: Path): database = SqliteStateDatabase(tmp_path) test_unit = FortranWorkingState(database) # Test on an empty list # with pytest.raises(WorkingStateException): _ = test_unit.get_program_unit('tigger') # Test we can retrieve an item from a single element list test_unit.add_fortran_program_unit( FortranUnitID('tigger', Path('tigger.f90'))) assert test_unit.get_program_unit('tigger') \ == [FortranInfo(FortranUnitID('tigger', Path('tigger.f90')))] with pytest.raises(WorkingStateException): _ = test_unit.get_program_unit('eeor') # Test retrieval from a multi-element list and with prerequisites. # test_unit.add_fortran_program_unit( FortranUnitID('eeor', Path('eeor.f90'))) test_unit.add_fortran_dependency( FortranUnitID('eeor', Path('eeor.f90')), 'pooh') test_unit.add_fortran_dependency( FortranUnitID('eeor', Path('eeor.f90')), 'piglet') assert test_unit.get_program_unit('tigger') \ == [FortranInfo(FortranUnitID('tigger', Path('tigger.f90')))] assert test_unit.get_program_unit('eeor') \ == [FortranInfo(FortranUnitID('eeor', Path('eeor.f90')), ['piglet', 'pooh'])] with pytest.raises(WorkingStateException): _ = test_unit.get_program_unit('pooh') # Test a multiply defined program unit. # test_unit.add_fortran_program_unit( FortranUnitID('tigger', Path('hundred.f90'))) assert test_unit.get_program_unit('tigger') \ == [FortranInfo(FortranUnitID('tigger', Path('hundred.f90'))), FortranInfo(FortranUnitID('tigger', Path('tigger.f90')))] assert test_unit.get_program_unit('eeor') \ == [FortranInfo(FortranUnitID('eeor', Path('eeor.f90')), ['piglet', 'pooh'])] with pytest.raises(WorkingStateException): _ = test_unit.get_program_unit('pooh')
def test_add_remove_sequence(self, tmp_path: Path): database = SqliteStateDatabase(tmp_path) test_unit = FortranWorkingState(database) assert list(iter(test_unit)) == [] # Add a file containing a program unit and an unsatisfied dependency. # test_unit.add_fortran_program_unit( FortranUnitID('foo', Path('foo.f90'))) test_unit.add_fortran_dependency(FortranUnitID('foo', Path('foo.f90')), 'bar') assert list(iter(test_unit)) \ == [FortranInfo(FortranUnitID('foo', Path('foo.f90')), ['bar'])] assert list(test_unit.depends_on(FortranUnitID('foo', Path('foo.f90')))) \ == [FortranUnitUnresolvedID('bar')] # Add a second file containing a second program unit. # # This satisfies the previously dangling dependency and adds a new # one. # test_unit.add_fortran_program_unit( FortranUnitID('bar', Path('bar.F90'))) test_unit.add_fortran_dependency(FortranUnitID('bar', Path('bar.F90')), 'baz') assert list(iter(test_unit)) \ == [FortranInfo(FortranUnitID('bar', Path('bar.F90')), ['baz']), FortranInfo(FortranUnitID('foo', Path('foo.f90')), ['bar'])] assert list(test_unit.depends_on(FortranUnitID('foo', Path('foo.f90')))) \ == [FortranUnitID('bar', Path('bar.F90'))] assert list(test_unit.depends_on(FortranUnitID('bar', Path('bar.F90')))) \ == [FortranUnitUnresolvedID('baz')] # Add a third file also containing a third program unit and another # copy of the first. # # The new unit depends on two other units. # test_unit.add_fortran_program_unit( FortranUnitID('baz', Path('baz.F90'))) test_unit.add_fortran_program_unit( FortranUnitID('foo', Path('baz.F90'))) test_unit.add_fortran_dependency(FortranUnitID('baz', Path('baz.F90')), 'qux') test_unit.add_fortran_dependency(FortranUnitID('baz', Path('baz.F90')), 'cheese') assert list(iter(test_unit)) \ == [FortranInfo(FortranUnitID('bar', Path('bar.F90')), ['baz']), FortranInfo(FortranUnitID('baz', Path('baz.F90')), ['cheese', 'qux']), FortranInfo(FortranUnitID('foo', Path('baz.F90'))), FortranInfo(FortranUnitID('foo', Path('foo.f90')), ['bar'])] assert list(test_unit.depends_on(FortranUnitID('foo', Path('foo.f90')))) \ == [FortranUnitID('bar', Path('bar.F90'))] assert list(test_unit.depends_on(FortranUnitID('foo', Path('baz.F90')))) \ == [] assert list(test_unit.depends_on(FortranUnitID('bar', Path('bar.F90')))) \ == [FortranUnitID('baz', Path('baz.F90'))] assert list(test_unit.depends_on(FortranUnitID('baz', Path('baz.F90')))) \ == [FortranUnitUnresolvedID('qux'), FortranUnitUnresolvedID('cheese')] # Remove a previously added file # test_unit.remove_fortran_file(Path('baz.F90')) assert list(iter(test_unit)) \ == [FortranInfo(FortranUnitID('bar', Path('bar.F90')), ['baz']), FortranInfo(FortranUnitID('foo', Path('foo.f90')), ['bar'])] assert list(test_unit.depends_on(FortranUnitID('foo', Path('foo.f90')))) \ == [FortranUnitID('bar', Path('bar.F90'))] assert list(test_unit.depends_on(FortranUnitID('bar', Path('bar.F90')))) \ == [FortranUnitUnresolvedID('baz')]
def test_analyser_cbinding(self, caplog, tmp_path): """ Tests that C bind procedures are correctly detected. """ caplog.set_level(logging.DEBUG) test_file: Path = tmp_path / 'test.f90' test_file.write_text( dedent(''' module foo integer, bind(c), target, save :: quuz real, bind(c, name="corge"), target, save :: varname function bar() bind(c, name="bar_c") implicit none end function bar subroutine baz(), bind(c) implicit none end subroutine baz interface function qux() bind(c, name="qux_c") implicit none end function qux subroutine quux() bind(c) implicit none end subroutine quux end interface end module foo ''')) database: SqliteStateDatabase = SqliteStateDatabase(tmp_path) test_unit = FortranAnalyser(tmp_path) test_artifact = Artifact(test_file, FortranSource, Raw) output_artifacts = test_unit.run([test_artifact]) # Confirm database is updated # Fortran part working_state = FortranWorkingState(database) assert list(working_state) \ == [FortranInfo(FortranUnitID('foo', tmp_path/'test.f90'), ['quux', 'qux_c'])] # C part cworking_state = CWorkingState(database) assert list(cworking_state) \ == [CInfo(CSymbolID('bar_c', tmp_path/'test.f90'), []), CInfo(CSymbolID('baz', tmp_path/'test.f90'), []), CInfo(CSymbolID('corge', tmp_path/'test.f90'), []), CInfo(CSymbolID('quuz', tmp_path/'test.f90'), [])] # Confirm returned Artifact is updated assert len(output_artifacts) == 1 assert output_artifacts[0].defines \ == ['foo', 'quuz', 'corge', 'bar_c', 'baz'] assert output_artifacts[0].depends_on == ['qux_c', 'quux'] assert output_artifacts[0].location == test_file assert output_artifacts[0].filetype is FortranSource assert output_artifacts[0].state is Analysed