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,84 @@
CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_job,
job_id TYPE c LENGTH 10,
mechanic TYPE c LENGTH 8,
work_hours TYPE i,
start_date TYPE c LENGTH 8,
END OF ty_job,
tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY,
BEGIN OF ty_workload,
mechanic TYPE c LENGTH 8,
job_count TYPE i,
total_hours TYPE i,
first_date TYPE c LENGTH 8,
END OF ty_workload,
tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY.
METHODS summarize
IMPORTING it_jobs TYPE tt_job
RETURNING VALUE(rt_workload) TYPE tt_workload.
PRIVATE SECTION.
TYPES:
ty_date TYPE c LENGTH 8,
tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic.
METHODS is_relevant
IMPORTING is_job TYPE ty_job
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS add_job
IMPORTING is_job TYPE ty_job
CHANGING ct_workload TYPE tt_workload_hashed.
METHODS is_date_set
IMPORTING iv_date TYPE ty_date
RETURNING VALUE(rv_set) TYPE abap_bool.
ENDCLASS.
CLASS {{p}}workload_planner IMPLEMENTATION.
METHOD summarize.
DATA lt_workload TYPE tt_workload_hashed.
LOOP AT it_jobs INTO DATA(ls_job).
IF is_relevant( ls_job ) = abap_true.
add_job( EXPORTING is_job = ls_job
CHANGING ct_workload = lt_workload ).
ENDIF.
ENDLOOP.
rt_workload = lt_workload.
SORT rt_workload BY mechanic.
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL
AND is_job-mechanic IS NOT INITIAL
AND is_job-work_hours <= 0 ).
ENDMETHOD.
METHOD add_job.
ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(<ls_workload>).
IF sy-subrc <> 0.
INSERT VALUE #( mechanic = is_job-mechanic
job_count = 1
total_hours = is_job-work_hours
first_date = COND #( WHEN is_date_set( is_job-start_date )
THEN is_job-start_date ) )
INTO TABLE ct_workload ASSIGNING <ls_workload>.
RETURN.
ENDIF.
<ls_workload>-job_count = <ls_workload>-job_count + 1.
<ls_workload>-total_hours = <ls_workload>-total_hours + is_job-work_hours.
IF is_date_set( is_job-start_date ) = abap_true
AND ( <ls_workload>-first_date IS INITIAL
OR is_job-start_date < <ls_workload>-first_date ).
<ls_workload>-first_date = is_job-start_date.
ENDIF.
ENDMETHOD.
METHOD is_date_set.
rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,84 @@
CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_job,
job_id TYPE c LENGTH 10,
mechanic TYPE c LENGTH 8,
work_hours TYPE i,
start_date TYPE c LENGTH 8,
END OF ty_job,
tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY,
BEGIN OF ty_workload,
mechanic TYPE c LENGTH 8,
job_count TYPE i,
total_hours TYPE i,
first_date TYPE c LENGTH 8,
END OF ty_workload,
tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY.
METHODS summarize
IMPORTING it_jobs TYPE tt_job
RETURNING VALUE(rt_workload) TYPE tt_workload.
PRIVATE SECTION.
TYPES:
ty_date TYPE c LENGTH 8,
tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic.
METHODS is_relevant
IMPORTING is_job TYPE ty_job
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS add_job
IMPORTING is_job TYPE ty_job
CHANGING ct_workload TYPE tt_workload_hashed.
METHODS is_date_set
IMPORTING iv_date TYPE ty_date
RETURNING VALUE(rv_set) TYPE abap_bool.
ENDCLASS.
CLASS {{p}}workload_planner IMPLEMENTATION.
METHOD summarize.
DATA lt_workload TYPE tt_workload_hashed.
LOOP AT it_jobs INTO DATA(ls_job).
IF is_relevant( ls_job ) = abap_true.
add_job( EXPORTING is_job = ls_job
CHANGING ct_workload = lt_workload ).
ENDIF.
ENDLOOP.
rt_workload = lt_workload.
SORT rt_workload BY mechanic.
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL
AND is_job-mechanic IS NOT INITIAL
AND is_job-work_hours > 0 ).
ENDMETHOD.
METHOD add_job.
ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(<ls_workload>).
IF sy-subrc <> 0.
INSERT VALUE #( mechanic = is_job-mechanic
job_count = 2
total_hours = is_job-work_hours
first_date = COND #( WHEN is_date_set( is_job-start_date )
THEN is_job-start_date ) )
INTO TABLE ct_workload ASSIGNING <ls_workload>.
RETURN.
ENDIF.
<ls_workload>-job_count = <ls_workload>-job_count + 1.
<ls_workload>-total_hours = <ls_workload>-total_hours + is_job-work_hours.
IF is_date_set( is_job-start_date ) = abap_true
AND ( <ls_workload>-first_date IS INITIAL
OR is_job-start_date < <ls_workload>-first_date ).
<ls_workload>-first_date = is_job-start_date.
ENDIF.
ENDMETHOD.
METHOD is_date_set.
rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,84 @@
CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_job,
job_id TYPE c LENGTH 10,
mechanic TYPE c LENGTH 8,
work_hours TYPE i,
start_date TYPE c LENGTH 8,
END OF ty_job,
tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY,
BEGIN OF ty_workload,
mechanic TYPE c LENGTH 8,
job_count TYPE i,
total_hours TYPE i,
first_date TYPE c LENGTH 8,
END OF ty_workload,
tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY.
METHODS summarize
IMPORTING it_jobs TYPE tt_job
RETURNING VALUE(rt_workload) TYPE tt_workload.
PRIVATE SECTION.
TYPES:
ty_date TYPE c LENGTH 8,
tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic.
METHODS is_relevant
IMPORTING is_job TYPE ty_job
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS add_job
IMPORTING is_job TYPE ty_job
CHANGING ct_workload TYPE tt_workload_hashed.
METHODS is_date_set
IMPORTING iv_date TYPE ty_date
RETURNING VALUE(rv_set) TYPE abap_bool.
ENDCLASS.
CLASS {{p}}workload_planner IMPLEMENTATION.
METHOD summarize.
DATA lt_workload TYPE tt_workload_hashed.
LOOP AT it_jobs INTO DATA(ls_job).
IF is_relevant( ls_job ) = abap_true.
add_job( EXPORTING is_job = ls_job
CHANGING ct_workload = lt_workload ).
ENDIF.
ENDLOOP.
rt_workload = lt_workload.
SORT rt_workload BY mechanic.
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL
AND is_job-mechanic IS NOT INITIAL
AND is_job-work_hours > 0 ).
ENDMETHOD.
METHOD add_job.
ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(<ls_workload>).
IF sy-subrc <> 0.
INSERT VALUE #( mechanic = is_job-mechanic
job_count = 1
total_hours = is_job-work_hours
first_date = COND #( WHEN is_date_set( is_job-start_date )
THEN is_job-start_date ) )
INTO TABLE ct_workload ASSIGNING <ls_workload>.
RETURN.
ENDIF.
<ls_workload>-job_count = <ls_workload>-job_count + 1.
<ls_workload>-total_hours = <ls_workload>-total_hours - is_job-work_hours.
IF is_date_set( is_job-start_date ) = abap_true
AND ( <ls_workload>-first_date IS INITIAL
OR is_job-start_date < <ls_workload>-first_date ).
<ls_workload>-first_date = is_job-start_date.
ENDIF.
ENDMETHOD.
METHOD is_date_set.
rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,84 @@
CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_job,
job_id TYPE c LENGTH 10,
mechanic TYPE c LENGTH 8,
work_hours TYPE i,
start_date TYPE c LENGTH 8,
END OF ty_job,
tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY,
BEGIN OF ty_workload,
mechanic TYPE c LENGTH 8,
job_count TYPE i,
total_hours TYPE i,
first_date TYPE c LENGTH 8,
END OF ty_workload,
tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY.
METHODS summarize
IMPORTING it_jobs TYPE tt_job
RETURNING VALUE(rt_workload) TYPE tt_workload.
PRIVATE SECTION.
TYPES:
ty_date TYPE c LENGTH 8,
tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic.
METHODS is_relevant
IMPORTING is_job TYPE ty_job
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS add_job
IMPORTING is_job TYPE ty_job
CHANGING ct_workload TYPE tt_workload_hashed.
METHODS is_date_set
IMPORTING iv_date TYPE ty_date
RETURNING VALUE(rv_set) TYPE abap_bool.
ENDCLASS.
CLASS {{p}}workload_planner IMPLEMENTATION.
METHOD summarize.
DATA lt_workload TYPE tt_workload_hashed.
LOOP AT it_jobs INTO DATA(ls_job).
IF is_relevant( ls_job ) = abap_true.
add_job( EXPORTING is_job = ls_job
CHANGING ct_workload = lt_workload ).
ENDIF.
ENDLOOP.
rt_workload = lt_workload.
SORT rt_workload BY mechanic.
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL
AND is_job-mechanic IS NOT INITIAL
AND is_job-work_hours > 0 ).
ENDMETHOD.
METHOD add_job.
ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(<ls_workload>).
IF sy-subrc <> 0.
INSERT VALUE #( mechanic = is_job-mechanic
job_count = 1
total_hours = is_job-work_hours
first_date = COND #( WHEN is_date_set( is_job-start_date )
THEN is_job-start_date ) )
INTO TABLE ct_workload ASSIGNING <ls_workload>.
RETURN.
ENDIF.
<ls_workload>-job_count = <ls_workload>-job_count + 1.
<ls_workload>-total_hours = <ls_workload>-total_hours + is_job-work_hours.
IF is_date_set( is_job-start_date ) = abap_false
AND ( <ls_workload>-first_date IS INITIAL
OR is_job-start_date < <ls_workload>-first_date ).
<ls_workload>-first_date = is_job-start_date.
ENDIF.
ENDMETHOD.
METHOD is_date_set.
rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,84 @@
CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_job,
job_id TYPE c LENGTH 10,
mechanic TYPE c LENGTH 8,
work_hours TYPE i,
start_date TYPE c LENGTH 8,
END OF ty_job,
tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY,
BEGIN OF ty_workload,
mechanic TYPE c LENGTH 8,
job_count TYPE i,
total_hours TYPE i,
first_date TYPE c LENGTH 8,
END OF ty_workload,
tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY.
METHODS summarize
IMPORTING it_jobs TYPE tt_job
RETURNING VALUE(rt_workload) TYPE tt_workload.
PRIVATE SECTION.
TYPES:
ty_date TYPE c LENGTH 8,
tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic.
METHODS is_relevant
IMPORTING is_job TYPE ty_job
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS add_job
IMPORTING is_job TYPE ty_job
CHANGING ct_workload TYPE tt_workload_hashed.
METHODS is_date_set
IMPORTING iv_date TYPE ty_date
RETURNING VALUE(rv_set) TYPE abap_bool.
ENDCLASS.
CLASS {{p}}workload_planner IMPLEMENTATION.
METHOD summarize.
DATA lt_workload TYPE tt_workload_hashed.
LOOP AT it_jobs INTO DATA(ls_job).
IF is_relevant( ls_job ) = abap_true.
add_job( EXPORTING is_job = ls_job
CHANGING ct_workload = lt_workload ).
ENDIF.
ENDLOOP.
rt_workload = lt_workload.
SORT rt_workload BY mechanic.
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL
AND is_job-mechanic IS NOT INITIAL
AND is_job-work_hours > 0 ).
ENDMETHOD.
METHOD add_job.
ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(<ls_workload>).
IF sy-subrc <> 0.
INSERT VALUE #( mechanic = is_job-mechanic
job_count = 1
total_hours = is_job-work_hours
first_date = COND #( WHEN is_date_set( is_job-start_date )
THEN is_job-start_date ) )
INTO TABLE ct_workload ASSIGNING <ls_workload>.
RETURN.
ENDIF.
<ls_workload>-job_count = <ls_workload>-job_count + 1.
<ls_workload>-total_hours = <ls_workload>-total_hours + is_job-work_hours.
IF is_date_set( is_job-start_date ) = abap_true
OR ( <ls_workload>-first_date IS INITIAL
OR is_job-start_date < <ls_workload>-first_date ).
<ls_workload>-first_date = is_job-start_date.
ENDIF.
ENDMETHOD.
METHOD is_date_set.
rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ).
ENDMETHOD.
ENDCLASS.

View File

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

View File

@@ -0,0 +1,175 @@
CLASS {{p}}t02_hidden DEFINITION PUBLIC FINAL CREATE PUBLIC
FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES:
ty_mechanic TYPE c LENGTH 8,
ty_date TYPE c LENGTH 8.
DATA mo_cut TYPE REF TO {{p}}workload_planner.
METHODS setup.
METHODS empty_input FOR TESTING.
METHODS single_job FOR TESTING.
METHODS same_mechanic_aggregated FOR TESTING.
METHODS different_mechanics_sorted FOR TESTING.
METHODS non_positive_hours_ignored FOR TESTING.
METHODS empty_mechanic_ignored FOR TESTING.
METHODS empty_job_id_ignored FOR TESTING.
METHODS earliest_date_wins FOR TESTING.
METHODS unset_dates_ignored FOR TESTING.
METHODS all_dates_unset FOR TESTING.
METHODS all_jobs_invalid FOR TESTING.
METHODS add_job
IMPORTING iv_mechanic TYPE ty_mechanic
iv_hours TYPE i
iv_date TYPE ty_date
CHANGING ct_jobs TYPE {{p}}workload_planner=>tt_job.
ENDCLASS.
CLASS {{p}}t02_hidden IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}workload_planner( ).
ENDMETHOD.
METHOD add_job.
DATA lv_id TYPE c LENGTH 10.
lv_id = lines( ct_jobs ) + 1.
INSERT VALUE #( job_id = lv_id
mechanic = iv_mechanic
work_hours = iv_hours
start_date = iv_date )
INTO TABLE ct_jobs.
ENDMETHOD.
METHOD empty_input.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
cl_abap_unit_assert=>assert_initial( act = mo_cut->summarize( it_jobs = lt_jobs ) ).
ENDMETHOD.
METHOD single_job.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 'M1' act = lt_result[ 1 ]-mechanic ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-job_count ).
cl_abap_unit_assert=>assert_equals( exp = 4 act = lt_result[ 1 ]-total_hours ).
cl_abap_unit_assert=>assert_equals( exp = '20250110' act = lt_result[ 1 ]-first_date ).
ENDMETHOD.
METHOD same_mechanic_aggregated.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 6 iv_date = '20250112'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-job_count ).
cl_abap_unit_assert=>assert_equals( exp = 10 act = lt_result[ 1 ]-total_hours ).
cl_abap_unit_assert=>assert_equals( exp = '20250110' act = lt_result[ 1 ]-first_date ).
ENDMETHOD.
METHOD different_mechanics_sorted.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
add_job( EXPORTING iv_mechanic = 'M3' iv_hours = 2 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 3 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M2' iv_hours = 5 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = 3 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 'M1' act = lt_result[ 1 ]-mechanic ).
cl_abap_unit_assert=>assert_equals( exp = 'M2' act = lt_result[ 2 ]-mechanic ).
cl_abap_unit_assert=>assert_equals( exp = 'M3' act = lt_result[ 3 ]-mechanic ).
ENDMETHOD.
METHOD non_positive_hours_ignored.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 0 iv_date = '20250111'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = -3 iv_date = '20250112'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lt_result[ 1 ]-job_count ).
cl_abap_unit_assert=>assert_equals( exp = 4 act = lt_result[ 1 ]-total_hours ).
ENDMETHOD.
METHOD empty_mechanic_ignored.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
DATA lv_empty TYPE ty_mechanic.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = lv_empty iv_hours = 7 iv_date = '20250111'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 4 act = lt_result[ 1 ]-total_hours ).
ENDMETHOD.
METHOD empty_job_id_ignored.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
INSERT VALUE #( mechanic = 'M1'
work_hours = 7
start_date = '20250111' )
INTO TABLE lt_jobs.
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 4 act = lt_result[ 1 ]-total_hours ).
ENDMETHOD.
METHOD earliest_date_wins.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250120'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250105'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250115'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = '20250105' act = lt_result[ 1 ]-first_date ).
ENDMETHOD.
METHOD unset_dates_ignored.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
DATA lv_unset TYPE ty_date.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = lv_unset
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '00000000'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250115'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = '20250115' act = lt_result[ 1 ]-first_date ).
ENDMETHOD.
METHOD all_dates_unset.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '00000000'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 2 iv_date = '00000000'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_initial( act = lt_result[ 1 ]-first_date ).
ENDMETHOD.
METHOD all_jobs_invalid.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
DATA lv_empty TYPE ty_mechanic.
add_job( EXPORTING iv_mechanic = lv_empty iv_hours = 4 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 0 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_initial( act = mo_cut->summarize( it_jobs = lt_jobs ) ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,69 @@
{
"task": "G0119",
"mutants": [
{
"object": "{{P}}WORKLOAD_PLANNER",
"mutant": "line 58: > -> <= (rel)",
"status": "killed",
"hidden": "1/11",
"failed_tests": [
"ALL_DATES_UNSET",
"ALL_JOBS_INVALID",
"DIFFERENT_MECHANICS_SORTED",
"EARLIEST_DATE_WINS",
"EMPTY_JOB_ID_IGNORED",
"EMPTY_MECHANIC_IGNORED",
"NON_POSITIVE_HOURS_IGNORED",
"SAME_MECHANIC_AGGREGATED",
"SINGLE_JOB",
"UNSET_DATES_IGNORED"
]
},
{
"object": "{{P}}WORKLOAD_PLANNER",
"mutant": "line 65: 1 -> 2 (const)",
"status": "killed",
"hidden": "8/11",
"failed_tests": [
"NON_POSITIVE_HOURS_IGNORED",
"SAME_MECHANIC_AGGREGATED",
"SINGLE_JOB"
]
},
{
"object": "{{P}}WORKLOAD_PLANNER",
"mutant": "line 73: + -> - (arith)",
"status": "killed",
"hidden": "10/11",
"failed_tests": [
"SAME_MECHANIC_AGGREGATED"
]
},
{
"object": "{{P}}WORKLOAD_PLANNER",
"mutant": "line 74: abap_true -> abap_false (bool)",
"status": "killed",
"hidden": "8/11",
"failed_tests": [
"ALL_DATES_UNSET",
"EARLIEST_DATE_WINS",
"UNSET_DATES_IGNORED"
]
},
{
"object": "{{P}}WORKLOAD_PLANNER",
"mutant": "line 75: AND -> OR (logic)",
"status": "killed",
"hidden": "8/11",
"failed_tests": [
"ALL_DATES_UNSET",
"EARLIEST_DATE_WINS",
"SAME_MECHANIC_AGGREGATED"
]
}
],
"valid": 5,
"killed": 5,
"kill_rate": 1.0,
"ok": true
}

View File

@@ -0,0 +1,84 @@
CLASS {{p}}workload_planner DEFINITION PUBLIC FINAL CREATE PUBLIC.
PUBLIC SECTION.
TYPES:
BEGIN OF ty_job,
job_id TYPE c LENGTH 10,
mechanic TYPE c LENGTH 8,
work_hours TYPE i,
start_date TYPE c LENGTH 8,
END OF ty_job,
tt_job TYPE STANDARD TABLE OF ty_job WITH EMPTY KEY,
BEGIN OF ty_workload,
mechanic TYPE c LENGTH 8,
job_count TYPE i,
total_hours TYPE i,
first_date TYPE c LENGTH 8,
END OF ty_workload,
tt_workload TYPE STANDARD TABLE OF ty_workload WITH EMPTY KEY.
METHODS summarize
IMPORTING it_jobs TYPE tt_job
RETURNING VALUE(rt_workload) TYPE tt_workload.
PRIVATE SECTION.
TYPES:
ty_date TYPE c LENGTH 8,
tt_workload_hashed TYPE HASHED TABLE OF ty_workload WITH UNIQUE KEY mechanic.
METHODS is_relevant
IMPORTING is_job TYPE ty_job
RETURNING VALUE(rv_relevant) TYPE abap_bool.
METHODS add_job
IMPORTING is_job TYPE ty_job
CHANGING ct_workload TYPE tt_workload_hashed.
METHODS is_date_set
IMPORTING iv_date TYPE ty_date
RETURNING VALUE(rv_set) TYPE abap_bool.
ENDCLASS.
CLASS {{p}}workload_planner IMPLEMENTATION.
METHOD summarize.
DATA lt_workload TYPE tt_workload_hashed.
LOOP AT it_jobs INTO DATA(ls_job).
IF is_relevant( ls_job ) = abap_true.
add_job( EXPORTING is_job = ls_job
CHANGING ct_workload = lt_workload ).
ENDIF.
ENDLOOP.
rt_workload = lt_workload.
SORT rt_workload BY mechanic.
ENDMETHOD.
METHOD is_relevant.
rv_relevant = xsdbool( is_job-job_id IS NOT INITIAL
AND is_job-mechanic IS NOT INITIAL
AND is_job-work_hours > 0 ).
ENDMETHOD.
METHOD add_job.
ASSIGN ct_workload[ mechanic = is_job-mechanic ] TO FIELD-SYMBOL(<ls_workload>).
IF sy-subrc <> 0.
INSERT VALUE #( mechanic = is_job-mechanic
job_count = 1
total_hours = is_job-work_hours
first_date = COND #( WHEN is_date_set( is_job-start_date )
THEN is_job-start_date ) )
INTO TABLE ct_workload ASSIGNING <ls_workload>.
RETURN.
ENDIF.
<ls_workload>-job_count = <ls_workload>-job_count + 1.
<ls_workload>-total_hours = <ls_workload>-total_hours + is_job-work_hours.
IF is_date_set( is_job-start_date ) = abap_true
AND ( <ls_workload>-first_date IS INITIAL
OR is_job-start_date < <ls_workload>-first_date ).
<ls_workload>-first_date = is_job-start_date.
ENDIF.
ENDMETHOD.
METHOD is_date_set.
rv_set = xsdbool( iv_date IS NOT INITIAL AND iv_date <> '00000000' ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,85 @@
CLASS ltc_workload_planner DEFINITION FINAL FOR TESTING DURATION SHORT RISK LEVEL HARMLESS.
PRIVATE SECTION.
TYPES:
ty_mechanic TYPE c LENGTH 8,
ty_date TYPE c LENGTH 8.
DATA mo_cut TYPE REF TO {{p}}workload_planner.
METHODS setup.
METHODS empty_input FOR TESTING.
METHODS one_mechanic FOR TESTING.
METHODS sorted_by_mechanic FOR TESTING.
METHODS ignores_invalid_jobs FOR TESTING.
METHODS keeps_earliest_date FOR TESTING.
METHODS add_job
IMPORTING iv_mechanic TYPE ty_mechanic
iv_hours TYPE i
iv_date TYPE ty_date
CHANGING ct_jobs TYPE {{p}}workload_planner=>tt_job.
ENDCLASS.
CLASS ltc_workload_planner IMPLEMENTATION.
METHOD setup.
mo_cut = NEW {{p}}workload_planner( ).
ENDMETHOD.
METHOD add_job.
INSERT VALUE #( job_id = 'JOB'
mechanic = iv_mechanic
work_hours = iv_hours
start_date = iv_date )
INTO TABLE ct_jobs.
ENDMETHOD.
METHOD empty_input.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
cl_abap_unit_assert=>assert_initial( act = mo_cut->summarize( it_jobs = lt_jobs ) ).
ENDMETHOD.
METHOD one_mechanic.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 4 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 6 iv_date = '20250112'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = 1 act = lines( lt_result ) ).
cl_abap_unit_assert=>assert_equals( exp = 2 act = lt_result[ 1 ]-job_count ).
cl_abap_unit_assert=>assert_equals( exp = 10 act = lt_result[ 1 ]-total_hours ).
cl_abap_unit_assert=>assert_equals( exp = '20250110' act = lt_result[ 1 ]-first_date ).
ENDMETHOD.
METHOD sorted_by_mechanic.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
add_job( EXPORTING iv_mechanic = 'M2' iv_hours = 1 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = 'M1' act = lt_result[ 1 ]-mechanic ).
cl_abap_unit_assert=>assert_equals( exp = 'M2' act = lt_result[ 2 ]-mechanic ).
ENDMETHOD.
METHOD ignores_invalid_jobs.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
DATA lv_empty TYPE ty_mechanic.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 0 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = lv_empty iv_hours = 5 iv_date = '20250110'
CHANGING ct_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_initial( act = mo_cut->summarize( it_jobs = lt_jobs ) ).
ENDMETHOD.
METHOD keeps_earliest_date.
DATA lt_jobs TYPE {{p}}workload_planner=>tt_job.
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250201'
CHANGING ct_jobs = lt_jobs ).
add_job( EXPORTING iv_mechanic = 'M1' iv_hours = 1 iv_date = '20250101'
CHANGING ct_jobs = lt_jobs ).
DATA(lt_result) = mo_cut->summarize( it_jobs = lt_jobs ).
cl_abap_unit_assert=>assert_equals( exp = '20250101' act = lt_result[ 1 ]-first_date ).
ENDMETHOD.
ENDCLASS.

View File

@@ -0,0 +1,54 @@
# 1. Goal
A vehicle workshop plans the workload of its mechanics. A planning program calls
one service of the class {{P}}WORKLOAD_PLANNER. The service groups the open
repair jobs by mechanic and returns one row per mechanic.
# 2. Open questions
None.
# 3. Context
- The workshop receives the open repair jobs from an external planning system.
The start date arrives as a character field in the format YYYYMMDD.
- The class is new. No other object is in scope.
# 4. Contract
- Create the class {{P}}WORKLOAD_PLANNER in package $TMP.
- The class is public and final. It has a public constructor without parameters.
- The class has these public types:
- ty_job with the components job_id (c LENGTH 10), mechanic (c LENGTH 8),
work_hours (i), start_date (c LENGTH 8).
- tt_job: a standard table of ty_job with an empty key.
- ty_workload with the components mechanic (c LENGTH 8), job_count (i),
total_hours (i), first_date (c LENGTH 8).
- tt_workload: a standard table of ty_workload with an empty key.
- The class has this public method:
summarize
IMPORTING it_jobs TYPE tt_job
RETURNING VALUE(rt_workload) TYPE tt_workload.
- Do not add other public methods.
# 5. Business rules
1. A job is relevant if the job ID is not empty, the mechanic is not empty, and
the work hours are greater than zero. Ignore all other jobs.
2. Group the relevant jobs by mechanic.
3. The result has one row per mechanic:
- mechanic: the mechanic.
- job_count: the number of relevant jobs of this mechanic.
- total_hours: the sum of the work hours of these jobs.
- first_date: the earliest start date of these jobs.
4. A start date is set if it is not empty and if it is not 00000000. When you
determine the earliest date, consider only set dates. If no job of a mechanic
has a set date, first_date is empty.
5. Sort the result by mechanic in ascending order.
6. If no relevant job exists, return an empty table.
# 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 objects outside package $TMP.
# 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,42 @@
{
"id": "G0119",
"category": "C",
"object_type": "CLAS",
"difficulty": 2,
"release_target": "v816",
"expected_outcome": "implement",
"budget": {
"max_tool_calls": 40,
"max_activations": 10
},
"seed": [],
"contract": [
{
"type": "CLAS",
"name": "{{P}}WORKLOAD_PLANNER"
}
],
"out_of_scope": [],
"hidden_tests": [
{
"type": "CLAS",
"name": "{{P}}T02_HIDDEN",
"file": "hidden/t02_hidden.clas.abap",
"description": "T02 hidden tests"
}
],
"reference": [
{
"type": "CLAS",
"name": "{{P}}WORKLOAD_PLANNER",
"file": "reference/workload_planner.clas.abap",
"description": "Workload planner",
"testclasses_file": "reference/workload_planner.testclasses.abap"
}
],
"craft_checks": [
"method_length",
"nested_loops",
"internal_table_access"
]
}