diff --git a/changelog.txt b/changelog.txt index 3c8ea512..e604774d 100644 --- a/changelog.txt +++ b/changelog.txt @@ -18,6 +18,7 @@ Legend 2021-08-XX v.1.16.0 ------------------ +* DB Access in UT: Local Inheritance (#498) * Skip 'Prefer RETURNING to EXPORTING' when both exist (#490) * Pseudo Comment Usage: Multiple Inline Entries (#494) * Prefer Pragmas to Pseudo Comments: Multiple Pseudo Comments (#489) diff --git a/src/checks/y_check_db_access_in_ut.clas.abap b/src/checks/y_check_db_access_in_ut.clas.abap index 29ba335e..4a592932 100644 --- a/src/checks/y_check_db_access_in_ut.clas.abap +++ b/src/checks/y_check_db_access_in_ut.clas.abap @@ -39,8 +39,9 @@ CLASS y_check_db_access_in_ut DEFINITION PUBLIC INHERITING FROM y_check_base CRE END OF keys. TYPES: BEGIN OF properties, - name TYPE string, - risk_level TYPE string, + name TYPE string, + risk_level TYPE string, + has_framework TYPE abap_bool, END OF properties. DATA defined_classes TYPE STANDARD TABLE OF properties. @@ -59,8 +60,8 @@ CLASS y_check_db_access_in_ut DEFINITION PUBLIC INHERITING FROM y_check_base CRE statement TYPE sstmnt structure TYPE sstruc. - METHODS is_part_of_framework IMPORTING structure TYPE sstruc - RETURNING VALUE(result) TYPE abap_bool. + METHODS is_it_using_framework RETURNING VALUE(result) TYPE abap_bool. + METHODS is_superclass_using_framework RETURNING VALUE(result) TYPE abap_bool. METHODS is_persistent_object IMPORTING obj_name TYPE tadir-obj_name RETURNING VALUE(result) TYPE abap_bool. @@ -101,6 +102,15 @@ CLASS Y_CHECK_DB_ACCESS_IN_UT IMPLEMENTATION. ENDMETHOD. + METHOD inspect_structures. + relevant_statement_types = VALUE #( ( scan_struc_stmnt_type-class_definition ) ). + super->inspect_structures( ). + + relevant_statement_types = VALUE #( ( scan_struc_stmnt_type-class_implementation ) ). + super->inspect_structures( ). + ENDMETHOD. + + METHOD inspect_tokens. CASE structure-stmnt_type. WHEN scan_struc_stmnt_type-class_definition. @@ -116,57 +126,73 @@ CLASS Y_CHECK_DB_ACCESS_IN_UT IMPLEMENTATION. ENDMETHOD. - METHOD is_persistent_object. - DATA(upper_name) = to_upper( obj_name ). - - SELECT SINGLE @abap_true - FROM tadir - INTO @result - WHERE obj_name = @upper_name - AND object = @keys-table - AND delflag = @space. - ENDMETHOD. + METHOD add_line_to_defined_classes. + TRY. + DATA(class_config) = VALUE properties( name = get_class_name( structure ) ). + CATCH cx_sy_itab_line_not_found. + RETURN. + ENDTRY. + IF line_exists( defined_classes[ name = class_config-name ] ). + RETURN. + ENDIF. - METHOD is_internal_table. - CONSTANTS max_name_size TYPE i VALUE 40. + class_config-risk_level = get_risk_level( statement ). - DATA(second_token) = get_token_abs( statement-from + 1 ). - DATA(third_token) = get_token_abs( statement-from + 2 ). - DATA(fourth_token) = get_token_abs( statement-from + 2 ). + class_config-has_framework = xsdbool( is_it_using_framework( ) + OR is_superclass_using_framework( ) ). - IF second_token = keys-into. - RETURN. - ENDIF. + APPEND class_config TO defined_classes. + ENDMETHOD. - DATA(table_name) = COND #( WHEN second_token = keys-from THEN third_token - WHEN third_token = keys-into THEN fourth_token - ELSE second_token ). - IF strlen( table_name ) > max_name_size. - RETURN. + METHOD get_class_name. + DATA(index) = ref_scan->statements[ structure-stmnt_from ]-from. + IF get_token_abs( index ) = keys-class. + result = get_token_abs( index + 1 ). ENDIF. + ENDMETHOD. - result = xsdbool( is_persistent_object( CONV #( table_name ) ) = abap_false ). + + METHOD get_risk_level. + LOOP AT ref_scan->tokens ASSIGNING FIELD-SYMBOL() + FROM statement-from TO statement-to + WHERE str = risk_level-harmless + OR str = risk_level-dangerous + OR str = risk_level-critical. + result = -str. + ENDLOOP. ENDMETHOD. - METHOD add_line_to_defined_classes. - TRY. - DATA(class_config) = VALUE properties( name = get_class_name( structure ) ). - CATCH cx_sy_itab_line_not_found. + METHOD is_it_using_framework. + LOOP AT ref_scan->statements ASSIGNING FIELD-SYMBOL() + FROM structure_wa-stmnt_from TO structure_wa-stmnt_to. + LOOP AT ref_scan->tokens TRANSPORTING NO FIELDS + FROM -from TO -to + WHERE str = framework-qsql_if + OR str = framework-qsql_cl + OR str = framework-cds_if + OR str = framework-cds_cl. + result = abap_true. RETURN. - ENDTRY. + ENDLOOP. + ENDLOOP. + ENDMETHOD. - IF line_exists( defined_classes[ name = class_config-name ] ). - RETURN. - ENDIF. - class_config-risk_level = get_risk_level( statement ). + METHOD is_superclass_using_framework. + statement_wa = ref_scan->statements[ structure_wa-stmnt_from ]. + + DATA(superclass) = next2( p_word1 = 'INHERITING' + p_word2 = 'FROM' ). - IF is_part_of_framework( structure ) = abap_false. - APPEND class_config TO defined_classes. + IF superclass IS INITIAL. + RETURN. ENDIF. + + result = xsdbool( line_exists( defined_classes[ name = superclass + has_framework = abap_true ] ) ). ENDMETHOD. @@ -177,7 +203,8 @@ CLASS Y_CHECK_DB_ACCESS_IN_UT IMPLEMENTATION. RETURN. ENDTRY. - IF NOT line_exists( defined_classes[ name = class_name ] ). + IF NOT line_exists( defined_classes[ name = class_name + has_framework = abap_false ] ). RETURN. ENDIF. @@ -232,54 +259,47 @@ CLASS Y_CHECK_DB_ACCESS_IN_UT IMPLEMENTATION. ENDMETHOD. - METHOD is_part_of_framework. - LOOP AT ref_scan->statements ASSIGNING FIELD-SYMBOL() - FROM structure-stmnt_from TO structure-stmnt_to. - LOOP AT ref_scan->tokens TRANSPORTING NO FIELDS - FROM -from TO -to - WHERE str = framework-qsql_if - OR str = framework-qsql_cl - OR str = framework-cds_if - OR str = framework-cds_cl. - result = abap_true. - RETURN. - ENDLOOP. - ENDLOOP. + METHOD has_ddic_itab_same_syntax. + result = xsdbool( token-str = check_for-modify + OR token-str = check_for-update + OR token-str = check_for-delete + OR token-str = check_for-insert ). ENDMETHOD. - METHOD get_class_name. - DATA(index) = ref_scan->statements[ structure-stmnt_from ]-from. - IF get_token_abs( index ) = keys-class. - result = get_token_abs( index + 1 ). - ENDIF. + METHOD is_persistent_object. + DATA(upper_name) = to_upper( obj_name ). + + SELECT SINGLE @abap_true + FROM tadir + INTO @result + WHERE obj_name = @upper_name + AND object = @keys-table + AND delflag = @space. ENDMETHOD. - METHOD get_risk_level. - LOOP AT ref_scan->tokens ASSIGNING FIELD-SYMBOL() - FROM statement-from TO statement-to - WHERE str = risk_level-harmless - OR str = risk_level-dangerous - OR str = risk_level-critical. - result = -str. - ENDLOOP. - ENDMETHOD. + METHOD is_internal_table. + CONSTANTS max_name_size TYPE i VALUE 40. + DATA(second_token) = get_token_abs( statement-from + 1 ). + DATA(third_token) = get_token_abs( statement-from + 2 ). + DATA(fourth_token) = get_token_abs( statement-from + 2 ). - METHOD inspect_structures. - relevant_statement_types = VALUE #( ( scan_struc_stmnt_type-class_definition ) ). - super->inspect_structures( ). + IF second_token = keys-into. + RETURN. + ENDIF. - relevant_statement_types = VALUE #( ( scan_struc_stmnt_type-class_implementation ) ). - super->inspect_structures( ). - ENDMETHOD. + DATA(table_name) = COND #( WHEN second_token = keys-from THEN third_token + WHEN third_token = keys-into THEN fourth_token + ELSE second_token ). + IF strlen( table_name ) > max_name_size. + RETURN. + ENDIF. - METHOD has_ddic_itab_same_syntax. - result = xsdbool( token-str = check_for-modify - OR token-str = check_for-update - OR token-str = check_for-delete - OR token-str = check_for-insert ). + result = xsdbool( is_persistent_object( CONV #( table_name ) ) = abap_false ). ENDMETHOD. + + ENDCLASS. diff --git a/src/checks/y_check_db_access_in_ut.clas.testclasses.abap b/src/checks/y_check_db_access_in_ut.clas.testclasses.abap index 8bcfb4ba..870f7bee 100644 --- a/src/checks/y_check_db_access_in_ut.clas.testclasses.abap +++ b/src/checks/y_check_db_access_in_ut.clas.testclasses.abap @@ -100,6 +100,74 @@ CLASS ltc_osql_framework IMPLEMENTATION. ENDCLASS. + + +CLASS ltc_osql_framework_inherited DEFINITION INHERITING FROM ltc_osql_framework FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. + PROTECTED SECTION. + METHODS get_code_without_issue REDEFINITION. +ENDCLASS. + +CLASS ltc_osql_framework_inherited IMPLEMENTATION. + + METHOD get_code_without_issue. + result = VALUE #( + ( ' REPORT ut_test. ' ) + + ( ' CLASS lcl_base DEFINITION FOR TESTING. ' ) + ( ' PROTECTED SECTION. ' ) + ( ' METHODS given_fake_value. ' ) + ( ' METHODS when_select. ' ) + ( ' METHODS then_has_entry. ' ) + ( ' PRIVATE SECTION. ' ) + ( ' CLASS-DATA osql_test_environment TYPE REF TO if_osql_test_environment. ' ) + ( ' DATA cut TYPE tadir. ' ) + ( ' CLASS-METHODS class_setup. ' ) + ( ' CLASS-METHODS class_teardown. ' ) + ( ' ENDCLASS.' ) + + ( ' CLASS lcl_base IMPLEMENTATION. ' ) + + ( ' METHOD class_setup. ' ) + ( | osql_test_environment = cl_osql_test_environment=>create( VALUE #( ( 'tadir' ) ) ). | ) + ( ' ENDMETHOD. ' ) + + ( ' METHOD class_teardown. ' ) + ( ' osql_test_environment->destroy( ). ' ) + ( ' ENDMETHOD. ' ) + + ( ' METHOD given_fake_value. ' ) + ( ' "osql_test_environment->insert_test_data( data ). ' ) + ( ' ENDMETHOD. ' ) + + ( ' METHOD when_select. ' ) + ( ' SELECT SINGLE * FROM tadir INTO cut. ' ) + ( ' ENDMETHOD. ' ) + + ( ' METHOD then_has_entry. ' ) + ( ' cl_abap_unit_assert=>assert_not_initial( cut ). ' ) + ( ' ENDMETHOD. ' ) + + ( ' ENDCLASS. ' ) + + ( ' CLASS lcl_test DEFINITION FOR TESTING INHERITING FROM lcl_base. ' ) + ( ' PUBLIC SECTION. ' ) + ( ' METHODS scenario FOR TESTING. ' ) + ( ' ENDCLASS.' ) + + ( ' CLASS lcl_test IMPLEMENTATION. ' ) + ( ' METHOD scenario. ' ) + ( ' DATA entries TYPE tadir. ' ) + ( ' given_fake_value( ). ' ) + ( ' SELECT SINGLE * FROM tadir INTO entries. ' ) + ( ' ENDMETHOD. ' ) + ( ' ENDCLASS. ' ) + ). + ENDMETHOD. + +ENDCLASS. + + + CLASS ltc_risk_harmless DEFINITION INHERITING FROM y_unit_test_base FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PROTECTED SECTION. METHODS get_cut REDEFINITION. @@ -205,6 +273,8 @@ CLASS ltc_risk_harmless IMPLEMENTATION. ENDCLASS. + + CLASS ltc_risk_critical DEFINITION INHERITING FROM y_unit_test_base FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PROTECTED SECTION. METHODS get_cut REDEFINITION. @@ -292,6 +362,8 @@ CLASS ltc_risk_critical IMPLEMENTATION. ENDCLASS. + + CLASS ltc_risk_dangerous DEFINITION INHERITING FROM ltc_risk_critical FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PROTECTED SECTION. METHODS get_cut REDEFINITION. @@ -333,6 +405,8 @@ CLASS ltc_risk_dangerous IMPLEMENTATION. ENDCLASS. + + CLASS ltc_risk_not_set DEFINITION INHERITING FROM ltc_risk_harmless FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PROTECTED SECTION. METHODS get_code_with_issue REDEFINITION. @@ -370,6 +444,7 @@ CLASS ltc_risk_not_set IMPLEMENTATION. ENDCLASS. + CLASS ltc_exec_sql DEFINITION INHERITING FROM y_unit_test_base FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PROTECTED SECTION. METHODS get_cut REDEFINITION. @@ -440,6 +515,7 @@ CLASS ltc_exec_sql IMPLEMENTATION. ENDCLASS. + CLASS ltc_itab DEFINITION INHERITING FROM ltc_risk_harmless FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PROTECTED SECTION. METHODS get_code_without_issue REDEFINITION.