diff --git a/test/test_server.py b/test/test_server.py index 83996634..152d24ae 100644 --- a/test/test_server.py +++ b/test/test_server.py @@ -1,11 +1,4 @@ -# from types import NoneType -from setup_tests import ( - path_to_uri, - run_request, - test_dir, - write_rpc_notification, - write_rpc_request, -) +from setup_tests import run_request, test_dir, write_rpc_notification, write_rpc_request def test_init(): @@ -216,461 +209,3 @@ def check_return(result_array): # assert errcode == 0 check_return(results[1]) - - -def test_comp(): - def check_return(result_array, checks): - assert len(result_array) == checks[0] - if checks[0] > 0: - assert result_array[0]["label"] == checks[1] - assert result_array[0]["detail"] == checks[2] - try: - assert result_array[0]["insertText"] == checks[3] - except KeyError: - pass - - def comp_request(file_path, line, char): - return write_rpc_request( - 1, - "textDocument/completion", - { - "textDocument": {"uri": str(file_path)}, - "position": {"line": line, "character": char}, - }, - ) - - # - string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) - file_path = test_dir / "test_prog.f08" - string += comp_request(file_path, 12, 6) - string += comp_request(file_path, 13, 6) - string += comp_request(file_path, 17, 24) - string += comp_request(file_path, 18, 23) - string += comp_request(file_path, 20, 7) - string += comp_request(file_path, 21, 20) - string += comp_request(file_path, 21, 42) - string += comp_request(file_path, 23, 26) - file_path = test_dir / "subdir" / "test_submod.F90" - string += comp_request(file_path, 30, 12) - string += comp_request(file_path, 31, 8) - string += comp_request(file_path, 31, 23) - string += comp_request(file_path, 35, 12) - string += comp_request(file_path, 36, 48) - file_path = test_dir / "test_inc.f90" - string += comp_request(file_path, 10, 2) - file_path = test_dir / "subdir" / "test_inc2.f90" - string += comp_request(file_path, 3, 2) - file_path = test_dir / "subdir" / "test_abstract.f90" - string += comp_request(file_path, 7, 12) - file_path = test_dir / "subdir" / "test_free.f90" - string += comp_request(file_path, 10, 22) - string += comp_request(file_path, 14, 27) - string += comp_request(file_path, 28, 15) - file_path = test_dir / "subdir" / "test_fixed.f" - string += comp_request(file_path, 15, 8) - string += comp_request(file_path, 15, 21) - file_path = test_dir / "subdir" / "test_select.f90" - string += comp_request(file_path, 21, 7) - string += comp_request(file_path, 23, 7) - string += comp_request(file_path, 25, 7) - string += comp_request(file_path, 30, 7) - file_path = test_dir / "test_block.f08" - string += comp_request(file_path, 2, 2) - string += comp_request(file_path, 5, 4) - string += comp_request(file_path, 8, 6) - file_path = test_dir / "subdir" / "test_generic.f90" - string += comp_request(file_path, 14, 10) - file_path = test_dir / "subdir" / "test_inherit.f90" - string += comp_request(file_path, 10, 11) - file_path = test_dir / "subdir" / "test_rename.F90" - string += comp_request(file_path, 13, 5) - string += comp_request(file_path, 14, 5) - file_path = test_dir / "subdir" / "test_vis.f90" - string += comp_request(file_path, 8, 10) - file_path = test_dir / "test_import.f90" - string += comp_request(file_path, 15, 20) - file_path = test_dir / "completion" / "test_vis_mod_completion.f90" - string += comp_request(file_path, 12, 16) - string += comp_request(file_path, 12, 24) - errcode, results = run_request(string, ["--use_signature_help"]) - assert errcode == 0 - - string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) - file_path = test_dir / "test_prog.f08" - string += comp_request(file_path, 12, 6) - errcode, res = run_request(string) - assert errcode == 0 - results.extend(res[1:]) - - # - exp_results = ( - # test_prog.f08 - [1, "myfun", "DOUBLE PRECISION FUNCTION myfun(n, xval)", "myfun"], - [9, "glob_sub", "SUBROUTINE glob_sub(n, xval, yval)", "glob_sub"], - [1, "bound_nopass", "SUBROUTINE bound_nopass(a, b)", "bound_nopass"], - [1, "bound_pass", "SUBROUTINE bound_pass(arg1)", "bound_pass"], - [1, "stretch_vector", "TYPE(scaled_vector)"], - [6, "scale", "TYPE(scale_type)"], - [2, "n", "INTEGER(4)"], - [1, "val", "REAL(8)"], - # subdir/test_submod.F90 - [1, "point", "TYPE"], - [1, "distance", "REAL"], - [2, "x", "REAL"], - [1, "point", "TYPE"], - [2, "x", "REAL"], - # test_inc.f90 - [2, "val1", "REAL(8)"], - # subdir/test_inc2.f90 - [2, "val1", "REAL(8)"], - # subdir/test_abstract.f90 - [1, "abs_interface", "SUBROUTINE"], - # subdir/test_free.f90 - [1, "DIMENSION(:)", "KEYWORD"], - [2, "vector_create", "SUBROUTINE"], - [3, "INTENT(IN)", "KEYWORD"], - # subdir/test_fixed.f90 - [1, "bob", "CHARACTER*(LEN=200)"], - [1, "dave", "CHARACTER*(20)"], - # subdir/test_select.f90 - [2, "a", "REAL(8)"], - [2, "a", "COMPLEX(8)"], - [1, "n", "INTEGER(4)"], - [2, "a", "REAL(8)"], - # test_block.f08 - [9, "READ", "STATEMENT"], - [10, "READ", "STATEMENT"], - [11, "READ", "STATEMENT"], - # subdir/test_generic.f90 - [ - 4, - "my_gen", - "SUBROUTINE my_gen(self, a, b)", - "my_gen(${1:self}, ${2:a}, ${3:b})", - ], - # subdir/test_inherit.f90 - [1, "val", "REAL(8)"], - # subdir/test_rename.F90 - [1, "localname", "INTEGER"], - [2, "renamed_var2", "REAL(8)"], - # subdir/test_vis.f90 - [3, "some_type", "TYPE"], - # test_import.f90 - # TODO: this should be 1, mytype2 should not appear in autocomplete - # see #5 and #8 on GitHub - [2, "mytype", "TYPE"], - # completion/test_vis_mod_completion.f90 - [1, "some_var", "INTEGER"], - [3, "length", "INTEGER"], - # test_prog.f08, completion without signature_help - # returns the entire completion as a snippet - [ - 1, - "myfun", - "DOUBLE PRECISION FUNCTION myfun(n, xval)", - "myfun(${1:n}, ${2:xval})", - ], - ) - assert len(exp_results) + 1 == len(results) - for i in range(len(exp_results)): - check_return(results[i + 1], exp_results[i]) - - -def test_sig(): - def check_return(results, checks): - assert results.get("activeParameter", -1) == checks[0] - signatures = results.get("signatures") - assert signatures[0].get("label") == checks[2] - assert len(signatures[0].get("parameters")) == checks[1] - - def sig_request(file_path, line, char): - return write_rpc_request( - 1, - "textDocument/signatureHelp", - { - "textDocument": {"uri": str(file_path)}, - "position": {"line": line, "character": char}, - }, - ) - - # - string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) - file_path = test_dir / "test_prog.f08" - string += sig_request(file_path, 25, 18) - string += sig_request(file_path, 25, 20) - string += sig_request(file_path, 25, 22) - string += sig_request(file_path, 25, 27) - string += sig_request(file_path, 25, 29) - errcode, results = run_request(string) - assert errcode == 0 - # - sub_sig = "test_sig_Sub(arg1, arg2, opt1=opt1, opt2=opt2, opt3=opt3)" - exp_results = ( - [0, 5, sub_sig], - [1, 5, sub_sig], - [2, 5, sub_sig], - [3, 5, sub_sig], - [4, 5, sub_sig], - ) - assert len(exp_results) + 1 == len(results) - for i in range(len(exp_results)): - check_return(results[i + 1], exp_results[i]) - - -def test_def(): - def check_return(result_array, checks): - # If no definition is given result is None - if result_array is None: - assert not checks[0] - return None - assert result_array["uri"] == path_to_uri(checks[2]) - assert result_array["range"]["start"]["line"] == checks[0] - assert result_array["range"]["start"]["line"] == checks[1] - - def def_request(file_path, line, char): - return write_rpc_request( - 1, - "textDocument/definition", - { - "textDocument": {"uri": str(file_path)}, - "position": {"line": line, "character": char}, - }, - ) - - # - string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) - file_path = test_dir / "test_prog.f08" - string += def_request(file_path, 12, 6) - string += def_request(file_path, 13, 6) - string += def_request(file_path, 20, 7) - string += def_request(file_path, 21, 20) - string += def_request(file_path, 21, 42) - string += def_request(file_path, 23, 26) - file_path = test_dir / "subdir" / "test_submod.F90" - string += def_request(file_path, 30, 12) - string += def_request(file_path, 35, 12) - file_path = test_dir / "test_inc.f90" - string += def_request(file_path, 2, 15) - string += def_request(file_path, 10, 2) - string += def_request(file_path, 12, 13) - file_path = test_dir / "subdir" / "test_inc2.f90" - string += def_request(file_path, 3, 2) - file_path = test_dir / "subdir" / "test_rename.F90" - string += def_request(file_path, 13, 5) - string += def_request(file_path, 14, 5) - file_path = test_dir / "hover" / "functions.f90" - string += def_request(file_path, 3, 17) - errcode, results = run_request(string) - assert errcode == 0 - # - fixed_path = str(test_dir / "subdir" / "test_fixed.f") - free_path = str(test_dir / "subdir" / "test_free.f90") - exp_results = ( - # test_prog.f08 - [0, 0, fixed_path], - [22, 22, fixed_path], - [10, 10, str(test_dir / "test_prog.f08")], - [21, 21, free_path], - [14, 14, free_path], - [5, 5, free_path], - # subdir/test_submod.F90 - [1, 1, str(test_dir / "subdir" / "test_submod.F90")], - [1, 1, str(test_dir / "subdir" / "test_submod.F90")], - # test_inc.f90 - [2, 2, str(test_dir / "subdir" / "test_inc2.f90")], - [0, 0, str(test_dir / "subdir" / "test_inc2.f90")], - [None], - # subdir/test_inc2.f90 - [4, 4, str(test_dir / "test_inc.f90")], - # subdir/test_rename.F90 - [6, 6, str(test_dir / "subdir" / "test_rename.F90")], - [1, 1, str(test_dir / "subdir" / "test_rename.F90")], - # hover/functions.f90 - [3, 3, str(test_dir / "hover" / "functions.f90")], - ) - assert len(exp_results) + 1 == len(results) - for i in range(len(exp_results)): - check_return(results[i + 1], exp_results[i]) - - -def test_refs(): - def check_return(result_array, checks): - def find_in_results(uri, sline): - for (i, result) in enumerate(result_array): - if (result["uri"] == uri) and ( - result["range"]["start"]["line"] == sline - ): - del result_array[i] - return result - return None - - assert len(result_array) == len(checks) - for check in checks: - result = find_in_results(path_to_uri(check[0]), check[1]) - assert result is not None - assert result["range"]["start"]["character"] == check[2] - assert result["range"]["end"]["character"] == check[3] - - # - string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) - file_path = test_dir / "test_prog.f08" - string += write_rpc_request( - 2, - "textDocument/references", - { - "textDocument": {"uri": str(file_path)}, - "position": {"line": 9, "character": 8}, - }, - ) - errcode, results = run_request(string) - assert errcode == 0 - # - free_path = str(test_dir / "subdir" / "test_free.f90") - check_return( - results[1], - ( - [str(test_dir / "test_prog.f08"), 2, 21, 27], - [str(test_dir / "test_prog.f08"), 9, 5, 11], - [free_path, 8, 8, 14], - [free_path, 16, 9, 15], - [free_path, 18, 14, 20], - [free_path, 36, 6, 12], - [free_path, 44, 6, 12], - [free_path, 78, 6, 12], - ), - ) - - -def test_hover(): - def hover_req(file_path: str, ln: int, col: int) -> str: - return write_rpc_request( - 1, - "textDocument/hover", - { - "textDocument": {"uri": str(file_path)}, - "position": {"line": ln, "character": col}, - }, - ) - - def check_return(result_array, checks): - assert len(result_array) == len(checks) - for (i, check) in enumerate(checks): - assert result_array[i]["contents"][0]["value"] == check - - # - string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) - file_path = test_dir / "subdir" / "test_abstract.f90" - string += hover_req(file_path, 7, 30) - file_path = test_dir / "hover" / "parameters.f90" - string += hover_req(file_path, 2, 28) - string += hover_req(file_path, 3, 28) - string += hover_req(file_path, 4, 28) - string += hover_req(file_path, 4, 41) - string += hover_req(file_path, 6, 28) - string += hover_req(file_path, 7, 38) - string += hover_req(file_path, 7, 55) - string += hover_req(file_path, 8, 37) - string += hover_req(file_path, 8, 50) - string += hover_req(file_path, 9, 37) - string += hover_req(file_path, 9, 48) - string += hover_req(file_path, 10, 37) - string += hover_req(file_path, 10, 48) - file_path = test_dir / "hover" / "pointers.f90" - string += hover_req(file_path, 1, 26) - file_path = test_dir / "hover" / "functions.f90" - string += hover_req(file_path, 1, 11) - string += hover_req(file_path, 7, 19) - string += hover_req(file_path, 12, 12) - string += hover_req(file_path, 18, 19) - string += hover_req(file_path, 23, 34) - string += hover_req(file_path, 28, 11) - string += hover_req(file_path, 34, 21) - string += hover_req(file_path, 46, 11) - string += hover_req(file_path, 51, 11) - string += hover_req(file_path, 55, 11) - file_path = test_dir / "hover" / "spaced_keywords.f90" - string += hover_req(file_path, 1, 45) - string += hover_req(file_path, 2, 99) - file_path = test_dir / "hover" / "recursive.f90" - string += hover_req(file_path, 9, 40) - file_path = test_dir / "subdir" / "test_submod.F90" - string += hover_req(file_path, 29, 24) - string += hover_req(file_path, 34, 24) - file_path = test_dir / "test_diagnostic_int.f90" - string += hover_req(file_path, 19, 14) - - errcode, results = run_request(string, fortls_args=["--sort_keywords"]) - assert errcode == 0 - # - ref_results = ( - """SUBROUTINE test(a, b) - INTEGER(4), DIMENSION(3,6), INTENT(IN) :: a - REAL(8), DIMENSION(4), INTENT(OUT) :: b""", - "INTEGER, PARAMETER :: var = 1000", - "INTEGER", - "INTEGER, PARAMETER :: var2 = 23", - "INTEGER, PARAMETER :: var3 = var*var2", - "INTEGER, PARAMETER :: var4 = 123", - "DOUBLE PRECISION, PARAMETER :: somevar = 23.12", - "DOUBLE PRECISION, PARAMETER :: some = 1e-19", - "LOGICAL(kind=8), PARAMETER :: long_bool = .true.", - "LOGICAL", - "CHARACTER(len=5), PARAMETER :: sq_str = '12345'", - "CHARACTER(LEN=5)", - 'CHARACTER(len=5), PARAMETER :: dq_str = "12345"', - "CHARACTER(LEN=5)", - "INTEGER, POINTER", - """FUNCTION fun1(arg) RESULT(fun1) - INTEGER, INTENT(IN) :: arg - INTEGER :: fun1""", - """FUNCTION fun2(arg) RESULT(fun2) - INTEGER, INTENT(IN) :: arg - INTEGER :: fun2""", - """FUNCTION fun3(arg) RESULT(retval) - INTEGER, INTENT(IN) :: arg - INTEGER :: retval""", - """FUNCTION fun4(arg) RESULT(retval) - INTEGER, INTENT(IN) :: arg - INTEGER :: retval""", - # Notice that the order of the modifiers does not match the source code - # This is part of the test, ideally they would be identical but previously - # any modifiers before the type would be discarded - """PURE ELEMENTAL FUNCTION fun5(arg) RESULT(retval) - INTEGER, INTENT(IN) :: arg - INTEGER :: retval""", - """FUNCTION fun6(arg) RESULT(retval) - INTEGER, INTENT(IN) :: arg - INTEGER, DIMENSION(10,10) :: retval""", - """PURE FUNCTION outer_product(x, y) RESULT(outer_product) - REAL, DIMENSION(:), INTENT(IN) :: x - REAL, DIMENSION(:), INTENT(IN) :: y - REAL, DIMENSION(SIZE(X), SIZE(Y)) :: outer_product""", - """FUNCTION dlamch(cmach) RESULT(dlamch) - CHARACTER :: CMACH""", - """FUNCTION fun7() RESULT(val) - TYPE(c_ptr) :: val""", - """TYPE(c_ptr) FUNCTION c_loc(x) RESULT(c_loc)""", - """REAL, DIMENSION(:, :), INTENT(IN)""", - """REAL, DIMENSION( SIZE(ARG1, 1), MAXVAL([SIZE(ARG1, 2), """ - """SIZE(ARG1, 1)]) ), INTENT(OUT)""", - """RECURSIVE SUBROUTINE recursive_assign_descending(node, vector, current_loc) - TYPE(tree_inode), POINTER, INTENT(IN) :: node - INTEGER, DIMENSION(:), INTENT(INOUT) :: vector - INTEGER, INTENT(INOUT) :: current_loc""", - """FUNCTION point_dist(a, b) RESULT(distance) - TYPE(point), INTENT(IN) :: a - TYPE(point), INTENT(IN) :: b - REAL :: distance""", - """FUNCTION is_point_equal_a(a, b) RESULT(is_point_equal_a) - TYPE(point), INTENT(IN) :: a - TYPE(point), INTENT(IN) :: b - LOGICAL :: is_point_equal_a""", - # Could be subject to change - """FUNCTION foo2(f, g, h) RESULT(arg3) - FUNCTION f(x) :: f - FUNCTION g(x) :: g - FUNCTION h(x) :: h - REAL :: arg3""", - ) - assert len(ref_results) == len(results) - 1 - check_return(results[1:], ref_results) diff --git a/test/test_server_completion.py b/test/test_server_completion.py new file mode 100644 index 00000000..15ff05c4 --- /dev/null +++ b/test/test_server_completion.py @@ -0,0 +1,323 @@ +from setup_tests import run_request, test_dir, write_rpc_request + + +def validate_comp(result_array, checks): + assert len(result_array) == checks[0] + if checks[0] > 0: + assert result_array[0]["label"] == checks[1] + assert result_array[0]["detail"] == checks[2] + try: + assert result_array[0]["insertText"] == checks[3] + except KeyError: + pass + + +def comp_request(file_path, line, char): + return write_rpc_request( + 1, + "textDocument/completion", + { + "textDocument": {"uri": str(file_path)}, + "position": {"line": line, "character": char}, + }, + ) + + +def test_comp1(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_prog.f08" + string += comp_request(file_path, 12, 6) + string += comp_request(file_path, 13, 6) + string += comp_request(file_path, 17, 24) + string += comp_request(file_path, 18, 23) + string += comp_request(file_path, 20, 7) + string += comp_request(file_path, 21, 20) + string += comp_request(file_path, 21, 42) + string += comp_request(file_path, 23, 26) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # test_prog.f08 + [1, "myfun", "DOUBLE PRECISION FUNCTION myfun(n, xval)", "myfun"], + [9, "glob_sub", "SUBROUTINE glob_sub(n, xval, yval)", "glob_sub"], + [1, "bound_nopass", "SUBROUTINE bound_nopass(a, b)", "bound_nopass"], + [1, "bound_pass", "SUBROUTINE bound_pass(arg1)", "bound_pass"], + [1, "stretch_vector", "TYPE(scaled_vector)"], + [6, "scale", "TYPE(scale_type)"], + [2, "n", "INTEGER(4)"], + [1, "val", "REAL(8)"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp2(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_submod.F90" + string += comp_request(file_path, 30, 12) + string += comp_request(file_path, 31, 8) + string += comp_request(file_path, 31, 23) + string += comp_request(file_path, 35, 12) + string += comp_request(file_path, 36, 48) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # subdir/test_submod.F90 + [1, "point", "TYPE"], + [1, "distance", "REAL"], + [2, "x", "REAL"], + [1, "point", "TYPE"], + [2, "x", "REAL"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp3(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_inc.f90" + string += comp_request(file_path, 10, 2) + file_path = test_dir / "subdir" / "test_inc2.f90" + string += comp_request(file_path, 3, 2) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # test_inc.f90 + [2, "val1", "REAL(8)"], + # subdir/test_inc2.f90 + [2, "val1", "REAL(8)"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp4(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_abstract.f90" + string += comp_request(file_path, 7, 12) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # subdir/test_abstract.f90 + [1, "abs_interface", "SUBROUTINE"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp5(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_free.f90" + string += comp_request(file_path, 10, 22) + string += comp_request(file_path, 14, 27) + string += comp_request(file_path, 28, 15) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # subdir/test_free.f90 + [1, "DIMENSION(:)", "KEYWORD"], + [2, "vector_create", "SUBROUTINE"], + [3, "INTENT(IN)", "KEYWORD"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp6(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_select.f90" + string += comp_request(file_path, 21, 7) + string += comp_request(file_path, 23, 7) + string += comp_request(file_path, 25, 7) + string += comp_request(file_path, 30, 7) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # subdir/test_select.f90 + [2, "a", "REAL(8)"], + [2, "a", "COMPLEX(8)"], + [1, "n", "INTEGER(4)"], + [2, "a", "REAL(8)"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp7(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_block.f08" + string += comp_request(file_path, 2, 2) + string += comp_request(file_path, 5, 4) + string += comp_request(file_path, 8, 6) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # test_block.f08 + [9, "READ", "STATEMENT"], + [10, "READ", "STATEMENT"], + [11, "READ", "STATEMENT"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp8(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_inherit.f90" + string += comp_request(file_path, 10, 11) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # subdir/test_inherit.f90 + [1, "val", "REAL(8)"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp9(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_rename.F90" + string += comp_request(file_path, 13, 5) + string += comp_request(file_path, 14, 5) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # subdir/test_rename.F90 + [1, "localname", "INTEGER"], + [2, "renamed_var2", "REAL(8)"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp10(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_vis.f90" + string += comp_request(file_path, 8, 10) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # subdir/test_vis.f90 + [3, "some_type", "TYPE"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp_import_host_association(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_import.f90" + string += comp_request(file_path, 15, 20) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # TODO: this should be 1, mytype2 should not appear in autocomplete + # see #5 and #8 on GitHub + [2, "mytype", "TYPE"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp_visibility_scopes(): + """Test that PUBLIC, PRIVATE scopes are enforced in autocomplete results.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "completion" / "test_vis_mod_completion.f90" + string += comp_request(file_path, 12, 16) + string += comp_request(file_path, 12, 24) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # completion/test_vis_mod_completion.f90 + [1, "some_var", "INTEGER"], + [3, "length", "INTEGER"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp_interface(): + """Test that the interface signature autocompletion, with placeholders, works.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_generic.f90" + string += comp_request(file_path, 14, 10) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # subdir/test_generic.f90 + [ + 4, + "my_gen", + "SUBROUTINE my_gen(self, a, b)", + "my_gen(${1:self}, ${2:a}, ${3:b})", + ], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp_no_signature_help(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_prog.f08" + string += comp_request(file_path, 12, 6) + errcode, results = run_request(string) + assert errcode == 0 + + exp_results = ( + # test_prog.f08, completion without signature_help + # returns the entire completion as a snippet + [ + 1, + "myfun", + "DOUBLE PRECISION FUNCTION myfun(n, xval)", + "myfun(${1:n}, ${2:xval})", + ], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) + + +def test_comp_fixed(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_fixed.f" + string += comp_request(file_path, 15, 8) + string += comp_request(file_path, 15, 21) + errcode, results = run_request(string, ["--use_signature_help"]) + assert errcode == 0 + + exp_results = ( + # subdir/test_fixed.f90 + [1, "bob", "CHARACTER*(LEN=200)"], + [1, "dave", "CHARACTER*(20)"], + ) + assert len(exp_results) == len(results) - 1 + for i, ref in enumerate(exp_results): + validate_comp(results[i + 1], ref) diff --git a/test/test_server_definitions.py b/test/test_server_definitions.py new file mode 100644 index 00000000..c77e93a6 --- /dev/null +++ b/test/test_server_definitions.py @@ -0,0 +1,214 @@ +from pathlib import Path + +from setup_tests import path_to_uri, run_request, test_dir, write_rpc_request + + +def validate_def(result_array, checks): + # If no definition is given result is None + if result_array is None: + assert not checks[0] + return None + assert result_array["uri"] == path_to_uri(checks[2]) + assert result_array["range"]["start"]["line"] == checks[0] + assert result_array["range"]["start"]["line"] == checks[1] + + +def def_request(uri: Path, line, char): + return write_rpc_request( + 1, + "textDocument/definition", + { + "textDocument": {"uri": str(uri)}, + "position": {"line": line - 1, "character": char - 1}, + }, + ) + + +def test_def_fun_sub_fixed(): + """Test that going to definition of a function or submodule works.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_prog.f08" + string += def_request(file_path, 13, 7) + string += def_request(file_path, 14, 7) + errcode, results = run_request(string) + assert errcode == 0 + fixed_path = str(test_dir / "subdir" / "test_fixed.f") + ref_res = [[0, 0, fixed_path], [22, 22, fixed_path]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_variable(): + """Test that going to definition of a variable works.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_prog.f08" + string += def_request(file_path, 21, 8) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[10, 10, str(test_dir / "test_prog.f08")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_type_bound_procedure1(): + """Test that going to definition of type bound procedure works.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_prog.f08" + string += def_request(file_path, 22, 21) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[21, 21, str(test_dir / "subdir" / "test_free.f90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_type_bound_procedure2(): + """Test that going to definition of type bound procedure works.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_prog.f08" + string += def_request(file_path, 22, 43) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[14, 14, str(test_dir / "subdir" / "test_free.f90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_type_nested_variable(): + """Test that going to definition of type bound nested variables works.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_prog.f08" + string += def_request(file_path, 24, 27) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[5, 5, str(test_dir / "subdir" / "test_free.f90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_type_in_submod_function(): + """Test that going into the definition of a type bound function in a submodule""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_submod.F90" + string += def_request(file_path, 31, 13) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[1, 1, str(test_dir / "subdir" / "test_submod.F90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_type_in_submod_procedure(): + """Test that going into the definition of a type bound procedure in a submodule""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_submod.F90" + string += def_request(file_path, 36, 13) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[1, 1, str(test_dir / "subdir" / "test_submod.F90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_include_file(): + """Test that going into the location of an include file works.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_inc.f90" + string += def_request(file_path, 3, 16) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[2, 2, str(test_dir / "subdir" / "test_inc2.f90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_include_variable1(): + """Test that going to definition of a variable in an include file works.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_inc.f90" + string += def_request(file_path, 11, 3) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[0, 0, str(test_dir / "subdir" / "test_inc2.f90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_include_variable2(): + """Test that going to definition of a variable in an include file works.""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_inc2.f90" + string += def_request(file_path, 4, 3) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[4, 4, str(test_dir / "test_inc.f90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_include_file_missing(): + """Test that going to the definition of a missing file will not break fortls""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_inc.f90" + string += def_request(file_path, 13, 14) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[None]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_rename_only_variable(): + """Test that going to definition of a renamed list variable will take you + to the original definition. + """ + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_rename.F90" + string += def_request(file_path, 14, 6) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[6, 6, str(test_dir / "subdir" / "test_rename.F90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_rename_only_variable_nested(): + """Test that going to definition of a renamed list variable will take you + to the original definition, tests the multiply renamed case. + """ + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_rename.F90" + string += def_request(file_path, 15, 6) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[1, 1, str(test_dir / "subdir" / "test_rename.F90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) + + +def test_def_function_implicit_result_variable(): + """Test that going to definition on the implicitly defined variable RESULT + works. + """ + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "functions.f90" + string += def_request(file_path, 4, 18) + errcode, results = run_request(string) + assert errcode == 0 + ref_res = [[3, 3, str(test_dir / "hover" / "functions.f90")]] + assert len(ref_res) == len(results) - 1 + for i, res in enumerate(ref_res): + validate_def(results[i + 1], res) diff --git a/test/test_server_hover.py b/test/test_server_hover.py new file mode 100644 index 00000000..7b9b956f --- /dev/null +++ b/test/test_server_hover.py @@ -0,0 +1,317 @@ +from setup_tests import run_request, test_dir, write_rpc_request + + +def hover_req(file_path: str, ln: int, col: int) -> str: + return write_rpc_request( + 1, + "textDocument/hover", + { + "textDocument": {"uri": str(file_path)}, + "position": {"line": ln, "character": col}, + }, + ) + + +def validate_hover(result_array: list, checks: list): + assert len(result_array) - 1 == len(checks) + for (i, check) in enumerate(checks): + assert result_array[i + 1]["contents"][0]["value"] == check + + +def test_hover_abstract_int_procedure(): + """Tests that the binding of an abstract interface is correctly resolved""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_abstract.f90" + string += hover_req(file_path, 7, 30) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = [ + """SUBROUTINE test(a, b) + INTEGER(4), DIMENSION(3,6), INTENT(IN) :: a + REAL(8), DIMENSION(4), INTENT(OUT) :: b""" + ] + validate_hover(results, ref_results) + + +def test_hover_parameter_multiline(): + """Test that hover parameters display value correctly across lines""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 2, 28) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["INTEGER, PARAMETER :: var = 1000"] + validate_hover(results, ref_results) + + +def test_hover_literal_num(): + """Test that hovering over literals shows their type INTEGER""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 3, 28) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["INTEGER"] + validate_hover(results, ref_results) + + +def test_hover_parameter(): + """Test that hover parameters display value correctly""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 4, 28) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["INTEGER, PARAMETER :: var2 = 23"] + validate_hover(results, ref_results) + + +def test_hover_parameter_nested(): + """Test that hover parameters using other parameter values works""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 4, 41) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["INTEGER, PARAMETER :: var3 = var*var2"] + validate_hover(results, ref_results) + + +def test_hover_parameter_multiline_missing_type(): + """Test that hover parameters display correctly when type is split across lines""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 6, 28) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["INTEGER, PARAMETER :: var4 = 123"] + validate_hover(results, ref_results) + + +def test_hover_literal_real(): + """Test that hovering over literals shows their values REAL""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 7, 47) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["REAL"] + validate_hover(results, ref_results) + + +def test_hover_parameter_double(): + """Test that hovering over parameters shows their type DOUBLE PRECISION""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 7, 38) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["DOUBLE PRECISION, PARAMETER :: somevar = 23.12"] + validate_hover(results, ref_results) + + +def test_hover_parameter_double_sf(): + """Test that hovering over parameters shows their type scientific notation""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 7, 55) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["DOUBLE PRECISION, PARAMETER :: some = 1e-19"] + validate_hover(results, ref_results) + + +def test_hover_parameter_bool(): + """Test that hovering over parameters shows their values LOGICAL""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 8, 38) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["LOGICAL(kind=8), PARAMETER :: long_bool = .true."] + validate_hover(results, ref_results) + + +def test_hover_literal_bool(): + """Test that hovering over literals shows their type LOGICAL""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 8, 50) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["LOGICAL"] + validate_hover(results, ref_results) + + +def test_hover_parameter_str_sq(): + """Test that hovering over parameters shows their value, single quote STRING""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 9, 37) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["CHARACTER(len=5), PARAMETER :: sq_str = '12345'"] + validate_hover(results, ref_results) + + +def test_hover_literal_string_sq(): + """Test that hovering over literals shows their values single quote STRING""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 9, 48) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["CHARACTER(LEN=5)"] + validate_hover(results, ref_results) + + +def test_hover_parameter_str_dq(): + """Test that hovering over parameters shows their value, double quote STRING""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 10, 37) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ['CHARACTER(len=5), PARAMETER :: dq_str = "12345"'] + validate_hover(results, ref_results) + + +def test_hover_literal_string_dq(): + """Test that hovering over literals shows their values double quote STRING""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "parameters.f90" + string += hover_req(file_path, 10, 48) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["CHARACTER(LEN=5)"] + validate_hover(results, ref_results) + + +def test_hover_pointer_attr(): + """Test that hovering maintains the variable attributes e.g. POINTER""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "pointers.f90" + string += hover_req(file_path, 1, 26) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ["INTEGER, POINTER"] + validate_hover(results, ref_results) + + +def test_hover_functions(): + """Test that hovering over functions provides the expected results""" + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "functions.f90" + string += hover_req(file_path, 1, 11) + string += hover_req(file_path, 7, 19) + string += hover_req(file_path, 12, 12) + string += hover_req(file_path, 18, 19) + string += hover_req(file_path, 23, 34) + string += hover_req(file_path, 28, 11) + string += hover_req(file_path, 34, 21) + string += hover_req(file_path, 46, 11) + string += hover_req(file_path, 51, 11) + string += hover_req(file_path, 55, 11) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + + ref_results = [ + """FUNCTION fun1(arg) RESULT(fun1) + INTEGER, INTENT(IN) :: arg + INTEGER :: fun1""", + """FUNCTION fun2(arg) RESULT(fun2) + INTEGER, INTENT(IN) :: arg + INTEGER :: fun2""", + """FUNCTION fun3(arg) RESULT(retval) + INTEGER, INTENT(IN) :: arg + INTEGER :: retval""", + """FUNCTION fun4(arg) RESULT(retval) + INTEGER, INTENT(IN) :: arg + INTEGER :: retval""", + # Notice that the order of the modifiers does not match the source code + # This is part of the test, ideally they would be identical but previously + # any modifiers before the type would be discarded + """PURE ELEMENTAL FUNCTION fun5(arg) RESULT(retval) + INTEGER, INTENT(IN) :: arg + INTEGER :: retval""", + """FUNCTION fun6(arg) RESULT(retval) + INTEGER, INTENT(IN) :: arg + INTEGER, DIMENSION(10,10) :: retval""", + """PURE FUNCTION outer_product(x, y) RESULT(outer_product) + REAL, DIMENSION(:), INTENT(IN) :: x + REAL, DIMENSION(:), INTENT(IN) :: y + REAL, DIMENSION(SIZE(X), SIZE(Y)) :: outer_product""", + """FUNCTION dlamch(cmach) RESULT(dlamch) + CHARACTER :: CMACH""", + """FUNCTION fun7() RESULT(val) + TYPE(c_ptr) :: val""", + """TYPE(c_ptr) FUNCTION c_loc(x) RESULT(c_loc)""", + ] + validate_hover(results, ref_results) + + +def test_hover_spaced_keywords(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "spaced_keywords.f90" + string += hover_req(file_path, 1, 45) + string += hover_req(file_path, 2, 99) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = [ + """REAL, DIMENSION(:, :), INTENT(IN)""", + """REAL, DIMENSION( SIZE(ARG1, 1), MAXVAL([SIZE(ARG1, 2), """ + """SIZE(ARG1, 1)]) ), INTENT(OUT)""", + ] + validate_hover(results, ref_results) + + +def test_hover_recursive(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "hover" / "recursive.f90" + string += hover_req(file_path, 9, 40) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = [ + """RECURSIVE SUBROUTINE recursive_assign_descending(node, vector, current_loc) + TYPE(tree_inode), POINTER, INTENT(IN) :: node + INTEGER, DIMENSION(:), INTENT(INOUT) :: vector + INTEGER, INTENT(INOUT) :: current_loc""" + ] + validate_hover(results, ref_results) + + +def test_hover_subroutine(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "subdir" / "test_submod.F90" + string += hover_req(file_path, 29, 24) + string += hover_req(file_path, 34, 24) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = [ + """FUNCTION point_dist(a, b) RESULT(distance) + TYPE(point), INTENT(IN) :: a + TYPE(point), INTENT(IN) :: b + REAL :: distance""", + """FUNCTION is_point_equal_a(a, b) RESULT(is_point_equal_a) + TYPE(point), INTENT(IN) :: a + TYPE(point), INTENT(IN) :: b + LOGICAL :: is_point_equal_a""", + ] + validate_hover(results, ref_results) + + +def test_hover_interface_as_argument(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_diagnostic_int.f90" + string += hover_req(file_path, 19, 14) + errcode, results = run_request(string, fortls_args=["--sort_keywords"]) + assert errcode == 0 + ref_results = ( + # Could be subject to change + """FUNCTION foo2(f, g, h) RESULT(arg3) + FUNCTION f(x) :: f + FUNCTION g(x) :: g + FUNCTION h(x) :: h + REAL :: arg3""", + ) + validate_hover(results, ref_results) diff --git a/test/test_server_references.py b/test/test_server_references.py new file mode 100644 index 00000000..46205582 --- /dev/null +++ b/test/test_server_references.py @@ -0,0 +1,53 @@ +from pathlib import Path + +from setup_tests import path_to_uri, run_request, test_dir, write_rpc_request + + +def validate_refs(result_array, checks): + def find_in_results(uri, sline): + for (i, result) in enumerate(result_array): + if (result["uri"] == uri) and (result["range"]["start"]["line"] == sline): + del result_array[i] + return result + return None + + assert len(result_array) == len(checks) + for check in checks: + result = find_in_results(path_to_uri(check[0]), check[1]) + assert result is not None + assert result["range"]["start"]["character"] == check[2] + assert result["range"]["end"]["character"] == check[3] + + +def ref_req(uri: Path, ln: int, ch: int): + return write_rpc_request( + 2, + "textDocument/references", + { + "textDocument": {"uri": str(uri)}, + "position": {"line": ln - 1, "character": ch - 1}, + }, + ) + + +def test_references(): + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_prog.f08" + string += ref_req(file_path, 10, 9) + errcode, results = run_request(string) + assert errcode == 0 + # + free_path = str(test_dir / "subdir" / "test_free.f90") + validate_refs( + results[1], + ( + [str(test_dir / "test_prog.f08"), 2, 21, 27], + [str(test_dir / "test_prog.f08"), 9, 5, 11], + [free_path, 8, 8, 14], + [free_path, 16, 9, 15], + [free_path, 18, 14, 20], + [free_path, 36, 6, 12], + [free_path, 44, 6, 12], + [free_path, 78, 6, 12], + ), + ) diff --git a/test/test_server_signature_help.py b/test/test_server_signature_help.py new file mode 100644 index 00000000..7c885930 --- /dev/null +++ b/test/test_server_signature_help.py @@ -0,0 +1,48 @@ +from pathlib import Path + +from setup_tests import run_request, test_dir, write_rpc_request + + +def sigh_request(uri: Path, line: int, char: int): + return write_rpc_request( + 1, + "textDocument/signatureHelp", + { + "textDocument": {"uri": str(uri)}, + "position": {"line": line, "character": char}, + }, + ) + + +def validate_sigh(results, refs): + assert results.get("activeParameter", -1) == refs[0] + signatures = results.get("signatures") + assert signatures[0].get("label") == refs[2] + assert len(signatures[0].get("parameters")) == refs[1] + + +def test_subroutine_signature_help(): + """Test that the signature help is correctly resolved for all arguments and + that the autocompletion is correct for the subroutine signature. + """ + string = write_rpc_request(1, "initialize", {"rootPath": str(test_dir)}) + file_path = test_dir / "test_prog.f08" + string += sigh_request(file_path, 25, 18) + string += sigh_request(file_path, 25, 20) + string += sigh_request(file_path, 25, 22) + string += sigh_request(file_path, 25, 27) + string += sigh_request(file_path, 25, 29) + errcode, results = run_request(string) + assert errcode == 0 + + sub_sig = "test_sig_Sub(arg1, arg2, opt1=opt1, opt2=opt2, opt3=opt3)" + ref = ( + [0, 5, sub_sig], + [1, 5, sub_sig], + [2, 5, sub_sig], + [3, 5, sub_sig], + [4, 5, sub_sig], + ) + assert len(ref) == len(results) - 1 + for i, r in enumerate(ref): + validate_sigh(results[i + 1], r) diff --git a/test/test_source/tmp.py b/test/test_source/tmp.py new file mode 100644 index 00000000..5e3bc411 --- /dev/null +++ b/test/test_source/tmp.py @@ -0,0 +1,18 @@ +import os + +from fparser.common.readfortran import FortranFileReader +from fparser.two.parser import ParserFactory +from fparser.two.utils import Base, walk + +os.chdir("/home/gn/Code/Python/fortls/test/test_source") + +reader = FortranFileReader("test.f90", include_dirs=["subdir"], ignore_comments=False) +parser = ParserFactory().create(std="f2008") +parse_tree = parser(reader) + +for i, node in enumerate(walk(parse_tree)): + if node is None: + continue + if not isinstance(node, Base): + continue + print(i, type(node), ": ", node)