Author: Jacques Nomssi
Submitted: 15.09.2014
Related Links:
- http://scn.sap.com/community/abap/blog/2013/08/21/auto-generating-uml-diagrams-from-abap-code
- http://www.wombling.com/coding/code-for-uml-sequence-diagram-extract-from-abap-for-plantuml/
Synopsis
Some shortcomings of the SAP standard sequence diagram generation from ABAP run-time measurements are addressed:
- a secondary internal table index speeds up parsing
- a custom filter displays calls from standard to custom code in addition to the standard filter logic
- loop compaction produces smaller diagrams
- the plain text output in PlantUML format is an editable representation of the sequence diagram.
Local installation of PlantUML is recommended but not mandatory as a web service is available. The JNet OCX is not needed.
Introduction
UML from ABAP Code
Transaction SAT records the execution of an ABAP program and provides analysis tools for performance, program flow and memory consumption. Measurements can be imported from other SAP systems using either download/upload or RFC. Since SAP Netweaver 7.02, an UML sequence diagram can be generated if the measurement was recorded without aggregation.
- Start transaction SAT -> Evaluate -> Double Click on a measurement; on the next screen press F8 (Display measurement as UML sequence diagram).
A sequence diagram will be displayed if the JNet OCX component is installed (e.g. with SAP GUI). This feature was never fully delivered according to SAP OSS Note 1744063 : development for this function has not yet finished and therefore contains some serious errors including program terminations.
Sequence Diagrams
A sequence diagram visualizes the time dependent (dynamic) interactions between actors in the program. In the ABAP context, actors are function groups, reports, logical databases, global or local classes, etc. An actor has a lifeline and requests a service from another actor by sending a message like PERFORM, CALL METHOD, CALL FUNCTION... Check the texts: agile introduction, UML basics, TAM, sourcemaking
Design-Time Sequence Diagrams
At design time, sequence diagrams visualize collaboration aspects of the desired system actors. Let us compare two diagrams generated for reports from the thread Classical way to ABAP OO style of coding with the same functionality.
The first diagram reveals the logic: select and then display some business data. The second program attempts to separate concerns: the new actor LCL_MAIN exposes the business logic as a service requested by sending a START( ) message. The design pattern here is to evaluate which parts of the system are likely to change and to isolate those parts in new actors with carefully crafted interfaces. With a stable interface, caller and called actors can change independently. As long as they abide by the contract, e.g. method START( ), there is no ripple effect of change through the system. It is tempting to create layers of abstractions everywhere to increase flexibility, I made a more complex proposal in the use case above. The designer must ultimately decide if the increased complexity (new classes) is justified.
Motivation
Sequence Diagrams from Runtime Data
Our aim is to automate the sequence diagrams generation from run-time measurements. When the creation cost is low, the feedback loop is short and we can routinely use sequence diagrams to appreciate the impact of code changes on the overall system design. Object oriented ABAP code e.g. defines classes while the application behavior is better explained by messages exchanged between run-time objects. The appealing visualization revealing actual communication patterns makes it easier to discuss separation of concern, stable interfaces and simpler protocols for objects exposing too much of their inner structure.
Enhance Sequence Diagrams Output
SAP has provided an easy enhancement path for the UML class diagram output (subclass class CL_UML_CLASS_DECORATOR). Some hard coded limitations in the ATRA_UML_DECIDER function module and the CL_ATRA_FACTORY class must be overridden to enhance sequence diagrams by subclassing CL_ATRA_UML (implementation of the IF_ATRA_UML_TOOL interface). I have been using Chris Paine's code and I liked it very much. I now believe his breakthrough design decision was to delegate the graphical representation to an external tool. His code saves a sequence diagram in the format of the free PlantUML tool that requires Java on your machine. PlantUML syntax is human readable and editable plain text. A Chrome browser extension displays the output as soon as the text file is updated.
My first real contribution was to implement loop compaction, i.e. use UML combined fragments to express loops. This is done recursively until no further gain is achieved; it is gratifying to generate a small diagram with LOOP statements from large traces.
A future improvement could be to build a dictionary of non-adjacent blocks and increase trace compaction by using conditionals ALT or OPT combined fragments. I also found the trace parsing could be accelerated by using a secondary internal table index, so the current code replaces the standard sequence diagram display logic completely instead of extending it. I could consume the PlantUML web service in ABAP.
This leads to a short feedback loop and learning is enjoyable... if the image is not too large.
Avoiding Large Traces
Transaction SAT can generate very large traces, with much more than 1 million entries, that are impossible to process: the evaluation is extremely slow or fails with a timeout. This, I presume, is why the size limit is so prominently featured in the variant customizing. But it does not take the largest trace to generate diagrams too large to deliver useful insights into the design. Care is needed if we want to selectively trace program parts and so restrict the generated diagram to the parts we are interested in. We can start a trace
- In Dialog - the measurement variant can be setup to switch measurement off and on manually or with the statement SET RUN TIME ANALYZER ON/OFF, to discard some processing blocks and limit the measurement to given program components.
- In a parallel session - the measurement is then always started/stopped manually
- In a background session (for another user/service) - cf. Chris Paine's blog.
While evaluating an existing trace, we can use the filter dialog (Ctrl+F1) to narrow the processing to selected actors or messages. Chris Paine proposed a custom code filter that also displays calls from standard to custom code.
We can optimize the diagram generation for speed or size: Following advice from a PlantUML developer, I introduced a scale factor. Scale value 0,5 reduces the generated image size by 1/4.
Setup
Class CL_ATRA_UML_HANDLING controls the generation of an UML sequence diagram from a measurement without aggregation in transaction SAT. Method SHOW_SEQ_DIAGRAM will call method DISPLAY_UML( ) of local class LCL_UML_HANDLING. Three steps are needed to override the standard behavior:
- Create a new Include YY_SATRA_INCLUDE using the following code (Netweaver 7.4 SP05 required, a down port to 7.31 is at the end of this document):
*&---------------------------------------------------------------------* *& Include YY_SATRA_INCLUDE *&---------------------------------------------------------------------* * Custom logic to generate a PlantUML sequence diagram from an * ABAP trace recorded in transaction SAT without aggregation. * * Usage: * Start transaction SAT -> Evaluate -> Double Click on a measurement; * on the next screen press F8 / click icon to Display sequence diagram * * Setup: * 1) to add the following local classes to class CL_ATRA_UML_HANDLING * create a source code plugin in include LS_ABAP_TRACE_DATAD07 * e.g. as implicit enhancement at the end: * *ENHANCEMENT 1 YY_SATR_SEQUENCE. "active version * INCLUDE YY_SATRA_INCLUDE. *ENDENHANCEMENT. * * 2) add an implicit enhancement at the beginning of Method * SHOW_SEQ_DIAGRAM( ) of CL_ATRA_UML_HANDLING to replace the logic: * *ENHANCEMENT 2 YY_SATR_SEQUENCE. "active version * TRY. * lcl_sequence=>to_diagram( lcl_configuration=>query( ) )->output( ). * CATCH cx_dynamic_check INTO DATA(lx_error). * MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. "#EC CI_USE_WANTED * ENDTRY. * RETURN. *ENDENHANCEMENT. *&---------------------------------------------------------------------* * Note: Unit test execution is disabled for standard classes, but you * can add this include to a custom program and run the tests there. * Glossary: module = sequence diagram actor * event = sequence diagram message * Convention: Most factory methods have NEW in their name * Valid actor type: program, function group, class, logical databases CONSTANTS: c_type_class TYPE trobjtype VALUE 'CLAS', c_type_prog TYPE trobjtype VALUE 'PROG', c_type_fugr TYPE trobjtype VALUE 'FUGR', c_type_ldba TYPE trobjtype VALUE 'LDBA', " Log. DB c_type_tran TYPE trobjtype VALUE 'TRAN'. * Caller / Called object TYPES: BEGIN OF ts_object, global TYPE program, local TYPE program, type TYPE trobjtype, instance TYPE satr_de_instance, END OF ts_object. TYPES tv_key TYPE i. " Object numbers in call TYPES: BEGIN OF ts_path, caller TYPE tv_key, called TYPE tv_key, END OF ts_path. * Trace step (Message): Caller -> Called object, message name (msg) TYPES: BEGIN OF ts_message, id TYPE satr_de_id, from_level TYPE satr_de_ebene. INCLUDE TYPE ts_path AS path. TYPES: msg TYPE seocpdname, END OF ts_message. TYPES tt_message TYPE STANDARD TABLE OF ts_message WITH KEY id WITH NON-UNIQUE SORTED KEY key_msg COMPONENTS table_line. INTERFACE lif_unit_test. ENDINTERFACE. * Message - Called object information TYPES: BEGIN OF ts_call. INCLUDE TYPE ts_object AS object. TYPES: mod TYPE seocpdname, END OF ts_call. TYPES: BEGIN OF ts_seq, called TYPE ts_call, " Called fields aus_tabix TYPE i, " Index for Table Access END OF ts_seq. * Target structure for call hierarchy used in sequence diagrams, with the fields * ID - indicator describes the type of the recorded event * (call method, perform, call function, call screen, database operation,#) * from_level - The call level in the call hierarchy (an integer) * caller - source of the message (program, function group, class, etc.) * called - receiver of the message * system - system flag " only used in custom filter TYPES: BEGIN OF ts_sat, id TYPE satr_de_id, " General Information. from_level TYPE satr_de_ebene, caller TYPE ts_call. " Caller fields INCLUDE TYPE ts_seq AS seq. TYPES: system TYPE satr_de_sysflag, END OF ts_sat. TYPES tv_text TYPE char128. * Each actor (sender or receiver of a message) in the sequence diagram has a life line TYPES: BEGIN OF ts_lifeline, index TYPE tv_key, " secondary index in table of ts_actor label TYPE tv_text, END OF ts_lifeline. INTERFACE lif_actors. TYPES tt_lifeline TYPE SORTED TABLE OF ts_lifeline WITH UNIQUE KEY index. METHODS lifelines RETURNING VALUE(rt_lifeline) TYPE tt_lifeline. METHODS short_text IMPORTING is_message TYPE ts_message RETURNING VALUE(rv_text) TYPE tv_text. METHODS new_path IMPORTING is_sat TYPE ts_sat RETURNING VALUE(rs_path) TYPE ts_path. ENDINTERFACE. TYPES tv_element TYPE char30. * Abstract UML sequence diagram code generator - buffers the UML text * representation and pass the complete string back to the caller CLASS lcl_uml DEFINITION ABSTRACT FRIENDS lif_unit_test. PUBLIC SECTION. TYPES tv_uml TYPE i. CONSTANTS: c_uml_plant TYPE tv_uml VALUE 0, c_uml_graph TYPE tv_uml VALUE 2, c_uml_mscgen TYPE tv_uml VALUE 3, c_uml_mock TYPE tv_uml VALUE 9. CLASS-METHODS new IMPORTING iv_uml TYPE tv_uml ii_actors TYPE REF TO lif_actors RETURNING VALUE(ro_uml) TYPE REF TO lcl_uml. METHODS top. METHODS bottom RETURNING VALUE(rv_diagram) TYPE string. "! Draws a message between two objects, with the given label. "! Self messages (where an object sends a message to itself) are supported. METHODS message IMPORTING is_message TYPE ts_message. METHODS return ABSTRACT IMPORTING iv_from TYPE i iv_to TYPE i. METHODS begin_loop ABSTRACT IMPORTING iv_from TYPE program OPTIONAL iv_times TYPE sytabix iv_name TYPE tv_element OPTIONAL. METHODS end_loop ABSTRACT IMPORTING iv_to TYPE program OPTIONAL iv_name TYPE tv_element OPTIONAL. PROTECTED SECTION. CONSTANTS c_txt_std TYPE string VALUE 'Standard SAP code has called some custom code ' ##NO_TEXT. DATA mv_diagram TYPE string. DATA mi_actors TYPE REF TO lif_actors. METHODS add IMPORTING iv_code TYPE string. METHODS call ABSTRACT IMPORTING is_message TYPE ts_message. METHODS skip_note ABSTRACT IMPORTING is_path TYPE ts_path. METHODS header ABSTRACT. METHODS footer ABSTRACT. "! Defines an object is_lifeline-index, labeled on the diagram as is_lifeline-text METHODS participant ABSTRACT IMPORTING is_lifeline TYPE ts_lifeline iv_create TYPE xsdboolean DEFAULT abap_false. "! Object lifeline completion METHODS complete IMPORTING is_lifeline TYPE ts_lifeline. METHODS separator. " between two participants METHODS delimiter. " end of statement ENDCLASS. * iterator over an internal table of messages allowing to * - skip a given number of entries (default 1). * - look ahead to read the next value without updating the index * - is_first to known if the current values is the first CLASS lcl_messages DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. CONSTANTS c_default_level TYPE satr_de_ebene VALUE 1. METHODS constructor IMPORTING it_messages TYPE tt_message iv_start TYPE sytabix DEFAULT 1 iv_stop TYPE sytabix OPTIONAL. METHODS is_first RETURNING VALUE(rv_flag) TYPE xsdboolean. METHODS has_next RETURNING VALUE(rv_flag) TYPE xsdboolean. METHODS skip IMPORTING iv_count TYPE i DEFAULT 1. METHODS next RETURNING VALUE(rs_data) TYPE ts_message RAISING cx_sy_itab_error. METHODS first_message RETURNING VALUE(rs_message) TYPE ts_message. METHODS next_level RETURNING VALUE(rv_level) TYPE satr_de_ebene. PROTECTED SECTION. DATA mv_idx TYPE sytabix. DATA mv_size TYPE sytabix. DATA mt_list TYPE tt_message. PRIVATE SECTION. CONSTANTS c_first_key TYPE tv_key VALUE 1. " Code of very first call DATA mv_first_level TYPE i. ENDCLASS. CLASS lcl_stack DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. TYPES: BEGIN OF ts_call_level, actor_key TYPE tv_key, from_level TYPE satr_de_ebene, END OF ts_call_level. METHODS push IMPORTING is_ref TYPE ts_call_level. METHODS pop RETURNING VALUE(rs_to) TYPE ts_call_level. PROTECTED SECTION. TYPES BEGIN OF ts_level. INCLUDE TYPE ts_call_level AS call_level. TYPES next TYPE REF TO data. TYPES END OF ts_level. DATA mr_top TYPE REF TO ts_level. ENDCLASS. CLASS lcl_uml_stack DEFINITION INHERITING FROM lcl_stack FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING io_uml TYPE REF TO lcl_uml. METHODS call IMPORTING is_message TYPE ts_message. METHODS return IMPORTING iv_to_level TYPE satr_de_ebene. PROTECTED SECTION. DATA mv_previous_level TYPE satr_de_ebene. DATA mo_uml TYPE REF TO lcl_uml. METHODS return_to IMPORTING iv_level TYPE satr_de_ebene. ENDCLASS. CLASS lcl_call_stack DEFINITION INHERITING FROM lcl_uml_stack FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING io_uml TYPE REF TO lcl_uml io_messages TYPE REF TO lcl_messages. "! traverses the list of messages list and generates an "UML as text" diagram METHODS to_uml RETURNING VALUE(rv_diagram) TYPE string. PROTECTED SECTION. DATA mo_messages TYPE REF TO lcl_messages. METHODS message IMPORTING is_message TYPE ts_message iv_idx TYPE sytabix. ENDCLASS. * Trace cycle description TYPES: BEGIN OF ts_cycle, start TYPE sytabix, end TYPE sytabix, last TYPE sytabix, times TYPE i, END OF ts_cycle. CLASS lcl_call_stack_compact DEFINITION INHERITING FROM lcl_call_stack FRIENDS lif_unit_test. PUBLIC SECTION. TYPES tt_cycle TYPE SORTED TABLE OF ts_cycle WITH NON-UNIQUE KEY start. METHODS constructor IMPORTING io_uml TYPE REF TO lcl_uml io_messages TYPE REF TO lcl_messages it_cycles TYPE tt_cycle. PROTECTED SECTION. DATA mt_cycle TYPE tt_cycle. METHODS message REDEFINITION. METHODS begin IMPORTING iv_idx TYPE sytabix iv_from TYPE tv_key. METHODS end IMPORTING iv_idx TYPE sytabix iv_to TYPE tv_key RETURNING VALUE(rv_step) TYPE sytabix. PRIVATE SECTION. METHODS name IMPORTING iv_start TYPE sytabix iv_end TYPE sytabix RETURNING VALUE(rv_name) TYPE tv_element. ENDCLASS. CLASS lcl_actors DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. INTERFACES lif_actors. PROTECTED SECTION. TYPES: BEGIN OF ts_actor. INCLUDE TYPE ts_object AS object. INCLUDE TYPE ts_lifeline AS lifeline. TYPES: END OF ts_actor. TYPES tt_actor TYPE SORTED TABLE OF ts_actor WITH UNIQUE KEY type instance global local WITH UNIQUE SORTED KEY obj_nr COMPONENTS index. DATA mt_actor TYPE tt_actor. DATA mv_last_number TYPE i. METHODS index_of IMPORTING is_object TYPE ts_object RETURNING VALUE(rv_idx) TYPE sytabix. METHODS next_number RETURNING VALUE(rv_key) TYPE tv_key. METHODS lifeline IMPORTING is_object TYPE ts_object RETURNING VALUE(rv_text) TYPE tv_text. METHODS call_short_text IMPORTING is_message TYPE ts_message RETURNING VALUE(rv_text) TYPE tv_text. METHODS class_name IMPORTING iv_index TYPE sytabix RETURNING VALUE(rv_short_name) TYPE tv_text. METHODS put IMPORTING is_object TYPE ts_object RETURNING VALUE(rv_key) TYPE tv_key. ENDCLASS. TYPES tv_scale TYPE perct. CONSTANTS c_default_scale TYPE tv_scale VALUE '0.5'. TYPES: BEGIN OF ts_diagram_config, local_path TYPE string, java_jar TYPE string, java_appl TYPE string, server_url TYPE string, output_mode TYPE char01, skip_dialog TYPE flag, compact_trace TYPE flag, system_events TYPE flag, scale TYPE tv_scale, pattern TYPE sci_pattern, handwritten TYPE flag, shadowing TYPE flag, display_source TYPE flag, teoz_architecture TYPE flag, uml_format TYPE lcl_uml=>tv_uml, progress TYPE flag, END OF ts_diagram_config. CLASS lcl_diagram_text DEFINITION ABSTRACT FRIENDS lif_unit_test. PUBLIC SECTION. CONSTANTS: c_mode_aut TYPE char01 VALUE 'T', " for ABAP Unit Test c_mode_url TYPE char01 VALUE 'U', c_mode_txt TYPE char01 VALUE space, c_mode_exe TYPE char01 VALUE 'E'. CLASS-METHODS new IMPORTING is_cfg TYPE ts_diagram_config iv_text TYPE string RETURNING VALUE(ro_diagram) TYPE REF TO lcl_diagram_text. METHODS constructor IMPORTING iv_diagram TYPE string iv_mode TYPE char01. METHODS output RAISING cx_dynamic_check. PROTECTED SECTION. DATA mv_diagram TYPE string. DATA ms_cfg TYPE ts_diagram_config. METHODS save_file IMPORTING iv_mode TYPE char01 RETURNING VALUE(rv_fname) TYPE string RAISING cx_dynamic_check. METHODS to_xstring IMPORTING iv_string TYPE string RETURNING VALUE(rv_xstring) TYPE xstring RAISING cx_dynamic_check. ENDCLASS. INTERFACE lif_trace_filter. METHODS accepts IMPORTING is_sat TYPE ts_sat RETURNING VALUE(rv_flag) TYPE xsdboolean. ENDINTERFACE. CLASS lcl_file_name DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. CLASS-METHODS new IMPORTING iv_mode TYPE char01 RETURNING VALUE(ro_file) TYPE REF TO lcl_file_name. METHODS constructor IMPORTING iv_mode TYPE char01. METHODS dialog RETURNING VALUE(rv_user_action) TYPE i. METHODS get_prefix RETURNING VALUE(rv_name) TYPE string RAISING cx_dynamic_check. METHODS get_fullpath RETURNING VALUE(rv_name) TYPE string. PROTECTED SECTION. TYPES: BEGIN OF ts_fullpath, title TYPE string, name TYPE string, ext TYPE string, path TYPE string, END OF ts_fullpath. DATA ms_file TYPE ts_fullpath. ENDCLASS. CLASS lcl_file_test_name DEFINITION INHERITING FROM lcl_file_name FRIENDS lif_unit_test. PUBLIC SECTION. METHODS dialog REDEFINITION. ENDCLASS. CLASS lcl_file DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. CLASS-METHODS download IMPORTING iv_data TYPE xstring io_name TYPE REF TO lcl_file_name RETURNING VALUE(rv_subrc) TYPE sysubrc. ENDCLASS. CLASS lcl_diagram_plant_uml DEFINITION INHERITING FROM lcl_diagram_text FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING is_cfg TYPE ts_diagram_config iv_diagram TYPE string. METHODS output REDEFINITION. CLASS-METHODS get_java_path RETURNING VALUE(rv_fullpath) TYPE string. PROTECTED SECTION. TYPES tv_base64 TYPE c LENGTH 65. CONSTANTS: c_charset_standard TYPE tv_base64 VALUE 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' ##NO_TEXT, c_charset_plantuml TYPE tv_base64 VALUE '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_0' ##NO_TEXT. METHODS to_url IMPORTING iv_base_url TYPE string RETURNING VALUE(rv_url) TYPE string RAISING cx_dynamic_check. METHODS to_png IMPORTING io_name TYPE REF TO lcl_file_name RETURNING VALUE(rv_name) TYPE string. METHODS display_html RAISING cx_dynamic_check. METHODS local_plant_uml RAISING cx_dynamic_check. METHODS parameter_string IMPORTING io_name TYPE REF TO lcl_file_name RETURNING VALUE(rv_param) TYPE string. METHODS png_file_name IMPORTING io_name TYPE REF TO lcl_file_name RETURNING VALUE(rv_name) TYPE string. METHODS encoded_url_suffix RETURNING VALUE(rv_url) TYPE string RAISING cx_dynamic_check. METHODS source RETURNING VALUE(rv_source) TYPE string. METHODS show_html IMPORTING iv_html TYPE string RAISING cx_dynamic_check. ENDCLASS. CLASS lcl_configuration DEFINITION CREATE PRIVATE FRIENDS lif_unit_test. PUBLIC SECTION. CLASS-METHODS: get RETURNING VALUE(rs_cfg) TYPE ts_diagram_config, query RETURNING VALUE(rs_cfg) TYPE ts_diagram_config, class_constructor. PRIVATE SECTION. TYPES: BEGIN OF ts_param, local_path TYPE localfile, java_jar TYPE localfile, java_appl TYPE localfile, server_url TYPE localfile, output_mode TYPE char01, skip_dialog TYPE flag, compact_trace TYPE flag, system_events TYPE flag, scale TYPE perct, handwritten TYPE flag, shadowing TYPE flag, teoz_architecture TYPE flag, display_source TYPE flag, pattern TYPE sci_pattern, uml_format TYPE int4, progress TYPE flag, END OF ts_param. METHODS get_attributes RETURNING VALUE(rt_attr) TYPE sci_atttab. METHODS to_radiobutton. METHODS from_radiobutton. CLASS-DATA gs_cfg TYPE ts_param. DATA: mv_mode_url TYPE flag VALUE 'X', mv_mode_exe TYPE flag, mv_mode_txt TYPE flag. METHODS dialog. ENDCLASS. CLASS lcl_filter DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. INTERFACES lif_trace_filter. ALIASES accepts FOR lif_trace_filter~accepts. CLASS-METHODS new IMPORTING is_cfg TYPE ts_diagram_config RETURNING VALUE(ri_filter) TYPE REF TO lif_trace_filter. METHODS constructor IMPORTING is_cfg TYPE ts_diagram_config. PROTECTED SECTION. DATA ms_cfg TYPE ts_diagram_config. DATA mv_config_id_reject TYPE string. ENDCLASS. CLASS lcl_filter_null DEFINITION INHERITING FROM lcl_filter FRIENDS lif_unit_test. PUBLIC SECTION. METHODS accepts REDEFINITION. ENDCLASS. CLASS lcl_filter_custom DEFINITION INHERITING FROM lcl_filter FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING is_cfg TYPE ts_diagram_config. METHODS accepts REDEFINITION. PRIVATE SECTION. DATA mv_regex TYPE string. METHODS customer_namespace IMPORTING iv_name TYPE program RETURNING VALUE(rv_flag) TYPE xsdboolean. METHODS match_pattern IMPORTING iv_name TYPE program RETURNING VALUE(rv_flag) TYPE xsdboolean. ENDCLASS. CLASS lcl_bag DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. METHODS add IMPORTING iv_entry TYPE sytabix. METHODS remove IMPORTING iv_entry TYPE sytabix. METHODS contains IMPORTING iv_entry TYPE sytabix RETURNING VALUE(rv_flag) TYPE xsdboolean. PRIVATE SECTION. DATA mt_index TYPE SORTED TABLE OF sytabix WITH UNIQUE DEFAULT KEY. ENDCLASS. "! Class information CLASS lcl_class_name DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. METHODS: is_global IMPORTING iv_name TYPE sobj_name RETURNING VALUE(rv_flag) TYPE xsdboolean, external CHANGING cv_clas TYPE program, technical IMPORTING iv_name TYPE program RETURNING VALUE(rv_class) TYPE program, to_internal IMPORTING iv_name TYPE program RETURNING VALUE(rv_class) TYPE program. PRIVATE SECTION. * Cache proxy for method IS_GLOBAL( ) TYPES: BEGIN OF ts_class, name TYPE sobj_name, global TYPE flag, END OF ts_class. TYPES tt_class TYPE HASHED TABLE OF ts_class WITH UNIQUE KEY name. DATA mt_class TYPE tt_class. METHODS fetch_is_global IMPORTING iv_name TYPE sobj_name RETURNING VALUE(rv_flag) TYPE xsdboolean. ENDCLASS. *----------------------------------------------------------------------* "! Parse call hierarchy in ABAP trace stored as static attributes of "! standard class CL_ATRA_TOOL_SE30_MAIN, extract trace details needed "! for UML sequence diagram generation from internal tables "! IT_TRACEPROG - Table of Recorded Program Names "! IT_TRACETEXT - Table of TRACE Text Elements "! IT_TRACEMETH - Table of Recorded Method Names "! IT_AUSTAB_HIER - Call Hierarchy table with All TRACE Information CLASS lcl_abap_trace DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. CONSTANTS c_create_method TYPE seocpdname VALUE 'CONSTRUCTOR'. METHODS constructor. METHODS parse RETURNING VALUE(rv_flag) TYPE xsdboolean. PROTECTED SECTION. TYPES: BEGIN OF ts_event. INCLUDE TYPE ts_object AS object. TYPES: event TYPE seocpdname, " Called component of object system TYPE satr_de_sysflag, END OF ts_event. DATA mo_atra TYPE REF TO cl_atra_tool_se30_main. DATA mr_src TYPE REF TO satr_austab_gesamt. DATA ms_target TYPE ts_event. " Called actor DATA mo_class_name TYPE REF TO lcl_class_name. METHODS module IMPORTING iv_index TYPE satr_de_pgix RETURNING VALUE(rv_subrc) TYPE sysubrc. METHODS event RETURNING VALUE(rv_subrc) TYPE sysubrc. * the following methods are redefined in test double METHODS set_constructor. METHODS set_meth. METHODS set_form. METHODS set_function_module. METHODS set_submit. METHODS set_tran. PRIVATE SECTION. METHODS set_local_class IMPORTING iv_progname TYPE program. ENDCLASS. INTERFACE lif_collector. "! Add trace entry to collection METHODS collect IMPORTING is_sat TYPE ts_sat RETURNING VALUE(rv_flag) TYPE xsdboolean. ENDINTERFACE. *----------------------------------------------------------------------* * CLASS lcl_trace DEFINITION * *----------------------------------------------------------------------* CLASS lcl_trace DEFINITION INHERITING FROM lcl_abap_trace FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor. METHODS fill IMPORTING ii_collector TYPE REF TO lif_collector. PROTECTED SECTION. CONSTANTS c_event_entry TYPE satr_de_event VALUE '>'. TYPES tt_seq_k1 TYPE STANDARD TABLE OF ts_seq WITH KEY aus_tabix " Performance WITH NON-UNIQUE SORTED KEY k1 COMPONENTS aus_tabix. DATA mt_caller TYPE tt_seq_k1. PRIVATE SECTION. DATA mo_bag TYPE REF TO lcl_bag. METHODS skip_record. METHODS put_caller IMPORTING is_sat TYPE ts_sat. METHODS get_sat RETURNING VALUE(rs_sat) TYPE ts_sat. METHODS new_record RETURNING VALUE(rv_flag) TYPE xsdboolean. ENDCLASS. CLASS lcl_progress_indicator DEFINITION CREATE PRIVATE FRIENDS lif_unit_test. PUBLIC SECTION. CONSTANTS: c_enable TYPE char01 VALUE 'X', c_disable TYPE char01 VALUE '0'. CLASS-METHODS new IMPORTING is_cfg TYPE ts_diagram_config OPTIONAL RETURNING VALUE(ro_prog) TYPE REF TO lcl_progress_indicator. CLASS-METHODS echo IMPORTING iv_percentage TYPE numeric DEFAULT 0 iv_text TYPE clike. METHODS set_mode IMPORTING iv_mode TYPE char01. METHODS restore. PRIVATE SECTION. CONSTANTS c_param_id_sin TYPE char20 VALUE 'SIN'. DATA mv_mode TYPE char01. METHODS constructor IMPORTING iv_progress TYPE flag. METHODS get_mode RETURNING VALUE(rv_mode) TYPE char01. ENDCLASS. *-----------------------------------------------------------------------------------------* INTERFACE lif_cycles. METHODS collect IMPORTING is_cycle TYPE ts_cycle. METHODS accepts IMPORTING iv_index TYPE sytabix RETURNING VALUE(rv_flag) TYPE xsdboolean. ENDINTERFACE. CLASS lcl_trace_index DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. * to benefit from the performance of the FIND IN TABLE statement * we implement a poor man's table index management here TYPES tv_x_tabix TYPE x LENGTH 4. TYPES tt_x_index TYPE STANDARD TABLE OF tv_x_tabix. METHODS constructor IMPORTING it_trace TYPE tt_message ii_cycles TYPE REF TO lif_cycles. METHODS to_trace CHANGING ct_trace TYPE tt_message. METHODS shrink IMPORTING iv_pass TYPE syindex RETURNING VALUE(rv_shrink) TYPE xsdboolean. PROTECTED SECTION. TYPES tt_components TYPE SORTED TABLE OF ts_message WITH UNIQUE KEY id from_level caller called msg. METHODS to_index. METHODS compact IMPORTING VALUE(it_xindex) TYPE tt_x_index EXPORTING et_xindex TYPE tt_x_index RETURNING VALUE(rv_shrink) TYPE xsdboolean. PRIVATE SECTION. DATA mt_trace TYPE tt_message. DATA mi_cycles TYPE REF TO lif_cycles. DATA mt_components TYPE tt_components. DATA mt_xindex TYPE tt_x_index. ENDCLASS. CLASS lcl_sequence DEFINITION CREATE PRIVATE FRIENDS lif_unit_test. PUBLIC SECTION. INTERFACES lif_collector. CLASS-METHODS to_diagram IMPORTING is_cfg TYPE ts_diagram_config RETURNING VALUE(ro_diagram) TYPE REF TO lcl_diagram_text. METHODS get_calls RETURNING VALUE(ro_calls) TYPE REF TO lcl_call_stack. PROTECTED SECTION. DATA mi_filter TYPE REF TO lif_trace_filter. DATA mi_actors TYPE REF TO lif_actors. DATA mt_trace TYPE tt_message. DATA ms_previous TYPE ts_sat. DATA mo_uml TYPE REF TO lcl_uml. DATA mv_compact_trace TYPE flag. METHODS constructor IMPORTING is_cfg TYPE ts_diagram_config. METHODS add IMPORTING is_sat TYPE ts_sat. METHODS to_call_stack RETURNING VALUE(ro_calls) TYPE REF TO lcl_call_stack. METHODS link_to_previous IMPORTING is_sat TYPE ts_sat RETURNING VALUE(rs_sat) TYPE ts_sat. ENDCLASS. CLASS lcl_uml_factory DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. CLASS-METHODS new IMPORTING iv_compact_trace TYPE xsdboolean RETURNING VALUE(ro_factory) TYPE REF TO lcl_uml_factory. CLASS-METHODS new_calls IMPORTING iv_compact_trace TYPE xsdboolean it_trace TYPE tt_message io_uml TYPE REF TO lcl_uml RETURNING VALUE(ro_calls) TYPE REF TO lcl_call_stack. PROTECTED SECTION. METHODS new_call_stack IMPORTING io_uml TYPE REF TO lcl_uml it_trace TYPE tt_message RETURNING VALUE(ro_calls) TYPE REF TO lcl_call_stack. METHODS new_iterator IMPORTING it_trace TYPE tt_message RETURNING VALUE(ro_iterator) TYPE REF TO lcl_messages. METHODS fold IMPORTING it_trace TYPE tt_message EXPORTING et_messages TYPE tt_message. ENDCLASS. CLASS lcl_shrinkage DEFINITION CREATE PRIVATE FRIENDS lif_unit_test. PUBLIC SECTION. CLASS-METHODS shrink_references CHANGING ct_cycles TYPE lcl_call_stack_compact=>tt_cycle. PRIVATE SECTION. TYPES tt_sorted_loop TYPE SORTED TABLE OF ts_cycle WITH UNIQUE KEY start end last times. DATA mt_shrinked_loop TYPE tt_sorted_loop. DATA mv_tabix TYPE sytabix. DATA mt_cycles TYPE lcl_call_stack_compact=>tt_cycle. DATA ms_stop TYPE ts_cycle. METHODS constructor IMPORTING it_cycles TYPE lcl_call_stack_compact=>tt_cycle. METHODS insert IMPORTING ls_cycle TYPE ts_cycle. METHODS renew RETURNING VALUE(ro_shrink) TYPE REF TO lcl_shrinkage. METHODS rebuild RETURNING VALUE(ro_shrink) TYPE REF TO lcl_shrinkage. ENDCLASS. CLASS lcl_uml_compactor DEFINITION INHERITING FROM lcl_uml_factory FRIENDS lif_unit_test. PUBLIC SECTION. INTERFACES lif_cycles. PROTECTED SECTION. METHODS new_call_stack REDEFINITION. METHODS fold REDEFINITION. PRIVATE SECTION. DATA mt_cycles TYPE lcl_call_stack_compact=>tt_cycle. ENDCLASS. "! Class LCL_PATTERN finds repetitions (cycles/loops) in the internal table. "! Method CLONE( ) generates new iterators starting at the next repeated "! entry in the given trace table and recursively checks within the match. "! When a loop is found, an entry with "! the index of the starting entry of the repeated pattern (e.g. 1) "! the index of the end entry of the repetition (e.g. 4) "! the number of loop counts (e.g. 3) "! the index of the last entry when the loop is exited (e.g. 12) "! is stored using the COLLECT( ) method of interface LIF_CYCLES. CLASS lcl_pattern DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING it_xindex TYPE lcl_trace_index=>tt_x_index iv_start TYPE sytabix DEFAULT 1 iv_stop TYPE sytabix OPTIONAL. METHODS has_next RETURNING VALUE(rv_flag) TYPE xsdboolean. METHODS skip IMPORTING iv_count TYPE i DEFAULT 1. METHODS detect_cycles. CLASS-METHODS new IMPORTING it_xindex TYPE lcl_trace_index=>tt_x_index iv_start TYPE sytabix DEFAULT 1 iv_stop TYPE sytabix OPTIONAL ii_cycles TYPE REF TO lif_cycles RETURNING VALUE(ro_pattern) TYPE REF TO lcl_pattern. PROTECTED SECTION. CONSTANTS c_max_step TYPE i VALUE 1024. DATA mi_cycles TYPE REF TO lif_cycles. METHODS clone IMPORTING iv_start TYPE sytabix DEFAULT 1 iv_stop TYPE sytabix OPTIONAL PREFERRED PARAMETER iv_start RETURNING VALUE(ro_loop) TYPE REF TO lcl_pattern. METHODS next_occurrence_from IMPORTING iv_from TYPE sytabix RETURNING VALUE(rv_row) TYPE sytabix. METHODS occurs_in IMPORTING iv_start TYPE sytabix iv_end TYPE sytabix RETURNING VALUE(rv_same) TYPE xsdboolean. * Condense loop logic METHODS match_pattern IMPORTING iv_end TYPE sytabix RETURNING VALUE(rv_match) TYPE xsdboolean. PRIVATE SECTION. DATA mv_idx TYPE sytabix. DATA mv_size TYPE sytabix. DATA mt_xindex TYPE lcl_trace_index=>tt_x_index. ENDCLASS. * PlantUML code generator - CLASS lcl_uml_plant DEFINITION INHERITING FROM lcl_uml FRIENDS lif_unit_test. PUBLIC SECTION. METHODS return REDEFINITION. METHODS begin_loop REDEFINITION. METHODS end_loop REDEFINITION. PROTECTED SECTION. CONSTANTS c_min_scale TYPE tv_scale VALUE '0.1'. METHODS call REDEFINITION. METHODS parameters IMPORTING is_cfg TYPE ts_diagram_config RETURNING VALUE(rv_param) TYPE string. METHODS participant REDEFINITION. METHODS header REDEFINITION. METHODS footer REDEFINITION. METHODS skip_note REDEFINITION. PRIVATE SECTION. DATA mv_param TYPE string. METHODS add_param IMPORTING iv_cond TYPE xsdboolean DEFAULT abap_true iv_command TYPE string. METHODS scale IMPORTING is_cfg TYPE ts_diagram_config RETURNING VALUE(rv_scale) TYPE string. ENDCLASS. * UMLGraph code generator - CLASS lcl_uml_graph DEFINITION INHERITING FROM lcl_uml FRIENDS lif_unit_test. PUBLIC SECTION. METHODS return REDEFINITION. METHODS begin_loop REDEFINITION. METHODS end_loop REDEFINITION. METHODS top REDEFINITION. PROTECTED SECTION. METHODS call REDEFINITION. METHODS participant REDEFINITION. METHODS header REDEFINITION. METHODS complete REDEFINITION. METHODS footer REDEFINITION. METHODS skip_note REDEFINITION. ENDCLASS. CLASS lcl_uml_mscgen DEFINITION INHERITING FROM lcl_uml FRIENDS lif_unit_test. PUBLIC SECTION. METHODS return REDEFINITION. METHODS begin_loop REDEFINITION. METHODS end_loop REDEFINITION. PROTECTED SECTION. METHODS call REDEFINITION. METHODS header REDEFINITION. METHODS footer REDEFINITION. METHODS participant REDEFINITION. METHODS skip_note REDEFINITION. METHODS separator REDEFINITION. METHODS delimiter REDEFINITION. ENDCLASS. CLASS lcl_logger DEFINITION CREATE PRIVATE FRIENDS lif_unit_test. PUBLIC SECTION. TYPES: BEGIN OF ts_log, method TYPE char30, params TYPE string, END OF ts_log. TYPES tt_log TYPE STANDARD TABLE OF ts_log WITH EMPTY KEY. CLASS-METHODS new RETURNING VALUE(ro_log) TYPE REF TO lcl_logger. METHODS info IMPORTING is_log TYPE ts_log. METHODS verify IMPORTING it_exp TYPE tt_log iv_msg TYPE string. PROTECTED SECTION. DATA mt_log TYPE tt_log. ENDCLASS. * Mock UML object CLASS lcl_uml_logger DEFINITION INHERITING FROM lcl_uml FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor. METHODS return REDEFINITION. METHODS begin_loop REDEFINITION. METHODS end_loop REDEFINITION. PROTECTED SECTION. METHODS call REDEFINITION. METHODS participant REDEFINITION. METHODS header REDEFINITION. METHODS footer REDEFINITION. METHODS skip_note REDEFINITION. PRIVATE SECTION. DATA mo_log TYPE REF TO lcl_logger. METHODS log IMPORTING iv_method TYPE char30 iv_params TYPE string OPTIONAL. ENDCLASS. *--------------------------------------------------------------------------------* * Object Identifier in a call CONSTANTS: c_id_func TYPE satr_de_id VALUE 'U', " Function module c_id_form TYPE satr_de_id VALUE 'F', c_id_class TYPE satr_de_id VALUE 'R', " Create object´/ Run time management c_id_method TYPE satr_de_id VALUE 'm', c_id_record TYPE satr_de_id VALUE 'O', " Record of measurement c_id_program TYPE satr_de_id VALUE 'P', " Program c_id_skip TYPE satr_de_id VALUE 'K'. " skip over SAP code CONSTANTS: c_id_load TYPE satr_de_id VALUE 'L', " Load c_id_module TYPE satr_de_id VALUE 'M', " Module call c_id_report TYPE satr_de_id VALUE 'C', " CALL screen/report c_id_dynpro TYPE satr_de_id VALUE 'A', " PBO / PAI c_id_flow TYPE satr_de_id VALUE 'E'. " Flow Logic CONSTANTS: " to be skipped c_id_title TYPE satr_de_id VALUE 'Z', " Dyn. Assign, Status/Title, SCAN, SET Local c_id_message TYPE satr_de_id VALUE 'W', " Message/Continue/Wait c_id_db_oper TYPE satr_de_id VALUE 'V', " Database Operation c_id_sap_sql TYPE satr_de_id VALUE 'S', " Open SQL c_id_db_sql TYPE satr_de_id VALUE 'Q', " Native SQL c_id_import TYPE satr_de_id VALUE 'X', " Export/Import c_id_db_tech TYPE satr_de_id VALUE 'N'. " Buffer/Technical DB Operation CLASS lcl_bag IMPLEMENTATION. METHOD add. INSERT iv_entry INTO TABLE mt_index. ENDMETHOD. METHOD remove. DELETE TABLE mt_index FROM iv_entry. ENDMETHOD. METHOD contains. rv_flag = xsdbool( line_exists( mt_index[ table_line = iv_entry ] ) ). ENDMETHOD. ENDCLASS. CLASS lcl_filter_null IMPLEMENTATION. METHOD accepts. rv_flag = abap_true. ENDMETHOD. ENDCLASS. CLASS lcl_filter IMPLEMENTATION. METHOD constructor. ms_cfg = is_cfg. mv_config_id_reject = c_id_title " Z - Dyn. Assign, Status/Title, SCAN, SET Local && c_id_message " W - Message/Continue/Wait && c_id_db_oper " V - Database Operation && c_id_sap_sql " S - Open SQL && c_id_db_sql " Q - Native SQL && c_id_import " X - Export/Import && c_id_db_tech " N - Buffer/Technical DB Operation && c_id_program " P - Program start point && c_id_record " O - record of measurement && ` `. " space ENDMETHOD. METHOD new. IF is_cfg IS INITIAL. ri_filter = NEW lcl_filter_null( is_cfg ). ELSEIF is_cfg-pattern IS INITIAL. ri_filter = NEW lcl_filter( is_cfg ). ELSE. ri_filter = NEW lcl_filter_custom( is_cfg ). ENDIF. ENDMETHOD. METHOD accepts. CONSTANTS c_system_flag TYPE satr_de_sysflag VALUE 'S'. IF is_sat-id CA mv_config_id_reject. rv_flag = abap_false. ELSEIF ms_cfg-system_events EQ abap_false. rv_flag = xsdbool( is_sat-system NE c_system_flag ). ELSE. rv_flag = abap_true. ENDIF. ENDMETHOD. ENDCLASS. CLASS lcl_filter_custom IMPLEMENTATION. METHOD constructor. super->constructor( is_cfg ). mv_regex = concat_lines_of( table = ms_cfg-pattern sep = '|' ). ENDMETHOD. METHOD match_pattern. FIND REGEX mv_regex IN iv_name MATCH OFFSET DATA(lv_offset) MATCH LENGTH DATA(lv_mlen). rv_flag = xsdbool( 0 EQ sy-subrc AND 0 EQ lv_offset AND lv_mlen GT 0 ). ENDMETHOD. METHOD customer_namespace. rv_flag = xsdbool( mv_regex IS INITIAL OR match_pattern( iv_name ) ). ENDMETHOD. METHOD accepts. rv_flag = super->accepts( is_sat ). CHECK rv_flag EQ abap_true. * Filter logic rv_flag = xsdbool( * Called object is custom code ( is_sat-called-global IS NOT INITIAL AND customer_namespace( is_sat-called-global ) ) OR * If initial, caller object must be custom code ( is_sat-called-global IS INITIAL AND customer_namespace( is_sat-caller-global ) ) ). ENDMETHOD. ENDCLASS. CLASS lcl_class_name IMPLEMENTATION. METHOD external. CHECK cv_clas+30(2) = 'CP'. cv_clas = translate( val = cv_clas(30) from = '=' to = ' ' ). cv_clas+30(2) = space. ENDMETHOD. METHOD technical. rv_class = to_internal( iv_name ). rv_class+30(2) = 'CP'. ENDMETHOD. METHOD to_internal. rv_class = iv_name. * rv_class = translate( val = rv_class(30) from = ' ' to = '=' ). "DOES not work!? TRANSLATE rv_class(30) USING ' ='. ENDMETHOD. METHOD is_global. rv_flag = VALUE #( mt_class[ name = iv_name ]-global DEFAULT fetch_is_global( iv_name ) ). ENDMETHOD. METHOD fetch_is_global. CLEAR rv_flag. SELECT SINGLE 'X' FROM tadir INTO @rv_flag " Single Record buffered WHERE pgmid = 'R3TR' AND object = 'CLAS' AND obj_name = @iv_name. * add to cache proxy for table TADIR INSERT VALUE #( name = iv_name global = rv_flag ) INTO TABLE mt_class. ENDMETHOD. ENDCLASS. CLASS lcl_abap_trace IMPLEMENTATION. METHOD constructor. * Query ABAP execution trace created by transaction SAT without aggregation mo_atra = NEW #( ). mo_class_name = NEW #( ). ENDMETHOD. METHOD module. * PROGINDEX : Index for Table TRACEPROG READ TABLE mo_atra->it_traceprog INDEX iv_index ASSIGNING FIELD-SYMBOL(<ls_prog>). rv_subrc = sy-subrc. CHECK rv_subrc = 0. ms_target-global = <ls_prog>-cprog. ms_target-type = <ls_prog>-object. ms_target-system = <ls_prog>-sysflag. ENDMETHOD. METHOD event. * TEXTINDEX : Index for Internal Table TRACETEXT READ TABLE mo_atra->it_tracetext INDEX mr_src->textindex ASSIGNING FIELD-SYMBOL(<ls_text>). rv_subrc = sy-subrc. CHECK rv_subrc = 0. ms_target-event = <ls_text>-tracetext. ms_target-system = <ls_text>-sysflag. ENDMETHOD. METHOD set_meth. DATA lv_tracetext TYPE program. * METHINDEX : Index for Method Name Table SATR_T_TRACEMETH module( mr_src->progindex ). * CALLED-Data ms_target-event = VALUE #( mo_atra->it_tracemeth[ mr_src->methindex ]-methode OPTIONAL ). READ TABLE mo_atra->it_tracetext INDEX mr_src->textindex ASSIGNING FIELD-SYMBOL(<ls_text>). CHECK sy-subrc = 0. lv_tracetext = <ls_text>-tracetext. " type conversion CHECK module( line_index( mo_atra->it_traceprog[ cprog = mo_class_name->to_internal( lv_tracetext ) ] ) ) NE 0. set_local_class( lv_tracetext ). ENDMETHOD. METHOD set_constructor. ASSIGN mo_atra->it_tracetext[ mr_src->textindex ] TO FIELD-SYMBOL(<ls_text>). CHECK sy-subrc = 0. ms_target-type = c_type_class. ms_target-event = c_create_method. ms_target-system = <ls_text>-sysflag. DATA(lv_tracetext) = CONV program( <ls_text>-tracetext ). CHECK NOT line_exists( mo_atra->it_traceprog[ cprog = lv_tracetext ] ). ms_target-global = mo_class_name->technical( lv_tracetext ). set_local_class( lv_tracetext ). ENDMETHOD. METHOD set_local_class. CHECK NOT mo_class_name->is_global( iv_progname ). ms_target-local = iv_progname. CHECK module( mr_src->progindex ) EQ 0. ms_target-type = c_type_class. " overwrite TYPE IF found ENDMETHOD. METHOD set_form. event( ). module( mr_src->progindex ). ENDMETHOD. METHOD set_tran. CHECK event( ) EQ 0. ms_target-global = ms_target-event. ms_target-type = c_type_tran. ENDMETHOD. METHOD set_submit. CHECK event( ) EQ 0. module( line_index( mo_atra->it_traceprog[ cprog = ms_target-event ] ) ). ENDMETHOD. METHOD set_function_module. * PR_2INDEX : Index of Int. Table TRACEPROG for Performs CHECK event( ) EQ 0. module( mr_src->pr_2index ). ENDMETHOD. METHOD parse. rv_flag = abap_true. * General data ms_target = VALUE #( instance = mr_src->instance ). CASE mr_src->id. " ID of a Trace Record (Recorded Event) WHEN c_id_class. " R - We only check for CREATE OBJECT (R/O) event set_constructor( ). WHEN c_id_method. " m - Record of methods-calls set_meth( ). WHEN c_id_func. " U - Record of Function Modules set_function_module( ). WHEN c_id_module. " M - Module Calls set_form( ). WHEN c_id_load. " L - Load IF mr_src->subid = 'D'. " Dynpro set_form( ). ENDIF. WHEN c_id_report. " CALL - e.g. Call SCREEN/CALL Report CASE mr_src->subid. " Sub-ID of a TRACE Record WHEN 'D' OR 'S'. " CALL Dialog OR Call Screen set_form( ). WHEN 'P'. " CALL Program module( mr_src->progindex ). WHEN 'T'. " CALL Transaction set_tran( ). WHEN 'R'. " SUBMIT Report set_submit( ). ENDCASE. WHEN c_id_form. " F - Record of Form-Routines CASE mr_src->subid. WHEN ' '. set_form( ). " internal FORM WHEN 'E'. set_function_module( ). " external FORM WHEN 'N'. " Not Found. e.g. PERFORM IF FOUND failed rv_flag = abap_false. ENDCASE. WHEN c_id_record. " O - Record of measurement CASE mr_src->subid. WHEN ' '. module( mr_src->progindex ). WHEN OTHERS. ENDCASE. WHEN c_id_dynpro " A - Maybe PBO/PAI-Modules OR c_id_flow. " E - Processing Logic (Flow Logic) * Screen handling does not add anything new, the module is needed module( mr_src->progindex ). WHEN OTHERS. " Y - SAP GUI Events? ENDCASE. mo_class_name->external( CHANGING cv_clas = ms_target-global ). ENDMETHOD. ENDCLASS. CLASS lcl_trace IMPLEMENTATION. METHOD constructor. super->constructor( ). lcl_progress_indicator=>echo( iv_text = |Parse { lines( mo_atra->it_austab_hier ) } entries| ). mo_bag = NEW lcl_bag( ). ENDMETHOD. METHOD fill. * Parse call hierarchy, apply custom filters, fill collection of messages LOOP AT mo_atra->it_austab_hier REFERENCE INTO mr_src. CHECK new_record( ). DATA(ls_sat) = get_sat( ). IF NOT ii_collector->collect( ls_sat ). skip_record( ). ENDIF. put_caller( ls_sat ). ENDLOOP. ENDMETHOD. METHOD new_record. rv_flag = abap_false. IF mr_src->event = c_event_entry. " Event ID (> Entry, < Exit) mo_bag->add( mr_src->index ). * Parse call hierarchy in trace rv_flag = parse( ). ELSE. skip_record( ). ENDIF. ENDMETHOD. METHOD get_sat. rs_sat = VALUE ts_sat( id = mr_src->id " Gen. data aus_tabix = mr_src->index " Index of DUMP List from_level = mr_src->ebene " Call Level (Call Hierarchy) caller = VALUE #( mt_caller[ KEY k1 COMPONENTS aus_tabix = mr_src->caller ]-called OPTIONAL ) called-object = ms_target-object called-mod = ms_target-event system = ms_target-system ). ENDMETHOD. METHOD put_caller. DATA(ls_seq) = is_sat-seq. IF ls_seq-called-global IS INITIAL. ls_seq-called = is_sat-caller. ENDIF. mo_class_name->external( CHANGING cv_clas = ls_seq-called-global ). APPEND ls_seq TO mt_caller. ENDMETHOD. METHOD skip_record. * Aussprungsätze. READ TABLE mt_caller ASSIGNING FIELD-SYMBOL(<ls_seq>) WITH KEY k1 COMPONENTS aus_tabix = mr_src->start. IF sy-subrc EQ 0. IF mr_src->id = c_id_class AND mr_src->subid = 'O'. IF mo_bag->contains( mr_src->start ). <ls_seq>-called-instance = mr_src->instance. ENDIF. ENDIF. APPEND VALUE #( called-instance = mr_src->instance aus_tabix = mr_src->index ) TO mt_caller. ELSE. mo_bag->remove( mr_src->start ). ENDIF. ENDMETHOD. ENDCLASS. CLASS lcl_actors IMPLEMENTATION. METHOD next_number. ADD 1 TO mv_last_number. rv_key = mv_last_number. ENDMETHOD. METHOD index_of. rv_idx = VALUE #( mt_actor[ object = is_object ]-index OPTIONAL ). CHECK rv_idx EQ 0. IF is_object-type EQ c_type_class. IF is_object-instance IS NOT INITIAL. * Inheritance cases: do not check object name rv_idx = VALUE #( mt_actor[ instance = is_object-instance ]-index OPTIONAL ). ELSE. rv_idx = VALUE #( mt_actor[ instance = is_object-instance local = is_object-local global = is_object-global ]-index OPTIONAL ). ENDIF. ELSEIF is_object-type IS NOT INITIAL. rv_idx = VALUE #( mt_actor[ global = is_object-global local = is_object-local type = is_object-type ]-index OPTIONAL ). ENDIF. ENDMETHOD. METHOD put. rv_key = index_of( is_object ). CHECK rv_key IS INITIAL. rv_key = next_number( ). " Next primary key field or SORTED INSERT VALUE #( object = is_object index = rv_key label = lifeline( is_object ) ) INTO TABLE mt_actor. ENDMETHOD. METHOD lif_actors~new_path. rs_path = VALUE #( caller = put( is_sat-caller-object ) called = put( is_sat-called-object ) ). ENDMETHOD. METHOD lifeline. DATA lv_name TYPE string. CASE is_object-type. WHEN c_type_class. lv_name = |{ is_object-local }\\n{ is_object-global }|. rv_text = SWITCH #( is_object-instance WHEN 0 THEN |Static Methods of Class { lv_name }| ELSE |ObjectId:{ is_object-instance } of Class { lv_name }| ). WHEN c_type_fugr. lv_name = SWITCH #( is_object-global+0(1) WHEN '/' THEN is_object-global+0(6) && is_object-global+10 ELSE is_object-global+4 ). rv_text = |Function Group\\n{ lv_name }|. WHEN OTHERS. rv_text = |{ is_object-type }\\n{ is_object-global }|. ENDCASE. ENDMETHOD. METHOD class_name. CLEAR rv_short_name. READ TABLE mt_actor WITH TABLE KEY obj_nr COMPONENTS index = iv_index INTO DATA(ls_obj). CHECK sy-subrc EQ 0. rv_short_name = SWITCH program( ls_obj-local WHEN space THEN ls_obj-global ELSE ls_obj-local ). ENDMETHOD. METHOD lif_actors~lifelines. rt_lifeline = CORRESPONDING #( mt_actor ). ENDMETHOD. METHOD lif_actors~short_text. CASE is_message-id. WHEN c_id_skip. rv_text = |<b>Skipping over SAP code until calling { is_message-msg }</b>|. WHEN c_id_class. rv_text = |Create instance of class { class_name( is_message-called ) }|. WHEN OTHERS. rv_text = call_short_text( is_message ). ENDCASE. ENDMETHOD. METHOD call_short_text. rv_text = |{ SWITCH #( is_message-id WHEN c_id_form THEN |Perform| WHEN c_id_func THEN |Call FM| WHEN c_id_method THEN |Call method| ELSE space ) } { is_message-msg }|. ENDMETHOD. ENDCLASS. CLASS lcl_sequence IMPLEMENTATION. METHOD constructor. mi_actors = NEW lcl_actors( ). mi_filter = lcl_filter=>new( is_cfg ). " Custom filter mo_uml = lcl_uml=>new( iv_uml = is_cfg-uml_format ii_actors = mi_actors ). mv_compact_trace = is_cfg-compact_trace. ENDMETHOD. METHOD link_to_previous. * add a link to last valid entry to fill gaps created by filter logic rs_sat = is_sat. IF ms_previous IS NOT INITIAL AND ms_previous-called NE is_sat-caller AND is_sat-from_level GT ms_previous-from_level. IF is_sat-caller IS INITIAL. rs_sat-caller = ms_previous-called. " check initial? ELSE. * insert a new line into the table at this point (link to last valid line). add( VALUE #( id = c_id_skip from_level = ms_previous-from_level caller = ms_previous-called called = is_sat-caller ) ). ENDIF. ENDIF. ms_previous = rs_sat. ENDMETHOD. METHOD lif_collector~collect. rv_flag = mi_filter->accepts( is_sat ). CHECK rv_flag EQ abap_true. * Update previous entry (fill gaps if needed) add( link_to_previous( is_sat ) ). ENDMETHOD. METHOD add. APPEND VALUE ts_message( id = is_sat-id from_level = is_sat-from_level msg = is_sat-called-mod path = mi_actors->new_path( is_sat ) ) TO mt_trace. ENDMETHOD. METHOD to_diagram. DATA(lo_progress) = lcl_progress_indicator=>new( is_cfg ). ro_diagram = lcl_diagram_text=>new( is_cfg = is_cfg iv_text = NEW lcl_sequence( is_cfg )->get_calls( )->to_uml( ) ). lo_progress->restore( ). ENDMETHOD. METHOD get_calls. * filter and convert ABAP call hierarchy trace: NEW lcl_trace( )->fill( me ). ro_calls = to_call_stack( ). ENDMETHOD. METHOD to_call_stack. * Convert trace to call stack object ro_calls = lcl_uml_factory=>new_calls( iv_compact_trace = mv_compact_trace it_trace = mt_trace io_uml = mo_uml ). ENDMETHOD. ENDCLASS. CLASS lcl_uml IMPLEMENTATION. METHOD new. CASE iv_uml. WHEN c_uml_graph. ro_uml = NEW lcl_uml_graph( ). WHEN c_uml_mscgen. ro_uml = NEW lcl_uml_mscgen( ). WHEN c_uml_mock. ro_uml = NEW lcl_uml_logger( ). WHEN OTHERS. ro_uml = NEW lcl_uml_plant( ). ENDCASE. ro_uml->mi_actors = ii_actors. ENDMETHOD. METHOD add. mv_diagram = mv_diagram && iv_code. ENDMETHOD. METHOD complete. RETURN. ENDMETHOD. METHOD separator. RETURN. ENDMETHOD. METHOD delimiter. RETURN. ENDMETHOD. METHOD top. header( ). * Init life lines LOOP AT mi_actors->lifelines( ) INTO DATA(ls_lifeline). participant( ls_lifeline ). AT LAST. delimiter( ). "if needed: e.g. add( |;\n| ). EXIT. ENDAT. separator( ). "if needed: e.g. add( |, | ). ENDLOOP. ENDMETHOD. METHOD bottom. * Complete all life lines LOOP AT mi_actors->lifelines( ) INTO DATA(ls_lifeline). complete( ls_lifeline ). ENDLOOP. footer( ). rv_diagram = mv_diagram. ENDMETHOD. METHOD message. CHECK is_message IS NOT INITIAL. call( is_message ). IF is_message-id = c_id_skip. skip_note( is_message-path ). ENDIF. ENDMETHOD. ENDCLASS. CLASS lcl_uml_plant IMPLEMENTATION. METHOD add_param. CHECK iv_cond EQ abap_true. mv_param = mv_param && iv_command. ENDMETHOD. METHOD scale. DATA lv_scale TYPE tv_scale. lv_scale = nmin( val1 = 1 val2 = nmax( val1 = is_cfg-scale val2 = c_min_scale ) ). rv_scale = |scale { lv_scale DECIMALS = 2 }\n|. ENDMETHOD. METHOD parameters. CLEAR mv_param. add_param( scale( is_cfg ) ). " Reduce the size of the output image add_param( iv_cond = is_cfg-teoz_architecture iv_command = |!pragma teoz true\n| ). add_param( |skinparam \{\n| ). * For performance: disable shadowing add_param( iv_cond = xsdbool( is_cfg-shadowing EQ abap_false ) iv_command = |shadowing false\n| ). add_param( iv_cond = is_cfg-handwritten " mimic hand writting iv_command = |handwritten true\n| ). add_param( |\}\n| ). rv_param = mv_param. ENDMETHOD. METHOD header. * Plant UML Header add( |@startuml\nhide footbox\nautonumber\n{ parameters( lcl_configuration=>get( ) ) }| ). ENDMETHOD. METHOD call. add( |{ is_message-caller } -> { is_message-called }: { mi_actors->short_text( is_message ) }\n| ). add( |activate { is_message-called }\n| ). ENDMETHOD. METHOD return. * return and deactivate the actor IF iv_from NE iv_to. add( |{ iv_from } --> { iv_to }\n| ). ENDIF. CHECK iv_from IS NOT INITIAL. add( |deactivate { iv_from }\n| ). ENDMETHOD. METHOD participant. add( |participant "{ is_lifeline-label }" as { is_lifeline-index }\n| ). ENDMETHOD. METHOD footer. add( |@enduml\n| ). ENDMETHOD. METHOD skip_note. add( |note over { is_path-caller },{ is_path-called }\n{ c_txt_std }\nend note\n| ). ENDMETHOD. METHOD begin_loop. add( |loop { iv_times } times\n| ). ENDMETHOD. METHOD end_loop. add( |end\n| ). ENDMETHOD. ENDCLASS. CLASS lcl_messages IMPLEMENTATION. METHOD constructor. CLEAR mv_idx. mt_list = it_messages. " shared, never changed mv_size = COND #( WHEN iv_stop IS INITIAL THEN lines( mt_list ) ELSE iv_stop ). skip( iv_start - 1 ). mv_first_level = next_level( ). ENDMETHOD. METHOD next. skip( ). rs_data = mt_list[ mv_idx ]. ENDMETHOD. METHOD has_next. rv_flag = xsdbool( mv_idx < mv_size ). ENDMETHOD. METHOD skip. ADD iv_count TO mv_idx. ENDMETHOD. METHOD is_first. rv_flag = xsdbool( mv_idx LE 1 ). ENDMETHOD. METHOD first_message. rs_message = VALUE #( caller = mv_first_level called = c_first_key ). ENDMETHOD. METHOD next_level. rv_level = VALUE i( mt_list[ mv_idx + 1 ]-from_level DEFAULT c_default_level ). ENDMETHOD. ENDCLASS. CLASS lcl_stack IMPLEMENTATION. METHOD push. mr_top = NEW ts_level( call_level = is_ref next = mr_top ). ENDMETHOD. METHOD pop. CLEAR rs_to. CHECK mr_top IS BOUND. rs_to = mr_top->call_level. mr_top ?= mr_top->next. ENDMETHOD. ENDCLASS. CLASS lcl_uml_stack IMPLEMENTATION. METHOD constructor. super->constructor( ). mo_uml = io_uml. mv_previous_level = lcl_messages=>c_default_level. ENDMETHOD. METHOD call. push( VALUE #( actor_key = is_message-called from_level = is_message-from_level ) ). * generate UML in text form mo_uml->message( is_message ). ENDMETHOD. METHOD return. IF iv_to_level <= mv_previous_level. return_to( iv_to_level ). ENDIF. mv_previous_level = iv_to_level. ENDMETHOD. METHOD return_to. DATA(ls_from) = pop( ). WHILE ls_from-from_level >= iv_level AND mr_top IS BOUND. DATA(ls_to) = pop( ). * UML diagram mo_uml->return( iv_from = ls_from-actor_key iv_to = ls_to-actor_key ). ls_from = ls_to. ENDWHILE. CHECK ls_to IS NOT INITIAL. push( ls_to ). ENDMETHOD. ENDCLASS. CLASS lcl_call_stack IMPLEMENTATION. METHOD constructor. super->constructor( io_uml ). mo_messages = io_messages. ENDMETHOD. METHOD message. IF mo_messages->is_first( ). call( mo_messages->first_message( ) ). " very first call ENDIF. call( is_message ). return( mo_messages->next_level( ) ). ENDMETHOD. METHOD to_uml. * UML header mo_uml->top( ). * Convert call stack to UML sequence diagram in text format WHILE mo_messages->has_next( ). message( is_message = mo_messages->next( ) iv_idx = sy-index ). ENDWHILE. * add UML footer rv_diagram = mo_uml->bottom( ). ENDMETHOD. ENDCLASS. CLASS lcl_call_stack_compact IMPLEMENTATION. METHOD constructor. super->constructor( io_uml = io_uml io_messages = io_messages ). mt_cycle = it_cycles. ENDMETHOD. METHOD message. begin( iv_idx = iv_idx iv_from = is_message-caller ). " if needed super->message( is_message = is_message iv_idx = iv_idx ). * after return so the loop encloses the return path mo_messages->skip( end( iv_idx = iv_idx iv_to = is_message-called ) ). ENDMETHOD. METHOD name. * Loop ID rv_name = |{ iv_start }to{ iv_end }|. ENDMETHOD. METHOD begin. LOOP AT mt_cycle INTO DATA(ls_cycle) WHERE start = iv_idx. mo_uml->begin_loop( iv_from = |{ iv_from }| iv_times = ls_cycle-times iv_name = name( iv_start = iv_idx iv_end = ls_cycle-end ) ). ENDLOOP. ENDMETHOD. METHOD end. CLEAR rv_step. LOOP AT mt_cycle INTO DATA(ls_cycle) WHERE end = iv_idx. "#EC CI_SORTSEQ mo_uml->end_loop( iv_to = |{ iv_to }| iv_name = name( iv_start = ls_cycle-start iv_end = iv_idx ) ). rv_step = nmax( val1 = ls_cycle-last - iv_idx val2 = rv_step ). ENDLOOP. ENDMETHOD. ENDCLASS. CLASS lcl_trace_index IMPLEMENTATION. METHOD constructor. mt_trace = it_trace. mi_cycles = ii_cycles. to_index( ). lcl_progress_indicator=>echo( iv_text = |Compact trace with { lines( it_trace ) } entries| ). ENDMETHOD. METHOD to_index. * mt_components = VALUE #( FOR t IN mt_trace ( t-message ) ). dump on duplicates! does not pass test CLEAR mt_components. LOOP AT mt_trace ASSIGNING FIELD-SYMBOL(<ls_trace>). INSERT <ls_trace> INTO TABLE mt_components. ENDLOOP. mt_xindex = VALUE #( FOR t IN mt_trace ( line_index( mt_components[ table_line = t ] ) ) ). ENDMETHOD. METHOD to_trace. ct_trace = VALUE #( FOR x_idx IN mt_xindex ( mt_components[ x_idx ] ) ). ENDMETHOD. METHOD shrink. lcl_pattern=>new( it_xindex = mt_xindex ii_cycles = mi_cycles )->detect_cycles( ). rv_shrink = compact( EXPORTING it_xindex = mt_xindex IMPORTING et_xindex = mt_xindex ). lcl_progress_indicator=>echo( iv_percentage = iv_pass iv_text = |Compact trace pass { iv_pass } - { lines( mt_xindex ) } entries| ). ENDMETHOD. METHOD compact. CLEAR et_xindex. LOOP AT it_xindex ASSIGNING FIELD-SYMBOL(<lv_x_index>). CHECK mi_cycles->accepts( sy-tabix ). APPEND <lv_x_index> TO et_xindex. ENDLOOP. rv_shrink = xsdbool( it_xindex NE et_xindex ). ENDMETHOD. ENDCLASS. CLASS lcl_uml_factory IMPLEMENTATION. METHOD new. * create UML sequence diagram translator IF iv_compact_trace EQ abap_true. ro_factory = NEW lcl_uml_compactor( ). ELSE. ro_factory = NEW lcl_uml_factory( ). ENDIF. ENDMETHOD. METHOD new_calls. ro_calls = lcl_uml_factory=>new( iv_compact_trace )->new_call_stack( io_uml = io_uml it_trace = it_trace ). ENDMETHOD. METHOD new_iterator. fold( EXPORTING it_trace = it_trace IMPORTING et_messages = DATA(lt_messages) ). ro_iterator = NEW lcl_messages( lt_messages ). ENDMETHOD. METHOD fold. et_messages = it_trace. ENDMETHOD. METHOD new_call_stack. ro_calls = NEW lcl_call_stack( io_uml = io_uml io_messages = new_iterator( it_trace ) ). ENDMETHOD. ENDCLASS. CLASS lcl_uml_compactor IMPLEMENTATION. METHOD fold. CLEAR et_messages. DATA(lo_evt) = NEW lcl_trace_index( it_trace = it_trace ii_cycles = me ). WHILE lo_evt->shrink( sy-index ). lcl_shrinkage=>shrink_references( CHANGING ct_cycles = mt_cycles ). ENDWHILE. lo_evt->to_trace( CHANGING ct_trace = et_messages ). ENDMETHOD. METHOD new_call_stack. ro_calls = NEW lcl_call_stack_compact( io_uml = io_uml io_messages = new_iterator( it_trace ) it_cycles = mt_cycles ). ENDMETHOD. METHOD lif_cycles~collect. * duplicate entries are inserted BEFORE the existing row INSERT is_cycle INTO TABLE mt_cycles. ENDMETHOD. METHOD lif_cycles~accepts. LOOP AT mt_cycles TRANSPORTING NO FIELDS "#EC CI_SORTSEQ WHERE end LT iv_index AND last GE iv_index. rv_flag = abap_false. RETURN. ENDLOOP. rv_flag = abap_true. ENDMETHOD. ENDCLASS. CLASS lcl_shrinkage IMPLEMENTATION. METHOD shrink_references. * reconcile the loop reference table DATA(lo_shrink) = NEW lcl_shrinkage( ct_cycles ). * Change needed ? WHILE lo_shrink->ms_stop IS NOT INITIAL. * Rebuild loop references table: lo_shrink = lo_shrink->rebuild( ). ENDWHILE. ct_cycles = lo_shrink->mt_cycles. ENDMETHOD. METHOD constructor. mt_cycles = it_cycles. CLEAR mv_tabix. LOOP AT it_cycles INTO DATA(c). mv_tabix = sy-tabix. * Change needed in subsequent entries? CHECK c-last GT c-end. ms_stop = c. EXIT. ENDLOOP. ENDMETHOD. METHOD renew. ro_shrink = NEW lcl_shrinkage( CONV #( mt_shrinked_loop ) ). ENDMETHOD. METHOD rebuild. * after removing repetition from the active loop in the trace table, * rebuild (compress) the loop reference table LOOP AT mt_cycles INTO DATA(ls_cycle). AT FIRST. DATA(lv_delta) = ms_stop-last - ms_stop-end. ENDAT. IF ls_cycle-last LT ms_stop-start. * Loops entries before the active loop - verbatim insert( ls_cycle ). ENDIF. IF ls_cycle-start LT ms_stop-start AND ls_cycle-end GE ms_stop-last. * Loops entries starting before the active loop to be compacted and * that contains it (i.e. they enclose it ) - adjust insert( VALUE #( BASE ls_cycle end = ls_cycle-end - lv_delta last = ls_cycle-last - lv_delta ) ). ENDIF. IF sy-tabix EQ mv_tabix. * Update the active loop entry insert( VALUE #( BASE ms_stop last = ms_stop-end ) ). ELSEIF ls_cycle-start GE ms_stop-start AND ls_cycle-last LE ms_stop-end. * Loops entries completely enclosed by the active loop entry - verbatim insert( ls_cycle ). ENDIF. IF ls_cycle-start GT ms_stop-last. * Loops entries after the active loop - shifted by Offset insert( VALUE #( start = ls_cycle-start - lv_delta end = ls_cycle-end - lv_delta last = ls_cycle-last - lv_delta times = ls_cycle-times ) ). ENDIF. ENDLOOP. * Write back ro_shrink = renew( ). ENDMETHOD. METHOD insert. INSERT ls_cycle INTO TABLE mt_shrinked_loop. ENDMETHOD. ENDCLASS. CLASS lcl_pattern IMPLEMENTATION. METHOD new. ro_pattern = NEW #( it_xindex = it_xindex iv_start = iv_start iv_stop = iv_stop ). ro_pattern->mi_cycles = ii_cycles. ENDMETHOD. METHOD constructor. CLEAR mv_idx. mt_xindex = it_xindex. " Copied or Shared??? never changed mv_size = COND #( WHEN iv_stop IS INITIAL THEN lines( mt_xindex ) ELSE iv_stop ). skip( iv_start - 1 ). ENDMETHOD. METHOD clone. ro_loop = new( iv_start = iv_start iv_stop = COND #( WHEN iv_stop IS SUPPLIED THEN iv_stop ELSE mv_size ) it_xindex = mt_xindex ii_cycles = mi_cycles ). ENDMETHOD. METHOD skip. ADD iv_count TO mv_idx. ENDMETHOD. METHOD has_next. rv_flag = xsdbool( mv_idx < mv_size ). ENDMETHOD. METHOD occurs_in. DATA(lv_other) = iv_end. rv_same = abap_false. LOOP AT mt_xindex FROM iv_start TO iv_end ASSIGNING FIELD-SYMBOL(<lv_index>). ADD 1 TO lv_other. CHECK mt_xindex[ lv_other ] NE <lv_index>. RETURN. ENDLOOP. * returning abap_true for zero length tests will lead to infinite loop rv_same = xsdbool( sy-subrc EQ 0 ). ENDMETHOD. METHOD match_pattern. rv_match = abap_false. DATA(lv_dist) = iv_end - mv_idx + 1. CHECK iv_end GE 0 AND lv_dist GT 0. DATA(ls_group) = VALUE ts_cycle( start = mv_idx end = iv_end last = iv_end times = 1 ). * Find consecutive occurrences of group WHILE ( ls_group-last + lv_dist <= mv_size ) AND occurs_in( iv_start = mv_idx iv_end = ls_group-last ). ADD 1 TO ls_group-times. ADD lv_dist TO ls_group-last. skip( lv_dist ). ENDWHILE. CHECK ls_group-times GT 1. rv_match = abap_true. skip( lv_dist - 1 ). mi_cycles->collect( ls_group ). * repeat search in a subrange (recursive call) clone( iv_start = ls_group-start + 1 iv_stop = ls_group-end )->detect_cycles( ). ENDMETHOD. METHOD detect_cycles. * identify blocks of repeated messages (cycles) * Pass the cycle information to interface LIF_CYCLES WHILE has_next( ). skip( ). DATA(lv_index) = mv_idx. DO. "#EC CI_NESTED. lv_index = next_occurrence_from( lv_index ). CHECK lv_index IS INITIAL OR match_pattern( lv_index - 1 ). EXIT. ENDDO. ENDWHILE. ENDMETHOD. METHOD next_occurrence_from. CLEAR rv_row. DATA(lv_until) = nmin( val1 = iv_from + c_max_step val2 = mv_size ). CHECK lv_until GT iv_from. READ TABLE mt_xindex INDEX iv_from ASSIGNING FIELD-SYMBOL(<lv_org>). CHECK sy-subrc EQ 0. FIND FIRST OCCURRENCE OF <lv_org> IN TABLE mt_xindex FROM iv_from + 1 TO lv_until IN BYTE MODE MATCH LINE rv_row. ENDMETHOD. ENDCLASS. CLASS lcl_file_name IMPLEMENTATION. METHOD new. CASE iv_mode. WHEN lcl_diagram_text=>c_mode_aut. ro_file = NEW lcl_file_test_name( iv_mode ). WHEN OTHERS. ro_file = NEW lcl_file_name( iv_mode ). ENDCASE. ENDMETHOD. METHOD constructor. CASE iv_mode. WHEN lcl_diagram_text=>c_mode_txt. ms_file = VALUE #( title = |Save UML text source| ext = |.txt| ). WHEN OTHERS. ms_file = VALUE #( title = |Save As...| ext = |.txt| ). ENDCASE. ENDMETHOD. METHOD get_prefix. rv_name = shift_right( val = ms_file-name places = strlen( ms_file-ext ) ). ENDMETHOD. METHOD get_fullpath. rv_name = ms_file-path. ENDMETHOD. METHOD dialog. DATA lv_path TYPE string. CLEAR rv_user_action. cl_gui_frontend_services=>file_save_dialog( EXPORTING window_title = ms_file-title " Window Title default_extension = ms_file-ext " Default Extension CHANGING filename = ms_file-name " File Name to Save path = lv_path " Path to File fullpath = ms_file-path " Path + File Name user_action = rv_user_action " User Action (C Class Const ACTION_OK, ACTION_OVERWRITE etc) * file_encoding = EXCEPTIONS OTHERS = 0 ). ENDMETHOD. ENDCLASS. CLASS lcl_file_test_name IMPLEMENTATION. METHOD dialog. ms_file-path = |test.txt|. rv_user_action = cl_gui_frontend_services=>action_cancel. ENDMETHOD. ENDCLASS. CLASS lcl_file IMPLEMENTATION. METHOD download. rv_subrc = 1. CHECK io_name->dialog( ) NE cl_gui_frontend_services=>action_cancel. rv_subrc = cl_uml_utilities=>save_xml_local( xml = iv_data filename = io_name->get_fullpath( ) ). ENDMETHOD. ENDCLASS. CLASS lcl_diagram_text IMPLEMENTATION. METHOD new. CASE is_cfg-uml_format. WHEN OTHERS. * create PlantUML diagram by default ro_diagram = NEW lcl_diagram_plant_uml( is_cfg = is_cfg iv_diagram = iv_text ). ENDCASE. ENDMETHOD. METHOD constructor. mv_diagram = iv_diagram. ms_cfg-output_mode = iv_mode. ENDMETHOD. METHOD output. save_file( ms_cfg-output_mode ). ENDMETHOD. METHOD to_xstring. cl_abap_conv_out_ce=>create( encoding = 'UTF-8' )->convert( EXPORTING data = iv_string IMPORTING buffer = rv_xstring ). ENDMETHOD. METHOD save_file. * export data as (PlantUML) source rv_fname = lcl_file=>download( iv_data = to_xstring( mv_diagram ) io_name = lcl_file_name=>new( iv_mode ) ). ENDMETHOD. "save_file ENDCLASS. CLASS lcl_diagram_plant_uml IMPLEMENTATION. METHOD constructor. super->constructor( iv_diagram = iv_diagram iv_mode = is_cfg-output_mode ). ms_cfg = is_cfg. ENDMETHOD. METHOD output. CASE ms_cfg-output_mode. WHEN c_mode_url. display_html( ). WHEN c_mode_exe. local_plant_uml( ). WHEN OTHERS. super->output( ). ENDCASE. ENDMETHOD. METHOD show_html. cl_abap_browser=>show_html( html_string = iv_html size = cl_abap_browser=>xlarge context_menu = abap_true ). ENDMETHOD. METHOD display_html. show_html( |<img src="{ to_url( ms_cfg-server_url ) }"/>\n{ source( ) }| ). ENDMETHOD. "display_html METHOD to_url. rv_url = iv_base_url && encoded_url_suffix( ). ENDMETHOD. METHOD encoded_url_suffix. * for PlantUML Server: Convert to UTF-8, then deflate, then encode (base64 variant) cl_abap_gzip=>compress_binary( EXPORTING raw_in = to_xstring( mv_diagram ) ##TYPE " UTF-8 compress_level = 9 IMPORTING gzip_out = DATA(lv_bin) ). rv_url = translate( val = cl_http_utility=>encode_x_base64( lv_bin ) from = c_charset_standard to = c_charset_plantuml ). ENDMETHOD. METHOD local_plant_uml. DATA(lo_name) = lcl_file_name=>new( c_mode_txt ). CHECK lcl_file=>download( iv_data = to_xstring( mv_diagram ) io_name = lo_name ) IS INITIAL. show_html( |<img src="{ to_png( lo_name ) }"/>\n{ source( ) }| ). ENDMETHOD. METHOD parameter_string. rv_param = |-jar { ms_cfg-java_jar } -o { ms_cfg-local_path } "{ io_name->get_fullpath( ) }"|. ENDMETHOD. METHOD png_file_name. TRY. rv_name = |{ ms_cfg-local_path }{ io_name->get_prefix( ) }.png|. CATCH cx_dynamic_check. CLEAR rv_name. ENDTRY. ENDMETHOD. METHOD source. CASE ms_cfg-display_source. WHEN abap_false. CLEAR rv_source. WHEN OTHERS. rv_source = |<p>{ mv_diagram }</p>|. ENDCASE. ENDMETHOD. METHOD to_png. CLEAR rv_name. cl_gui_frontend_services=>execute( EXPORTING application = ms_cfg-java_appl parameter = parameter_string( io_name ) synchronous = 'X' EXCEPTIONS OTHERS = 1 ). IF sy-subrc EQ 0. rv_name = png_file_name( io_name ). ENDIF. ENDMETHOD. METHOD get_java_path. CONSTANTS c_registry_java_base_key TYPE string VALUE 'SOFTWARE\JavaSoft\Java Runtime Environment' ##NO_TEXT. DATA lv_path TYPE string. CLEAR rv_fullpath. cl_gui_frontend_services=>registry_get_value( EXPORTING root = cl_gui_frontend_services=>hkey_local_machine key = c_registry_java_base_key value = 'CurrentVersion' IMPORTING reg_value = lv_path EXCEPTIONS OTHERS = 5 ). CHECK sy-subrc EQ 0. cl_gui_frontend_services=>registry_get_value( EXPORTING root = cl_gui_frontend_services=>hkey_local_machine key = |{ c_registry_java_base_key }\\{ lv_path }| value = 'JavaHome' IMPORTING reg_value = lv_path EXCEPTIONS OTHERS = 5 ). CHECK sy-subrc EQ 0. rv_fullpath = |{ lv_path }\\bin\\java|. ENDMETHOD. ENDCLASS. CLASS lcl_progress_indicator IMPLEMENTATION. METHOD new. DATA(ls_cfg) = COND #( WHEN is_cfg IS SUPPLIED THEN is_cfg ELSE lcl_configuration=>get( ) ). ro_prog = NEW #( ls_cfg-progress ). ENDMETHOD. METHOD constructor. mv_mode = get_mode( ). set_mode( COND #( WHEN iv_progress EQ abap_true THEN c_enable ELSE c_disable ) ). ENDMETHOD. METHOD restore. set_mode( mv_mode ). ENDMETHOD. METHOD get_mode. GET PARAMETER ID c_param_id_sin FIELD rv_mode. ENDMETHOD. METHOD set_mode. SET PARAMETER ID c_param_id_sin FIELD iv_mode. ENDMETHOD. METHOD echo. CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR' EXPORTING percentage = iv_percentage text = iv_text. ENDMETHOD. ENDCLASS. CLASS lcl_configuration IMPLEMENTATION. METHOD class_constructor. gs_cfg = VALUE #( java_appl = lcl_diagram_plant_uml=>get_java_path( ) * PlantUML jar file and output path local_path = `C:\Temp\Dokumente\UML\` java_jar = `C:\Temp\Dokumente\UML\plantuml.jar` * PlantUML server URL server_url = `http://www.plantuml.com/plantuml/img/` ##NO_TEXT output_mode = lcl_diagram_text=>c_mode_url skip_dialog = space compact_trace = abap_true scale = c_default_scale pattern = VALUE #( ( 'Y*' ) ( 'Z*' ) ) progress = abap_true handwritten = abap_false shadowing = abap_false display_source = abap_true teoz_architecture = abap_false ). * Windows: Local Java installation IF gs_cfg-java_appl IS INITIAL. gs_cfg-java_appl = `C:\Windows\System32\java`. ENDIF. ENDMETHOD. METHOD get_attributes. DEFINE fill_att. INSERT VALUE #( ref = REF #( &1 ) text = &2 kind = &3 ) INTO TABLE rt_attr. END-OF-DEFINITION. DEFINE fill_radio. INSERT VALUE #( ref = REF #( &1 ) text = &2 kind = 'R' button_group = &3 ) INTO TABLE rt_attr. END-OF-DEFINITION. * Table Type has type 'T' - patterns SCI_PATTERN * ' ' - ?? private attributes? * 'I' - ?? Integer? fill_att gs_cfg-skip_dialog 'Remember my settings'(c00) 'C'. fill_att: '' 'Trace Settings'(c01) 'G', gs_cfg-progress 'Progress indicator'(c02) 'C', gs_cfg-system_events 'System events'(c03) 'C', gs_cfg-compact_trace 'Loops compacted'(c04) 'C', gs_cfg-pattern 'Customer namespace'(c05) 'T'. fill_att: sy-index 'PlantUML Execution Mode'(c10) 'G'. " Group fill_radio: mv_mode_url 'PlantUML web service'(c11) 'MOD', mv_mode_txt 'Save text file'(c12) 'MOD', mv_mode_exe 'Local PlantUML '(c13) 'MOD'. fill_att: '' 'PlantUML Settings'(c20) 'G', gs_cfg-scale 'Scale '(c21) 'S'. fill_att: gs_cfg-server_url 'PlantUML Server'(c25) 'S', gs_cfg-local_path 'Local PlantUML path'(c26) 'S', gs_cfg-java_jar 'Local PlantUML jar file'(c27) ' ', gs_cfg-java_appl 'Local Java path'(c28) 'S'. " Select-Options fill_att: gs_cfg-handwritten 'Handwritten '(c30) 'C', gs_cfg-shadowing 'Shadowing '(c31) 'C', gs_cfg-display_source 'Display source '(c32) 'C', gs_cfg-teoz_architecture 'Use TEOZ architecture '(c33) 'C'. ENDMETHOD. METHOD to_radiobutton. mv_mode_url = xsdbool( gs_cfg-output_mode EQ lcl_diagram_text=>c_mode_url ). mv_mode_exe = xsdbool( gs_cfg-output_mode EQ lcl_diagram_text=>c_mode_exe ). mv_mode_txt = xsdbool( gs_cfg-output_mode EQ lcl_diagram_text=>c_mode_txt ). ENDMETHOD. METHOD from_radiobutton. IF mv_mode_url EQ abap_true. gs_cfg-output_mode = lcl_diagram_text=>c_mode_url. ELSEIF mv_mode_exe EQ abap_true. gs_cfg-output_mode = lcl_diagram_text=>c_mode_exe. ELSEIF mv_mode_txt EQ abap_true. gs_cfg-output_mode = lcl_diagram_text=>c_mode_txt. ENDIF. ENDMETHOD. METHOD get. MOVE-CORRESPONDING gs_cfg TO rs_cfg. ENDMETHOD. METHOD query. DATA(lo_config) = NEW lcl_configuration( ). lo_config->dialog( ). rs_cfg = lo_config->get( ). ENDMETHOD. METHOD dialog. to_radiobutton( ). CHECK gs_cfg-skip_dialog EQ abap_false. CHECK cl_ci_query_attributes=>generic( p_name = CONV #( sy-repid ) " unique screen ID p_title = 'Sequence Diagram Parameters' " Screen title p_attributes = get_attributes( ) " Screen fields p_display = abap_false " Edit / Display only ) EQ abap_false. " Do not cancel from_radiobutton( ). ENDMETHOD. ENDCLASS. CLASS lcl_uml_graph IMPLEMENTATION. METHOD header. add( |.PS\n| ). " GNU plot pic2plot macro header for sequence diagram add( |copy "sequence.pic";\n| ). add( |\n| ). ENDMETHOD. METHOD top. super->top( ). add( |step();\n| ). ENDMETHOD. METHOD call. * alternative: * - create_message(from_object,to_object,object_label); * Has from_object create the to_object, labeled with object_label. * The message is labeled with the «create» stereotype. Can also be written as cmessage. add( |message({ is_message-caller },{ is_message-called },"{ mi_actors->short_text( is_message ) }");\n| ). add( |active({ is_message-called });\n| ). ENDMETHOD. METHOD return. * - return_message(from_object,to_object,label) * Draws a return message between two objects, with the given label. Can also be written as rmessage. IF iv_from NE iv_to. add( |return_message({ iv_from },{ iv_to });\n| ). ENDIF. CHECK iv_from IS NOT INITIAL. add( |inactive({ iv_from });\n| ). ENDMETHOD. METHOD participant. * - object(name,label); OR actor(name,label); * Defines an object / actor with the given name, labeled on the diagram as specified. * Actors are typically used instead of objects to indicate operations initiated by human action add( |object({ is_lifeline-index },"{ is_lifeline-label }");\n| ). ENDMETHOD. METHOD complete. add( |complete({ is_lifeline-index });\n| ). ENDMETHOD. METHOD footer. add( |.PE\n| ). ENDMETHOD. METHOD skip_note. * - lifeline_constraint(object,label); * Displays a constraint label (typically given inside curly braces) for the given object. * The constraint will appear on the right of the object's lifeline at the time it appears. * Can also be used to place an message label on the left of a message arrow, rather than its center. * Can also be written as lconstraint. add( |lifeline_constraint({ is_path-caller },"{ c_txt_std }")\n| ). ENDMETHOD. METHOD begin_loop. * - begin_frame(left_object,name,label_text); * Begins a frame with the upper left corner at left_object column and the current line. * The specified label_text is shown in the upper left corner. add( |begin_frame({ iv_from },{ iv_name },"Loop { iv_times } times");\n| ). " pop ENDMETHOD. METHOD end_loop. * - end_frame(right_object,name); * Ends a frame with the lower right corner at right_object column and the current line. * The name must correspond to a begin_frame's name. add( |end_frame({ iv_to }, { iv_name });\n| ). ENDMETHOD. ENDCLASS. CLASS lcl_uml_mscgen IMPLEMENTATION. METHOD header. add( |msc \{\n| ). ENDMETHOD. METHOD delimiter. add( |;\n| ). ENDMETHOD. METHOD separator. add( |, | ). ENDMETHOD. METHOD call. add( |{ is_message-caller }=>{ is_message-called } [label="{ mi_actors->short_text( is_message ) }"];\n| ). ENDMETHOD. METHOD return. CHECK iv_from NE iv_to. add( |{ iv_to }<<{ iv_from }\n| ). ENDMETHOD. METHOD participant. add( |{ is_lifeline-index } [label="{ is_lifeline-label }"]| ). ENDMETHOD. METHOD footer. add( |\}\n| ). ENDMETHOD. METHOD skip_note. add( |{ is_path-called } note { is_path-caller } [label="{ c_txt_std }", textbgcolour="#7fff7f"];\n| ). ENDMETHOD. METHOD begin_loop. * add( |[c:loop { iv_times } times]\n| ). " pop RETURN. ENDMETHOD. METHOD end_loop. * add( |[/c]\n| ). RETURN. ENDMETHOD. ENDCLASS. *---------------- Unit Tests ----------------------------------------------------* CLASS lcl_logger IMPLEMENTATION. METHOD new. ro_log = NEW lcl_logger( ). ENDMETHOD. METHOD verify. cl_abap_unit_assert=>assert_equals( act = mt_log exp = it_exp msg = iv_msg ). ENDMETHOD. METHOD info. APPEND is_log TO mt_log. ENDMETHOD. ENDCLASS. CLASS lcl_uml_logger IMPLEMENTATION. METHOD constructor. super->constructor( ). mo_log = lcl_logger=>new( ). ENDMETHOD. METHOD log. mo_log->info( VALUE #( method = iv_method params = iv_params ) ). ENDMETHOD. METHOD header. log( 'HEADER' ). ENDMETHOD. METHOD call. log( iv_method = 'CALL' iv_params = |{ is_message-id } { is_message-caller } { is_message-called } { mi_actors->short_text( is_message ) }| ). ENDMETHOD. METHOD return. log( iv_method = 'RETURN' iv_params = |{ iv_from } { iv_to }| ). ENDMETHOD. METHOD participant. log( iv_method = 'PARTICIPANT' iv_params = |{ is_lifeline-index } { is_lifeline-label }| ). ENDMETHOD. METHOD footer. log( 'FOOTER' ). ENDMETHOD. METHOD skip_note. log( iv_method = 'SKIP_NOTE' iv_params = |{ is_path-called } { is_path-caller }| ). ENDMETHOD. METHOD begin_loop. log( iv_method = 'BEGIN_LOOP' iv_params = |{ iv_times }| ). ENDMETHOD. METHOD end_loop. log( 'END_LOOP' ). ENDMETHOD. ENDCLASS. CLASS lcl_abap_trace_logger DEFINITION INHERITING FROM lcl_abap_trace FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor. PROTECTED SECTION. METHODS set_constructor REDEFINITION. METHODS set_meth REDEFINITION. METHODS set_form REDEFINITION. METHODS set_function_module REDEFINITION. METHODS set_submit REDEFINITION. METHODS set_tran REDEFINITION. METHODS module REDEFINITION. METHODS event REDEFINITION. PRIVATE SECTION. DATA mo_log TYPE REF TO lcl_logger. METHODS log IMPORTING iv_method TYPE lcl_logger=>ts_log-method iv_params TYPE lcl_logger=>ts_log-params OPTIONAL. ENDCLASS. CLASS lcl_abap_trace_logger IMPLEMENTATION. METHOD constructor. super->constructor( ). mo_log = lcl_logger=>new( ). ENDMETHOD. METHOD log. mo_log->info( VALUE #( method = iv_method params = iv_params ) ). ENDMETHOD. METHOD set_constructor. log( iv_method = 'SET_CONSTRUCTOR' iv_params = |{ mr_src->progindex } { mr_src->textindex }| ). super->set_constructor( ). ENDMETHOD. METHOD set_meth. log( iv_method = 'SET_METH' iv_params = |{ mr_src->progindex } { mr_src->textindex } { mr_src->methindex }| ). super->set_meth( ). ENDMETHOD. METHOD set_form. log( iv_method = 'SET_FORM' iv_params = |{ mr_src->progindex } { mr_src->textindex }| ). super->set_form( ). ENDMETHOD. METHOD set_function_module. log( iv_method = 'SET_FUNCTION_MODULE' iv_params = |{ mr_src->progindex } { mr_src->textindex }| ). super->set_function_module( ). ENDMETHOD. METHOD set_submit. log( iv_method = 'SET_SUBMIT' iv_params = |{ mr_src->progindex } { mr_src->textindex }| ). super->set_submit( ). ENDMETHOD. METHOD set_tran. log( iv_method = 'SET_TRAN' iv_params = |{ mr_src->progindex } { mr_src->textindex }| ). super->set_tran( ). ENDMETHOD. METHOD module. log( iv_method = 'MODULE' iv_params = |{ iv_index }| ). super->module( iv_index ). ENDMETHOD. METHOD event. log( iv_method = 'EVENT' iv_params = |{ mr_src->textindex }| ). super->event( ). ENDMETHOD. ENDCLASS. CONSTANTS: c_my_program TYPE program VALUE 'Y_TEST_PROG'. CLASS ltc_bag DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. CONSTANTS c_index TYPE i VALUE 42. TYPES tt_index TYPE STANDARD TABLE OF sytabix WITH EMPTY KEY. DATA mo_bag TYPE REF TO lcl_bag. METHODS fixture IMPORTING it_index TYPE tt_index. METHODS teardown. METHODS remove_value FOR TESTING. METHODS remove_empty FOR TESTING. METHODS contains_empty FOR TESTING. METHODS contains_value FOR TESTING. ENDCLASS. CLASS ltc_bag IMPLEMENTATION. METHOD fixture. mo_bag = NEW #( ). LOOP AT it_index INTO DATA(lv_idx). mo_bag->add( lv_idx ). ENDLOOP. ENDMETHOD. METHOD teardown. FREE mo_bag. ENDMETHOD. METHOD contains_empty. fixture( VALUE #( ) ). cl_abap_unit_assert=>assert_false( act = mo_bag->contains( c_index ) msg = 'LCL_BAG CONTAINS( ) No Entries Error' ). ENDMETHOD. METHOD contains_value. fixture( VALUE #( ( c_index ) ) ). cl_abap_unit_assert=>assert_true( act = mo_bag->contains( c_index ) msg = 'LCL_BAG CONTAINS( ) One Entry Error' ). ENDMETHOD. METHOD remove_empty. fixture( VALUE #( ) ). mo_bag->remove( c_index ). cl_abap_unit_assert=>assert_false( act = mo_bag->contains( c_index ) msg = 'LCL_BAG REMOVE( ) Empty Error' ). ENDMETHOD. METHOD remove_value. fixture( VALUE #( ( c_index ) ) ). mo_bag->remove( c_index ). cl_abap_unit_assert=>assert_false( act = mo_bag->contains( c_index ) msg = 'LCL_BAG REMOVE( ) Error' ). ENDMETHOD. ENDCLASS. CLASS lcl_satr_state DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING io_atra TYPE REF TO cl_atra_tool_se30_main. METHODS save. METHODS fill IMPORTING iv_textindex TYPE sytabix OPTIONAL iv_methindex TYPE sytabix OPTIONAL iv_progindex TYPE sytabix OPTIONAL. METHODS restore. PRIVATE SECTION. TYPES: BEGIN OF ts_satr_data, it_austab_hier TYPE satr_t_austab_gesamt, it_traceprog TYPE satr_t_traceprog, it_tracetext TYPE satr_t_tracetext, it_tracemeth TYPE satr_t_tracemeth, END OF ts_satr_data. DATA mo_atra TYPE REF TO cl_atra_tool_se30_main. DATA ms_satr TYPE ts_satr_data. ENDCLASS. CLASS lcl_satr_state IMPLEMENTATION. METHOD constructor. mo_atra = io_atra. save( ). ENDMETHOD. METHOD fill. DEFINE macro_fill. DO &1 TIMES. APPEND VALUE #( ) TO mo_atra->&2. ENDDO. END-OF-DEFINITION. macro_fill: iv_textindex it_tracetext, iv_progindex it_traceprog, iv_methindex it_tracemeth. ENDMETHOD. METHOD save. ms_satr-it_austab_hier = mo_atra->it_austab_hier[]. ms_satr-it_traceprog = mo_atra->it_traceprog[]. ms_satr-it_tracetext = mo_atra->it_tracetext[]. ms_satr-it_tracemeth = mo_atra->it_tracemeth[]. ENDMETHOD. METHOD restore. mo_atra->it_austab_hier[] = ms_satr-it_austab_hier. mo_atra->it_traceprog[] = ms_satr-it_traceprog. mo_atra->it_tracetext[] = ms_satr-it_tracetext. mo_atra->it_tracemeth[] = ms_satr-it_tracemeth. ENDMETHOD. ENDCLASS. CLASS ltc_abap_trace DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. CONSTANTS: c_textindex TYPE sytabix VALUE 1, c_progindex TYPE sytabix VALUE 2, " > 1 c_methindex TYPE sytabix VALUE 3, c_fm_index TYPE sytabix VALUE 4. DATA mo_trace TYPE REF TO lcl_abap_trace_logger. DATA mo_log TYPE REF TO lcl_logger. DATA mo_state TYPE REF TO lcl_satr_state. METHODS setup. METHODS teardown. METHODS parse IMPORTING iv_id TYPE satr_austab_gesamt-id iv_subid TYPE satr_austab_gesamt-subid OPTIONAL iv_methindex TYPE sytabix OPTIONAL iv_fm_index TYPE sytabix OPTIONAL. METHODS expected_after_set_form RETURNING VALUE(rt_exp) TYPE lcl_logger=>tt_log. METHODS parse_form_in_module FOR TESTING. METHODS parse_form_at_load FOR TESTING. METHODS parse_form FOR TESTING. METHODS parse_form_in_call_screen FOR TESTING. METHODS parse_form_in_call_dialog FOR TESTING. METHODS parse_function_module FOR TESTING. METHODS parse_method FOR TESTING. METHODS parse_constructor FOR TESTING. METHODS set_constructor FOR TESTING. METHODS parse_dynpro FOR TESTING. METHODS parse_module FOR TESTING. METHODS parse_submit FOR TESTING. METHODS parse_transaction FOR TESTING. ENDCLASS. CLASS ltc_abap_trace IMPLEMENTATION. METHOD setup. mo_trace = NEW lcl_abap_trace_logger( ). mo_log = mo_trace->mo_log. mo_state = NEW #( mo_trace->mo_atra ). mo_state->fill( iv_textindex = c_textindex iv_progindex = nmax( val1 = c_progindex val2 = c_fm_index ) iv_methindex = c_methindex ). ENDMETHOD. METHOD teardown. mo_state->restore( ). FREE mo_state. FREE mo_trace. ENDMETHOD. METHOD parse. mo_trace->mo_atra->it_traceprog[ c_progindex ] = 'MY_PROG'. mo_trace->mr_src = NEW satr_austab_gesamt( id = iv_id subid = iv_subid textindex = c_textindex progindex = c_progindex methindex = iv_methindex pr_2index = iv_fm_index ). CLEAR mo_trace->ms_target. " Called actor mo_trace->parse( ). ENDMETHOD. METHOD expected_after_set_form. rt_exp = VALUE #( ( method = 'SET_FORM' params = |{ c_progindex } { c_textindex }| ) ( method = 'EVENT' params = |{ c_textindex }| ) ( method = 'MODULE' params = |{ c_progindex }| ) ). ENDMETHOD. METHOD parse_form_in_module. parse( iv_id = c_id_module ). mo_log->verify( it_exp = expected_after_set_form( ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FORM in Module Error' ). ENDMETHOD. METHOD parse_form_at_load. parse( iv_id = c_id_load iv_subid = 'D' ). " Dynpro mo_log->verify( it_exp = expected_after_set_form( ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FORM at LOAD Error' ). ENDMETHOD. METHOD parse_form_in_call_dialog. parse( iv_id = c_id_report " CALL - e.g. Call SCREEN/CALL Report iv_subid = 'D' ). " CALL Dialog OR Call Screen mo_log->verify( it_exp = expected_after_set_form( ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FORM in CALL Dialog' ). ENDMETHOD. METHOD parse_form_in_call_screen. parse( iv_id = c_id_report " CALL - e.g. Call SCREEN/CALL Report iv_subid = 'S' ). " CALL Dialog OR Call Screen ). " Called actor mo_log->verify( it_exp = expected_after_set_form( ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FORM in CALL Screen' ). ENDMETHOD. METHOD parse_form. parse( iv_id = c_id_form ). mo_log->verify( it_exp = expected_after_set_form( ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FORM' ). ENDMETHOD. METHOD parse_constructor. parse( iv_id = c_id_class ). mo_log->verify( it_exp = VALUE #( ( method = 'SET_CONSTRUCTOR' params = |{ c_progindex } { c_textindex }| ) ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) SET CONSTRUCTOR Call Error' ). ENDMETHOD. METHOD set_constructor. parse( iv_id = c_id_class ). cl_abap_unit_assert=>assert_equals( act = mo_trace->ms_target exp = VALUE lcl_abap_trace=>ts_event( type = c_type_class event = lcl_abap_trace=>c_create_method global = space ) msg = 'LCL_ABAP_TRACE=>SET_CONSTRUCTOR( ) Error' ). ENDMETHOD. METHOD parse_method. parse( iv_id = c_id_method iv_methindex = c_methindex ). mo_log->verify( it_exp = VALUE #( ( method = 'SET_METH' params = |{ c_progindex } { c_textindex } { c_methindex }| ) ( method = 'MODULE' params = |{ c_progindex }| ) ( method = 'MODULE' params = |{ 0 }| ) ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) METHOD' ). ENDMETHOD. METHOD parse_function_module. parse( iv_id = c_id_func iv_fm_index = c_fm_index ). mo_log->verify( it_exp = VALUE #( ( method = 'SET_FUNCTION_MODULE' params = |{ c_progindex } { c_textindex }| ) ( method = 'EVENT' params = |{ c_textindex }| ) ( method = 'MODULE' params = |{ c_fm_index }| ) ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FUNCTION_MODULE' ). ENDMETHOD. METHOD parse_module. parse( iv_id = c_id_report iv_subid = 'P' ). mo_log->verify( it_exp = VALUE #( ( method = 'MODULE' params = |{ c_progindex }| ) ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) MODULE' ). ENDMETHOD. METHOD parse_dynpro. parse( iv_id = c_id_dynpro iv_subid = 'P' ). mo_log->verify( it_exp = VALUE #( ( method = 'MODULE' params = |{ c_progindex }| ) ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) MODULE Dynpro' ). ENDMETHOD. METHOD parse_submit. CONSTANTS: c_mod_index TYPE sytabix VALUE 1, " larger 0, smaller than c_progindex c_my_event TYPE program VALUE 'MY_EVENT'. mo_trace->mo_atra->it_tracetext[ c_textindex ]-tracetext = c_my_event. mo_trace->mo_atra->it_traceprog[ c_mod_index ] = c_my_event. parse( iv_id = c_id_report iv_subid = 'R' ). mo_log->verify( it_exp = VALUE #( ( method = 'SET_SUBMIT' params = |{ c_progindex } { c_textindex }| ) ( method = 'EVENT' params = |{ c_textindex }| ) ( method = 'MODULE' params = |{ c_mod_index }| ) ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) SET_SUBMIT Error' ). ENDMETHOD. METHOD parse_transaction. parse( iv_id = c_id_report iv_subid = 'T' ). mo_log->verify( it_exp = VALUE #( ( method = 'SET_TRAN' params = |{ c_progindex } { c_textindex }| ) ( method = 'EVENT' params = |{ c_textindex }| ) ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) SET_TRAN Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_class DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mo_class_name TYPE REF TO lcl_class_name. METHODS setup. METHODS teardown. METHODS section_name FOR TESTING. METHODS technical_name FOR TESTING. METHODS external_name FOR TESTING. METHODS is_global FOR TESTING. METHODS is_not_global FOR TESTING. ENDCLASS. CLASS ltc_sequence DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. TYPES tt_sat TYPE STANDARD TABLE OF ts_sat WITH KEY aus_tabix WITH NON-UNIQUE SORTED KEY k1 COMPONENTS aus_tabix. " Performance ? CLASS-METHODS new_sequence IMPORTING is_cfg TYPE ts_diagram_config it_sat TYPE tt_sat RETURNING VALUE(ro_sequence) TYPE REF TO lcl_sequence. CLASS-METHODS new_collection IMPORTING is_cfg TYPE ts_diagram_config it_sat TYPE tt_sat RETURNING VALUE(ro_sequence) TYPE REF TO lcl_sequence. CLASS-METHODS sat_fixture_events RETURNING VALUE(rt_sat) TYPE tt_sat. PRIVATE SECTION. DATA mo_sequence TYPE REF TO lcl_sequence. METHODS setup. METHODS teardown. METHODS collect FOR TESTING. METHODS add FOR TESTING. ENDCLASS. CLASS ltc_sequence IMPLEMENTATION. METHOD new_sequence. ro_sequence = NEW lcl_sequence( is_cfg ). * insert into collection LOOP AT it_sat ASSIGNING FIELD-SYMBOL(<ls_sat>). ro_sequence->lif_collector~collect( <ls_sat> ). ENDLOOP. ENDMETHOD. METHOD new_collection. ro_sequence = NEW lcl_sequence( is_cfg ). * add to collection LOOP AT it_sat ASSIGNING FIELD-SYMBOL(<ls_sat>). ro_sequence->add( <ls_sat> ). ENDLOOP. ENDMETHOD. METHOD sat_fixture_events. CONSTANTS: c_prog TYPE program VALUE 'Y_TEST_PROG', c_ctrl TYPE program VALUE 'LCL_CTRL', c_model TYPE program VALUE 'LCL_MODEL'. rt_sat = VALUE tt_sat( id = 'm' ( from_level = 1 caller = VALUE #( global = c_prog type = c_type_prog ) called = VALUE #( global = c_prog type = c_type_class local = c_ctrl mod = 'MAIN' ) ) ( from_level = 2 caller = VALUE #( global = c_prog type = c_type_class local = c_ctrl mod = 'MAIN' ) called = VALUE #( global = 'SAPPROG' type = c_type_class instance = 6 local = c_model mod = lcl_abap_trace=>c_create_method ) ) ( from_level = 2 caller = VALUE #( global = 'SAPPROG' type = c_type_class local = c_model mod = 'SETUP' ) called = VALUE #( global = c_prog type = c_type_class instance = 0 local = c_ctrl mod = 'CHANGE' ) ) ). ENDMETHOD. METHOD setup. mo_sequence = new_sequence( it_sat = sat_fixture_events( ) is_cfg = lcl_configuration=>get( ) ). ENDMETHOD. METHOD teardown. FREE mo_sequence. ENDMETHOD. METHOD collect. cl_abap_unit_assert=>assert_equals( act = mo_sequence->mt_trace exp = VALUE tt_message( ( id = 'm' from_level = 1 caller = 1 called = 2 msg = 'MAIN' ) ( id = 'K' from_level = 1 caller = 2 called = 3 msg = 'SETUP' ) "<- Skipped ( id = 'm' from_level = 2 caller = 3 called = 2 msg = 'CHANGE' ) ) msg = 'LCL_SEQUENCE->COLLECT( ) Error' ). ENDMETHOD. METHOD add. mo_sequence = new_collection( it_sat = sat_fixture_events( ) is_cfg = lcl_configuration=>get( ) ). cl_abap_unit_assert=>assert_equals( act = mo_sequence->mt_trace exp = VALUE tt_message( ( id = 'm' from_level = 1 caller = 1 called = 2 msg = 'MAIN' ) ( id = 'm' from_level = 2 caller = 2 called = 3 msg = lcl_abap_trace=>c_create_method ) ( id = 'm' from_level = 2 caller = 4 called = 2 msg = 'CHANGE' ) ) msg = 'LCL_SEQUENCE->ADD( ) Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_class IMPLEMENTATION. METHOD setup. mo_class_name = NEW #( ). ENDMETHOD. METHOD teardown. FREE mo_class_name. ENDMETHOD. METHOD section_name. cl_abap_unit_assert=>assert_equals( act = mo_class_name->to_internal( '/IDV0/JN_CL_ASM_ACT' ) exp = '/IDV0/JN_CL_ASM_ACT===========' msg = 'LCL_CLASS SECTION_NAME( ) Error' ). ENDMETHOD. METHOD technical_name. cl_abap_unit_assert=>assert_equals( act = mo_class_name->technical( 'CL_GUI_ALV_GRID' ) exp = 'CL_GUI_ALV_GRID===============CP' msg = 'LCL_CLASS TECHNICAL_NAME( ) Error' ). ENDMETHOD. METHOD external_name. DATA lv_class TYPE program VALUE '/IDV0/JN_CL_ASM_ACT===========CP'. mo_class_name->external( CHANGING cv_clas = lv_class ). cl_abap_unit_assert=>assert_equals( act = lv_class exp = '/IDV0/JN_CL_ASM_ACT' msg = 'LCL_CLASS EXTERNAL_NAME( ) Error' ). ENDMETHOD. METHOD is_global. cl_abap_unit_assert=>assert_true( act = mo_class_name->is_global( 'CL_ABAP_MEMORY_X_WRITER' ) msg = 'LCL_ABAP_TRACE=>IS_GLOBAL_CLASS( ) Error' ). ENDMETHOD. METHOD is_not_global. cl_abap_unit_assert=>assert_false( act = mo_class_name->is_global( 'LCL_BAG' ) msg = 'LCL_ABAP_TRACE=>IS_GLOBAL_CLASS( ) Local Class Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_actors DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. CONSTANTS: c_prog TYPE program VALUE 'Y_TEST_PROG', c_ctrl TYPE program VALUE 'LCL_CTRL', c_model TYPE program VALUE 'LCL_MODEL'. INTERFACES lif_unit_test. CLASS-METHODS sat_fixture_record RETURNING VALUE(rs_sat) TYPE ts_sat. CLASS-METHODS sat_fixture_plant_uml RETURNING VALUE(rt_sat) TYPE ltc_sequence=>tt_sat. CLASS-METHODS sat_fixture_messages RETURNING VALUE(rt_sat) TYPE ltc_sequence=>tt_sat. PRIVATE SECTION. CONSTANTS c_my_class TYPE program VALUE 'YCL_TEST_CLASS'. DATA mi_actors TYPE REF TO lif_actors. DATA ms_exp TYPE ts_sat. METHODS constructor. METHODS setup. METHODS teardown. METHODS get_count RETURNING VALUE(rv_count) TYPE i. METHODS get_text_class IMPORTING iv_id TYPE satr_de_id iv_exp TYPE string iv_msg TYPE string. METHODS put FOR TESTING. METHODS get_text_global_class FOR TESTING. METHODS get_text_local_class FOR TESTING. ENDCLASS. CLASS ltc_actors IMPLEMENTATION. METHOD constructor. ms_exp = VALUE #( id = 1 from_level = 1 caller = VALUE #( global = c_my_program type = c_type_fugr ) called = VALUE #( global = c_my_program local = c_model type = c_type_class instance = 1 mod = lcl_abap_trace=>c_create_method ) ). ENDMETHOD. METHOD sat_fixture_messages. DATA(ls_main) = VALUE ts_call( global = c_prog type = c_type_class local = c_ctrl mod = 'MAIN' ). rt_sat = VALUE ltc_sequence=>tt_sat( id = 'm' ( from_level = 1 caller = VALUE #( global = c_prog type = c_type_prog ) called = ls_main ) ( from_level = 2 caller = ls_main called = VALUE #( global = c_prog type = c_type_class instance = 6 local = c_model mod = lcl_abap_trace=>c_create_method ) ) ( from_level = 2 caller = ls_main called = VALUE #( global = c_prog type = c_type_class local = c_ctrl mod = 'CHANGE' ) ) ( from_level = 3 caller = VALUE #( global = c_prog type = c_type_class local = c_ctrl mod = 'CHANGE' ) called = VALUE #( global = c_prog type = c_type_class instance = 6 local = c_model mod = 'UP' ) ) ) . ENDMETHOD. METHOD sat_fixture_plant_uml. rt_sat = VALUE ltc_sequence=>tt_sat( ( id = 1 from_level = 1 caller = VALUE #( global = c_my_program type = c_type_prog ) called = VALUE #( type = c_type_class instance = 0 global = c_my_program local = 'LCL_CTRL' mod = 'MAIN' ) ) ( id = 'm' from_level = 2 caller = VALUE #( type = c_type_class instance = 0 global = c_my_program local = 'LCL_CTRL' mod = 'MAIN' ) called = VALUE #( type = c_type_class instance = 1 global = c_my_program local = 'LCL_MODEL' mod = lcl_abap_trace=>c_create_method ) ) ( id = ' ' from_level = 3 caller = VALUE #( type = c_type_class instance = 447 global = c_my_program local = 'LCL_CALL_STACK' mod = 'PLANTUML' ) called = VALUE #( type = c_type_class instance = 448 global = c_my_program local = 'LCL_ITERATOR' mod = 'HAS_NEXT' ) ) ). ENDMETHOD. METHOD sat_fixture_record. rs_sat = VALUE ts_sat( id = 'm' from_level = 1 caller = VALUE #( global = c_prog type = c_type_prog ) called = VALUE #( global = c_prog type = c_type_class local = 'LCL_CTRL' mod = 'MAIN' ) aus_tabix = 1 ). ENDMETHOD. METHOD setup. mi_actors = NEW lcl_actors( ). ENDMETHOD. METHOD teardown. FREE mi_actors. ENDMETHOD. METHOD get_count. rv_count = lines( CAST lcl_actors( mi_actors )->mt_actor ). ENDMETHOD. METHOD put. mi_actors->new_path( ms_exp ). DATA(lv_count) = get_count( ). mi_actors->new_path( ms_exp ). " <-- Duplicate cl_abap_unit_assert=>assert_equals( act = get_count( ) exp = lv_count msg = 'LCL_ACTORS->PUT( ) Duplicate Error' ). ENDMETHOD. METHOD get_text_class. ms_exp = VALUE #( id = 1 from_level = 1 caller = VALUE #( global = c_my_program type = c_type_fugr ) called = VALUE #( global = c_my_class type = c_type_class instance = 1 mod = lcl_abap_trace=>c_create_method ) ). mi_actors->new_path( ms_exp ). DATA(ls_msg) = VALUE ts_message( id = iv_id from_level = 1 path-caller = 1 path-called = 2 msg = 'CONSTRUCTOR' ). cl_abap_unit_assert=>assert_equals( act = mi_actors->short_text( ls_msg ) exp = iv_exp msg = |LCL_ACTORS->GET_TEXT( ) { iv_msg }| ). ENDMETHOD. METHOD get_text_global_class. get_text_class( iv_id = c_id_class iv_exp = |Create instance of class { c_my_class }| iv_msg = 'Global Class Get Text Error' ). ENDMETHOD. METHOD get_text_local_class. get_text_class( iv_id = c_id_method iv_exp = |Call method CONSTRUCTOR| iv_msg = 'Local Class Get Text Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_stack DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA ms_exp TYPE lcl_stack=>ts_call_level. DATA mo_stack TYPE REF TO lcl_stack. " CUT METHODS setup. METHODS teardown. METHODS is_empty FOR TESTING. METHODS is_empty_set FOR TESTING. METHODS is_empty_reset FOR TESTING. METHODS push FOR TESTING. METHODS pop FOR TESTING. METHODS pop_empty FOR TESTING. ENDCLASS. CLASS ltc_stack IMPLEMENTATION. METHOD setup. mo_stack = NEW #( ). ms_exp = VALUE #( actor_key = 1 from_level = 1 ). ENDMETHOD. METHOD teardown. FREE mo_stack. ENDMETHOD. METHOD is_empty. cl_abap_unit_assert=>assert_not_bound( act = mo_stack->mr_top msg = 'LCL_STACK is not Empty' ). ENDMETHOD. METHOD pop. mo_stack->push( ms_exp ). cl_abap_unit_assert=>assert_equals( act = mo_stack->pop( ) exp = ms_exp msg = 'LCL_STACK POP( ) Error' ). ENDMETHOD. METHOD push. mo_stack->push( ms_exp ). DATA(ls_act) = mo_stack->pop( ). cl_abap_unit_assert=>assert_true( act = xsdbool( ( ms_exp EQ ls_act ) AND mo_stack->mr_top IS NOT BOUND ) msg = 'LCL_STACK PUSH( ) Error' ). ENDMETHOD. METHOD pop_empty. cl_abap_unit_assert=>assert_initial( act = mo_stack->pop( ) msg = 'LCL_STACK POP( ) Empty' ). ENDMETHOD. METHOD is_empty_reset. mo_stack->push( ms_exp ). cl_abap_unit_assert=>assert_bound( act = mo_stack->mr_top msg = 'LCL_STACK Empty not reset/PUSH( )' ). ENDMETHOD. METHOD is_empty_set. mo_stack->push( ms_exp ). mo_stack->pop( ). cl_abap_unit_assert=>assert_not_bound( act = mo_stack->mr_top msg = 'LCL_STACK Empty not set/POP( )' ). ENDMETHOD. ENDCLASS. CLASS ltc_call DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PROTECTED SECTION. DATA mo_uml TYPE REF TO lcl_uml_logger. DATA mo_actors TYPE REF TO lcl_actors. DATA ms_exp TYPE lcl_stack=>ts_call_level. METHODS create_uml. METHODS free_uml. PRIVATE SECTION. CONSTANTS c_level TYPE i VALUE 1. DATA mo_stack TYPE REF TO lcl_uml_stack. " CUT METHODS setup. METHODS teardown. METHODS fixture_message RETURNING VALUE(rs_message) TYPE ts_message. METHODS call FOR TESTING. METHODS return FOR TESTING. ENDCLASS. CLASS ltc_call IMPLEMENTATION. METHOD create_uml. mo_actors = NEW #( ). mo_uml ?= lcl_uml=>new( iv_uml = lcl_uml=>c_uml_mock ii_actors = mo_actors ). ENDMETHOD. METHOD setup. create_uml( ). mo_stack = NEW #( io_uml = mo_uml ). ms_exp = VALUE #( actor_key = 1 from_level = 1 ). ENDMETHOD. METHOD free_uml. FREE mo_uml. FREE mo_actors. ENDMETHOD. METHOD teardown. FREE mo_stack. free_uml( ). ENDMETHOD. METHOD call. mo_stack->call( fixture_message( ) ). mo_uml->mo_log->verify( it_exp = VALUE lcl_logger=>tt_log( ( method = 'CALL' params = 'm 1 2 Call method Test Call' ) ) iv_msg = 'LCL_UML_STACK MESSAGE Error' ). ENDMETHOD. METHOD fixture_message. rs_message = VALUE ts_message( id = 'm' from_level = c_level caller = 1 called = 2 msg = 'Test Call' ). ENDMETHOD. METHOD return. DATA(ls_message) = fixture_message( ). mo_stack->call( ls_message ). ls_message-caller = 2. ls_message-called = 3. mo_stack->call( ls_message ). mo_stack->return_to( c_level ). mo_uml->mo_log->verify( it_exp = VALUE lcl_logger=>tt_log( ( method = 'CALL' params = 'm 1 2 Call method Test Call' ) ( method = 'CALL' params = 'm 2 3 Call method Test Call' ) ( method = 'RETURN' params = '3 2' ) ) iv_msg = 'LCL_UML_STACK RETURN Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_call_stack DEFINITION FOR TESTING INHERITING FROM ltc_call RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. CONSTANTS c_level TYPE i VALUE 42. DATA mo_messages TYPE REF TO lcl_messages. DATA mo_call TYPE REF TO lcl_call_stack. METHODS setup. METHODS teardown. METHODS first_call FOR TESTING. ENDCLASS. CLASS ltc_call_stack IMPLEMENTATION. METHOD setup. create_uml( ). mo_messages = NEW lcl_messages( it_messages = VALUE #( ( id = 'm' from_level = c_level caller = 1 called = 2 msg = 'TEST' ) ) ). mo_call = NEW lcl_call_stack( io_uml = mo_uml io_messages = mo_messages ). ms_exp = VALUE #( actor_key = 1 from_level = 1 ). ENDMETHOD. METHOD teardown. FREE mo_messages. FREE mo_call. free_uml( ). ENDMETHOD. METHOD first_call. mo_call->message( is_message = mo_messages->next( ) iv_idx = 1 ). mo_uml->mo_log->verify( it_exp = VALUE lcl_logger=>tt_log( ( method = 'CALL' params = | { c_level } { lcl_messages=>c_first_key } | ) ( method = 'CALL' params = 'm 1 2 Call method TEST' ) ( method = 'RETURN' params = '2 1' ) ) iv_msg = 'LCL_CALL_STACK MESSAGE FIRST CALL Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_iterator DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mo_iter TYPE REF TO lcl_messages. " CUT DATA mt_messages TYPE tt_message. METHODS constructor. METHODS setup. METHODS teardown. METHODS has_next_after_init FOR TESTING. METHODS has_next_after_end FOR TESTING. METHODS skip_once FOR TESTING. METHODS skip_3_times FOR TESTING. METHODS next FOR TESTING. METHODS next_error FOR TESTING. METHODS clone_rest FOR TESTING. METHODS clone_resize FOR TESTING. METHODS: has_previous_after_init FOR TESTING, has_previous_at_first FOR TESTING, has_previous_after_first FOR TESTING. ENDCLASS. CLASS ltc_iterator IMPLEMENTATION. METHOD constructor. CONSTANTS c_main TYPE tv_key VALUE 2. mt_messages = VALUE #( ( id = 1 from_level = 1 caller = 1 called = c_main msg = 'MAIN' ) " PROG -> Main ( id = 2 from_level = 2 caller = c_main called = 2 msg = lcl_abap_trace=>c_create_method ) " Main -> Model=>Constructor ( id = 3 from_level = 1 caller = c_main called = 3 msg = 'QUERY' ) " Main -> Model->Query ( id = 4 from_level = 2 caller = 3 called = 4 msg = 'FILTER' ) ). " Model->Query -> Model->Filter ENDMETHOD. METHOD setup. mo_iter = NEW lcl_messages( mt_messages ). ENDMETHOD. METHOD teardown. FREE mo_iter. ENDMETHOD. METHOD skip_once. mo_iter->skip( ). cl_abap_unit_assert=>assert_equals( act = mo_iter->mv_idx exp = 1 msg = 'LCL_MESSAGES SKIP( ) Error' ). ENDMETHOD. METHOD skip_3_times. mo_iter->skip( 3 ). cl_abap_unit_assert=>assert_equals( act = mo_iter->mv_idx exp = 3 msg = 'LCL_MESSAGES SKIP( 2 ) Error' ). ENDMETHOD. METHOD has_next_after_init. cl_abap_unit_assert=>assert_true( act = mo_iter->has_next( ) msg = 'LCL_MESSAGES HAS_NEXT( ) Start Error' ). ENDMETHOD. METHOD has_next_after_end. mo_iter->skip( mo_iter->mv_size ). cl_abap_unit_assert=>assert_false( act = mo_iter->has_next( ) msg = 'LCL_MESSAGES HAS_NEXT( ) End Error' ). ENDMETHOD. METHOD next. cl_abap_unit_assert=>assert_equals( act = mo_iter->next( ) exp = VALUE #( mo_iter->mt_list[ 1 ] ) msg = 'LCL_MESSAGES_ITERATOR NEXT( ) Error' ). ENDMETHOD. METHOD next_error. TRY. mo_iter->skip( mo_iter->mv_size ). mo_iter->next( ). cl_abap_unit_assert=>fail( msg = 'LCL_MESSAGES NEXT( ) No Exception' ). CATCH cx_sy_itab_line_not_found ##NO_HANDLER. " Do nothing ENDTRY. ENDMETHOD. METHOD has_previous_after_init. cl_abap_unit_assert=>assert_true( act = mo_iter->is_first( ) msg = 'LCL_MESSAGES IS_FIRST( ) Error' ). ENDMETHOD. METHOD has_previous_at_first. mo_iter->next( ). cl_abap_unit_assert=>assert_true( act = mo_iter->is_first( ) msg = 'LCL_MESSAGES IS_FIRST( ) First' ). ENDMETHOD. METHOD has_previous_after_first. mo_iter->next( ). mo_iter->next( ). cl_abap_unit_assert=>assert_false( act = mo_iter->is_first( ) msg = 'LCL_MESSAGES IS_FIRST( ) Next' ). ENDMETHOD. METHOD clone_rest. CONSTANTS c_start TYPE i VALUE 2. cl_abap_unit_assert=>assert_equals( act = NEW lcl_messages( it_messages = mo_iter->mt_list iv_start = c_start )->mv_idx exp = c_start - 1 msg = 'LCL_MESSAGESCONSTRUCTOR( ) Index Error' ). ENDMETHOD. METHOD clone_resize. CONSTANTS: c_start TYPE i VALUE 2, c_size TYPE i VALUE 3. cl_abap_unit_assert=>assert_equals( act = NEW lcl_messages( it_messages = mo_iter->mt_list iv_start = c_start iv_stop = c_size )->mv_size exp = c_size msg = 'LCL_MESSAGES CONSTRUCTOR( ) Size Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_uml DEFINITION FOR TESTING ABSTRACT CREATE PROTECTED RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PROTECTED SECTION. DATA mi_actors TYPE REF TO lif_actors. DATA mo_uml TYPE REF TO lcl_uml. " CUT DATA mv_obj_name TYPE sobj_name. METHODS test_participant IMPORTING iv_exp TYPE string. METHODS test_message IMPORTING iv_exp TYPE string. METHODS test_return IMPORTING iv_exp TYPE string. METHODS test_footer IMPORTING iv_exp TYPE string. METHODS setup_uml IMPORTING iv_uml TYPE lcl_uml=>tv_uml iv_name TYPE sobj_name. METHODS teardown_uml. ENDCLASS. CLASS ltc_uml IMPLEMENTATION. METHOD setup_uml. mi_actors = NEW lcl_actors( ). mo_uml = lcl_uml=>new( iv_uml = iv_uml ii_actors = mi_actors ). mv_obj_name = iv_name. ENDMETHOD. METHOD teardown_uml. FREE mo_uml. FREE mi_actors. ENDMETHOD. METHOD test_participant. DATA(lv_exp) = mo_uml->mv_diagram && iv_exp. mo_uml->participant( is_lifeline = VALUE #( label = `Test Participant` index = 42 ) ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = mv_obj_name && ` PARTICIPANT( ) Error` ). ENDMETHOD. METHOD test_message. DATA(lv_exp) = mo_uml->mv_diagram && iv_exp. mo_uml->message( VALUE ts_message( id = c_id_method caller = 3 called = 5 from_level = 2 msg = 'MAIN' ) ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = mv_obj_name && ` MESSAGE( ) Error` ). ENDMETHOD. METHOD test_return. DATA(lv_exp) = mo_uml->mv_diagram && iv_exp. mo_uml->return( iv_from = 2 iv_to = 1 ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = mv_obj_name && ` RETURN( ) Error` ). ENDMETHOD. METHOD test_footer. DATA(lv_exp) = mo_uml->mv_diagram && iv_exp. mo_uml->footer( ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = mv_obj_name && ` FOOTER( ) Error` ). ENDMETHOD. ENDCLASS. CLASS ltc_plant_uml DEFINITION INHERITING FROM ltc_uml FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. METHODS setup. METHODS teardown. METHODS add FOR TESTING. METHODS message FOR TESTING. METHODS call_and_skip FOR TESTING. METHODS initial_message FOR TESTING. METHODS first_message FOR TESTING. METHODS call_factory FOR TESTING. METHODS return FOR TESTING. METHODS return_from_initial FOR TESTING. METHODS return_same_level FOR TESTING. METHODS participant FOR TESTING. METHODS plantuml_header FOR TESTING. METHODS footer FOR TESTING. ENDCLASS. CLASS ltc_plant_uml IMPLEMENTATION. METHOD setup. setup_uml( iv_uml = lcl_uml=>c_uml_plant iv_name = 'LCL_PLANT_UML' ). ENDMETHOD. METHOD teardown. teardown_uml( ). ENDMETHOD. METHOD add. CONSTANTS c_code TYPE string VALUE 'New PlantUML Code'. DATA(lv_before) = mo_uml->mv_diagram. mo_uml->add( c_code ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_before && c_code msg = 'LCL_UML_PLANT ADD( ) Error' ). ENDMETHOD. METHOD message. test_message( |3 -> 5: Call method MAIN\nactivate 5\n| ). ENDMETHOD. METHOD call_and_skip. DATA(lv_exp) = mo_uml->mv_diagram && |1 -> 2: <b>Skipping over SAP code until calling MODIFIER</b>\n| && |activate 2\nnote over 1,2\n{ lcl_uml_plant=>c_txt_std }\nend note\n|. mo_uml->message( VALUE ts_message( id = c_id_skip from_level = 1 msg = 'MODIFIER' caller = 1 called = 2 ) ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT MESSAGE( ) Skip' ). ENDMETHOD. METHOD first_message. DATA(lv_exp) = mo_uml->mv_diagram && |{ lcl_messages=>c_first_key } -> 2: \nactivate 2\n|. mo_uml->message( is_message = VALUE ts_message( caller = lcl_messages=>c_first_key called = 2 ) ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT MESSAGE( ) First' ). ENDMETHOD. METHOD initial_message. DATA(lv_exp) = mo_uml->mv_diagram. mo_uml->message( is_message = VALUE ts_message( ) ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT MESSAGE( ) Initial' ). ENDMETHOD. METHOD call_factory. DATA(ls_path) = mi_actors->new_path( VALUE ts_sat( called = VALUE #( local = 'LCL_TEST' type = c_type_class ) ) ). DATA(lv_exp) = mo_uml->mv_diagram && |3 -> { ls_path-called }: Create instance of class LCL_TEST\nactivate { ls_path-called }\n|. mo_uml->message( VALUE ts_message( id = c_id_class caller = 3 called = ls_path-called from_level = 2 msg = 'CONSTRUCTOR' ) ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT MESSAGE( ) Factory' ). ENDMETHOD. METHOD return. test_return( |2 --> 1\ndeactivate 2\n| ). ENDMETHOD. METHOD return_from_initial. DATA(lv_exp) = mo_uml->mv_diagram && |0 --> 1\n|. mo_uml->return( iv_from = 0 iv_to = 1 ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT RETURN( ) From Initial Error' ). ENDMETHOD. METHOD return_same_level. DATA(lv_exp) = mo_uml->mv_diagram && |deactivate 3\n|. mo_uml->return( iv_from = 3 iv_to = 3 ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT RETURN( ) Same Level' ). ENDMETHOD. METHOD participant. test_participant( |participant "Test Participant" as 42\n| ). ENDMETHOD. METHOD plantuml_header. cl_abap_unit_assert=>assert_equals( act = CAST lcl_uml_plant( mo_uml )->scale( lcl_configuration=>get( ) ) exp = |scale 0.50\n| msg = 'LCL_UML_PLANT->SCALE( ) Error' ). ENDMETHOD. METHOD footer. test_footer( |@enduml\n| ). ENDMETHOD. ENDCLASS. CLASS ltc_graph_uml DEFINITION INHERITING FROM ltc_uml FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. METHODS setup. METHODS teardown. METHODS call FOR TESTING. METHODS return FOR TESTING. METHODS participant FOR TESTING. METHODS footer FOR TESTING. ENDCLASS. CLASS ltc_graph_uml IMPLEMENTATION. METHOD setup. setup_uml( iv_uml = lcl_uml=>c_uml_graph iv_name = 'LCL_GRAPH_UML' ). ENDMETHOD. METHOD teardown. teardown_uml( ). ENDMETHOD. METHOD call. test_message( |message(3,5,"Call method MAIN");\nactive(5);\n| ). ENDMETHOD. METHOD return. test_return( |return_message(2,1);\ninactive(2);\n| ). ENDMETHOD. METHOD participant. test_participant( |object(42,"Test Participant");\n| ). ENDMETHOD. METHOD footer. test_footer( |.PE\n| ). ENDMETHOD. ENDCLASS. CLASS ltc_mscgen_uml DEFINITION INHERITING FROM ltc_uml FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. METHODS setup. METHODS teardown. METHODS call FOR TESTING. METHODS return FOR TESTING. METHODS participant FOR TESTING. METHODS footer FOR TESTING. ENDCLASS. CLASS ltc_mscgen_uml IMPLEMENTATION. METHOD setup. setup_uml( iv_uml = lcl_uml=>c_uml_mscgen iv_name = 'LCL_MSCGEN_UML' ). ENDMETHOD. METHOD teardown. teardown_uml( ). ENDMETHOD. METHOD call. test_message( |3=>5 [label="Call method MAIN"];\n| ). ENDMETHOD. METHOD return. test_return( |1<<2\n| ). ENDMETHOD. METHOD participant. test_participant( |42 [label="Test Participant"]| ). ENDMETHOD. METHOD footer. test_footer( |\}\n| ). ENDMETHOD. ENDCLASS. CLASS ltc_file_name DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. CONSTANTS: c_path TYPE string VALUE 'C:\Temp\Dokumente\UML\', c_prefix TYPE string VALUE 'test' ##NO_TEXT. DATA mo_name TYPE REF TO lcl_file_name. METHODS setup. METHODS teardown. METHODS fixture. METHODS prefix_with_txt_extension FOR TESTING. METHODS prefix_without_extension FOR TESTING. METHODS get_fullpath FOR TESTING. ENDCLASS. CLASS ltc_file_name IMPLEMENTATION. METHOD setup. mo_name = lcl_file_name=>new( lcl_diagram_text=>c_mode_txt ). fixture( ). ENDMETHOD. METHOD fixture. mo_name->ms_file-name = c_prefix && mo_name->ms_file-ext. mo_name->ms_file-path = c_path && mo_name->ms_file-name. ENDMETHOD. METHOD teardown. FREE mo_name. ENDMETHOD. METHOD prefix_with_txt_extension. cl_abap_unit_assert=>assert_equals( exp = c_prefix act = mo_name->get_prefix( ) msg = 'LCL_FILE_NAME->GET_PREFIX( ) Error' ). ENDMETHOD. METHOD prefix_without_extension. mo_name->ms_file-ext = space. fixture( ). cl_abap_unit_assert=>assert_equals( exp = c_prefix act = mo_name->get_prefix( ) msg = 'LCL_FILE_NAME->GET_PREFIX( ) Error No Prefix' ). ENDMETHOD. METHOD get_fullpath. cl_abap_unit_assert=>assert_equals( exp = mo_name->ms_file-path act = mo_name->get_fullpath( ) msg = 'LCL_FILE_NAME->GET_FULLPATH( ) Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_diagram DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mo_gen TYPE REF TO lcl_diagram_plant_uml. METHODS setup. METHODS teardown. METHODS set_diagram IMPORTING iv_text TYPE string. METHODS to_xstring FOR TESTING. METHODS to_url FOR TESTING. ENDCLASS. CLASS ltc_diagram IMPLEMENTATION. METHOD setup. lcl_configuration=>gs_cfg-skip_dialog = abap_true. lcl_configuration=>gs_cfg-output_mode = lcl_diagram_text=>c_mode_aut. mo_gen ?= lcl_sequence=>to_diagram( lcl_configuration=>get( ) ). ENDMETHOD. METHOD teardown. FREE mo_gen. ENDMETHOD. METHOD set_diagram. mo_gen->mv_diagram = iv_text. ENDMETHOD. METHOD to_url. set_diagram( |Bob -> Alice : hello| ). cl_abap_unit_assert=>assert_equals( exp = 'SyfFKj2rKt3CoKnELR1Io4ZDoSa70000' act = mo_gen->to_url( iv_base_url = space ) msg = 'LCL_SEQ_GEN TO_URL( ) Error' ). ENDMETHOD. METHOD to_xstring. set_diagram( 'Polyfon zwitschernd aßen Mäxchens Vögel Rüben, Joghurt und Quark' ). cl_abap_unit_assert=>assert_equals( exp = 'UG9seWZvbiB6d2l0c2NoZXJuZCBhw59lbiBNw6R4Y2hlbnMgVsO2Z2VsIFLDvGJlbiwgSm9naHVydCB1bmQgUXVhcms=' act = cl_http_utility=>encode_x_base64( mo_gen->to_xstring( mo_gen->mv_diagram ) ) msg = 'LCL_SEQ_GEN TO_XSTRING( ) Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_messages DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. METHODS constructor. PROTECTED SECTION. DATA mt_sat TYPE ltc_sequence=>tt_sat. DATA mo_uml TYPE REF TO lcl_uml. METHODS to_uml IMPORTING it_sat TYPE ltc_sequence=>tt_sat iv_uml TYPE lcl_uml=>tv_uml DEFAULT lcl_uml=>c_uml_plant iv_compact_trace TYPE char01 DEFAULT abap_true RETURNING VALUE(rv_uml) TYPE string. METHODS get_log RETURNING VALUE(ro_log) TYPE REF TO lcl_logger. PRIVATE SECTION. METHODS plantuml_header RETURNING VALUE(rv_header) TYPE string. METHODS expected_plantuml_source RETURNING VALUE(rv_exp) TYPE string. METHODS: calls_for_empty_diagram FOR TESTING, plantuml_for_empty_diagram FOR TESTING. METHODS sample_plantuml_body FOR TESTING. METHODS calls_for_sample_output FOR TESTING. ENDCLASS. CLASS ltc_messages IMPLEMENTATION. METHOD constructor. mt_sat = ltc_actors=>sat_fixture_messages( ). ENDMETHOD. METHOD to_uml. DATA(lo_calls) = ltc_sequence=>new_sequence( it_sat = it_sat is_cfg = VALUE #( uml_format = iv_uml compact_trace = iv_compact_trace ) )->to_call_stack( ). rv_uml = lo_calls->to_uml( ). mo_uml = lo_calls->mo_uml. ENDMETHOD. METHOD get_log. ro_log = CAST lcl_uml_logger( mo_uml )->mo_log. ENDMETHOD. METHOD calls_for_empty_diagram. to_uml( it_sat = VALUE #( ) iv_uml = lcl_uml=>c_uml_mock ). get_log( )->verify( it_exp = VALUE lcl_logger=>tt_log( ( method = 'HEADER' ) ( method = 'FOOTER' ) ) iv_msg = 'LCL_MESSAGES TO_UML( ) Call Stack Empty Diagram Header/Footer Error' ). ENDMETHOD. METHOD calls_for_sample_output. to_uml( it_sat = mt_sat iv_uml = lcl_uml=>c_uml_mock ). get_log( )->verify( it_exp = VALUE lcl_logger=>tt_log( ( method = 'HEADER' ) ( method = 'PARTICIPANT' params = |1 PROG\\nY_TEST_PROG| ) ( method = 'PARTICIPANT' params = |2 Static Methods of Class LCL_CTRL\\nY_TEST_PROG| ) ( method = 'PARTICIPANT' params = |3 ObjectId:6 of Class LCL_MODEL\\nY_TEST_PROG| ) ( method = 'CALL' params = | 1 1 | ) " First Call w/o type ( method = 'CALL' params = |m 1 2 Call method MAIN| ) ( method = 'CALL' params = |m 2 3 Call method CONSTRUCTOR| ) ( method = 'RETURN' params = |3 2| ) ( method = 'CALL' params = |m 2 2 Call method CHANGE| ) ( method = 'CALL' params = |m 2 3 Call method UP| ) ( method = 'RETURN' params = |3 2| ) ( method = 'RETURN' params = |2 2| ) " Why ??? ( method = 'RETURN' params = |2 1| ) ( method = 'FOOTER' ) ) iv_msg = 'LCL_MESSAGES TO_UML( ) Call Stack Error' ). ENDMETHOD. METHOD plantuml_header. DATA(lo_uml) = NEW lcl_uml_plant( ). lo_uml->header( ). rv_header = lo_uml->mv_diagram. ENDMETHOD. METHOD expected_plantuml_source. rv_exp = plantuml_header( ) && |participant "PROG\\nY_TEST_PROG" as 1\nparticipant "Static Methods of Class LCL_CTRL\\nY_TEST_PROG" as 2\n| && |participant "ObjectId:6 of Class LCL_MODEL\\nY_TEST_PROG" as 3\n1 -> 1: \nactivate 1\n| && |1 -> 2: Call method MAIN\nactivate 2\n2 -> 3: Call method CONSTRUCTOR\nactivate 3\n3 --> 2\ndeactivate 3\n2 -> 2: Call method CHANGE\n| && |activate 2\n2 -> 3: Call method UP\nactivate 3\n3 --> 2\ndeactivate 3\ndeactivate 2\n2 --> 1\n| && |deactivate 2\n@enduml\n|. ENDMETHOD. METHOD sample_plantuml_body. cl_abap_unit_assert=>assert_equals( act = to_uml( mt_sat ) exp = expected_plantuml_source( ) msg = 'LCL_MESSAGES->TO_UML( ) PlantUML Error' ). ENDMETHOD. METHOD plantuml_for_empty_diagram. cl_abap_unit_assert=>assert_char_cp( act = to_uml( VALUE #( ) ) exp = |@startuml\nhide footbox\nautonumber\n*skinparam*@enduml\n| msg = 'LCL_MESSAGES TO_UML( ) Empty Diagram Header/Footer Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_call_compact DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mo_calls TYPE REF TO lcl_call_stack_compact. " CUT DATA mo_iterator TYPE REF TO lcl_messages. DATA mo_uml TYPE REF TO lcl_uml. METHODS setup. METHODS teardown. METHODS cycle_trace RETURNING VALUE(rt_trace) TYPE tt_message. METHODS fixture IMPORTING iv_compact_trace TYPE xsdboolean iv_uml TYPE lcl_uml=>tv_uml DEFAULT lcl_uml=>c_uml_plant RETURNING VALUE(ro_calls) TYPE REF TO lcl_call_stack. * METHODS create_cycle FOR TESTING. METHODS cycle_no_compress FOR TESTING. METHODS begin_loop FOR TESTING. METHODS calls_for_begin_loop FOR TESTING. METHODS end_loop FOR TESTING. METHODS no_end_loop FOR TESTING. ENDCLASS. CLASS ltc_call_compact IMPLEMENTATION. METHOD cycle_trace. rt_trace = VALUE #( ( id = 'm' from_level = 1 caller = 1 called = 2 msg = 'MAIN' ) ( id = 'm' from_level = 2 caller = 2 called = 3 msg = lcl_abap_trace=>c_create_method ) ). APPEND LINES OF rt_trace TO rt_trace. " Create Loop ENDMETHOD. METHOD setup. mo_calls ?= fixture( iv_compact_trace = abap_true iv_uml = lcl_uml=>c_uml_plant ). ENDMETHOD. METHOD fixture. mo_uml = lcl_uml=>new( iv_uml = iv_uml ii_actors = NEW lcl_actors( ) ). ro_calls = lcl_uml_factory=>new_calls( iv_compact_trace = iv_compact_trace it_trace = cycle_trace( ) io_uml = mo_uml ). mo_iterator = ro_calls->mo_messages. ENDMETHOD. METHOD teardown. FREE mo_iterator. FREE mo_calls. FREE mo_uml. ENDMETHOD. METHOD create_cycle. cl_abap_unit_assert=>assert_equals( act = lines( mo_calls->mt_cycle ) exp = 1 msg = 'LCL_CALL_STACK_COMPACT CONSTRUCTOR( ) Cycle Error' ). ENDMETHOD. METHOD cycle_no_compress. TRY. mo_calls ?= fixture( iv_compact_trace = abap_false ). cl_abap_unit_assert=>fail( msg = 'LCL_CALL_STACK_COMPACT CONSTRUCTOR( ) No Cycle Error' ). CATCH cx_sy_move_cast_error ##NO_HANDLER. * Expected: Object of type LCL_MESSAGES created, not compatible with LCL_MESSAGE_COMPACT ENDTRY. ENDMETHOD. METHOD begin_loop. mo_iterator->next( ). mo_calls->begin( iv_idx = mo_iterator->mv_idx iv_from = 1 ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = |loop 2 times\n| msg = 'LCL_CALL_STACK_COMPACT BEGIN_LOOP( ) Error' ). ENDMETHOD. METHOD calls_for_begin_loop. mo_calls ?= fixture( iv_compact_trace = abap_true iv_uml = lcl_uml=>c_uml_mock ). mo_iterator->next( ). mo_calls->begin( iv_idx = mo_iterator->mv_idx iv_from = 1 ). CAST lcl_uml_logger( mo_uml )->mo_log->verify( it_exp = VALUE lcl_logger=>tt_log( ( method = 'BEGIN_LOOP' params = '2' ) ) iv_msg = 'LCL_CALL_STACK_COMPACT BEGIN_LOOP( ) Calls Error' ). ENDMETHOD. METHOD end_loop. mo_iterator->skip( 2 ). mo_calls->end( iv_idx = mo_iterator->mv_idx iv_to = 2 ). " need a MOCK object here cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = |end\n| msg = 'LCL_CALL_STACK_COMPACT END_LOOP( ) Error' ). ENDMETHOD. METHOD no_end_loop. mo_calls ?= fixture( abap_true ). mo_iterator->skip( 4 ). mo_calls->end( iv_idx = mo_iterator->mv_idx iv_to = 2 ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = space msg = 'LCL_CALL_STACK_COMPACT END_LOOP( ) No End Loop Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_cycles DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. INTERFACES lif_cycles. PRIVATE SECTION. DATA mt_messages TYPE tt_message. DATA mo_index TYPE REF TO lcl_trace_index. DATA mt_cycles TYPE lcl_call_stack_compact=>tt_cycle. METHODS fixture IMPORTING iv_cycles TYPE n DEFAULT 1. METHODS detect_cycles IMPORTING iv_cycles TYPE n DEFAULT 1. METHODS next_match FOR TESTING. METHODS detect FOR TESTING. METHODS detect_4 FOR TESTING. METHODS fold FOR TESTING. ENDCLASS. CLASS ltc_cycles IMPLEMENTATION. METHOD fixture. mt_messages = VALUE #( id = 'm' ( from_level = 1 caller = 1 called = 2 msg = 'MAIN' ) ( from_level = 2 caller = 2 called = 3 msg = lcl_abap_trace=>c_create_method ) ( from_level = 2 caller = 2 called = 4 msg = 'CHANGE' ) ( from_level = 2 caller = 2 called = 4 msg = 'CHANGE' ) ( from_level = 3 caller = 4 called = 6 msg = 'UP' ) ). DATA(lt_msg) = mt_messages. DO iv_cycles TIMES. APPEND LINES OF lt_msg TO mt_messages. ENDDO. lt_msg = VALUE #( id = 'm' ( from_level = 1 caller = 10 called = 11 msg = 'MAIN' ) ( from_level = 2 caller = 11 called = 16 msg = lcl_abap_trace=>c_create_method ) ). APPEND LINES OF lt_msg TO mt_messages. mo_index = NEW #( it_trace = mt_messages ii_cycles = me ). ENDMETHOD. METHOD lif_cycles~collect. INSERT is_cycle INTO TABLE mt_cycles. ENDMETHOD. METHOD lif_cycles~accepts. rv_flag = abap_true. ENDMETHOD. METHOD detect_cycles. fixture( iv_cycles ). lcl_pattern=>new( it_xindex = mo_index->mt_xindex ii_cycles = me )->detect_cycles( ). ENDMETHOD. METHOD next_match. TRY. fixture( 1 ). lcl_pattern=>new( it_xindex = mo_index->mt_xindex ii_cycles = me )->next_occurrence_from( -10 ). * Should not throw an exception. CATCH cx_dynamic_check. * If an exception is triggered, the run-time will catch it (not propagated) * It will appear in the result list without a proper message. * So this is just for developer information, for now cl_abap_unit_assert=>fail( msg = 'LCL_PATTERN NEXT_OCCURRENCE_FROM( ) Index Error' ). ENDTRY. ENDMETHOD. METHOD detect. detect_cycles( ). cl_abap_unit_assert=>assert_equals( act = lines( mt_cycles ) exp = 2 msg = 'LCL_PATTERN DETECT_CYCLES( ) Error' ). ENDMETHOD. METHOD detect_4. detect_cycles( 4 ). cl_abap_unit_assert=>assert_equals( act = lines( mt_cycles ) exp = 2 msg = 'LCL_PATTERN DETECT_CYCLES( ) Level 4 Error' ). ENDMETHOD. METHOD fold. detect_cycles( 3 ). NEW lcl_uml_compactor( )->fold( EXPORTING it_trace = mt_messages IMPORTING et_messages = DATA(lt_act) ). DATA(lt_exp) = VALUE tt_message( id = 'm' ( from_level = 1 caller = 1 called = 2 msg = 'MAIN' ) ( from_level = 2 caller = 2 called = 3 msg = lcl_abap_trace=>c_create_method ) ( from_level = 2 caller = 2 called = 4 msg = 'CHANGE' ) ( from_level = 3 caller = 4 called = 6 msg = 'UP' ) ( from_level = 1 caller = 10 called = 11 msg = 'MAIN' ) ( from_level = 2 caller = 11 called = 16 msg = lcl_abap_trace=>c_create_method ) ). cl_abap_unit_assert=>assert_equals( act = lt_act exp = lt_exp msg = 'LCL_LOOP_COMPACTOR->FOLD( ) LOGIC Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_shrinkage DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. METHODS reconcile_loop_references FOR TESTING. ENDCLASS. CLASS ltc_shrinkage IMPLEMENTATION. METHOD reconcile_loop_references. DATA(lt_cycles) = VALUE lcl_call_stack_compact=>tt_cycle( ( start = 1 end = 48 last = 192 times = 4 ) " 1 + 48 x 4 - 1 = 192 ( start = 3 end = 24 last = 46 times = 2 ) " 3 + 22 x 2 - 1 = 46 ( start = 19 end = 20 last = 22 times = 2 ) " 19 + 2 x 2 - 1 = 22 ( start = 193 end = 194 last = 200 times = 4 ) " 193 + 2 x 4 - 1 = 200 ( start = 201 end = 632 last = 476264 times = 1102 ) " 201 + 432 x 1102 - 1 = 476264 ( start = 217 end = 218 last = 220 times = 2 ) ). " 217 + 2 x 2 - 1 = 220 lcl_shrinkage=>shrink_references( CHANGING ct_cycles = lt_cycles ). cl_abap_unit_assert=>assert_equals( act = lt_cycles exp = VALUE lcl_call_stack_compact=>tt_cycle( ( start = 1 end = 24 last = 24 times = 4 ) ( start = 3 end = 22 last = 22 times = 2 ) ( start = 19 end = 20 last = 20 times = 2 ) ( start = 25 end = 26 last = 26 times = 4 ) ( start = 27 end = 456 last = 456 times = 1102 ) ( start = 43 end = 44 last = 44 times = 2 ) ) msg = 'LCL_SHRINKAGE->SHRINK_REFERENCES( ) Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_trace DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. INTERFACES lif_collector. PRIVATE SECTION. DATA ms_cfg TYPE ts_diagram_config. DATA mo_trace TYPE REF TO lcl_trace. " CUT DATA mo_state TYPE REF TO lcl_satr_state. DATA mt_act_trace TYPE tt_message. DATA mt_sat TYPE STANDARD TABLE OF ts_sat WITH EMPTY KEY. METHODS setup. METHODS teardown. METHODS fill_trace. METHODS fixture_add_more. METHODS fixture_caller. METHODS fixture_prepare. METHODS add_message FOR TESTING. METHODS add_more FOR TESTING. METHODS skip_message FOR TESTING. METHODS filter_message FOR TESTING. METHODS save_caller FOR TESTING. METHODS test_output FOR TESTING. METHODS skip_record FOR TESTING. ENDCLASS. CLASS ltc_trace IMPLEMENTATION. METHOD setup. CLEAR mt_act_trace. CLEAR mt_sat. ms_cfg = VALUE ts_diagram_config( pattern = VALUE #( ( 'Y*' ) ) compact_trace = abap_true output_mode = lcl_diagram_text=>c_mode_aut ). mo_trace = NEW #( ). mo_state = NEW #( mo_trace->mo_atra ). ENDMETHOD. METHOD teardown. mo_state->restore( ). FREE mo_state. FREE mo_trace. ENDMETHOD. METHOD fill_trace. DATA(lo_collector) = NEW lcl_sequence( ms_cfg ). mo_trace->fill( lo_collector ). mt_act_trace = lo_collector->mt_trace. ENDMETHOD. METHOD lif_collector~collect. * No filter / No conversion APPEND is_sat TO mt_sat. ENDMETHOD. METHOD add_message. mo_trace->mo_atra->it_austab_hier = VALUE #( ( id = 'm' index = 1 ebene = 2 caller = 3 event = '>' ) ). mo_trace->fill( me ). cl_abap_unit_assert=>assert_equals( act = lines( mt_sat ) exp = 1 msg = 'LCL_TRACE ADD_MESSAGE( ) Error' ). ENDMETHOD. METHOD filter_message. mo_trace->mo_atra->it_traceprog = VALUE #( ( cprog = 'Y_PROG' object = c_type_prog ) ( cprog = 'SAPPROG' object = c_type_class ) ). mo_trace->mo_atra->it_austab_hier = VALUE #( ( id = 'O' index = 1 ebene = 0 caller = 0 event = '>' ) ( id = 'V' subid = 'O' index = 2 ebene = 1 caller = 1 event = '>' ) "DB Op. ( id = 'P' index = 3 ebene = 1 caller = 1 event = '>' progindex = 1 textindex = 1 ) ( id = 'm' index = 4 ebene = 2 caller = 1 event = '>' progindex = 1 methindex = 1 ) ( id = 'm' subid = 'x' index = 5 ebene = 2 caller = 1 event = '<' ) ). fill_trace( ). cl_abap_unit_assert=>assert_equals( act = lines( mt_act_trace ) exp = 1 msg = 'LCL_TRACE ADD_MESSAGE( ) Filter Error' ). ENDMETHOD. METHOD skip_message. mo_trace->mo_atra->it_austab_hier = VALUE #( ( id = 'm' index = 1 ebene = 2 caller = 2 event = '<' ) ). fill_trace( ). cl_abap_unit_assert=>assert_initial( act = lines( mt_act_trace ) msg = 'LCL_TRACE ADD_MESSAGE( ) Skip Error' ). ENDMETHOD. METHOD save_caller. mo_trace->mo_atra->it_austab_hier = VALUE #( id = 'm' ( index = 1 ebene = 2 caller = 3 event = '>' ) ( index = 1 ebene = 2 caller = 2 event = '<' ) ). fill_trace( ). cl_abap_unit_assert=>assert_equals( act = lines( mo_trace->mt_caller ) exp = 1 msg = 'LCL_TRACE ADD_MESSAGE( ) Save Caller' ). ENDMETHOD. METHOD test_output. lcl_configuration=>gs_cfg-skip_dialog = abap_true. lcl_configuration=>gs_cfg-output_mode = lcl_diagram_text=>c_mode_aut. lcl_sequence=>to_diagram( lcl_configuration=>get( ) )->output( ). ENDMETHOD. METHOD fixture_prepare. mo_trace->mo_atra->it_traceprog = VALUE #( ( cprog = ltc_actors=>c_prog object = c_type_prog ) ( cprog = ltc_actors=>c_prog object = c_type_class ) ( cprog = 'SAPPROG' object = c_type_class ) ). mo_trace->mo_atra->it_tracetext = VALUE #( ( tracetext = 'MAIN' ) ( tracetext = lcl_abap_trace=>c_create_method ) ( tracetext = 'CHANGE' ) ( tracetext = ltc_actors=>c_ctrl ) ( tracetext = ltc_actors=>c_model ) ( tracetext = ltc_actors=>c_prog ) ). mo_trace->mo_atra->it_tracemeth = VALUE #( ( methode = 'MAIN' ) ( methode = lcl_abap_trace=>c_create_method ) ( methode = 'CHANGE' ) ). ENDMETHOD. METHOD fixture_add_more. * Fixture: Scenario from ltc_objects=>sat_fixture_events( ) fixture_prepare( ). mo_trace->mo_atra->it_austab_hier = VALUE #( ( id = 'O' index = 1 ebene = 0 caller = 0 event = '>' ) ( id = 'C' subid = 'R' index = 2 ebene = 1 caller = 1 event = '>' textindex = 6 ) "Submit ( id = 'V' subid = 'O' index = 3 ebene = 2 caller = 2 event = '>' ) "DB Op. ( id = 'P' index = 4 ebene = 2 caller = 2 event = '>' progindex = 1 textindex = 1 ) "Program ( id = 'm' index = 5 ebene = 3 caller = 4 event = '>' progindex = 2 textindex = 4 methindex = 1 ) ( id = 'm' subid = 'x' index = 6 ebene = 4 caller = 5 event = '>' progindex = 3 textindex = 5 methindex = 2 ) ( id = 'm' subid = 'x' index = 7 ebene = 4 caller = 6 event = '>' progindex = 1 textindex = 4 methindex = 3 ) ). fill_trace( ). ENDMETHOD. METHOD add_more. fixture_add_more( ). DATA(lt_exp) = VALUE tt_message( ( id = 'C' from_level = 1 caller = 1 called = 2 msg = ltc_actors=>c_prog ) ( id = 'm' from_level = 3 caller = 2 called = 3 msg = 'MAIN' ) ( id = c_id_skip from_level = 3 caller = 3 called = 4 msg = 'CONSTRUCTOR' ) ( id = 'm' from_level = 4 caller = 4 called = 3 msg = 'CHANGE' ) ). cl_abap_unit_assert=>assert_equals( act = mt_act_trace exp = lt_exp msg = 'LCL_TRACE ADD_MESSAGE( ) More - Error' ). ENDMETHOD. METHOD fixture_caller. fixture_prepare( ). mo_trace->mo_atra->it_austab_hier = VALUE #( ( id = 'O' index = 1 ebene = 0 caller = 0 event = '>' ) ( id = 'C' subid = 'R' index = 2 ebene = 1 caller = 1 event = '>' textindex = 6 ) "Submit ( id = 'C' subid = 'R' index = 2 ebene = 1 caller = 1 event = '<' textindex = 6 ) ). "Submit fill_trace( ). ENDMETHOD. METHOD skip_record. fixture_caller( ). * TO DO: check coverage/review fixture cl_abap_unit_assert=>assert_equals( act = mo_trace->mt_caller exp = VALUE lcl_trace=>tt_seq_k1( ( aus_tabix = 1 ) ( called = VALUE #( global = ltc_actors=>c_prog type = c_type_prog mod = ltc_actors=>c_prog ) aus_tabix = 2 ) ) msg = 'LCL_TRACE SKIP_RECORD( ) Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_filter_null DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mi_filter TYPE REF TO lif_trace_filter. METHODS setup. METHODS teardown. METHODS filter_empty FOR TESTING. METHODS filter_valid FOR TESTING. ENDCLASS. CLASS ltc_filter_null IMPLEMENTATION. METHOD setup. mi_filter = lcl_filter=>new( VALUE #( ) ). ENDMETHOD. METHOD teardown. FREE mi_filter. ENDMETHOD. METHOD filter_empty. cl_abap_unit_assert=>assert_true( act = mi_filter->accepts( VALUE #( ) ) msg = 'LCL_FILTER ACCEPTS( ) Empty - Null Filter Error' ). ENDMETHOD. METHOD filter_valid. cl_abap_unit_assert=>assert_true( act = mi_filter->accepts( ltc_actors=>sat_fixture_record( ) ) msg = 'LCL_FILTER ACCEPTS( ) Valid - Null Filter Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_filter_custom DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mi_filter TYPE REF TO lif_trace_filter. METHODS setup. METHODS teardown. METHODS sample_sat RETURNING VALUE(rs_sat) TYPE ts_sat. METHODS filter_empty FOR TESTING. METHODS filter_valid FOR TESTING. METHODS accepts FOR TESTING. METHODS system_events FOR TESTING. METHODS skip_constructor FOR TESTING. ENDCLASS. CLASS ltc_filter_custom IMPLEMENTATION. METHOD setup. mi_filter = lcl_filter=>new( VALUE #( pattern = VALUE #( ( 'Y*' ) ) ) ). ENDMETHOD. METHOD teardown. FREE mi_filter. ENDMETHOD. METHOD filter_empty. cl_abap_unit_assert=>assert_false( act = mi_filter->accepts( VALUE #( ) ) msg = 'LCL_FILTER_CUSTOM ACCEPTS( ) Empty - Error' ). ENDMETHOD. METHOD filter_valid. cl_abap_unit_assert=>assert_true( act = mi_filter->accepts( ltc_actors=>sat_fixture_record( ) ) msg = 'LCL_FILTER_CUSTOM ACCEPTS( ) Valid - Error' ). ENDMETHOD. METHOD sample_sat. rs_sat = VALUE ts_sat( id = 'L' from_level = 8 aus_tabix = 21 caller = VALUE #( global = 'SAPMSSYD' type = 'PROG' mod = '%_IMODE_INIT' ) called = VALUE #( ) ). ENDMETHOD. METHOD accepts. cl_abap_unit_assert=>assert_false( act = mi_filter->accepts( sample_sat( ) ) msg = 'LCL_FILTER_CUSTOM ACCEPTS( ) Custom - Filter Error' ). ENDMETHOD. METHOD system_events. mi_filter = lcl_filter=>new( VALUE #( system_events = abap_true pattern = VALUE #( ( 'SAPM*' ) ) ) ). cl_abap_unit_assert=>assert_true( act = mi_filter->accepts( sample_sat( ) ) msg = 'LCL_FILTER_CUSTOM ACCEPTS( ) Custom - <system Event' ). ENDMETHOD. METHOD skip_constructor. mi_filter = lcl_filter=>new( VALUE #( system_events = abap_true pattern = VALUE #( ( 'SAPM*' ) ) ) ). DATA(ls_sat) = sample_sat( ). mi_filter->accepts( VALUE ts_sat( BASE ls_sat caller-mod = 'CONSTRUCTOR' ) ). cl_abap_unit_assert=>assert_true( act = mi_filter->accepts( ls_sat ) msg = 'LCL_FILTER_CUSTOM ACCEPTS( ) Custom - Filter Error' ). ENDMETHOD. ENDCLASS. CLASS ltc_configuration DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mo_config TYPE REF TO lcl_configuration. METHODS setup. METHODS teardown. METHODS get_result RETURNING VALUE(rv_result) TYPE char3. METHODS radiobutton_url FOR TESTING. METHODS radiobutton_txt FOR TESTING. METHODS radiobutton_exe FOR TESTING. METHODS from_radiobutton_txt FOR TESTING. ENDCLASS. CLASS ltc_configuration IMPLEMENTATION. METHOD setup. mo_config = NEW #( ). ENDMETHOD. METHOD teardown. FREE mo_config. ENDMETHOD. METHOD get_result. rv_result+0(1) = mo_config->mv_mode_url. rv_result+1(1) = mo_config->mv_mode_txt. rv_result+2(1) = mo_config->mv_mode_exe. ENDMETHOD. METHOD radiobutton_url. mo_config->gs_cfg-output_mode = lcl_diagram_text=>c_mode_url. mo_config->to_radiobutton( ). cl_abap_unit_assert=>assert_equals( act = get_result( ) exp = 'X ' msg = 'LCL_CONFIGURATION->TO_RADIOBUTTON( ) - URL Error' ). ENDMETHOD. METHOD radiobutton_exe. mo_config->gs_cfg-output_mode = lcl_diagram_text=>c_mode_exe. mo_config->to_radiobutton( ). cl_abap_unit_assert=>assert_equals( act = get_result( ) exp = ' X' msg = 'LCL_CONFIGURATION->TO_RADIOBUTTON( ) - EXE Error' ). ENDMETHOD. METHOD radiobutton_txt. mo_config->gs_cfg-output_mode = lcl_diagram_text=>c_mode_txt. mo_config->to_radiobutton( ). cl_abap_unit_assert=>assert_equals( act = get_result( ) exp = ' X ' msg = 'LCL_CONFIGURATION->TO_RADIOBUTTON( ) - TXT Error' ). ENDMETHOD. METHOD from_radiobutton_txt. mo_config->gs_cfg-output_mode = lcl_diagram_text=>c_mode_txt. mo_config->to_radiobutton( ). mo_config->from_radiobutton( ). cl_abap_unit_assert=>assert_equals( act = mo_config->gs_cfg-output_mode exp = lcl_diagram_text=>c_mode_txt msg = 'LCL_CONFIGURATION->FROM_RADIOBUTTON( ) - TXT Error' ). ENDMETHOD. ENDCLASS. *-------------------------------------------------------------------------------------------------*
- In class builder for class CL_ATRA_UML_HANDLING Goto -> Local Definitions/Implementations -> Local Definitions/Implementations Ctrl+Shift+F6. The definition/implementation of local class LCL_UML_HANDLING is displayed. At the bottom of the source code, implement an implicit enhancement and insert the logic from the include:
ENHANCEMENT 2 YY_SATRA_SEQUENCE. "active version * INCLUDE YY_SATRA_INCLUDE. ENDENHANCEMENT.
- The last step is to override the call in method SHOW_SEQ_DIAGRAM( ) using an enhancement:
ENHANCEMENT 1 YY_SATRA_SEQUENCE. "active version DATA lx_error TYPE REF TO cx_dynamic_check. TRY. lcl_sequence=>to_diagram( lcl_configuration=>query( ) )->output( ). CATCH cx_dynamic_check INTO lx_error. MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. "#EC CI_USE_WANTED ENDTRY. RETURN. ENDENHANCEMENT.
After activation, your system behavior will have changed. A new customizing popup appears while displaying a sequence diagram.
Domain Model
Trace Analysis Steps
- Query ABAP execution trace created by transaction SAT without aggregation;
- Parse call hierarchy in trace, apply custom filters;
- Convert filtered trace to sequence diagram in text form;
- Save UML as text and generate and/or display image.
Local class LCL_ABAP_TRACE, Local class LCL_TRACE, LIF_COLLECTOR Interface
Class LCL_ABAP_TRACE parses the call hierarchy (form routines, methods, function modules, screen flow...) and actors (programs, function groups, classes, logical databases..) of an ABAP trace stored in static attributes of standard class CL_ATRA_TOOL_SE30_MAIN, i.e. the internal tables
- IT_TRACEPROG - Table of Recorded Program Names
- IT_TRACETEXT - Table of TRACE Text Elements
- IT_TRACEMETH - Table of Recorded Method Names
- IT_AUSTAB_HIER – Call Hierarchy table with All TRACE Information
Class LCL_TRACE uses the logic inherited from LCL_ABAP_TRACE to transform trace entries into the target structure TS_SAT used for sequence diagram creation.
Structure Field | Description |
---|---|
ID | Indicator describes the type of the recorded event (call method, perform, call function, call screen, database operation, message...) |
FROM_LEVEL | Call level in the call hierarchy (an integer) |
CALLER | caller object - Source of the message (program, function group, class, etc.) |
CALLED | called object - Receiver (target) of the message |
AUS_TABIX | Index for table access |
SYSTEM | System flag " only used in custom filter |
This structure is passed to any collector class like LCL_SEQUENCE implementing the LIF_COLLECTOR interface. The collector is responsible for applying custom filter, the COLLECT( ) method must returns ABAP_FALSE if the entry was rejected (filtered out) so the LCL_TRACE method can adjust the whole call hierarchy. Some helper classes were introduced:
- LCL_BAG - Abstract data type with the interface ADD/REMOVE/CONTAINS simplifies the filter logic
- LCL_CLASS_NAME to transform between tehnical and external ABAP class name
Local class LCL_ACTORS, Interface LIF_ACTORS
The rest of the code uses the term Actor according to the sequence diagram definition to denote any object that sends or receives messages. The collection of actors (or actor dictionary) is maintained in class LCL_ACTORS and accessed via the LIF_ACTORS interface defines:
- method NEW_PATH( ) saves the caller and called object from structure TS_SAT in the actor collection.New entries are created and existing entries are retrieved from the collection of actors. A unique key is returned for each actor, so a pair of actor indices (a PATH) is returned for by the NEW_PATH( ) method for the caller and called objects.
- the short text of a message (mostly determined by the called object/actor) is formatted using the SHORT_TEXT( ) method
- method LIFELINES( ) returns the complete list (internal table) of sequence diagram lifelines. It needed at initialization and completion of the sequence diagram.
Class LCL_ACTORS implements this interface as a SORTED internal table MT_ACTOR with a unique secondary key OBJ_NR for performance. While comparing actors, a special treatment of objects (the object oriented instance of a class) is needed, since each object instance has its separate identity - cf method INDEX_OF( ).
Local interface LIF_TRACE_FILTER, Local classes LCL_FILTER, LCL_FILTER_NULL, LCL_FILTER_CUSTOM
The filter logic is abstracted to interface LIF_TRACE_FILTER to make it easy to try new ideas. To the easiest way to create a new custom filter is to subclass class LCL_FILTER and implement the custom logic in method ACCEPTS( ) . Adjust the factory method NEW( ) of class LCL_FILTER to inject the new logic and not additional changes to the collector in LCL_SEQUENCE would be needed. We provide three filter implementations:
- Class LCL_FILTER_NULL is a dummy filter without impact. It is only used if the Loops compacted checkbox is disabled and no entries exist in the Customer namespace field
- Class LCL_FILTER rejects some low-level object identifiers. It is used if the Loops compacted checkbox is disabled and the Customer namespace field is maintained.
- The default implementation in class LCL_FILTER_CUSTOM applies a Customer namespace filter but also keeps actors from other name spaces calling that trigger messages to custom code (Chris Paine's logic).
Note: A lifeline without short text will be displayed if the actor is filtered out but the message is not.
Local class LCL_MESSAGES
is an implementation of the iterator pattern that traverses the collected messages for UML sequence diagram generation. The canonical methods are HAS_NEXT( ) - check for availability of an entry - and NEXT( ) - get the next entry - for an internal table. We further expose methods SKIP( ) - skip a number of entries (default 1) - and IS_FIRST( ), FIRST_LEVEL( ) and NEXT_LEVEL( ) to return the current call depth.
Local class LCL_STACK, Local class LCL_UML_STACK
implements the stack (last in-first out queue) methods PUSH( ) and POP( ) to store call levels (structure TS_CALL_LEVEL with fields ACTOR_KEY and FROM_LEVEL). This implementation always updates flag MV_EMPTY but it does never throw any exception; instead, it returns an initial value when the stack is empty.The subclass LCL_UML_STACK uses the stack to implement a call stack with methods CALL( ) and the RETURN( ) behavior depending on the call level. Note the LCL_STACK was only extracted to try a different implementation of the abstract data type stack. Currently we are using a linked list instead of the ubiquitous internal table.
Local class LCL_CALL_STACK, Local class LCL_CALL_STACK_COMPACT
inherits from LCL_UML_STACK and implements the generation of messages, The very first call and the very last return are implemented as special cases.
Local class LCL_SEQUENCE
method LIF_COLLECTOR~COLLECT( ) of class LCL_SEQUENCE converts the trace entries of type TS_SAT into message entries of type TS_MESSAGE referring to the actors dictionary MI_ACTORS. After the MT_TRACE list of message is filled, the message iterator is created and linked to an UML translator, we call the resulting object composition a Call Stack (class LCL_CALL_STACK or its loops aware subclass LCL_CALL_STACK_COMPACT) that can use the TO_UML( ) method to traverse the call hierarchy and generate an LCL_DIAGRAM_TEXT class (UML sequence diagram in text mode).
The abstract LCL_UML class is used, concrete subclasses define the output format (default: subclass LCL_UML_PLANT for PlantUML output). Subclass LCL_MESSAGES_COMPACT creates an LCL_LOOP_COMPACTOR object in its constructor and uses it for loop compaction by calling the FOLD( ) method described below and by creating an object of class LCL_UML_CYCLES for the correct generation of UML combined fragments for loops.
Local Class LCL_UML, Local class LCL_UML_PLANT
Local class LCL_DIAGRAM_TEXT, Local class LCL_PLANT_UML_DIAGRAM
After generation, the abstract class LCL_DIAGRAM_TEXT orchestrates the output. Its subclass LCL_PLANT_UML_DIAGRAM either
- saves the plain text source of the diagram using class LCL_FILE or
- executes a local PlantUML installation to generate an image file or
- encodes the diagram into an URL for the PlantUML server that will create an image.
The result is displayed in an ABAP browser window (standard class CL_ABAP_BROWSER).
Local class LCL_PATTERN, Local Interface LIF_CYCLES, Local class LCL_LOOP_COMPACTOR, Local class LCL_TRACE_INDEX
Class LCL_PATTERN method DETECT_CYCLES( ) searches for verbatim repetition sequences in a trace. The cycle detector traverses the trace table and tries to locate the next occurrence of current entry in the rest of the trace. If a match is found, a second iterator starting at the position of this next occurrence is created for the same trace table. By comparing both iterators output we can find the longest matching sequence. This logic is used recursively to find loops inside loops. This simple matching approach cannot recognize cases with conditional flow in the loop.
The ABAP statement FIND FIRST OCCURENCE OF.. IN TABLE outperforms a combination of LOOP / READ TABLE statements. To benefit from this, the loop detection and compaction are performed on an index/dictionary table rather than on the trace data structure MT_TRACE. Class LCL_TRACE_INDEX implements the dictionary creation, the reverse conversion and the single loop detection step by delegating to the LCL_PATTERN class.
For each loop found, a loop entry of structure TS_CYCLE with
Field | Description |
---|---|
START | index of the starting entry of the repeated pattern (e.g. 1) |
END | index of the end entry of the repeated pattern (e.g. 4) |
LAST | index of the last entry of the repetition, when the loop is exited (e.g. 12) |
TIMES | number of loop counts (e.g. 3) |
is passed to the COLLECT( ) method of interface LIF_CYCLES, allowing to limit the dependency between classes. Our implementation uses SKIP( ) instead of NEXT( ) to traverse entries and a CLONE( ) method to generate new iterators on the same table. Class LCL_LOOP_COMPACTOR implements the LIF_CYCLES interface and uses the loop entries to compact the trace table.
The compaction step in done both for in the dictionary table and in the loop entry table of class LCL_LOOP_COMPACTOR. Call LCL_MESSAGES_COMPACT initiates loop compaction by calling method LCL_LOOP_COMPACTOR->FOLD( ) that repeat loop detection and elimination until no further trace table size reduction is achieved.
Local class LCL_PROGRESS_INDICATOR
calls to the ECHO( ) method are used for update information in the status line in processing intensive code.
Local class LCL_CONFIGURATION
sets and retrieves user settings. Method QUERY( ) generates a popup customizing dialog and method GET( ) reads the current settings.
Automated Test Suite
The ABAP unit tests characterize the current system behavior and so provide a regression test suite to help maintain the code. About 20 ABAP unit test classes and 100 test methods currently achieve branch coverage of ca. 80%, so we are confident to have a high sensibility to change. As all classes are local, all classes to be tested grant friendship to local interface LIF_UNIT_TEST. All ABAP unit test classes implement this interface to enable access to private sections of the code. Since no test double framework is available for local classes, some helper classes were created to enable behavior verification.
- LCL_LOGGER
- LCL_ABAP_TRACE_LOGGER
- LCL_UML_LOGGER
- LCL_FILE_NULL
- LCL_SATRA_STATE
Test Case
The following report is from a SCN thread. Check the generated sequence diagram below
Error rendering macro 'code': Invalid value specified for parameter 'com.atlassian.confluence.ext.code.render.InvalidValueException'REPORT ZZZ_CLONE. CLASS lcl_range DEFINITION. PUBLIC SECTION. TYPES tv_date TYPE d. DATA: mv_start TYPE tv_date, mv_stop TYPE tv_date. METHODS constructor IMPORTING iv_start TYPE tv_date iv_stop TYPE tv_date. METHODS create_copy IMPORTING iv_start TYPE tv_date OPTIONAL iv_stop TYPE tv_date OPTIONAL RETURNING VALUE(ro_range) TYPE REF TO lcl_range. ENDCLASS. CLASS lcl_range IMPLEMENTATION. METHOD constructor. super->constructor( ). mv_start = iv_start. mv_stop = iv_stop. ENDMETHOD. METHOD create_copy. DATA lv_start TYPE tv_date. DATA lv_stop TYPE tv_date. WRITE:/ 'Called with START = ', iv_start, ' STOP=', iv_stop. IF iv_start IS NOT SUPPLIED. lv_start = mv_start. ELSE. lv_start = iv_start. ENDIF. IF iv_stop IS NOT SUPPLIED. lv_stop = mv_stop. ELSE. lv_stop = iv_stop. ENDIF. WRITE:/ 'Updated to START = ', lv_start, ' STOP=', lv_stop. CREATE OBJECT ro_range EXPORTING iv_start = lv_start iv_stop = lv_stop. ENDMETHOD. ENDCLASS. DATA go_2009_range TYPE REF TO lcl_range. DATA go_copy TYPE REF TO lcl_range. START-OF-SELECTION. CREATE OBJECT go_2009_range EXPORTING iv_start = '20090101' iv_stop = '20091221'. * Clone go_copy = go_2009_range->create_copy( ). * Extend range to end of 2010 go_copy = go_2009_range->create_copy( iv_stop = '20101231' ). * New start date: May 1st 2009 go_copy = go_2009_range->create_copy( iv_start = '20090501' ).
The sequence diagram for a one line WRITE 'Hello!' program shows that much is going on under the hood:
This report is generated from report to display Short Dump Texts
Part of the UML diagram generation code as it is executed (Parse Trace Data)
Error rendering macro 'code': Invalid value specified for parameter 'com.atlassian.confluence.ext.code.render.InvalidValueException'
*&---------------------------------------------------------------------* *& Include YY_SATRA_INCLUDE *&---------------------------------------------------------------------* * Custom logic to generate a PlantUML sequence diagram from an * ABAP trace recorded in transaction SAT without aggregation. * * Usage: * Start transaction SAT -> Evaluate -> Double Click on a measurement; * on the next screen press F8 / click icon to Display sequence diagram * * Setup: * 1) to add the following local classes to class CL_ATRA_UML_HANDLING * create a source code plugin in include LS_ABAP_TRACE_DATAD07 * e.g. as implicit enhancement at the end: * *ENHANCEMENT 1 YY_SATR_SEQUENCE. "active version * INCLUDE YY_SATRA_INCLUDE. *ENDENHANCEMENT. * * 2) add an implicit enhancement at the beginning of Method * SHOW_SEQ_DIAGRAM( ) of CL_ATRA_UML_HANDLING to replace the logic: * *ENHANCEMENT 2 YY_SATR_SEQUENCE. "active version * TRY. * lcl_sequence=>to_diagram( lcl_configuration=>query( ) )->output( ). * CATCH cx_dynamic_check INTO DATA(lx_error). * MESSAGE lx_error TYPE 'I' DISPLAY LIKE 'E'. "#EC CI_USE_WANTED * ENDTRY. * RETURN. *ENDENHANCEMENT. *&---------------------------------------------------------------------* * Note: Unit test execution is disabled for standard classes, but you * can add this include to a custom program and run the tests there. * Glossary: module = sequence diagram actor * event = sequence diagram message * Convention: Most factory methods have NEW in their name * Valid actor type: program, function group, class, logical databases CONSTANTS: c_type_class TYPE trobjtype VALUE 'CLAS', c_type_prog TYPE trobjtype VALUE 'PROG', c_type_fugr TYPE trobjtype VALUE 'FUGR', c_type_ldba TYPE trobjtype VALUE 'LDBA', " Log. DB c_type_tran TYPE trobjtype VALUE 'TRAN'. * Caller / Called object TYPES: BEGIN OF ts_object, global TYPE program, local TYPE program, type TYPE trobjtype, instance TYPE satr_de_instance, END OF ts_object. TYPES tv_key TYPE i. " Object numbers in call TYPES: BEGIN OF ts_path, caller TYPE tv_key, called TYPE tv_key, END OF ts_path. * Trace step (Message): Caller -> Called object, message name (msg) TYPES: BEGIN OF ts_message, id TYPE satr_de_id, from_level TYPE satr_de_ebene. INCLUDE TYPE ts_path AS path. TYPES: msg TYPE seocpdname, END OF ts_message. TYPES tt_message TYPE STANDARD TABLE OF ts_message WITH KEY id WITH NON-UNIQUE SORTED KEY key_msg COMPONENTS table_line. *----------------------------------------------------------------------* * INTERFACE lif_unit_test *----------------------------------------------------------------------* * *----------------------------------------------------------------------* INTERFACE lif_unit_test. ENDINTERFACE. "lif_unit_test * Message - Called object information TYPES: BEGIN OF ts_call. INCLUDE TYPE ts_object AS object. TYPES: mod TYPE seocpdname, END OF ts_call. TYPES: BEGIN OF ts_seq, called TYPE ts_call, " Called fields aus_tabix TYPE i, " Index for Table Access END OF ts_seq. * Target structure for call hierarchy used in sequence diagrams, with the fields * ID - indicator describes the type of the recorded event * (call method, perform, call function, call screen, database operation,#) * from_level - The call level in the call hierarchy (an integer) * caller - source of the message (program, function group, class, etc.) * called - receiver of the message * system - system flag " only used in custom filter TYPES: BEGIN OF ts_sat, id TYPE satr_de_id, " General Information. from_level TYPE satr_de_ebene, caller TYPE ts_call. " Caller fields INCLUDE TYPE ts_seq AS seq. TYPES: system TYPE satr_de_sysflag, END OF ts_sat. TYPES tv_text TYPE char128. * Each actor (sender or receiver of a message) in the sequence diagram has a life line TYPES: BEGIN OF ts_lifeline, index TYPE tv_key, " secondary index in table of ts_actor label TYPE tv_text, END OF ts_lifeline. *----------------------------------------------------------------------* * INTERFACE lif_actors *----------------------------------------------------------------------* * *----------------------------------------------------------------------* INTERFACE lif_actors. TYPES tt_lifeline TYPE SORTED TABLE OF ts_lifeline WITH UNIQUE KEY index. METHODS lifelines RETURNING value(rt_lifeline) TYPE tt_lifeline. METHODS short_text IMPORTING is_message TYPE ts_message RETURNING value(rv_text) TYPE tv_text. METHODS new_path IMPORTING is_sat TYPE ts_sat RETURNING value(rs_path) TYPE ts_path. ENDINTERFACE. "lif_actors TYPES tv_element TYPE char30. * Abstract UML sequence diagram code generator - buffers the UML text * representation and pass the complete string back to the caller CLASS lcl_uml DEFINITION ABSTRACT FRIENDS lif_unit_test. PUBLIC SECTION. TYPES tv_uml TYPE i. CONSTANTS: c_uml_plant TYPE tv_uml VALUE 0, c_uml_graph TYPE tv_uml VALUE 2, c_uml_mscgen TYPE tv_uml VALUE 3, c_uml_mock TYPE tv_uml VALUE 9. CLASS-METHODS new IMPORTING iv_uml TYPE tv_uml ii_actors TYPE REF TO lif_actors RETURNING value(ro_uml) TYPE REF TO lcl_uml. METHODS top. METHODS bottom RETURNING value(rv_diagram) TYPE string. "! Draws a message between two objects, with the given label. "! Self messages (where an object sends a message to itself) are supported. METHODS message IMPORTING is_message TYPE ts_message. METHODS return ABSTRACT IMPORTING iv_from TYPE i iv_to TYPE i. METHODS begin_loop ABSTRACT IMPORTING iv_from TYPE program OPTIONAL iv_times TYPE sytabix iv_name TYPE tv_element OPTIONAL. METHODS end_loop ABSTRACT IMPORTING iv_to TYPE program OPTIONAL iv_name TYPE tv_element OPTIONAL. PROTECTED SECTION. CONSTANTS c_txt_std TYPE string VALUE 'Standard SAP code has called some custom code ' ##no_text. DATA mv_diagram TYPE string. DATA mi_actors TYPE REF TO lif_actors. METHODS add IMPORTING iv_code TYPE string. METHODS call ABSTRACT IMPORTING is_message TYPE ts_message. METHODS skip_note ABSTRACT IMPORTING is_path TYPE ts_path. METHODS header ABSTRACT. METHODS footer ABSTRACT. "! Defines an object is_lifeline-index, labeled on the diagram as is_lifeline-text METHODS participant ABSTRACT IMPORTING is_lifeline TYPE ts_lifeline iv_create TYPE xsdboolean DEFAULT abap_false. "! Object lifeline completion METHODS complete IMPORTING is_lifeline TYPE ts_lifeline. METHODS separator. " between two participants METHODS delimiter. " end of statement ENDCLASS. "lcl_uml DEFINITION * iterator over an internal table of messages allowing to * - skip a given number of entries (default 1). * - look ahead to read the next value without updating the index * - is_first to known if the current values is the first CLASS lcl_messages DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. CONSTANTS c_default_level TYPE satr_de_ebene VALUE 1. METHODS constructor IMPORTING it_messages TYPE tt_message iv_start TYPE sytabix DEFAULT 1 iv_stop TYPE sytabix OPTIONAL. METHODS is_first RETURNING value(rv_flag) TYPE xsdboolean. METHODS has_next RETURNING value(rv_flag) TYPE xsdboolean. METHODS skip IMPORTING iv_count TYPE i DEFAULT 1. METHODS next RETURNING value(rs_data) TYPE ts_message RAISING cx_sy_itab_error. METHODS first_level RETURNING value(rv_level) TYPE satr_de_ebene. METHODS next_level RETURNING value(rv_level) TYPE satr_de_ebene. PROTECTED SECTION. DATA mv_idx TYPE sytabix. DATA mv_size TYPE sytabix. DATA mt_list TYPE tt_message. PRIVATE SECTION. DATA mv_first_level TYPE i. ENDCLASS. "lcl_messages DEFINITION *----------------------------------------------------------------------* * CLASS lcl_stack DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_stack DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. TYPES: BEGIN OF ts_call_level, actor_key TYPE tv_key, from_level TYPE satr_de_ebene, END OF ts_call_level. METHODS push IMPORTING is_ref TYPE ts_call_level. METHODS pop RETURNING value(rs_to) TYPE ts_call_level. PROTECTED SECTION. DATA mv_empty TYPE xsdboolean VALUE abap_true. PRIVATE SECTION. TYPES BEGIN OF ts_level. INCLUDE TYPE ts_call_level AS call_level. TYPES next TYPE REF TO data. TYPES END OF ts_level. DATA mr_level TYPE REF TO ts_level. ENDCLASS. "lcl_stack DEFINITION *----------------------------------------------------------------------* * CLASS lcl_uml_stack DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_stack DEFINITION INHERITING FROM lcl_stack FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING io_uml TYPE REF TO lcl_uml. METHODS call IMPORTING is_message TYPE ts_message. METHODS return IMPORTING iv_to_level TYPE satr_de_ebene. PROTECTED SECTION. DATA mv_previous_level TYPE satr_de_ebene. DATA mo_uml TYPE REF TO lcl_uml. METHODS return_to IMPORTING iv_level TYPE satr_de_ebene. ENDCLASS. "lcl_uml_stack DEFINITION *----------------------------------------------------------------------* * CLASS lcl_call_stack DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_call_stack DEFINITION INHERITING FROM lcl_uml_stack FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING io_uml TYPE REF TO lcl_uml io_messages TYPE REF TO lcl_messages. "! traverses the list of messages list and generates an "UML as text" diagram METHODS to_uml RETURNING value(rv_diagram) TYPE string. PROTECTED SECTION. CONSTANTS c_first_key TYPE tv_key VALUE 1. " Code of very first call DATA mo_messages TYPE REF TO lcl_messages. METHODS message IMPORTING is_message TYPE ts_message iv_idx TYPE sytabix. ENDCLASS. "lcl_call_stack DEFINITION * Trace cycle description TYPES: BEGIN OF ts_cycle, start TYPE sytabix, end TYPE sytabix, last TYPE sytabix, times TYPE i, END OF ts_cycle. *----------------------------------------------------------------------* * CLASS lcl_call_stack_compact DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_call_stack_compact DEFINITION INHERITING FROM lcl_call_stack FRIENDS lif_unit_test. PUBLIC SECTION. TYPES tt_cycle TYPE SORTED TABLE OF ts_cycle WITH NON-UNIQUE KEY start. METHODS constructor IMPORTING io_uml TYPE REF TO lcl_uml io_messages TYPE REF TO lcl_messages it_cycles TYPE tt_cycle. PROTECTED SECTION. DATA mt_cycle TYPE tt_cycle. METHODS message REDEFINITION. METHODS begin IMPORTING iv_idx TYPE sytabix iv_from TYPE tv_key. METHODS end IMPORTING iv_idx TYPE sytabix iv_to TYPE tv_key RETURNING value(rv_step) TYPE sytabix. PRIVATE SECTION. METHODS name IMPORTING iv_start TYPE sytabix iv_end TYPE sytabix RETURNING value(rv_name) TYPE tv_element. ENDCLASS. "lcl_call_stack_compact DEFINITION *----------------------------------------------------------------------* * CLASS lcl_actors DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_actors DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. INTERFACES lif_actors. PROTECTED SECTION. TYPES: BEGIN OF ts_actor. INCLUDE TYPE ts_object AS object. INCLUDE TYPE ts_lifeline AS lifeline. TYPES: END OF ts_actor. TYPES tt_actor TYPE SORTED TABLE OF ts_actor WITH UNIQUE KEY type instance global local WITH UNIQUE SORTED KEY obj_nr COMPONENTS index. DATA mt_actor TYPE tt_actor. DATA mv_last_number TYPE i. METHODS index_of IMPORTING is_object TYPE ts_object RETURNING value(rv_idx) TYPE sytabix. METHODS next_number RETURNING value(rv_key) TYPE tv_key. METHODS lifeline IMPORTING is_object TYPE ts_object RETURNING value(rv_text) TYPE tv_text. METHODS call_short_text IMPORTING is_message TYPE ts_message RETURNING value(rv_text) TYPE tv_text. METHODS class_name IMPORTING iv_index TYPE sytabix RETURNING value(rv_short_name) TYPE tv_text. METHODS put IMPORTING is_object TYPE ts_object RETURNING value(rv_key) TYPE tv_key. ENDCLASS. "lcl_actors DEFINITION TYPES tv_scale TYPE perct. CONSTANTS c_default_scale TYPE tv_scale VALUE '0.5'. TYPES: BEGIN OF ts_diagram_config, local_path TYPE string, java_jar TYPE string, java_appl TYPE string, server_url TYPE string, output_mode TYPE char01, skip_dialog TYPE flag, compact_trace TYPE flag, system_events TYPE flag, scale TYPE tv_scale, pattern TYPE sci_pattern, handwritten TYPE flag, shadowing TYPE flag, display_source TYPE flag, teoz_architecture TYPE flag, uml_format TYPE lcl_uml=>tv_uml, progress TYPE flag, END OF ts_diagram_config. *----------------------------------------------------------------------* * CLASS lcl_diagram_text DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_diagram_text DEFINITION ABSTRACT FRIENDS lif_unit_test. PUBLIC SECTION. CONSTANTS: c_mode_aut TYPE char01 VALUE 'T', " for ABAP Unit Test c_mode_url TYPE char01 VALUE 'U', c_mode_txt TYPE char01 VALUE space, c_mode_exe TYPE char01 VALUE 'E'. CLASS-METHODS new IMPORTING is_cfg TYPE ts_diagram_config iv_text TYPE string RETURNING value(ro_diagram) TYPE REF TO lcl_diagram_text. METHODS constructor IMPORTING iv_diagram TYPE string iv_mode TYPE char01. METHODS output RAISING cx_dynamic_check. PROTECTED SECTION. DATA mv_diagram TYPE string. DATA ms_cfg TYPE ts_diagram_config. METHODS save_file IMPORTING iv_mode TYPE char01 RETURNING value(rv_fname) TYPE string RAISING cx_dynamic_check. METHODS to_xstring IMPORTING iv_string TYPE string RETURNING value(rv_xstring) TYPE xstring RAISING cx_dynamic_check. ENDCLASS. "lcl_diagram_text DEFINITION *----------------------------------------------------------------------* * INTERFACE lif_trace_filter DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* INTERFACE lif_trace_filter. METHODS accepts IMPORTING is_sat TYPE ts_sat RETURNING value(rv_flag) TYPE xsdboolean. ENDINTERFACE. "lif_trace_filter DEFINITION *----------------------------------------------------------------------* * CLASS lcl_file_name DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_file_name DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. CLASS-METHODS new IMPORTING iv_mode TYPE char01 RETURNING value(ro_file) TYPE REF TO lcl_file_name. METHODS constructor IMPORTING iv_mode TYPE char01. METHODS dialog RETURNING value(rv_user_action) TYPE i. METHODS get_prefix RETURNING value(rv_name) TYPE string RAISING cx_dynamic_check. METHODS get_fullpath RETURNING value(rv_name) TYPE string. PROTECTED SECTION. TYPES: BEGIN OF ts_fullpath, title TYPE string, name TYPE string, ext TYPE string, path TYPE string, END OF ts_fullpath. DATA ms_file TYPE ts_fullpath. ENDCLASS. "lcl_file_name DEFINITION *----------------------------------------------------------------------* * CLASS lcl_file_test_name DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_file_test_name DEFINITION INHERITING FROM lcl_file_name FRIENDS lif_unit_test. PUBLIC SECTION. METHODS dialog REDEFINITION. ENDCLASS. "lcl_file_test_name DEFINITION *----------------------------------------------------------------------* * CLASS lcl_file DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_file DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. CLASS-METHODS download IMPORTING iv_data TYPE xstring io_name TYPE REF TO lcl_file_name RETURNING value(rv_subrc) TYPE sysubrc. ENDCLASS. "lcl_file DEFINITION *----------------------------------------------------------------------* * CLASS lcl_diagram_plant_uml DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_diagram_plant_uml DEFINITION INHERITING FROM lcl_diagram_text FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING is_cfg TYPE ts_diagram_config iv_diagram TYPE string. METHODS output REDEFINITION. CLASS-METHODS get_java_path RETURNING value(rv_fullpath) TYPE string. PROTECTED SECTION. TYPES tv_base64 TYPE c LENGTH 65. CONSTANTS: c_charset_standard TYPE tv_base64 VALUE 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=' ##no_text, c_charset_plantuml TYPE tv_base64 VALUE '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_0' ##no_text. METHODS to_url IMPORTING iv_base_url TYPE string RETURNING value(rv_url) TYPE string RAISING cx_dynamic_check. METHODS to_png IMPORTING io_name TYPE REF TO lcl_file_name RETURNING value(rv_name) TYPE string. METHODS display_html RAISING cx_dynamic_check. METHODS local_plant_uml RAISING cx_dynamic_check. METHODS parameter_string IMPORTING io_name TYPE REF TO lcl_file_name RETURNING value(rv_param) TYPE string. METHODS png_file_name IMPORTING io_name TYPE REF TO lcl_file_name RETURNING value(rv_name) TYPE string. METHODS encoded_url_suffix RETURNING value(rv_url) TYPE string RAISING cx_dynamic_check. METHODS source RETURNING value(rv_source) TYPE string. METHODS display_url IMPORTING iv_url TYPE string RAISING cx_dynamic_check. ENDCLASS. "lcl_diagram_plant_uml DEFINITION *----------------------------------------------------------------------* * CLASS lcl_configuration DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_configuration DEFINITION CREATE PRIVATE FRIENDS lif_unit_test. PUBLIC SECTION. CLASS-METHODS: get RETURNING value(rs_cfg) TYPE ts_diagram_config, query RETURNING value(rs_cfg) TYPE ts_diagram_config, class_constructor. PRIVATE SECTION. TYPES: BEGIN OF ts_param, local_path TYPE localfile, java_jar TYPE localfile, java_appl TYPE localfile, server_url TYPE localfile, output_mode TYPE char01, skip_dialog TYPE flag, compact_trace TYPE flag, system_events TYPE flag, scale TYPE perct, handwritten TYPE flag, shadowing TYPE flag, teoz_architecture TYPE flag, display_source TYPE flag, pattern TYPE sci_pattern, uml_format TYPE int4, progress TYPE flag, END OF ts_param. METHODS get_attributes RETURNING value(rt_attr) TYPE sci_atttab. METHODS to_radiobutton. METHODS from_radiobutton. CLASS-DATA gs_cfg TYPE ts_param. DATA: mv_mode_url TYPE flag VALUE 'X', mv_mode_exe TYPE flag, mv_mode_txt TYPE flag. METHODS dialog. ENDCLASS. "lcl_configuration DEFINITION *----------------------------------------------------------------------* * CLASS lcl_filter DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_filter DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. INTERFACES lif_trace_filter. ALIASES accepts FOR lif_trace_filter~accepts. CLASS-METHODS new IMPORTING is_cfg TYPE ts_diagram_config RETURNING value(ri_filter) TYPE REF TO lif_trace_filter. METHODS constructor IMPORTING is_cfg TYPE ts_diagram_config. PROTECTED SECTION. DATA ms_cfg TYPE ts_diagram_config. DATA mv_config_id_reject TYPE string. ENDCLASS. "lcl_filter DEFINITION *----------------------------------------------------------------------* * CLASS lcl_filter_null DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_filter_null DEFINITION INHERITING FROM lcl_filter FRIENDS lif_unit_test. PUBLIC SECTION. METHODS accepts REDEFINITION. ENDCLASS. "lcl_filter_null DEFINITION *----------------------------------------------------------------------* * CLASS lcl_filter_custom DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_filter_custom DEFINITION INHERITING FROM lcl_filter FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING is_cfg TYPE ts_diagram_config. METHODS accepts REDEFINITION. PRIVATE SECTION. DATA mv_regex TYPE string. METHODS customer_namespace IMPORTING iv_name TYPE program RETURNING value(rv_flag) TYPE xsdboolean. METHODS get_regex IMPORTING it_pattern TYPE sci_pattern RETURNING value(rv_regex) TYPE string. ENDCLASS. "lcl_filter_custom DEFINITION *----------------------------------------------------------------------* * CLASS lcl_bag DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_bag DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. METHODS add IMPORTING iv_entry TYPE sytabix. METHODS remove IMPORTING iv_entry TYPE sytabix. METHODS contains IMPORTING iv_entry TYPE sytabix RETURNING value(rv_flag) TYPE xsdboolean. PRIVATE SECTION. DATA mt_index TYPE SORTED TABLE OF sytabix WITH UNIQUE DEFAULT KEY. ENDCLASS. "lcl_bag DEFINITION "! Class information *----------------------------------------------------------------------* * CLASS lcl_class_name DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_class_name DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. METHODS: is_global IMPORTING iv_name TYPE sobj_name RETURNING value(rv_flag) TYPE xsdboolean, external CHANGING cv_clas TYPE program, technical IMPORTING iv_name TYPE program RETURNING value(rv_class) TYPE program, to_internal IMPORTING iv_name TYPE program RETURNING value(rv_class) TYPE program. PRIVATE SECTION. * Cache proxy for method IS_GLOBAL( ) TYPES: BEGIN OF ts_class, name TYPE sobj_name, global TYPE flag, END OF ts_class. TYPES tt_class TYPE HASHED TABLE OF ts_class WITH UNIQUE KEY name. DATA mt_class TYPE tt_class. METHODS is_global_class IMPORTING iv_name TYPE sobj_name RETURNING value(rv_flag) TYPE xsdboolean. ENDCLASS. "lcl_class_name DEFINITION *----------------------------------------------------------------------* "! Parse call hierarchy in ABAP trace stored as static attributes of "! standard class CL_ATRA_TOOL_SE30_MAIN, extract trace details needed "! for UML sequence diagram generation from internal tables "! IT_TRACEPROG - Table of Recorded Program Names "! IT_TRACETEXT - Table of TRACE Text Elements "! IT_TRACEMETH - Table of Recorded Method Names "! IT_AUSTAB_HIER - Call Hierarchy table with All TRACE Information *----------------------------------------------------------------------* * CLASS lcl_abap_trace DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_abap_trace DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. CONSTANTS c_create_method TYPE seocpdname VALUE 'CONSTRUCTOR'. METHODS constructor. METHODS parse RETURNING value(rv_flag) TYPE xsdboolean. PROTECTED SECTION. TYPES: BEGIN OF ts_event. INCLUDE TYPE ts_object AS object. TYPES: event TYPE seocpdname, " Called component of object system TYPE satr_de_sysflag, END OF ts_event. DATA mo_atra TYPE REF TO cl_atra_tool_se30_main. DATA mr_src TYPE REF TO satr_austab_gesamt. DATA ms_target TYPE ts_event. " Called actor DATA mo_class_name TYPE REF TO lcl_class_name. METHODS module IMPORTING iv_index TYPE satr_de_pgix RETURNING value(rv_subrc) TYPE sysubrc. METHODS event RETURNING value(rv_subrc) TYPE sysubrc. * the following methods are redefined in test double METHODS set_constructor. METHODS set_meth. METHODS set_form. METHODS set_function_module. METHODS set_submit. METHODS set_tran. PRIVATE SECTION. METHODS set_local_class IMPORTING iv_progname TYPE program. ENDCLASS. "lcl_abap_trace DEFINITION *----------------------------------------------------------------------* * INTERFACE lif_collector DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* INTERFACE lif_collector. "! Add trace entry to collection METHODS collect IMPORTING is_sat TYPE ts_sat RETURNING value(rv_flag) TYPE xsdboolean. ENDINTERFACE. "lif_collector DEFINITION *----------------------------------------------------------------------* * CLASS lcl_trace DEFINITION * *----------------------------------------------------------------------* CLASS lcl_trace DEFINITION INHERITING FROM lcl_abap_trace FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor. METHODS fill IMPORTING ii_collector TYPE REF TO lif_collector. PROTECTED SECTION. CONSTANTS c_event_entry TYPE satr_de_event VALUE '>'. TYPES tt_seq_k1 TYPE STANDARD TABLE OF ts_seq WITH KEY aus_tabix " Performance WITH NON-UNIQUE SORTED KEY k1 COMPONENTS aus_tabix. DATA mt_caller TYPE tt_seq_k1. PRIVATE SECTION. DATA mo_bag TYPE REF TO lcl_bag. METHODS skip_record. METHODS put_caller IMPORTING is_sat TYPE ts_sat. METHODS get_sat RETURNING value(rs_sat) TYPE ts_sat. METHODS new_record RETURNING value(rv_flag) TYPE xsdboolean. ENDCLASS. "lcl_trace DEFINITION *----------------------------------------------------------------------* * CLASS lcl_progress_indicator DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_progress_indicator DEFINITION CREATE PRIVATE FRIENDS lif_unit_test. PUBLIC SECTION. CONSTANTS: c_enable TYPE char01 VALUE 'X', c_disable TYPE char01 VALUE '0'. CLASS-METHODS new IMPORTING is_cfg TYPE ts_diagram_config OPTIONAL RETURNING value(ro_prog) TYPE REF TO lcl_progress_indicator. CLASS-METHODS echo IMPORTING iv_percentage TYPE numeric DEFAULT 0 iv_text TYPE clike. METHODS set_mode IMPORTING iv_mode TYPE char01. METHODS restore. PRIVATE SECTION. CONSTANTS c_param_id_sin TYPE char20 VALUE 'SIN'. DATA mv_mode TYPE char01. METHODS constructor IMPORTING iv_progress TYPE flag. METHODS get_mode RETURNING value(rv_mode) TYPE char01. ENDCLASS. "lcl_progress_indicator DEFINITION *-----------------------------------------------------------------------------------------* INTERFACE lif_cycles. METHODS collect IMPORTING is_cycle TYPE ts_cycle. METHODS accepts IMPORTING iv_index TYPE sytabix RETURNING value(rv_flag) TYPE xsdboolean. ENDINTERFACE. "lif_cycles DEFINITION *----------------------------------------------------------------------* * CLASS lcl_trace_index DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_trace_index DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. * to benefit from the performance of the FIND IN TABLE statement * we implement a poor man's table index management here TYPES tv_x_tabix TYPE x LENGTH 4. TYPES tt_x_index TYPE STANDARD TABLE OF tv_x_tabix. METHODS constructor IMPORTING it_trace TYPE tt_message ii_cycles TYPE REF TO lif_cycles. METHODS to_trace CHANGING ct_trace TYPE tt_message. METHODS shrink IMPORTING iv_pass TYPE syindex RETURNING value(rv_shrink) TYPE xsdboolean. PROTECTED SECTION. TYPES tt_components TYPE SORTED TABLE OF ts_message WITH UNIQUE KEY id from_level caller called msg. METHODS to_index. METHODS filter RETURNING value(rv_shrink) TYPE xsdboolean. METHODS compact IMPORTING it_xindex TYPE tt_x_index EXPORTING et_xindex TYPE tt_x_index. PRIVATE SECTION. DATA mt_trace TYPE tt_message. DATA mi_cycles TYPE REF TO lif_cycles. DATA mt_components TYPE tt_components. DATA mt_xindex TYPE tt_x_index. ENDCLASS. "lcl_trace_index DEFINITION *----------------------------------------------------------------------* * CLASS lcl_sequence DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_sequence DEFINITION CREATE PRIVATE FRIENDS lif_unit_test. PUBLIC SECTION. INTERFACES lif_collector. CLASS-METHODS to_diagram IMPORTING is_cfg TYPE ts_diagram_config RETURNING value(ro_diagram) TYPE REF TO lcl_diagram_text. METHODS get_calls RETURNING value(ro_calls) TYPE REF TO lcl_call_stack. PROTECTED SECTION. DATA mi_filter TYPE REF TO lif_trace_filter. DATA mi_actors TYPE REF TO lif_actors. DATA mt_trace TYPE tt_message. DATA ms_previous TYPE ts_sat. DATA mo_uml TYPE REF TO lcl_uml. DATA mv_compact_trace TYPE flag. METHODS constructor IMPORTING is_cfg TYPE ts_diagram_config. METHODS add IMPORTING is_sat TYPE ts_sat. METHODS to_call_stack RETURNING value(ro_calls) TYPE REF TO lcl_call_stack. METHODS link_to_previous IMPORTING is_sat TYPE ts_sat RETURNING value(rs_sat) TYPE ts_sat. ENDCLASS. "lcl_sequence DEFINITION *----------------------------------------------------------------------* * CLASS lcl_uml_factory DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_factory DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. CLASS-METHODS new IMPORTING iv_compact_trace TYPE xsdboolean RETURNING value(ro_factory) TYPE REF TO lcl_uml_factory. CLASS-METHODS new_calls IMPORTING iv_compact_trace TYPE xsdboolean it_trace TYPE tt_message io_uml TYPE REF TO lcl_uml RETURNING value(ro_calls) TYPE REF TO lcl_call_stack. PROTECTED SECTION. METHODS new_call_stack IMPORTING io_uml TYPE REF TO lcl_uml it_trace TYPE tt_message RETURNING value(ro_calls) TYPE REF TO lcl_call_stack. METHODS new_iterator IMPORTING it_trace TYPE tt_message RETURNING value(ro_iterator) TYPE REF TO lcl_messages. METHODS fold IMPORTING it_trace TYPE tt_message EXPORTING et_messages TYPE tt_message. ENDCLASS. "lcl_uml_factory DEFINITION *----------------------------------------------------------------------* * CLASS lcl_shrinkage DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_shrinkage DEFINITION CREATE PRIVATE FRIENDS lif_unit_test. PUBLIC SECTION. CLASS-METHODS shrink_references CHANGING ct_cycles TYPE lcl_call_stack_compact=>tt_cycle. PRIVATE SECTION. TYPES tt_sorted_loop TYPE SORTED TABLE OF ts_cycle WITH UNIQUE KEY start end last times. DATA mt_shrinked_loop TYPE tt_sorted_loop. DATA mv_tabix TYPE sytabix. DATA mt_cycles TYPE lcl_call_stack_compact=>tt_cycle. DATA ms_stop TYPE ts_cycle. METHODS constructor IMPORTING it_cycles TYPE lcl_call_stack_compact=>tt_cycle. METHODS insert IMPORTING ls_cycle TYPE ts_cycle. METHODS renew RETURNING value(ro_shrink) TYPE REF TO lcl_shrinkage. METHODS rebuild RETURNING value(ro_shrink) TYPE REF TO lcl_shrinkage. ENDCLASS. "lcl_shrinkage DEFINITION *----------------------------------------------------------------------* * CLASS lcl_uml_compactor DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_compactor DEFINITION INHERITING FROM lcl_uml_factory FRIENDS lif_unit_test. PUBLIC SECTION. INTERFACES lif_cycles. PROTECTED SECTION. METHODS new_call_stack REDEFINITION. METHODS fold REDEFINITION. PRIVATE SECTION. DATA mt_cycles TYPE lcl_call_stack_compact=>tt_cycle. ENDCLASS. "lcl_uml_compactor DEFINITION "! Class LCL_PATTERN finds repetitions (cycles/loops) in the internal table. "! Method CLONE( ) generates new iterators starting at the next repeated "! entry in the given trace table and recursively checks within the match. "! When a loop is found, an entry with "! the index of the starting entry of the repeated pattern (e.g. 1) "! the index of the end entry of the repetition (e.g. 4) "! the number of loop counts (e.g. 3) "! the index of the last entry when the loop is exited (e.g. 12) "! is stored using the COLLECT( ) method of interface LIF_CYCLES. *----------------------------------------------------------------------* * CLASS lcl_pattern DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_pattern DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING it_xindex TYPE lcl_trace_index=>tt_x_index iv_start TYPE sytabix DEFAULT 1 iv_stop TYPE sytabix OPTIONAL. METHODS has_next RETURNING value(rv_flag) TYPE xsdboolean. METHODS skip IMPORTING iv_count TYPE i DEFAULT 1. METHODS detect_cycles. CLASS-METHODS new IMPORTING it_xindex TYPE lcl_trace_index=>tt_x_index iv_start TYPE sytabix DEFAULT 1 iv_stop TYPE sytabix OPTIONAL ii_cycles TYPE REF TO lif_cycles RETURNING value(ro_pattern) TYPE REF TO lcl_pattern. PROTECTED SECTION. CONSTANTS c_max_step TYPE i VALUE 1024. DATA mi_cycles TYPE REF TO lif_cycles. METHODS clone IMPORTING iv_start TYPE sytabix DEFAULT 1 iv_stop TYPE sytabix OPTIONAL PREFERRED PARAMETER iv_start RETURNING value(ro_loop) TYPE REF TO lcl_pattern. METHODS next_occurrence_from IMPORTING iv_from TYPE sytabix RETURNING value(rv_row) TYPE sytabix. METHODS occurs_in IMPORTING iv_start TYPE sytabix iv_end TYPE sytabix RETURNING value(rv_same) TYPE xsdboolean. * Condense loop logic METHODS match_pattern IMPORTING iv_end TYPE sytabix RETURNING value(rv_match) TYPE xsdboolean. PRIVATE SECTION. DATA mv_idx TYPE sytabix. DATA mv_size TYPE sytabix. DATA mt_xindex TYPE lcl_trace_index=>tt_x_index. ENDCLASS. "lcl_pattern DEFINITION * PlantUML code generator - CLASS lcl_uml_plant DEFINITION INHERITING FROM lcl_uml FRIENDS lif_unit_test. PUBLIC SECTION. METHODS return REDEFINITION. METHODS begin_loop REDEFINITION. METHODS end_loop REDEFINITION. PROTECTED SECTION. CONSTANTS c_min_scale TYPE tv_scale VALUE '0.1'. METHODS call REDEFINITION. METHODS parameters IMPORTING is_cfg TYPE ts_diagram_config RETURNING value(rv_param) TYPE string. METHODS participant REDEFINITION. METHODS header REDEFINITION. METHODS footer REDEFINITION. METHODS skip_note REDEFINITION. PRIVATE SECTION. DATA mv_param TYPE string. METHODS add_param IMPORTING iv_cond TYPE xsdboolean DEFAULT abap_true iv_command TYPE string. METHODS scale IMPORTING is_cfg TYPE ts_diagram_config RETURNING value(rv_scale) TYPE string. ENDCLASS. "lcl_uml_plant DEFINITION * UMLGraph code generator - CLASS lcl_uml_graph DEFINITION INHERITING FROM lcl_uml FRIENDS lif_unit_test. PUBLIC SECTION. METHODS return REDEFINITION. METHODS begin_loop REDEFINITION. METHODS end_loop REDEFINITION. METHODS top REDEFINITION. PROTECTED SECTION. METHODS call REDEFINITION. METHODS participant REDEFINITION. METHODS header REDEFINITION. METHODS complete REDEFINITION. METHODS footer REDEFINITION. METHODS skip_note REDEFINITION. ENDCLASS. "lcl_uml_graph DEFINITION *----------------------------------------------------------------------* * CLASS lcl_uml_mscgen DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_mscgen DEFINITION INHERITING FROM lcl_uml FRIENDS lif_unit_test. PUBLIC SECTION. METHODS return REDEFINITION. METHODS begin_loop REDEFINITION. METHODS end_loop REDEFINITION. PROTECTED SECTION. METHODS call REDEFINITION. METHODS header REDEFINITION. METHODS footer REDEFINITION. METHODS participant REDEFINITION. METHODS skip_note REDEFINITION. METHODS separator REDEFINITION. METHODS delimiter REDEFINITION. ENDCLASS. "lcl_uml_mscgen DEFINITION *----------------------------------------------------------------------* * CLASS lcl_logger DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_logger DEFINITION CREATE PRIVATE FRIENDS lif_unit_test. PUBLIC SECTION. TYPES: BEGIN OF ts_log, method TYPE char30, params TYPE string, END OF ts_log. TYPES tt_log TYPE STANDARD TABLE OF ts_log WITH DEFAULT KEY. CLASS-METHODS new RETURNING value(ro_log) TYPE REF TO lcl_logger. METHODS info IMPORTING is_log TYPE ts_log. METHODS verify IMPORTING it_exp TYPE tt_log iv_msg TYPE string. PROTECTED SECTION. DATA mt_log TYPE tt_log. ENDCLASS. "lcl_logger DEFINITION * Mock UML object CLASS lcl_uml_logger DEFINITION INHERITING FROM lcl_uml FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor. METHODS return REDEFINITION. METHODS begin_loop REDEFINITION. METHODS end_loop REDEFINITION. PROTECTED SECTION. METHODS call REDEFINITION. METHODS participant REDEFINITION. METHODS header REDEFINITION. METHODS footer REDEFINITION. METHODS skip_note REDEFINITION. PRIVATE SECTION. DATA mo_log TYPE REF TO lcl_logger. METHODS log IMPORTING iv_method TYPE char30 iv_params TYPE string OPTIONAL. ENDCLASS. "lcl_uml_logger DEFINITION *--------------------------------------------------------------------------------* * Object Identifier in a call CONSTANTS: c_id_func TYPE satr_de_id VALUE 'U', " Function module c_id_form TYPE satr_de_id VALUE 'F', c_id_class TYPE satr_de_id VALUE 'R', " Create object´/ Run time management c_id_method TYPE satr_de_id VALUE 'm', c_id_record TYPE satr_de_id VALUE 'O', " Record of measurement c_id_program TYPE satr_de_id VALUE 'P', " Program c_id_skip TYPE satr_de_id VALUE 'K'. " skip over SAP code CONSTANTS: c_id_load TYPE satr_de_id VALUE 'L', " Load c_id_module TYPE satr_de_id VALUE 'M', " Module call c_id_report TYPE satr_de_id VALUE 'C', " CALL screen/report c_id_dynpro TYPE satr_de_id VALUE 'A', " PBO / PAI c_id_flow TYPE satr_de_id VALUE 'E'. " Flow Logic CONSTANTS: " to be skipped c_id_title TYPE satr_de_id VALUE 'Z', " Dyn. Assign, Status/Title, SCAN, SET Local c_id_message TYPE satr_de_id VALUE 'W', " Message/Continue/Wait c_id_db_oper TYPE satr_de_id VALUE 'V', " Database Operation c_id_sap_sql TYPE satr_de_id VALUE 'S', " Open SQL c_id_db_sql TYPE satr_de_id VALUE 'Q', " Native SQL c_id_import TYPE satr_de_id VALUE 'X', " Export/Import c_id_db_tech TYPE satr_de_id VALUE 'N'. " Buffer/Technical DB Operation *----------------------------------------------------------------------* * CLASS lcl_bag IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_bag IMPLEMENTATION. METHOD add. INSERT iv_entry INTO TABLE mt_index. ENDMETHOD. "add METHOD remove. DELETE TABLE mt_index FROM iv_entry. ENDMETHOD. "remove METHOD contains. READ TABLE mt_index TRANSPORTING NO FIELDS WITH KEY table_line = iv_entry. rv_flag = boolc( sy-subrc EQ 0 ). ENDMETHOD. "contains ENDCLASS. "lcl_bag IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_filter_null IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_filter_null IMPLEMENTATION. METHOD accepts. rv_flag = abap_true. ENDMETHOD. "accepts ENDCLASS. "lcl_filter_null IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_filter IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_filter IMPLEMENTATION. METHOD constructor. ms_cfg = is_cfg. mv_config_id_reject = c_id_title " Z - Dyn. Assign, Status/Title, SCAN, SET Local && c_id_message " W - Message/Continue/Wait && c_id_db_oper " V - Database Operation && c_id_sap_sql " S - Open SQL && c_id_db_sql " Q - Native SQL && c_id_import " X - Export/Import && c_id_db_tech " N - Buffer/Technical DB Operation && c_id_program " P - Program start point && c_id_record " O - record of measurement && ` `. " space ENDMETHOD. "constructor METHOD new. IF is_cfg IS INITIAL. CREATE OBJECT ri_filter TYPE lcl_filter_null EXPORTING is_cfg = is_cfg. ELSEIF is_cfg-pattern IS INITIAL. CREATE OBJECT ri_filter TYPE lcl_filter EXPORTING is_cfg = is_cfg. ELSE. CREATE OBJECT ri_filter TYPE lcl_filter_custom EXPORTING is_cfg = is_cfg. ENDIF. ENDMETHOD. "new METHOD accepts. CONSTANTS c_system_flag TYPE satr_de_sysflag VALUE 'S'. IF is_sat-id CA mv_config_id_reject. rv_flag = abap_false. ELSEIF ms_cfg-system_events EQ abap_false. rv_flag = boolc( is_sat-system NE c_system_flag ). ELSE. rv_flag = abap_true. ENDIF. ENDMETHOD. "accepts ENDCLASS. "lcl_filter IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_filter_custom IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_filter_custom IMPLEMENTATION. METHOD constructor. super->constructor( is_cfg ). mv_regex = get_regex( ms_cfg-pattern ). ENDMETHOD. "constructor METHOD get_regex. FIELD-SYMBOLS <lv_pattern> LIKE LINE OF it_pattern. CLEAR rv_regex. LOOP AT it_pattern ASSIGNING <lv_pattern> WHERE table_line IS NOT INITIAL. IF rv_regex IS INITIAL. rv_regex = <lv_pattern>. ELSE. rv_regex = |{ rv_regex }\|{ <lv_pattern> }|. ENDIF. ENDLOOP. ENDMETHOD. "get_regex METHOD customer_namespace. * Check if IV_NAME satisfies MV_REGEX pattern DATA lv_offset TYPE i. DATA lv_mlen TYPE i. rv_flag = boolc( mv_regex IS INITIAL ). CHECK rv_flag EQ abap_false. FIND REGEX mv_regex IN iv_name MATCH OFFSET lv_offset MATCH LENGTH lv_mlen. rv_flag = boolc( 0 EQ sy-subrc AND 0 EQ lv_offset AND lv_mlen GT 0 ). ENDMETHOD. "customer_namespace METHOD accepts. rv_flag = super->accepts( is_sat ). CHECK rv_flag EQ abap_true. * Filter logic rv_flag = boolc( * Called object is custom code ( is_sat-called-global IS NOT INITIAL AND customer_namespace( is_sat-called-global ) EQ abap_true ) OR * If initial, caller object must be custom code ( is_sat-called-global IS INITIAL AND customer_namespace( is_sat-caller-global ) EQ abap_true ) ). ENDMETHOD. "accepts ENDCLASS. "lcl_filter_custom IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_class_name IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_class_name IMPLEMENTATION. METHOD external. CHECK cv_clas+30(2) = 'CP'. cv_clas = translate( val = cv_clas(30) from = '=' to = ' ' ). cv_clas+30(2) = space. ENDMETHOD. "external METHOD technical. rv_class = to_internal( iv_name ). rv_class+30(2) = 'CP'. ENDMETHOD. "technical METHOD to_internal. rv_class = iv_name. * rv_class = translate( val = rv_class(30) from = ' ' to = '=' ). "DOES not work!? TRANSLATE rv_class(30) USING ' ='. ENDMETHOD. "to_internal METHOD is_global. DATA ls_class LIKE LINE OF mt_class. * Cache Proxy for table TADIR READ TABLE mt_class INTO ls_class WITH KEY name = iv_name. IF sy-subrc EQ 0. rv_flag = ls_class-global. ELSE. rv_flag = is_global_class( iv_name ). ENDIF. ENDMETHOD. "is_global METHOD is_global_class. DATA lv_pgmid TYPE tadir-pgmid. DATA ls_class LIKE LINE OF mt_class. CLEAR rv_flag. SELECT SINGLE pgmid FROM tadir INTO lv_pgmid " Single Record buffered WHERE pgmid = 'R3TR' AND object = 'CLAS' AND obj_name = iv_name. rv_flag = boolc( sy-subrc EQ 0 ). * add to cache proxy ls_class-name = iv_name. ls_class-global = rv_flag. INSERT ls_class INTO TABLE mt_class. ENDMETHOD. "is_global_class ENDCLASS. "lcl_class_name IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_abap_trace IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_abap_trace IMPLEMENTATION. METHOD constructor. * Query ABAP execution trace created by transaction SAT without aggregation CREATE OBJECT: mo_atra, mo_class_name. ENDMETHOD. "constructor METHOD module. FIELD-SYMBOLS <ls_prog> TYPE satr_prog. * PROGINDEX : Index for Table TRACEPROG READ TABLE mo_atra->it_traceprog INDEX iv_index ASSIGNING <ls_prog>. rv_subrc = sy-subrc. CHECK rv_subrc = 0. ms_target-global = <ls_prog>-cprog. ms_target-type = <ls_prog>-object. ms_target-system = <ls_prog>-sysflag. ENDMETHOD. "module METHOD event. FIELD-SYMBOLS <ls_text> TYPE satr_tracetext. * TEXTINDEX : Index for Internal Table TRACETEXT READ TABLE mo_atra->it_tracetext INDEX mr_src->textindex ASSIGNING <ls_text>. rv_subrc = sy-subrc. CHECK rv_subrc = 0. ms_target-event = <ls_text>-tracetext. ms_target-system = <ls_text>-sysflag. ENDMETHOD. "event METHOD set_meth. FIELD-SYMBOLS <ls_meth> TYPE satr_tracmeth. FIELD-SYMBOLS <ls_text> TYPE satr_tracetext. DATA lv_tracetext TYPE program. * METHINDEX : Index for Method Name Table SATR_T_TRACEMETH module( mr_src->progindex ). * CALLED-Data READ TABLE mo_atra->it_tracemeth INDEX mr_src->methindex ASSIGNING <ls_meth>. IF sy-subrc = 0. ms_target-event = <ls_meth>-methode. ENDIF. READ TABLE mo_atra->it_tracetext INDEX mr_src->textindex ASSIGNING <ls_text>. CHECK sy-subrc = 0. lv_tracetext = <ls_text>-tracetext. " type conversion READ TABLE mo_atra->it_traceprog TRANSPORTING NO FIELDS WITH KEY cprog = mo_class_name->to_internal( lv_tracetext ). CHECK module( sy-tabix ) NE 0. set_local_class( lv_tracetext ). ENDMETHOD. "set_meth METHOD set_constructor. FIELD-SYMBOLS <ls_text> TYPE satr_tracetext. DATA lv_tracetext TYPE program. READ TABLE mo_atra->it_tracetext INDEX mr_src->textindex ASSIGNING <ls_text>. CHECK sy-subrc = 0. ms_target-type = c_type_class. ms_target-event = c_create_method. ms_target-system = <ls_text>-sysflag. lv_tracetext = <ls_text>-tracetext. " type conversion READ TABLE mo_atra->it_traceprog TRANSPORTING NO FIELDS WITH KEY cprog = lv_tracetext. CHECK sy-subrc NE 0. ms_target-global = mo_class_name->technical( lv_tracetext ). set_local_class( lv_tracetext ). ENDMETHOD. "set_constructor METHOD set_local_class. CHECK mo_class_name->is_global( iv_progname ) EQ abap_false. ms_target-local = iv_progname. CHECK module( mr_src->progindex ) EQ 0. ms_target-type = c_type_class. " overwrite TYPE IF found ENDMETHOD. "set_local_class METHOD set_form. event( ). module( mr_src->progindex ). ENDMETHOD. "set_form METHOD set_tran. CHECK event( ) EQ 0. ms_target-global = ms_target-event. ms_target-type = c_type_tran. ENDMETHOD. "set_tran METHOD set_submit. CHECK event( ) EQ 0. READ TABLE mo_atra->it_traceprog TRANSPORTING NO FIELDS WITH KEY cprog = ms_target-event. module( sy-tabix ). ENDMETHOD. "set_submit METHOD set_function_module. * PR_2INDEX : Index of Int. Table TRACEPROG for Performs CHECK event( ) EQ 0. module( mr_src->pr_2index ). ENDMETHOD. "set_function_module METHOD parse. rv_flag = abap_true. * General data CLEAR ms_target. ms_target-instance = mr_src->instance. CASE mr_src->id. " ID of a Trace Record (Recorded Event) WHEN c_id_class. " R - We only check for CREATE OBJECT (R/O) event set_constructor( ). WHEN c_id_method. " m - Record of methods-calls set_meth( ). WHEN c_id_func. " U - Record of Function Modules set_function_module( ). WHEN c_id_module. " M - Module Calls set_form( ). WHEN c_id_load. " L - Load IF mr_src->subid = 'D'. " Dynpro set_form( ). ENDIF. WHEN c_id_report. " CALL - e.g. Call SCREEN/CALL Report CASE mr_src->subid. " Sub-ID of a TRACE Record WHEN 'D' OR 'S'. " CALL Dialog OR Call Screen set_form( ). WHEN 'P'. " CALL Program module( mr_src->progindex ). WHEN 'T'. " CALL Transaction set_tran( ). WHEN 'R'. " SUBMIT Report set_submit( ). ENDCASE. WHEN c_id_form. " F - Record of Form-Routines CASE mr_src->subid. WHEN ' '. set_form( ). " internal FORM WHEN 'E'. set_function_module( ). " external FORM WHEN 'N'. " Not Found. e.g. PERFORM IF FOUND failed rv_flag = abap_false. ENDCASE. WHEN c_id_record. " O - Record of measurement CASE mr_src->subid. WHEN ' '. module( mr_src->progindex ). WHEN OTHERS. ENDCASE. WHEN c_id_dynpro " A - Maybe PBO/PAI-Modules OR c_id_flow. " E - Processing Logic (Flow Logic) * Screen handling does not add anything new, the module is needed module( mr_src->progindex ). WHEN OTHERS. " Y - SAP GUI Events? ENDCASE. mo_class_name->external( CHANGING cv_clas = ms_target-global ). ENDMETHOD. "parse ENDCLASS. "lcl_abap_trace IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_trace IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_trace IMPLEMENTATION. METHOD constructor. super->constructor( ). lcl_progress_indicator=>echo( iv_text = |Parse { lines( mo_atra->it_austab_hier ) } entries| ). CREATE OBJECT mo_bag. ENDMETHOD. "constructor METHOD fill. DATA ls_sat TYPE ts_sat. * Parse call hierarchy, apply custom filters, fill collection of messages LOOP AT mo_atra->it_austab_hier REFERENCE INTO mr_src. CHECK new_record( ) EQ abap_true. ls_sat = get_sat( ). IF ii_collector->collect( ls_sat ) EQ abap_false. skip_record( ). ENDIF. put_caller( ls_sat ). ENDLOOP. ENDMETHOD. "fill METHOD new_record. rv_flag = abap_false. IF mr_src->event = c_event_entry. " Event ID (> Entry, < Exit) mo_bag->add( mr_src->index ). * Parse call hierarchy in trace rv_flag = parse( ). ELSE. skip_record( ). ENDIF. ENDMETHOD. "new_record METHOD get_sat. DATA ls_seq TYPE ts_seq. CLEAR rs_sat. rs_sat-id = mr_src->id. " Gen. data rs_sat-aus_tabix = mr_src->index. " Index of DUMP List rs_sat-from_level = mr_src->ebene. " Call Level (Call Hierarchy) rs_sat-called-object = ms_target-object. rs_sat-called-mod = ms_target-event. rs_sat-system = ms_target-system. * IV_CALLER : Entry No. of CALLER in Table TRACE READ TABLE mt_caller INTO ls_seq WITH KEY k1 COMPONENTS aus_tabix = mr_src->caller. IF sy-subrc = 0. rs_sat-caller = ls_seq-called. ENDIF. ENDMETHOD. "get_sat METHOD put_caller. DATA ls_seq TYPE ts_seq. ls_seq = is_sat-seq. IF ls_seq-called-global IS INITIAL. ls_seq-called = is_sat-caller. ENDIF. mo_class_name->external( CHANGING cv_clas = ls_seq-called-global ). APPEND ls_seq TO mt_caller. ENDMETHOD. "put_caller METHOD skip_record. FIELD-SYMBOLS <ls_seq> TYPE ts_seq. DATA ls_seq TYPE ts_seq. * Aussprungsätze. READ TABLE mt_caller ASSIGNING <ls_seq> WITH KEY k1 COMPONENTS aus_tabix = mr_src->start. IF sy-subrc EQ 0. IF mr_src->id = c_id_class AND mr_src->subid = 'O'. IF mo_bag->contains( mr_src->start ) EQ abap_true. <ls_seq>-called-instance = mr_src->instance. ENDIF. ENDIF. CLEAR ls_seq. ls_seq-called-instance = mr_src->instance. ls_seq-aus_tabix = mr_src->index. APPEND ls_seq TO mt_caller. ELSE. mo_bag->remove( mr_src->start ). ENDIF. ENDMETHOD. "skip_record ENDCLASS. "lcl_trace IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_actors IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_actors IMPLEMENTATION. METHOD next_number. ADD 1 TO mv_last_number. rv_key = mv_last_number. ENDMETHOD. "next_number METHOD index_of. DATA ls_actor TYPE ts_actor. CLEAR rv_idx. READ TABLE mt_actor INTO ls_actor WITH KEY object = is_object. IF sy-subrc EQ 0. rv_idx = sy-tabix. ELSE. IF is_object-type EQ c_type_class. IF is_object-instance IS NOT INITIAL. * Inheritance cases: do not check object name READ TABLE mt_actor INTO ls_actor WITH KEY instance = is_object-instance. ELSE. READ TABLE mt_actor INTO ls_actor WITH KEY instance = is_object-instance local = is_object-local global = is_object-global. ENDIF. ELSEIF is_object-type IS NOT INITIAL. READ TABLE mt_actor INTO ls_actor WITH KEY local = is_object-local global = is_object-global type = is_object-type. ENDIF. IF sy-subrc EQ 0. rv_idx = sy-tabix. ENDIF. ENDIF. CHECK rv_idx IS NOT INITIAL. rv_idx = ls_actor-index. " mapping ENDMETHOD. "index_of METHOD put. DATA ls_actor TYPE ts_actor. rv_key = index_of( is_object ). CHECK rv_key IS INITIAL. rv_key = next_number( ). " Next primary key field or SORTED ls_actor-object = is_object. ls_actor-index = rv_key. ls_actor-label = lifeline( is_object ). INSERT ls_actor INTO TABLE mt_actor. ENDMETHOD. "put METHOD lif_actors~new_path. rs_path-caller = put( is_sat-caller-object ). rs_path-called = put( is_sat-called-object ). ENDMETHOD. "lif_actors~new_path METHOD lifeline. DATA lv_name TYPE string. CASE is_object-type. WHEN c_type_class. lv_name = |{ is_object-local }\\n{ is_object-global }|. IF is_object-instance EQ 0. rv_text = |Static Methods of Class { lv_name }|. ELSE. rv_text = |ObjectId:{ is_object-instance } of Class { lv_name }|. ENDIF. WHEN c_type_fugr. IF is_object-global+0(1) EQ '/'. lv_name = is_object-global+0(6) && is_object-global+10. ELSE. lv_name = is_object-global+4. ENDIF. rv_text = |Function Group\\n{ lv_name }|. WHEN OTHERS. rv_text = |{ is_object-type }\\n{ is_object-global }|. ENDCASE. ENDMETHOD. "lifeline METHOD class_name. DATA ls_actor TYPE ts_actor. CLEAR rv_short_name. READ TABLE mt_actor WITH TABLE KEY obj_nr COMPONENTS index = iv_index INTO ls_actor. CHECK sy-subrc EQ 0. IF ls_actor-local EQ space. rv_short_name = ls_actor-global. ELSE. rv_short_name = ls_actor-local. ENDIF. ENDMETHOD. "class_name METHOD lif_actors~lifelines. DATA ls_actor TYPE ts_actor. CLEAR rt_lifeline. LOOP AT mt_actor INTO ls_actor. INSERT ls_actor-lifeline INTO TABLE rt_lifeline. ENDLOOP. ENDMETHOD. "lif_actors~lifelines METHOD lif_actors~short_text. CASE is_message-id. WHEN c_id_skip. rv_text = |<b>Skipping over SAP code until calling { is_message-msg }</b>|. WHEN c_id_class. rv_text = |Create instance of class { class_name( is_message-called ) }|. WHEN OTHERS. rv_text = call_short_text( is_message ). ENDCASE. ENDMETHOD. "lif_actors~short_text METHOD call_short_text. DATA lv_call_text TYPE string. CASE is_message-id. WHEN c_id_form. lv_call_text = |Perform|. WHEN c_id_func. lv_call_text = |Call FM|. WHEN c_id_method. lv_call_text = |Call method|. WHEN OTHERS. lv_call_text = space. ENDCASE. rv_text = |{ lv_call_text } { is_message-msg }|. ENDMETHOD. "call_short_text ENDCLASS. "lcl_actors IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_sequence IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_sequence IMPLEMENTATION. METHOD constructor. CREATE OBJECT mi_actors TYPE lcl_actors. mi_filter = lcl_filter=>new( is_cfg ). " Custom filter mo_uml = lcl_uml=>new( iv_uml = is_cfg-uml_format ii_actors = mi_actors ). mv_compact_trace = is_cfg-compact_trace. ENDMETHOD. "constructor METHOD link_to_previous. DATA ls_sat TYPE ts_sat. * add a link to last valid entry to fill gaps created by filter logic rs_sat = is_sat. IF ms_previous IS NOT INITIAL AND ms_previous-called NE is_sat-caller AND is_sat-from_level GT ms_previous-from_level. IF is_sat-caller IS INITIAL. rs_sat-caller = ms_previous-called. " check initial? ELSE. * insert a new line into the table at this point (link to last valid line). CLEAR ls_sat. ls_sat-id = c_id_skip. ls_sat-from_level = ms_previous-from_level. " Call Level (Call Hierarchy) ls_sat-caller = ms_previous-called. ls_sat-called = is_sat-caller. add( ls_sat ). ENDIF. ENDIF. ms_previous = rs_sat. ENDMETHOD. "link_to_previous METHOD lif_collector~collect. rv_flag = mi_filter->accepts( is_sat ). CHECK rv_flag EQ abap_true. * Update previous entry (fill gaps if needed) add( link_to_previous( is_sat ) ). ENDMETHOD. "lif_collector~collect METHOD add. DATA ls_message TYPE ts_message. ls_message-id = is_sat-id. ls_message-from_level = is_sat-from_level. ls_message-msg = is_sat-called-mod. ls_message-path = mi_actors->new_path( is_sat ). APPEND ls_message TO mt_trace. ENDMETHOD. "add METHOD to_diagram. DATA lo_progress TYPE REF TO lcl_progress_indicator. DATA lo_sequence TYPE REF TO lcl_sequence. lo_progress = lcl_progress_indicator=>new( is_cfg ). CREATE OBJECT lo_sequence EXPORTING is_cfg = is_cfg. ro_diagram = lcl_diagram_text=>new( is_cfg = is_cfg iv_text = lo_sequence->get_calls( )->to_uml( ) ). lo_progress->restore( ). ENDMETHOD. "to_diagram METHOD get_calls. DATA lo_trace TYPE REF TO lcl_trace. * filter and convert ABAP call hierarchy trace: CREATE OBJECT lo_trace. lo_trace->fill( me ). ro_calls = to_call_stack( ). ENDMETHOD. "get_calls METHOD to_call_stack. * Convert trace to call stack object ro_calls = lcl_uml_factory=>new_calls( iv_compact_trace = mv_compact_trace it_trace = mt_trace io_uml = mo_uml ). ENDMETHOD. "to_call_stack ENDCLASS. "lcl_sequence IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_uml IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml IMPLEMENTATION. METHOD new. CASE iv_uml. WHEN c_uml_graph. CREATE OBJECT ro_uml TYPE lcl_uml_graph. WHEN c_uml_mscgen. CREATE OBJECT ro_uml TYPE lcl_uml_mscgen. WHEN c_uml_mock. CREATE OBJECT ro_uml TYPE lcl_uml_logger. WHEN OTHERS. CREATE OBJECT ro_uml TYPE lcl_uml_plant. ENDCASE. ro_uml->mi_actors = ii_actors. ENDMETHOD. "new METHOD add. mv_diagram = mv_diagram && iv_code. ENDMETHOD. "add METHOD complete. RETURN. ENDMETHOD. "complete METHOD separator. RETURN. ENDMETHOD. "separator METHOD delimiter. RETURN. ENDMETHOD. "delimiter METHOD top. DATA ls_lifeline TYPE ts_lifeline. DATA lt_lifeline TYPE lif_actors=>tt_lifeline. header( ). * Init life lines lt_lifeline = mi_actors->lifelines( ). LOOP AT lt_lifeline INTO ls_lifeline. participant( ls_lifeline ). AT LAST. delimiter( ). "if needed: e.g. add( |;\n| ). EXIT. ENDAT. separator( ). "if needed: e.g. add( |, | ). ENDLOOP. ENDMETHOD. "top METHOD bottom. DATA ls_lifeline TYPE ts_lifeline. DATA lt_lifeline TYPE lif_actors=>tt_lifeline. * Complete all life lines lt_lifeline = mi_actors->lifelines( ). LOOP AT lt_lifeline INTO ls_lifeline. complete( ls_lifeline ). ENDLOOP. footer( ). rv_diagram = mv_diagram. ENDMETHOD. "bottom METHOD message. CHECK is_message IS NOT INITIAL. call( is_message ). IF is_message-id = c_id_skip. skip_note( is_message-path ). ENDIF. ENDMETHOD. "message ENDCLASS. "lcl_uml IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_uml_plant IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_plant IMPLEMENTATION. METHOD add_param. CHECK iv_cond EQ abap_true. mv_param = mv_param && iv_command. ENDMETHOD. "add_param METHOD scale. DATA lv_scale TYPE tv_scale. lv_scale = nmin( val1 = 1 val2 = nmax( val1 = is_cfg-scale val2 = c_min_scale ) ). rv_scale = |scale { lv_scale DECIMALS = 2 }\n|. ENDMETHOD. "scale METHOD parameters. CLEAR mv_param. add_param( scale( is_cfg ) ). " Reduce the size of the output image add_param( iv_cond = is_cfg-teoz_architecture iv_command = |!pragma teoz true\n| ). add_param( |skinparam \{\n| ). * For performance: disable shadowing add_param( iv_cond = boolc( is_cfg-shadowing EQ abap_false ) iv_command = |shadowing false\n| ). * mimic hand writting add_param( iv_cond = is_cfg-handwritten iv_command = |handwritten true\n| ). add_param( |\}\n| ). rv_param = mv_param. ENDMETHOD. "parameters METHOD header. * Plant UML Header add( |@startuml\nhide footbox\nautonumber\n{ parameters( lcl_configuration=>get( ) ) }| ). ENDMETHOD. "header METHOD call. add( |{ is_message-caller } -> { is_message-called }: { mi_actors->short_text( is_message ) }\n| ). add( |activate { is_message-called }\n| ). ENDMETHOD. "call METHOD return. * return and deactivate the actor IF iv_from NE iv_to. add( |{ iv_from } --> { iv_to }\n| ). ENDIF. CHECK iv_from IS NOT INITIAL. add( |deactivate { iv_from }\n| ). ENDMETHOD. "return METHOD participant. add( |participant "{ is_lifeline-label }" as { is_lifeline-index }\n| ). ENDMETHOD. "participant METHOD footer. add( |@enduml\n| ). ENDMETHOD. "footer METHOD skip_note. add( |note over { is_path-caller },{ is_path-called }\n{ c_txt_std }\nend note\n| ). ENDMETHOD. "skip_note METHOD begin_loop. add( |loop { iv_times } times\n| ). " pop ENDMETHOD. "begin_loop METHOD end_loop. add( |end\n| ). ENDMETHOD. "end_loop ENDCLASS. "lcl_uml_plant IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_messages IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_messages IMPLEMENTATION. METHOD constructor. CLEAR mv_idx. mt_list = it_messages. " shared, never changed IF iv_stop IS INITIAL. mv_size = lines( mt_list ). ELSE. mv_size = iv_stop. ENDIF. skip( iv_start - 1 ). mv_first_level = next_level( ). ENDMETHOD. "constructor METHOD next. skip( ). READ TABLE mt_list INDEX mv_idx INTO rs_data. CHECK sy-subrc NE 0. RAISE EXCEPTION TYPE cx_sy_itab_error. ENDMETHOD. "next METHOD has_next. rv_flag = boolc( mv_idx < mv_size ). ENDMETHOD. "has_next METHOD skip. ADD iv_count TO mv_idx. ENDMETHOD. "skip METHOD is_first. rv_flag = boolc( mv_idx LE 1 ). ENDMETHOD. "is_first METHOD first_level. rv_level = mv_first_level. ENDMETHOD. "first_level METHOD next_level. DATA ls_data TYPE ts_message. IF mv_idx < mv_size. READ TABLE mt_list INDEX mv_idx + 1 INTO ls_data. IF sy-subrc NE 0. RAISE EXCEPTION TYPE cx_sy_itab_error. ELSE. rv_level = ls_data-from_level. ENDIF. ELSE. rv_level = c_default_level. ENDIF. ENDMETHOD. "next_level ENDCLASS. "lcl_messages IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_stack IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_stack IMPLEMENTATION. METHOD push. DATA lr_level LIKE mr_level. * First create new node CREATE DATA lr_level. lr_level->call_level = is_ref. lr_level->next = mr_level. * then overwrite old node mr_level = lr_level. mv_empty = abap_false. ENDMETHOD. "push METHOD pop. FIELD-SYMBOLS <lr_next> TYPE ts_level. CLEAR rs_to. IF mr_level IS BOUND. rs_to = mr_level->call_level. mr_level ?= mr_level->next. ENDIF. mv_empty = boolc( mr_level IS NOT BOUND ). ENDMETHOD. "pop ENDCLASS. "lcl_stack IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_uml_stack IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_stack IMPLEMENTATION. METHOD constructor. super->constructor( ). mo_uml = io_uml. mv_previous_level = lcl_messages=>c_default_level. ENDMETHOD. "constructor METHOD call. DATA ls_call TYPE ts_call_level. ls_call-actor_key = is_message-called. ls_call-from_level = is_message-from_level. push( ls_call ). * UML text form generator mo_uml->message( is_message ). ENDMETHOD. "call METHOD return. IF iv_to_level <= mv_previous_level. return_to( iv_to_level ). ENDIF. mv_previous_level = iv_to_level. ENDMETHOD. "return METHOD return_to. DATA ls_from TYPE ts_call_level. DATA ls_to TYPE ts_call_level. ls_from = pop( ). WHILE ls_from-from_level >= iv_level AND mv_empty EQ abap_false. ls_to = pop( ). * UML diagram mo_uml->return( iv_from = ls_from-actor_key iv_to = ls_to-actor_key ). ls_from = ls_to. ENDWHILE. CHECK ls_to IS NOT INITIAL. push( ls_to ). ENDMETHOD. "return_to ENDCLASS. "lcl_uml_stack IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_call_stack IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_call_stack IMPLEMENTATION. METHOD constructor. super->constructor( io_uml ). mo_messages = io_messages. ENDMETHOD. "constructor METHOD message. DATA ls_message TYPE ts_message. IF mo_messages->is_first( ) EQ abap_true. * very first call ls_message-caller = mo_messages->first_level( ). ls_message-called = c_first_key. call( ls_message ). ENDIF. call( is_message ). return( mo_messages->next_level( ) ). ENDMETHOD. "message METHOD to_uml. * UML header mo_uml->top( ). * Convert call stack to UML sequence diagram in text format WHILE mo_messages->has_next( ) EQ abap_true. message( is_message = mo_messages->next( ) iv_idx = sy-index ). ENDWHILE. * add UML footer rv_diagram = mo_uml->bottom( ). ENDMETHOD. "to_uml ENDCLASS. "lcl_call_stack IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_call_stack_compact IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_call_stack_compact IMPLEMENTATION. METHOD constructor. super->constructor( io_uml = io_uml io_messages = io_messages ). mt_cycle = it_cycles. ENDMETHOD. "constructor METHOD message. begin( iv_idx = iv_idx iv_from = is_message-caller ). " if needed super->message( is_message = is_message iv_idx = iv_idx ). * after return so the loop encloses the return path mo_messages->skip( end( iv_idx = iv_idx iv_to = is_message-called ) ). ENDMETHOD. "message METHOD name. * Loop ID rv_name = |{ iv_start }to{ iv_end }|. ENDMETHOD. "name METHOD begin. DATA ls_cycle TYPE ts_cycle. LOOP AT mt_cycle INTO ls_cycle WHERE start = iv_idx. mo_uml->begin_loop( iv_from = |{ iv_from }| iv_times = ls_cycle-times iv_name = name( iv_start = iv_idx iv_end = ls_cycle-end ) ). ENDLOOP. ENDMETHOD. "begin METHOD end. DATA ls_cycle TYPE ts_cycle. CLEAR rv_step. LOOP AT mt_cycle INTO ls_cycle WHERE end = iv_idx. "#EC CI_SORTSEQ mo_uml->end_loop( iv_to = |{ iv_to }| iv_name = name( iv_start = ls_cycle-start iv_end = iv_idx ) ). rv_step = nmax( val1 = ls_cycle-last - iv_idx val2 = rv_step ). ENDLOOP. ENDMETHOD. "end ENDCLASS. "lcl_call_stack_compact IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_trace_index IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_trace_index IMPLEMENTATION. METHOD constructor. mt_trace = it_trace. mi_cycles = ii_cycles. to_index( ). lcl_progress_indicator=>echo( iv_text = |Compact trace with { lines( it_trace ) } entries| ). ENDMETHOD. "constructor METHOD to_index. TYPES tt_trace_index TYPE STANDARD TABLE OF sytabix. DATA lt_trace_index TYPE tt_trace_index. FIELD-SYMBOLS <ls_trace> LIKE LINE OF mt_trace. * mt_components = VALUE #( FOR t IN mt_trace ( t-message ) ). dump on duplicates! does not pass test CLEAR mt_components. LOOP AT mt_trace ASSIGNING <ls_trace>. INSERT <ls_trace> INTO TABLE mt_components. ENDLOOP. CLEAR lt_trace_index. LOOP AT mt_trace ASSIGNING <ls_trace>. READ TABLE mt_components FROM <ls_trace> TRANSPORTING NO FIELDS. APPEND sy-tabix TO lt_trace_index. ENDLOOP. mt_xindex = lt_trace_index. ENDMETHOD. "to_index METHOD to_trace. DATA lv_x_idx TYPE tv_x_tabix. DATA ls_trace TYPE ts_message. CLEAR ct_trace. LOOP AT mt_xindex INTO lv_x_idx. CLEAR ls_trace. READ TABLE mt_components INDEX lv_x_idx INTO ls_trace. APPEND ls_trace TO ct_trace. ENDLOOP. ENDMETHOD. "to_trace METHOD shrink. lcl_pattern=>new( it_xindex = mt_xindex ii_cycles = mi_cycles )->detect_cycles( ). rv_shrink = filter( ). lcl_progress_indicator=>echo( iv_percentage = iv_pass iv_text = |Compact trace pass { iv_pass } - { lines( mt_xindex ) } entries| ). ENDMETHOD. "shrink METHOD filter. DATA lt_xorg LIKE mt_xindex. lt_xorg = mt_xindex. compact( EXPORTING it_xindex = lt_xorg IMPORTING et_xindex = mt_xindex ). rv_shrink = boolc( lt_xorg NE mt_xindex ). ENDMETHOD. "filter METHOD compact. FIELD-SYMBOLS <lv_x_index> TYPE tv_x_tabix. CLEAR et_xindex. LOOP AT it_xindex ASSIGNING <lv_x_index>. CHECK mi_cycles->accepts( sy-tabix ) EQ abap_true. APPEND <lv_x_index> TO et_xindex. ENDLOOP. ENDMETHOD. "compact ENDCLASS. "lcl_trace_index IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_uml_factory IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_factory IMPLEMENTATION. METHOD new. * create UML sequence diagram translator IF iv_compact_trace EQ abap_true. CREATE OBJECT ro_factory TYPE lcl_uml_compactor. ELSE. CREATE OBJECT ro_factory TYPE lcl_uml_factory. ENDIF. ENDMETHOD. "new METHOD new_calls. ro_calls = lcl_uml_factory=>new( iv_compact_trace )->new_call_stack( io_uml = io_uml it_trace = it_trace ). ENDMETHOD. "new_calls METHOD new_iterator. DATA lt_messages TYPE tt_message. fold( EXPORTING it_trace = it_trace IMPORTING et_messages = lt_messages ). CREATE OBJECT ro_iterator EXPORTING it_messages = lt_messages. ENDMETHOD. "new_iterator METHOD fold. et_messages = it_trace. ENDMETHOD. "fold METHOD new_call_stack. CREATE OBJECT ro_calls EXPORTING io_uml = io_uml io_messages = new_iterator( it_trace ). ENDMETHOD. "new_call_stack ENDCLASS. "lcl_uml_factory IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_uml_compactor IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_compactor IMPLEMENTATION. METHOD fold. DATA lo_evt TYPE REF TO lcl_trace_index. CLEAR et_messages. CREATE OBJECT lo_evt EXPORTING it_trace = it_trace ii_cycles = me. WHILE lo_evt->shrink( sy-index ) EQ abap_true. lcl_shrinkage=>shrink_references( CHANGING ct_cycles = mt_cycles ). ENDWHILE. lo_evt->to_trace( CHANGING ct_trace = et_messages ). ENDMETHOD. "fold METHOD new_call_stack. CREATE OBJECT ro_calls TYPE lcl_call_stack_compact EXPORTING io_uml = io_uml io_messages = new_iterator( it_trace ) it_cycles = mt_cycles. ENDMETHOD. "new_call_stack METHOD lif_cycles~collect. * duplicate entries are inserted BEFORE the existing row INSERT is_cycle INTO TABLE mt_cycles. ENDMETHOD. "lif_cycles~collect METHOD lif_cycles~accepts. LOOP AT mt_cycles TRANSPORTING NO FIELDS "#EC CI_SORTSEQ WHERE end LT iv_index AND last GE iv_index. rv_flag = abap_false. RETURN. ENDLOOP. rv_flag = abap_true. ENDMETHOD. "lif_cycles~accepts ENDCLASS. "lcl_uml_compactor IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_shrinkage IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_shrinkage IMPLEMENTATION. METHOD shrink_references. DATA lo_shrink TYPE REF TO lcl_shrinkage. * reconcile the loop reference table CREATE OBJECT lo_shrink EXPORTING it_cycles = ct_cycles. * Change needed ? WHILE lo_shrink->ms_stop IS NOT INITIAL. * Rebuild loop references table: lo_shrink = lo_shrink->rebuild( ). ENDWHILE. ct_cycles = lo_shrink->mt_cycles. ENDMETHOD. "shrink_references METHOD constructor. DATA ls_cycle TYPE ts_cycle. mt_cycles = it_cycles. CLEAR mv_tabix. LOOP AT it_cycles INTO ls_cycle. mv_tabix = sy-tabix. * Change needed in subsequent entries? CHECK ls_cycle-last GT ls_cycle-end. ms_stop = ls_cycle. EXIT. ENDLOOP. ENDMETHOD. "constructor METHOD renew. DATA lt_cycles TYPE lcl_call_stack_compact=>tt_cycle. lt_cycles = mt_shrinked_loop. CREATE OBJECT ro_shrink EXPORTING it_cycles = lt_cycles. ENDMETHOD. "renew METHOD rebuild. DATA ls_cycle TYPE ts_cycle. DATA lv_delta TYPE i. * after removing repetition from the active loop in the trace table, * rebuild (compress) the loop reference table LOOP AT mt_cycles INTO ls_cycle. AT FIRST. lv_delta = ms_stop-last - ms_stop-end. ENDAT. IF ls_cycle-last LT ms_stop-start. * Loops entries before the active loop - verbatim insert( ls_cycle ). ENDIF. IF ls_cycle-start LT ms_stop-start AND ls_cycle-end GE ms_stop-last. * Loops entries starting before the active loop to be compacted and * that contains it (i.e. they enclose it ) - adjust ls_cycle-end = ls_cycle-end - lv_delta. ls_cycle-last = ls_cycle-last - lv_delta. insert( ls_cycle ). ENDIF. IF sy-tabix EQ mv_tabix. * Update the active loop entry ls_cycle = ms_stop. ls_cycle-last = ms_stop-end. insert( ls_cycle ). ELSEIF ls_cycle-start GE ms_stop-start AND ls_cycle-last LE ms_stop-end. * Loops entries completely enclosed by the active loop entry - verbatim insert( ls_cycle ). ENDIF. IF ls_cycle-start GT ms_stop-last. * Loops entries after the active loop - shifted by Offset ls_cycle-start = ls_cycle-start - lv_delta. ls_cycle-end = ls_cycle-end - lv_delta. ls_cycle-last = ls_cycle-last - lv_delta. insert( ls_cycle ). ENDIF. ENDLOOP. * Write back ro_shrink = renew( ). ENDMETHOD. "rebuild METHOD insert. INSERT ls_cycle INTO TABLE mt_shrinked_loop. ENDMETHOD. "insert ENDCLASS. "lcl_shrinkage IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_pattern IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_pattern IMPLEMENTATION. METHOD new. CREATE OBJECT ro_pattern EXPORTING it_xindex = it_xindex iv_start = iv_start iv_stop = iv_stop. ro_pattern->mi_cycles = ii_cycles. ENDMETHOD. "new METHOD constructor. CLEAR mv_idx. mt_xindex = it_xindex. " Copied or Shared??? never changed IF iv_stop IS INITIAL. mv_size = lines( mt_xindex ). ELSE. mv_size = iv_stop. ENDIF. skip( iv_start - 1 ). ENDMETHOD. "constructor METHOD clone. DATA lv_stop LIKE iv_stop. IF iv_stop IS SUPPLIED. lv_stop = iv_stop. ELSE. lv_stop = mv_size. ENDIF. ro_loop = new( iv_start = iv_start iv_stop = lv_stop it_xindex = mt_xindex ii_cycles = mi_cycles ). ENDMETHOD. "clone METHOD skip. ADD iv_count TO mv_idx. ENDMETHOD. "skip METHOD has_next. rv_flag = boolc( mv_idx < mv_size ). ENDMETHOD. "has_next METHOD occurs_in. FIELD-SYMBOLS <lv_index> LIKE LINE OF mt_xindex. DATA lv_new LIKE LINE OF mt_xindex. DATA lv_other LIKE iv_end. lv_other = iv_end. rv_same = abap_false. LOOP AT mt_xindex FROM iv_start TO iv_end ASSIGNING <lv_index>. ADD 1 TO lv_other. READ TABLE mt_xindex INDEX lv_other INTO lv_new. CHECK lv_new NE <lv_index>. RETURN. ENDLOOP. * returning abap_true for zero length tests will lead to infinite loop rv_same = boolc( sy-subrc EQ 0 ). ENDMETHOD. "occurs_in METHOD match_pattern. DATA ls_group TYPE ts_cycle. DATA lv_dist TYPE i. rv_match = abap_false. lv_dist = iv_end - mv_idx + 1. CHECK iv_end GE 0 AND lv_dist GT 0. ls_group-start = mv_idx. ls_group-end = iv_end. ls_group-last = iv_end. ls_group-times = 1. * Find consecutive occurrences of group WHILE ( ls_group-last + lv_dist <= mv_size ) AND occurs_in( iv_start = mv_idx iv_end = ls_group-last ) EQ abap_true. ADD 1 TO ls_group-times. ADD lv_dist TO ls_group-last. skip( lv_dist ). ENDWHILE. CHECK ls_group-times GT 1. rv_match = abap_true. skip( lv_dist - 1 ). mi_cycles->collect( ls_group ). * repeat search in a subrange (recursive call) clone( iv_start = ls_group-start + 1 iv_stop = ls_group-end )->detect_cycles( ). ENDMETHOD. "match_pattern METHOD detect_cycles. DATA lv_index TYPE sytabix. * identify blocks of repeated messages (cycles) * Pass the cycle information to interface LIF_CYCLES WHILE has_next( ) EQ abap_true. skip( ). lv_index = mv_idx. DO. "#EC CI_NESTED. lv_index = next_occurrence_from( lv_index ). CHECK lv_index IS INITIAL OR match_pattern( lv_index - 1 ) EQ abap_true. EXIT. ENDDO. ENDWHILE. ENDMETHOD. "detect_cycles METHOD next_occurrence_from. DATA lv_until TYPE sytabix. FIELD-SYMBOLS <lv_org> LIKE LINE OF mt_xindex. CLEAR rv_row. lv_until = nmin( val1 = iv_from + c_max_step val2 = mv_size ). CHECK lv_until GT iv_from. READ TABLE mt_xindex INDEX iv_from ASSIGNING <lv_org>. CHECK sy-subrc EQ 0. FIND FIRST OCCURRENCE OF <lv_org> IN TABLE mt_xindex FROM iv_from + 1 TO lv_until IN BYTE MODE MATCH LINE rv_row. ENDMETHOD. "next_occurrence_from ENDCLASS. "lcl_pattern IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_file_name IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_file_name IMPLEMENTATION. METHOD new. CASE iv_mode. WHEN lcl_diagram_text=>c_mode_aut. CREATE OBJECT ro_file TYPE lcl_file_test_name EXPORTING iv_mode = iv_mode. WHEN OTHERS. CREATE OBJECT ro_file TYPE lcl_file_name EXPORTING iv_mode = iv_mode. ENDCASE. ENDMETHOD. "new METHOD constructor. CASE iv_mode. WHEN lcl_diagram_text=>c_mode_txt. ms_file-title = |Save UML text source|. ms_file-ext = |.txt|. WHEN OTHERS. ms_file-title = |Save As...|. ms_file-ext = |.txt|. ENDCASE. ENDMETHOD. "constructor METHOD get_prefix. rv_name = shift_right( val = ms_file-name places = strlen( ms_file-ext ) ). ENDMETHOD. "get_prefix METHOD get_fullpath. rv_name = ms_file-path. ENDMETHOD. "get_fullpath METHOD dialog. DATA lv_path TYPE string. CLEAR rv_user_action. cl_gui_frontend_services=>file_save_dialog( EXPORTING window_title = ms_file-title " Window Title default_extension = ms_file-ext " Default Extension CHANGING filename = ms_file-name " File Name to Save path = lv_path " Path to File fullpath = ms_file-path " Path + File Name user_action = rv_user_action " User Action (C Class Const ACTION_OK, ACTION_OVERWRITE etc) * file_encoding = EXCEPTIONS OTHERS = 0 ). ENDMETHOD. "dialog ENDCLASS. "lcl_file_name IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_file_test_name IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_file_test_name IMPLEMENTATION. METHOD dialog. ms_file-path = |test.txt|. rv_user_action = cl_gui_frontend_services=>action_cancel. ENDMETHOD. "dialog ENDCLASS. "lcl_file_test_name IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_file IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_file IMPLEMENTATION. METHOD download. rv_subrc = 1. CHECK io_name->dialog( ) NE cl_gui_frontend_services=>action_cancel. rv_subrc = cl_uml_utilities=>save_xml_local( xml = iv_data filename = io_name->get_fullpath( ) ). ENDMETHOD. "download ENDCLASS. "lcl_file IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_diagram_text IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_diagram_text IMPLEMENTATION. METHOD new. CASE is_cfg-uml_format. WHEN OTHERS. * create PlantUML diagram by default CREATE OBJECT ro_diagram TYPE lcl_diagram_plant_uml EXPORTING is_cfg = is_cfg iv_diagram = iv_text. ENDCASE. ENDMETHOD. "new METHOD constructor. mv_diagram = iv_diagram. ms_cfg-output_mode = iv_mode. ENDMETHOD. "constructor METHOD output. save_file( ms_cfg-output_mode ). ENDMETHOD. "output METHOD to_xstring. cl_abap_conv_out_ce=>create( encoding = 'UTF-8' )->convert( EXPORTING data = iv_string IMPORTING buffer = rv_xstring ). ENDMETHOD. "to_xstring METHOD save_file. * export data as (PlantUML) source rv_fname = lcl_file=>download( iv_data = to_xstring( mv_diagram ) io_name = lcl_file_name=>new( iv_mode ) ). ENDMETHOD. "save_file ENDCLASS. "lcl_diagram_text IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_diagram_plant_uml IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_diagram_plant_uml IMPLEMENTATION. METHOD constructor. super->constructor( iv_diagram = iv_diagram iv_mode = is_cfg-output_mode ). ms_cfg = is_cfg. ENDMETHOD. "constructor METHOD output. CASE ms_cfg-output_mode. WHEN c_mode_url. display_html( ). WHEN c_mode_exe. local_plant_uml( ). WHEN OTHERS. super->output( ). ENDCASE. ENDMETHOD. "output METHOD display_url. cl_abap_browser=>show_html( html_string = iv_url size = cl_abap_browser=>xlarge context_menu = abap_true ). ENDMETHOD. "display_url METHOD display_html. display_url( |<img src="{ to_url( ms_cfg-server_url ) }"/>\n{ source( ) }| ). ENDMETHOD. "display_html METHOD to_url. rv_url = iv_base_url && encoded_url_suffix( ). ENDMETHOD. "to_url METHOD encoded_url_suffix. DATA lv_bin TYPE xstring. * for PlantUML Server: Convert to UTF-8, then deflate, then encode (base64 variant) cl_abap_gzip=>compress_binary( EXPORTING raw_in = to_xstring( mv_diagram ) ##type " UTF-8 compress_level = 9 IMPORTING gzip_out = lv_bin ). rv_url = translate( val = cl_http_utility=>encode_x_base64( lv_bin ) from = c_charset_standard to = c_charset_plantuml ). ENDMETHOD. "encoded_url_suffix METHOD local_plant_uml. DATA lo_name TYPE REF TO lcl_file_name. lo_name = lcl_file_name=>new( c_mode_txt ). CHECK lcl_file=>download( iv_data = to_xstring( mv_diagram ) io_name = lo_name ) IS INITIAL. display_url( |<img src="{ to_png( lo_name ) }"/>\n{ source( ) }| ). ENDMETHOD. "local_plant_uml METHOD parameter_string. rv_param = |-jar { ms_cfg-java_jar } -o { ms_cfg-local_path } "{ io_name->get_fullpath( ) }"|. ENDMETHOD. "parameter_string METHOD png_file_name. TRY. rv_name = |{ ms_cfg-local_path }{ io_name->get_prefix( ) }.png|. CATCH cx_dynamic_check. CLEAR rv_name. ENDTRY. ENDMETHOD. "png_file_name METHOD source. CASE ms_cfg-display_source. WHEN abap_false. CLEAR rv_source. WHEN OTHERS. rv_source = |<p>{ mv_diagram }</p>|. ENDCASE. ENDMETHOD. "source METHOD to_png. CLEAR rv_name. cl_gui_frontend_services=>execute( EXPORTING application = ms_cfg-java_appl parameter = parameter_string( io_name ) synchronous = 'X' EXCEPTIONS OTHERS = 1 ). IF sy-subrc EQ 0. rv_name = png_file_name( io_name ). ENDIF. ENDMETHOD. "to_png METHOD get_java_path. CONSTANTS c_registry_java_base_key TYPE string VALUE 'SOFTWARE\JavaSoft\Java Runtime Environment' ##no_text. DATA lv_path TYPE string. CLEAR rv_fullpath. cl_gui_frontend_services=>registry_get_value( EXPORTING root = cl_gui_frontend_services=>hkey_local_machine key = c_registry_java_base_key value = 'CurrentVersion' IMPORTING reg_value = lv_path EXCEPTIONS OTHERS = 5 ). CHECK sy-subrc EQ 0. cl_gui_frontend_services=>registry_get_value( EXPORTING root = cl_gui_frontend_services=>hkey_local_machine key = |{ c_registry_java_base_key }\\{ lv_path }| value = 'JavaHome' IMPORTING reg_value = lv_path EXCEPTIONS OTHERS = 5 ). CHECK sy-subrc EQ 0. rv_fullpath = |{ lv_path }\\bin\\java|. ENDMETHOD. "get_java_path ENDCLASS. "lcl_diagram_plant_uml IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_progress_indicator IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_progress_indicator IMPLEMENTATION. METHOD new. DATA ls_cfg LIKE is_cfg. IF is_cfg IS SUPPLIED. ls_cfg = is_cfg. ELSE. ls_cfg = lcl_configuration=>get( ). ENDIF. CREATE OBJECT ro_prog EXPORTING iv_progress = ls_cfg-progress. ENDMETHOD. "new METHOD constructor. mv_mode = get_mode( ). IF iv_progress EQ abap_true. set_mode( c_enable ). ELSE. set_mode( c_disable ). ENDIF. ENDMETHOD. "constructor METHOD restore. set_mode( mv_mode ). ENDMETHOD. "restore METHOD get_mode. GET PARAMETER ID c_param_id_sin FIELD rv_mode. ENDMETHOD. "get_mode METHOD set_mode. SET PARAMETER ID c_param_id_sin FIELD iv_mode. ENDMETHOD. "set_mode METHOD echo. CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR' EXPORTING percentage = iv_percentage text = iv_text. ENDMETHOD. "echo ENDCLASS. "lcl_progress_indicator IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_configuration IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_configuration IMPLEMENTATION. METHOD class_constructor. DATA ls_pattern LIKE LINE OF gs_cfg-pattern. * Windows: Local Java installation gs_cfg-java_appl = lcl_diagram_plant_uml=>get_java_path( ). IF gs_cfg-java_appl IS INITIAL. gs_cfg-java_appl = `C:\Windows\System32\java`. ENDIF. * PlantUML jar file and output path gs_cfg-local_path = `C:\Temp\Dokumente\UML\`. gs_cfg-java_jar = 'C:\Temp\Dokumente\UML\plantuml.jar'. * PlantUML server URL gs_cfg-server_url = `http://www.plantuml.com/plantuml/img/` ##no_text. * Output mode: c_mode_aut for ABAP Unit Test gs_cfg-output_mode = lcl_diagram_text=>c_mode_url. gs_cfg-skip_dialog = space. gs_cfg-compact_trace = abap_true. gs_cfg-scale = c_default_scale. ls_pattern = 'Y*'. APPEND ls_pattern TO gs_cfg-pattern. ls_pattern = 'Z*'. APPEND ls_pattern TO gs_cfg-pattern. gs_cfg-progress = abap_true. gs_cfg-handwritten = abap_false. gs_cfg-shadowing = abap_false. gs_cfg-display_source = abap_true. gs_cfg-teoz_architecture = abap_false. ENDMETHOD. "class_constructor METHOD get_attributes. DATA ls_attr LIKE LINE OF rt_attr. DEFINE fill_att. clear ls_attr. get reference of &1 into ls_attr-ref. ls_attr-text = &2. ls_attr-kind = &3. insert ls_attr into table rt_attr. END-OF-DEFINITION. DEFINE fill_radio. clear ls_attr. get reference of &1 into ls_attr-ref. ls_attr-text = &2. ls_attr-kind = 'R'. ls_attr-button_group = &3. insert ls_attr into table rt_attr. END-OF-DEFINITION. * Table Type has type 'T' - patterns SCI_PATTERN * ' ' - ?? private attributes? * 'I' - ?? Integer? fill_att gs_cfg-skip_dialog 'Remember my settings'(c00) 'C'. fill_att: '' 'Trace Settings'(c01) 'G', gs_cfg-progress 'Progress indicator'(c02) 'C', gs_cfg-system_events 'System events'(c03) 'C', gs_cfg-compact_trace 'Loops compacted'(c04) 'C', gs_cfg-pattern 'Customer namespace'(c05) 'T'. fill_att: sy-index 'PlantUML Execution Mode'(c10) 'G'. " Group fill_radio: mv_mode_url 'PlantUML web service'(c11) 'MOD', mv_mode_txt 'Save text file'(c12) 'MOD', mv_mode_exe 'Local PlantUML '(c13) 'MOD'. fill_att: '' 'PlantUML Settings'(c20) 'G', gs_cfg-scale 'Scale '(c21) 'S'. fill_att: gs_cfg-server_url 'PlantUML Server'(c25) 'S', gs_cfg-local_path 'Local PlantUML path'(c26) 'S', gs_cfg-java_jar 'Local PlantUML jar file'(c27) ' ', gs_cfg-java_appl 'Local Java path'(c28) 'S'. " Select-Options fill_att: gs_cfg-handwritten 'Handwritten '(c30) 'C', gs_cfg-shadowing 'Shadowing '(c31) 'C', gs_cfg-display_source 'Display source '(c32) 'C', gs_cfg-teoz_architecture 'Use TEOZ architecture '(c33) 'C'. ENDMETHOD. "get_attributes METHOD to_radiobutton. mv_mode_url = boolc( gs_cfg-output_mode EQ lcl_diagram_text=>c_mode_url ). mv_mode_exe = boolc( gs_cfg-output_mode EQ lcl_diagram_text=>c_mode_exe ). mv_mode_txt = boolc( gs_cfg-output_mode EQ lcl_diagram_text=>c_mode_txt ). ENDMETHOD. "to_radiobutton METHOD from_radiobutton. IF mv_mode_url EQ abap_true. gs_cfg-output_mode = lcl_diagram_text=>c_mode_url. ELSEIF mv_mode_exe EQ abap_true. gs_cfg-output_mode = lcl_diagram_text=>c_mode_exe. ELSEIF mv_mode_txt EQ abap_true. gs_cfg-output_mode = lcl_diagram_text=>c_mode_txt. ENDIF. ENDMETHOD. "from_radiobutton METHOD get. MOVE-CORRESPONDING gs_cfg TO rs_cfg. ENDMETHOD. "get METHOD query. DATA lo_config TYPE REF TO lcl_configuration. CREATE OBJECT lo_config. lo_config->dialog( ). rs_cfg = lo_config->get( ). ENDMETHOD. "query METHOD dialog. DATA lv_screen_id TYPE sychar30. to_radiobutton( ). CHECK gs_cfg-skip_dialog EQ abap_false. lv_screen_id = sy-repid. CHECK cl_ci_query_attributes=>generic( p_name = lv_screen_id " unique screen ID p_title = 'Sequence Diagram Parameters' " Screen title p_attributes = get_attributes( ) " Screen fields p_display = abap_false " Edit / Display only ) EQ abap_false. " Do not cancel from_radiobutton( ). ENDMETHOD. "dialog ENDCLASS. "lcl_configuration IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_uml_graph IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_graph IMPLEMENTATION. METHOD header. add( |.PS\n| ). " GNU plot pic2plot macro header for sequence diagram add( |copy "sequence.pic";\n| ). add( |\n| ). ENDMETHOD. "header METHOD top. super->top( ). add( |step();\n| ). ENDMETHOD. "top METHOD call. * alternative: * - create_message(from_object,to_object,object_label); * Has from_object create the to_object, labeled with object_label. * The message is labeled with the «create» stereotype. Can also be written as cmessage. add( |message({ is_message-caller },{ is_message-called },"{ mi_actors->short_text( is_message ) }");\n| ). add( |active({ is_message-called });\n| ). ENDMETHOD. "call METHOD return. * - return_message(from_object,to_object,label) * Draws a return message between two objects, with the given label. Can also be written as rmessage. IF iv_from NE iv_to. add( |return_message({ iv_from },{ iv_to });\n| ). ENDIF. CHECK iv_from IS NOT INITIAL. add( |inactive({ iv_from });\n| ). ENDMETHOD. "return METHOD participant. * - object(name,label); OR actor(name,label); * Defines an object / actor with the given name, labeled on the diagram as specified. * Actors are typically used instead of objects to indicate operations initiated by human action add( |object({ is_lifeline-index },"{ is_lifeline-label }");\n| ). ENDMETHOD. "participant METHOD complete. add( |complete({ is_lifeline-index });\n| ). ENDMETHOD. "complete METHOD footer. add( |.PE\n| ). ENDMETHOD. "footer METHOD skip_note. * - lifeline_constraint(object,label); * Displays a constraint label (typically given inside curly braces) for the given object. * The constraint will appear on the right of the object's lifeline at the time it appears. * Can also be used to place an message label on the left of a message arrow, rather than its center. * Can also be written as lconstraint. add( |lifeline_constraint({ is_path-caller },"{ c_txt_std }")\n| ). ENDMETHOD. "skip_note METHOD begin_loop. * - begin_frame(left_object,name,label_text); * Begins a frame with the upper left corner at left_object column and the current line. * The specified label_text is shown in the upper left corner. add( |begin_frame({ iv_from },{ iv_name },"Loop { iv_times } times");\n| ). " pop ENDMETHOD. "begin_loop METHOD end_loop. * - end_frame(right_object,name); * Ends a frame with the lower right corner at right_object column and the current line. * The name must correspond to a begin_frame's name. add( |end_frame({ iv_to }, { iv_name });\n| ). ENDMETHOD. "end_loop ENDCLASS. "lcl_uml_graph IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_uml_mscgen IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_mscgen IMPLEMENTATION. METHOD header. add( |msc \{\n| ). ENDMETHOD. "header METHOD delimiter. add( |;\n| ). ENDMETHOD. "delimiter METHOD separator. add( |, | ). ENDMETHOD. "separator METHOD call. add( |{ is_message-caller }=>{ is_message-called } [label="{ mi_actors->short_text( is_message ) }"];\n| ). ENDMETHOD. "call METHOD return. CHECK iv_from NE iv_to. add( |{ iv_to }<<{ iv_from }\n| ). ENDMETHOD. "return METHOD participant. add( |{ is_lifeline-index } [label="{ is_lifeline-label }"]| ). ENDMETHOD. "participant METHOD footer. add( |\}\n| ). ENDMETHOD. "footer METHOD skip_note. add( |{ is_path-called } note { is_path-caller } [label="{ c_txt_std }", textbgcolour="#7fff7f"];\n| ). ENDMETHOD. "skip_note METHOD begin_loop. * add( |[c:loop { iv_times } times]\n| ). " pop RETURN. ENDMETHOD. "begin_loop METHOD end_loop. * add( |[/c]\n| ). RETURN. ENDMETHOD. "end_loop ENDCLASS. "lcl_uml_mscgen IMPLEMENTATION *---------------- Unit Tests ----------------------------------------------------* CLASS lcl_logger IMPLEMENTATION. METHOD new. CREATE OBJECT ro_log. ENDMETHOD. "new METHOD verify. cl_abap_unit_assert=>assert_equals( act = mt_log exp = it_exp msg = iv_msg ). ENDMETHOD. "verify METHOD info. APPEND is_log TO mt_log. ENDMETHOD. "info ENDCLASS. "lcl_logger IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_uml_logger IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_uml_logger IMPLEMENTATION. METHOD constructor. super->constructor( ). mo_log = lcl_logger=>new( ). ENDMETHOD. "constructor METHOD log. DATA ls_log TYPE lcl_logger=>ts_log. ls_log-method = iv_method. ls_log-params = iv_params. mo_log->info( ls_log ). ENDMETHOD. "log METHOD header. log( 'HEADER' ). ENDMETHOD. "header METHOD call. log( iv_method = 'CALL' iv_params = |{ is_message-id } { is_message-caller } { is_message-called } { mi_actors->short_text( is_message ) }| ). ENDMETHOD. "call METHOD return. log( iv_method = 'RETURN' iv_params = |{ iv_from } { iv_to }| ). ENDMETHOD. "return METHOD participant. log( iv_method = 'PARTICIPANT' iv_params = |{ is_lifeline-index } { is_lifeline-label }| ). ENDMETHOD. "participant METHOD footer. log( 'FOOTER' ). ENDMETHOD. "footer METHOD skip_note. log( iv_method = 'SKIP_NOTE' iv_params = |{ is_path-called } { is_path-caller }| ). ENDMETHOD. "skip_note METHOD begin_loop. log( iv_method = 'BEGIN_LOOP' iv_params = |{ iv_times }| ). ENDMETHOD. "begin_loop METHOD end_loop. log( 'END_LOOP' ). ENDMETHOD. "end_loop ENDCLASS. "lcl_uml_logger IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_abap_trace_logger DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_abap_trace_logger DEFINITION INHERITING FROM lcl_abap_trace FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor. PROTECTED SECTION. METHODS set_constructor REDEFINITION. METHODS set_meth REDEFINITION. METHODS set_form REDEFINITION. METHODS set_function_module REDEFINITION. METHODS set_submit REDEFINITION. METHODS set_tran REDEFINITION. METHODS module REDEFINITION. METHODS event REDEFINITION. PRIVATE SECTION. DATA mo_log TYPE REF TO lcl_logger. METHODS log IMPORTING iv_method TYPE lcl_logger=>ts_log-method iv_params TYPE lcl_logger=>ts_log-params OPTIONAL. ENDCLASS. "lcl_abap_trace_logger DEFINITION *----------------------------------------------------------------------* * CLASS lcl_abap_trace_logger IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_abap_trace_logger IMPLEMENTATION. METHOD constructor. super->constructor( ). mo_log = lcl_logger=>new( ). ENDMETHOD. "constructor METHOD log. DATA ls_log TYPE lcl_logger=>ts_log. ls_log-method = iv_method. ls_log-params = iv_params. mo_log->info( ls_log ). ENDMETHOD. "log METHOD set_constructor. log( iv_method = 'SET_CONSTRUCTOR' iv_params = |{ mr_src->progindex } { mr_src->textindex }| ). super->set_constructor( ). ENDMETHOD. "set_constructor METHOD set_meth. log( iv_method = 'SET_METH' iv_params = |{ mr_src->progindex } { mr_src->textindex } { mr_src->methindex }| ). super->set_meth( ). ENDMETHOD. "set_meth METHOD set_form. log( iv_method = 'SET_FORM' iv_params = |{ mr_src->progindex } { mr_src->textindex }| ). super->set_form( ). ENDMETHOD. "set_form METHOD set_function_module. log( iv_method = 'SET_FUNCTION_MODULE' iv_params = |{ mr_src->progindex } { mr_src->textindex }| ). super->set_function_module( ). ENDMETHOD. "set_function_module METHOD set_submit. log( iv_method = 'SET_SUBMIT' iv_params = |{ mr_src->progindex } { mr_src->textindex }| ). super->set_submit( ). ENDMETHOD. "set_submit METHOD set_tran. log( iv_method = 'SET_TRAN' iv_params = |{ mr_src->progindex } { mr_src->textindex }| ). super->set_tran( ). ENDMETHOD. "set_tran METHOD module. log( iv_method = 'MODULE' iv_params = |{ iv_index }| ). super->module( iv_index ). ENDMETHOD. "module METHOD event. log( iv_method = 'EVENT' iv_params = |{ mr_src->textindex }| ). super->event( ). ENDMETHOD. "event ENDCLASS. "lcl_abap_trace_logger IMPLEMENTATION CONSTANTS: c_my_program TYPE program VALUE 'Y_TEST_PROG'. *----------------------------------------------------------------------* * CLASS ltc_bag DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_bag DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. CONSTANTS c_index TYPE i VALUE 42. TYPES tt_index TYPE STANDARD TABLE OF sytabix. DATA mo_bag TYPE REF TO lcl_bag. METHODS fixture IMPORTING it_index TYPE tt_index OPTIONAL. METHODS teardown. METHODS remove_value FOR TESTING. METHODS remove_empty FOR TESTING. METHODS contains_empty FOR TESTING. METHODS contains_value FOR TESTING. ENDCLASS. "ltc_bag DEFINITION *----------------------------------------------------------------------* * CLASS ltc_bag IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_bag IMPLEMENTATION. METHOD fixture. DATA lv_idx TYPE sytabix. CREATE OBJECT mo_bag. LOOP AT it_index INTO lv_idx. mo_bag->add( lv_idx ). ENDLOOP. ENDMETHOD. "fixture METHOD teardown. FREE mo_bag. ENDMETHOD. "teardown METHOD contains_empty. fixture( ). cl_abap_unit_assert=>assert_false( act = mo_bag->contains( c_index ) msg = 'LCL_BAG CONTAINS( ) No Entries Error' ). ENDMETHOD. "contains_empty METHOD contains_value. DATA lt_index TYPE tt_index. APPEND c_index TO lt_index. fixture( lt_index ). cl_abap_unit_assert=>assert_true( act = mo_bag->contains( c_index ) msg = 'LCL_BAG CONTAINS( ) One Entry Error' ). ENDMETHOD. "contains_value METHOD remove_empty. fixture( ). mo_bag->remove( c_index ). cl_abap_unit_assert=>assert_false( act = mo_bag->contains( c_index ) msg = 'LCL_BAG REMOVE( ) Empty Error' ). ENDMETHOD. "remove_empty METHOD remove_value. DATA lt_index TYPE tt_index. APPEND c_index TO lt_index. fixture( lt_index ). mo_bag->remove( c_index ). cl_abap_unit_assert=>assert_false( act = mo_bag->contains( c_index ) msg = 'LCL_BAG REMOVE( ) Error' ). ENDMETHOD. "remove_value ENDCLASS. "ltc_bag IMPLEMENTATION *----------------------------------------------------------------------* * CLASS lcl_satr_state DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_satr_state DEFINITION FRIENDS lif_unit_test. PUBLIC SECTION. METHODS constructor IMPORTING io_atra TYPE REF TO cl_atra_tool_se30_main. METHODS save. METHODS fill IMPORTING iv_textindex TYPE sytabix OPTIONAL iv_methindex TYPE sytabix OPTIONAL iv_progindex TYPE sytabix OPTIONAL. METHODS restore. PRIVATE SECTION. TYPES: BEGIN OF ts_satr_data, it_austab_hier TYPE satr_t_austab_gesamt, it_traceprog TYPE satr_t_traceprog, it_tracetext TYPE satr_t_tracetext, it_tracemeth TYPE satr_t_tracemeth, END OF ts_satr_data. DATA mo_atra TYPE REF TO cl_atra_tool_se30_main. DATA ms_satr TYPE ts_satr_data. ENDCLASS. "lcl_satr_state DEFINITION *----------------------------------------------------------------------* * CLASS lcl_satr_state IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS lcl_satr_state IMPLEMENTATION. METHOD constructor. mo_atra = io_atra. save( ). ENDMETHOD. "constructor METHOD fill. DEFINE macro_fill. do &1 times. append initial line to mo_atra->&2. enddo. END-OF-DEFINITION. macro_fill: iv_textindex it_tracetext, iv_progindex it_traceprog, iv_methindex it_tracemeth. ENDMETHOD. "fill METHOD save. ms_satr-it_austab_hier = mo_atra->it_austab_hier[]. ms_satr-it_traceprog = mo_atra->it_traceprog[]. ms_satr-it_tracetext = mo_atra->it_tracetext[]. ms_satr-it_tracemeth = mo_atra->it_tracemeth[]. ENDMETHOD. "save METHOD restore. mo_atra->it_austab_hier[] = ms_satr-it_austab_hier. mo_atra->it_traceprog[] = ms_satr-it_traceprog. mo_atra->it_tracetext[] = ms_satr-it_tracetext. mo_atra->it_tracemeth[] = ms_satr-it_tracemeth. ENDMETHOD. "restore ENDCLASS. "lcl_satr_state IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_abap_trace DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_abap_trace DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. CONSTANTS: c_textindex TYPE sytabix VALUE 1, c_progindex TYPE sytabix VALUE 2, " > 1 c_methindex TYPE sytabix VALUE 3, c_fm_index TYPE sytabix VALUE 4. DATA mo_trace TYPE REF TO lcl_abap_trace_logger. DATA mo_log TYPE REF TO lcl_logger. DATA mo_state TYPE REF TO lcl_satr_state. METHODS setup. METHODS teardown. METHODS parse IMPORTING iv_id TYPE satr_austab_gesamt-id iv_subid TYPE satr_austab_gesamt-subid OPTIONAL iv_methindex TYPE sytabix OPTIONAL iv_fm_index TYPE sytabix OPTIONAL. METHODS expected_after_set_form RETURNING value(rt_exp) TYPE lcl_logger=>tt_log. METHODS parse_form_in_module FOR TESTING. METHODS parse_form_at_load FOR TESTING. METHODS parse_form FOR TESTING. METHODS parse_form_in_call_screen FOR TESTING. METHODS parse_form_in_call_dialog FOR TESTING. METHODS parse_function_module FOR TESTING. METHODS parse_method FOR TESTING. METHODS parse_constructor FOR TESTING. METHODS set_constructor FOR TESTING. METHODS parse_dynpro FOR TESTING. METHODS parse_module FOR TESTING. METHODS parse_submit FOR TESTING. METHODS parse_transaction FOR TESTING. ENDCLASS. "ltc_abap_trace DEFINITION *----------------------------------------------------------------------* * CLASS ltc_abap_trace IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_abap_trace IMPLEMENTATION. METHOD setup. CREATE OBJECT mo_trace TYPE lcl_abap_trace_logger. mo_log = mo_trace->mo_log. CREATE OBJECT mo_state EXPORTING io_atra = mo_trace->mo_atra. mo_state->fill( iv_textindex = c_textindex iv_progindex = nmax( val1 = c_progindex val2 = c_fm_index ) iv_methindex = c_methindex ). ENDMETHOD. "setup METHOD teardown. mo_state->restore( ). FREE mo_state. FREE mo_trace. ENDMETHOD. "teardown METHOD parse. DATA lr_gesamt TYPE REF TO satr_austab_gesamt. FIELD-SYMBOLS <ls_prog> TYPE satr_prog. CREATE DATA lr_gesamt. lr_gesamt->id = iv_id. lr_gesamt->subid = iv_subid. lr_gesamt->textindex = c_textindex. lr_gesamt->progindex = c_progindex. lr_gesamt->methindex = iv_methindex. lr_gesamt->pr_2index = iv_fm_index. mo_trace->mr_src = lr_gesamt. READ TABLE mo_trace->mo_atra->it_traceprog INDEX c_progindex ASSIGNING <ls_prog>. <ls_prog> = 'MY_PROG'. CLEAR mo_trace->ms_target. " Called actor mo_trace->parse( ). ENDMETHOD. "parse METHOD expected_after_set_form. DATA ls_log TYPE lcl_logger=>ts_log. ls_log-method = 'SET_FORM'. ls_log-params = |{ c_progindex } { c_textindex }|. APPEND ls_log TO rt_exp. ls_log-method = 'EVENT'. ls_log-params = |{ c_textindex }|. APPEND ls_log TO rt_exp. ls_log-method = 'MODULE'. ls_log-params = |{ c_progindex }|. APPEND ls_log TO rt_exp. ENDMETHOD. "expected_after_set_form METHOD parse_form_in_module. parse( iv_id = c_id_module ). mo_log->verify( it_exp = expected_after_set_form( ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FORM in Module Error' ). ENDMETHOD. "parse_form_in_module METHOD parse_form_at_load. parse( iv_id = c_id_load iv_subid = 'D' ). " Dynpro mo_log->verify( it_exp = expected_after_set_form( ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FORM at LOAD Error' ). ENDMETHOD. "parse_form_at_load METHOD parse_form_in_call_dialog. parse( iv_id = c_id_report " CALL - e.g. Call SCREEN/CALL Report iv_subid = 'D' ). " CALL Dialog OR Call Screen mo_log->verify( it_exp = expected_after_set_form( ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FORM in CALL Dialog' ). ENDMETHOD. "parse_form_in_call_dialog METHOD parse_form_in_call_screen. parse( iv_id = c_id_report " CALL - e.g. Call SCREEN/CALL Report iv_subid = 'S' ). " CALL Dialog OR Call Screen ). " Called actor mo_log->verify( it_exp = expected_after_set_form( ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FORM in CALL Screen' ). ENDMETHOD. "parse_form_in_call_screen METHOD parse_form. parse( iv_id = c_id_form ). mo_log->verify( it_exp = expected_after_set_form( ) iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FORM' ). ENDMETHOD. "parse_form METHOD parse_constructor. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. ls_log-method = 'SET_CONSTRUCTOR'. ls_log-params = |{ c_progindex } { c_textindex }|. APPEND ls_log TO lt_log. parse( iv_id = c_id_class ). mo_log->verify( it_exp = lt_log iv_msg = 'LCL_ABAP_TRACE->PARSE( ) SET CONSTRUCTOR Call Error' ). ENDMETHOD. "parse_constructor METHOD set_constructor. DATA ls_event TYPE lcl_abap_trace=>ts_event. ls_event-type = c_type_class. ls_event-event = lcl_abap_trace=>c_create_method. ls_event-global = space. parse( iv_id = c_id_class ). cl_abap_unit_assert=>assert_equals( act = mo_trace->ms_target exp = ls_event msg = 'LCL_ABAP_TRACE=>SET_CONSTRUCTOR( ) Error' ). ENDMETHOD. "set_constructor METHOD parse_method. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. ls_log-method = 'SET_METH'. ls_log-params = |{ c_progindex } { c_textindex } { c_methindex }|. APPEND ls_log TO lt_log. ls_log-method = 'MODULE'. ls_log-params = |{ c_progindex }|. APPEND ls_log TO lt_log. ls_log-method = 'MODULE'. ls_log-params = |{ 0 }|. APPEND ls_log TO lt_log. parse( iv_id = c_id_method iv_methindex = c_methindex ). mo_log->verify( it_exp = lt_log iv_msg = 'LCL_ABAP_TRACE->PARSE( ) METHOD' ). ENDMETHOD. "parse_method METHOD parse_function_module. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. ls_log-method = 'SET_FUNCTION_MODULE'. ls_log-params = |{ c_progindex } { c_textindex }|. APPEND ls_log TO lt_log. ls_log-method = 'EVENT'. ls_log-params = |{ c_textindex }|. APPEND ls_log TO lt_log. ls_log-method = 'MODULE'. ls_log-params = |{ c_fm_index }|. APPEND ls_log TO lt_log. parse( iv_id = c_id_func iv_fm_index = c_fm_index ). mo_log->verify( it_exp = lt_log iv_msg = 'LCL_ABAP_TRACE->PARSE( ) FUNCTION_MODULE' ). ENDMETHOD. "parse_function_module METHOD parse_module. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. ls_log-method = 'MODULE'. ls_log-params = |{ c_progindex }|. APPEND ls_log TO lt_log. parse( iv_id = c_id_report iv_subid = 'P' ). mo_log->verify( it_exp = lt_log iv_msg = 'LCL_ABAP_TRACE->PARSE( ) MODULE' ). ENDMETHOD. "parse_module METHOD parse_dynpro. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. ls_log-method = 'MODULE'. ls_log-params = |{ c_progindex }|. APPEND ls_log TO lt_log. parse( iv_id = c_id_dynpro iv_subid = 'P' ). mo_log->verify( it_exp = lt_log iv_msg = 'LCL_ABAP_TRACE->PARSE( ) MODULE Dynpro' ). ENDMETHOD. "parse_dynpro METHOD parse_submit. CONSTANTS: c_mod_index TYPE sytabix VALUE 1, " larger 0, smaller than c_progindex c_my_event TYPE program VALUE 'MY_EVENT'. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. FIELD-SYMBOLS <ls_prog> TYPE satr_prog. FIELD-SYMBOLS <ls_text> TYPE satr_tracetext. ls_log-method = 'SET_SUBMIT'. ls_log-params = |{ c_progindex } { c_textindex }|. APPEND ls_log TO lt_log. ls_log-method = 'EVENT'. ls_log-params = |{ c_textindex }|. APPEND ls_log TO lt_log. ls_log-method = 'MODULE'. ls_log-params = |{ c_mod_index }|. APPEND ls_log TO lt_log. READ TABLE mo_trace->mo_atra->it_tracetext INDEX c_textindex ASSIGNING <ls_text>. <ls_text>-tracetext = c_my_event. READ TABLE mo_trace->mo_atra->it_traceprog INDEX c_mod_index ASSIGNING <ls_prog>. <ls_prog> = c_my_event. parse( iv_id = c_id_report iv_subid = 'R' ). mo_log->verify( it_exp = lt_log iv_msg = 'LCL_ABAP_TRACE->PARSE( ) SET_SUBMIT Error' ). ENDMETHOD. "parse_submit METHOD parse_transaction. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. ls_log-method = 'SET_TRAN'. ls_log-params = |{ c_progindex } { c_textindex }|. APPEND ls_log TO lt_log. ls_log-method = 'EVENT'. ls_log-params = |{ c_textindex }|. APPEND ls_log TO lt_log. parse( iv_id = c_id_report iv_subid = 'T' ). mo_log->verify( it_exp = lt_log iv_msg = 'LCL_ABAP_TRACE->PARSE( ) SET_TRAN Error' ). ENDMETHOD. "parse_transaction ENDCLASS. "ltc_abap_trace IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_class DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_class DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mo_class_name TYPE REF TO lcl_class_name. METHODS setup. METHODS teardown. METHODS section_name FOR TESTING. METHODS technical_name FOR TESTING. METHODS external_name FOR TESTING. METHODS is_global FOR TESTING. METHODS is_not_global FOR TESTING. ENDCLASS. "ltc_class DEFINITION *----------------------------------------------------------------------* * CLASS ltc_sequence DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_sequence DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. TYPES tt_sat TYPE STANDARD TABLE OF ts_sat WITH KEY aus_tabix WITH NON-UNIQUE SORTED KEY k1 COMPONENTS aus_tabix. " Performance ? CLASS-METHODS new_sequence IMPORTING is_cfg TYPE ts_diagram_config it_sat TYPE tt_sat RETURNING value(ro_sequence) TYPE REF TO lcl_sequence. CLASS-METHODS new_collection IMPORTING is_cfg TYPE ts_diagram_config it_sat TYPE tt_sat RETURNING value(ro_sequence) TYPE REF TO lcl_sequence. CLASS-METHODS sat_fixture_events RETURNING value(rt_sat) TYPE tt_sat. PRIVATE SECTION. DATA mo_sequence TYPE REF TO lcl_sequence. METHODS setup. METHODS teardown. METHODS collect FOR TESTING. METHODS add FOR TESTING. ENDCLASS. "ltc_sequence DEFINITION *----------------------------------------------------------------------* * CLASS ltc_sequence IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_sequence IMPLEMENTATION. METHOD new_sequence. FIELD-SYMBOLS <ls_sat> TYPE ts_sat. CREATE OBJECT ro_sequence EXPORTING is_cfg = is_cfg. * insert into collection LOOP AT it_sat ASSIGNING <ls_sat>. ro_sequence->lif_collector~collect( <ls_sat> ). ENDLOOP. ENDMETHOD. "new_sequence METHOD new_collection. FIELD-SYMBOLS <ls_sat> TYPE ts_sat. CREATE OBJECT ro_sequence EXPORTING is_cfg = is_cfg. * add to collection LOOP AT it_sat ASSIGNING <ls_sat>. ro_sequence->add( <ls_sat> ). ENDLOOP. ENDMETHOD. "new_collection METHOD sat_fixture_events. CONSTANTS: c_prog TYPE program VALUE 'Y_TEST_PROG', c_ctrl TYPE program VALUE 'LCL_CTRL', c_model TYPE program VALUE 'LCL_MODEL'. DATA ls_sat TYPE ts_sat. CLEAR ls_sat. ls_sat-id = 'm'. ls_sat-from_level = 1. ls_sat-caller-global = c_prog. ls_sat-caller-type = c_type_prog. ls_sat-called-global = c_prog. ls_sat-called-type = c_type_class. ls_sat-called-local = c_ctrl. ls_sat-called-mod = 'MAIN'. APPEND ls_sat TO rt_sat. CLEAR ls_sat. ls_sat-id = 'm'. ls_sat-from_level = 2. ls_sat-caller-global = c_prog. ls_sat-caller-type = c_type_class. ls_sat-caller-local = c_ctrl. ls_sat-caller-mod = 'MAIN'. ls_sat-called-global = 'SAPPROG'. ls_sat-called-type = c_type_class. ls_sat-called-instance = 6. ls_sat-called-local = c_model. ls_sat-called-mod = lcl_abap_trace=>c_create_method. APPEND ls_sat TO rt_sat. CLEAR ls_sat. ls_sat-id = 'm'. ls_sat-from_level = 2. ls_sat-caller-global = 'SAPPROG'. ls_sat-caller-type = c_type_class. ls_sat-caller-local = c_model. ls_sat-caller-mod = 'SETUP'. ls_sat-called-global = c_prog. ls_sat-called-type = c_type_class. ls_sat-called-local = c_ctrl. ls_sat-called-instance = 0. ls_sat-called-mod = 'CHANGE'. APPEND ls_sat TO rt_sat. ENDMETHOD. "sat_fixture_events METHOD setup. mo_sequence = new_sequence( it_sat = sat_fixture_events( ) is_cfg = lcl_configuration=>get( ) ). ENDMETHOD. "setup METHOD teardown. FREE mo_sequence. ENDMETHOD. "teardown METHOD collect. DATA lt_message TYPE tt_message. DATA ls_message TYPE ts_message. ls_message-id = 'm'. ls_message-from_level = 1. ls_message-caller = 1. ls_message-called = 2. ls_message-msg = 'MAIN'. APPEND ls_message TO lt_message. ls_message-id = 'K'. "<--- skipped ls_message-from_level = 1. ls_message-caller = 2. ls_message-called = 3. ls_message-msg = 'SETUP'. APPEND ls_message TO lt_message. ls_message-id = 'm'. ls_message-from_level = 2. ls_message-caller = 3. ls_message-called = 2. ls_message-msg = 'CHANGE'. APPEND ls_message TO lt_message. cl_abap_unit_assert=>assert_equals( act = mo_sequence->mt_trace exp = lt_message msg = 'LCL_SEQUENCE->COLLECT( ) Error' ). ENDMETHOD. "collect METHOD add. DATA lt_message TYPE tt_message. DATA ls_message TYPE ts_message. ls_message-id = 'm'. ls_message-from_level = 1. ls_message-caller = 1. ls_message-called = 2. ls_message-msg = 'MAIN'. APPEND ls_message TO lt_message. ls_message-id = 'm'. ls_message-from_level = 2. ls_message-caller = 2. ls_message-called = 3. ls_message-msg = lcl_abap_trace=>c_create_method. APPEND ls_message TO lt_message. ls_message-id = 'm'. ls_message-from_level = 2. ls_message-caller = 4. ls_message-called = 2. ls_message-msg = 'CHANGE'. APPEND ls_message TO lt_message. mo_sequence = new_collection( it_sat = sat_fixture_events( ) is_cfg = lcl_configuration=>get( ) ). cl_abap_unit_assert=>assert_equals( act = mo_sequence->mt_trace exp = lt_message msg = 'LCL_SEQUENCE->ADD( ) Error' ). ENDMETHOD. "add ENDCLASS. "ltc_sequence IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_class IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_class IMPLEMENTATION. METHOD setup. CREATE OBJECT mo_class_name. ENDMETHOD. "setup METHOD teardown. FREE mo_class_name. ENDMETHOD. "teardown METHOD section_name. cl_abap_unit_assert=>assert_equals( act = mo_class_name->to_internal( '/IDV0/JN_CL_ASM_ACT' ) exp = '/IDV0/JN_CL_ASM_ACT===========' msg = 'LCL_CLASS SECTION_NAME( ) Error' ). ENDMETHOD. "section_name METHOD technical_name. cl_abap_unit_assert=>assert_equals( act = mo_class_name->technical( 'CL_GUI_ALV_GRID' ) exp = 'CL_GUI_ALV_GRID===============CP' msg = 'LCL_CLASS TECHNICAL_NAME( ) Error' ). ENDMETHOD. "technical_name METHOD external_name. DATA lv_class TYPE program VALUE '/IDV0/JN_CL_ASM_ACT===========CP'. mo_class_name->external( CHANGING cv_clas = lv_class ). cl_abap_unit_assert=>assert_equals( act = lv_class exp = '/IDV0/JN_CL_ASM_ACT' msg = 'LCL_CLASS EXTERNAL_NAME( ) Error' ). ENDMETHOD. "external_name METHOD is_global. cl_abap_unit_assert=>assert_true( act = mo_class_name->is_global( 'CL_ABAP_MEMORY_X_WRITER' ) msg = 'LCL_ABAP_TRACE=>IS_GLOBAL_CLASS( ) Error' ). ENDMETHOD. "is_global METHOD is_not_global. cl_abap_unit_assert=>assert_false( act = mo_class_name->is_global( 'LCL_BAG' ) msg = 'LCL_ABAP_TRACE=>IS_GLOBAL_CLASS( ) Local Class Error' ). ENDMETHOD. "is_not_global ENDCLASS. "ltc_class IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_actors DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_actors DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. CONSTANTS: c_prog TYPE program VALUE 'Y_TEST_PROG', c_ctrl TYPE program VALUE 'LCL_CTRL', c_model TYPE program VALUE 'LCL_MODEL'. INTERFACES lif_unit_test. CLASS-METHODS sat_fixture_record RETURNING value(rs_sat) TYPE ts_sat. CLASS-METHODS sat_fixture_plant_uml RETURNING value(rt_sat) TYPE ltc_sequence=>tt_sat. CLASS-METHODS sat_fixture_messages RETURNING value(rt_sat) TYPE ltc_sequence=>tt_sat. PRIVATE SECTION. CONSTANTS c_my_class TYPE program VALUE 'YCL_TEST_CLASS'. DATA mi_actors TYPE REF TO lif_actors. DATA ms_exp TYPE ts_sat. METHODS constructor. METHODS setup. METHODS teardown. METHODS get_count RETURNING value(rv_count) TYPE i. METHODS get_text_class IMPORTING iv_id TYPE satr_de_id iv_exp TYPE string iv_msg TYPE string. METHODS put FOR TESTING. METHODS get_text_global_class FOR TESTING. METHODS get_text_local_class FOR TESTING. ENDCLASS. "ltc_actors DEFINITION *----------------------------------------------------------------------* * CLASS ltc_actors IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_actors IMPLEMENTATION. METHOD constructor. ms_exp-id = 1. ms_exp-from_level = 1. ms_exp-caller-global = c_my_program. ms_exp-caller-type = c_type_fugr. ms_exp-called-global = c_my_program. ms_exp-called-local = c_model. ms_exp-called-type = c_type_class. ms_exp-called-instance = 1. ms_exp-called-mod = lcl_abap_trace=>c_create_method. ENDMETHOD. "constructor METHOD sat_fixture_messages. DATA ls_main TYPE ts_call. DATA ls_sat TYPE ts_sat. ls_main-global = c_prog. ls_main-type = c_type_class. ls_main-local = c_ctrl. ls_main-mod = 'MAIN'. CLEAR ls_sat. ls_sat-id = 'm'. ls_sat-from_level = 1. ls_sat-caller-global = c_prog. ls_sat-caller-type = c_type_prog. ls_sat-called = ls_main. APPEND ls_sat TO rt_sat. CLEAR ls_sat. ls_sat-id = 'm'. ls_sat-from_level = 2. ls_sat-caller = ls_main. ls_sat-called-global = c_prog. ls_sat-called-type = c_type_class. ls_sat-called-instance = 6. ls_sat-called-local = c_model. ls_sat-called-mod = lcl_abap_trace=>c_create_method. APPEND ls_sat TO rt_sat. CLEAR ls_sat. ls_sat-id = 'm'. ls_sat-from_level = 2. ls_sat-caller = ls_main. ls_sat-called-global = c_prog. ls_sat-called-type = c_type_class. ls_sat-called-local = c_ctrl. ls_sat-called-mod = 'CHANGE'. APPEND ls_sat TO rt_sat. CLEAR ls_sat. ls_sat-id = 'm'. ls_sat-from_level = 3. ls_sat-caller-global = c_prog. ls_sat-caller-type = c_type_class. ls_sat-caller-local = c_ctrl. ls_sat-caller-mod = 'CHANGE'. ls_sat-called-global = c_prog. ls_sat-called-type = c_type_class. ls_sat-called-instance = 6. ls_sat-called-local = c_model. ls_sat-called-mod = 'UP'. APPEND ls_sat TO rt_sat. ENDMETHOD. "sat_fixture_messages METHOD sat_fixture_plant_uml. DATA ls_sat TYPE ts_sat. CLEAR ls_sat. ls_sat-id = 1. "<------ ? ls_sat-from_level = 1. ls_sat-caller-global = c_my_program. ls_sat-caller-type = c_type_prog. ls_sat-called-global = c_my_program. ls_sat-called-type = c_type_class. ls_sat-called-instance = 0. ls_sat-called-local = 'LCL_CTRL'. ls_sat-called-mod = 'MAIN'. APPEND ls_sat TO rt_sat. CLEAR ls_sat. ls_sat-id = 'm'. ls_sat-from_level = 2. ls_sat-caller-global = c_my_program. ls_sat-caller-type = c_type_class. ls_sat-caller-instance = 0. ls_sat-caller-local = 'LCL_CTRL'. ls_sat-caller-mod = 'MAIN'. ls_sat-called-global = c_my_program. ls_sat-called-type = c_type_class. ls_sat-called-instance = 1. ls_sat-called-local = 'LCL_MODEL'. ls_sat-called-mod = lcl_abap_trace=>c_create_method. APPEND ls_sat TO rt_sat. CLEAR ls_sat. ls_sat-id = space. ls_sat-from_level = 3. ls_sat-caller-global = c_my_program. ls_sat-caller-type = c_type_class. ls_sat-caller-instance = 447. ls_sat-caller-local = 'LCL_CALL_STACK'. ls_sat-caller-mod = 'PLANTUML'. ls_sat-called-global = c_my_program. ls_sat-called-type = c_type_class. ls_sat-called-instance = 448. ls_sat-called-local = 'LCL_ITERATOR'. ls_sat-called-mod = 'HAS_NEXT'. APPEND ls_sat TO rt_sat. ENDMETHOD. "sat_fixture_plant_uml METHOD sat_fixture_record. CLEAR rs_sat. rs_sat-id = 'm'. rs_sat-from_level = 1. rs_sat-aus_tabix = 1. rs_sat-caller-global = c_prog. rs_sat-caller-type = c_type_prog. rs_sat-called-global = c_prog. rs_sat-called-type = c_type_class. rs_sat-called-local = 'LCL_CTRL'. rs_sat-called-mod = 'MAIN'. ENDMETHOD. "sat_fixture_record METHOD setup. CREATE OBJECT mi_actors TYPE lcl_actors. ENDMETHOD. "setup METHOD teardown. FREE mi_actors. ENDMETHOD. "teardown METHOD get_count. DATA lo_actors TYPE REF TO lcl_actors. lo_actors ?= mi_actors. rv_count = lines( lo_actors->mt_actor ). ENDMETHOD. "get_count METHOD put. DATA lv_count TYPE i. mi_actors->new_path( ms_exp ). lv_count = get_count( ). mi_actors->new_path( ms_exp ). " <-- Duplicate cl_abap_unit_assert=>assert_equals( act = get_count( ) exp = lv_count msg = 'LCL_ACTORS->PUT( ) Duplicate Error' ). ENDMETHOD. "put METHOD get_text_class. DATA ls_msg TYPE ts_message. CLEAR ms_exp. ms_exp-id = 1. ms_exp-from_level = 1. ms_exp-caller-global = c_my_program. ms_exp-caller-type = c_type_fugr. ms_exp-called-global = c_my_class. ms_exp-called-type = c_type_class. ms_exp-called-instance = 1. ms_exp-called-mod = lcl_abap_trace=>c_create_method. mi_actors->new_path( ms_exp ). ls_msg-id = iv_id. ls_msg-from_level = 1. ls_msg-caller = 1. ls_msg-called = 2. ls_msg-msg = lcl_abap_trace=>c_create_method. cl_abap_unit_assert=>assert_equals( act = mi_actors->short_text( ls_msg ) exp = iv_exp msg = |LCL_ACTORS->GET_TEXT( ) { iv_msg }| ). ENDMETHOD. "get_text_class METHOD get_text_global_class. get_text_class( iv_id = c_id_class iv_exp = |Create instance of class { c_my_class }| iv_msg = 'Global Class Get Text Error' ). ENDMETHOD. "get_text_global_class METHOD get_text_local_class. get_text_class( iv_id = c_id_method iv_exp = |Call method CONSTRUCTOR| iv_msg = 'Local Class Get Text Error' ). ENDMETHOD. "get_text_local_class ENDCLASS. "ltc_actors IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_stack DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_stack DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA ms_exp TYPE lcl_stack=>ts_call_level. DATA mo_stack TYPE REF TO lcl_stack. " CUT METHODS setup. METHODS teardown. METHODS is_empty FOR TESTING. METHODS is_empty_set FOR TESTING. METHODS is_empty_reset FOR TESTING. METHODS push FOR TESTING. METHODS pop FOR TESTING. METHODS pop_empty FOR TESTING. ENDCLASS. "ltc_stack DEFINITION *----------------------------------------------------------------------* * CLASS ltc_stack IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_stack IMPLEMENTATION. METHOD setup. CREATE OBJECT mo_stack. ms_exp-actor_key = 1. ms_exp-from_level = 1. ENDMETHOD. "setup METHOD teardown. FREE mo_stack. ENDMETHOD. "teardown METHOD is_empty. cl_abap_unit_assert=>assert_true( act = mo_stack->mv_empty msg = 'LCL_STACK is not Empty' ). ENDMETHOD. "is_empty METHOD pop. mo_stack->push( ms_exp ). cl_abap_unit_assert=>assert_equals( act = mo_stack->pop( ) exp = ms_exp msg = 'LCL_STACK POP( ) Error' ). ENDMETHOD. "pop METHOD push. DATA ls_act TYPE lcl_stack=>ts_call_level. mo_stack->push( ms_exp ). ls_act = mo_stack->pop( ). cl_abap_unit_assert=>assert_true( act = boolc( ( ms_exp EQ ls_act ) AND mo_stack->mv_empty EQ abap_true ) msg = 'LCL_STACK PUSH( ) Error' ). ENDMETHOD. "push METHOD pop_empty. cl_abap_unit_assert=>assert_initial( act = mo_stack->pop( ) msg = 'LCL_STACK POP( ) Empty' ). ENDMETHOD. "pop_empty METHOD is_empty_reset. mo_stack->push( ms_exp ). cl_abap_unit_assert=>assert_false( act = mo_stack->mv_empty msg = 'LCL_STACK Empty not reset/PUSH( )' ). ENDMETHOD. "is_empty_reset METHOD is_empty_set. mo_stack->push( ms_exp ). mo_stack->pop( ). cl_abap_unit_assert=>assert_true( act = mo_stack->mv_empty msg = 'LCL_STACK Empty not set/POP( )' ). ENDMETHOD. "is_empty_set ENDCLASS. "ltc_stack IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_call DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_call DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PROTECTED SECTION. DATA mo_uml TYPE REF TO lcl_uml_logger. DATA mo_actors TYPE REF TO lcl_actors. DATA ms_exp TYPE lcl_stack=>ts_call_level. METHODS create_uml. METHODS free_uml. PRIVATE SECTION. CONSTANTS c_level TYPE i VALUE 1. DATA mo_stack TYPE REF TO lcl_uml_stack. " CUT METHODS setup. METHODS teardown. METHODS fixture_message RETURNING value(rs_message) TYPE ts_message. METHODS call FOR TESTING. METHODS return FOR TESTING. ENDCLASS. "ltc_call DEFINITION *----------------------------------------------------------------------* * CLASS ltc_call IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_call IMPLEMENTATION. METHOD create_uml. CREATE OBJECT mo_actors. mo_uml ?= lcl_uml=>new( iv_uml = lcl_uml=>c_uml_mock ii_actors = mo_actors ). ENDMETHOD. "create_uml METHOD setup. create_uml( ). CREATE OBJECT mo_stack EXPORTING io_uml = mo_uml. ms_exp-actor_key = 1. ms_exp-from_level = 1. ENDMETHOD. "setup METHOD free_uml. FREE mo_uml. FREE mo_actors. ENDMETHOD. "free_uml METHOD teardown. FREE mo_stack. free_uml( ). ENDMETHOD. "teardown METHOD call. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. CLEAR ls_log. ls_log-method = 'CALL'. ls_log-params = 'm 1 2 Call method Test Call'. APPEND ls_log TO lt_log. mo_stack->call( fixture_message( ) ). mo_uml->mo_log->verify( it_exp = lt_log iv_msg = 'LCL_UML_STACK MESSAGE Error' ). ENDMETHOD. "call METHOD fixture_message. CLEAR rs_message. rs_message-id = 'm'. rs_message-from_level = c_level. rs_message-caller = 1. rs_message-called = 2. rs_message-msg = 'Test Call'. ENDMETHOD. "fixture_message METHOD return. DATA ls_message TYPE ts_message. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. CLEAR ls_log. ls_log-method = 'CALL'. ls_log-params = 'm 1 2 Call method Test Call'. APPEND ls_log TO lt_log. CLEAR ls_log. ls_log-method = 'CALL'. ls_log-params = 'm 2 3 Call method Test Call'. APPEND ls_log TO lt_log. CLEAR ls_log. ls_log-method = 'RETURN'. ls_log-params = '3 2'. APPEND ls_log TO lt_log. ls_message = fixture_message( ). mo_stack->call( ls_message ). ls_message-caller = 2. ls_message-called = 3. mo_stack->call( ls_message ). mo_stack->return_to( c_level ). mo_uml->mo_log->verify( it_exp = lt_log iv_msg = 'LCL_UML_STACK RETURN Error' ). ENDMETHOD. "return ENDCLASS. "ltc_call IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_call_stack DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_call_stack DEFINITION FOR TESTING INHERITING FROM ltc_call RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. CONSTANTS c_level TYPE i VALUE 42. DATA mo_messages TYPE REF TO lcl_messages. DATA mo_call TYPE REF TO lcl_call_stack. METHODS setup. METHODS teardown. METHODS first_call FOR TESTING. ENDCLASS. "ltc_call_stack DEFINITION *----------------------------------------------------------------------* * CLASS ltc_call_stack IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_call_stack IMPLEMENTATION. METHOD setup. DATA lt_message TYPE tt_message. DATA ls_message TYPE ts_message. ls_message-id = 'm'. ls_message-from_level = c_level. ls_message-caller = 1. ls_message-called = 2. ls_message-msg = 'TEST'. APPEND ls_message TO lt_message. create_uml( ). CREATE OBJECT mo_messages EXPORTING it_messages = lt_message. CREATE OBJECT mo_call EXPORTING io_uml = mo_uml io_messages = mo_messages. ms_exp-actor_key = 1. ms_exp-from_level = 1. ENDMETHOD. "setup METHOD teardown. FREE mo_messages. FREE mo_call. free_uml( ). ENDMETHOD. "teardown METHOD first_call. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. CLEAR ls_log. ls_log-method = 'CALL'. ls_log-params = | { c_level } { lcl_call_stack=>c_first_key } |. APPEND ls_log TO lt_log. CLEAR ls_log. ls_log-method = 'CALL'. ls_log-params = 'm 1 2 Call method TEST'. APPEND ls_log TO lt_log. CLEAR ls_log. ls_log-method = 'RETURN'. ls_log-params = '2 1'. APPEND ls_log TO lt_log. mo_call->message( is_message = mo_messages->next( ) iv_idx = 1 ). mo_uml->mo_log->verify( it_exp = lt_log iv_msg = 'LCL_CALL_STACK MESSAGE FIRST CALL Error' ). ENDMETHOD. "first_call ENDCLASS. "ltc_call_stack IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_iterator DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_iterator DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mo_iter TYPE REF TO lcl_messages. " CUT DATA mt_messages TYPE tt_message. METHODS constructor. METHODS setup. METHODS teardown. METHODS has_next_after_init FOR TESTING. METHODS has_next_after_end FOR TESTING. METHODS skip_once FOR TESTING. METHODS skip_3_times FOR TESTING. METHODS next FOR TESTING. METHODS next_error FOR TESTING. METHODS clone_rest FOR TESTING. METHODS clone_resize FOR TESTING. METHODS: has_previous_after_init FOR TESTING, has_previous_at_first FOR TESTING, has_previous_after_first FOR TESTING. ENDCLASS. "ltc_iterator DEFINITION *----------------------------------------------------------------------* * CLASS ltc_iterator IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_iterator IMPLEMENTATION. METHOD constructor. CONSTANTS c_main TYPE tv_key VALUE 2. DATA ls_message TYPE ts_message. CLEAR ls_message. "Prog. MAIN ls_message-id = 1. ls_message-from_level = 1. ls_message-caller = 1. ls_message-called = c_main. ls_message-msg = 'MAIN'. APPEND ls_message TO mt_messages. CLEAR ls_message. ls_message-id = 2. ls_message-from_level = 2. ls_message-caller = c_main. ls_message-called = 2. ls_message-msg = lcl_abap_trace=>c_create_method. " Main -> Model=>Constructor. APPEND ls_message TO mt_messages. CLEAR ls_message. " Main -> Model->Query ls_message-id = 3. ls_message-from_level = 1. ls_message-caller = c_main. ls_message-called = 3. ls_message-msg = 'QUERY'. APPEND ls_message TO mt_messages. CLEAR ls_message. ls_message-id = 4. ls_message-from_level = 2. ls_message-caller = 3. ls_message-called = 4. ls_message-msg = 'FILTER'. " Model->Query -> Model->Filter APPEND ls_message TO mt_messages. ENDMETHOD. "constructor METHOD setup. CREATE OBJECT mo_iter EXPORTING it_messages = mt_messages. ENDMETHOD. "setup METHOD teardown. FREE mo_iter. ENDMETHOD. "teardown METHOD skip_once. mo_iter->skip( ). cl_abap_unit_assert=>assert_equals( act = mo_iter->mv_idx exp = 1 msg = 'LCL_MESSAGES SKIP( ) Error' ). ENDMETHOD. "skip_once METHOD skip_3_times. mo_iter->skip( 3 ). cl_abap_unit_assert=>assert_equals( act = mo_iter->mv_idx exp = 3 msg = 'LCL_MESSAGES SKIP( 2 ) Error' ). ENDMETHOD. "skip_3_times METHOD has_next_after_init. cl_abap_unit_assert=>assert_true( act = mo_iter->has_next( ) msg = 'LCL_MESSAGES HAS_NEXT( ) Start Error' ). ENDMETHOD. "has_next_after_init METHOD has_next_after_end. mo_iter->skip( mo_iter->mv_size ). cl_abap_unit_assert=>assert_false( act = mo_iter->has_next( ) msg = 'LCL_MESSAGES HAS_NEXT( ) End Error' ). ENDMETHOD. "has_next_after_end METHOD next. DATA ls_exp TYPE ts_message. READ TABLE mo_iter->mt_list INDEX 1 INTO ls_exp. cl_abap_unit_assert=>assert_equals( act = mo_iter->next( ) exp = ls_exp msg = 'LCL_MESSAGES_ITERATOR NEXT( ) Error' ). ENDMETHOD. "next METHOD next_error. TRY. mo_iter->skip( mo_iter->mv_size ). mo_iter->next( ). cl_abap_unit_assert=>fail( msg = 'LCL_MESSAGES NEXT( ) No Exception' ). CATCH cx_sy_itab_error ##no_handler. " Do nothing ENDTRY. ENDMETHOD. "next_error METHOD has_previous_after_init. cl_abap_unit_assert=>assert_true( act = mo_iter->is_first( ) msg = 'LCL_MESSAGES IS_FIRST( ) Error' ). ENDMETHOD. "has_previous_after_init METHOD has_previous_at_first. mo_iter->next( ). cl_abap_unit_assert=>assert_true( act = mo_iter->is_first( ) msg = 'LCL_MESSAGES IS_FIRST( ) First' ). ENDMETHOD. "has_previous_at_first METHOD has_previous_after_first. mo_iter->next( ). mo_iter->next( ). cl_abap_unit_assert=>assert_false( act = mo_iter->is_first( ) msg = 'LCL_MESSAGES IS_FIRST( ) Next' ). ENDMETHOD. "has_previous_after_first METHOD clone_rest. CONSTANTS c_start TYPE i VALUE 2. DATA lo_messages TYPE REF TO lcl_messages. CREATE OBJECT lo_messages EXPORTING it_messages = mo_iter->mt_list iv_start = c_start. cl_abap_unit_assert=>assert_equals( act = lo_messages->mv_idx exp = c_start - 1 msg = 'LCL_MESSAGESCONSTRUCTOR( ) Index Error' ). ENDMETHOD. "clone_rest METHOD clone_resize. CONSTANTS: c_start TYPE i VALUE 2, c_size TYPE i VALUE 3. DATA lo_messages TYPE REF TO lcl_messages. CREATE OBJECT lo_messages EXPORTING it_messages = mo_iter->mt_list iv_start = c_start iv_stop = c_size. cl_abap_unit_assert=>assert_equals( act = lo_messages->mv_size exp = c_size msg = 'LCL_MESSAGES CONSTRUCTOR( ) Size Error' ). ENDMETHOD. "clone_resize ENDCLASS. "ltc_iterator IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_uml DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_uml DEFINITION FOR TESTING ABSTRACT CREATE PROTECTED RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PROTECTED SECTION. DATA mi_actors TYPE REF TO lif_actors. DATA mo_uml TYPE REF TO lcl_uml. " CUT DATA mv_obj_name TYPE sobj_name. METHODS test_participant IMPORTING iv_exp TYPE string. METHODS test_message IMPORTING iv_exp TYPE string. METHODS test_return IMPORTING iv_exp TYPE string. METHODS test_footer IMPORTING iv_exp TYPE string. METHODS setup_uml IMPORTING iv_uml TYPE lcl_uml=>tv_uml iv_name TYPE sobj_name. METHODS teardown_uml. ENDCLASS. "ltc_uml DEFINITION *----------------------------------------------------------------------* * CLASS ltc_uml IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_uml IMPLEMENTATION. METHOD setup_uml. CREATE OBJECT mi_actors TYPE lcl_actors. mo_uml = lcl_uml=>new( iv_uml = iv_uml ii_actors = mi_actors ). mv_obj_name = iv_name. ENDMETHOD. "setup_uml METHOD teardown_uml. FREE mo_uml. FREE mi_actors. ENDMETHOD. "teardown_uml METHOD test_participant. DATA lv_exp TYPE string. DATA ls_lifeline TYPE ts_lifeline. ls_lifeline-label = `Test Participant`. ls_lifeline-index = 42. lv_exp = mo_uml->mv_diagram && iv_exp. mo_uml->participant( is_lifeline = ls_lifeline ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = mv_obj_name && ` PARTICIPANT( ) Error` ). ENDMETHOD. "test_participant METHOD test_message. DATA lv_exp TYPE string. DATA ls_message TYPE ts_message. ls_message-id = c_id_method. ls_message-caller = 3. ls_message-called = 5. ls_message-from_level = 2. ls_message-msg = 'MAIN'. lv_exp = mo_uml->mv_diagram && iv_exp. mo_uml->message( ls_message ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = mv_obj_name && ` MESSAGE( ) Error` ). ENDMETHOD. "test_message METHOD test_return. DATA lv_exp TYPE string. lv_exp = mo_uml->mv_diagram && iv_exp. mo_uml->return( iv_from = 2 iv_to = 1 ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = mv_obj_name && ` RETURN( ) Error` ). ENDMETHOD. "test_return METHOD test_footer. DATA lv_exp TYPE string. lv_exp = mo_uml->mv_diagram && iv_exp. mo_uml->footer( ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = mv_obj_name && ` FOOTER( ) Error` ). ENDMETHOD. "test_footer ENDCLASS. "ltc_uml IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_plant_uml DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_plant_uml DEFINITION INHERITING FROM ltc_uml FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. METHODS setup. METHODS teardown. METHODS add FOR TESTING. METHODS message FOR TESTING. METHODS call_and_skip FOR TESTING. METHODS initial_message FOR TESTING. METHODS first_message FOR TESTING. METHODS call_factory FOR TESTING. METHODS return FOR TESTING. METHODS return_from_initial FOR TESTING. METHODS return_same_level FOR TESTING. METHODS participant FOR TESTING. METHODS plantuml_header FOR TESTING. METHODS footer FOR TESTING. ENDCLASS. "ltc_plant_uml DEFINITION *----------------------------------------------------------------------* * CLASS ltc_plant_uml IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_plant_uml IMPLEMENTATION. METHOD setup. setup_uml( iv_uml = lcl_uml=>c_uml_plant iv_name = 'LCL_PLANT_UML' ). ENDMETHOD. "setup METHOD teardown. teardown_uml( ). ENDMETHOD. "teardown METHOD add. CONSTANTS c_code TYPE string VALUE 'New PlantUML Code'. DATA lv_before TYPE string. lv_before = mo_uml->mv_diagram. mo_uml->add( c_code ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_before && c_code msg = 'LCL_UML_PLANT ADD( ) Error' ). ENDMETHOD. "add METHOD message. test_message( |3 -> 5: Call method MAIN\nactivate 5\n| ). ENDMETHOD. "message METHOD call_and_skip. DATA lv_exp TYPE string. DATA ls_message TYPE ts_message. lv_exp = mo_uml->mv_diagram && |1 -> 2: <b>Skipping over SAP code until calling MODIFIER</b>\n| && |activate 2\nnote over 1,2\n{ lcl_uml_plant=>c_txt_std }\nend note\n|. ls_message-id = c_id_skip. ls_message-from_level = 1. ls_message-msg = 'MODIFIER'. ls_message-caller = 1. ls_message-called = 2. mo_uml->message( ls_message ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT MESSAGE( ) Skip' ). ENDMETHOD. "call_and_skip METHOD first_message. DATA lv_exp TYPE string. DATA ls_message TYPE ts_message. lv_exp = mo_uml->mv_diagram && |{ lcl_call_stack=>c_first_key } -> 2: \nactivate 2\n|. ls_message-caller = lcl_call_stack=>c_first_key. ls_message-called = 2. mo_uml->message( is_message = ls_message ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT MESSAGE( ) First' ). ENDMETHOD. "first_message METHOD initial_message. DATA lv_exp TYPE string. DATA ls_message TYPE ts_message. lv_exp = mo_uml->mv_diagram. mo_uml->message( is_message = ls_message ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT MESSAGE( ) Initial' ). ENDMETHOD. "initial_message METHOD call_factory. DATA ls_path TYPE ts_path. DATA lv_exp TYPE string. DATA ls_message TYPE ts_message. DATA ls_sat TYPE ts_sat. ls_sat-called-local = 'LCL_TEST'. ls_sat-called-type = c_type_class . ls_path = mi_actors->new_path( ls_sat ). lv_exp = mo_uml->mv_diagram && |3 -> { ls_path-called }: Create instance of class LCL_TEST\nactivate { ls_path-called }\n|. ls_message-id = c_id_class. ls_message-from_level = 2. ls_message-msg = lcl_abap_trace=>c_create_method. ls_message-caller = 3. ls_message-called = ls_path-called. mo_uml->message( ls_message ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT MESSAGE( ) Factory' ). ENDMETHOD. "call_factory METHOD return. test_return( |2 --> 1\ndeactivate 2\n| ). ENDMETHOD. "return METHOD return_from_initial. DATA lv_exp TYPE string. lv_exp = mo_uml->mv_diagram && |0 --> 1\n|. mo_uml->return( iv_from = 0 iv_to = 1 ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT RETURN( ) From Initial Error' ). ENDMETHOD. "return_from_initial METHOD return_same_level. DATA lv_exp TYPE string. lv_exp = mo_uml->mv_diagram && |deactivate 3\n|. mo_uml->return( iv_from = 3 iv_to = 3 ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = lv_exp msg = 'LCL_UML_PLANT RETURN( ) Same Level' ). ENDMETHOD. "return_same_level METHOD participant. test_participant( |participant "Test Participant" as 42\n| ). ENDMETHOD. "participant METHOD plantuml_header. DATA lo_uml TYPE REF TO lcl_uml_plant. lo_uml ?= mo_uml. cl_abap_unit_assert=>assert_equals( act = lo_uml->scale( lcl_configuration=>get( ) ) exp = |scale 0.50\n| msg = 'LCL_UML_PLANT->SCALE( ) Error' ). ENDMETHOD. "plantuml_header METHOD footer. test_footer( |@enduml\n| ). ENDMETHOD. "footer ENDCLASS. "ltc_plant_uml IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_graph_uml DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_graph_uml DEFINITION INHERITING FROM ltc_uml FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. METHODS setup. METHODS teardown. METHODS call FOR TESTING. METHODS return FOR TESTING. METHODS participant FOR TESTING. METHODS footer FOR TESTING. ENDCLASS. "ltc_graph_uml DEFINITION *----------------------------------------------------------------------* * CLASS ltc_graph_uml IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_graph_uml IMPLEMENTATION. METHOD setup. setup_uml( iv_uml = lcl_uml=>c_uml_graph iv_name = 'LCL_GRAPH_UML' ). ENDMETHOD. "setup METHOD teardown. teardown_uml( ). ENDMETHOD. "teardown METHOD call. test_message( |message(3,5,"Call method MAIN");\nactive(5);\n| ). ENDMETHOD. "call METHOD return. test_return( |return_message(2,1);\ninactive(2);\n| ). ENDMETHOD. "return METHOD participant. test_participant( |object(42,"Test Participant");\n| ). ENDMETHOD. "participant METHOD footer. test_footer( |.PE\n| ). ENDMETHOD. "footer ENDCLASS. "ltc_graph_uml IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_mscgen_uml DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_mscgen_uml DEFINITION INHERITING FROM ltc_uml FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PRIVATE SECTION. METHODS setup. METHODS teardown. METHODS call FOR TESTING. METHODS return FOR TESTING. METHODS participant FOR TESTING. METHODS footer FOR TESTING. ENDCLASS. "ltc_mscgen_uml DEFINITION *----------------------------------------------------------------------* * CLASS ltc_mscgen_uml IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_mscgen_uml IMPLEMENTATION. METHOD setup. setup_uml( iv_uml = lcl_uml=>c_uml_mscgen iv_name = 'LCL_MSCGEN_UML' ). ENDMETHOD. "setup METHOD teardown. teardown_uml( ). ENDMETHOD. "teardown METHOD call. test_message( |3=>5 [label="Call method MAIN"];\n| ). ENDMETHOD. "call METHOD return. test_return( |1<<2\n| ). ENDMETHOD. "return METHOD participant. test_participant( |42 [label="Test Participant"]| ). ENDMETHOD. "participant METHOD footer. test_footer( |\}\n| ). ENDMETHOD. "footer ENDCLASS. "ltc_mscgen_uml IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_file_name DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_file_name DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. CONSTANTS: c_path TYPE string VALUE 'C:\Temp\Dokumente\UML\', c_prefix TYPE string VALUE 'test' ##no_text. DATA mo_name TYPE REF TO lcl_file_name. METHODS setup. METHODS teardown. METHODS fixture. METHODS prefix_with_txt_extension FOR TESTING. METHODS prefix_without_extension FOR TESTING. METHODS get_fullpath FOR TESTING. ENDCLASS. "ltc_file_name DEFINITION *----------------------------------------------------------------------* * CLASS ltc_file_name IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_file_name IMPLEMENTATION. METHOD setup. mo_name = lcl_file_name=>new( lcl_diagram_text=>c_mode_txt ). fixture( ). ENDMETHOD. "setup METHOD fixture. mo_name->ms_file-name = c_prefix && mo_name->ms_file-ext. mo_name->ms_file-path = c_path && mo_name->ms_file-name. ENDMETHOD. "fixture METHOD teardown. FREE mo_name. ENDMETHOD. "teardown METHOD prefix_with_txt_extension. cl_abap_unit_assert=>assert_equals( exp = c_prefix act = mo_name->get_prefix( ) msg = 'LCL_FILE_NAME->GET_PREFIX( ) Error' ). ENDMETHOD. "prefix_with_txt_extension METHOD prefix_without_extension. mo_name->ms_file-ext = space. fixture( ). cl_abap_unit_assert=>assert_equals( exp = c_prefix act = mo_name->get_prefix( ) msg = 'LCL_FILE_NAME->GET_PREFIX( ) Error No Prefix' ). ENDMETHOD. "prefix_without_extension METHOD get_fullpath. cl_abap_unit_assert=>assert_equals( exp = mo_name->ms_file-path act = mo_name->get_fullpath( ) msg = 'LCL_FILE_NAME->GET_FULLPATH( ) Error' ). ENDMETHOD. "get_fullpath ENDCLASS. "ltc_file_name IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_diagram DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_diagram DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mo_gen TYPE REF TO lcl_diagram_plant_uml. METHODS setup. METHODS teardown. METHODS set_diagram IMPORTING iv_text TYPE string. METHODS to_xstring FOR TESTING. METHODS to_url FOR TESTING. ENDCLASS. "ltc_diagram DEFINITION *----------------------------------------------------------------------* * CLASS ltc_diagram IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_diagram IMPLEMENTATION. METHOD setup. lcl_configuration=>gs_cfg-skip_dialog = abap_true. lcl_configuration=>gs_cfg-output_mode = lcl_diagram_text=>c_mode_aut. mo_gen ?= lcl_sequence=>to_diagram( lcl_configuration=>get( ) ). ENDMETHOD. "setup METHOD teardown. FREE mo_gen. ENDMETHOD. "teardown METHOD set_diagram. mo_gen->mv_diagram = iv_text. ENDMETHOD. "set_diagram METHOD to_url. set_diagram( |Bob -> Alice : hello| ). cl_abap_unit_assert=>assert_equals( exp = 'SyfFKj2rKt3CoKnELR1Io4ZDoSa70000' act = mo_gen->to_url( iv_base_url = space ) msg = 'LCL_SEQ_GEN TO_URL( ) Error' ). ENDMETHOD. "to_url METHOD to_xstring. set_diagram( 'Polyfon zwitschernd aßen Mäxchens Vögel Rüben, Joghurt und Quark' ). cl_abap_unit_assert=>assert_equals( exp = 'UG9seWZvbiB6d2l0c2NoZXJuZCBhw59lbiBNw6R4Y2hlbnMgVsO2Z2VsIFLDvGJlbiwgSm9naHVydCB1bmQgUXVhcms=' act = cl_http_utility=>encode_x_base64( mo_gen->to_xstring( mo_gen->mv_diagram ) ) msg = 'LCL_SEQ_GEN TO_XSTRING( ) Error' ). ENDMETHOD. "to_xstring ENDCLASS. "ltc_diagram IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_messages DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_messages DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. METHODS constructor. PROTECTED SECTION. DATA mt_sat TYPE ltc_sequence=>tt_sat. DATA mo_uml TYPE REF TO lcl_uml. METHODS to_uml IMPORTING it_sat TYPE ltc_sequence=>tt_sat iv_uml TYPE lcl_uml=>tv_uml DEFAULT lcl_uml=>c_uml_plant iv_compact_trace TYPE char01 DEFAULT abap_true RETURNING value(rv_uml) TYPE string. METHODS get_log RETURNING value(ro_log) TYPE REF TO lcl_logger. PRIVATE SECTION. METHODS plantuml_header RETURNING value(rv_header) TYPE string. METHODS expected_plantuml_source RETURNING value(rv_exp) TYPE string. METHODS: calls_for_empty_diagram FOR TESTING, plantuml_for_empty_diagram FOR TESTING. METHODS sample_plantuml_body FOR TESTING. METHODS calls_for_sample_output FOR TESTING. ENDCLASS. "ltc_messages DEFINITION *----------------------------------------------------------------------* * CLASS ltc_messages IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_messages IMPLEMENTATION. METHOD constructor. mt_sat = ltc_actors=>sat_fixture_messages( ). ENDMETHOD. "constructor METHOD to_uml. DATA lo_calls TYPE REF TO lcl_call_stack. DATA ls_cfg TYPE ts_diagram_config. ls_cfg-uml_format = iv_uml. ls_cfg-compact_trace = iv_compact_trace. lo_calls = ltc_sequence=>new_sequence( it_sat = it_sat is_cfg = ls_cfg )->to_call_stack( ). rv_uml = lo_calls->to_uml( ). mo_uml = lo_calls->mo_uml. ENDMETHOD. "to_uml METHOD get_log. DATA lo_logger TYPE REF TO lcl_uml_logger. lo_logger ?= mo_uml. ro_log = lo_logger->mo_log. ENDMETHOD. "get_log METHOD calls_for_empty_diagram. DATA lt_sat TYPE ltc_sequence=>tt_sat. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. CLEAR ls_log. ls_log-method = 'HEADER'. APPEND ls_log TO lt_log. CLEAR ls_log. ls_log-method = 'FOOTER'. APPEND ls_log TO lt_log. to_uml( it_sat = lt_sat iv_uml = lcl_uml=>c_uml_mock ). get_log( )->verify( it_exp = lt_log iv_msg = 'LCL_MESSAGES TO_UML( ) Call Stack Empty Diagram Header/Footer Error' ). ENDMETHOD. "calls_for_empty_diagram METHOD calls_for_sample_output. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. DEFINE add_log. clear ls_log. ls_log-method = &1. ls_log-params = &2. append ls_log to lt_log. END-OF-DEFINITION. CLEAR ls_log. ls_log-method = 'HEADER'. APPEND ls_log TO lt_log. CLEAR ls_log. ls_log-method = 'PARTICIPANT'. ls_log-params = |1 PROG\\nY_TEST_PROG|. APPEND ls_log TO lt_log. CLEAR ls_log. ls_log-method = 'PARTICIPANT'. ls_log-params = |2 Static Methods of Class LCL_CTRL\\nY_TEST_PROG|. APPEND ls_log TO lt_log. CLEAR ls_log. ls_log-method = 'PARTICIPANT'. ls_log-params = |3 ObjectId:6 of Class LCL_MODEL\\nY_TEST_PROG|. APPEND ls_log TO lt_log. add_log: 'CALL' ` 1 1 `, 'CALL' `m 1 2 Call method MAIN`, 'CALL' `m 2 3 Call method CONSTRUCTOR`, 'RETURN' `3 2`, 'CALL' `m 2 2 Call method CHANGE`, 'CALL' `m 2 3 Call method UP`, 'RETURN' `3 2`, 'RETURN' `2 2`, 'RETURN' `2 1`. CLEAR ls_log. ls_log-method = 'FOOTER'. APPEND ls_log TO lt_log. to_uml( it_sat = mt_sat iv_uml = lcl_uml=>c_uml_mock ). get_log( )->verify( it_exp = lt_log iv_msg = 'LCL_MESSAGES TO_UML( ) Call Stack Error' ). ENDMETHOD. "calls_for_sample_output METHOD plantuml_header. DATA lo_uml TYPE REF TO lcl_uml_plant. CREATE OBJECT lo_uml. lo_uml->header( ). rv_header = lo_uml->mv_diagram. ENDMETHOD. "plantuml_header METHOD expected_plantuml_source. rv_exp = plantuml_header( ) && |participant "PROG\\nY_TEST_PROG" as 1\nparticipant "Static Methods of Class LCL_CTRL\\nY_TEST_PROG" as 2\n| && |participant "ObjectId:6 of Class LCL_MODEL\\nY_TEST_PROG" as 3\n1 -> 1: \nactivate 1\n| && |1 -> 2: Call method MAIN\nactivate 2\n2 -> 3: Call method CONSTRUCTOR\nactivate 3\n3 --> 2\ndeactivate 3\n2 -> 2: Call method CHANGE\n| && |activate 2\n2 -> 3: Call method UP\nactivate 3\n3 --> 2\ndeactivate 3\ndeactivate 2\n2 --> 1\n| && |deactivate 2\n@enduml\n|. ENDMETHOD. "expected_plantuml_source METHOD sample_plantuml_body. cl_abap_unit_assert=>assert_equals( act = to_uml( mt_sat ) exp = expected_plantuml_source( ) msg = 'LCL_MESSAGES->TO_UML( ) PlantUML Error' ). ENDMETHOD. "sample_plantuml_body METHOD plantuml_for_empty_diagram. DATA lt_sat TYPE ltc_sequence=>tt_sat. cl_abap_unit_assert=>assert_char_cp( act = to_uml( lt_sat ) exp = |@startuml\nhide footbox\nautonumber\n*skinparam*@enduml\n| msg = 'LCL_MESSAGES TO_UML( ) Empty Diagram Header/Footer Error' ). ENDMETHOD. "plantuml_for_empty_diagram ENDCLASS. "ltc_messages IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_call_compact DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_call_compact DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mo_calls TYPE REF TO lcl_call_stack_compact. " CUT DATA mo_iterator TYPE REF TO lcl_messages. DATA mo_uml TYPE REF TO lcl_uml. METHODS setup. METHODS teardown. METHODS cycle_trace RETURNING value(rt_trace) TYPE tt_message. METHODS fixture IMPORTING iv_compact_trace TYPE xsdboolean iv_uml TYPE lcl_uml=>tv_uml DEFAULT lcl_uml=>c_uml_plant RETURNING value(ro_calls) TYPE REF TO lcl_call_stack. * METHODS create_cycle FOR TESTING. METHODS cycle_no_compress FOR TESTING. METHODS begin_loop FOR TESTING. METHODS calls_for_begin_loop FOR TESTING. METHODS end_loop FOR TESTING. METHODS no_end_loop FOR TESTING. ENDCLASS. "ltc_call_compact DEFINITION *----------------------------------------------------------------------* * CLASS ltc_call_compact IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_call_compact IMPLEMENTATION. METHOD cycle_trace. DATA ls_msg TYPE ts_message. ls_msg-id = 'm'. ls_msg-from_level = 1. ls_msg-caller = 1. ls_msg-called = 2. ls_msg-msg = 'MAIN'. APPEND ls_msg TO rt_trace. CLEAR ls_msg. ls_msg-id = 'm'. ls_msg-from_level = 2. ls_msg-caller = 2. ls_msg-called = 3. ls_msg-msg = lcl_abap_trace=>c_create_method. APPEND ls_msg TO rt_trace. APPEND LINES OF rt_trace TO rt_trace. " Create Loop ENDMETHOD. "cycle_trace METHOD setup. mo_calls ?= fixture( iv_compact_trace = abap_true iv_uml = lcl_uml=>c_uml_plant ). ENDMETHOD. "setup METHOD fixture. DATA lo_actors TYPE REF TO lcl_actors. CREATE OBJECT lo_actors. mo_uml = lcl_uml=>new( iv_uml = iv_uml ii_actors = lo_actors ). ro_calls = lcl_uml_factory=>new_calls( iv_compact_trace = iv_compact_trace it_trace = cycle_trace( ) io_uml = mo_uml ). mo_iterator = ro_calls->mo_messages. ENDMETHOD. "fixture METHOD teardown. FREE mo_iterator. FREE mo_calls. FREE mo_uml. ENDMETHOD. "teardown METHOD create_cycle. cl_abap_unit_assert=>assert_equals( act = lines( mo_calls->mt_cycle ) exp = 1 msg = 'LCL_CALL_STACK_COMPACT CONSTRUCTOR( ) Cycle Error' ). ENDMETHOD. "create_cycle METHOD cycle_no_compress. TRY. mo_calls ?= fixture( iv_compact_trace = abap_false ). cl_abap_unit_assert=>fail( msg = 'LCL_CALL_STACK_COMPACT CONSTRUCTOR( ) No Cycle Error' ). CATCH cx_sy_move_cast_error ##no_handler. * Expected: Object of type LCL_MESSAGES created, not compatible with LCL_MESSAGE_COMPACT ENDTRY. ENDMETHOD. "cycle_no_compress METHOD begin_loop. mo_iterator->next( ). mo_calls->begin( iv_idx = mo_iterator->mv_idx iv_from = 1 ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = |loop 2 times\n| msg = 'LCL_CALL_STACK_COMPACT BEGIN_LOOP( ) Error' ). ENDMETHOD. "begin_loop METHOD calls_for_begin_loop. DATA lo_logger TYPE REF TO lcl_uml_logger. DATA ls_log TYPE lcl_logger=>ts_log. DATA lt_log TYPE lcl_logger=>tt_log. ls_log-method = 'BEGIN_LOOP'. ls_log-params = '2'. APPEND ls_log TO lt_log. mo_calls ?= fixture( iv_compact_trace = abap_true iv_uml = lcl_uml=>c_uml_mock ). mo_iterator->next( ). mo_calls->begin( iv_idx = mo_iterator->mv_idx iv_from = 1 ). lo_logger ?= mo_uml. lo_logger->mo_log->verify( it_exp = lt_log iv_msg = 'LCL_CALL_STACK_COMPACT BEGIN_LOOP( ) Calls Error' ). ENDMETHOD. "calls_for_begin_loop METHOD end_loop. mo_iterator->skip( 2 ). mo_calls->end( iv_idx = mo_iterator->mv_idx iv_to = 2 ). " need a MOCK object here cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = |end\n| msg = 'LCL_CALL_STACK_COMPACT END_LOOP( ) Error' ). ENDMETHOD. "end_loop METHOD no_end_loop. mo_calls ?= fixture( abap_true ). mo_iterator->skip( 4 ). mo_calls->end( iv_idx = mo_iterator->mv_idx iv_to = 2 ). cl_abap_unit_assert=>assert_equals( act = mo_uml->mv_diagram exp = space msg = 'LCL_CALL_STACK_COMPACT END_LOOP( ) No End Loop Error' ). ENDMETHOD. "no_end_loop ENDCLASS. "ltc_call_compact IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_cycles DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_cycles DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. INTERFACES lif_cycles. PRIVATE SECTION. DATA mt_messages TYPE tt_message. DATA mo_index TYPE REF TO lcl_trace_index. DATA mt_cycles TYPE lcl_call_stack_compact=>tt_cycle. METHODS fixture IMPORTING iv_cycles TYPE n DEFAULT 1. METHODS detect_cycles IMPORTING iv_cycles TYPE n DEFAULT 1. METHODS next_match FOR TESTING. METHODS detect FOR TESTING. METHODS detect_4 FOR TESTING. METHODS fold FOR TESTING. ENDCLASS. "ltc_cycles DEFINITION *----------------------------------------------------------------------* * CLASS ltc_cycles IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_cycles IMPLEMENTATION. METHOD fixture. DATA ls_message TYPE ts_message. DATA lt_msg LIKE mt_messages. ls_message-id = 'm'. ls_message-from_level = 1. ls_message-caller = 1. ls_message-called = 2. ls_message-msg = 'MAIN'. APPEND ls_message TO mt_messages. ls_message-id = 'm'. ls_message-from_level = 2. ls_message-caller = 2. ls_message-called = 3. ls_message-msg = lcl_abap_trace=>c_create_method. APPEND ls_message TO mt_messages. ls_message-id = 'm'. ls_message-from_level = 2. ls_message-caller = 2. ls_message-called = 4. ls_message-msg = 'CHANGE'. APPEND ls_message TO mt_messages. ls_message-id = 'm'. ls_message-from_level = 2. ls_message-caller = 2. ls_message-called = 4. ls_message-msg = 'CHANGE'. APPEND ls_message TO mt_messages. ls_message-id = 'm'. ls_message-from_level = 3. ls_message-caller = 4. ls_message-called = 6. ls_message-msg = 'UP'. APPEND ls_message TO mt_messages. lt_msg = mt_messages. DO iv_cycles TIMES. APPEND LINES OF lt_msg TO mt_messages. ENDDO. CLEAR lt_msg. ls_message-id = 'm'. ls_message-from_level = 1. ls_message-caller = 10. ls_message-called = 11. ls_message-msg = 'MAIN'. APPEND ls_message TO lt_msg. ls_message-id = 'm'. ls_message-from_level = 2. ls_message-caller = 11. ls_message-called = 16. ls_message-msg = lcl_abap_trace=>c_create_method. APPEND ls_message TO lt_msg. APPEND LINES OF lt_msg TO mt_messages. CREATE OBJECT mo_index EXPORTING it_trace = mt_messages ii_cycles = me. ENDMETHOD. "fixture METHOD lif_cycles~collect. INSERT is_cycle INTO TABLE mt_cycles. ENDMETHOD. "lif_cycles~collect METHOD lif_cycles~accepts. rv_flag = abap_true. ENDMETHOD. "lif_cycles~accepts METHOD detect_cycles. fixture( iv_cycles ). lcl_pattern=>new( it_xindex = mo_index->mt_xindex ii_cycles = me )->detect_cycles( ). ENDMETHOD. "detect_cycles METHOD next_match. TRY. fixture( 1 ). lcl_pattern=>new( it_xindex = mo_index->mt_xindex ii_cycles = me )->next_occurrence_from( -10 ). * Should not throw an exception. CATCH cx_dynamic_check. * If an exception is triggered, the run-time will catch it (not propagated) * It will appear in the result list without a proper message. * So this is just for developer information, for now cl_abap_unit_assert=>fail( msg = 'LCL_PATTERN NEXT_OCCURRENCE_FROM( ) Index Error' ). ENDTRY. ENDMETHOD. "next_match METHOD detect. detect_cycles( ). cl_abap_unit_assert=>assert_equals( act = lines( mt_cycles ) exp = 2 msg = 'LCL_PATTERN DETECT_CYCLES( ) Error' ). ENDMETHOD. "detect METHOD detect_4. detect_cycles( 4 ). cl_abap_unit_assert=>assert_equals( act = lines( mt_cycles ) exp = 2 msg = 'LCL_PATTERN DETECT_CYCLES( ) Level 4 Error' ). ENDMETHOD. "detect_4 METHOD fold. DATA lo_compactor TYPE REF TO lcl_uml_compactor. DATA lt_act TYPE tt_message. DATA lt_exp TYPE tt_message. DATA ls_message TYPE ts_message. detect_cycles( 3 ). CREATE OBJECT lo_compactor. lo_compactor->fold( EXPORTING it_trace = mt_messages IMPORTING et_messages = lt_act ). ls_message-id = 'm'. ls_message-from_level = 1. ls_message-caller = 1. ls_message-called = 2. ls_message-msg = 'MAIN'. APPEND ls_message TO lt_exp. CLEAR ls_message. ls_message-id = 'm'. ls_message-from_level = 2. ls_message-caller = 2. ls_message-called = 3. ls_message-msg = lcl_abap_trace=>c_create_method. APPEND ls_message TO lt_exp. CLEAR ls_message. ls_message-id = 'm'. ls_message-from_level = 2. ls_message-caller = 2. ls_message-called = 4. ls_message-msg = 'CHANGE'. APPEND ls_message TO lt_exp. CLEAR ls_message. ls_message-id = 'm'. ls_message-from_level = 3. ls_message-caller = 4. ls_message-called = 6. ls_message-msg = 'UP'. APPEND ls_message TO lt_exp. CLEAR ls_message. ls_message-id = 'm'. ls_message-from_level = 1. ls_message-caller = 10. ls_message-called = 11. ls_message-msg = 'MAIN'. APPEND ls_message TO lt_exp. CLEAR ls_message. ls_message-id = 'm'. ls_message-from_level = 2. ls_message-caller = 11. ls_message-called = 16. ls_message-msg = lcl_abap_trace=>c_create_method. APPEND ls_message TO lt_exp. cl_abap_unit_assert=>assert_equals( act = lt_act exp = lt_exp msg = 'LCL_LOOP_COMPACTOR->FOLD( ) LOGIC Error' ). ENDMETHOD. "fold ENDCLASS. "ltc_cycles IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_shrinkage DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_shrinkage DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. METHODS reconcile_loop_references FOR TESTING. ENDCLASS. "ltc_shrinkage DEFINITION *----------------------------------------------------------------------* * CLASS ltc_shrinkage IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_shrinkage IMPLEMENTATION. METHOD reconcile_loop_references. DATA lt_cycles TYPE lcl_call_stack_compact=>tt_cycle. DATA lt_exp LIKE lt_cycles. DATA ls_cycle TYPE ts_cycle. DEFINE cycle_value. ls_cycle-start = &2. ls_cycle-end = &3. ls_cycle-last = &4. ls_cycle-times = &5. insert ls_cycle into table &1. END-OF-DEFINITION. cycle_value lt_cycles: 1 48 192 4, 3 24 46 2, 19 20 22 2, 193 194 200 4, 201 632 476264 1102, 217 218 220 2. * ( start = 1 end = 48 last = 192 times = 4 ) " 1 + 48 x 4 - 1 = 192 * ( start = 3 end = 24 last = 46 times = 2 ) " 3 + 22 x 2 - 1 = 46 * ( start = 19 end = 20 last = 22 times = 2 ) " 19 + 2 x 2 - 1 = 22 * ( start = 193 end = 194 last = 200 times = 4 ) " 193 + 2 x 4 - 1 = 200 * ( start = 201 end = 632 last = 476264 times = 1102 ) " 201 + 432 x 1102 - 1 = 476264 * ( start = 217 end = 218 last = 220 times = 2 ) ). " 217 + 2 x 2 - 1 = 220 lcl_shrinkage=>shrink_references( CHANGING ct_cycles = lt_cycles ). cycle_value lt_exp: 1 24 24 4, 3 22 22 2, 19 20 20 2, 25 26 26 4, 27 456 456 1102, 43 44 44 2. cl_abap_unit_assert=>assert_equals( act = lt_cycles exp = lt_exp msg = 'LCL_SHRINKAGE->SHRINK_REFERENCES( ) Error' ). ENDMETHOD. "reconcile_loop_references ENDCLASS. "ltc_shrinkage IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_trace DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_trace DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. INTERFACES lif_collector. PRIVATE SECTION. DATA ms_cfg TYPE ts_diagram_config. DATA mo_trace TYPE REF TO lcl_trace. " CUT DATA mo_state TYPE REF TO lcl_satr_state. DATA mt_act_trace TYPE tt_message. DATA mt_sat TYPE STANDARD TABLE OF ts_sat. METHODS setup. METHODS teardown. METHODS fill_trace. METHODS fixture_add_more. METHODS fixture_prepare_traceprog. METHODS fixture_prepare_tracetext. METHODS fixture_prepare_tracemeth. METHODS fixture_caller. METHODS add_message FOR TESTING. METHODS add_more FOR TESTING. METHODS skip_message FOR TESTING. METHODS filter_message FOR TESTING. METHODS save_caller FOR TESTING. METHODS test_output FOR TESTING. METHODS skip_record FOR TESTING. ENDCLASS. "ltc_trace DEFINITION *----------------------------------------------------------------------* * CLASS ltc_trace IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_trace IMPLEMENTATION. METHOD setup. CLEAR mt_act_trace. CLEAR mt_sat. APPEND 'Y*' TO ms_cfg-pattern. ms_cfg-compact_trace = abap_true. ms_cfg-output_mode = lcl_diagram_text=>c_mode_aut. CREATE OBJECT: mo_trace, mo_state EXPORTING io_atra = mo_trace->mo_atra. ENDMETHOD. "setup METHOD teardown. mo_state->restore( ). FREE mo_state. FREE mo_trace. ENDMETHOD. "teardown METHOD fill_trace. DATA lo_sequence TYPE REF TO lcl_sequence. CREATE OBJECT lo_sequence EXPORTING is_cfg = ms_cfg. mo_trace->fill( lo_sequence ). mt_act_trace = lo_sequence->mt_trace. ENDMETHOD. "fill_trace METHOD lif_collector~collect. * No filter / No conversion APPEND is_sat TO mt_sat. ENDMETHOD. "lif_collector~collect METHOD add_message. DATA ls_austab TYPE satr_austab_gesamt. ls_austab-id = 'm'. ls_austab-index = 1. ls_austab-ebene = 2. ls_austab-caller = 3. ls_austab-event = '>'. CLEAR mo_trace->mo_atra->it_austab_hier[]. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. mo_trace->fill( me ). cl_abap_unit_assert=>assert_equals( act = lines( mt_sat ) exp = 1 msg = 'LCL_TRACE ADD_MESSAGE( ) Error' ). ENDMETHOD. "add_message METHOD filter_message. DATA ls_prog TYPE satr_prog. DATA ls_austab TYPE satr_austab_gesamt. CLEAR mo_trace->mo_atra->it_traceprog[]. ls_prog-cprog = 'Y_PROG'. ls_prog-object = c_type_prog. APPEND ls_prog TO mo_trace->mo_atra->it_traceprog. ls_prog-cprog = 'SAPPROG'. ls_prog-object = c_type_class. APPEND ls_prog TO mo_trace->mo_atra->it_traceprog. CLEAR mo_trace->mo_atra->it_austab_hier[]. ls_austab-id = 'O'. ls_austab-index = 1. ls_austab-ebene = 0. ls_austab-caller = 0. ls_austab-event = '>'. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'V'. ls_austab-subid = 'O'. ls_austab-index = 2. ls_austab-ebene = 1. ls_austab-caller = 1. ls_austab-event = '>'. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'P'. ls_austab-index = 3. ls_austab-ebene = 1. ls_austab-caller = 1. ls_austab-event = '>'. ls_austab-progindex = 1. ls_austab-textindex = 1. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'm'. ls_austab-index = 4. ls_austab-ebene = 2. ls_austab-caller = 1. ls_austab-event = '>'. ls_austab-progindex = 1. ls_austab-methindex = 1. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'm'. ls_austab-subid = 'x'. ls_austab-index = 5. ls_austab-ebene = 2. ls_austab-caller = 1. ls_austab-event = '<'. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. fill_trace( ). cl_abap_unit_assert=>assert_equals( act = lines( mt_act_trace ) exp = 1 msg = 'LCL_TRACE ADD_MESSAGE( ) Filter Error' ). ENDMETHOD. "filter_message METHOD skip_message. DATA ls_austab TYPE satr_austab_gesamt. CLEAR mo_trace->mo_atra->it_austab_hier[]. ls_austab-id = 'm'. ls_austab-index = 1. ls_austab-ebene = 2. ls_austab-caller = 2. ls_austab-event = '<'. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. fill_trace( ). cl_abap_unit_assert=>assert_initial( act = lines( mt_act_trace ) msg = 'LCL_TRACE ADD_MESSAGE( ) Skip Error' ). ENDMETHOD. "skip_message METHOD save_caller. DATA ls_austab TYPE satr_austab_gesamt. CLEAR mo_trace->mo_atra->it_austab_hier[]. ls_austab-id = 'm'. ls_austab-index = 1. ls_austab-ebene = 2. ls_austab-caller = 3. ls_austab-event = '>'. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'm'. ls_austab-index = 1. ls_austab-ebene = 2. ls_austab-caller = 2. ls_austab-event = '<'. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. fill_trace( ). cl_abap_unit_assert=>assert_equals( act = lines( mo_trace->mt_caller ) exp = 1 msg = 'LCL_TRACE ADD_MESSAGE( ) Save Caller' ). ENDMETHOD. "save_caller METHOD test_output. lcl_configuration=>gs_cfg-skip_dialog = abap_true. lcl_configuration=>gs_cfg-output_mode = lcl_diagram_text=>c_mode_aut. lcl_sequence=>to_diagram( lcl_configuration=>get( ) )->output( ). ENDMETHOD. "test_output METHOD fixture_prepare_traceprog. DATA ls_prog TYPE satr_prog. CLEAR mo_trace->mo_atra->it_traceprog[]. CLEAR ls_prog. ls_prog-cprog = ltc_actors=>c_prog. ls_prog-object = c_type_prog. APPEND ls_prog TO mo_trace->mo_atra->it_traceprog. CLEAR ls_prog. ls_prog-cprog = ltc_actors=>c_prog. ls_prog-object = c_type_class. APPEND ls_prog TO mo_trace->mo_atra->it_traceprog. CLEAR ls_prog. ls_prog-cprog = 'SAPPROG'. ls_prog-object = c_type_class. APPEND ls_prog TO mo_trace->mo_atra->it_traceprog. ENDMETHOD. "fixture_prepare_traceprog METHOD fixture_prepare_tracetext. DATA ls_text LIKE LINE OF mo_trace->mo_atra->it_tracetext. CLEAR mo_trace->mo_atra->it_tracetext[]. ls_text-tracetext = 'MAIN'. APPEND ls_text TO mo_trace->mo_atra->it_tracetext. ls_text-tracetext = lcl_abap_trace=>c_create_method. APPEND ls_text TO mo_trace->mo_atra->it_tracetext. ls_text-tracetext = 'CHANGE'. APPEND ls_text TO mo_trace->mo_atra->it_tracetext. ls_text-tracetext = ltc_actors=>c_ctrl. APPEND ls_text TO mo_trace->mo_atra->it_tracetext. ls_text-tracetext = ltc_actors=>c_model. APPEND ls_text TO mo_trace->mo_atra->it_tracetext. ls_text-tracetext = ltc_actors=>c_prog. APPEND ls_text TO mo_trace->mo_atra->it_tracetext. ENDMETHOD. "fixture_prepare_tracetext METHOD fixture_prepare_tracemeth. DATA ls_meth LIKE LINE OF mo_trace->mo_atra->it_tracemeth. CLEAR mo_trace->mo_atra->it_tracemeth[]. ls_meth-methode = 'MAIN'. APPEND ls_meth TO mo_trace->mo_atra->it_tracemeth. ls_meth-methode = lcl_abap_trace=>c_create_method. APPEND ls_meth TO mo_trace->mo_atra->it_tracemeth. ls_meth-methode = 'CHANGE'. APPEND ls_meth TO mo_trace->mo_atra->it_tracemeth. ENDMETHOD. "fixture_prepare_tracemeth METHOD fixture_add_more. DATA ls_austab TYPE satr_austab_gesamt. * Fixture: Scenario from ltc_objects=>sat_fixture_events( ) fixture_prepare_traceprog( ). fixture_prepare_tracetext( ). fixture_prepare_tracemeth( ). CLEAR mo_trace->mo_atra->it_austab_hier[]. ls_austab-id = 'O'. ls_austab-index = 1. ls_austab-ebene = 0. ls_austab-caller = 0. ls_austab-event = '>'. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'C'. ls_austab-subid = 'R'. ls_austab-index = 2. ls_austab-ebene = 1. ls_austab-caller = 1. ls_austab-event = '>'. ls_austab-textindex = 6. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'V'. ls_austab-subid = 'O'. ls_austab-index = 3. ls_austab-ebene = 2. ls_austab-caller = 2. ls_austab-event = '>'. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'P'. ls_austab-index = 4. ls_austab-ebene = 2. ls_austab-caller = 2. ls_austab-event = '>'. ls_austab-progindex = 1. ls_austab-textindex = 1. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'm'. ls_austab-index = 5. ls_austab-ebene = 3. ls_austab-caller = 4. ls_austab-event = '>'. ls_austab-progindex = 2. ls_austab-textindex = 4. ls_austab-methindex = 1. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'm'. ls_austab-subid = 'x'. ls_austab-index = 6. ls_austab-ebene = 4. ls_austab-caller = 5. ls_austab-event = '>'. ls_austab-progindex = 3. ls_austab-textindex = 5. ls_austab-methindex = 2. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'm'. ls_austab-subid = 'x'. ls_austab-index = 7. ls_austab-ebene = 4. ls_austab-caller = 6. ls_austab-event = '>'. ls_austab-progindex = 1. ls_austab-textindex = 4. ls_austab-methindex = 3. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. fill_trace( ). ENDMETHOD. "fixture_add_more METHOD add_more. DATA ls_message TYPE ts_message. DATA lt_exp TYPE tt_message. fixture_add_more( ). CLEAR ls_message. ls_message-id = 'C'. ls_message-from_level = 1. ls_message-caller = 1. ls_message-called = 2. ls_message-msg = ltc_actors=>c_prog. APPEND ls_message TO lt_exp. CLEAR ls_message. ls_message-id = 'm'. ls_message-from_level = 3. ls_message-caller = 2. ls_message-called = 3. ls_message-msg = 'MAIN'. APPEND ls_message TO lt_exp. CLEAR ls_message. ls_message-id = c_id_skip. ls_message-from_level = 3. ls_message-caller = 3. ls_message-called = 4. ls_message-msg = lcl_abap_trace=>c_create_method. APPEND ls_message TO lt_exp. CLEAR ls_message. ls_message-id = 'm'. ls_message-from_level = 4. ls_message-caller = 4. ls_message-called = 3. ls_message-msg = 'CHANGE'. APPEND ls_message TO lt_exp. cl_abap_unit_assert=>assert_equals( act = mt_act_trace exp = lt_exp msg = 'LCL_TRACE ADD_MESSAGE( ) More - Error' ). ENDMETHOD. "add_more METHOD fixture_caller. DATA ls_austab TYPE satr_austab_gesamt. fixture_prepare_traceprog( ). fixture_prepare_tracetext( ). fixture_prepare_tracemeth( ). CLEAR mo_trace->mo_atra->it_austab_hier[]. ls_austab-id = 'O'. ls_austab-index = 1. ls_austab-ebene = 0. ls_austab-caller = 0. ls_austab-event = '>'. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. CLEAR ls_austab. ls_austab-id = 'C'. ls_austab-subid = 'R'. ls_austab-index = 2. ls_austab-ebene = 1. ls_austab-caller = 1. ls_austab-event = '>'. ls_austab-textindex = 6. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. ls_austab-event = '<'. APPEND ls_austab TO mo_trace->mo_atra->it_austab_hier. fill_trace( ). ENDMETHOD. "fixture_caller METHOD skip_record. DATA lt_exp TYPE lcl_trace=>tt_seq_k1. DATA ls_seq TYPE ts_seq. * TO DO: check coverage/review fixture fixture_caller( ). CLEAR ls_seq. ls_seq-called-instance = 0. ls_seq-aus_tabix = 1. APPEND ls_seq TO lt_exp. CLEAR ls_seq. ls_seq-called-global = ltc_actors=>c_prog. ls_seq-called-type = c_type_prog. ls_seq-called-instance = 0. ls_seq-called-mod = ltc_actors=>c_prog. ls_seq-aus_tabix = 2. APPEND ls_seq TO lt_exp. cl_abap_unit_assert=>assert_equals( act = mo_trace->mt_caller exp = lt_exp msg = 'LCL_TRACE SKIP_RECORD( ) Error' ). ENDMETHOD. "skip_record ENDCLASS. "ltc_trace IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_filter_null DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_filter_null DEFINITION FOR TESTING CREATE PRIVATE RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mi_filter TYPE REF TO lif_trace_filter. METHODS setup. METHODS teardown. METHODS filter_empty FOR TESTING. METHODS filter_valid FOR TESTING. ENDCLASS. "ltc_filter_null DEFINITION *----------------------------------------------------------------------* * CLASS ltc_filter_null IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_filter_null IMPLEMENTATION. METHOD setup. DATA ls_cfg TYPE ts_diagram_config. mi_filter = lcl_filter=>new( ls_cfg ). ENDMETHOD. "setup METHOD teardown. FREE mi_filter. ENDMETHOD. "teardown METHOD filter_empty. DATA ls_sat TYPE ts_sat. cl_abap_unit_assert=>assert_true( act = mi_filter->accepts( ls_sat ) msg = 'LCL_FILTER ACCEPTS( ) Empty - Null Filter Error' ). ENDMETHOD. "filter_empty METHOD filter_valid. cl_abap_unit_assert=>assert_true( act = mi_filter->accepts( ltc_actors=>sat_fixture_record( ) ) msg = 'LCL_FILTER ACCEPTS( ) Valid - Null Filter Error' ). ENDMETHOD. "filter_valid ENDCLASS. "ltc_filter_null IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_filter_custom DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_filter_custom DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mi_filter TYPE REF TO lif_trace_filter. METHODS setup. METHODS teardown. METHODS sample_sat RETURNING value(rs_sat) TYPE ts_sat. METHODS filter_empty FOR TESTING. METHODS filter_valid FOR TESTING. METHODS accepts FOR TESTING. METHODS system_events FOR TESTING. METHODS skip_constructor FOR TESTING. ENDCLASS. "ltc_filter_custom DEFINITION *----------------------------------------------------------------------* * CLASS ltc_filter_custom IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_filter_custom IMPLEMENTATION. METHOD setup. DATA ls_cfg TYPE ts_diagram_config. APPEND 'Y*' TO ls_cfg-pattern. mi_filter = lcl_filter=>new( ls_cfg ). ENDMETHOD. "setup METHOD teardown. FREE mi_filter. ENDMETHOD. "teardown METHOD filter_empty. DATA ls_sat TYPE ts_sat. cl_abap_unit_assert=>assert_false( act = mi_filter->accepts( ls_sat ) msg = 'LCL_FILTER_CUSTOM ACCEPTS( ) Empty - Error' ). ENDMETHOD. "filter_empty METHOD filter_valid. cl_abap_unit_assert=>assert_true( act = mi_filter->accepts( ltc_actors=>sat_fixture_record( ) ) msg = 'LCL_FILTER_CUSTOM ACCEPTS( ) Valid - Error' ). ENDMETHOD. "filter_valid METHOD sample_sat. CLEAR rs_sat. rs_sat-id = 'L'. rs_sat-from_level = 8. rs_sat-aus_tabix = 21. rs_sat-caller-global = 'SAPMSSYD'. rs_sat-caller-type = 'PROG'. rs_sat-caller-mod = '%_IMODE_INIT'. ENDMETHOD. "sample_sat METHOD accepts. cl_abap_unit_assert=>assert_false( act = mi_filter->accepts( sample_sat( ) ) msg = 'LCL_FILTER_CUSTOM ACCEPTS( ) Custom - Filter Error' ). ENDMETHOD. "accepts METHOD system_events. DATA ls_cfg TYPE ts_diagram_config. ls_cfg-system_events = abap_true. APPEND 'SAPM*' TO ls_cfg-pattern. mi_filter = lcl_filter=>new( ls_cfg ). cl_abap_unit_assert=>assert_true( act = mi_filter->accepts( sample_sat( ) ) msg = 'LCL_FILTER_CUSTOM ACCEPTS( ) Custom - <system Event' ). ENDMETHOD. "system_events METHOD skip_constructor. DATA ls_sat TYPE ts_sat. DATA ls_prev TYPE ts_sat. DATA ls_cfg TYPE ts_diagram_config. ls_cfg-system_events = abap_true. APPEND 'SAPM*' TO ls_cfg-pattern. mi_filter = lcl_filter=>new( ls_cfg ). ls_sat = sample_sat( ). ls_prev = ls_sat. ls_prev-caller-mod = lcl_abap_trace=>c_create_method. mi_filter->accepts( ls_prev ). cl_abap_unit_assert=>assert_true( act = mi_filter->accepts( ls_sat ) msg = 'LCL_FILTER_CUSTOM ACCEPTS( ) Custom - Filter Error' ). ENDMETHOD. "skip_constructor ENDCLASS. "ltc_filter_custom IMPLEMENTATION *----------------------------------------------------------------------* * CLASS ltc_configuration DEFINITION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_configuration DEFINITION FOR TESTING RISK LEVEL HARMLESS DURATION SHORT. PUBLIC SECTION. INTERFACES lif_unit_test. PRIVATE SECTION. DATA mo_config TYPE REF TO lcl_configuration. METHODS setup. METHODS teardown. METHODS get_result RETURNING value(rv_result) TYPE char3. METHODS radiobutton_url FOR TESTING. METHODS radiobutton_txt FOR TESTING. METHODS radiobutton_exe FOR TESTING. METHODS from_radiobutton_txt FOR TESTING. ENDCLASS. "ltc_configuration DEFINITION *----------------------------------------------------------------------* * CLASS ltc_configuration IMPLEMENTATION *----------------------------------------------------------------------* * *----------------------------------------------------------------------* CLASS ltc_configuration IMPLEMENTATION. METHOD setup. CREATE OBJECT mo_config. ENDMETHOD. "setup METHOD teardown. FREE mo_config. ENDMETHOD. "teardown METHOD get_result. rv_result+0(1) = mo_config->mv_mode_url. rv_result+1(1) = mo_config->mv_mode_txt. rv_result+2(1) = mo_config->mv_mode_exe. ENDMETHOD. "get_result METHOD radiobutton_url. mo_config->gs_cfg-output_mode = lcl_diagram_text=>c_mode_url. mo_config->to_radiobutton( ). cl_abap_unit_assert=>assert_equals( act = get_result( ) exp = 'X ' msg = 'LCL_CONFIGURATION->TO_RADIOBUTTON( ) - URL Error' ). ENDMETHOD. "radiobutton_url METHOD radiobutton_exe. mo_config->gs_cfg-output_mode = lcl_diagram_text=>c_mode_exe. mo_config->to_radiobutton( ). cl_abap_unit_assert=>assert_equals( act = get_result( ) exp = ' X' msg = 'LCL_CONFIGURATION->TO_RADIOBUTTON( ) - EXE Error' ). ENDMETHOD. "radiobutton_exe METHOD radiobutton_txt. mo_config->gs_cfg-output_mode = lcl_diagram_text=>c_mode_txt. mo_config->to_radiobutton( ). cl_abap_unit_assert=>assert_equals( act = get_result( ) exp = ' X ' msg = 'LCL_CONFIGURATION->TO_RADIOBUTTON( ) - TXT Error' ). ENDMETHOD. "radiobutton_txt METHOD from_radiobutton_txt. mo_config->gs_cfg-output_mode = lcl_diagram_text=>c_mode_txt. mo_config->to_radiobutton( ). mo_config->from_radiobutton( ). cl_abap_unit_assert=>assert_equals( act = mo_config->gs_cfg-output_mode exp = lcl_diagram_text=>c_mode_txt msg = 'LCL_CONFIGURATION->FROM_RADIOBUTTON( ) - TXT Error' ). ENDMETHOD. "from_radiobutton_txt ENDCLASS. "ltc_configuration IMPLEMENTATION *-------------------------------------------------------------------------------------------------*
Web Search for Existing Research Papers
- Extracting Sequence Diagram from Execution Trace of Java Program
- http://libra.msra.cn/Publication/1868795/extracting-sequence-diagram-from-execution-trace-of-java-program
- Improving The Scalability of Tools Incorporating Sequence Diagram Visualizations of Large Execution Traces
- Visualisations of Execution Traces
- Sequence Diagram Slicing
- http://www.researchgate.net/publication/46047751_Reverse-engineering_of_UML_2.0_Sequence_Diagrams_from_Execution_Traces
- http://www.academia.edu/6536352/Execution_Trace_Analysis_through_Massive_Sequence_and_Circular_Bundle_Views or http://www.st.ewi.tudelft.nl/~cornel/pub/JSS08.pdf
- Static Control-Flow Analysis for Reverse Engineering of UML Sequence Diagrams
- A Fully Dynamic Approach to the Reverse Engineering of UML Sequence Diagrams
- Execution Trace Abstraction based on Meta Patterns Usage
- http://www.researchgate.net/publication/224198185_Extracting_Sequence_Diagrams_from_Execution_Traces_Using_Interactive_Visualization
- A Metamodel for Dynamic Information Generated from Object-Oriented Systems or http://dmct.dorsal.polymtl.ca/sites/dmct.dorsal.polymtl.ca/files/Sept17-SOTA-TraceAbstractionTechniques.pdf
- http://users.encs.concordia.ca/~abdelw/sba/papers/WahabPhDThesisFinal.pdf
- ANALYTIC VARIATIONS ON THE COMMON SUBEXPRESSION PROBLEM
Sharp and Rountev paper Interactive Exploration of UML Sequence Diagrams (2005) and their material propose the following filters for reverse-engineered sequence diagram I would like to implement:
- Choose starting and ending message
- Choose max. call stack depth
- Focus on a single message
- Filter out everything outside of a chosen fragment