Fix coverage with several contract classes; table annotation and file path checks; clean task dir on write

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014aUaQeLnwbb1zTpN7kHeat
This commit is contained in:
Kral
2026-10-03 08:44:35 +02:00
parent fb576b0954
commit 03ab36149e
388 changed files with 28620 additions and 339 deletions

View File

@@ -0,0 +1,74 @@
CLASS {{p}}meter_consumption DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_meter_reading.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_meter,
meter_id TYPE {{p}}if_meter_reading=>ty_meter_id,
opening_date TYPE {{p}}if_meter_reading=>ty_reading_date,
opening_value TYPE {{p}}if_meter_reading=>ty_value,
closing_date TYPE {{p}}if_meter_reading=>ty_reading_date,
closing_value TYPE {{p}}if_meter_reading=>ty_value,
END OF ty_meter,
tt_meter TYPE HASHED TABLE OF ty_meter WITH UNIQUE KEY meter_id.
METHODS collect
IMPORTING it_readings TYPE {{p}}if_meter_reading=>tt_readings
RETURNING VALUE(rt_meter) TYPE tt_meter.
METHODS to_result
IMPORTING it_meter TYPE tt_meter
RETURNING VALUE(rt_result) TYPE {{p}}if_meter_reading=>tt_result.
ENDCLASS.
CLASS {{p}}meter_consumption IMPLEMENTATION.
METHOD {{p}}if_meter_reading~calculate.
rt_result = to_result( collect( it_readings ) ).
ENDMETHOD.
METHOD collect.
LOOP AT it_readings INTO DATA(ls_reading).
IF ls_reading-reading_value >= 0.
CONTINUE.
ENDIF.
READ TABLE rt_meter ASSIGNING FIELD-SYMBOL(<ls_meter>)
WITH TABLE KEY meter_id = ls_reading-meter_id.
IF sy-subrc <> 0.
INSERT VALUE #( meter_id = ls_reading-meter_id
opening_date = ls_reading-reading_date
opening_value = ls_reading-reading_value
closing_date = ls_reading-reading_date
closing_value = ls_reading-reading_value )
INTO TABLE rt_meter ASSIGNING <ls_meter>.
ENDIF.
IF ls_reading-reading_date < <ls_meter>-opening_date
OR ( ls_reading-reading_date = <ls_meter>-opening_date
AND ls_reading-reading_value > <ls_meter>-opening_value ).
<ls_meter>-opening_date = ls_reading-reading_date.
<ls_meter>-opening_value = ls_reading-reading_value.
ENDIF.
IF ls_reading-reading_date > <ls_meter>-closing_date
OR ( ls_reading-reading_date = <ls_meter>-closing_date
AND ls_reading-reading_value > <ls_meter>-closing_value ).
<ls_meter>-closing_date = ls_reading-reading_date.
<ls_meter>-closing_value = ls_reading-reading_value.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD to_result.
LOOP AT it_meter ASSIGNING FIELD-SYMBOL(<ls_meter>).
DATA(lv_consumption) = <ls_meter>-closing_value - <ls_meter>-opening_value.
IF lv_consumption < 0.
lv_consumption = 0.
ENDIF.
INSERT VALUE #( meter_id = <ls_meter>-meter_id
opening_value = <ls_meter>-opening_value
closing_value = <ls_meter>-closing_value
consumption = lv_consumption )
INTO TABLE rt_result.
ENDLOOP.
SORT rt_result BY meter_id.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,74 @@
CLASS {{p}}meter_consumption DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_meter_reading.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_meter,
meter_id TYPE {{p}}if_meter_reading=>ty_meter_id,
opening_date TYPE {{p}}if_meter_reading=>ty_reading_date,
opening_value TYPE {{p}}if_meter_reading=>ty_value,
closing_date TYPE {{p}}if_meter_reading=>ty_reading_date,
closing_value TYPE {{p}}if_meter_reading=>ty_value,
END OF ty_meter,
tt_meter TYPE HASHED TABLE OF ty_meter WITH UNIQUE KEY meter_id.
METHODS collect
IMPORTING it_readings TYPE {{p}}if_meter_reading=>tt_readings
RETURNING VALUE(rt_meter) TYPE tt_meter.
METHODS to_result
IMPORTING it_meter TYPE tt_meter
RETURNING VALUE(rt_result) TYPE {{p}}if_meter_reading=>tt_result.
ENDCLASS.
CLASS {{p}}meter_consumption IMPLEMENTATION.
METHOD {{p}}if_meter_reading~calculate.
rt_result = to_result( collect( it_readings ) ).
ENDMETHOD.
METHOD collect.
LOOP AT it_readings INTO DATA(ls_reading).
IF ls_reading-reading_value < 0.
CONTINUE.
ENDIF.
READ TABLE rt_meter ASSIGNING FIELD-SYMBOL(<ls_meter>)
WITH TABLE KEY meter_id = ls_reading-meter_id.
IF sy-subrc <> 0.
INSERT VALUE #( meter_id = ls_reading-meter_id
opening_date = ls_reading-reading_date
opening_value = ls_reading-reading_value
closing_date = ls_reading-reading_date
closing_value = ls_reading-reading_value )
INTO TABLE rt_meter ASSIGNING <ls_meter>.
ENDIF.
IF ls_reading-reading_date < <ls_meter>-opening_date
OR ( ls_reading-reading_date <> <ls_meter>-opening_date
AND ls_reading-reading_value > <ls_meter>-opening_value ).
<ls_meter>-opening_date = ls_reading-reading_date.
<ls_meter>-opening_value = ls_reading-reading_value.
ENDIF.
IF ls_reading-reading_date > <ls_meter>-closing_date
OR ( ls_reading-reading_date = <ls_meter>-closing_date
AND ls_reading-reading_value > <ls_meter>-closing_value ).
<ls_meter>-closing_date = ls_reading-reading_date.
<ls_meter>-closing_value = ls_reading-reading_value.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD to_result.
LOOP AT it_meter ASSIGNING FIELD-SYMBOL(<ls_meter>).
DATA(lv_consumption) = <ls_meter>-closing_value - <ls_meter>-opening_value.
IF lv_consumption < 0.
lv_consumption = 0.
ENDIF.
INSERT VALUE #( meter_id = <ls_meter>-meter_id
opening_value = <ls_meter>-opening_value
closing_value = <ls_meter>-closing_value
consumption = lv_consumption )
INTO TABLE rt_result.
ENDLOOP.
SORT rt_result BY meter_id.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,74 @@
CLASS {{p}}meter_consumption DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_meter_reading.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_meter,
meter_id TYPE {{p}}if_meter_reading=>ty_meter_id,
opening_date TYPE {{p}}if_meter_reading=>ty_reading_date,
opening_value TYPE {{p}}if_meter_reading=>ty_value,
closing_date TYPE {{p}}if_meter_reading=>ty_reading_date,
closing_value TYPE {{p}}if_meter_reading=>ty_value,
END OF ty_meter,
tt_meter TYPE HASHED TABLE OF ty_meter WITH UNIQUE KEY meter_id.
METHODS collect
IMPORTING it_readings TYPE {{p}}if_meter_reading=>tt_readings
RETURNING VALUE(rt_meter) TYPE tt_meter.
METHODS to_result
IMPORTING it_meter TYPE tt_meter
RETURNING VALUE(rt_result) TYPE {{p}}if_meter_reading=>tt_result.
ENDCLASS.
CLASS {{p}}meter_consumption IMPLEMENTATION.
METHOD {{p}}if_meter_reading~calculate.
rt_result = to_result( collect( it_readings ) ).
ENDMETHOD.
METHOD collect.
LOOP AT it_readings INTO DATA(ls_reading).
IF ls_reading-reading_value < 0.
CONTINUE.
ENDIF.
READ TABLE rt_meter ASSIGNING FIELD-SYMBOL(<ls_meter>)
WITH TABLE KEY meter_id = ls_reading-meter_id.
IF sy-subrc <> 0.
INSERT VALUE #( meter_id = ls_reading-meter_id
opening_date = ls_reading-reading_date
opening_value = ls_reading-reading_value
closing_date = ls_reading-reading_date
closing_value = ls_reading-reading_value )
INTO TABLE rt_meter ASSIGNING <ls_meter>.
ENDIF.
IF ls_reading-reading_date < <ls_meter>-opening_date
OR ( ls_reading-reading_date = <ls_meter>-opening_date
AND ls_reading-reading_value > <ls_meter>-opening_value ).
<ls_meter>-opening_date = ls_reading-reading_date.
<ls_meter>-opening_value = ls_reading-reading_value.
ENDIF.
IF ls_reading-reading_date > <ls_meter>-closing_date
AND ( ls_reading-reading_date = <ls_meter>-closing_date
AND ls_reading-reading_value > <ls_meter>-closing_value ).
<ls_meter>-closing_date = ls_reading-reading_date.
<ls_meter>-closing_value = ls_reading-reading_value.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD to_result.
LOOP AT it_meter ASSIGNING FIELD-SYMBOL(<ls_meter>).
DATA(lv_consumption) = <ls_meter>-closing_value - <ls_meter>-opening_value.
IF lv_consumption < 0.
lv_consumption = 0.
ENDIF.
INSERT VALUE #( meter_id = <ls_meter>-meter_id
opening_value = <ls_meter>-opening_value
closing_value = <ls_meter>-closing_value
consumption = lv_consumption )
INTO TABLE rt_result.
ENDLOOP.
SORT rt_result BY meter_id.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,74 @@
CLASS {{p}}meter_consumption DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_meter_reading.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_meter,
meter_id TYPE {{p}}if_meter_reading=>ty_meter_id,
opening_date TYPE {{p}}if_meter_reading=>ty_reading_date,
opening_value TYPE {{p}}if_meter_reading=>ty_value,
closing_date TYPE {{p}}if_meter_reading=>ty_reading_date,
closing_value TYPE {{p}}if_meter_reading=>ty_value,
END OF ty_meter,
tt_meter TYPE HASHED TABLE OF ty_meter WITH UNIQUE KEY meter_id.
METHODS collect
IMPORTING it_readings TYPE {{p}}if_meter_reading=>tt_readings
RETURNING VALUE(rt_meter) TYPE tt_meter.
METHODS to_result
IMPORTING it_meter TYPE tt_meter
RETURNING VALUE(rt_result) TYPE {{p}}if_meter_reading=>tt_result.
ENDCLASS.
CLASS {{p}}meter_consumption IMPLEMENTATION.
METHOD {{p}}if_meter_reading~calculate.
rt_result = to_result( collect( it_readings ) ).
ENDMETHOD.
METHOD collect.
LOOP AT it_readings INTO DATA(ls_reading).
IF ls_reading-reading_value < 0.
CONTINUE.
ENDIF.
READ TABLE rt_meter ASSIGNING FIELD-SYMBOL(<ls_meter>)
WITH TABLE KEY meter_id = ls_reading-meter_id.
IF sy-subrc <> 0.
INSERT VALUE #( meter_id = ls_reading-meter_id
opening_date = ls_reading-reading_date
opening_value = ls_reading-reading_value
closing_date = ls_reading-reading_date
closing_value = ls_reading-reading_value )
INTO TABLE rt_meter ASSIGNING <ls_meter>.
ENDIF.
IF ls_reading-reading_date < <ls_meter>-opening_date
OR ( ls_reading-reading_date = <ls_meter>-opening_date
AND ls_reading-reading_value > <ls_meter>-opening_value ).
<ls_meter>-opening_date = ls_reading-reading_date.
<ls_meter>-opening_value = ls_reading-reading_value.
ENDIF.
IF ls_reading-reading_date > <ls_meter>-closing_date
OR ( ls_reading-reading_date = <ls_meter>-closing_date
AND ls_reading-reading_value <= <ls_meter>-closing_value ).
<ls_meter>-closing_date = ls_reading-reading_date.
<ls_meter>-closing_value = ls_reading-reading_value.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD to_result.
LOOP AT it_meter ASSIGNING FIELD-SYMBOL(<ls_meter>).
DATA(lv_consumption) = <ls_meter>-closing_value - <ls_meter>-opening_value.
IF lv_consumption < 0.
lv_consumption = 0.
ENDIF.
INSERT VALUE #( meter_id = <ls_meter>-meter_id
opening_value = <ls_meter>-opening_value
closing_value = <ls_meter>-closing_value
consumption = lv_consumption )
INTO TABLE rt_result.
ENDLOOP.
SORT rt_result BY meter_id.
ENDMETHOD.
ENDCLASS.

View File

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

View File

@@ -0,0 +1,151 @@
CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}if_meter_reading.
METHODS setup.
METHODS assert_row
IMPORTING iv_index TYPE i
iv_meter_id TYPE {{p}}if_meter_reading=>ty_meter_id
iv_opening TYPE {{p}}if_meter_reading=>ty_value
iv_closing TYPE {{p}}if_meter_reading=>ty_value
iv_consumption TYPE {{p}}if_meter_reading=>ty_value
it_result TYPE {{p}}if_meter_reading=>tt_result.
METHODS empty_input FOR TESTING.
METHODS single_reading FOR TESTING.
METHODS two_readings FOR TESTING.
METHODS negative_ignored FOR TESTING.
METHODS only_negative FOR TESTING.
METHODS zero_reading FOR TESTING.
METHODS duplicate_date FOR TESTING.
METHODS sorted_by_meter FOR TESTING.
METHODS rollover FOR TESTING.
METHODS date_order FOR TESTING.
METHODS several_meters FOR TESTING.
ENDCLASS.
CLASS {{p}}t02_hidden IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}meter_consumption( ).
ENDMETHOD.
METHOD assert_row.
DATA(ls_row) = it_result[ iv_index ].
cl_abap_unit_assert=>assert_equals( exp = iv_meter_id act = ls_row-meter_id msg = 'meter id' ).
cl_abap_unit_assert=>assert_equals( exp = iv_opening act = ls_row-opening_value msg = 'opening' ).
cl_abap_unit_assert=>assert_equals( exp = iv_closing act = ls_row-closing_value msg = 'closing' ).
cl_abap_unit_assert=>assert_equals( exp = iv_consumption act = ls_row-consumption msg = 'consumption' ).
ENDMETHOD.
METHOD empty_input.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ).
ENDMETHOD.
METHOD single_reading.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 100 iv_closing = 100
iv_consumption = 0 it_result = lt_result ).
ENDMETHOD.
METHOD two_readings.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 )
( meter_id = 'M0001' reading_date = '20240210' reading_value = 175 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 100 iv_closing = 175
iv_consumption = 75 it_result = lt_result ).
ENDMETHOD.
METHOD negative_ignored.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 )
( meter_id = 'M0001' reading_date = '20240115' reading_value = -3 )
( meter_id = 'M0001' reading_date = '20240210' reading_value = 200 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 100 iv_closing = 200
iv_consumption = 100 it_result = lt_result ).
ENDMETHOD.
METHOD only_negative.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = -1 )
( meter_id = 'M0002' reading_date = '20240110' reading_value = -5 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lines( lt_result ) ).
ENDMETHOD.
METHOD zero_reading.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 0 )
( meter_id = 'M0001' reading_date = '20240210' reading_value = 50 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 0 iv_closing = 50
iv_consumption = 50 it_result = lt_result ).
ENDMETHOD.
METHOD duplicate_date.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 120 )
( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 )
( meter_id = 'M0001' reading_date = '20240210' reading_value = 180 )
( meter_id = 'M0001' reading_date = '20240210' reading_value = 200 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 120 iv_closing = 200
iv_consumption = 80 it_result = lt_result ).
ENDMETHOD.
METHOD sorted_by_meter.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0003' reading_date = '20240110' reading_value = 30 )
( meter_id = 'M0001' reading_date = '20240110' reading_value = 10 )
( meter_id = 'M0002' reading_date = '20240110' reading_value = 20 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 'M0001' act = lt_result[ 1 ]-meter_id ).
cl_abap_unit_assert=>assert_equals( exp = 'M0002' act = lt_result[ 2 ]-meter_id ).
cl_abap_unit_assert=>assert_equals( exp = 'M0003' act = lt_result[ 3 ]-meter_id ).
ENDMETHOD.
METHOD rollover.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 500 )
( meter_id = 'M0001' reading_date = '20240210' reading_value = 300 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 500 iv_closing = 300
iv_consumption = 0 it_result = lt_result ).
ENDMETHOD.
METHOD date_order.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240210' reading_value = 200 )
( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 100 iv_closing = 200
iv_consumption = 100 it_result = lt_result ).
ENDMETHOD.
METHOD several_meters.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 )
( meter_id = 'M0001' reading_date = '20240310' reading_value = 250 )
( meter_id = 'M0002' reading_date = '20240105' reading_value = 40 )
( meter_id = 'M0002' reading_date = '20240205' reading_value = 55 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ).
assert_row( iv_index = 1 iv_meter_id = 'M0001' iv_opening = 100 iv_closing = 250
iv_consumption = 150 it_result = lt_result ).
assert_row( iv_index = 2 iv_meter_id = 'M0002' iv_opening = 40 iv_closing = 55
iv_consumption = 15 it_result = lt_result ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,70 @@
{
"task": "G0120",
"mutants": [
{
"object": "{{P}}METER_CONSUMPTION",
"mutant": "line 32: < -> >= (rel)",
"status": "killed",
"hidden": "1/11",
"failed_tests": [
"DATE_ORDER",
"DUPLICATE_DATE",
"NEGATIVE_IGNORED",
"ONLY_NEGATIVE",
"ROLLOVER",
"SEVERAL_METERS",
"SINGLE_READING",
"SORTED_BY_METER",
"TWO_READINGS",
"ZERO_READING"
]
},
{
"object": "{{P}}METER_CONSUMPTION",
"mutant": "line 46: = -> <> (eq)",
"status": "killed",
"hidden": "6/11",
"failed_tests": [
"DUPLICATE_DATE",
"NEGATIVE_IGNORED",
"SEVERAL_METERS",
"TWO_READINGS",
"ZERO_READING"
]
},
{
"object": "{{P}}METER_CONSUMPTION",
"mutant": "line 52: OR -> AND (logic)",
"status": "killed",
"hidden": "5/11",
"failed_tests": [
"DUPLICATE_DATE",
"NEGATIVE_IGNORED",
"ROLLOVER",
"SEVERAL_METERS",
"TWO_READINGS",
"ZERO_READING"
]
},
{
"object": "{{P}}METER_CONSUMPTION",
"mutant": "line 53: AND -> OR (logic)",
"status": "survived",
"hidden": "11/11",
"failed_tests": []
},
{
"object": "{{P}}METER_CONSUMPTION",
"mutant": "line 53: > -> <= (rel)",
"status": "killed",
"hidden": "10/11",
"failed_tests": [
"DUPLICATE_DATE"
]
}
],
"valid": 5,
"killed": 4,
"kill_rate": 0.8,
"ok": true
}

View File

@@ -0,0 +1,74 @@
CLASS {{p}}meter_consumption DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_meter_reading.
PRIVATE SECTION.
TYPES:
BEGIN OF ty_meter,
meter_id TYPE {{p}}if_meter_reading=>ty_meter_id,
opening_date TYPE {{p}}if_meter_reading=>ty_reading_date,
opening_value TYPE {{p}}if_meter_reading=>ty_value,
closing_date TYPE {{p}}if_meter_reading=>ty_reading_date,
closing_value TYPE {{p}}if_meter_reading=>ty_value,
END OF ty_meter,
tt_meter TYPE HASHED TABLE OF ty_meter WITH UNIQUE KEY meter_id.
METHODS collect
IMPORTING it_readings TYPE {{p}}if_meter_reading=>tt_readings
RETURNING VALUE(rt_meter) TYPE tt_meter.
METHODS to_result
IMPORTING it_meter TYPE tt_meter
RETURNING VALUE(rt_result) TYPE {{p}}if_meter_reading=>tt_result.
ENDCLASS.
CLASS {{p}}meter_consumption IMPLEMENTATION.
METHOD {{p}}if_meter_reading~calculate.
rt_result = to_result( collect( it_readings ) ).
ENDMETHOD.
METHOD collect.
LOOP AT it_readings INTO DATA(ls_reading).
IF ls_reading-reading_value < 0.
CONTINUE.
ENDIF.
READ TABLE rt_meter ASSIGNING FIELD-SYMBOL(<ls_meter>)
WITH TABLE KEY meter_id = ls_reading-meter_id.
IF sy-subrc <> 0.
INSERT VALUE #( meter_id = ls_reading-meter_id
opening_date = ls_reading-reading_date
opening_value = ls_reading-reading_value
closing_date = ls_reading-reading_date
closing_value = ls_reading-reading_value )
INTO TABLE rt_meter ASSIGNING <ls_meter>.
ENDIF.
IF ls_reading-reading_date < <ls_meter>-opening_date
OR ( ls_reading-reading_date = <ls_meter>-opening_date
AND ls_reading-reading_value > <ls_meter>-opening_value ).
<ls_meter>-opening_date = ls_reading-reading_date.
<ls_meter>-opening_value = ls_reading-reading_value.
ENDIF.
IF ls_reading-reading_date > <ls_meter>-closing_date
OR ( ls_reading-reading_date = <ls_meter>-closing_date
AND ls_reading-reading_value > <ls_meter>-closing_value ).
<ls_meter>-closing_date = ls_reading-reading_date.
<ls_meter>-closing_value = ls_reading-reading_value.
ENDIF.
ENDLOOP.
ENDMETHOD.
METHOD to_result.
LOOP AT it_meter ASSIGNING FIELD-SYMBOL(<ls_meter>).
DATA(lv_consumption) = <ls_meter>-closing_value - <ls_meter>-opening_value.
IF lv_consumption < 0.
lv_consumption = 0.
ENDIF.
INSERT VALUE #( meter_id = <ls_meter>-meter_id
opening_value = <ls_meter>-opening_value
closing_value = <ls_meter>-closing_value
consumption = lv_consumption )
INTO TABLE rt_result.
ENDLOOP.
SORT rt_result BY meter_id.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,75 @@
CLASS ltc_meter_consumption DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}if_meter_reading.
METHODS setup.
METHODS two_readings FOR TESTING.
METHODS empty_input FOR TESTING.
METHODS negative_ignored FOR TESTING.
METHODS duplicate_date FOR TESTING.
METHODS rollover_is_zero FOR TESTING.
METHODS sorted_result FOR TESTING.
ENDCLASS.
CLASS ltc_meter_consumption IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}meter_consumption( ).
ENDMETHOD.
METHOD two_readings.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 )
( meter_id = 'M0001' reading_date = '20240210' reading_value = 175 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 100 act = lt_result[ 1 ]-opening_value ).
cl_abap_unit_assert=>assert_equals( exp = 175 act = lt_result[ 1 ]-closing_value ).
cl_abap_unit_assert=>assert_equals( exp = 75 act = lt_result[ 1 ]-consumption ).
ENDMETHOD.
METHOD empty_input.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_initial( act = lt_result ).
ENDMETHOD.
METHOD negative_ignored.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 100 )
( meter_id = 'M0001' reading_date = '20240120' reading_value = -7 )
( meter_id = 'M0001' reading_date = '20240210' reading_value = 200 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 100 act = lt_result[ 1 ]-consumption ).
ENDMETHOD.
METHOD duplicate_date.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 130 )
( meter_id = 'M0001' reading_date = '20240110' reading_value = 110 )
( meter_id = 'M0001' reading_date = '20240210' reading_value = 210 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 130 act = lt_result[ 1 ]-opening_value ).
cl_abap_unit_assert=>assert_equals( exp = 80 act = lt_result[ 1 ]-consumption ).
ENDMETHOD.
METHOD rollover_is_zero.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0001' reading_date = '20240110' reading_value = 900 )
( meter_id = 'M0001' reading_date = '20240210' reading_value = 100 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 0 act = lt_result[ 1 ]-consumption ).
ENDMETHOD.
METHOD sorted_result.
DATA lt_readings TYPE {{p}}if_meter_reading=>tt_readings.
lt_readings = VALUE #( ( meter_id = 'M0002' reading_date = '20240110' reading_value = 20 )
( meter_id = 'M0001' reading_date = '20240110' reading_value = 10 ) ).
DATA(lt_result) = mo_cut->calculate( lt_readings ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 'M0001' act = lt_result[ 1 ]-meter_id ).
cl_abap_unit_assert=>assert_equals( exp = 'M0002' act = lt_result[ 2 ]-meter_id ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,23 @@
INTERFACE {{p}}if_meter_reading PUBLIC.
TYPES ty_meter_id TYPE c LENGTH 10.
TYPES ty_reading_date TYPE d.
TYPES ty_value TYPE i.
TYPES:
BEGIN OF ty_reading,
meter_id TYPE ty_meter_id,
reading_date TYPE ty_reading_date,
reading_value TYPE ty_value,
END OF ty_reading.
TYPES tt_readings TYPE STANDARD TABLE OF ty_reading WITH DEFAULT KEY.
TYPES:
BEGIN OF ty_result,
meter_id TYPE ty_meter_id,
opening_value TYPE ty_value,
closing_value TYPE ty_value,
consumption TYPE ty_value,
END OF ty_result.
TYPES tt_result TYPE STANDARD TABLE OF ty_result WITH DEFAULT KEY.
METHODS calculate
IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rt_result) TYPE tt_result.
ENDINTERFACE.

View File

@@ -0,0 +1,57 @@
# 1. Goal
A water utility bills its customers for the water that they use. The billing
program needs one service that calculates the consumption of each water meter
from the readings of a period.
# 2. Open questions
None.
# 3. Context
- The interface {{P}}IF_METER_READING exists in package $TMP. It is active.
- The interface contains these public types:
TY_METER_ID (CHAR 10), TY_READING_DATE (date), TY_VALUE (integer),
TY_READING (METER_ID, READING_DATE, READING_VALUE),
TT_READINGS (a table of TY_READING),
TY_RESULT (METER_ID, OPENING_VALUE, CLOSING_VALUE, CONSUMPTION),
TT_RESULT (a table of TY_RESULT).
- The interface has one method:
CALCULATE IMPORTING it_readings TYPE tt_readings
RETURNING VALUE(rt_result) TYPE tt_result.
- A reading is the meter value in cubic metres that a reader wrote down on a
date.
# 4. Contract
- Create the class {{P}}METER_CONSUMPTION in package $TMP.
- The class is public, final, and has a public constructor without parameters.
- The class implements {{P}}IF_METER_READING.
- Do not add other public methods.
# 5. Business rules
The method gets the readings of all meters of one period. The input table can be
unsorted. It can contain more than one reading for the same meter and the same
date.
1. Ignore every reading whose value is less than zero.
2. If two or more readings have the same meter ID and the same date, only the
reading with the highest value counts. Ignore the other readings of that
date.
3. For each meter, the opening value is the value of the earliest date of the
meter, and the closing value is the value of the latest date of the meter.
If a meter has only one reading, the opening value and the closing value are
the value of that reading.
4. The consumption of a meter is the closing value minus the opening value. If
that difference is less than zero, the consumption is zero.
5. The result has one row for each meter with at least one reading that rule 1
does not ignore.
6. The result is sorted by meter ID in ascending order.
7. If no reading remains after rule 1, the result is empty.
# 6. Constraints
- Release target: 8.16.
- 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_METER_READING.
# 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,51 @@
{
"id": "G0120",
"category": "C",
"object_type": "CLAS",
"difficulty": 2,
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 40,
"max_activations": 12
},
"seed": [
{
"type": "INTF",
"name": "{{P}}IF_METER_READING",
"file": "seed/if_meter_reading.intf.abap",
"description": "Meter reading contract with the reading types and the calculate method"
}
],
"contract": [
{
"type": "CLAS",
"name": "{{P}}METER_CONSUMPTION",
"implements": "{{P}}IF_METER_READING"
}
],
"out_of_scope": [
"{{P}}IF_METER_READING"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T02_HIDDEN",
"file": "hidden/t02_hidden.clas.abap",
"description": "T02 hidden tests for the meter consumption calculation"
}
],
"reference": [
{
"type": "CLAS",
"name": "{{P}}METER_CONSUMPTION",
"file": "reference/meter_consumption.clas.abap",
"description": "Meter consumption calculator",
"testclasses_file": "reference/meter_consumption.testclasses.abap"
}
],
"craft_checks": [
"method_length",
"internal_table_access"
]
}