Pipeline controller: plan 2 (hard +, error +50 %), backlog throttle, 2 trajectory workers, stop rules, summaries every 50; budget reserve

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
Kral
2026-10-05 13:35:54 +02:00
parent 1c4667a0fc
commit 13360292dd
781 changed files with 50211 additions and 22 deletions

View File

@@ -0,0 +1,77 @@
CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_licence_merge.
PRIVATE SECTION.
TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence
WITH UNIQUE KEY product cost_centre.
METHODS add_rows
IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences
CHANGING ct_merged TYPE tt_merged.
METHODS normalize
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence.
METHODS is_relevant
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS trim_upper
IMPORTING iv_text TYPE string
RETURNING VALUE(rv_text) TYPE string.
ENDCLASS.
CLASS {{p}}licence_merger IMPLEMENTATION.
METHOD {{p}}if_licence_merge~merge.
DATA lt_merged TYPE tt_merged.
add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ).
add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ).
rt_merged = lt_merged.
SORT rt_merged BY product cost_centre.
ENDMETHOD.
METHOD add_rows.
LOOP AT it_rows INTO DATA(ls_row).
DATA(ls_norm) = normalize( ls_row ).
IF is_relevant( ls_norm ) <> abap_false.
CONTINUE.
ENDIF.
READ TABLE ct_merged ASSIGNING FIELD-SYMBOL(<ls_merged>)
WITH TABLE KEY product = ls_norm-product
cost_centre = ls_norm-cost_centre.
IF sy-subrc = 0.
<ls_merged>-seats = <ls_merged>-seats + ls_norm-seats.
IF ls_norm-unit_price > <ls_merged>-unit_price.
<ls_merged>-unit_price = ls_norm-unit_price.
ENDIF.
ELSE.
INSERT ls_norm INTO TABLE ct_merged.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD normalize.
rs_row = is_row.
rs_row-product = trim_upper( CONV string( is_row-product ) ).
rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ).
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_row-product IS NOT INITIAL
AND is_row-cost_centre IS NOT INITIAL
AND is_row-seats >= 1 ).
ENDMETHOD.
METHOD trim_upper.
DATA(lv_text) = iv_text.
SHIFT lv_text LEFT DELETING LEADING space.
SHIFT lv_text RIGHT DELETING TRAILING space.
rv_text = to_upper( lv_text ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,77 @@
CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_licence_merge.
PRIVATE SECTION.
TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence
WITH UNIQUE KEY product cost_centre.
METHODS add_rows
IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences
CHANGING ct_merged TYPE tt_merged.
METHODS normalize
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence.
METHODS is_relevant
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS trim_upper
IMPORTING iv_text TYPE string
RETURNING VALUE(rv_text) TYPE string.
ENDCLASS.
CLASS {{p}}licence_merger IMPLEMENTATION.
METHOD {{p}}if_licence_merge~merge.
DATA lt_merged TYPE tt_merged.
add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ).
add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ).
rt_merged = lt_merged.
SORT rt_merged BY product cost_centre.
ENDMETHOD.
METHOD add_rows.
LOOP AT it_rows INTO DATA(ls_row).
DATA(ls_norm) = normalize( ls_row ).
IF is_relevant( ls_norm ) = abap_false.
CONTINUE.
ENDIF.
READ TABLE ct_merged ASSIGNING FIELD-SYMBOL(<ls_merged>)
WITH TABLE KEY product = ls_norm-product
cost_centre = ls_norm-cost_centre.
IF sy-subrc = 0.
<ls_merged>-seats = <ls_merged>-seats - ls_norm-seats.
IF ls_norm-unit_price > <ls_merged>-unit_price.
<ls_merged>-unit_price = ls_norm-unit_price.
ENDIF.
ELSE.
INSERT ls_norm INTO TABLE ct_merged.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD normalize.
rs_row = is_row.
rs_row-product = trim_upper( CONV string( is_row-product ) ).
rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ).
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_row-product IS NOT INITIAL
AND is_row-cost_centre IS NOT INITIAL
AND is_row-seats >= 1 ).
ENDMETHOD.
METHOD trim_upper.
DATA(lv_text) = iv_text.
SHIFT lv_text LEFT DELETING LEADING space.
SHIFT lv_text RIGHT DELETING TRAILING space.
rv_text = to_upper( lv_text ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,77 @@
CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_licence_merge.
PRIVATE SECTION.
TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence
WITH UNIQUE KEY product cost_centre.
METHODS add_rows
IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences
CHANGING ct_merged TYPE tt_merged.
METHODS normalize
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence.
METHODS is_relevant
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS trim_upper
IMPORTING iv_text TYPE string
RETURNING VALUE(rv_text) TYPE string.
ENDCLASS.
CLASS {{p}}licence_merger IMPLEMENTATION.
METHOD {{p}}if_licence_merge~merge.
DATA lt_merged TYPE tt_merged.
add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ).
add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ).
rt_merged = lt_merged.
SORT rt_merged BY product cost_centre.
ENDMETHOD.
METHOD add_rows.
LOOP AT it_rows INTO DATA(ls_row).
DATA(ls_norm) = normalize( ls_row ).
IF is_relevant( ls_norm ) = abap_false.
CONTINUE.
ENDIF.
READ TABLE ct_merged ASSIGNING FIELD-SYMBOL(<ls_merged>)
WITH TABLE KEY product = ls_norm-product
cost_centre = ls_norm-cost_centre.
IF sy-subrc = 0.
<ls_merged>-seats = <ls_merged>-seats + ls_norm-seats.
IF ls_norm-unit_price <= <ls_merged>-unit_price.
<ls_merged>-unit_price = ls_norm-unit_price.
ENDIF.
ELSE.
INSERT ls_norm INTO TABLE ct_merged.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD normalize.
rs_row = is_row.
rs_row-product = trim_upper( CONV string( is_row-product ) ).
rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ).
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_row-product IS NOT INITIAL
AND is_row-cost_centre IS NOT INITIAL
AND is_row-seats >= 1 ).
ENDMETHOD.
METHOD trim_upper.
DATA(lv_text) = iv_text.
SHIFT lv_text LEFT DELETING LEADING space.
SHIFT lv_text RIGHT DELETING TRAILING space.
rv_text = to_upper( lv_text ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,77 @@
CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_licence_merge.
PRIVATE SECTION.
TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence
WITH UNIQUE KEY product cost_centre.
METHODS add_rows
IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences
CHANGING ct_merged TYPE tt_merged.
METHODS normalize
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence.
METHODS is_relevant
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS trim_upper
IMPORTING iv_text TYPE string
RETURNING VALUE(rv_text) TYPE string.
ENDCLASS.
CLASS {{p}}licence_merger IMPLEMENTATION.
METHOD {{p}}if_licence_merge~merge.
DATA lt_merged TYPE tt_merged.
add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ).
add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ).
rt_merged = lt_merged.
SORT rt_merged BY product cost_centre.
ENDMETHOD.
METHOD add_rows.
LOOP AT it_rows INTO DATA(ls_row).
DATA(ls_norm) = normalize( ls_row ).
IF is_relevant( ls_norm ) = abap_false.
CONTINUE.
ENDIF.
READ TABLE ct_merged ASSIGNING FIELD-SYMBOL(<ls_merged>)
WITH TABLE KEY product = ls_norm-product
cost_centre = ls_norm-cost_centre.
IF sy-subrc = 0.
<ls_merged>-seats = <ls_merged>-seats + ls_norm-seats.
IF ls_norm-unit_price > <ls_merged>-unit_price.
<ls_merged>-unit_price = ls_norm-unit_price.
ENDIF.
ELSE.
INSERT ls_norm INTO TABLE ct_merged.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD normalize.
rs_row = is_row.
rs_row-product = trim_upper( CONV string( is_row-product ) ).
rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ).
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_row-product IS NOT INITIAL
OR is_row-cost_centre IS NOT INITIAL
AND is_row-seats >= 1 ).
ENDMETHOD.
METHOD trim_upper.
DATA(lv_text) = iv_text.
SHIFT lv_text LEFT DELETING LEADING space.
SHIFT lv_text RIGHT DELETING TRAILING space.
rv_text = to_upper( lv_text ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,77 @@
CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_licence_merge.
PRIVATE SECTION.
TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence
WITH UNIQUE KEY product cost_centre.
METHODS add_rows
IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences
CHANGING ct_merged TYPE tt_merged.
METHODS normalize
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence.
METHODS is_relevant
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS trim_upper
IMPORTING iv_text TYPE string
RETURNING VALUE(rv_text) TYPE string.
ENDCLASS.
CLASS {{p}}licence_merger IMPLEMENTATION.
METHOD {{p}}if_licence_merge~merge.
DATA lt_merged TYPE tt_merged.
add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ).
add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ).
rt_merged = lt_merged.
SORT rt_merged BY product cost_centre.
ENDMETHOD.
METHOD add_rows.
LOOP AT it_rows INTO DATA(ls_row).
DATA(ls_norm) = normalize( ls_row ).
IF is_relevant( ls_norm ) = abap_false.
CONTINUE.
ENDIF.
READ TABLE ct_merged ASSIGNING FIELD-SYMBOL(<ls_merged>)
WITH TABLE KEY product = ls_norm-product
cost_centre = ls_norm-cost_centre.
IF sy-subrc = 0.
<ls_merged>-seats = <ls_merged>-seats + ls_norm-seats.
IF ls_norm-unit_price > <ls_merged>-unit_price.
<ls_merged>-unit_price = ls_norm-unit_price.
ENDIF.
ELSE.
INSERT ls_norm INTO TABLE ct_merged.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD normalize.
rs_row = is_row.
rs_row-product = trim_upper( CONV string( is_row-product ) ).
rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ).
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_row-product IS NOT INITIAL
AND is_row-cost_centre IS NOT INITIAL
AND is_row-seats < 1 ).
ENDMETHOD.
METHOD trim_upper.
DATA(lv_text) = iv_text.
SHIFT lv_text LEFT DELETING LEADING space.
SHIFT lv_text RIGHT DELETING TRAILING space.
rv_text = to_upper( lv_text ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,29 @@
{
"id": "G1029",
"pool": "train",
"object_type": "CLAS",
"category": "C",
"attempts": [
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 0,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 5,
"killed": 5,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -0,0 +1,197 @@
CLASS {{p}}t_lic_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}if_licence_merge.
METHODS setup.
METHODS merges_both_lists FOR TESTING.
METHODS sums_duplicates FOR TESTING.
METHODS ignores_zero_seats FOR TESTING.
METHODS ignores_negative_seats FOR TESTING.
METHODS ignores_blank_keys FOR TESTING.
METHODS normalizes_keys FOR TESTING.
METHODS sorts_result FOR TESTING.
METHODS keeps_secondary_only FOR TESTING.
METHODS empty_lists FOR TESTING.
METHODS takes_highest_price FOR TESTING.
ENDCLASS.
CLASS {{p}}t_lic_hidden IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}licence_merger( ).
ENDMETHOD.
METHOD merges_both_lists.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 5 unit_price = '100.00' )
( product = 'BETA' cost_centre = '2000' seats = 3 unit_price = '50.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 2 unit_price = '120.00' )
( product = 'GAMMA' cost_centre = '3000' seats = 4 unit_price = '10.00' ) ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = '1000' act = ls_row-cost_centre ).
cl_abap_unit_assert=>assert_equals( exp = 7 act = ls_row-seats ).
cl_abap_unit_assert=>assert_equals(
exp = CONV {{p}}if_licence_merge=>ty_price( '120.00' )
act = ls_row-unit_price ).
ls_row = lt_act[ 3 ].
cl_abap_unit_assert=>assert_equals( exp = 'GAMMA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_row-seats ).
ENDMETHOD.
METHOD sums_duplicates.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 5 unit_price = '100.00' )
( product = 'ALPHA' cost_centre = '1000' seats = 3 unit_price = '90.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 8 act = ls_row-seats ).
cl_abap_unit_assert=>assert_equals(
exp = CONV {{p}}if_licence_merge=>ty_price( '100.00' )
act = ls_row-unit_price ).
ENDMETHOD.
METHOD ignores_zero_seats.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 0 unit_price = '100.00' )
( product = 'BETA' cost_centre = '2000' seats = 4 unit_price = '10.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 0 unit_price = '50.00' ) ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = 4 act = ls_row-seats ).
ENDMETHOD.
METHOD ignores_negative_seats.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = -3 unit_price = '100.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'BETA' cost_centre = '2000' seats = 2 unit_price = '10.00' ) ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-seats ).
ENDMETHOD.
METHOD ignores_blank_keys.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = '' cost_centre = '1000' seats = 5 unit_price = '10.00' )
( product = 'ALPHA' cost_centre = '' seats = 5 unit_price = '10.00' )
( product = ' ' cost_centre = '2000' seats = 5 unit_price = '10.00' )
( product = 'BETA' cost_centre = '2000' seats = 1 unit_price = '2.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = '2000' act = ls_row-cost_centre ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = ls_row-seats ).
ENDMETHOD.
METHOD normalizes_keys.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'alpha' cost_centre = '1000' seats = 2 unit_price = '10.00' )
( product = ' beta ' cost_centre = '2000' seats = 1 unit_price = '5.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 3 unit_price = '20.00' ) ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = '1000' act = ls_row-cost_centre ).
cl_abap_unit_assert=>assert_equals( exp = 5 act = ls_row-seats ).
ls_row = lt_act[ 2 ].
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = '2000' act = ls_row-cost_centre ).
ENDMETHOD.
METHOD sorts_result.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'BETA' cost_centre = '2000' seats = 1 unit_price = '1.00' )
( product = 'ALPHA' cost_centre = '3000' seats = 1 unit_price = '1.00' )
( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '1.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = '1000' act = ls_row-cost_centre ).
ls_row = lt_act[ 2 ].
cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = '3000' act = ls_row-cost_centre ).
ls_row = lt_act[ 3 ].
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = '2000' act = ls_row-cost_centre ).
ENDMETHOD.
METHOD keeps_secondary_only.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '5.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'DELTA' cost_centre = '4000' seats = 2 unit_price = '7.00' ) ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 2 ].
cl_abap_unit_assert=>assert_equals( exp = 'DELTA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = '4000' act = ls_row-cost_centre ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-seats ).
ENDMETHOD.
METHOD empty_lists.
DATA(lt_empty) = VALUE {{p}}if_licence_merge=>tt_licences( ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_empty
it_secondary = lt_empty ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_act ) ).
ENDMETHOD.
METHOD takes_highest_price.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '80.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '95.50' ) ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-seats ).
cl_abap_unit_assert=>assert_equals(
exp = CONV {{p}}if_licence_merge=>ty_price( '95.50' )
act = ls_row-unit_price ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,77 @@
{
"task": "G1029",
"mutants": [
{
"object": "{{P}}LICENCE_MERGER",
"mutant": "line 41: = -> <> (eq)",
"status": "killed",
"hidden": "1/10",
"failed_tests": [
"IGNORES_BLANK_KEYS",
"IGNORES_NEGATIVE_SEATS",
"IGNORES_ZERO_SEATS",
"KEEPS_SECONDARY_ONLY",
"MERGES_BOTH_LISTS",
"NORMALIZES_KEYS",
"SORTS_RESULT",
"SUMS_DUPLICATES",
"TAKES_HIGHEST_PRICE"
]
},
{
"object": "{{P}}LICENCE_MERGER",
"mutant": "line 49: + -> - (arith)",
"status": "killed",
"hidden": "6/10",
"failed_tests": [
"MERGES_BOTH_LISTS",
"NORMALIZES_KEYS",
"SUMS_DUPLICATES",
"TAKES_HIGHEST_PRICE"
]
},
{
"object": "{{P}}LICENCE_MERGER",
"mutant": "line 50: > -> <= (rel)",
"status": "killed",
"hidden": "7/10",
"failed_tests": [
"MERGES_BOTH_LISTS",
"SUMS_DUPLICATES",
"TAKES_HIGHEST_PRICE"
]
},
{
"object": "{{P}}LICENCE_MERGER",
"mutant": "line 67: AND -> OR (logic)",
"status": "killed",
"hidden": "7/10",
"failed_tests": [
"IGNORES_BLANK_KEYS",
"IGNORES_NEGATIVE_SEATS",
"IGNORES_ZERO_SEATS"
]
},
{
"object": "{{P}}LICENCE_MERGER",
"mutant": "line 68: >= -> < (rel)",
"status": "killed",
"hidden": "1/10",
"failed_tests": [
"IGNORES_BLANK_KEYS",
"IGNORES_NEGATIVE_SEATS",
"IGNORES_ZERO_SEATS",
"KEEPS_SECONDARY_ONLY",
"MERGES_BOTH_LISTS",
"NORMALIZES_KEYS",
"SORTS_RESULT",
"SUMS_DUPLICATES",
"TAKES_HIGHEST_PRICE"
]
}
],
"valid": 5,
"killed": 5,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,77 @@
CLASS {{p}}licence_merger DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_licence_merge.
PRIVATE SECTION.
TYPES tt_merged TYPE HASHED TABLE OF {{p}}if_licence_merge=>ty_licence
WITH UNIQUE KEY product cost_centre.
METHODS add_rows
IMPORTING it_rows TYPE {{p}}if_licence_merge=>tt_licences
CHANGING ct_merged TYPE tt_merged.
METHODS normalize
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rs_row) TYPE {{p}}if_licence_merge=>ty_licence.
METHODS is_relevant
IMPORTING is_row TYPE {{p}}if_licence_merge=>ty_licence
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS trim_upper
IMPORTING iv_text TYPE string
RETURNING VALUE(rv_text) TYPE string.
ENDCLASS.
CLASS {{p}}licence_merger IMPLEMENTATION.
METHOD {{p}}if_licence_merge~merge.
DATA lt_merged TYPE tt_merged.
add_rows( EXPORTING it_rows = it_primary CHANGING ct_merged = lt_merged ).
add_rows( EXPORTING it_rows = it_secondary CHANGING ct_merged = lt_merged ).
rt_merged = lt_merged.
SORT rt_merged BY product cost_centre.
ENDMETHOD.
METHOD add_rows.
LOOP AT it_rows INTO DATA(ls_row).
DATA(ls_norm) = normalize( ls_row ).
IF is_relevant( ls_norm ) = abap_false.
CONTINUE.
ENDIF.
READ TABLE ct_merged ASSIGNING FIELD-SYMBOL(<ls_merged>)
WITH TABLE KEY product = ls_norm-product
cost_centre = ls_norm-cost_centre.
IF sy-subrc = 0.
<ls_merged>-seats = <ls_merged>-seats + ls_norm-seats.
IF ls_norm-unit_price > <ls_merged>-unit_price.
<ls_merged>-unit_price = ls_norm-unit_price.
ENDIF.
ELSE.
INSERT ls_norm INTO TABLE ct_merged.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD normalize.
rs_row = is_row.
rs_row-product = trim_upper( CONV string( is_row-product ) ).
rs_row-cost_centre = trim_upper( CONV string( is_row-cost_centre ) ).
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_row-product IS NOT INITIAL
AND is_row-cost_centre IS NOT INITIAL
AND is_row-seats >= 1 ).
ENDMETHOD.
METHOD trim_upper.
DATA(lv_text) = iv_text.
SHIFT lv_text LEFT DELETING LEADING space.
SHIFT lv_text RIGHT DELETING TRAILING space.
rv_text = to_upper( lv_text ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,92 @@
CLASS ltc_licence_merger DEFINITION FINAL FOR TESTING
DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}if_licence_merge.
METHODS setup.
METHODS merges_two_lists FOR TESTING.
METHODS ignores_invalid_rows FOR TESTING.
METHODS normalizes_keys FOR TESTING.
METHODS sorts_the_result FOR TESTING.
METHODS empty_input FOR TESTING.
ENDCLASS.
CLASS ltc_licence_merger IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}licence_merger( ).
ENDMETHOD.
METHOD merges_two_lists.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 5 unit_price = '100.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 2 unit_price = '120.00' ) ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 7 act = ls_row-seats ).
cl_abap_unit_assert=>assert_equals(
exp = CONV {{p}}if_licence_merge=>ty_price( '120.00' )
act = ls_row-unit_price ).
ENDMETHOD.
METHOD ignores_invalid_rows.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = '' cost_centre = '1000' seats = 5 unit_price = '10.00' )
( product = 'ALPHA' cost_centre = '1000' seats = 0 unit_price = '10.00' )
( product = 'BETA' cost_centre = '2000' seats = 2 unit_price = '3.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ).
ENDMETHOD.
METHOD normalizes_keys.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = ' alpha ' cost_centre = '1000' seats = 1 unit_price = '2.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '2.00' ) ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = ls_row-seats ).
ENDMETHOD.
METHOD sorts_the_result.
DATA(lt_primary) = VALUE {{p}}if_licence_merge=>tt_licences(
( product = 'BETA' cost_centre = '2000' seats = 1 unit_price = '1.00' )
( product = 'ALPHA' cost_centre = '3000' seats = 1 unit_price = '1.00' )
( product = 'ALPHA' cost_centre = '1000' seats = 1 unit_price = '1.00' ) ).
DATA(lt_secondary) = VALUE {{p}}if_licence_merge=>tt_licences( ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_primary
it_secondary = lt_secondary ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_act ) ).
DATA(ls_row) = lt_act[ 1 ].
cl_abap_unit_assert=>assert_equals( exp = 'ALPHA' act = ls_row-product ).
cl_abap_unit_assert=>assert_equals( exp = '1000' act = ls_row-cost_centre ).
ls_row = lt_act[ 3 ].
cl_abap_unit_assert=>assert_equals( exp = 'BETA' act = ls_row-product ).
ENDMETHOD.
METHOD empty_input.
DATA(lt_empty) = VALUE {{p}}if_licence_merge=>tt_licences( ).
DATA(lt_act) = mo_cut->merge( it_primary = lt_empty
it_secondary = lt_empty ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_act ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,18 @@
INTERFACE {{p}}if_licence_merge PUBLIC.
TYPES ty_product TYPE c LENGTH 10.
TYPES ty_cost_centre TYPE c LENGTH 6.
TYPES ty_price TYPE p LENGTH 8 DECIMALS 2.
TYPES:
BEGIN OF ty_licence,
product TYPE ty_product,
cost_centre TYPE ty_cost_centre,
seats TYPE i,
unit_price TYPE ty_price,
END OF ty_licence.
TYPES tt_licences TYPE STANDARD TABLE OF ty_licence WITH EMPTY KEY.
METHODS merge
IMPORTING it_primary TYPE tt_licences
it_secondary TYPE tt_licences
RETURNING VALUE(rt_merged) TYPE tt_licences.
ENDINTERFACE.

View File

@@ -0,0 +1,56 @@
# 1. Goal
An IT asset manager consolidates the software licence records of two
subsidiaries. A reporting program calls one service that merges the two lists
into one list. The result has one row for each product and cost centre.
# 2. Open questions
None.
# 3. Context
- The interface {{P}}IF_LICENCE_MERGE exists in package $TMP. It is active.
- The interface has the type TY_PRODUCT (character, length 10), the type
TY_COST_CENTRE (character, length 6), and the type TY_PRICE (packed number,
length 8, 2 decimals).
- The interface has the structure type TY_LICENCE with these components:
PRODUCT (TY_PRODUCT), COST_CENTRE (TY_COST_CENTRE), SEATS (integer),
UNIT_PRICE (TY_PRICE).
- The interface has the table type TT_LICENCES. TT_LICENCES is a standard table
of TY_LICENCE.
- The interface has one method:
MERGE
IMPORTING it_primary TYPE tt_licences
it_secondary TYPE tt_licences
RETURNING VALUE(rt_merged) TYPE tt_licences.
# 4. Contract
- Create the class {{P}}LICENCE_MERGER in package $TMP.
- The class is public, final, and has a public constructor without parameters.
- The class implements {{P}}IF_LICENCE_MERGE.
- Do not add other public methods.
# 5. Business rules
The method MERGE reads the two input tables IT_PRIMARY and IT_SECONDARY.
1. Normalize the product and the cost centre of each input row: remove the
leading and the trailing blanks and convert all letters to upper case.
2. Ignore an input row if the normalized product is empty, or if the normalized
cost centre is empty, or if the seats are less than 1.
3. All remaining input rows with the same product and the same cost centre
become one result row. Rows of the same input table and rows of different
input tables are combined in the same way.
4. The seats of a result row are the sum of the seats of its input rows.
5. The unit price of a result row is the highest unit price of its input rows.
6. The result has one row for each pair of product and cost centre that has at
least one remaining input row.
7. Sort the result by product in ascending order. If two rows have the same
product, sort them by cost centre in ascending order.
# 6. Constraints
- Release target: ABAP Platform 2025 (SAP_BASIS 816).
- Coding standards: Clean ABAP. Method length below 40 statements. No global
variables. No comment that restates the code.
- Out of scope: do not change {{P}}IF_LICENCE_MERGE.
# 7. Acceptance
- The class is active and has no syntax error.
- The hidden tests pass.
- Write your own ABAP Unit tests for the class.

View File

@@ -0,0 +1,52 @@
{
"id": "G1029",
"category": "C",
"object_type": "CLAS",
"difficulty": 2,
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 60,
"max_activations": 15
},
"seed": [
{
"type": "INTF",
"name": "{{P}}IF_LICENCE_MERGE",
"file": "seed/if_licence_merge.intf.abap",
"description": "Licence merge contract: types and method signature"
}
],
"contract": [
{
"type": "CLAS",
"name": "{{P}}LICENCE_MERGER",
"implements": "{{P}}IF_LICENCE_MERGE"
}
],
"out_of_scope": [
"{{P}}IF_LICENCE_MERGE"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T_LIC_HIDDEN",
"file": "hidden/t_lic_hidden.clas.abap",
"description": "Hidden tests for the licence merger"
}
],
"reference": [
{
"type": "CLAS",
"name": "{{P}}LICENCE_MERGER",
"file": "reference/licence_merger.clas.abap",
"description": "Licence merger reference solution",
"testclasses_file": "reference/licence_merger.testclasses.abap"
}
],
"craft_checks": [
"method_length",
"nested_loops",
"linear_search"
]
}