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,102 @@
CLASS {{p}}park_fee_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_park_fee.
PRIVATE SECTION.
CONSTANTS:
c_price_per_hour TYPE {{p}}if_park_fee=>ty_amount VALUE '2.50',
c_lost_ticket TYPE {{p}}if_park_fee=>ty_amount VALUE '45.00',
c_max_fee TYPE {{p}}if_park_fee=>ty_amount VALUE '60.00',
c_discount_rate TYPE p LENGTH 3 DECIMALS 2 VALUE '0.75',
c_free_minutes TYPE i VALUE 15,
c_max_minutes TYPE i VALUE 43200,
c_seconds_per_day TYPE i VALUE 86400,
c_reference_date TYPE d VALUE '19000101',
c_sunday_offset TYPE i VALUE 6.
METHODS duration_in_minutes
IMPORTING iv_entry_date TYPE d
iv_entry_time TYPE t
iv_exit_date TYPE d
iv_exit_time TYPE t
RETURNING VALUE(rv_minutes) TYPE i.
METHODS started_hours
IMPORTING iv_minutes TYPE i
RETURNING VALUE(rv_hours) TYPE i.
METHODS is_sunday
IMPORTING iv_date TYPE d
RETURNING VALUE(rv_sunday) TYPE abap_bool.
METHODS discounted
IMPORTING iv_fee TYPE {{p}}if_park_fee=>ty_amount
iv_discount TYPE abap_bool
RETURNING VALUE(rv_fee) TYPE {{p}}if_park_fee=>ty_amount.
ENDCLASS.
CLASS {{p}}park_fee_calculator IMPLEMENTATION.
METHOD {{p}}if_park_fee~calculate.
DATA(lv_minutes) = duration_in_minutes( iv_entry_date = iv_entry_date
iv_entry_time = iv_entry_time
iv_exit_date = iv_exit_date
iv_exit_time = iv_exit_time ).
IF lv_minutes < 0.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-bad_time
fee = 0 ).
RETURN.
ENDIF.
IF lv_minutes <= c_max_minutes.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-too_long
fee = 0 ).
RETURN.
ENDIF.
IF iv_lost_ticket = abap_true.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = c_lost_ticket ).
RETURN.
ENDIF.
IF lv_minutes <= c_free_minutes OR is_sunday( iv_entry_date ) = abap_true.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = 0 ).
RETURN.
ENDIF.
DATA lv_fee TYPE {{p}}if_park_fee=>ty_amount.
lv_fee = started_hours( lv_minutes ) * c_price_per_hour.
lv_fee = discounted( iv_fee = lv_fee iv_discount = iv_discount ).
IF lv_fee > c_max_fee.
lv_fee = c_max_fee.
ENDIF.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = lv_fee ).
ENDMETHOD.
METHOD duration_in_minutes.
DATA(lv_entry_time) = iv_entry_time.
DATA(lv_exit_time) = iv_exit_time.
lv_entry_time+4(2) = '00'.
lv_exit_time+4(2) = '00'.
DATA(lv_seconds) = ( iv_exit_date - iv_entry_date ) * c_seconds_per_day
+ ( lv_exit_time - lv_entry_time ).
rv_minutes = lv_seconds DIV 60.
ENDMETHOD.
METHOD started_hours.
rv_hours = iv_minutes DIV 60.
IF iv_minutes MOD 60 <> 0.
rv_hours = rv_hours + 1.
ENDIF.
ENDMETHOD.
METHOD is_sunday.
DATA(lv_offset) = iv_date - c_reference_date.
rv_sunday = xsdbool( lv_offset MOD 7 = c_sunday_offset ).
ENDMETHOD.
METHOD discounted.
rv_fee = iv_fee.
IF iv_discount = abap_true.
rv_fee = round( val = iv_fee * c_discount_rate
dec = 2
mode = cl_abap_math=>round_half_up ).
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,102 @@
CLASS {{p}}park_fee_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_park_fee.
PRIVATE SECTION.
CONSTANTS:
c_price_per_hour TYPE {{p}}if_park_fee=>ty_amount VALUE '2.50',
c_lost_ticket TYPE {{p}}if_park_fee=>ty_amount VALUE '45.00',
c_max_fee TYPE {{p}}if_park_fee=>ty_amount VALUE '60.00',
c_discount_rate TYPE p LENGTH 3 DECIMALS 2 VALUE '0.75',
c_free_minutes TYPE i VALUE 15,
c_max_minutes TYPE i VALUE 43200,
c_seconds_per_day TYPE i VALUE 86400,
c_reference_date TYPE d VALUE '19000101',
c_sunday_offset TYPE i VALUE 6.
METHODS duration_in_minutes
IMPORTING iv_entry_date TYPE d
iv_entry_time TYPE t
iv_exit_date TYPE d
iv_exit_time TYPE t
RETURNING VALUE(rv_minutes) TYPE i.
METHODS started_hours
IMPORTING iv_minutes TYPE i
RETURNING VALUE(rv_hours) TYPE i.
METHODS is_sunday
IMPORTING iv_date TYPE d
RETURNING VALUE(rv_sunday) TYPE abap_bool.
METHODS discounted
IMPORTING iv_fee TYPE {{p}}if_park_fee=>ty_amount
iv_discount TYPE abap_bool
RETURNING VALUE(rv_fee) TYPE {{p}}if_park_fee=>ty_amount.
ENDCLASS.
CLASS {{p}}park_fee_calculator IMPLEMENTATION.
METHOD {{p}}if_park_fee~calculate.
DATA(lv_minutes) = duration_in_minutes( iv_entry_date = iv_entry_date
iv_entry_time = iv_entry_time
iv_exit_date = iv_exit_date
iv_exit_time = iv_exit_time ).
IF lv_minutes < 0.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-bad_time
fee = 0 ).
RETURN.
ENDIF.
IF lv_minutes > c_max_minutes.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-too_long
fee = 0 ).
RETURN.
ENDIF.
IF iv_lost_ticket <> abap_true.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = c_lost_ticket ).
RETURN.
ENDIF.
IF lv_minutes <= c_free_minutes OR is_sunday( iv_entry_date ) = abap_true.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = 0 ).
RETURN.
ENDIF.
DATA lv_fee TYPE {{p}}if_park_fee=>ty_amount.
lv_fee = started_hours( lv_minutes ) * c_price_per_hour.
lv_fee = discounted( iv_fee = lv_fee iv_discount = iv_discount ).
IF lv_fee > c_max_fee.
lv_fee = c_max_fee.
ENDIF.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = lv_fee ).
ENDMETHOD.
METHOD duration_in_minutes.
DATA(lv_entry_time) = iv_entry_time.
DATA(lv_exit_time) = iv_exit_time.
lv_entry_time+4(2) = '00'.
lv_exit_time+4(2) = '00'.
DATA(lv_seconds) = ( iv_exit_date - iv_entry_date ) * c_seconds_per_day
+ ( lv_exit_time - lv_entry_time ).
rv_minutes = lv_seconds DIV 60.
ENDMETHOD.
METHOD started_hours.
rv_hours = iv_minutes DIV 60.
IF iv_minutes MOD 60 <> 0.
rv_hours = rv_hours + 1.
ENDIF.
ENDMETHOD.
METHOD is_sunday.
DATA(lv_offset) = iv_date - c_reference_date.
rv_sunday = xsdbool( lv_offset MOD 7 = c_sunday_offset ).
ENDMETHOD.
METHOD discounted.
rv_fee = iv_fee.
IF iv_discount = abap_true.
rv_fee = round( val = iv_fee * c_discount_rate
dec = 2
mode = cl_abap_math=>round_half_up ).
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,102 @@
CLASS {{p}}park_fee_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_park_fee.
PRIVATE SECTION.
CONSTANTS:
c_price_per_hour TYPE {{p}}if_park_fee=>ty_amount VALUE '2.50',
c_lost_ticket TYPE {{p}}if_park_fee=>ty_amount VALUE '45.00',
c_max_fee TYPE {{p}}if_park_fee=>ty_amount VALUE '60.00',
c_discount_rate TYPE p LENGTH 3 DECIMALS 2 VALUE '0.75',
c_free_minutes TYPE i VALUE 15,
c_max_minutes TYPE i VALUE 43200,
c_seconds_per_day TYPE i VALUE 86400,
c_reference_date TYPE d VALUE '19000101',
c_sunday_offset TYPE i VALUE 6.
METHODS duration_in_minutes
IMPORTING iv_entry_date TYPE d
iv_entry_time TYPE t
iv_exit_date TYPE d
iv_exit_time TYPE t
RETURNING VALUE(rv_minutes) TYPE i.
METHODS started_hours
IMPORTING iv_minutes TYPE i
RETURNING VALUE(rv_hours) TYPE i.
METHODS is_sunday
IMPORTING iv_date TYPE d
RETURNING VALUE(rv_sunday) TYPE abap_bool.
METHODS discounted
IMPORTING iv_fee TYPE {{p}}if_park_fee=>ty_amount
iv_discount TYPE abap_bool
RETURNING VALUE(rv_fee) TYPE {{p}}if_park_fee=>ty_amount.
ENDCLASS.
CLASS {{p}}park_fee_calculator IMPLEMENTATION.
METHOD {{p}}if_park_fee~calculate.
DATA(lv_minutes) = duration_in_minutes( iv_entry_date = iv_entry_date
iv_entry_time = iv_entry_time
iv_exit_date = iv_exit_date
iv_exit_time = iv_exit_time ).
IF lv_minutes < 0.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-bad_time
fee = 0 ).
RETURN.
ENDIF.
IF lv_minutes > c_max_minutes.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-too_long
fee = 0 ).
RETURN.
ENDIF.
IF iv_lost_ticket = abap_true.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = c_lost_ticket ).
RETURN.
ENDIF.
IF lv_minutes <= c_free_minutes OR is_sunday( iv_entry_date ) = abap_true.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = 0 ).
RETURN.
ENDIF.
DATA lv_fee TYPE {{p}}if_park_fee=>ty_amount.
lv_fee = started_hours( lv_minutes ) / c_price_per_hour.
lv_fee = discounted( iv_fee = lv_fee iv_discount = iv_discount ).
IF lv_fee > c_max_fee.
lv_fee = c_max_fee.
ENDIF.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = lv_fee ).
ENDMETHOD.
METHOD duration_in_minutes.
DATA(lv_entry_time) = iv_entry_time.
DATA(lv_exit_time) = iv_exit_time.
lv_entry_time+4(2) = '00'.
lv_exit_time+4(2) = '00'.
DATA(lv_seconds) = ( iv_exit_date - iv_entry_date ) * c_seconds_per_day
+ ( lv_exit_time - lv_entry_time ).
rv_minutes = lv_seconds DIV 60.
ENDMETHOD.
METHOD started_hours.
rv_hours = iv_minutes DIV 60.
IF iv_minutes MOD 60 <> 0.
rv_hours = rv_hours + 1.
ENDIF.
ENDMETHOD.
METHOD is_sunday.
DATA(lv_offset) = iv_date - c_reference_date.
rv_sunday = xsdbool( lv_offset MOD 7 = c_sunday_offset ).
ENDMETHOD.
METHOD discounted.
rv_fee = iv_fee.
IF iv_discount = abap_true.
rv_fee = round( val = iv_fee * c_discount_rate
dec = 2
mode = cl_abap_math=>round_half_up ).
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,102 @@
CLASS {{p}}park_fee_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_park_fee.
PRIVATE SECTION.
CONSTANTS:
c_price_per_hour TYPE {{p}}if_park_fee=>ty_amount VALUE '2.50',
c_lost_ticket TYPE {{p}}if_park_fee=>ty_amount VALUE '45.00',
c_max_fee TYPE {{p}}if_park_fee=>ty_amount VALUE '60.00',
c_discount_rate TYPE p LENGTH 3 DECIMALS 2 VALUE '0.75',
c_free_minutes TYPE i VALUE 15,
c_max_minutes TYPE i VALUE 43200,
c_seconds_per_day TYPE i VALUE 86400,
c_reference_date TYPE d VALUE '19000101',
c_sunday_offset TYPE i VALUE 6.
METHODS duration_in_minutes
IMPORTING iv_entry_date TYPE d
iv_entry_time TYPE t
iv_exit_date TYPE d
iv_exit_time TYPE t
RETURNING VALUE(rv_minutes) TYPE i.
METHODS started_hours
IMPORTING iv_minutes TYPE i
RETURNING VALUE(rv_hours) TYPE i.
METHODS is_sunday
IMPORTING iv_date TYPE d
RETURNING VALUE(rv_sunday) TYPE abap_bool.
METHODS discounted
IMPORTING iv_fee TYPE {{p}}if_park_fee=>ty_amount
iv_discount TYPE abap_bool
RETURNING VALUE(rv_fee) TYPE {{p}}if_park_fee=>ty_amount.
ENDCLASS.
CLASS {{p}}park_fee_calculator IMPLEMENTATION.
METHOD {{p}}if_park_fee~calculate.
DATA(lv_minutes) = duration_in_minutes( iv_entry_date = iv_entry_date
iv_entry_time = iv_entry_time
iv_exit_date = iv_exit_date
iv_exit_time = iv_exit_time ).
IF lv_minutes < 0.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-bad_time
fee = 0 ).
RETURN.
ENDIF.
IF lv_minutes > c_max_minutes.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-too_long
fee = 0 ).
RETURN.
ENDIF.
IF iv_lost_ticket = abap_true.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = c_lost_ticket ).
RETURN.
ENDIF.
IF lv_minutes <= c_free_minutes OR is_sunday( iv_entry_date ) = abap_true.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = 0 ).
RETURN.
ENDIF.
DATA lv_fee TYPE {{p}}if_park_fee=>ty_amount.
lv_fee = started_hours( lv_minutes ) * c_price_per_hour.
lv_fee = discounted( iv_fee = lv_fee iv_discount = iv_discount ).
IF lv_fee > c_max_fee.
lv_fee = c_max_fee.
ENDIF.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = lv_fee ).
ENDMETHOD.
METHOD duration_in_minutes.
DATA(lv_entry_time) = iv_entry_time.
DATA(lv_exit_time) = iv_exit_time.
lv_entry_time+4(2) = '00'.
lv_exit_time+4(2) = '00'.
DATA(lv_seconds) = ( iv_exit_date - iv_entry_date ) * c_seconds_per_day
+ ( lv_exit_time - lv_entry_time ).
rv_minutes = lv_seconds DIV 60.
ENDMETHOD.
METHOD started_hours.
rv_hours = iv_minutes DIV 60.
IF iv_minutes MOD 60 <> 0.
rv_hours = rv_hours + 1.
ENDIF.
ENDMETHOD.
METHOD is_sunday.
DATA(lv_offset) = iv_date - c_reference_date.
rv_sunday = xsdbool( lv_offset MOD 7 = c_sunday_offset ).
ENDMETHOD.
METHOD discounted.
rv_fee = iv_fee.
IF iv_discount = abap_false.
rv_fee = round( val = iv_fee * c_discount_rate
dec = 2
mode = cl_abap_math=>round_half_up ).
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,29 @@
{
"id": "G0101",
"pool": "eval",
"object_type": "CLAS",
"category": "A",
"attempts": [
{
"stage": "validate",
"oracle": 85.2,
"null": 0
},
{
"stage": "validate",
"oracle": 85.2,
"null": 0
},
{
"stage": "validate",
"oracle": 100.0,
"null": 0,
"mutation": {
"valid": 4,
"killed": 4,
"ok": true
}
}
],
"accepted": true
}

View File

@@ -0,0 +1,156 @@
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_park_fee.
METHODS setup.
METHODS assert_result
IMPORTING is_result TYPE {{p}}if_park_fee=>ty_result
iv_fee TYPE {{p}}if_park_fee=>ty_amount
iv_reason TYPE {{p}}if_park_fee=>ty_reason.
METHODS bad_time_order FOR TESTING.
METHODS too_long FOR TESTING.
METHODS free_period FOR TESTING.
METHODS one_started_hour FOR TESTING.
METHODS two_started_hours FOR TESTING.
METHODS sunday_free FOR TESTING.
METHODS lost_ticket_flat FOR TESTING.
METHODS lost_ticket_on_sunday FOR TESTING.
METHODS discount_rounds FOR TESTING.
METHODS discount_rounds_up FOR TESTING.
METHODS daily_cap FOR TESTING.
ENDCLASS.
CLASS {{p}}t02_hidden IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}park_fee_calculator( ).
ENDMETHOD.
METHOD assert_result.
cl_abap_unit_assert=>assert_equals( exp = iv_fee act = is_result-fee ).
cl_abap_unit_assert=>assert_equals( exp = iv_reason act = is_result-reason ).
ENDMETHOD.
METHOD bad_time_order.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '120000'
iv_exit_date = '20240108'
iv_exit_time = '110000'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '0.00'
iv_reason = {{p}}if_park_fee=>c_reason-bad_time ).
ENDMETHOD.
METHOD too_long.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240208'
iv_exit_time = '100100'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '0.00'
iv_reason = {{p}}if_park_fee=>c_reason-too_long ).
ENDMETHOD.
METHOD free_period.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '100015'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '0.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD one_started_hour.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '103000'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '2.50'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD two_started_hours.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '110100'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '5.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD sunday_free.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240107'
iv_entry_time = '100000'
iv_exit_date = '20240107'
iv_exit_time = '130000'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '0.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD lost_ticket_flat.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '100500'
iv_lost_ticket = abap_true
iv_discount = abap_false )
iv_fee = '45.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD lost_ticket_on_sunday.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240107'
iv_entry_time = '100000'
iv_exit_date = '20240107'
iv_exit_time = '120000'
iv_lost_ticket = abap_true
iv_discount = abap_false )
iv_fee = '45.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD discount_rounds.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '110100'
iv_lost_ticket = abap_false
iv_discount = abap_true )
iv_fee = '3.75'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD discount_rounds_up.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '103000'
iv_lost_ticket = abap_false
iv_discount = abap_true )
iv_fee = '1.88'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD daily_cap.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '060000'
iv_exit_date = '20240109'
iv_exit_time = '120000'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '60.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,77 @@
{
"task": "G0101",
"mutants": [
{
"object": "{{P}}PARK_FEE_CALCULATOR",
"mutant": "line 47: > -> <= (rel)",
"status": "killed",
"hidden": "1/11",
"failed_tests": [
"DAILY_CAP",
"DISCOUNT_ROUNDS",
"DISCOUNT_ROUNDS_UP",
"FREE_PERIOD",
"LOST_TICKET_FLAT",
"LOST_TICKET_ON_SUNDAY",
"ONE_STARTED_HOUR",
"SUNDAY_FREE",
"TOO_LONG",
"TWO_STARTED_HOURS"
]
},
{
"object": "{{P}}PARK_FEE_CALCULATOR",
"mutant": "line 52: = -> <> (eq)",
"status": "killed",
"hidden": "2/11",
"failed_tests": [
"DAILY_CAP",
"DISCOUNT_ROUNDS",
"DISCOUNT_ROUNDS_UP",
"FREE_PERIOD",
"LOST_TICKET_FLAT",
"LOST_TICKET_ON_SUNDAY",
"ONE_STARTED_HOUR",
"SUNDAY_FREE",
"TWO_STARTED_HOURS"
]
},
{
"object": "{{P}}PARK_FEE_CALCULATOR",
"mutant": "line 63: * -> / (arith)",
"status": "killed",
"hidden": "6/11",
"failed_tests": [
"DAILY_CAP",
"DISCOUNT_ROUNDS",
"DISCOUNT_ROUNDS_UP",
"ONE_STARTED_HOUR",
"TWO_STARTED_HOURS"
]
},
{
"object": "{{P}}PARK_FEE_CALCULATOR",
"mutant": "line 76: 2 -> 3 (const)",
"status": "invalid",
"hidden": "0/0",
"failed_tests": []
},
{
"object": "{{P}}PARK_FEE_CALCULATOR",
"mutant": "line 96: abap_true -> abap_false (bool)",
"status": "killed",
"hidden": "6/11",
"failed_tests": [
"DAILY_CAP",
"DISCOUNT_ROUNDS",
"DISCOUNT_ROUNDS_UP",
"ONE_STARTED_HOUR",
"TWO_STARTED_HOURS"
]
}
],
"valid": 4,
"killed": 4,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,102 @@
CLASS {{p}}park_fee_calculator DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
INTERFACES {{p}}if_park_fee.
PRIVATE SECTION.
CONSTANTS:
c_price_per_hour TYPE {{p}}if_park_fee=>ty_amount VALUE '2.50',
c_lost_ticket TYPE {{p}}if_park_fee=>ty_amount VALUE '45.00',
c_max_fee TYPE {{p}}if_park_fee=>ty_amount VALUE '60.00',
c_discount_rate TYPE p LENGTH 3 DECIMALS 2 VALUE '0.75',
c_free_minutes TYPE i VALUE 15,
c_max_minutes TYPE i VALUE 43200,
c_seconds_per_day TYPE i VALUE 86400,
c_reference_date TYPE d VALUE '19000101',
c_sunday_offset TYPE i VALUE 6.
METHODS duration_in_minutes
IMPORTING iv_entry_date TYPE d
iv_entry_time TYPE t
iv_exit_date TYPE d
iv_exit_time TYPE t
RETURNING VALUE(rv_minutes) TYPE i.
METHODS started_hours
IMPORTING iv_minutes TYPE i
RETURNING VALUE(rv_hours) TYPE i.
METHODS is_sunday
IMPORTING iv_date TYPE d
RETURNING VALUE(rv_sunday) TYPE abap_bool.
METHODS discounted
IMPORTING iv_fee TYPE {{p}}if_park_fee=>ty_amount
iv_discount TYPE abap_bool
RETURNING VALUE(rv_fee) TYPE {{p}}if_park_fee=>ty_amount.
ENDCLASS.
CLASS {{p}}park_fee_calculator IMPLEMENTATION.
METHOD {{p}}if_park_fee~calculate.
DATA(lv_minutes) = duration_in_minutes( iv_entry_date = iv_entry_date
iv_entry_time = iv_entry_time
iv_exit_date = iv_exit_date
iv_exit_time = iv_exit_time ).
IF lv_minutes < 0.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-bad_time
fee = 0 ).
RETURN.
ENDIF.
IF lv_minutes > c_max_minutes.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-too_long
fee = 0 ).
RETURN.
ENDIF.
IF iv_lost_ticket = abap_true.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = c_lost_ticket ).
RETURN.
ENDIF.
IF lv_minutes <= c_free_minutes OR is_sunday( iv_entry_date ) = abap_true.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = 0 ).
RETURN.
ENDIF.
DATA lv_fee TYPE {{p}}if_park_fee=>ty_amount.
lv_fee = started_hours( lv_minutes ) * c_price_per_hour.
lv_fee = discounted( iv_fee = lv_fee iv_discount = iv_discount ).
IF lv_fee > c_max_fee.
lv_fee = c_max_fee.
ENDIF.
rs_result = VALUE #( reason = {{p}}if_park_fee=>c_reason-ok
fee = lv_fee ).
ENDMETHOD.
METHOD duration_in_minutes.
DATA(lv_entry_time) = iv_entry_time.
DATA(lv_exit_time) = iv_exit_time.
lv_entry_time+4(2) = '00'.
lv_exit_time+4(2) = '00'.
DATA(lv_seconds) = ( iv_exit_date - iv_entry_date ) * c_seconds_per_day
+ ( lv_exit_time - lv_entry_time ).
rv_minutes = lv_seconds DIV 60.
ENDMETHOD.
METHOD started_hours.
rv_hours = iv_minutes DIV 60.
IF iv_minutes MOD 60 <> 0.
rv_hours = rv_hours + 1.
ENDIF.
ENDMETHOD.
METHOD is_sunday.
DATA(lv_offset) = iv_date - c_reference_date.
rv_sunday = xsdbool( lv_offset MOD 7 = c_sunday_offset ).
ENDMETHOD.
METHOD discounted.
rv_fee = iv_fee.
IF iv_discount = abap_true.
rv_fee = round( val = iv_fee * c_discount_rate
dec = 2
mode = cl_abap_math=>round_half_up ).
ENDIF.
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,143 @@
CLASS ltc_park_fee DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
DATA mo_cut TYPE REF TO {{p}}if_park_fee.
METHODS setup.
METHODS assert_result
IMPORTING is_result TYPE {{p}}if_park_fee=>ty_result
iv_fee TYPE {{p}}if_park_fee=>ty_amount
iv_reason TYPE {{p}}if_park_fee=>ty_reason.
METHODS free_period_is_free FOR TESTING.
METHODS one_hour_costs_250 FOR TESTING.
METHODS two_hours_cost_500 FOR TESTING.
METHODS sunday_is_free FOR TESTING.
METHODS lost_ticket_is_flat FOR TESTING.
METHODS discount_is_rounded FOR TESTING.
METHODS discount_rounds_up FOR TESTING.
METHODS fee_is_capped FOR TESTING.
METHODS exit_before_entry FOR TESTING.
METHODS long_parking FOR TESTING.
ENDCLASS.
CLASS ltc_park_fee IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}park_fee_calculator( ).
ENDMETHOD.
METHOD assert_result.
cl_abap_unit_assert=>assert_equals( exp = iv_fee act = is_result-fee ).
cl_abap_unit_assert=>assert_equals( exp = iv_reason act = is_result-reason ).
ENDMETHOD.
METHOD free_period_is_free.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '100015'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '0.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD one_hour_costs_250.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '103000'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '2.50'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD two_hours_cost_500.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '110100'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '5.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD sunday_is_free.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240107'
iv_entry_time = '090000'
iv_exit_date = '20240107'
iv_exit_time = '120000'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '0.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD lost_ticket_is_flat.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '100500'
iv_lost_ticket = abap_true
iv_discount = abap_false )
iv_fee = '45.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD discount_is_rounded.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '110100'
iv_lost_ticket = abap_false
iv_discount = abap_true )
iv_fee = '3.75'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD discount_rounds_up.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240108'
iv_exit_time = '103000'
iv_lost_ticket = abap_false
iv_discount = abap_true )
iv_fee = '1.88'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD fee_is_capped.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '060000'
iv_exit_date = '20240109'
iv_exit_time = '120000'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '60.00'
iv_reason = {{p}}if_park_fee=>c_reason-ok ).
ENDMETHOD.
METHOD exit_before_entry.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '120000'
iv_exit_date = '20240108'
iv_exit_time = '110000'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '0.00'
iv_reason = {{p}}if_park_fee=>c_reason-bad_time ).
ENDMETHOD.
METHOD long_parking.
assert_result( is_result = mo_cut->calculate( iv_entry_date = '20240108'
iv_entry_time = '100000'
iv_exit_date = '20240208'
iv_exit_time = '100100'
iv_lost_ticket = abap_false
iv_discount = abap_false )
iv_fee = '0.00'
iv_reason = {{p}}if_park_fee=>c_reason-too_long ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,23 @@
INTERFACE {{p}}if_park_fee PUBLIC.
TYPES ty_amount TYPE p LENGTH 8 DECIMALS 2.
TYPES ty_reason TYPE c LENGTH 10.
TYPES:
BEGIN OF ty_result,
reason TYPE ty_reason,
fee TYPE ty_amount,
END OF ty_result.
CONSTANTS:
BEGIN OF c_reason,
ok TYPE ty_reason VALUE 'OK',
bad_time TYPE ty_reason VALUE 'BAD_TIME',
too_long TYPE ty_reason VALUE 'TOO_LONG',
END OF c_reason.
METHODS calculate
IMPORTING iv_entry_date TYPE d
iv_entry_time TYPE t
iv_exit_date TYPE d
iv_exit_time TYPE t
iv_lost_ticket TYPE abap_bool
iv_discount TYPE abap_bool
RETURNING VALUE(rs_result) TYPE ty_result.
ENDINTERFACE.

View File

@@ -0,0 +1,67 @@
# 1. Goal
A car park operator calculates the parking fee of a car. The calculation uses
the entry time and the exit time of the car. A payment terminal calls the
calculation before it charges the driver. The terminal needs the fee and a
reason code for each parking event.
# 2. Open questions
None.
# 3. Context
- The interface {{P}}IF_PARK_FEE exists in package $TMP. It is active.
- The interface contains the type TY_AMOUNT (a packed number, length 8, 2
decimals), the type TY_REASON (a character field, length 10), the structure
TY_RESULT with the components REASON (TY_REASON) and FEE (TY_AMOUNT), and the
constant structure C_REASON with the components OK, BAD_TIME and TOO_LONG.
- The interface contains one method:
CALCULATE
IMPORTING iv_entry_date TYPE d
iv_entry_time TYPE t
iv_exit_date TYPE d
iv_exit_time TYPE t
iv_lost_ticket TYPE abap_bool
iv_discount TYPE abap_bool
RETURNING VALUE(rs_result) TYPE ty_result.
# 4. Contract
- Create the class {{P}}PARK_FEE_CALCULATOR in package $TMP.
- The class is public and final. It has a public constructor without
parameters.
- The class implements the interface {{P}}IF_PARK_FEE.
- Do not add other public methods.
# 5. Business rules
The method CALCULATE applies the rules in this sequence. It returns the reason
and the fee of the first rule that applies.
1. The duration is the number of whole minutes between the entry and the exit.
Ignore the seconds of the two time fields. Example: the entry 10:00:30 and
the exit 10:01:20 give the duration 1 minute. The entry 10:00:00 and the
exit 10:30:00 give the duration 30 minutes.
2. If the exit is before the entry, return the reason BAD_TIME and the fee
0.00.
3. If the duration is more than 43200 minutes, return the reason TOO_LONG and
the fee 0.00.
4. If the ticket is lost (iv_lost_ticket = abap_true), the fee is 45.00 and the
reason is OK. Rules 5 to 9 do not apply.
5. If the duration is 15 minutes or less, the fee is 0.00 and the reason is OK.
6. If the entry date is a Sunday, the fee is 0.00 and the reason is OK.
7. Else the fee is the number of started hours multiplied by 2.50. A started
hour is a full hour or a part of an hour. Example: 61 minutes are 2 started
hours and give the fee 5.00. 30 minutes are 1 started hour and give the fee
2.50.
8. If the driver uses the discount card (iv_discount = abap_true), reduce the
fee by 25 percent. Round the result to 2 decimals, half up. Example: the fee
5.00 becomes 3.75. The fee 2.50 becomes 1.88.
9. The fee never exceeds 60.00. If the fee is higher, it is 60.00.
10. The reason is OK.
# 6. Constraints
- Release target: 8.16.
- Coding standards: Clean ABAP. Keep every method below 40 statements. Do not
use global variables. Do not write comments that restate the code.
- Out of scope: do not change the interface {{P}}IF_PARK_FEE.
# 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": "G0101",
"category": "A",
"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_PARK_FEE",
"file": "seed/if_park_fee.intf.abap",
"description": "Parking fee contract"
}
],
"contract": [
{
"type": "CLAS",
"name": "{{P}}PARK_FEE_CALCULATOR",
"implements": "{{P}}IF_PARK_FEE"
}
],
"out_of_scope": [
"{{P}}IF_PARK_FEE"
],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T02_HIDDEN",
"file": "hidden/t02_hidden.clas.abap",
"description": "T02 hidden tests"
}
],
"reference": [
{
"type": "CLAS",
"name": "{{P}}PARK_FEE_CALCULATOR",
"file": "reference/park_fee_calculator.clas.abap",
"description": "Parking fee calculator",
"testclasses_file": "reference/park_fee_calculator.testclasses.abap"
}
],
"craft_checks": [
"method_length",
"db_operation_in_loop"
]
}