From 7ee4b45baa9615933225d1648ddb0d51ac9bec72 Mon Sep 17 00:00:00 2001 From: Guenter Christian Date: Thu, 27 Mar 2025 15:38:05 +0000 Subject: [PATCH] Page Stage: fix case logic --- .../zcl_abapgit_gui_page_stage.clas.abap | 125 +---- ...bapgit_gui_page_stage.clas.locals_imp.abap | 198 ++++++++ ...apgit_gui_page_stage.clas.testclasses.abap | 439 ++++++++++++++++++ .../pages/zcl_abapgit_gui_page_stage.clas.xml | 1 + 4 files changed, 643 insertions(+), 120 deletions(-) create mode 100644 src/ui/pages/zcl_abapgit_gui_page_stage.clas.locals_imp.abap create mode 100644 src/ui/pages/zcl_abapgit_gui_page_stage.clas.testclasses.abap diff --git a/src/ui/pages/zcl_abapgit_gui_page_stage.clas.abap b/src/ui/pages/zcl_abapgit_gui_page_stage.clas.abap index 0e1eb9531..2dda8610d 100644 --- a/src/ui/pages/zcl_abapgit_gui_page_stage.clas.abap +++ b/src/ui/pages/zcl_abapgit_gui_page_stage.clas.abap @@ -61,11 +61,6 @@ CLASS zcl_abapgit_gui_page_stage DEFINITION DATA mv_sci_result TYPE zif_abapgit_definitions=>ty_sci_result. DATA mi_obj_filter TYPE REF TO zif_abapgit_object_filter. - METHODS check_selected - IMPORTING - !io_files TYPE REF TO zcl_abapgit_string_map - RAISING - zcx_abapgit_exception . METHODS find_changed_by IMPORTING !it_files TYPE zif_abapgit_definitions=>ty_stage_files @@ -137,51 +132,7 @@ ENDCLASS. -CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION. - - - METHOD check_selected. - - DATA: - ls_file TYPE zif_abapgit_git_definitions=>ty_file, - lv_pattern TYPE string, - lv_msg TYPE string. - - FIELD-SYMBOLS: - LIKE LINE OF io_files->mt_entries, - LIKE LINE OF io_files->mt_entries. - - " Check all added files if the exist in different paths (packages) without being removed - LOOP AT io_files->mt_entries ASSIGNING WHERE v = zif_abapgit_definitions=>c_method-add. - - " Allow mixed case path, but check filename to lower case - zcl_abapgit_path=>split_file_location( - EXPORTING - iv_fullpath = -k - IMPORTING - ev_path = ls_file-path - ev_filename = ls_file-filename ). - - ls_file-filename = to_lower( ls_file-filename ). - - " Skip packages since they all have identical filenames - IF ls_file-filename <> 'package.devc.xml'. - lv_pattern = '*/' && to_upper( ls_file-filename ). - REPLACE ALL OCCURRENCES OF '#' IN lv_pattern WITH '##'. " for CP - - LOOP AT io_files->mt_entries ASSIGNING - WHERE k CP lv_pattern AND k <> -k AND v <> zif_abapgit_definitions=>c_method-rm. - - lv_msg = |In order to add { to_lower( -k ) }, | && - |you have to remove { to_lower( -k ) }|. - zcx_abapgit_exception=>raise( lv_msg ). - - ENDLOOP. - ENDIF. - ENDLOOP. - - ENDMETHOD. - +CLASS zcl_abapgit_gui_page_stage IMPLEMENTATION. METHOD constructor. @@ -719,76 +670,10 @@ CLASS ZCL_ABAPGIT_GUI_PAGE_STAGE IMPLEMENTATION. METHOD stage_selected. - DATA ls_file TYPE zif_abapgit_git_definitions=>ty_file. - DATA lo_files TYPE REF TO zcl_abapgit_string_map. - - FIELD-SYMBOLS: - LIKE LINE OF ms_files-local, - LIKE LINE OF ms_files-status, - LIKE LINE OF lo_files->mt_entries. - - lo_files = ii_event->form_data( ). - - IF lo_files->size( ) = 0. - zcx_abapgit_exception=>raise( 'process_stage_list: empty list' ). - ENDIF. - - check_selected( lo_files ). - - CREATE OBJECT ro_stage. - - LOOP AT lo_files->mt_entries ASSIGNING - "Ignore Files that we don't want to stage, so any errors don't stop the staging process - WHERE v <> zif_abapgit_definitions=>c_method-skip. - - " Allow mixed case path, but check filename to lower case - zcl_abapgit_path=>split_file_location( - EXPORTING - iv_fullpath = -k - IMPORTING - ev_path = ls_file-path - ev_filename = ls_file-filename ). - - ls_file-filename = to_lower( ls_file-filename ). - - READ TABLE ms_files-status ASSIGNING - WITH TABLE KEY - path = ls_file-path - filename = ls_file-filename. - IF sy-subrc <> 0. -* see https://github.com/abapGit/abapGit/issues/3073 - zcx_abapgit_exception=>raise( - |Unable to stage { ls_file-filename }. If the filename contains spaces, this is a known issue.| && - | Consider ignoring or staging the file at a later time.| ). - ENDIF. - - CASE -v. - WHEN zif_abapgit_definitions=>c_method-add. - READ TABLE ms_files-local ASSIGNING - WITH KEY file-path = ls_file-path - file-filename = ls_file-filename. - - IF sy-subrc <> 0. - zcx_abapgit_exception=>raise( |process_stage_list: unknown file { ls_file-path }{ ls_file-filename }| ). - ENDIF. - - ro_stage->add( iv_path = -file-path - iv_filename = -file-filename - is_status = - iv_data = -file-data ). - WHEN zif_abapgit_definitions=>c_method-ignore. - ro_stage->ignore( iv_path = ls_file-path - iv_filename = ls_file-filename ). - WHEN zif_abapgit_definitions=>c_method-rm. - ro_stage->rm( iv_path = ls_file-path - is_status = - iv_filename = ls_file-filename ). - WHEN zif_abapgit_definitions=>c_method-skip. - " Do nothing - WHEN OTHERS. - zcx_abapgit_exception=>raise( |process_stage_list: unknown method { -v }| ). - ENDCASE. - ENDLOOP. + ro_stage = lcl_selected=>get_instance( )->stage_selected( + ii_event = ii_event + it_status = ms_files-status + it_local = ms_files-local ). ENDMETHOD. diff --git a/src/ui/pages/zcl_abapgit_gui_page_stage.clas.locals_imp.abap b/src/ui/pages/zcl_abapgit_gui_page_stage.clas.locals_imp.abap new file mode 100644 index 000000000..a8ba1cf55 --- /dev/null +++ b/src/ui/pages/zcl_abapgit_gui_page_stage.clas.locals_imp.abap @@ -0,0 +1,198 @@ +*"* use this source file for the definition and implementation of +*"* local helper classes, interface definitions and type +*"* declarations + +INTERFACE lif_selected. + + METHODS stage_selected + IMPORTING + ii_event TYPE REF TO zif_abapgit_gui_event + it_status TYPE zif_abapgit_definitions=>ty_results_ts_path + it_local TYPE zif_abapgit_definitions=>ty_files_item_tt + RETURNING + VALUE(ro_stage) TYPE REF TO zcl_abapgit_stage + RAISING + zcx_abapgit_exception. + +ENDINTERFACE. + +CLASS lcl_selected DEFINITION CREATE PRIVATE. + + PUBLIC SECTION. + CLASS-METHODS get_instance + RETURNING + VALUE(ro_instance) TYPE REF TO lif_selected. + + INTERFACES lif_selected. + + PRIVATE SECTION. + METHODS check_selected + IMPORTING + io_files TYPE REF TO zcl_abapgit_string_map + RAISING + zcx_abapgit_exception. + + METHODS read_status_case_insensitive + IMPORTING + is_file TYPE zif_abapgit_git_definitions=>ty_file + it_status TYPE zif_abapgit_definitions=>ty_results_ts_path + RETURNING + VALUE(rs_status) TYPE zif_abapgit_definitions=>ty_result + RAISING + zcx_abapgit_exception. + + CLASS-DATA: + gi_instance TYPE REF TO lif_selected. + +ENDCLASS. + + +CLASS lcl_selected IMPLEMENTATION. + + METHOD lif_selected~stage_selected. + + DATA ls_file TYPE zif_abapgit_git_definitions=>ty_file. + DATA ls_status LIKE LINE OF it_status. + DATA lo_files TYPE REF TO zcl_abapgit_string_map. + + FIELD-SYMBOLS: + LIKE LINE OF it_local, + LIKE LINE OF lo_files->mt_entries. + + lo_files = ii_event->form_data( ). + + IF lo_files->size( ) = 0. + zcx_abapgit_exception=>raise( 'process_stage_list: empty list' ). + ENDIF. + + check_selected( lo_files ). + + CREATE OBJECT ro_stage. + + LOOP AT lo_files->mt_entries ASSIGNING + "Ignore Files that we don't want to stage, so any errors don't stop the staging process + WHERE v <> zif_abapgit_definitions=>c_method-skip. + + zcl_abapgit_path=>split_file_location( + EXPORTING + iv_fullpath = -k + IMPORTING + ev_path = ls_file-path + ev_filename = ls_file-filename ). + + " you should remember that ls_file is sent from the client and is always uppercase, + " whereas it_status and it_local path could be lower, upper or mixed case. + ls_status = read_status_case_insensitive( + is_file = ls_file + it_status = it_status ). + + " ls_status has the right case, therefore use it also for ls_file + ls_file-path = ls_status-path. + ls_file-filename = ls_status-filename. + + CASE -v. + WHEN zif_abapgit_definitions=>c_method-add. + READ TABLE it_local ASSIGNING + WITH KEY file-path = ls_file-path + file-filename = ls_file-filename. + + IF sy-subrc <> 0. + zcx_abapgit_exception=>raise( |process_stage_list: unknown file { ls_file-path }{ ls_file-filename }| ). + ENDIF. + + ro_stage->add( iv_path = -file-path + iv_filename = -file-filename + is_status = ls_status + iv_data = -file-data ). + WHEN zif_abapgit_definitions=>c_method-ignore. + ro_stage->ignore( iv_path = ls_file-path + iv_filename = ls_file-filename ). + WHEN zif_abapgit_definitions=>c_method-rm. + ro_stage->rm( iv_path = ls_file-path + is_status = ls_status + iv_filename = ls_file-filename ). + WHEN zif_abapgit_definitions=>c_method-skip. + " Do nothing. Never happens as it is filtered out before. Just for completeness. + WHEN OTHERS. + zcx_abapgit_exception=>raise( |process_stage_list: unknown method { -v }| ). + ENDCASE. + ENDLOOP. + ENDMETHOD. + + + METHOD check_selected. + + DATA: + ls_file TYPE zif_abapgit_git_definitions=>ty_file, + lv_pattern TYPE string, + lv_msg TYPE string. + + FIELD-SYMBOLS: + LIKE LINE OF io_files->mt_entries, + LIKE LINE OF io_files->mt_entries. + + " Check all added files if the exist in different paths (packages) without being removed + LOOP AT io_files->mt_entries ASSIGNING WHERE v = zif_abapgit_definitions=>c_method-add. + + " Allow mixed case path, but check filename to lower case + zcl_abapgit_path=>split_file_location( + EXPORTING + iv_fullpath = -k + IMPORTING + ev_path = ls_file-path + ev_filename = ls_file-filename ). + + ls_file-filename = to_lower( ls_file-filename ). + + " Skip packages since they all have identical filenames + IF ls_file-filename <> 'package.devc.xml'. + lv_pattern = '*/' && to_upper( ls_file-filename ). + REPLACE ALL OCCURRENCES OF '#' IN lv_pattern WITH '##'. " for CP + + LOOP AT io_files->mt_entries ASSIGNING + WHERE k CP lv_pattern AND k <> -k AND v <> zif_abapgit_definitions=>c_method-rm. + + lv_msg = |In order to add { to_lower( -k ) }, | && + |you have to remove { to_lower( -k ) }|. + zcx_abapgit_exception=>raise( lv_msg ). + + ENDLOOP. + ENDIF. + ENDLOOP. + + ENDMETHOD. + + + METHOD get_instance. + + IF gi_instance IS INITIAL. + CREATE OBJECT gi_instance TYPE lcl_selected. + ENDIF. + + ro_instance = gi_instance. + + ENDMETHOD. + + + METHOD read_status_case_insensitive. + + FIELD-SYMBOLS: TYPE zif_abapgit_definitions=>ty_result. + + LOOP AT it_status ASSIGNING . + + IF to_upper( -filename ) = to_upper( is_file-filename ) + AND to_upper( -path ) = to_upper( is_file-path ). + rs_status = . + RETURN. + ENDIF. + + ENDLOOP. + + " see https://github.com/abapGit/abapGit/issues/3073 + zcx_abapgit_exception=>raise( + |Unable to stage { is_file-filename }. If the filename contains spaces, this is a known issue.| && + | Consider ignoring or staging the file at a later time.| ). + + ENDMETHOD. + +ENDCLASS. diff --git a/src/ui/pages/zcl_abapgit_gui_page_stage.clas.testclasses.abap b/src/ui/pages/zcl_abapgit_gui_page_stage.clas.testclasses.abap new file mode 100644 index 000000000..c01db67db --- /dev/null +++ b/src/ui/pages/zcl_abapgit_gui_page_stage.clas.testclasses.abap @@ -0,0 +1,439 @@ +*"* use this source file for your ABAP unit test classes +CLASS lcl_mock_event DEFINITION. + + PUBLIC SECTION. + INTERFACES zif_abapgit_gui_event. + METHODS constructor. + METHODS set_file + IMPORTING + iv_key TYPE csequence + iv_value TYPE csequence + RAISING + zcx_abapgit_exception. + + PRIVATE SECTION. + DATA mo_files TYPE REF TO zcl_abapgit_string_map. + +ENDCLASS. + + +CLASS ltcl_stage DEFINITION FINAL FOR TESTING + DURATION SHORT + RISK LEVEL HARMLESS. + + PRIVATE SECTION. + DATA: + mo_mock_event TYPE REF TO lcl_mock_event, + mt_status TYPE zif_abapgit_definitions=>ty_results_ts_path, + mt_local TYPE zif_abapgit_definitions=>ty_files_item_tt, + mo_stage TYPE REF TO zcl_abapgit_stage, + mx_error TYPE REF TO zcx_abapgit_exception. + + METHODS: setup RAISING cx_static_check, + emtpy_list FOR TESTING RAISING cx_static_check, + unknown_file FOR TESTING RAISING cx_static_check, + add FOR TESTING RAISING cx_static_check, + uppercase_path FOR TESTING RAISING cx_static_check, + uppercase_status_path FOR TESTING RAISING cx_static_check, + mixed_case FOR TESTING RAISING cx_static_check, + unknown_method FOR TESTING RAISING cx_static_check, + status_missing FOR TESTING RAISING cx_static_check, + remove FOR TESTING RAISING cx_static_check, + check_selected_rm FOR TESTING RAISING cx_static_check, + skip FOR TESTING RAISING cx_static_check, + when_stage_selected + RAISING + zcx_abapgit_exception, + given_file + IMPORTING + iv_key TYPE csequence + iv_value TYPE csequence + RAISING + zcx_abapgit_exception, + given_status + IMPORTING + iv_path TYPE csequence + iv_filename TYPE csequence, + then_file_is_ignored + IMPORTING + iv_path TYPE csequence + iv_filename TYPE csequence, + then_file_is_added + IMPORTING + iv_path TYPE csequence + iv_filename TYPE csequence, + then_error_contains + IMPORTING + iv_exp_error TYPE csequence, + given_local + IMPORTING + iv_path TYPE csequence + iv_filename TYPE csequence, + then_file_is_removed + IMPORTING + iv_path TYPE csequence + iv_filename TYPE csequence, + then_there_is_nothing_to_stage. + +ENDCLASS. + +CLASS lcl_mock_event IMPLEMENTATION. + + METHOD constructor. + + CREATE OBJECT mo_files. + + ENDMETHOD. + + METHOD zif_abapgit_gui_event~form_data. + + ro_string_map = mo_files. + + ENDMETHOD. + + + METHOD zif_abapgit_gui_event~query. + + ENDMETHOD. + + + METHOD set_file. + + mo_files->set( + iv_key = iv_key + iv_val = iv_value ). + + ENDMETHOD. + +ENDCLASS. + + +CLASS ltcl_stage IMPLEMENTATION. + + METHOD setup. + + CREATE OBJECT mo_mock_event TYPE lcl_mock_event. + + ENDMETHOD. + + + METHOD emtpy_list. + + when_stage_selected( ). + then_error_contains( 'empty list' ). + + ENDMETHOD. + + + METHOD unknown_file. + + given_file( + iv_key = '/src/zddls.ddls.baseinfo' + iv_value = zif_abapgit_definitions=>c_method-add ). + + given_status( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + when_stage_selected( ). + + then_error_contains( 'unknown file' ). + + ENDMETHOD. + + + METHOD add. + + given_file( + iv_key = '/src/zddls.ddls.baseinfo' + iv_value = zif_abapgit_definitions=>c_method-add ). + + given_status( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + given_local( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + when_stage_selected( ). + + then_file_is_added( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + ENDMETHOD. + + + METHOD uppercase_path. + + given_file( + iv_key = '/SRC/ZDDLS.DDLS.BASEINFO' + iv_value = zif_abapgit_definitions=>c_method-ignore ). + + given_status( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + when_stage_selected( ). + + then_file_is_ignored( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + ENDMETHOD. + + + METHOD uppercase_status_path. + + given_file( + iv_key = '/CODE/ZFALV.FUGR.SCREEN_0200.ABAP' + iv_value = zif_abapgit_definitions=>c_method-ignore ). + + given_status( + iv_path = '/CODE/' + iv_filename = 'zfalv.fugr.screen_0200.abap' ). + + given_local( + iv_path = '/CODE/' + iv_filename = 'zfalv.fugr.screen_0200.abap' ). + + when_stage_selected( ). + + then_file_is_ignored( + iv_path = '/CODE/' + iv_filename = 'zfalv.fugr.screen_0200.abap' ). + + ENDMETHOD. + + + METHOD mixed_case. + + given_file( + iv_key = '/SRC/PACKAGE.DEVC.XML' + iv_value = zif_abapgit_definitions=>c_method-add ). + + given_status( + iv_path = '/SrC/' + iv_filename = 'package.devc.xml' ). + + given_local( + iv_path = '/SrC/' + iv_filename = 'package.devc.xml' ). + + when_stage_selected( ). + + then_file_is_added( + iv_path = '/SrC/' + iv_filename = 'package.devc.xml' ). + + ENDMETHOD. + + + METHOD unknown_method. + + given_file( + iv_key = '/SRC/ZDDLS.DDLS.BASEINFO' + iv_value = 'X' ). + + given_status( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + when_stage_selected( ). + + then_error_contains( 'unknown method' ). + + ENDMETHOD. + + + METHOD status_missing. + + given_file( + iv_key = '/SRC/ZDDLS.DDLS.BASEINFO' + iv_value = zif_abapgit_definitions=>c_method-add ). + + when_stage_selected( ). + + then_error_contains( 'Unable to stage' ). + ENDMETHOD. + + + METHOD remove. + + given_file( + iv_key = '/src/zddls.ddls.baseinfo' + iv_value = zif_abapgit_definitions=>c_method-rm ). + + given_status( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + given_local( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + when_stage_selected( ). + + then_file_is_removed( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + ENDMETHOD. + + + METHOD check_selected_rm. + + given_file( + iv_key = '/src/p1/zddls.ddls.baseinfo' + iv_value = zif_abapgit_definitions=>c_method-add ). + + given_file( + iv_key = '/src/p2/zddls.ddls.baseinfo' + iv_value = zif_abapgit_definitions=>c_method-add ). + + given_status( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + given_local( + iv_path = '/src/' + iv_filename = 'zddls.ddls.baseinfo' ). + + when_stage_selected( ). + + then_error_contains( |you have to remove| ). + + ENDMETHOD. + + + METHOD skip. + + given_file( + iv_key = '/src/zddls.ddls.baseinfo' + iv_value = zif_abapgit_definitions=>c_method-skip ). + + when_stage_selected( ). + + then_there_is_nothing_to_stage( ). + + ENDMETHOD. + + + METHOD when_stage_selected. + + TRY. + mo_stage = lcl_selected=>get_instance( )->stage_selected( + ii_event = mo_mock_event + it_status = mt_status + it_local = mt_local ). + + CATCH zcx_abapgit_exception INTO mx_error. + ENDTRY. + + ENDMETHOD. + + + METHOD given_file. + + mo_mock_event->set_file( + iv_key = iv_key + iv_value = iv_value ). + + ENDMETHOD. + + + METHOD given_status. + + DATA ls_status LIKE LINE OF mt_status. + + ls_status-path = iv_path. + ls_status-filename = iv_filename. + INSERT ls_status INTO TABLE mt_status. + + ENDMETHOD. + + + METHOD then_file_is_ignored. + + DATA: lt_stage TYPE zif_abapgit_definitions=>ty_stage_tt. + DATA: ls_exp LIKE LINE OF lt_stage. + + lt_stage = mo_stage->get_all( ). + + ls_exp-file-path = iv_path. + ls_exp-file-filename = iv_filename. + ls_exp-method = zif_abapgit_definitions=>c_method-ignore. + + cl_abap_unit_assert=>assert_table_contains( + line = ls_exp + table = lt_stage ). + + ENDMETHOD. + + + METHOD then_file_is_added. + + DATA: lt_stage TYPE zif_abapgit_definitions=>ty_stage_tt. + DATA: ls_exp LIKE LINE OF lt_stage. + + lt_stage = mo_stage->get_all( ). + + ls_exp-file-path = iv_path. + ls_exp-file-filename = iv_filename. + ls_exp-method = zif_abapgit_definitions=>c_method-add. + ls_exp-status-filename = iv_filename. + ls_exp-status-path = iv_path. + + cl_abap_unit_assert=>assert_table_contains( + line = ls_exp + table = lt_stage ). + + ENDMETHOD. + + + METHOD then_error_contains. + cl_abap_unit_assert=>assert_text_matches( + pattern = iv_exp_error + text = mx_error->get_text( ) ). + ENDMETHOD. + + + METHOD given_local. + + DATA ls_local LIKE LINE OF mt_local. + + ls_local-file-path = iv_path. + ls_local-file-filename = iv_filename. + INSERT ls_local INTO TABLE mt_local. + + ENDMETHOD. + + + METHOD then_file_is_removed. + + DATA: lt_stage TYPE zif_abapgit_definitions=>ty_stage_tt. + DATA: ls_exp LIKE LINE OF lt_stage. + + lt_stage = mo_stage->get_all( ). + + ls_exp-file-path = iv_path. + ls_exp-file-filename = iv_filename. + ls_exp-method = zif_abapgit_definitions=>c_method-rm. + ls_exp-status-filename = iv_filename. + ls_exp-status-path = iv_path. + + cl_abap_unit_assert=>assert_table_contains( + line = ls_exp + table = lt_stage ). + + ENDMETHOD. + + + METHOD then_there_is_nothing_to_stage. + + cl_abap_unit_assert=>assert_initial( mo_stage->get_all( ) ). + + ENDMETHOD. + +ENDCLASS. diff --git a/src/ui/pages/zcl_abapgit_gui_page_stage.clas.xml b/src/ui/pages/zcl_abapgit_gui_page_stage.clas.xml index 65bcbede9..c9142c5c2 100644 --- a/src/ui/pages/zcl_abapgit_gui_page_stage.clas.xml +++ b/src/ui/pages/zcl_abapgit_gui_page_stage.clas.xml @@ -10,6 +10,7 @@ X X X + X